omni::structuredlog::IStructuredLog_abi

Defined in omni/structuredlog/IStructuredLog.h

class IStructuredLog_abi : public omni::core::Inherits<omni::core::IObject, std::integral_constant<uint64_t, carb::fnv1aHash("omni.structuredlog.IStructuredLog")>::value>

Main structured log interface.

This should be treated internally as a global singleton. Any attempt to create or acquire this interface will return the same object just with a new reference taken on it.

There are three main steps to using this interface:

  • Set up the interface. For most app’s usage, all of the default settings will suffice. The default log path will point to the Omniverse logs folder and the default user ID will be the one read from the current user’s privacy settings file (if it exists). If the default values for these is not sufficient, a new user ID should be set with omni::structuredlog::IStructuredLogSettings::setUserId() and a log output path should be set with omni::structuredlog::IStructuredLogSettings::setLogOutputPath(). If the privacy settings file is not present, the user name will default to a random number. The other defaults should be sufficient for most apps. This setup only needs to be performed once by the host app if at all. The omni::structuredlog::IStructuredLogSettings interface can be acquired either by casting an omni::structuredlog::IStructuredLog object to that type or by directly creating the omni::structuredlog::IStructuredLogSettings object using omni::core::ITypeFactory_abi::createType().

  • Register one or more event schemas. This is done with omni::structuredlog::IStructuredLog::allocSchema() and omni::structuredlog::IStructuredLog::commitSchema(). At least one event must be registered for any events to be processed. Once a schema has been registered, it will remain valid until the structured log module is unloaded from the process. There is no way to forcibly unregister a set of events once registered.

  • Send zero or more events. This is done with the omni::structuredlog::IStructuredLog::allocEvent() and omni::structuredlog::IStructuredLog::commitEvent() functions.

For the most part, the use of this interface will be dealt with in generated code. This generated code will come from the ‘omni.structuredlog’ tool in the form of an inlined header file. It is the host app’s responsibility to call the header’s schema registration helper function at some point on startup before any event helper functions are called.

All messages generated by this structured log system will be CloudEvents v1.0 compliant. These should be parsable by any tool that is capable of understanding CloudEvents.

Before an event can be sent, at least one schema describing at least one event must be registered. This is done with the omni::structuredlog::IStructuredLog::allocSchema() and omni::structuredlog::IStructuredLog::commitSchema() functions. The omni::structuredlog::IStructuredLog::allocSchema() function returns a handle to a block of memory owned by the structured log system and a pointer to that block’s data. The caller is responsible for both calculating the required size of the buffer before allocating, and filling it in with the schema data trees for each event that can be sent as part of that schema. The helper functions in omni::structuredlog::JsonTreeSizeCalculator and omni::structuredlog::JsonBuilder can be used to build these trees. Once these trees are built, they are stored in a number of entries in an array of omni::structuredlog::EventInfo objects. This array of event info objects is then passed to omni::structuredlog::IStructuredLog::commitSchema() to complete the schema registration process.

Sending of a message is split into two parts for efficiency. The general idea is that the caller will allocate a block of memory in the event queue’s buffer and write its data directly there. The allocation occurs with the omni::structuredlog::IStructuredLog::allocEvent() call. This will return a handle to the allocated block and a pointer to the first byte to start writing the payload data to. The buffer’s header will already have been filled in upon return from omni::structuredlog::IStructuredLog::allocEvent(). Once the caller has written its event payload information to the buffer, it will call omni::structuredlog::IStructuredLog::commitEvent() to commit the message to the queue. At this point, the message can be consumed by the event processing thread.

Multiple events may be safely allocated from and written to the queue’s buffer simultaneously. There is no required order that the messages be committed in however. If a buffer is allocated after one that has not been committed yet, and that newer event is committed first, the only side effect will be that the event processing thread will be stalled in processing new events until the first message is also committed. This is important since the events would still need to be committed to the log in the correct order.

All events that are processed through this interface will be written to a local log file. The log file will be periodically consumed by an external process (the Omniverse Transmitter app) that will send all the approved events to the telemetry servers. Only events that have been approved by legal will be sent. All other messages will be rejected and only remain in the log files on the local machine.

Subclassed by omni::core::Generated< omni::structuredlog::IStructuredLog_abi >

Public Functions

inline void *cast(omni::core::TypeId id) noexcept

Returns a pointer to the interface defined by the given type id if this object implements the type id’s interface.

Objects can support multiple interfaces, even interfaces that are in different inheritance chains.

The returned object will have omni::core::IObject::acquire() called on it before it is returned, meaning it is up to the caller to call omni::core::IObject::release() on the returned pointer.

The returned pointer can be safely reinterpret_cast<> to the type id’s C++ class. For example, “omni.windowing.IWindow” can be cast to omni::windowing::IWindow.

