ACF $AcfVersion:0$
ilog - Logging Library

Introduction

The ilog library provides a comprehensive, component-based logging framework for the ACF (Application Component Framework). It offers flexible message handling, multiple output targets, hierarchical message organization, and extensible message consumers. The library is designed for system-independent logging with support for severity levels, message filtering, tracing, and both synchronous and asynchronous message delivery.

Key Features

  • Structured Message System: Type-safe message objects with category, ID, timestamp, and source information
  • Message Consumer Architecture: Pluggable consumers for flexible message routing and processing
  • Console Logging: Colored console output with customizable formatting
  • Message Containers: Storage and retrieval of messages with hierarchical organization
  • Extended Messages: Rich message objects with attached custom data and metadata
  • Component Integration: Seamless integration with ACF component framework
  • Tracing Support: Configurable tracing levels for verbose diagnostic output
  • Stream Bridging: Redirect standard output/error streams to logging system
  • Log Routing: Forward messages between different logging components based on severity
  • Thread-Safe Operations: Qt signal-slot based asynchronous message handling
  • Serialization: Full support for message persistence and loading

Architecture

The ilog library is organized into several layers:

Core Interfaces

IMessageConsumer

The fundamental interface for consuming log messages:

  • ilog::IMessageConsumer: Base interface for all message consumers
    • IsMessageSupported(): Check if a message type/category is accepted
    • AddMessage(): Add a message to the consumer

IMessageContainer

Interface for storing and managing collections of messages:

  • ilog::IMessageContainer: Container interface with message retrieval
    • GetMessages(): Retrieve all stored messages
    • ClearMessages(): Remove all messages from container
    • GetWorstCategory(): Get highest severity level of stored messages
  • ilog::IHierarchicalMessageContainer: Hierarchical container support

ILoggable

Interface for objects that can have a log consumer attached:

  • ilog::ILoggable: Provides log attachment capability
    • SetLogPtr(): Attach a message consumer
    • GetLogPtr(): Retrieve attached consumer

ITracingConfiguration

Interface for configuring tracing verbosity:

  • ilog::ITracingConfiguration: Control tracing levels
    • GetTracingLevel(): Get current tracing level (-1=off, 0=all)
    • SetTracingLevel(): Set tracing level

Message Classes

CMessage

Basic implementation of information provider interface:

  • ilog::CMessage: Fundamental message class implementing istd::IInformationProvider
    • Stores category, ID, text, source, flags, and timestamp
    • Implements serialization for persistence
    • Supports cloning and copying

CExtMessage

Extended message with attached objects:

  • ilog::CExtMessage: Message with attached serializable objects
    • Inherits from CMessage
    • Can attach multiple IObject instances with descriptions
    • Useful for passing complex data with log messages

TExtMessage

Template-based extended message:

  • ilog::TExtMessage<Element>: Template for creating custom message types
    • Combines CMessage with user-defined Element type
    • Automatic serialization of both parts
    • Type-safe message specialization

Logging Components

CLogCompBase

Base class for logging components with event queue:

  • ilog::CLogCompBase: Threadsafe logging using Qt signals/slots
    • Inherits from QObject and TMessageDelegatorComp
    • Asynchronous message delivery via EmitAddMessage signal
    • Delegates messages to slave consumer
    • Abstract WriteMessageToLog() for derived implementations

CLogComp

Standard logging component with message storage:

  • ilog::CLogComp: Complete logging component with message container
    • Inherits from CLogCompBase and CMessageContainer
    • Configurable maximum message count
    • Automatic message pruning when limit exceeded
    • Supports message serialization
    • Provides IMessageContainer interface

CStreamLogCompBase

Base class for stream-based logging:

  • ilog::CStreamLogCompBase: Foundation for text stream logging
    • Configurable minimum severity level filtering
    • Optional dot display for filtered messages
    • Customizable message formatting (category, code, timestamp)
    • Time format configuration
    • Tracks worst category encountered
    • Abstract WriteText() for output implementation

CConsoleLogComp

Console output with color support:

  • ilog::CConsoleLogComp: Colored console logging
    • Platform-specific color support (Windows/Unix)
    • Severity-based coloring (error=red, warning=yellow, info=default)
    • Writes to std::wcout
    • Inherits all CStreamLogCompBase features

Utility Components

CLogRouterComp

Routes messages between containers:

  • ilog::CLogRouterComp: Observes source container and forwards messages
    • Monitors IMessageContainer for new messages
    • Filters by minimum severity category
    • Prevents duplicate message forwarding
    • Integrates with model-observer pattern

CStandardStreamBridge

