Data Logging
Holoscan provides a flexible data logging system that allows applications to capture and log data flowing through operator workflows. This system is designed to help with debugging, monitoring, and analyzing the behavior of Holoscan applications.
Overview
The data logging system consists of several key components:
- DataLogger (
holoscan::DataLogger) - The core interface that defines methods for logging different data types - DataLoggerResource (
holoscan::DataLoggerResource) - A resource-based implementation with common configuration parameters - BasicConsoleLogger (
holoscan::data_loggers::BasicConsoleLogger) - A concrete implementation that logs data to the console - GXFConsoleLogger (
holoscan::data_loggers::GXFConsoleLogger) - A version ofBasicConsoleLoggerwhich also logs Tensors and/ornvidia::gxf::VideoBufferobjects within messages that are emitted or received as aholoscan::gxf::Entityornvidia::gxf::Entity. It is recommended to use this version overBasicConsoleLoggeras it will also log the tensors emitted or received by a number of the built-in operators of the SDK which directly use GXF::Entity type to support both Tensors as well as GXF VideoBuffers.VideoBuffercomponents are not currently logged by this implementation, but this could be added by overriding thelog_backend_specificmethod. - AsyncConsoleLogger (
holoscan::data_loggers::AsyncConsoleLogger) - A version ofGXFConsoleLoggerwhich performs logging asynchronously by a queue that is processed by a worker thread owned by the logger. This version has the option of using separate logging queues for “large” data (e.g. Tensor/TensorMap) vs. other types so that data contents can be dropped if the queue size reaches a user-specfied limit. - SimpleTextSerializer (
holoscan::data_loggers::SimpleTextSerializer) - A serializer for converting data to human-readable text
When Logging Occurs
Data logging occurs automatically during the execution of operator workflows:
- Input logging: When operators call
receive()methods on their input ports - Output logging: When operators call
emit()methods on their output ports
The timing of logging corresponds exactly to these receive and emit calls within each operator’s compute() method.
Data Types Supported
The data logging system provides specialized methods for different data types:
log_data()- For general data types (passed asstd::any)log_tensor_data()- ForTensorobjects with optional data content logginglog_tensormap_data()- ForTensorMapobjects with optional data content logginglog_backend_specific()- An optional method that can be used to log backend-specific data types. Currently GXF is the only supported backend for Holscan SDK and this method is called byGXFInputContext::receive<T>()orGXFOutputContext::emit(data)when the data type,T, isholoscan::gxf::Entityornvidia::gxf::Entity.
DataLogger Interface
The DataLogger interface defines the contract that all data loggers must implement:
C++
Python
DataLoggerResource Base Class
The DataLoggerResource class provides a convenient base implementation with common configuration parameters:
Configuration Parameters
log_inputs(bool, default:true) - Whether to log data received on input portslog_outputs(bool, default:true) - Whether to log data emitted on output portslog_metadata(bool, default:true) - Whether to include metadata in logslog_tensor_data_content(bool, default:false) - Whether to log actual tensor data arrays (if false, only header info is logged)allowlist_patterns(vector<string>, default: empty) - Regex patterns for message IDs to always logdenylist_patterns(vector<string>, default: empty) - Regex patterns for message IDs to never log
Also note that Holoscan resources can have other Resource classes as a parameter as demonstrated by having a separate SimpleTextSerializer resource in the concrete BasicConsoleLogger class that inherits from DataLoggerResource. Note that when implementing a class that inherits from this one, it is mandatory to call the DataLoggerResource::setup method within the derived class’s setup method as in this example from BasicConsoleLogger. If the initialize method is overridden the parent class’s initialize method should also be called. This is an example from BasicConsoleLogger:
If allowlist_patterns is specified, only messages matching those patterns will be logged. If no allowlist is specified, all messages will be logged except those matching denylist_patterns.
Currently this simple DataLoggerResource performs logging synchronously on the same thread that is executing the Operator::compute call.
In cases where logging overhead may be non-negligible (e.g. logging tensor contents to disk), the AsyncDataLoggerResource which maintains its own queue and corresponding worker thread for data logging is likely to be advantageous. For the AsyncDataLoggerResource, the thread running Operator::compute is only responsible for pushing the item to be logged onto the queue. The actual logging is handled by the logger’s own worker thread(s).
BasicConsoleLogger Example
The BasicConsoleLogger and GXFConsoleLogger are concrete implementations that output logs to the console. For existing Holoscan apps which always use the GXF-based backend, GXFConsoleLogger should be preferred as it also implements logging of Tensor objects present within data emitted or received as a holoscan::gxf::Entity or nvidia::gxf::Entity.
C++
Python
The example above shows example code adding the logger within the compose method, but it can
also be added from the main application file via as done in the following example applications:
As with any other resource or operator in the SDK, parameters can be passed in directly via arguments or indirectly via reading from the YAML config.
YAML Configuration
Data loggers can be configured using YAML configuration files, making it easy to adjust logging behavior without recompiling:
YAML Configuration Example
Loading Configuration from YAML
C++
Python
Custom Data Logger Implementation
You can create custom data loggers by implementing the DataLogger interface. To be able to use
Holoscan Parameters and configure them via YAML it may be useful to inherit from the provided
DataLoggerResource (as done for GXFConsoleLogger). Note that it is not required to inherit
from DataLoggerResource, though, only the DataLogger interface.
C++
Python
Filtering and Pattern Matching
Data loggers inheriting from DataLoggerResource will automatically support filtering of messages using regex patterns:
For a distributed application, the fragments will be named and the unique_id format used for each port will be:
{fragment_name}.{operator_name}.{port_name}{fragment_name}.{operator_name}.{port_name}:index(for N:1 receiver ports (IOSpec::kAnySize))
For non-distributed applications, the single fragment is typically not named and the following simpler unique_id format is used:
{operator_name}.{port_name}{operator_name}.{port_name}:index(for N:1 receiver ports (IOSpec::kAnySize))
The allowlist_patterns and denylist_patterns provide a way to include or exclude messages based on operator and/or port names. If there are no allowlist patterns, any messages not matching one of the denylist patterns are logged. If both types of patterns are specified, any messages that match at least one of the allowlist patterns but are not excluded by any of the denylist patterns are logged.
Use the multithread example as a reference for a complete working implementation with data logging enabled.
Data logging can impact performance, especially when log_tensor_data_content is enabled for large tensors. Use filtering patterns to log only the data you need for debugging or monitoring.