Do not directly use this method, rather use a wrapper function like omni::core::cast() or omni::core::ObjectPtr::as().

Thread Safety

This method is thread safe.

inline void acquire() noexcept

Increments the object’s reference count.

Objects may have multiple reference counts (e.g. one per interface implemented). As such, it is important that you call omni::core::IObject::release() on the same pointer from which you called omni::core::IObject::acquire().

Do not directly use this method, rather use omni::core::ObjectPtr, which will manage calling omni::core::IObject::acquire() and omni::core::IObject::release() for you.

Thread Safety

This method is thread safe.

inline void release() noexcept

Decrements the objects reference count.

Most implementations will destroy the object if the reference count reaches 0 (though this is not a requirement).

Objects may have multiple reference counts (e.g. one per interface implemented). As such, it is important that you call omni::core::IObject::release() on the same pointer from which you called omni::core::IObject::acquire().

Do not directly use this method, rather use omni::core::ObjectPtr, which will manage calling omni::core::IObject::acquire() and omni::core::IObject::release() for you.

Thread Safety

This method is thread safe.

Protected Functions

virtual bool isEnabled_abi(EventId eventId) noexcept = 0

Checks whether a specific event or schema is enabled.

Remark

This checks if a named event or its schema is currently enabled in the structured log system. Individual events or entire schemas may be disabled at any given time. Both the schema and each event has its own enable state. A schema can be disabled while still leaving its events’ previous enable/disable states unmodified. When the schema is enabled again, its events will still retain their previous enable states. Set omni::structuredlog::IStructuredLog::setEnabled() for more information on how to enable and disable events and schemas.

Parameters

eventId[in] The unique ID of the event to check the enable state of. This is the ID that was originally used to register the event.

Returns

true if both the requested event and its schema is enabled.

Returns

false if either the requested event or its schema is disabled.

virtual void setEnabled_abi(EventId eventId, EnableFlags flags, bool enabled) noexcept = 0

Sets the enable state for a structured log event or schema, or the system globally.

Remark

This changes the current enable state for an event or schema. The scope of the enable change depends on the flags that are passed in. When an event is disabled (directly or from its schema or the structured log system being disabled), it will also prevent it from being generated manually. In this case, any attempt to call omni::structuredlog::IStructuredLog::allocEvent() for that disabled event or schema will simply fail immediately.

Remark

When a schema is disabled, it effectively disables all of its events. Depending on the flag usage however (ie: omni::structuredlog::fEnableFlagOverrideEnableState), disabling the schema may or may not change the enable states of each of its individual events as well (see omni::structuredlog::fEnableFlagOverrideEnableState for more information).

Note

The omni::structuredlog::fEnableFlagAll flag should only ever by used by the main host app since this will affect the behavior of all modules’ events regardless of their own internal state. Disabling the entire system should also only ever be used sparingly in cases where it is strictly necessary (ie: compliance with local privacy laws).

Parameters
Returns

No return value.

virtual uint8_t *allocSchema_abi(const char *schemaName, const char *schemaVersion, SchemaFlags flags, size_t size, AllocHandle *outHandle) noexcept = 0

Allocates a block of memory for an event schema.

Remark

This allocates a block of memory that the schema tree(s) for the event(s) in a schema can be created and stored in. Pointers to the start of each event schema within this block are expected to be stored in one of the omni::structuredlog::EventInfo objects that will later be passed to omni::structuredlog::IStructuredLog::commitSchema(). The caller is responsible for creating and filling in returned block and the array of omni::structuredlog::EventInfo objects. The omni::structuredlog::BlockAllocator helper class may be used to allocate smaller chunks of memory from the returned block.

Thread Safety

This call is thread safe.

Note

This should only be used in generated structured log source code. This should not be directly except when absolutely necessary. This should also not be used as a generic allocator. Failure to use this properly will result in memory being leaked.

Parameters
  • schemaName[in] The name of the schema being registered. This may not be nullptr or an empty string. There is no set format for this schema name, but it should at least convey some information about the app’s name and current release version. This name will be used to construct the log file name for the schema.

  • schemaVersion[in] The version number for the schema itself. This may not be nullptr or an empty string. This should be the version number of the schema itself, not of the app or component. This will be used to construct the ‘dataschema’ name that gets passed along with each CloudEvents message header.

  • flags[in] Flags to control the behavior of the schema. This may be zero or more of the omni::structuredlog::SchemaFlags flags.

  • size[in] The size of the block to allocate in bytes. If this is 0, a block will still be allocated, but its actual size cannot be guaranteed. The omni::structuredlog::JsonTreeSizeCalculator helper class can be used to calculate the size needs for the new schema.

  • outHandle[out] Receives the handle to the allocated memory block on success. On failure, this receives nullptr. Each successful call must pass this handle to omni::structuredlog::IStructuredLog::commitSchema() even if an intermediate failure occurs. In the case of a schema tree creation failure, nullptr should be passed for events in the corresponding omni::structuredlog::IStructuredLog::commitSchema() call. This will allow the allocated block to be cleaned up.