Bridges C++ streams to logging:

  • ilog::CStandardStreamBridge: Redirects std::cout/std::cerr
    • Inherits from std::basic_streambuf
    • Converts stream output to log messages
    • Restores original stream buffer on destruction
    • Thread-safe with mutex protection

CStandardStreamBridgeComp

Component wrapper for stream bridging:

  • ilog::CStandardStreamBridgeComp: Manages stream bridges as component
    • Creates bridges for both cout and cerr
    • Configurable message severity per stream
    • Automatic cleanup on component destruction

CTracingConfigurationComp

Tracing level configuration:

  • ilog::CTracingConfigurationComp: Manages tracing verbosity
    • Implements ITracingConfiguration interface
    • Configurable default tracing level
    • Serialization support for persistence
    • Component-based configuration

Base Classes

CLoggerBase

Foundation for logging functionality:

  • ilog::CLoggerBase: Implements ILogger via IMessageConsumer
    • Convenience methods: SendInfoMessage, SendWarningMessage, SendErrorMessage, SendCriticalMessage
    • "Send once" variants to prevent message duplication
    • Message decoration hook (DecorateMessage)
    • Log consumption checking (IsLogConsumed)
    • Manages attached IMessageConsumer

CMessageContainer

Message storage implementation:

  • ilog::CMessageContainer: Full-featured message container
    • Implements IMessageContainer and IMessageConsumer
    • Hierarchical container support
    • Configurable maximum message count
    • Slave consumer delegation
    • Message type registration for serialization
    • Thread-safe operations
    • Worst category tracking

Template Classes

TLoggerCompWrap

Logger functionality for components:

  • ilog::TLoggerCompWrap<Base>: Adds logging to any component
    • Inherits from Base and CLoggerBase
    • Automatic log consumer connection from "Log" reference
    • Verbose message support with tracing level
    • Component ID decoration of message sources
    • Type alias: CLoggerComponentBase for simple components

TMessageDelegatorComp

Message delegation component:

  • ilog::TMessageDelegatorComp<BaseComponent>: Forwards messages
    • Implements IMessageConsumer interface
    • Delegates all messages to slave consumer
    • Component-based message routing
    • Configurable via "SlaveMessageConsumer" reference

Usage Examples

Basic Logging with Simulation Components

#include <ilog/CMessage.h>
// Create console logger using simulation layer
consoleLog->SetIntAttr("MinCategory", 0); // Log all categories
consoleLog->SetBoolAttr("UseCategory", true);
consoleLog->SetBoolAttr("UseTimeStamp", false);
consoleLog->InitComponent();
// Create a message
1001,
"Application started successfully",
"MyApplication"
)
);
// Send message to console
consoleLog->AddMessage(message);
bool SetBoolAttr(const QByteArray &attributeId, bool value)
Set instance of bool attribute.
void InitComponent()
Initialilze component after setting all its attributes and references.
bool SetIntAttr(const QByteArray &attributeId, int value)
Set instance of int attribute.
Basic implementation of the istd::IInformationProvider interface for log messages.
Definition CMessage.h:80
@ IC_INFO
Normal information level.
Shared ownership smart pointer for interface types.
Note
Components are typically configured in .acc files and loaded through the ACF registry system. The simulation layer (TSimComponentWrap) is used for direct component instantiation in applications without full registry loading.

Component-Based Logging

// Define a component with logging capability
class MyComponent : public ilog::CLoggerComponentBase
{
public:
I_BEGIN_COMPONENT(MyComponent);
I_END_COMPONENT;
protected:
// reimplemented (icomp::CComponentBase)
virtual void OnComponentCreated() override
{
BaseClass::OnComponentCreated();
// Component is now initialized and can use logging
SendInfoMessage(2001, "Component created", "OnComponentCreated");
}
virtual void OnComponentDestroyed() override
{
SendInfoMessage(2002, "Component destroyed", "OnComponentDestroyed");
BaseClass::OnComponentDestroyed();
}
};
// Usage in application (with simulation layer)
component->SetRef("Log", log); // Connect log consumer
component->SetBoolAttr("EnableVerbose", false);
component->InitComponent();
// Or configure in .acc file:
// <Element Id="MyComp" ComponentId="MyComponent">
// <Reference Id="Log" Value="ConsoleLog"/>
// <Attribute Id="EnableVerbose" Value="false"/>
// </Element>
bool SetRef(const QByteArray &referenceId, IComponentSharedPtr componentPtr, const QByteArray &subelementId="")
Set named reference to some component.
bool SendInfoMessage(int id, const QString &message, const QString &messageSource=QString(), int flags=0) const
Send info message to log.
Template wrapper adding comprehensive logging functionality to components.
virtual void OnComponentCreated() override
Called when component is created.
virtual void OnComponentDestroyed() override
Called when component is destroyed.

