log (lx_log.hpp)

From The Foundry MODO SDK wiki
Jump to: navigation, search
There are security restrictions on this page


Contents

ILxLogService

The log service allows subsystems to be registered and walked.

(1) SDK: Declarations
 #define LXu_LOGSERVICE  "0BC355C2-5E6B-49EF-B368-600D9F26F543"
 #define LXa_LOGSERVICE  "logservice"

As with all globals, the first method gets the ILxScriptQueryID interface for the system.

(2) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 ScriptQuery) (
         LXtObjectID              self,
         void                   **ppvObj);

Subsystem Registration

Any client can choose to log events with the log manager. Each client should register one subsystem name for each set of logged entry types it wants to have. You can register as many subsystems as you like, using each for different types of logging events. Any client can use any defined subsystem. Each registration is defined as an ILxLogID.

The log system itself registers its own subsystem.

(3) SDK: Declarations
 #define LXsLOG_LOGSYS    "logsys"

Subsystems are registered automatically simply by adding a tag to any server.

(4) SDK: Declarations
 #define LXsSRV_LOGSUBSYSTEM     "server.logsubsystem"

Since a tag can only be defined once per server, the string associated with the tag is a space-delimited list of subsystems to define. Slashes can be used to define categories that group subsystems. Note that the group name is considered part of the subsystems name, and as such must be included when doing lookusp on the name.

(5) SDK: Log Subsystem Define Server Tag Example
 // Define a single subsystem
 const char *oneSubSystem        = "mySubSystem";
 
 // Define multiple subsystems in groups
 const char *multipleSubSystems  = "group1/mySubSystem group2/subGroup/anotherSubSystem";

Enumeration

The list of subsystems can be walked with these functions. As of 901, an entry can only belong to one subsystem, or zero if it hasn't been added to any yet.

(6) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 SubSystemCount) (
         LXtObjectID              self,
         unsigned int            *count);

(7) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 SubSystemByIndex) (
         LXtObjectID              self,
         unsigned int             index,
         void                   **ppvObj);

This looks up a subsystem by name or, if NULL, by ID.

(8) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 SubSystemLookup) (
         LXtObjectID              self,
         const char              *name,
         void                   **ppvObj);

There is also a special master subsystem. This is a combination of all logged entries, plus the most recently logged rolling entry. Entries cannot be directly added to this, but the ones that are there can be read in the normal manner.

(9) SDK: Declarations
 #define LXsLOG_MASTERSYS         "master"

(10) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 MasterSubSystem) (
         LXtObjectID              self,
         void                   **ppvObj);

The user class has methods for getting subsystems by index or by name.

(11) User Service Class: LogService method
         bool
 GetSubSystem (
         unsigned                 index,
         CLxLoc_Log              &sys)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (SubSystemByIndex (index, &obj)))
                 return false;
 
         return sys.take (obj);
 }
 
         bool
 GetSubSystem (
         const char              *name,
         CLxLoc_Log              &sys)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (SubSystemLookup (name, &obj)))
                 return false;
 
         return sys.take (obj);
 }

Info Block Registration

Besides simple string-based message logging, it is also possible to log more formated infromation.. An example would be the status of the box tool, which could include the coordinates of two of the box's corners. However, this information is less than useful unless other systems know how to deal with the information provided.

To help with this problem, clients define an object with an ILxLogInfoBlock interface. The name and datatype are used to construct a block of information for display to the user. The name can include periods to group common properties.

For example, the corners of the box consists of one value for each axis, totaling three values for each corner, or six to define an entire box. If there is enough room, this might be displayed as six separate lines with one value each. If there isn't enough room for all six lines, the display could compensate by displaying one line each for each corner by combining all of the subnames for a particular name on the same line. If not needed, the subname can be set to NULL.

In the case of a box, we might have low.x, low.y and low.z for one corner, and high.x, high.y and high.z for the other corner, each defined to use the distance exotype.

Strings defined in the config file and uireg can be used to get user-readable names for these definitions. The actual layout in the log view is dependant on the available space