Returns

A pointer to the allocated block on success. This block does not need to be explicitly freed - it will be managed internally. This pointer will point to the first byte that can be written to by the caller and will be at least size bytes in length. This pointer will always be aligned to the size of a pointer.

Returns

nullptr if no more memory is available.

Returns

nullptr if an invalid parameter is passed in.

virtual SchemaResult commitSchema_abi(AllocHandle schemaBlock, const EventInfo *events, size_t eventCount) noexcept = 0

Commits an allocated block and registers events for a single schema.

Remark

This registers a new schema and its events with the structured log system. This will create a new set of events in the structured log system. These events cannot be unregistered except by unloading the entire structured log system altogether. Upon successful registration, the events in the schema can be emitted using the event identifiers they were registered with.

Remark

If the new schema matches one that has already been registered, The operation will succeed with the result omni::structuredlog::SchemaResult::eAlreadyExists. The existing schema’s name, version, flags, and event table (including order of events) must match the new one exactly in order to be considered a match. If anything differs (even the flags for a single event or events out of order but otherwise the same content), the call will fail. If the schema with the differing flags or event order is to be used, its version or name must be changed to avoid conflict with the existing schema.

Remark

When generating the event identifiers for omni::structuredlog::EventInfo::eventId it is recommended that a string uniquely identifying the event be created then hashed using an algorithm such as FNV1. The string should contain the schema’s client name, the event’s name, the schema’s version, and any other values that may help to uniquely identify the event. Once hashed, it is very unlikely that the event identifier will collide with any others. This is the method that the code generator tool uses to create the unique event identifiers.

Remark

Up to 65536 (ie: 16-bit index values) events may be registered with the structured log system. The performance of managing a list of events this large is unknown and not suggested however. Each module, app, or component should only register its schema(s) once on startup. This can be a relatively expensive operation if done frequently and unnecessarily.

Thread Safety

This call is thread safe.

Parameters
  • schemaBlock[in] The block previously returned from omni::structuredlog::IStructuredLog::allocSchema() that contains the built event trees for the schema to register. These trees must be pointed to by the omni::structuredlog::EventInfo::schema members of the events array.

  • events[in] The table of events that belong to this schema. This provides information about each event such as its name, control flags, event identifier, and a schema describing how to interpret its binary blob on the consumer side. This may not be nullptr. Each of the trees pointed to by omni::structuredlog::EventInfo::schema in this table must either be set to nullptr or point to an address inside the allocated schema block schemaBlock that was returned from the corresponding call to omni::structuredlog::IStructuredLog::allocSchema(). If any of the schema trees point to an address outside of the schema block, this call will fail.

  • eventCount[in] The total number of events in the events table. At least one event must be registered. This may not be 0.

Return values
  • omni::structuredlog::SchemaResult::eSuccess – if the new schema is successfully registered as a set of unique events.

  • omni::structuredlog::SchemaResult::eAlreadyExists – if the new schema exactly matches one that has already been successfully registered. This can be considered a successful result. In this case, the schema block will be destroyed before return.

  • omni::structuredlog::SchemaResult::eEventIdCollision – if the new schema contains an event whose identifier matches that of an event that has already been registered with another schema. This indicates that the name of the event that was used to generate the identifier was likely not unique enough, or that two different versions of the same schema are trying to be registered without changing the schema’s version number first. No new events will be registered in this case and the schema block will be destroyed before return.

  • omni::structuredlog::SchemaResult::eFlagsDiffer – if the new schema exactly matches another schema that has already been registered except for the schema flags that were used in the new one. This is not allowed without a version change in the new schema. No new events will be registered in this case and the schema block will be destroyed before return.

Returns

Another omni::structuredlog::SchemaResult error code for other types of failures. No new events will be registered in this case and the schema block will be destroyed before return.

virtual uint8_t *allocEvent_abi(ParserVersion version, EventId eventId, AllocFlags flags, size_t payloadSize, AllocHandle *outHandle) noexcept = 0

Allocates a block of memory to store an event’s payload data in.

Remark

This is the main entry point for creating an event message. This allocates a block of memory that the caller can fill in with its event payload data. The caller is expected to fill in this buffer as quickly as possible. Once the buffer has been filled, its handle must be passed to the omni::structuredlog::IStructuredLog::commitEvent() function to finalize and send. Failing to pass a valid handle to omni::structuredlog::IStructuredLog::commitEvent() will stall the event queue indefinitely.

Remark

If the requested event has been marked as ‘critical’ by using the event flag omni::structuredlog::fEventFlagCriticalEvent, a blocking allocation will be used here instead. In this case, this will not fail due to the event queue being out of space.