Message Container Usage

#include <ilog/CLogComp.h>
#include <ilog/CMessage.h>
// Create log component using simulation layer
logComp->SetIntAttr("MaxMessageCount", 1000);
logComp->InitComponent();
// Add messages
3001, "Low memory warning", "System")));
3002, "File not found", "FileSystem")));
// Retrieve all messages
ilog::IMessageContainer::Messages messages = logComp->GetMessages();
for (const auto& msgPtr : messages) {
qDebug() << msgPtr->GetInformationDescription();
}
// Check worst category
logComp->GetWorstCategory();
qDebug() << "Errors were logged!";
}
// Clear all messages
logComp->ClearMessages();
std::list< IMessageConsumer::MessagePtr > Messages
List type for storing message shared pointers.
InformationCategory
Category of information.
@ IC_ERROR
Information about error, processing could not be done correctly.
@ IC_WARNING
Information about warning, processing could be done.
Configuration in .acc file
<Element Id="ApplicationLog" ComponentId="ilog::CLogComp">
<AttributeInfo Id="MaxMessageCount">
<Data IsEnabled="true" Value="5000"/>
</AttributeInfo>
</Element>

Extended Messages with Attachments

#include <iser/IObjectFactory.h>
// Create extended message
4001,
"Data validation failed",
"Validator",
0,
nullptr,
myObjectFactory // Factory for deserialization
)
);
// Attach custom objects
istd::TSharedInterfacePtr<iser::IObject> dataObject(new MyDataObject);
extMsg->InsertAttachedObject(dataObject.PopRootPtr(), "Invalid data sample");
istd::TSharedInterfacePtr<iser::IObject> contextObject(new MyContextObject);
extMsg->InsertAttachedObject(contextObject.PopRootPtr(), "Validation context");
// Access attached objects later
int count = extMsg->GetAttachedObjectsCount();
for (int i = 0; i < count; ++i) {
const iser::IObject* objPtr = extMsg->GetAttachedObject(i);
const QString& desc = extMsg->GetAttachedObjectDescription(i);
qDebug() << "Attachment" << i << ":" << desc;
}
Extended message supporting attached list of serializable objects.
Definition CExtMessage.h:83
Common interface for factorisable model objects.
Definition IObject.h:23

Custom Stream Formatting

// Create console log with custom formatting using simulation layer
console->SetIntAttr("MinCategory", 2); // Warnings and above
console->SetBoolAttr("UseCategory", true); // Show "Warning:", "Error:"
console->SetBoolAttr("UseCode", true); // Show message ID
console->SetBoolAttr("UseTimeStamp", true); // Show timestamp
console->SetStringAttr("TimeFormat", "hh:mm:ss.zzz"); // Custom time format
console->SetBoolAttr("ShowDots", true); // Show dots for filtered messages
console->InitComponent();
// Messages below warning level will show as "." if ShowDots=true
// Output format: "[12:34:56.789] Warning (5001): Low disk space - DiskMonitor"
// Or configure in .acc file:
// <Element Id="ConsoleLog" ComponentId="ilog::CConsoleLogComp">
// <AttributeInfo Id="MinCategory"><Data IsEnabled="true" Value="2"/></AttributeInfo>
// <AttributeInfo Id="UseCategory"><Data IsEnabled="true" Value="true"/></AttributeInfo>
// <AttributeInfo Id="UseTimeStamp"><Data IsEnabled="true" Value="true"/></AttributeInfo>
// <AttributeInfo Id="TimeFormat"><Data IsEnabled="true" Value="hh:mm:ss.zzz"/></AttributeInfo>
// </Element>
bool SetStringAttr(const QByteArray &attributeId, const QString &value)
Set instance of QString attribute.

Message Routing Between Logs