(12) Box Tool Info with maximum available space
 Box
   Low  X:   -1.0 m
        Y:   -1.0 m
        Z:   -1.0 m
   High X:    1.0 m
        Y:    1.0 m
        Z:    1.0 m

(13) Box Tool Info with less than optimal space
 Box
   Low  X, Y, Z:  -1.0 m, -1.0 m, -1.0 m
   High X, Y, Z:   1.0 m,  1.0 m,  1.0 m

(14) Box Tool Info with minimal vertical space
 Box   Low X, Y, Z:  -1.0 m, -1.0 m, -1.0 m  --  High X, Y, Z:   1.0 m,  1.0 m,  1.0 m

Info blocks are registered simply by defining a plug-in using the ILxLogInfoBlock interface. Once defined, any subsystem can use them to format their data. This fails if the name is already registered.

Enumeration

Like subsystems, the list of info blocks can be walked and looked up by name.

(15) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 InfoBlockCount) (
         LXtObjectID              self,
         unsigned int            *count);

(16) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 InfoBlockByIndex) (
         LXtObjectID              self,
         unsigned int             index,
         void                   **ppvObj);

This looks up a subsystem by name.

(17) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 InfoBlockLookup) (
         LXtObjectID              self,
         const char              *name,
         void                   **ppvObj);

These helpers check to see if two field names are in the same group (i.e., have the same name up to the first period), and return the names of the group and sub.

The group test returns LXe_TRUE if the two field names are in the same group and LXe_FALSE if not. Any other code is an error.

(18) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 InfoBlockFieldsAreSameGroup) (
         LXtObjectID              self,
         const char              *name1,
         const char              *name2);

This returns the group and/or sub parts of the field name. Either can be NULL if you only want the one. Note that *sub may be NULL if there is no sub. Also note that this function is NOT thread safe, and that the strings returned are only good until the next call to this function.

(19) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 InfoBlockFieldGetParts) (
         LXtObjectID               self,
         const char               *name,
         const char              **group,
         const char              **sub);

Creating Entires

Once registered, messages can be added to the log using simple strings. Each message will be added to both the master log and to the subsystem's own internal log. These logs are stored as lists, with each new messages being added to the end of the list, and with old entries purged as needed. The type can be any LxResult code.

LXe_INFO messages are relatively generic system states, such as a tooltip or confirmation that an operation has completed. LXe_WARNING and any LXx_FAIL() code designate non-fatal and fatal messages respectively. LXe_ABORT should be sent when an operation is aborted through a user action.

(20) SDK: Declarations

Debug messages are used only for internal purposes, and follow the same visibility rules as DBprintf(), as well as being output to the console through DBprintf() when added.

(21) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 CreateEntryMessage) (
         LXtObjectID              self,
         LxResult                 type,
         const char              *message,
         void                   **ppvObj);

Info blocks can be used to provide formatted messages. The first step is to get a new ILxLogEntryID with this method. The block arguments are the name of a previously registered info block. Once created, the ILxLogEntryID can be filled in with the the appropriate methods.

(22) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 CreateEntryInfoBlock) (
         LXtObjectID              self,
         LxResult                 type,
         const char              *blockName,
         void                   **ppvObj);

A second way to add complex formatted messages is through name/value pairs. The returned ILxLogEntryID will need to be populated with the appropriate methods.

(23) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 CreateEntryPaired) (
         LXtObjectID              self,
         LxResult                 type,
         void                   **ppvObj);

After an entry has been created and filled in, it can be added to a subsystem using ILxLog::AddEntry() or ILxLog::RollEntry(). Message entries can be added as children of other message entries via ILxLogEntry::AddEntry(). Note that the AddEntry() and RollEntry() methods do their own AddRef(). This allows the same message to be added to multiple subsystems, but also means that you must release the object you created when you are done with it.

