Developing with Areg Logging System
July 4, 2026 · View on GitHub
This guide provides instructions for activating, configuring, and utilizing Areg's comprehensive logging tools to monitor and debug multiprocess applications.
Tip
Start with the Quick Start section to see logging in action, then explore advanced features for production deployment.
Table of Contents
- Quick Start
- Overview
- Compile-Time Configuration
- Runtime Activation
- Using Logging Scopes
- Log Macros Reference
- Common Use Cases
- Troubleshooting
Quick Start
Add Logging in 2 Minutes
Step 1: Enable logging in your application
#include "areg/appbase/Application.hpp"
int main()
{
// Initialize with logging enabled (1st parameter = true)
Application::setup(
true, // Enable logging ← Important
true, // Enable service manager
false, // Disable router (for this example)
true, // Enable timer
false // Disable watchdog
);
// Your application code
Application::load_model("MyModel");
Application::wait_quit(areg::WAIT_INFINITE);
Application::release();
return 0;
}
Step 2: Add logging to your code
#include "areg/logging/areg_log.h"
// Define log scope for this function
DEF_LOG_SCOPE(myapp_main, initialize);
void initialize()
{
// Activate scope (tracks entry/exit)
LOG_SCOPE(myapp_main, initialize);
// Log messages at different levels
LOG_DBG("Starting initialization");
LOG_INFO("Configuration loaded");
LOG_WARN("Using default settings");
// ... your code ...
}
Step 3: Run and see logs
Expected output in log file:
2026-01-27 10:30:45.123: [ 12345 myapp.exe: Enter -->] Entering scope: myapp_main.initialize
2026-01-27 10:30:45.124: [ 12345 DEBUG >>> ] Starting initialization
2026-01-27 10:30:45.125: [ 12345 INFO >>> ] Configuration loaded
2026-01-27 10:30:45.126: [ 12345 WARN >>> ] Using default settings
2026-01-27 10:30:45.127: [ 12345 myapp.exe: Exit <-- ] Exiting scope: myapp_main.initialize
Setup time: ~2 minutes
Overview
What is Areg Logging System?
The Areg Framework features a robust, adaptable logging system designed for monitoring complex, multithreaded applications across distributed environments.
Key capabilities:
- ✅ Enable/disable logs at compile-time
- ✅ Control logs at runtime (no recompilation)
- ✅ Scope-based logging (track function entry/exit)
- ✅ Priority-based filtering (DEBUG, INFO, WARN, ERROR, FATAL)
- ✅ Multiple destinations (file, remote, debug output, database)
- ✅ Thread-safe logging
- ✅ Minimal performance overhead
Logging Architecture
┌─────────────────┐
│ Application │
│ ┌───────────┐ │
│ │ LOG_DBG() │──┼──► Log Message
│ └───────────┘ │
└─────────────────┘
│
▼
┌─────────────────┐
│ Logging Module │──► Scope Management
│ │──► Priority Filtering
│ │──► Message Formatting
└─────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Log Destinations │
├─────────┬──────────┬────────┬───────┤
│ File │ Remote │ Debug │ DB │
└─────────┴──────────┴────────┴───────┘
When to Use Logging
| Scenario | Use Logging | Benefit |
|---|---|---|
| Development | ✅ Always | Debug issues, understand flow |
| Testing | ✅ Always | Verify behavior, catch edge cases |
| Production | ✅ Selective | Monitor errors, track issues, reduce SCOPE, DEBUG and INFO |
| Performance Critical | ⚙️ Minimal | Error/Fatal only, disable DEBUG and SCOPE |
Compile-Time Configuration
Enable or Disable Logging
Logging can be enabled or disabled during compilation for optimal performance.
Default: Logging is enabled
Using CMake
Enable logging (default):
cmake -B ./build -DAREG_LOGGING=ON
cmake --build ./build
Disable logging:
cmake -B ./build -DAREG_LOGGING=OFF
cmake --build ./build
Expected output when enabled:
-- Areg: >>> Logs: ENABLED
Expected output when disabled:
-- Areg: >>> Logs: DISABLED
Using MSBuild (Visual Studio)
Enable logging (default):
MSBuild /m /property:AregLogs=1 .\areg-sdk.sln
Disable logging:
MSBuild /m /property:AregLogs=0 .\areg-sdk.sln
Using Visual Studio Property
Edit msvc_setup.props:
<!-- Enable logging -->
<AregLogs>1</AregLogs>
<!-- Disable logging -->
<AregLogs>0</AregLogs>
Location: areg-sdk/msvc_setup.props
Impact of Disabling Logs
When compiled with AREG_LOGGING=OFF:
- ❌ All logging macros are compiled out (no-op)
- ❌ No runtime overhead
- ❌ Cannot enable logging at runtime
- ✅ Smaller binary size
- ✅ Maximum performance
When compiled with AREG_LOGGING=ON:
- ✅ Logging available at runtime
- ✅ Can enable/disable dynamically
- ✅ Minimal overhead when disabled
- ⚙️ Slightly larger binary
Note
For production builds where logging might be needed for debugging, compile with AREG_LOGGING=ON but configure minimal logging in areg.init.
Runtime Activation
Once compiled with logging enabled, control logging at runtime using the Application class or macros.
Method 1: Using Application Class (Recommended)
Initialize with logging:
#include "areg/appbase/Application.hpp"
int main()
{
// Initialize application with logging enabled
Application::setup(
true, // startTracing - Enable logging
true, // startServicing - Enable service manager
false, // startRouting - Enable router client
true, // startTimer - Enable timer manager
false, // startWatchdog - Enable watchdog
"./config/areg.init", // Config file (optional)
nullptr // Log listener (optional)
);
// Application code
Application::load_model("MyModel");
Application::wait_quit(areg::WAIT_INFINITE);
// Cleanup (stops logging automatically)
Application::release();
return 0;
}
Parameters:
- startTracing (1st):
true= enable logging,false= disable - configFile (6th): Path to
areg.initconfiguration file - listener (7th): Optional custom listener of configuration update
Method 2: Explicit Logging Control
Start logging explicitly:
#include "areg/appbase/Application.hpp"
int main()
{
// Initialize without logging
Application::setup(false, true, false, true, false);
// Start logging later with specific config
Application::start_logging("./config/areg.init", true);
// Application code
Application::load_model("MyModel");
Application::wait_quit(areg::WAIT_INFINITE);
// Stop logging before cleanup
Application::stop_logging();
Application::release();
return 0;
}
Methods:
Application::start_logging(configFile, force)- Start loggingconfigFile- Path to configuration fileforce- Force restart if already running
Application::stop_logging()- Stop logging
Method 3: Using Macros
For macro-based control:
#include "areg/logging/areg_log.h"
int main()
{
// Start logging with configuration, force to start if no config
LOGGING_CONFIGURE_AND_START("./config/areg.init", true);
// Or force start without config file
// LOGGING_FORCE_START();
// Application code with logging
LOG_INFO("Application started");
// ... your code ...
LOG_INFO("Application stopping");
// Stop logging
LOGGING_STOP();
return 0;
}
Available macros:
LOGGING_CONFIGURE_AND_START(configFile, true)- Start with config, force to start if no configLOGGING_FORCE_START()- Start with default configLOGGING_STOP()- Stop logging
Important
If compiled with AREG_LOGGING=OFF, these macros are no-ops and do nothing.
Using Logging Scopes
What are Logging Scopes?
Scopes are named logging contexts that:
- Track function/method entry and exit
- Group related log messages
- Enable selective logging control
- Measure execution time
Define and Use Scopes
Step 1: Define scope (top of .cpp file or before each method)
The scope consists of 2 parts: the path of nodes and the leaf. The nodes are separated by underscore _ symbol, and the leaf is separated from the path by a comma (,).
#include "areg/logging/areg_log.h"
// Define scope - use unique descriptive name
DEF_LOG_SCOPE(myapp_MyClass, process_data);
DEF_LOG_SCOPE(myapp_MyClass, validate);
DEF_LOG_SCOPE(myapp_MyClass, save);
Naming convention:
<module_path_>_<class>.<function>
Examples:
network_tcp_HttpClient.send_requestdatabase_sqlite_Connection.execute_queryui_view_MainWindow.handle_click
Step 2: Activate scope in function
void MyClass::processData(const Data& data)
{
// Activate scope (logs entry/exit automatically)
LOG_SCOPE(myapp_MyClass, process_data);
// Log messages within scope
LOG_DBG("Processing data with ID: %d", data.id);
if (!validate(data))
{
LOG_ERR("Data validation failed");
return;
}
LOG_INFO("Data processed successfully");
}
Expected output:
2026-01-27 10:30:45.123: [ 12345 myapp.exe: Enter -->] Entering scope: myapp_MyClass_processData
2026-01-27 10:30:45.124: [ 12345 DEBUG >>> ] Processing data with ID: 42
2026-01-27 10:30:45.125: [ 12345 INFO >>> ] Data processed successfully
2026-01-27 10:30:45.126: [ 12345 myapp.exe: Exit <-- ] Exiting scope: myapp_MyClass_processData
Nested Scopes
Scopes can be nested:
DEF_LOG_SCOPE(myapp_MyClass, process_data);
DEF_LOG_SCOPE(myapp_MyClass, validate);
void MyClass::validate(const Data& data)
{
LOG_SCOPE(myapp_MyClass, validate);
LOG_DBG("Validating data");
// Validation logic
LOG_DBG("Validation complete");
}
void MyClass::process_data(const Data& data)
{
LOG_SCOPE(myapp_MyClass, process_data);
LOG_INFO("Processing data");
validate(data); // Nested scope
LOG_INFO("Processing complete");
}
Expected output:
2026-01-27 10:30:45.123: [ 12345 Enter -->] myapp_MyClass.process_data
2026-01-27 10:30:45.124: [ 12345 INFO >>> ] Processing data
2026-01-27 10:30:45.125: [ 12345 Enter -->] myapp_MyClass.validate
2026-01-27 10:30:45.126: [ 12345 DEBUG >>> ] Validating data
2026-01-27 10:30:45.127: [ 12345 DEBUG >>> ] Validation complete
2026-01-27 10:30:45.128: [ 12345 Exit <-- ] myapp_MyClass.validate
2026-01-27 10:30:45.129: [ 12345 INFO >>> ] Processing complete
2026-01-27 10:30:45.130: [ 12345 Exit <-- ] myapp_MyClass.process_data
Scope Control in Configuration
Enable/disable scopes in areg.init:
# Enable all scopes
log::*::scope::* = DEBUG | SCOPE
# Enable specific scope
log::myapp::scope::myapp_MyClass.process_data = DEBUG | SCOPE
# Disable specific scope
log::myapp::scope::myapp_MyClass.validate = NOTSET
# Enable scope with warnings only
log::myapp::scope::myapp_MyClass.save = WARN | SCOPE
See: Logging Configuration Guide for details
Log Macros Reference
Priority Levels
| Macro | Priority | Use Case | Example |
|---|---|---|---|
LOG_DBG() | DEBUG | Detailed debugging | LOG_DBG("Counter value: %d", count) |
LOG_INFO() | INFO | General information | LOG_INFO("Connection established") |
LOG_WARN() | WARN | Warnings | LOG_WARN("Retry attempt %d", retry) |
LOG_ERR() | ERROR | Errors | LOG_ERR("Failed to open file: %s", path) |
LOG_FATAL() | FATAL | Fatal errors | LOG_FATAL("Out of memory") |
Logging Macros
Basic logging:
LOG_DBG("Debug message");
LOG_INFO("Info message");
LOG_WARN("Warning message");
LOG_ERR("Error message");
LOG_FATAL("Fatal error message");
Formatted logging:
LOG_DBG("Processing item %d of %d", current, total);
LOG_INFO("User '%s' logged in from %s", username, ipAddress);
LOG_WARN("Timeout after %d seconds", timeout);
LOG_ERR("Error code: %d, message: %s", errorCode, errorMsg);
Scope Macros
// Define scope (outside function, typically at top of file)
DEF_LOG_SCOPE(node_path, leaf_name);
// Activate scope (inside function)
LOG_SCOPE(node_path, leaf_name);
Conditional Logging
Log only if condition is true (conditional logging):
LOG_DBG_IF(condition, "Message only logged if condition is true");
LOG_INFO_IF(count > 0, "Processed %d items", count);
LOG_WARN_IF(retries > MAX_RETRIES, "Retry limit exceeded");
LOG_ERR_IF(result != SUCCESS, "Operation failed with code %d", result);
Complete Example
#include "areg/logging/areg_log.h"
// Define scopes
DEF_LOG_SCOPE(network_HttpClient, connect);
DEF_LOG_SCOPE(network_HttpClient, send_request);
class HttpClient
{
public:
bool connect(const String& url)
{
LOG_SCOPE(network_HttpClient, connect);
LOG_INFO("Connecting to: %s", url.as_string());
bool success = perform_connect(url);
LOG_DBG_IF(success, "Connection established");
LOG_ERR_IF(!success, "Connection failed");
return success;
}
void send_request(const String& request)
{
LOG_SCOPE(network_HttpClient, send_request);
LOG_DBG("Request: %s", request.as_string());
if (request.is_empty())
{
LOG_WARN("Empty request");
return;
}
// Send logic
LOG_INFO("Request sent successfully");
}
};
Output:
2026-01-27 10:30:45.123: [ 12345 Enter -->] network_HttpClient.connect
2026-01-27 10:30:45.124: [ 12345 INFO >>> ] Connecting to: http://example.com
2026-01-27 10:30:45.125: [ 12345 DEBUG >>> ] Connection established
2026-01-27 10:30:45.126: [ 12345 Exit <-- ] network_HttpClient.connect
2026-01-27 10:30:45.127: [ 12345 Enter -->] network_HttpClient.send_request
2026-01-27 10:30:45.128: [ 12345 DEBUG >>> ] Request: GET /api/data
2026-01-27 10:30:45.129: [ 12345 INFO >>> ] Request sent successfully
2026-01-27 10:30:45.130: [ 12345 Exit <-- ] network_HttpClient.send_request
Common Use Cases
Use Case 1: Basic Application Logging
Goal: Log application startup and main events
#include "areg/appbase/Application.hpp"
#include "areg/logging/areg_log.h"
DEF_LOG_SCOPE(main_application, main);
int main()
{
// Enable logging
Application::setup(true, true, false, true, false, "./config/areg.init", nullptr);
LOG_SCOPE(main_application, main);
LOG_INFO("Application started");
// Load model
LOG_DBG("Loading service model");
Application::load_model("MyServiceModel");
LOG_INFO("Service model loaded");
// Run application
LOG_INFO("Application running, waiting for quit signal");
Application::wait_quit(areg::WAIT_INFINITE);
LOG_INFO("Application shutting down");
Application::release();
return 0;
}
Use Case 2: Class Method Logging
Goal: Track method calls and parameters
#include "areg/logging/areg_log.h"
// Define scopes for each method
DEF_LOG_SCOPE(database_Connection, connect);
DEF_LOG_SCOPE(database_Connection, execute);
DEF_LOG_SCOPE(database_Connection, disconnect);
class Connection
{
public:
bool connect(const String& connectionString)
{
LOG_SCOPE(database_Connection, connect);
LOG_INFO("Connecting to database");
LOG_DBG("Connection string: %s", connectionString.as_string());
// Connection logic
bool success = true;
LOG_INFO_IF(success, "Connected successfully");
LOG_ERR_IF(!success, "Connection failed");
return success;
}
void execute(const String& query)
{
LOG_SCOPE(database_Connection, execute);
LOG_DBG("Executing query: %s", query.as_string());
// Execute logic
LOG_INFO("Query executed successfully");
}
void disconnect()
{
LOG_SCOPE(database_Connection, disconnect);
LOG_INFO("Disconnecting from database");
// Disconnect logic
LOG_INFO("Disconnected");
}
};
Use Case 3: Error Handling with Logging
Goal: Log errors with context
#include "areg/logging/areg_log.h"
DEF_LOG_SCOPE(file_FileManager, open_file);
class FileManager
{
public:
bool open_file(const String& filename)
{
LOG_SCOPE(file_FileManager, open_file);
LOG_INFO("Opening file: %s", filename.as_string());
if (filename.is_empty())
{
LOG_ERR("Invalid filename: empty string");
return false;
}
File file(filename);
if (!file.exists())
{
LOG_ERR("File not found: %s", filename.as_string());
return false;
}
if (!file.can_read())
{
LOG_ERR("No read permission for file: %s", filename.as_string());
return false;
}
LOG_DBG("File opened successfully");
return true;
}
};
Use Case 4: Performance Monitoring
Goal: Track execution time with scopes
#include "areg/logging/areg_log.h"
DEF_LOG_SCOPE(processing_DataProcessor, process_large_dataset);
class DataProcessor
{
public:
void process_large_dataset(const DataSet& data)
{
// Scope automatically tracks entry/exit time
LOG_SCOPE(processing_DataProcessor, process_large_dataset);
LOG_INFO("Starting processing of %u items", data.size());
for (int i = 0; i < data.size(); ++i)
{
process_item(data.get_item(i));
// Log progress every 1000 items
if ((i + 1) % 1000 == 0)
{
LOG_INFO("Processed %d items", i + 1);
}
}
LOG_INFO("Processing complete");
}
// Scope exit logged automatically with execution time
};
Use Case 5: Conditional Debug Logging
Goal: Detailed logging only in debug builds
#include "areg/logging/areg_log.h"
DEF_LOG_SCOPE(algorithm_Sorter, sort);
class Sorter
{
public:
void sort(Array<int>& array)
{
LOG_SCOPE(algorithm_Sorter, sort);
LOG_INFO("Sorting array of size %d", array.getSize());
#ifdef DEBUG
// Detailed logging only in debug builds
LOG_DBG("Array before sort:");
for (int i = 0; i < array.size(); ++i)
{
LOG_DBG(" [%d] = %d", i, array[i]);
}
#endif
// Sorting algorithm
perform_sort(array);
#ifdef DEBUG
LOG_DBG("Array after sort:");
for (int i = 0; i < array.size(); ++i)
{
LOG_DBG(" [%d] = %d", i, array[i]);
}
#endif
LOG_INFO("Sorting complete");
}
};
Troubleshooting
No Log Output
Problem: Application runs but no logs appear.
Solution:
1. Check logging is enabled at compile-time:
# Verify AREG_LOGGING is ON
cmake -B ./build -DAREG_LOGGING=ON
cmake --build ./build
2. Check logging is enabled at runtime:
Application::setup(
true, // Must be true for logging ← Check this
true, true, false, true, false
);
3. Check configuration file:
# In areg.init
log::*::target = file
log::*::enable::file = true
log::*::scope::* = DEBUG | SCOPE
4. Check log directory exists:
mkdir -p ./logs
Logs Not Showing for Specific Scope
Problem: Some scopes don't log.
Solution:
1. Check scope is defined:
// Must define before using
DEF_LOG_SCOPE(my_scope_name, my_function);
2. Check scope is activated:
void my_function()
{
// Must activate scope
LOG_SCOPE(my_scope_name, my_function);
LOG_DBG("This message should appear");
}
3. Check scope is enabled in config:
# Verify scope not set to NOTSET
log::*::scope::my_scope_name.my_function = DEBUG | SCOPE
Compile Errors with Logging Macros
Problem: Compiler errors with LOG_DBG, LOG_INFO, etc.
Solution:
1. Include logging header:
#include "areg/logging/areg_log.h"
2. Check AREG_LOGGING is enabled:
cmake -B ./build -DAREG_LOGGING=ON
3. Verify macro syntax:
// Correct
LOG_DBG("Message");
LOG_INFO("Value: %d", value);
// Wrong - missing quotes
LOG_DBG(Message);
// Wrong - missing format specifier
LOG_INFO(value);
Performance Impact
Problem: Logging slows down application.
Solution:
1. Reduce log level in production:
# Only warnings and errors
log::*::scope::* = WARN
2. Disable verbose scopes:
# Most scopes: errors only
log::*::scope::* = ERROR
# Critical scopes only: detailed
log::*::scope::critical_module.tough = DEBUG | SCOPE
3. Disable scope tracking:
# Remove SCOPE from priorities
log::*::scope::* = WARN # No | SCOPE
4. For maximum performance, compile without logs:
cmake -B ./build -DAREG_LOGGING=OFF
Scope Name Conflicts
Problem: Duplicate scope name errors.
Solution:
Use unique, descriptive names:
// Wrong - too generic
DEF_LOG_SCOPE(process);
DEF_LOG_SCOPE(process); // Duplicate!
// Correct - specific and unique
DEF_LOG_SCOPE(network_HttpClient, process);
DEF_LOG_SCOPE(database_Connection, process);
Naming convention:
<module_path_>_<class>.<function>
Missing Log Messages
Problem: Some messages don't appear in logs.
Solution:
1. Check priority level:
# If set to WARN, DEBUG and INFO won't show
log::*::scope::* = WARN
# Change to see all messages
log::*::scope::* = DEBUG | SCOPE
2. Check conditional logging:
// Message only logged if condition true
LOG_DBG_IF(condition, "Message");
// Verify condition is actually true
3. Flush logs before exit:
// Ensure all logs written before exit
Application::release();
Log File Permission Denied
Problem: Cannot write to log file.
Solution:
1. Check directory permissions:
# Ensure write permissions
chmod 755 ./logs
# Or change log location
2. Use different log location:
# In areg.init
log::*::file::location = /tmp/myapp.log
3. Run with appropriate permissions:
# If logging to /var/log
sudo ./myapp
Additional Resources
Related Guides:
- Logging Configuration Guide - Detailed configuration options
- Log Collector Guide - Remote log collection
- Log Observer Guide - Runtime scope control
- Lusan Live Log Viewer - GUI log viewer
Source Code:
- Logging Module - Implementation
- areg_log.h - Logging macros
Configuration:
- areg.init - Default configuration
Help: For questions, open a discussion or issue on GitHub.
Copyright © 2026, Aregtech (Artak Avetyan), www.areg.tech, email: info[at]areg.tech