Configuration in .acc file
This example is best configured in .acc files rather than programmatically:
<!-- Source log that receives all messages -->
<Element Id="SourceLog" ComponentId="ilog::CLogComp">
<AttributeInfo Id="MaxMessageCount"><Data IsEnabled="true" Value="5000"/></AttributeInfo>
</Element>
<!-- Console log for all messages -->
<Element Id="ConsoleLog" ComponentId="ilog::CConsoleLogComp">
<AttributeInfo Id="MinCategory"><Data IsEnabled="true" Value="0"/></AttributeInfo>
</Element>
<!-- Error log for errors only -->
<Element Id="ErrorLog" ComponentId="ilog::CLogComp">
<AttributeInfo Id="MaxMessageCount"><Data IsEnabled="true" Value="1000"/></AttributeInfo>
</Element>
<!-- Router that forwards only errors from SourceLog to ErrorLog -->
<Element Id="ErrorRouter" ComponentId="ilog::CLogRouterComp">
<Reference Id="InputMessageContainer" Value="SourceLog"/>
<Reference Id="OutputMessageConsumer" Value="ErrorLog"/>
<AttributeInfo Id="MinimalCategory"><Data IsEnabled="true" Value="3"/></AttributeInfo>
</Element>
<!-- Connect console log as slave of source log -->
<Element Id="SourceLog">
<Reference Id="SlaveMessageConsumer" Value="ConsoleLog"/>
</Element>
Programmatic Usage (with simulation layer)
#include <ilog/CLogComp.h>
// Create source log
sourceLog->InitComponent();
// Create error log
errorLog->InitComponent();
// Create router for errors only
errorRouter->SetRef("InputMessageContainer", sourceLog);
errorRouter->SetRef("OutputMessageConsumer", errorLog);
errorRouter->SetIntAttr("MinimalCategory", 3); // IC_ERROR
errorRouter->InitComponent();
// Messages sent to sourceLog will be automatically forwarded
// to errorLog if they are errors or critical
sourceLog->AddMessage(errorMessage);

Standard Stream Bridging

Configuration in .acc file
<Element Id="ConsoleLog" ComponentId="ilog::CConsoleLogComp">
<AttributeInfo Id="MinCategory"><Data IsEnabled="true" Value="0"/></AttributeInfo>
</Element>
<Element Id="StreamBridge" ComponentId="ilog::CStandardStreamBridgeComp">
<Reference Id="SlaveMessageConsumer" Value="ConsoleLog"/>
</Element>
Programmatic Usage (with simulation layer)
#include <iostream>
// Create log destination
logComp->InitComponent();
// Create bridge component
bridge->SetRef("SlaveMessageConsumer", logComp);
bridge->InitComponent();
// Now std::cout and std::cerr are redirected to the log
std::cout << "This goes to log as INFO" << std::endl;
std::cerr << "This goes to log as ERROR" << std::endl;
// Bridge is destroyed when component is destroyed, restoring streams

Tracing Configuration

Configuration in .acc file
<Element Id="GlobalTracing" ComponentId="ilog::CTracingConfigurationComp">
<AttributeInfo Id="DefaulTracingtLevel"><Data IsEnabled="true" Value="1"/></AttributeInfo>
</Element>
<Element Id="MyComponent" ComponentId="MyComponentClass">
<Reference Id="Log" Value="ConsoleLog"/>
<Reference Id="TracingConfiguration" Value="GlobalTracing"/>
<AttributeInfo Id="EnableVerbose"><Data IsEnabled="true" Value="true"/></AttributeInfo>
</Element>
Component Implementation
class MyComponentClass : public ilog::CLoggerComponentBase
{
public:
I_BEGIN_COMPONENT(MyComponentClass);
I_END_COMPONENT;
protected:
// reimplemented (icomp::CComponentBase)
virtual void OnComponentCreated() override
{
BaseClass::OnComponentCreated();
// Level 0 tracing (always shown when tracing enabled)
SendVerboseMessage("Component initialized", QString(), 0);
// Level 1 tracing (more detailed)
if (IsVerboseEnabled(1)) {
SendVerboseMessage("Detailed initialization info", QString(), 1);
}
// Level 2 tracing (very detailed)
if (IsVerboseEnabled(2)) {
SendVerboseMessage("Internal state details", QString(), 2);
}
}
};
// Configuration with DefaulTracingtLevel = 1 enables tracing levels 0 and 1, but not 2
bool IsVerboseEnabled(int tracingLevel=0) const
Check if verbose messages are enabled for a given tracing level.
void SendVerboseMessage(const QString &message, const QString &messageSource=QString(), int tracingLevel=0) const
Send a verbose message if enabled.

Preventing Duplicate Messages

class MyComponent : public ilog::CLoggerBase
{
public:
void FrequentOperation()
{
// This message is only sent the first time
"Performance degraded, consider optimization",
"MyComponent");
// ... operation that might be called many times ...
}
void Reset()
{
// Allow the message to be sent again
}
};
Base class for objects that need logging functionality.
Definition CLoggerBase.h:91
bool SendWarningMessageOnce(int id, const QString &message, const QString &messageSource=QString(), int flags=0) const
Send once warning message to log.
bool AllowMessageOnceAgain(int id)
Reset message lock.

Custom Message Types