The C++ user classes methods get new entries and stores them in our user wrappers. These new COM objects are take()en by the CLxLoc_LogEntry(), and thus will automatically be released when the C++ class's destructor is called; you do NOT have to explicitly release their COM objects unless you explicitly AddRef()ed them.

(24) User Service Class: LogService method
         bool
 NewEntry (
         LxResult                 type,
         const char              *message,
         CLxLoc_LogEntry         &entry)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (CreateEntryMessage (type, message, &obj)))
                 return false;
 
         return entry.take (obj);
 }
 
         bool
 NewBlockEntry (
         LxResult                 type,
         const char              *name,
         CLxLoc_LogEntry         &entry)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (CreateEntryInfoBlock (type, name, &obj)))
                 return false;
 
         return entry.take (obj);
 }
 
         bool
 NewPairEntry (
         LxResult                 type,
         CLxLoc_LogEntry         &entry)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (CreateEntryPaired (type, &obj)))
                 return false;
 
         return entry.take (obj);
 }

Low-level Monitor

There are times when high-level operations trigger low-level operations that may take a while. In that case we should have a monitor, but there is no way to pass the monitor down from the UI command to the actual code that needs it. The log service allows us to prepare a monitor and deliver it to a lower-level function.

This method sets the global monitor. If called with null the monitor is cleared.

(25) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 SetMonitor) (
         LXtObjectID              self,
         LXtObjectID              monitor);

This method can be used by any low-level client to acquire the global monitor for use. The acquire method returns a "peek" of the global monitor, so it is not add-refed, and the method may return null if there is none or it has already been acquired previously.

(26) SDK: ILxLogService interface
         LXxMETHOD(  LXtObjectID,
 AcquireMonitor) (
         LXtObjectID              self);

Disabling and Enabling Logging

Logging can be enabled and disabled on a per-subsystem basis. When disabled, logged events will still go to that subsystem, but they will not show up in the master log or the console output. This does not affect rolling log entries.

(27) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 EnableLogging) (
         LXtObjectID              self,
         const char              *systemName,
         unsigned int             state);

This returns LXe_TRUE if loggin is enabled for a system, and LXe_FALSE if it is disabled.

(28) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 IsLoggingEnabled) (
         LXtObjectID              self,
         const char              *systemName);

nexus 501

This allows yout create a new entry from an ILxMessageID. The message itself and the type code are read from the ILxMessageID

(29) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 CreateEntryMessageFromMsgObj) (
         LXtObjectID              self,
         LXtObjectID              msgObj,
         void                   **ppvObj);

(30) User Service Class: LogService method
         bool
 NewEntryFromMsgObj (
         CLxLoc_Message          &message,
         CLxLoc_LogEntry         &entry)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (CreateEntryMessageFromMsgObj (message, &obj)))
                 return false;
 
         return entry.take (obj);
 }

This function lets plug-ins dump data to the normal debug log. The level indicates the severity of the information, allowing debug trace output to be hidden during normal usage.

(31) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 DebugLogOutput) (
         LXtObjectID              self,
         unsigned int             level,
         const char              *line);

(32) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 DebugLogOutputSys) (
         LXtObjectID              self,
         unsigned int             level,
         const char              *logSystem,
         const char              *line);

(33) SDK: Declarations
 #define LXi_DBLOG_ERROR          1
 #define LXi_DBLOG_NORMAL         2
 #define LXi_DBLOG_TRACE          3
 #define LXi_DBLOG_VERBOSE        4

Be careful when using DebugOut() and DebugOutSys(), as large strings will overflow the 1k buffers. You can increase the size of these buffers if necessary.

(34) User Service Class: LogService method
         bool
 DebugOut (
         unsigned int             level,
         const char              *format,
         ...)
 {
         char                     buf[1024];
         va_list                  marker;
 
         va_start (marker, format);
         vsprintf (buf, format, marker);
         va_end (marker);
 
         return LXx_OK (DebugLogOutput (level, buf));
 }

