Logging#
MatX provides a flexible, zero-overhead logging system for debugging and diagnostics. The logging system uses C++20’s std::format and std::source_location to provide rich, formatted log messages with automatic file and line information.
Features#
Zero overhead when disabled: Logging is disabled by default with minimal runtime cost
Multiple severity levels: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
ISO 8601 timestamps: All log messages include precise timestamps
Automatic source location: File name, line number, and optional function name are automatically captured
Flexible output: Logs can be directed to stdout, stderr, or a file
Thread-safe: Logging operations are protected by mutex
Type-safe formatting: Uses C++20
std::formatfor compile-time format string checking
Basic Usage#
Include MatX and use the logging macros:
#include <matx.h>
using namespace matx;
void my_function() {
int value = 42;
MATX_LOG_INFO("Processing value: {}", value);
double result = 3.14159;
MATX_LOG_DEBUG("Result computed: {:.2f}", result);
MATX_LOG_WARN("Warning: operation may be slow");
}
Logging Macros#
MatX provides convenience macros for each severity level:
MATX_LOG_TRACE("Detailed trace information: {}", data);
MATX_LOG_DEBUG("Debug information: x={}, y={}", x, y);
MATX_LOG_INFO("Informational message");
MATX_LOG_WARN("Warning message: threshold={}", threshold);
MATX_LOG_ERROR("Error occurred: {}", error_code);
MATX_LOG_FATAL("Fatal error: cannot continue");
You can also use the base macro with explicit severity:
MATX_LOG(matx::detail::LogLevel::DEBUG, "Custom message: {}", value);
Configuration#
Logging is controlled via environment variables, which must be set before the program starts.
MATX_LOG_LEVEL#
Controls which messages are displayed based on severity:
# Show all messages (most verbose)
export MATX_LOG_LEVEL=TRACE
# Show DEBUG and above
export MATX_LOG_LEVEL=DEBUG
# Show INFO and above (recommended for production)
export MATX_LOG_LEVEL=INFO
# Show only warnings and errors
export MATX_LOG_LEVEL=WARN
# Show only errors
export MATX_LOG_LEVEL=ERROR
# Disable all logging (default)
export MATX_LOG_LEVEL=OFF
Numeric levels are also supported: 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=FATAL, 6=OFF.
Severity Levels#
Level |
Value |
Use Case |
|---|---|---|
TRACE |
0 |
Very detailed information for diagnosing issues |
DEBUG |
1 |
Detailed information for debugging |
INFO |
2 |
Informational messages about normal operation |
WARN |
3 |
Warning messages for potentially problematic issues |
ERROR |
4 |
Error messages for recoverable errors |
FATAL |
5 |
Critical errors that may cause termination |
OFF |
6 |
Disable all logging |
MATX_LOG_DEST#
Controls where log messages are written:
# Write to stdout (default)
export MATX_LOG_DEST=stdout
# Write to stderr
export MATX_LOG_DEST=stderr
# Write to a file (will be created or appended)
export MATX_LOG_DEST=/path/to/logfile.log
If writing to a file fails, logging automatically falls back to stdout with a warning.
MATX_LOG_FUNC#
Controls whether function names are included in log output. Some function names are very large due to heavy templating:
# Show function names in log output
export MATX_LOG_FUNC=1
# or
export MATX_LOG_FUNC=true
# or
export MATX_LOG_FUNC=ON
# Hide function names (default)
export MATX_LOG_FUNC=0
# or
unset MATX_LOG_FUNC
This is useful for reducing log verbosity while still maintaining file and line information.
Log Format#
Log messages follow a structured format that varies based on configuration:
Without Function Names (Default)#
YYYY-MM-DDTHH:MM:SS.mmm [LEVEL] filename.ext:line - message
Example:
2025-10-21T14:32:45.123 [DEBUG] fft.h:277 - DYN_SHM_SIZE: 8192
2025-10-21T14:32:45.124 [INFO] my_app.cu:42 - Processing complete
With Function Names (MATX_LOG_FUNC=1)#
YYYY-MM-DDTHH:MM:SS.mmm [LEVEL] filename.ext:line (function_name) - message
Example:
2025-10-21T14:32:45.123 [DEBUG] fft.h:277 (get_capability) - DYN_SHM_SIZE: 8192
2025-10-21T14:32:45.124 [INFO] my_app.cu:42 (main) - Processing complete
Format String Syntax#
MatX logging uses C++20 std::format syntax, which is similar to Python’s format strings and Rust’s formatting:
Basic Formatting#
// Basic substitution
MATX_LOG_INFO("Value: {}", 42);
// Output: Value: 42
// Multiple arguments
int x = 10, y = 20;
MATX_LOG_INFO("Coordinates: x={}, y={}", x, y);
// Output: Coordinates: x=10, y=20
// Positional arguments
MATX_LOG_DEBUG("{0} + {1} = {2}", 5, 3, 8);
// Output: 5 + 3 = 8
Number Formatting#
// Floating-point precision
MATX_LOG_DEBUG("Pi: {:.2f}", 3.14159);
// Output: Pi: 3.14
MATX_LOG_DEBUG("Scientific: {:.2e}", 12345.6);
// Output: Scientific: 1.23e+04
// Hexadecimal
MATX_LOG_DEBUG("Address: 0x{:08x}", 0xDEADBEEF);
// Output: Address: 0xdeadbeef
// Binary
MATX_LOG_DEBUG("Flags: {:08b}", 42);
// Output: Flags: 00101010
// With thousands separator
MATX_LOG_INFO("Large number: {:L}", 1234567);
// Output: Large number: 1,234,567
Alignment and Padding#
// Left-aligned (default for strings)
MATX_LOG_INFO("Left: {:<10}", "text");
// Output: Left: text
// Right-aligned (default for numbers)
MATX_LOG_INFO("Right: {:>10}", "text");
// Output: Right: text
// Center-aligned
MATX_LOG_INFO("Center: {:^10}", "text");
// Output: Center: text
// Zero-padding for numbers
MATX_LOG_DEBUG("Index: {:04d}", 42);
// Output: Index: 0042
Arrays and Containers#
For arrays, you’ll need to format elements individually:
// Logging array elements
cuda::std::array<int, 3> dims = {128, 256, 512};
MATX_LOG_DEBUG("Dimensions: [{}, {}, {}]", dims[0], dims[1], dims[2]);
// Output: Dimensions: [128, 256, 512]
Practical Examples#
FFT Operations#
Enable debug logging for FFT operations:
export MATX_LOG_LEVEL=DEBUG
./my_fft_application
You’ll see detailed capability information:
2025-10-21T14:32:45.123 [DEBUG] fft.h:277 - DYN_SHM_SIZE: 8192
2025-10-21T14:32:45.124 [DEBUG] fft.h:293 - SUPPORTS_JIT: true
2025-10-21T14:32:45.125 [DEBUG] fft.h:318 - ELEMENTS_PER_THREAD (JIT supported): [8,16]
2025-10-21T14:32:45.126 [DEBUG] fft.h:339 - GROUPS_PER_BLOCK: [4,4]
CUDA Executor#
Debug kernel launch parameters:
export MATX_LOG_LEVEL=DEBUG
export MATX_LOG_FUNC=1
./my_cuda_app
Output includes function context:
2025-10-21T14:32:45.200 [DEBUG] get_grid_dims.h:283 (GetGridDims) - Blocks 32x1x1 Threads 256x1x1 groups_per_block=1
2025-10-21T14:32:45.201 [DEBUG] nvrtc_helper.h:551 (LaunchKernel) - Launching kernel with grid=(32, 1, 1), block=(256, 1, 1), dynamic_shmem_size=0 bytes
Cache Operations#
Debug cache hits and misses:
export MATX_LOG_LEVEL=DEBUG
./my_app
2025-10-21T14:32:45.100 [DEBUG] cache.h:329 - Cache HIT (memory) for: kernel_xyz.cubin
2025-10-21T14:32:45.101 [DEBUG] cache.h:343 - Cache MISS (disk) for: kernel_abc.cubin
2025-10-21T14:32:45.102 [DEBUG] cache.h:357 - Cache HIT (disk) for: kernel_def.cubin, size: 12345 bytes
Performance Considerations#
Overhead When Disabled#
When logging is disabled (default state), the overhead is minimal:
// This has negligible overhead when logging is OFF
for (int i = 0; i < 1000000; i++) {
MATX_LOG_TRACE("Iteration {}", i); // Only a single boolean check
}
The compiler optimizes away the format string and argument evaluation when the log level is disabled.
Overhead When Enabled#
When logging is enabled:
String formatting: Performed using
std::formatI/O operations: Writing to file/stream has normal I/O costs
Thread synchronization: Mutex lock/unlock for thread safety
Recommendations:
Use appropriate log levels for your use case
Avoid TRACE logging in production unless debugging
Use DEBUG for development and troubleshooting
Keep INFO logging for important operational events
Consider log file rotation for long-running applications
Best Practices#
Choose Appropriate Levels#
// TRACE: Very detailed, typically temporary debug code
MATX_LOG_TRACE("Entering function with params: a={}, b={}", a, b);
// DEBUG: Development debugging information
MATX_LOG_DEBUG("Cache size: {} entries, {} MB", count, size_mb);
// INFO: Important operational events
MATX_LOG_INFO("System initialized with {} GPUs", gpu_count);
// WARN: Unexpected but handled situations
MATX_LOG_WARN("Cache full, evicting {} old entries", evict_count);
// ERROR: Errors that don't stop execution
MATX_LOG_ERROR("Failed to allocate {} bytes, retrying", requested);
// FATAL: Critical errors
MATX_LOG_FATAL("CUDA device not found, cannot continue");
Provide Context#
Good logging includes relevant context:
// Bad: Not enough context
MATX_LOG_ERROR("Operation failed");
// Good: Includes context
MATX_LOG_ERROR("FFT operation failed: size={}, type={}, error_code={}",
fft_size, type_str, error);
// Good: Shows state
MATX_LOG_DEBUG("Memory usage: allocated={} MB, free={} MB, total={} MB",
allocated, free, total);
Be Concise#
Keep messages clear and to the point:
// Too verbose
MATX_LOG_DEBUG("Now about to execute the FFT transform operation on the input tensor with {} elements", n);
// Better
MATX_LOG_DEBUG("Executing FFT: {} elements", n);
Avoid Sensitive Data#
Don’t log sensitive information:
// Bad: Logging sensitive data
MATX_LOG_INFO("User password: {}", password); // Never do this!
// Good: Log safe information
MATX_LOG_INFO("User authenticated: id={}", user_id);
Conditional Verbose Logging#
Use TRACE for very detailed logging that you enable only when needed:
void process_large_dataset() {
for (size_t i = 0; i < data.size(); i++) {
// This won't impact performance unless TRACE is enabled
MATX_LOG_TRACE("Processing element {}: value={}", i, data[i]);
// Do actual work
process(data[i]);
}
// Always log completion
MATX_LOG_INFO("Processed {} elements", data.size());
}
Limitations#
Current Constraints#
Host-only: Logging is only available in host code, not inside CUDA kernels
C++20 required: Requires a C++20-compatible compiler with
std::formatsupportSynchronous: Log writes are synchronous and serialized across threads
Static configuration: Environment variables are read once at program startup
Device Code#
For logging from device code, use CUDA’s printf:
__global__ void my_kernel() {
if (threadIdx.x == 0 && blockIdx.x == 0) {
printf("Kernel executing: threads=%d, blocks=%d\n",
blockDim.x, gridDim.x);
}
}
Runtime Configuration#
The logger reads environment variables once during initialization. To change logging configuration:
// Set environment variable before MatX initialization
setenv("MATX_LOG_LEVEL", "DEBUG", 1);
// Or use the reinitialize method (mainly for testing)
matx::detail::Logger::instance().reinitialize();
Complete Example#
Here’s a complete example demonstrating logging in a MatX application:
#include <matx.h>
using namespace matx;
int main() {
MATX_LOG_INFO("MatX application starting");
// Create tensors
auto t1 = make_tensor<float>({1024, 1024});
auto t2 = make_tensor<float>({1024, 1024});
MATX_LOG_DEBUG("Tensors created: shape=[{}, {}]", t1.Size(0), t1.Size(1));
// Initialize
(t1 = ones(t1.Shape())).run();
(t2 = ones(t2.Shape())).run();
MATX_LOG_DEBUG("Tensors initialized");
// Perform FFT
MATX_LOG_INFO("Starting FFT operation");
auto t1_fft = fft(t1);
(t2 = t1_fft).run();
MATX_LOG_INFO("FFT operation complete");
// Check results
float result;
(result = sum(abs(t2))).run();
MATX_LOG_INFO("FFT result magnitude: {:.2e}", result);
if (result < 1e-6) {
MATX_LOG_WARN("Result magnitude is very small: {}", result);
}
MATX_LOG_INFO("Application complete");
return 0;
}
Running with different log levels:
# Minimal output
export MATX_LOG_LEVEL=INFO
./my_app
# Output:
# 2025-10-21T14:32:45.100 [INFO] my_app.cu:7 - MatX application starting
# 2025-10-21T14:32:45.120 [INFO] my_app.cu:17 - Starting FFT operation
# 2025-10-21T14:32:45.145 [INFO] my_app.cu:19 - FFT operation complete
# 2025-10-21T14:32:45.150 [INFO] my_app.cu:23 - FFT result magnitude: 1.05e+06
# 2025-10-21T14:32:45.151 [INFO] my_app.cu:29 - Application complete
# Detailed output with function names
export MATX_LOG_LEVEL=DEBUG
export MATX_LOG_FUNC=1
./my_app
# Includes all DEBUG messages plus function names