// Define custom data structure
struct DiagnosticData : public iser::ISerializable
{
double cpuUsage;
qint64 memoryUsed;
virtual bool Serialize(iser::IArchive& archive) override {
return archive.Process(cpuUsage, "cpu") &&
archive.Process(memoryUsed, "memory");
}
};
// Create message type combining CMessage and DiagnosticData
typedef ilog::TExtMessage<DiagnosticData> DiagnosticMessage;
// Register for serialization
I_REGISTER_MESSAGE_TYPE(DiagnosticMessage, "DiagnosticMessage");
// Use custom message
new DiagnosticMessage(
8001,
"System diagnostics",
"Monitor"
)
);
diagMsg->cpuUsage = 45.2;
diagMsg->memoryUsed = 1024*1024*512; // 512 MB
#define I_REGISTER_MESSAGE_TYPE(messageType, messageTypeId)
Template for creating extended messages with custom embedded data.
Definition TExtMessage.h:97
Represents an input/output persistence archive for object serialization.
Definition IArchive.h:164
virtual bool Process(bool &value)=0
Processes (reads or writes) a boolean value.
Common class for all classes which objects can be archived or restored from archive.
virtual bool Serialize(IArchive &archive)=0
Load or store state of this object as a archive stream.

Thread Safety

The ilog library uses several mechanisms to ensure thread-safe operation:

  • CLogCompBase: Uses Qt signals/slots for thread-safe message delivery across thread boundaries
  • CStandardStreamBridge: Uses QMutex to protect internal buffer during stream operations
  • Message Objects: Individual message objects (CMessage, CExtMessage) are immutable after creation
  • Shared Pointers: TSharedInterfacePtr ensures thread-safe reference counting
Warning
Message containers (CMessageContainer) are not inherently thread-safe for concurrent modifications. Use appropriate synchronization when accessing from multiple threads, or use CLogCompBase which provides thread-safe message addition via signals.

Serialization

The ilog library fully supports serialization of messages:

  • CMessage: Serializes all message properties (category, ID, text, source, flags, timestamp)
  • CExtMessage: Serializes base message plus attached objects
  • TExtMessage<T>: Serializes both message and custom element
  • CMessageContainer: Can serialize entire message collections
  • Message Type Registration: Use RegisterMessageType() or I_REGISTER_MESSAGE_TYPE macro
// Register custom message type for serialization
bool registered = ilog::CMessageContainer::RegisterMessageType<MyCustomMessage>(
"com.myapp.MyCustomMessage");
// Or use macro for automatic registration
I_REGISTER_MESSAGE_TYPE(MyCustomMessage, "com.myapp.MyCustomMessage");
// Serialization is then automatic via IMessageContainer

Dependencies

The ilog library depends on:

  • istd: Standard utilities (IChangeable, IInformationProvider, ILogger, smart pointers)
  • iser: Serialization framework (ISerializable, IObject, IArchive)
  • icomp: Component framework (CComponentBase, IComponent)
  • imod: Model-observer pattern (IModel, IObserver)
  • Qt Core: QString, QDateTime, QByteArray, QObject, signals/slots
  • Qt Widgets: (Optional, for some implementations)
  • C++ STL: iostream, streambuf for stream bridging

Best Practices

  1. Use Component-Based Logging: Prefer TLoggerCompWrap for components to get integrated logging
  2. Assign Unique Message IDs: Use unique IDs for each message type for filtering and processing
  3. Include Message Source: Always provide source information for easier debugging
  4. Choose Appropriate Severity: Use IC_INFO for informational, IC_WARNING for warnings, IC_ERROR for errors, IC_CRITICAL for critical failures
  5. Use Message Containers: Store messages in CLogComp for later analysis and export
  6. Configure Maximum Messages: Set reasonable limits to prevent memory issues in long-running apps
  7. Leverage Hierarchical Containers: Use child containers to organize messages by subsystem
  8. Use SendOnce Variants: Prevent log spam from repeated messages in loops or frequent operations
  9. Enable Tracing Selectively: Use tracing levels to control verbose output granularity
  10. Test with Stream Bridge: CStandardStreamBridgeComp helps capture legacy code output
  11. Format Consistently: Configure stream loggers with consistent time formats and message structure
  12. Route by Severity: Use CLogRouterComp to separate errors from general logs
  13. Attach Context with CExtMessage: For complex errors, attach relevant objects for debugging
  14. Implement Custom Decorators: Override DecorateMessage() to add application-specific formatting
  15. Handle Asynchronous Delivery: Remember CLogCompBase delivers messages asynchronously via events

See Also

Interfaces

Message Classes

Logging Components

Base Classes

Template Classes