(35) User Service Class: LogService method
         bool
 DebugOutSys (
         unsigned int             level,
         const char              *logSystem,
         const char              *format,
         ...)
 {
         char                     buf[1024];
         va_list                  marker;
 
         va_start (marker, format);
         vsprintf (buf, format, marker);
         va_end (marker);
 
         return LXx_OK (DebugLogOutputSys (level, logSystem, buf));
 }

nexus 801

The exception message is a single, global message object that holds a description of the cause of an unexpected failure. This can be set at the site of an initial error with a description of the cause of the error, and it will be displayed to the user by the controlling code higher up the stack.

ExceptionMessage() grabs the exception message for this caller and returns it to allow it to be changed. Flags control the action to perform. Even if this exception isn't the primary one it still gets captured for informational purposes. Since an exception mechanism that itself generates errors would be weird, this function will return null on errors which the client should check and do nothing.

(36) SDK: ILxLogService interface
         LXxMETHOD(  LXtObjectID,
 ExceptionMessage) (
         LXtObjectID              self,
         LxResult                 error,
         unsigned                 flags);

LOWLEVEL
Marks this as a low-level description that should be overridden by any later (presumably more useful) description.
OVERRIDE
Explicitly overriding any previous message.

(37) SDK: Declarations
 #define LXfEXMSG_LOWLEVEL        0x01
 #define LXfEXMSG_OVERRIDE        0x02

Exception messages are handled by a client system high enough to be able to display an error message, but general enough that any type of exception might be possible. The start method assures that past exceptions have been handled and clears the state. The collect method reads out any exception message. This generally only needs to be called when an error has happened, and if there is no error it will return NOTFOUND.

(38) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 ExceptionBlockStart) (
         LXtObjectID              self);

(39) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 ExceptionBlockCollect) (
         LXtObjectID              self,
         void                   **ppvObj);

nexus 801

This low-level utility provides a way to change the string and type code associated with a message-class log entry. This is rarely used, but can be useful when you need to add some summary information to a parent entry. This only works on log entries created with CreateEntryMessage().

(40) SDK: ILxLogService interface
         LXxMETHOD(  LxResult,
 ReplaceEntryMessage) (
         LXtObjectID              self,
         LXtObjectID              logEntry,
         LxResult                 type,
         const char              *msg);

Empty log service Python user class.

(41) PY: LogService method
 pass

ILxLogInfoBlock

Registering an info block requires an ILxLogInfoBlock object.

(42) SDK: Declarations
 #define LXu_LOGINFOBLOCK        "B9AEE11A-3501-4dc2-90A6-41F2435856C6"
 #define LXa_LOGINFOBLOCK        "loginfoblock"

These return the name of the block.

(43) SDK: ILxLogInfoBlock interface
         LXxMETHOD(  LxResult,
 Name) (
         LXtObjectID               self,
         const char              **name);

This walks the list of fields, returning the total count and the name and datatype of each field. Note that FieldName() is NOT thread safe, and the returned string is only valid until the next call to this method.

(44) SDK: ILxLogInfoBlock interface
         LXxMETHOD(  LxResult,
 FieldCount) (
         LXtObjectID              self,
         unsigned int            *count);
 
         LXxMETHOD(  LxResult,
 FieldName) (
         LXtObjectID               self,
         unsigned int              index,
         const char              **name);
 
         LXxMETHOD(  LxResult,
 FieldType) (
         LXtObjectID               self,
         unsigned int              index,
         const char              **type);

Empty LogInfoBlock Python user class.

(45) PY: LogInfoBlock method
 pass

ILxLog

A log subsystem manages it's individual entries. These objects are owned by the log system and should not be released by clients.

(46) SDK: Declarations
 #define LXu_LOG         "1890538F-D64C-478c-8472-228B7C9AB1DF"
 #define LXa_LOG         "logsubsystem"

Adding Entries

This adds an entry to a subsystem. Old entries will be dropped if needed. This does an AddRef() on the object, meaning you'll still need to do your own release on it after it has been added.

(47) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 AddEntry) (
         LXtObjectID              self,
         LXtObjectID              entry);

