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

  1. Quick Start
  2. Overview
  3. Compile-Time Configuration
  4. Runtime Activation
  5. Using Logging Scopes
  6. Log Macros Reference
  7. Common Use Cases
  8. 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

↑ Back to top ↑

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

ScenarioUse LoggingBenefit
Development✅ AlwaysDebug issues, understand flow
Testing✅ AlwaysVerify behavior, catch edge cases
Production✅ SelectiveMonitor errors, track issues, reduce SCOPE, DEBUG and INFO
Performance Critical⚙️ MinimalError/Fatal only, disable DEBUG and SCOPE
↑ Back to top ↑

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.

↑ Back to top ↑

Runtime Activation

Once compiled with logging enabled, control logging at runtime using the Application class or macros.


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.init configuration 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 logging
    • configFile - Path to configuration file
    • force - 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 config
  • LOGGING_FORCE_START() - Start with default config
  • LOGGING_STOP() - Stop logging

Important

If compiled with AREG_LOGGING=OFF, these macros are no-ops and do nothing.

↑ Back to top ↑

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_request
  • database_sqlite_Connection.execute_query
  • ui_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

↑ Back to top ↑

Log Macros Reference

Priority Levels

MacroPriorityUse CaseExample
LOG_DBG()DEBUGDetailed debuggingLOG_DBG("Counter value: %d", count)
LOG_INFO()INFOGeneral informationLOG_INFO("Connection established")
LOG_WARN()WARNWarningsLOG_WARN("Retry attempt %d", retry)
LOG_ERR()ERRORErrorsLOG_ERR("Failed to open file: %s", path)
LOG_FATAL()FATALFatal errorsLOG_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
↑ Back to top ↑

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");
    }
};
↑ Back to top ↑

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
↑ Back to top ↑

Additional Resources

Related Guides:

Source Code:

Configuration:

Help: For questions, open a discussion or issue on GitHub.

↑ Back to top ↑

Copyright © 2026, Aregtech (Artak Avetyan), www.areg.tech, email: info[at]areg.tech