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
1001,
"Application started successfully",
"MyApplication"
)
);
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.
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
{
public:
I_BEGIN_COMPONENT(MyComponent);
I_END_COMPONENT;
protected:
{
BaseClass::OnComponentCreated();
}
{
BaseClass::OnComponentDestroyed();
}
};
component->
SetRef(
"Log", log);
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
3001, "Low memory warning", "System")));
3002, "File not found", "FileSystem")));
for (const auto& msgPtr : messages) {
qDebug() << msgPtr->GetInformationDescription();
}
logComp->GetWorstCategory();
qDebug() << "Errors were logged!";
}
logComp->ClearMessages();
std::list< IMessageConsumer::MessagePtr > Messages
List type for storing message shared pointers.
- 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>
4001,
"Data validation failed",
"Validator",
0,
nullptr,
myObjectFactory
)
);
extMsg->InsertAttachedObject(dataObject.PopRootPtr(), "Invalid data sample");
extMsg->InsertAttachedObject(contextObject.PopRootPtr(), "Validation context");
int count = extMsg->GetAttachedObjectsCount();
for (int i = 0; i < count; ++i) {
const QString& desc = extMsg->GetAttachedObjectDescription(i);
qDebug() << "Attachment" << i << ":" << desc;
}
Extended message supporting attached list of serializable objects.
Common interface for factorisable model objects.
Custom Stream Formatting
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:
<Element Id="SourceLog" ComponentId="ilog::CLogComp">
<AttributeInfo Id="MaxMessageCount"><Data IsEnabled="true" Value="5000"/></AttributeInfo>
</Element>
<Element Id="ConsoleLog" ComponentId="ilog::CConsoleLogComp">
<AttributeInfo Id="MinCategory"><Data IsEnabled="true" Value="0"/></AttributeInfo>
</Element>
<Element Id="ErrorLog" ComponentId="ilog::CLogComp">
<AttributeInfo Id="MaxMessageCount"><Data IsEnabled="true" Value="1000"/></AttributeInfo>
</Element>
<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>
<Element Id="SourceLog">
<Reference Id="SlaveMessageConsumer" Value="ConsoleLog"/>
</Element>
- Programmatic Usage (with simulation layer)
errorRouter->
SetRef(
"InputMessageContainer", sourceLog);
errorRouter->
SetRef(
"OutputMessageConsumer", errorLog);
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>
bridge->
SetRef(
"SlaveMessageConsumer", logComp);
std::cout << "This goes to log as INFO" << std::endl;
std::cerr << "This goes to log as ERROR" << std::endl;
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
{
public:
I_BEGIN_COMPONENT(MyComponentClass);
I_END_COMPONENT;
protected:
{
BaseClass::OnComponentCreated();
}
}
}
};
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
{
public:
void FrequentOperation()
{
"Performance degraded, consider optimization",
"MyComponent");
}
void Reset()
{
}
};
Base class for objects that need logging functionality.
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
{
double cpuUsage;
qint64 memoryUsed;
return archive.
Process(cpuUsage,
"cpu") &&
archive.
Process(memoryUsed,
"memory");
}
};
new DiagnosticMessage(
8001,
"System diagnostics",
"Monitor"
)
);
diagMsg->cpuUsage = 45.2;
diagMsg->memoryUsed = 1024*1024*512;
#define I_REGISTER_MESSAGE_TYPE(messageType, messageTypeId)
Template for creating extended messages with custom embedded data.
Represents an input/output persistence archive for object serialization.
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
bool registered = ilog::CMessageContainer::RegisterMessageType<MyCustomMessage>(
"com.myapp.MyCustomMessage");
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
- Use Component-Based Logging: Prefer TLoggerCompWrap for components to get integrated logging
- Assign Unique Message IDs: Use unique IDs for each message type for filtering and processing
- Include Message Source: Always provide source information for easier debugging
- Choose Appropriate Severity: Use IC_INFO for informational, IC_WARNING for warnings, IC_ERROR for errors, IC_CRITICAL for critical failures
- Use Message Containers: Store messages in CLogComp for later analysis and export
- Configure Maximum Messages: Set reasonable limits to prevent memory issues in long-running apps
- Leverage Hierarchical Containers: Use child containers to organize messages by subsystem
- Use SendOnce Variants: Prevent log spam from repeated messages in loops or frequent operations
- Enable Tracing Selectively: Use tracing levels to control verbose output granularity
- Test with Stream Bridge: CStandardStreamBridgeComp helps capture legacy code output
- Format Consistently: Configure stream loggers with consistent time formats and message structure
- Route by Severity: Use CLogRouterComp to separate errors from general logs
- Attach Context with CExtMessage: For complex errors, attach relevant objects for debugging
- Implement Custom Decorators: Override DecorateMessage() to add application-specific formatting
- Handle Asynchronous Delivery: Remember CLogCompBase delivers messages asynchronously via events
See Also
Interfaces
Message Classes
Logging Components
Base Classes
Template Classes