Thread Safety

This call is thread safe.

Note

This call will fail immediately if either the requested event, its schema, or the entire system has been explicitly disabled. It is the caller’s responsibility to both check the enable state of the event before attempting to send it (ie: to avoid doing unnecessary work), and to gracefully handle the potential of this call failing.

Note

It is the caller’s responsibility to ensure that no events are generated during C++ static destruction time for the process during shutdown. Especially on Windows, doing so could result in an event being allocated but not committed thereby stalling the event queue. This could lead to a hang on shutdown.

Parameters
  • version[in] The version of the parser that should be used to read this event’s payload data. This should be omni::structuredlog::kParserVersion. If the structured log system that receives this message does not support this particular version (ie: a newer module is run on an old structured log system), the event message will simply be dropped.

  • eventId[in] the unique ID of the event that is being generated. This ID must exactly match the event ID that was provided in the omni::structuredlog::EventInfo::eventId value when the event was registered.

  • flags[in] Flags to control how the event’s block is allocated. This may be a combination of zero or more of the omni::structuredlog::AllocFlags flags.

  • payloadSize[in] The total number of bytes needed to store the event’s payload data. The caller is responsible for calculating this ahead of time. If the event does not have a payload, this should be 0. The number of bytes should be calculated according to how the requested event’s schema (ie: omni::structuredlog::EventInfo::schema) lays it out in memory.

  • outHandle[out] Receives the handle to the allocated block of memory. This must be passed to IStructuredLog::commitEvent() once the caller has finished writing all of the payload data to the returned buffer. The IStructuredLog::commitEvent() call acts as the cleanup function for this handle.

Returns

A pointer to the buffer to use for the event’s payload data if successfully allocated. The caller should start writing its payload data at this address according to the formatting information in the requested event’s schema. This returned pointer will always be aligned to the size of a pointer.

Returns

nullptr if the requested event, its schema, or the entire system is currently disabled.

Returns

nullptr if the event queue’s buffer is full and a buffer of the requested size could not be allocated. In this case, a invalid handle will be returned in outHandle. The IStructuredLog::commitEvent() function does not need to be called in this case.

Returns

nullptr if the event queue failed to be created or its processing thread failed start up.

Returns

nullptr if the given event ID is not valid.

virtual void commitEvent_abi(const AllocHandle handle) noexcept = 0

finishes writing a message’s payload and queues it for processing.

Remark

This commits a block that was previously allocated on this thread with omni::structuredlog::IStructuredLog::allocEvent(). It is required that the commit call occur on the same thread that the matching omni::structuredlog::IStructuredLog::allocEvent() call was made on. Each successful omni::structuredlog::IStructuredLog::allocEvent() call must be paired with exactly one omni::structuredlog::IStructuredLog::commitEvent() call on the same thread. Failing to do so would result in the event queue thread stalling.

Thread Safety

This call is thread safe.

Parameters

handle[in] The handle to the queue buffer block to be committed. This must not be nullptr. This must be the same handle that was returned through outHandle on a recent call to omni::structuredlog::IStructuredLog::allocEvent() on this same thread. Upon return, this handle will be invalid and should be discarded by the caller.

Returns

No return value.

virtual void *cast_abi(TypeId id) noexcept = 0

Returns a pointer to the interface defined by the given type id if this object implements the type id’s interface.

Objects can support multiple interfaces, even interfaces that are in different inheritance chains.

The returned object will have omni::core::IObject::acquire() called on it before it is returned, meaning it is up to the caller to call omni::core::IObject::release() on the returned pointer.

The returned pointer can be safely reinterpret_cast<> to the type id’s C++ class. For example, “omni.windowing.IWindow” can be cast to omni::windowing::IWindow.

Do not directly use this method, rather use a wrapper function like omni::core::cast() or omni::core::ObjectPtr::as().

Thread Safety

This method is thread safe.

virtual void acquire_abi() noexcept = 0

Increments the object’s reference count.

Objects may have multiple reference counts (e.g. one per interface implemented). As such, it is important that you call omni::core::IObject::release() on the same pointer from which you called omni::core::IObject::acquire().

Do not directly use this method, rather use omni::core::ObjectPtr, which will manage calling omni::core::IObject::acquire() and omni::core::IObject::release() for you.

Thread Safety

This method is thread safe.

virtual void release_abi() noexcept = 0

Decrements the objects reference count.

Most implementations will destroy the object if the reference count reaches 0 (though this is not a requirement).

Objects may have multiple reference counts (e.g. one per interface implemented). As such, it is important that you call omni::core::IObject::release() on the same pointer from which you called omni::core::IObject::acquire().

Do not directly use this method, rather use omni::core::ObjectPtr, which will manage calling omni::core::IObject::acquire() and omni::core::IObject::release() for you.

Thread Safety

This method is thread safe.