lwlog Logging Library

repository·master·Indexed 43 days ago

https://github.com/christianpanov/lwlog

A high-performance C++17 logging library designed for minimal overhead. It features zero-allocation log calls using static buffers and supports both synchronous and asynchronous logging modes. lwlog provides granular control over verbosity, custom sinks (Console and File), and flexible formatting patterns with support for source metainformation and custom attributes.

Tokens
6.9K
Snippets
19
Records
24
Agent score
6%

What's inside lwlog

  1. Key Features of lwlog

    master

    The lwlog library provides the following features:

    • Modern C++: Written in C++17.
    • Logging Modes: Very fast synchronous and asynchronous logging.
    • Verbosity: Supports various verbosity levels.
    • Sinks: Includes Console (stdout, stderr) and File sinks, with support for custom sinks.
    • Configuration: Supports both compile-time and runtime sink configuration.
    • Granular Control: Each sink can have a unique log pattern and verbosity level.
    • Formatting: Log formatting according to a custom user-defined pattern.
    • Metadata: Support for Source Metainformation Attributes and Custom Attributes.
  2. Organize logs using Topics and Subtopics

    master

    You can categorize log messages using a hierarchical topic system. This is useful for filtering and organizing logs in complex applications.

    • Use start_topic("Name") to begin a topic.
    • Use end_topic() to close the current topic.
    • Topics can be nested to create subtopics. The pattern flag %? shows the current topic, while %? (or {full_topic}) shows the full path of all topics and subtopics (e.g., topic1/topic2/topic3).

    Example:

    console->start_topic("Main Topic");
    {
        console->start_topic("Sub Topic");
        console->critical("Message in both topics");
        console->end_topic();
    }
    console->end_topic();
    #include "lwlog.h"
    
    int main()
    {
    	auto console = std::make_shared<lwlog::console_logger>("CONSOLE");
    	console->set_pattern("{full_topic} .red([%T] [%n]) .level([%l]): .cyan(%v)");
    
    	console->start_topic("Main Topic");
    	{
    		console->start_topic("Sub Topic");
    		{
    			console->critical("Message in both topics");
    		}
    		console->end_topic();
    
    		console->critical("Message in main topic");
    	}
    	console->end_topic();
    	
    	return 0;
    }
  3. How lwlog architecture works

    master

    The architecture of lwlog is divided into three main modules that compose the logging pipeline:

    1. Record: An object that encapsulates the data to be logged, including the message, logging level, metadata (current line, file, and function), timepoint, and execution context (current thread ID and process ID).
    2. Sink: An object that sends data to an output destination. Data can be handled by a writer object, or directly in the sink_it() function.
    3. Logger: An object that manages a collection of sinks. It distributes every logging operation to all contained sinks. Loggers can be configured to operate synchronously or asynchronously.

    Mental Model: Logger $\rightarrow$ Sink $\rightarrow$ Record.

  4. Configure logger policies

    master

    The lwlog::logger template accepts several policy structures to define its behavior:

    Execution Policies

    PolicyDescription
    lwlog::synchronous_policyStandard linear logging. Log calls are processed immediately on the calling thread.
    lwlog::asynchronous_policyLog calls are handed off to a background thread for processing, allowing the caller to continue immediately.

    Overflow Policies (for asynchronous_policy)

    PolicyDescription
    lwlog::default_overflow_policyAlias for lwlog::blocking_overflow_policy.
    lwlog::blocking_overflow_policyOn a full queue, the log operation busy-waits until a slot is available. Trades performance for safety (no lost messages).
    lwlog::overwrite_last_overflow_policyOn a full queue, overwrites the last message in the queue with the new one.
    lwlog::discard_new_overflow_policyOn a full queue, the new message is discarded.

    Flush Policies (for stream sinks like stdout/file)

    PolicyDescription
    lwlog::default_flush_policyAlias for lwlog::immediate_flush_policy.
    lwlog::buffered_flush_policy<Size>Uses a manually increased buffer for significant performance gains. Messages only flush when the buffer is full. Default size is $2^{22}$ bytes.
    lwlog::immediate_flush_policyCalls a flush function after every log. Slower but safer as no messages are lost.

    Threading Policies

    PolicyDescription
    lwlog::single_threaded_policyUses a placeholder mutex/locks. Use when thread-safety is not required for better performance.
    lwlog::multi_threaded_policyUses actual mutexes and locks to ensure thread-safety.
  5. Core Design Highlights of lwlog

    master

    Zero Allocation Log Call

    lwlog eliminates dynamic memory allocations during log calls by using a pre-allocated static buffer for string formatting. This is designed for high-frequency logging in resource-constrained or real-time environments.

    Performance

    Supports both synchronous and asynchronous logging modes with minimal impact on application performance.

    Extensibility and Configurability

    • Sinks: Easy to add new types of sinks.
    • Policies: Uses policy classes that can be plugged in based on specific needs.

    Ease of Use

    Provides convenience aliases for predefined types to simplify configuration for most use cases.

  6. Link lwlog to your project with CMake

    master

    Once installed, you can integrate lwlog into your CMake project by finding the package and linking against the lwlog::lwlog_lib target. Use the HINTS option in find_package to point to the directory containing the lib/cmake folder of your installation.

    find_package(lwlog_lib HINTS <Installation Directory>/lib/cmake)
    
    target_link_libraries(MyExe PRIVATE lwlog::lwlog_lib)
  7. Create a custom sink

    master

    To extend lwlog, implement a custom sink by following these steps:

    1. Inherit from lwlog::sinks::sink: The base class requires template parameters for BufferLimits, FlushPolicy, and ThreadingPolicy. The first template parameter of sink is a boolean indicating if the sink supports ANSI colors.
    2. Implement sink_it(const details::record<BufferLimits>& record): This handles formatted log messages. You should set the current severity level, compile the pattern, and output the message. Crucially, call m_pattern.reset_pattern() to prevent data bleeding from previous logs due to the static buffer design.
    3. Implement sink_it(std::string_view message): This handles raw messages. Since raw logs have no metadata, you simply output the string.
    4. Handle Flushing: If your sink is stream-based, ensure you respect the FlushPolicy provided in the template.
    namespace lwlog::sinks
    {
    	template<typename BufferLimits, typename FlushPolicy, typename ThreadingPolicy>
    	class new_custom_sink
    		: public sink</*EnableAnsiColors*/, BufferLimits, ThreadingPolicy>
    	{
    		using sink_t = sink</*EnableAnsiColors*/, BufferLimits, ThreadingPolicy>;
    
    	public:
    		void sink_it(const details::record<BufferLimits>& record) override
    		{
            	sink_t::m_current_level = record.log_level;
    			// sink message to somewhere
    			sink_t::m_pattern.reset_pattern();
    		}
    
    		void sink_it(std::string_view message) override
    		{
    			// sink message to somewhere
    		}
    	};
    }
  8. Install the lwlog CMake Package

    master

    To build and install lwlog as a CMake package, use the following commands. Replace <Build Directory> with your desired build folder, <Directory of CMakeLists.txt> with the source directory, <Installation Directory> with where you want the library installed, and <Debug/Release> with your desired build configuration.

    cmake -B <Build Directory> -S <Directory of CMakeLists.txt> -DCMAKE_INSTALL_PREFIX=<Installation Directory>
    cmake --build <Build Directory> --target install --config <Debug/Release>
  9. Use convenience logger aliases

    master

    For rapid integration, lwlog.h provides predefined aliases that come with default configurations.

    Note on Thread-Safety: The standard aliases (e.g., console_logger) are not thread-safe. For multi-threaded applications, use the variants suffixed with _mt (e.g., async_console_logger_mt).

    AliasDescription
    lwlog::basic_loggerSynchronous logger, standard flush policy, not thread-safe
    lwlog::console_loggerbasic_logger sinking to stdout
    lwlog::file_loggerbasic_logger sinking to a file
    lwlog::async_loggerAsynchronous logger, standard flush policy, not thread-safe
    lwlog::async_console_loggerasync_logger sinking to stdout
    lwlog::async_file_loggerasync_logger sinking to a file
    #include "lwlog.h"
    
    int main()
    {
    	auto basic = std::make_shared<lwlog::basic_logger<lwlog::sinks::stdout_sink>>("CONSOLE");
    	
    	auto console = std::make_shared<lwlog::console_logger>("CONSOLE");
    	auto file = std::make_shared<lwlog::file_logger>("FILE", "C:/Users/user/Desktop/LogFolder/LOGS.txt");
    	
    	return 0;
    }
  10. Enable local time instead of UTC

    master

    By default, lwlog uses UTC for time-based attributes. To use local time, you must define the LWLOG_LOCALTIME macro. You can do this by:

    1. Passing it as a compiler flag in CMake: -DCMAKE_CXX_FLAGS="-DLWLOG_LOCALTIME".
    2. Defining it in your code before including any lwlog headers.
    cmake -B <Build Directory> -S <Directory of CMakeLists.txt> \
        -DCMAKE_INSTALL_PREFIX=<Installation Directory> \
        -DCMAKE_CXX_FLAGS="-DLWLOG_LOCALTIME"
  11. Configure logger memory buffer limits

    master

    The first template parameter of lwlog::logger is lwlog::memory_buffer_limits. This allows you to tailor pre-allocated static buffers for specific use cases to optimize performance and memory usage.

    Use lwlog::default_memory_buffer_limits for standard settings. Otherwise, you can specify individual limits using the following template structures:

    ConfigurationDescription
    lwlog::pattern_limit<N>Maximum pattern length, including the log message itself
    lwlog::message_limit<N>Maximum log message length
    lwlog::argument_limit<N>Maximum length of a single log argument
    lwlog::arg_count_limit<N>Maximum number of log arguments per log call
    lwlog::padding_limit<N>Maximum length of flag padding (for alignment)
    lwlog::conv_limit<N>Maximum length of a single formatting flag
    #include "lwlog.h"
    
    int main()
    {
    	using buffer_limits = lwlog::memory_buffer_limits<
    		lwlog::pattern_limit<256>,
    			lwlog::message_limit<128>,
    				lwlog::argument_limit<64>,
    				lwlog::arg_count_limit<4>,
    				lwlog::padding_limit<24>,
    				lwlog::conv_limit<64>
    		>;
    
    	auto console = std::make_shared<
    		lwlog::logger<
    			buffer_limits,
    			lwlog::synchronous_policy,
    			lwlog::default_flush_policy,
    			lwlog::single_threaded_policy,
    			lwlog::sinks::stdout_sink
    		>
    		>("CONSOLE");
    	
    	return 0;
    }