It is also possible to create rolling messages. For example, it is not useful to log all of the Box Tool Info messages as described above; you really only want to see the current state of the Box Info Tool at any given time. A rolling message can be used to accomplish this. Another example is a context-sensitive status bar that provides specific information based on what the mouse is hovering over.

This changes the rolling message. A separate rolling message is stored for each subsystem, with the one most recently changed being used as the master message.

(48) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 RollEntry) (
         LXtObjectID              self,
         LXtObjectID              entry);

The rolling message can also be cleared when it is no longer needed.

(49) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 RollClear) (
         LXtObjectID              self);

Enumeration

Logged events specific to a particular subsystem can be scanned with these methods.

(50) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 EntryCount) (
         LXtObjectID              self,
         unsigned int            *count );

(51) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 EntryByIndex) (
         LXtObjectID              self,
         unsigned int             index,
         void                   **ppvObj );

If you don't want to or can't release an entry, you can "peek" at the entry with this method. These entries should be considered volatile and should not be held onto for very long unless you AddRef them yourself.

(52) SDK: ILxLog interface
         LXxMETHOD(  LXtObjectID,
 PeekEntryByIndex) (
         LXtObjectID              self,
         unsigned int             index );

The most recently added entry can be fetched with this function. This may be NULL if there is no recent entry.

(53) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 GetCurrentEntry) (
         LXtObjectID              self,
         void                   **ppvObj );

The maximum number of entries that a particular subsystem can store can be set and read with these functions.

(54) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 SetMaxEntries) (
         LXtObjectID              self,
         unsigned int             max );

(55) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 GetMaxEntries) (
         LXtObjectID              self,
         unsigned int            *max );

The current rolling message can also be read. This returns LXe_NOTFOUND if there is currently no rolling entry.

(56) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 GetRolling) (
         LXtObjectID              self,
         void                   **ppvObj );

This clears all entries from the subsystem. If called on the master subsystem, all subsystems are cleared.

(57) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 ClearAll) (
         LXtObjectID              self );

The name of a subsystem can be returned at any time.

(58) SDK: ILxLog interface
         LXxMETHOD(  LxResult,
 Name) (
         LXtObjectID               self,
         const char              **name );

User Methods

An alternate constructor for the subsystem allows the name to be specified. Or later, of you prefer.

(59) User Class: Log method
 CLxUser_Log (
         const char              *name)
 {
         _init ();
         setByName (name);
 }
 
         bool
 setByName (
         const char              *name)
 {
         CLxLoc_LogService        svc;
         LXtObjectID              obj;
 
         if (LXx_FAIL (svc.SubSystemLookup (name, &obj)))
                 return false;
 
         return take (obj);
 }

For the most common case of a single text message we also have a user method to make that easy. Be sure to watch your string length due to the 1k buffer. You can increase the size of this buffer if necessary.

(60) User Class: Log method
         bool
 Message (
         LxResult                 type,
         const char              *format,
         ...)
 {
         CLxLoc_LogService        svc;
         CLxLoc_LogEntry          entry;
         LXtObjectID              obj;
         char                     buf[1024];
         va_list                  marker;
 
         va_start (marker, format);
         vsprintf (buf, format, marker);
         va_end (marker);
 
         if (LXx_FAIL (svc.CreateEntryMessage (type, buf, &obj)))
                 return false;
 
         if (!entry.take (obj))
                 return false;
 
         return LXx_OK (AddEntry (entry));
 }
 
         bool
 Info (
         const char              *text)
 {
         return Message (LXe_INFO, text);
 }
 
         bool
 Warn (
         const char              *text)
 {
         return Message (LXe_WARNING, text);
 }

Empty Log Python user class.

(61) PY: Log method
 pass

ILxLogEntryID

A log entry contains a timestamp, an LxResult code and a message string. Log entries are owned by the subsystem and should not be released by the client.

(62) SDK: Declarations
 #define LXu_LOGENTRY    "E83679B2-DB4D-4D90-B81B-5F786D212FB3"
 #define LXa_LOGENTRY    "logentry"

Simple Messages

Log entries can have child entries. A common child event is simply another message that provides more detail for the first, such as why an event failed. Normally the root event is a warning or error, with one or more child messages describing exactly why this occured. Info messages may also have children, providing more details on what might be an otherwise simple statement.

Note that this only applies to simple messages, and not to complex data such as info blocks and name/value pairs.

This does an AddRef() on the object, meaning you'll still need to do your own release on it after it has been added.

A log entry can have multiple parents, and will thus appear as a child of each parent. Care should be taken to avoid loops.

(63) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 AddEntry) (
         LXtObjectID              self,
         LXtObjectID              entry);

Info Blocks

Once an info block message has been created with AddMessageInfoBlock(), it needs to be populated with useful data.

First, we'll show an example of a populated block:

(64) Box Tool Info with subtitle and description text
 Box (Squared)
   Low  X:   -1.0 m
        Y:   -1.0 m
        Z:   -1.0 m
   High X:    1.0 m
        Y:    1.0 m
        Z:    1.0 m
 
 Drag to change the size of the box.
 Right-click to make the box.
 Middle-click to scale the box around its center.

This sets a title that can appear at the top of the block, which in this case is "Box (Squared)".

(65) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 SetTitle) (
         LXtObjectID              self,
         const char              *title );

This sets a description, which here is the three lines of text at the bottom of the block.

(66) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 SetDesc) (
         LXtObjectID              self,
         const char              *desc );

This sets the value of one of the fields in the info block. The specific field can be specified by either it's name or it's index, if the name is NULL. A matching ILxValueID must be provided for the field.

(67) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 SetValue) (
         LXtObjectID              self,
         const char              *name,
         unsigned int             index,
         LXtObjectID              value );

Name/Value Pairs

Name/Value pairs are both strings. Unlike info blocks, which have a fixed number of fields, there can be any number of pairs in an entry.

(68) Name/Value pair example
 Left Click:          Select
 Left Double-Click:   Select Edge Loop
 Right Click:         Menu
 Middle Click:        Lasso
 
 Hold Ctrl, Shift and/or Alt for more options.

SetDesc() can again be used to add a description after the pairs.

This function can be used to add a pair of strings to the message.

(69) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 AddPair) (
         LXtObjectID              self,
         const char              *name,
         const char              *value );

Properties

The various properties of an entry can be read with these functions.

The class of the entry wuill be one of the following, depending on how the entry was created:

(70) SDK: Declarations

(71) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 Class) (
         LXtObjectID              self,
         unsigned int            *classType );

The type can be returned as an LxResult code.

(72) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 Type) (
         LXtObjectID              self,
         LxResult                *type );

The time at which the entry was logged can be read either as a C-style time_t or as a string via asctime().

(73) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 Time) (
         LXtObjectID              self,
         time_t                  *time );

(74) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 TimeString) (
         LXtObjectID               self,
         const char              **string );

The list of child entries for MESSAGE class entries can be enumerated with these methods.

(75) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 ChildCount) (
         LXtObjectID              self,
         unsigned int            *count );

(76) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 ChildByIndex) (
         LXtObjectID              self,
         unsigned int             index,
         void                   **ppvObj );

A peek method is also available.

(77) SDK: ILxLogEntry interface
         LXxMETHOD(  LXtObjectID,
 PeekChildByIndex) (
         LXtObjectID              self,
         unsigned int             index );

Each root-level entry knows what subsystems it has been added to.

(78) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 SubSystemCount) (
         LXtObjectID              self,
         unsigned int            *count );

(79) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 SubSystemByIndex) (
         LXtObjectID              self,
         unsigned int             index,
         void                   **ppvObj );

Messages

The message stored in a MESSAGE-type log entry can be read out with this function. This fails if used on a non-MESSAGE type.

(80) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 Message) (
         LXtObjectID               self,
         const char              **message );

Info Blocks

The title and description stored in a block-type log entry can be read out with these functions. These fail if used on a MESSAGE entry. Desc() can also be used on pairs, but not on MESSAGEs.

(81) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 Title) (
         LXtObjectID               self,
         const char              **title );

(82) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 Desc) (
         LXtObjectID               self,
         const char              **desc );

This returns the info block definition.

(83) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 InfoBlock) (
         LXtObjectID               self,
         void                    **ppvObj);

The ILxValueID for a particular field in the block can be extracted with this method. The value should be freed by the client when no longer needed. The field can be referenced by index or by name.

(84) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 InfoBlockValue) (
         LXtObjectID              self,
         const char              *name,
         unsigned int             index,
         void                   **ppvObj );

Name/Value Pairs

These mehtods return the number of pairs, and the name and value strings by index.

(85) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 PairCount) (
         LXtObjectID               self,
         unsigned int             *count );

(86) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 PairName) (
         LXtObjectID               self,
         unsigned int              index,
         const char              **name );

(87) SDK: ILxLogEntry interface
         LXxMETHOD(  LxResult,
 PairValue) (
         LXtObjectID               self,
         unsigned int              index,
         const char              **value );

Empty LogEntry Python user class.

(88) PY: LogEntry method
 pass

Threaded Support

We handle threading a slightly unique manner. By far the most common threaded operation is creating new log entries and adding them to a subsystem or another entry. There is little point in walking the log system hierarchy from a thread. Therefore, we only allow walking the hierarchy from the main thread, while any thread can create log entries and add them to subsystems or other entries as children. The only exception is the functions to walk the subsystem list, as a thread (such as an image I/O plug-in) will need to find the subsystem that its adding events to.

For simplicity, these new entries aren't actually added to their parents in the thread, but rather the add is defered until the main loop. The thread need not worry about this; it is handled in the background. Attempts to use other functions will fail.

Log Listener

The log listener can be used to listen for newly added log entries.

(89) SDK: Declarations
 #define LXu_LOGLISTENER         "c5fd260b-cab7-4283-b876-2314144ae83a"

This is called when a new log subsystem (ILxLogID) has been added to the application.

(90) SDK: ILxLogListener interface
         LXxMETHOD( void,
 SystemAdded) (
         LXtObjectID              self,
         LXtObjectID              system);

Called when a new log entry is added to a subsystem. Note that a single log entry many belong to multiple subsystems.

(91) SDK: ILxLogListener interface
         LXxMETHOD( void,
 EntryAdded) (
         LXtObjectID              self,
         LXtObjectID              system,
         LXtObjectID              entry);

Called when a new entry was added as a child of anohter log entry. This is only sent for root entries.

(92) SDK: ILxLogListener interface
         LXxMETHOD( void,
 ChildEntryAdded) (
         LXtObjectID              self,
         LXtObjectID              entry,
         LXtObjectID              parentEntry);

Called when an entry has been dropped from the log, usually because the number of entries exceeds te maximum number allowed, and thus the oldest entry is removed.

(93) SDK: ILxLogListener interface
         LXxMETHOD( void,
 EntryDropped) (
         LXtObjectID              self,
         LXtObjectID              system,
         LXtObjectID              entry);

Rolling log entries are much the same as normal logged entries. Since rolling logs only contain a single entry, RollingEntryAdded() implies RollingEntryDrop(). RollingEntryDrop() will still be called when the rolling log is cleared.

(94) SDK: ILxLogListener interface
         LXxMETHOD( void,
 RollingEntryAdded) (
         LXtObjectID              self,
         LXtObjectID              system,
         LXtObjectID              entry);
 
         LXxMETHOD( void,
 RollingChildEntryAdded) (
         LXtObjectID              self,
         LXtObjectID              entry,
         LXtObjectID              parentEntry);
 
         LXxMETHOD( void,
 RollingEntryDropped) (
         LXtObjectID              self,
         LXtObjectID              system,
         LXtObjectID              entry);

Empty LogListener Python user class.

(95) PY: LogListener method
 pass