Skip to content
/ echo Public

super simple C++ header olnly logging lib

License

Notifications You must be signed in to change notification settings

bresilla/echo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

91 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Echo

A modern C++20 logging library with modular architecture, rich formatting, visual widgets, and zero-overhead performance.

License: MIT C++20 Version

Overview

Echo is a high-performance, header-mostly logging library designed for modern C++20 applications. It combines powerful logging capabilities with rich terminal UI widgets, providing everything from simple console output to sophisticated multi-sink logging with custom formatters, category filtering, and visual progress indicators.

Key Features:

  • Zero overhead - Compile-time log filtering eliminates runtime cost (literally 0ns!)
  • Modular architecture - Enable only the sinks you need via compile-time flags
  • Performance first - 17M+ ops/sec for filtered logs, 3-4M ops/sec for active logging
  • Rich formatting - Pattern formatters, custom formatters, color support
  • Visual widgets - Progress bars, spinners, banners, boxes with themes
  • Developer friendly - Fluent API, category filtering, thread-safe

Echo is built for applications that demand both flexibility and performance, from embedded systems to high-throughput servers.

Quick Start

#include <echo/echo.hpp>

int main() {
    // Simple printing (no log levels)
    echo("Hello, world!");
    echo("Colored text").red().bold();

    // Logging with levels
    echo::info("Application started");
    echo::debug("Value: ", 42);
    echo::error("Something went wrong!").red();

    // Fluent interface with colors
    echo::warn("Warning!").yellow().bold();
    echo::info("Custom color").hex("#FF1493");
    echo::error("RGB color").rgb(255, 87, 51);

    // Print only once (useful in loops)
    for (int i = 0; i < 1000; i++) {
        echo::info("This prints only once!").once();
    }

    return 0;
}

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                            ECHO LIBRARY                                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚     Core     β”‚    Sinks     β”‚  Formatters  β”‚   Filters    β”‚   Utils     β”‚
β”‚              β”‚              β”‚              β”‚              β”‚             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ Proxy  β”‚  β”‚  β”‚Console β”‚  β”‚  β”‚Pattern β”‚  β”‚  β”‚Categoryβ”‚  β”‚ β”‚ Color  β”‚ β”‚
β”‚  β”‚ Level  β”‚  β”‚  β”‚  File  β”‚  β”‚  β”‚ Custom β”‚  β”‚  β”‚  Level β”‚  β”‚ β”‚Terminalβ”‚ β”‚
β”‚  β”‚ Mutex  β”‚  β”‚  β”‚Syslog* β”‚  β”‚  β”‚Default β”‚  β”‚  β”‚        β”‚  β”‚ β”‚  Hash  β”‚ β”‚
β”‚  β”‚  Once  β”‚  β”‚  β”‚Network*β”‚  β”‚  β”‚        β”‚  β”‚  β”‚        β”‚  β”‚ β”‚        β”‚ β”‚
β”‚  β”‚  Time  β”‚  β”‚  β”‚ Null*  β”‚  β”‚  β”‚        β”‚  β”‚  β”‚        β”‚  β”‚ β”‚        β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚              β”‚              β”‚              β”‚              β”‚             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚               β”‚               β”‚              β”‚              β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                            β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
                            β”‚   Your App     β”‚
                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

* Optional sinks (compile-time flags)

Data Flow:

Log Call β†’ Compile-Time Filter β†’ Runtime Filter β†’ Category Filter
    ↓
Format Message β†’ Formatter (Pattern/Custom/Default)
    ↓
Sink Registry β†’ [ConsoleSink, FileSink*, SyslogSink*, NetworkSink*, NullSink*]
    ↓
Output (stdout/stderr, file, syslog, network, /dev/null)

Installation

CMake FetchContent

include(FetchContent)
FetchContent_Declare(
  echo
  GIT_REPOSITORY https://github.com/robolibs/echo.git
  GIT_TAG v0.0.21
)
FetchContent_MakeAvailable(echo)

target_link_libraries(your_target PRIVATE echo::echo)

XMake

add_requires("echo")

target("your_target")
    set_kind("binary")
    add_packages("echo")
    add_files("src/*.cpp")

Manual Installation

git clone https://github.com/robolibs/echo.git
cd echo

# Using Make (auto-detects cmake/xmake)
make config
make build
make test

# Or directly with CMake
mkdir build && cd build
cmake -DECHO_BUILD_EXAMPLES=ON -DECHO_ENABLE_TESTS=ON ..
make -j$(nproc)
ctest

# Or with XMake
xmake f --examples=y --tests=y
xmake
xmake test

Core Features

1. Log Levels

Six log levels with compile-time and runtime filtering:

echo::trace("Detailed trace information");
echo::debug("Debug information");
echo::info("Informational message");
echo::warn("Warning message");
echo::error("Error message");
echo::critical("Critical error");

Compile-time filtering (zero overhead):

# CMake
cmake -DLOGLEVEL=Error ..

# XMake
xmake f -c -DLOGLEVEL=Error

# Result: trace/debug/info/warn calls are completely removed from binary

Runtime filtering:

echo::set_level(echo::Level::Warn);  // Only Warn and above

2. Fluent Interface with Colors

Chain colors, styles, and modifiers:

// Standard colors
echo::info("Message").red().bold();
echo::warn("Warning").yellow().italic();

// Custom colors
echo::info("HEX color").hex("#FF1493");
echo::error("RGB color").rgb(255, 87, 51);

// Multiple modifiers
echo::critical("Alert!").red().bold().underline();

Available colors: red, green, yellow, blue, magenta, cyan, white, gray

Available styles: bold, italic, underline

3. Rate Limiting & Conditional Logging

// Print only once (useful in loops)
for (int i = 0; i < 1000000; i++) {
    echo::warn("This prints only once!").once();
}

// Rate-limited printing (every N milliseconds)
for (int i = 0; i < 1000000; i++) {
    echo::info("Prints every 1 second").every(1000);
}

// Conditional printing
bool debug_mode = true;
echo::debug("Debug info").when(debug_mode);

4. In-place Updates

Perfect for progress indicators and status updates:

for (int i = 0; i <= 100; i++) {
    echo("Progress: ", i, "%").inplace();
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
echo("\n");  // Move to next line when done

5. Multiple Sinks

Route logs to multiple destinations simultaneously:

#define ECHO_ENABLE_FILE_SINK
#define ECHO_ENABLE_SYSLOG_SINK
#include <echo/echo.hpp>

int main() {
    // Clear default console sink
    echo::clear_sinks();

    // Add console sink with custom level
    auto console = std::make_shared<echo::ConsoleSink>();
    console->set_level(echo::Level::Info);
    echo::add_sink(console);

    // Add file sink with rotation
    auto file = std::make_shared<echo::FileSink>("app.log");
    file->enable_rotation(5 * 1024 * 1024, 3);  // 5MB, keep 3 files
    echo::add_sink(file);

    // Add syslog sink (Unix only)
    auto syslog = std::make_shared<echo::SyslogSink>("myapp");
    echo::add_sink(syslog);

    // Logs go to all sinks
    echo::info("Application started");
    echo::error("Error logged to console, file, and syslog");

    return 0;
}

Available sinks:

  • ConsoleSink - Always available (stdout/stderr)
  • FileSink - File logging with rotation (-DECHO_ENABLE_FILE_SINK)
  • SyslogSink - Unix syslog integration (-DECHO_ENABLE_SYSLOG_SINK)
  • NetworkSink - TCP/UDP logging (-DECHO_ENABLE_NETWORK_SINK)
  • NullSink - Discard output (-DECHO_ENABLE_NULL_SINK)

6. Custom Formatters

Three formatter types for maximum flexibility:

DefaultFormatter - Simple timestamp + level:

auto formatter = std::make_shared<echo::DefaultFormatter>(true, true);
// Output: [14:30:45][info] Message

PatternFormatter - Customizable patterns:

auto formatter = std::make_shared<echo::PatternFormatter>(
    "[{timestamp}] [{level}] {file}:{line} - {message}"
);
echo::set_global_formatter(formatter);
// Output: [2026-01-08 14:30:45] [INFO] main.cpp:42 - Message

Pattern placeholders:

  • {timestamp} or {time} - Timestamp
  • {level} - Log level
  • {message} or {msg} - Log message
  • {file} - Source file
  • {line} - Line number
  • {function} or {func} - Function name
  • {thread} - Thread ID

CustomFormatter - Lambda-based:

auto json_formatter = std::make_shared<echo::CustomFormatter>(
    [](const echo::LogRecord& rec) {
        return "{\"time\":\"" + rec.timestamp + 
               "\",\"level\":\"" + std::string(echo::detail::level_name(rec.level)) + 
               "\",\"msg\":\"" + rec.message + "\"}";
    }
);

7. Category-Based Filtering

Hierarchical category system with wildcard support:

// Set category-specific log levels
echo::set_category_level("network", echo::Level::Debug);
echo::set_category_level("database", echo::Level::Warn);
echo::set_category_level("app.*", echo::Level::Info);  // Wildcard

// Log with categories
echo::category("network").debug("TCP connection established");
echo::category("network").info("Received 1024 bytes");

echo::category("database").debug("Query executed");  // Filtered (< Warn)
echo::category("database").warn("Slow query detected");  // Logged

echo::category("app.auth").info("User logged in");  // Matches app.*
echo::category("app.api").error("Request failed");

Hierarchical matching:

Category: "app.network.tcp"
Search order:
1. "app.network.tcp" (exact match)
2. "app.network.*" (parent wildcard)
3. "app.*" (grandparent wildcard)
4. Global level (fallback)

Visual Widgets

Progress Bars

Rich progress bars with themes, time tracking, and byte formatting:

#include <echo/widget.hpp>

// Basic progress bar
echo::progress_bar bar(100);
for (int i = 0; i <= 100; ++i) {
    bar.set_progress(i);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
bar.finish();

// Themed progress bar with time and speed
echo::progress_bar download(1024 * 1024 * 100);  // 100MB
download.set_theme(echo::BarTheme::ocean());
download.set_show_elapsed(true);
download.set_show_remaining(true);
download.set_show_bytes(true, 1024);  // Show as KB/MB/GB
download.set_show_speed(true);

for (size_t i = 0; i <= 1024 * 1024 * 100; i += 1024 * 100) {
    download.set_progress(i);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
download.finish();

6 Bar Styles:

  • Classic: [===> ]
  • Blocks: [β–ˆβ–ˆβ–ˆβ–“β–’β–‘ ]
  • Smooth: [β–ˆβ–ˆβ–ˆβ–ˆβ–Œ ]
  • Arrows: [β†’β†’β†’β†’ ]
  • Dots: [●●●●○○○○]
  • ASCII: [###>... ]

5 Pre-configured Themes:

  • fire() - Red-orange-yellow gradient
  • ocean() - Dark blue to cyan
  • forest() - Dark green to light green
  • sunset() - Orange-red-gold
  • neon() - Magenta-cyan-yellow

Spinners

15 animated spinner styles:

echo::spinner spin(echo::SpinnerStyle::dots_scrolling);
spin.set_message("Loading...");
spin.set_color("#00FF00");

for (int i = 0; i < 50; ++i) {
    spin.tick();
    std::this_thread::sleep_for(std::chrono::milliseconds(
        spin.get_interval_ms()
    ));
}
spin.stop("Done!");

Spinner styles: line, pipe, simple_dots, dots_scrolling, flip, toggle, layer, point, dqpb, bouncing_bar, bouncing_ball, aesthetic, binary, grow_vertical, grow_horizontal

Step Indicators

Track multi-step workflows:

echo::steps workflow({"Initialize", "Process", "Validate", "Complete"});
workflow.set_color("#00FFFF");

workflow.next();  // Initialize
std::this_thread::sleep_for(std::chrono::seconds(1));
workflow.complete();

workflow.next();  // Process
std::this_thread::sleep_for(std::chrono::seconds(1));
workflow.complete();

workflow.next();  // Validate
std::this_thread::sleep_for(std::chrono::seconds(1));
workflow.fail();  // Mark as failed

workflow.next();  // Complete

Banners, Boxes, and Separators

// Separator
echo::separator("Section Title", '=');
echo::separator("Gradient", '─', {"#FF0000", "#00FF00", "#0000FF"});

// Box (6 styles: Single, Double, Rounded, Heavy, Dashed, ASCII)
echo::box("Important Message", echo::BoxStyle::Double);
echo::box("Colored Box", echo::BoxStyle::Rounded, "#FF00FF");

// Header
echo::header("Application Name");
echo::header("Colored Header", "#00FFFF");

// Title
echo::title("Chapter 1", '=');

// Banner
echo::banner("WELCOME", echo::BoxStyle::Heavy);

Advanced Color Utilities

Comprehensive color manipulation library:

#include <echo/utils/color.hpp>

// Color conversions
auto rgb = echo::hex_to_rgb("#FF1493");
auto hex = echo::to_hex(255, 20, 147);

// Color mixing (7 modes)
auto mixed = echo::mix(color1, color2, 0.5);
auto added = echo::add(color1, color2);
auto multiplied = echo::multiply(color1, color2);
auto screened = echo::screen(color1, color2);
auto overlayed = echo::overlay(color1, color2);

// Color adjustments (8 operations)
auto lighter = echo::lighten(rgb, 0.2);
auto darker = echo::darken(rgb, 0.2);
auto brighter = echo::brighten(rgb, 50);
auto dimmer = echo::dim(rgb, 50);
auto saturated = echo::saturate(rgb, 0.3);
auto desaturated = echo::desaturate(rgb, 0.3);
auto inverted = echo::invert(rgb);
auto gray = echo::grayscale(rgb);

// Color analysis
auto lum = echo::luminance(rgb);
bool dark = echo::is_dark(rgb);
bool light = echo::is_light(rgb);
auto dist = echo::distance(color1, color2);
auto contrast = echo::contrast_ratio(color1, color2);

// Color schemes (7 generators)
auto comp = echo::complementary(rgb);
auto analog = echo::analogous(rgb, 5, 30);
auto triadic = echo::triadic(rgb);
auto tints = echo::tints(rgb, 5);
auto shades = echo::shades(rgb, 5);
auto tones = echo::tones(rgb, 5);
auto temp = echo::from_temperature(0.5);  // Warm color

String Formatting

Powerful chainable string formatting with 60+ methods:

#include <echo/format.hpp>

// Colors and styles
auto str = echo::format::String("Hello")
    .red().bold().underline();

// RGB/HEX colors
auto custom = echo::format::String("Custom")
    .fg(255, 100, 50)
    .bg("#1E1E1E");

// Text alignment
auto aligned = echo::format::String("Text")
    .center(20)
    .border('*');

// Text transformation
auto transformed = echo::format::String("hello world")
    .title_case()
    .bold();

// Truncation and wrapping
auto truncated = echo::format::String("Long text...")
    .ellipsis(10);

auto wrapped = echo::format::String("Very long text that needs wrapping")
    .wrap(20);

// Numeric formatting
auto bytes = echo::format::String("1048576")
    .format_bytes();  // "1.00 MB"

auto duration = echo::format::String("3665")
    .format_duration();  // "1h 1m 5s"

// Special formatting
auto progress = echo::format::String("50")
    .progress(50);  // Progress bar representation

auto badge = echo::format::String("value")
    .badge("label");  // [label: value]

Performance

Echo is designed for invisible overhead in production code.

Benchmark Results

Scenario Latency Throughput Notes
Compile-time filtered 0 ns ∞ Code doesn't exist in binary
Runtime filtered 56-60 ns 16.7-17.7M ops/s Just integer comparison
Active logging (NullSink) 260-290 ns 3.4-3.8M ops/s Formatting only
Active logging (file) 350 ns 2.9M ops/s Includes file I/O
Active logging (console) 650 ns 1.5M ops/s Includes terminal I/O
.once() overhead +3-5 ns - After first call
Category filtering +30-40 ns - Hash lookup

Zero-Cost Abstraction

When you compile with -DLOGLEVEL=Error, filtered log statements are completely removed from the binary:

// Compiled with -DLOGLEVEL=Error
echo::debug("expensive", calculate());  // ← Doesn't exist in binary (0 ns)
echo::info("expensive", calculate());   // ← Doesn't exist in binary (0 ns)
echo::error("This runs", calculate());  // ← Exists in binary

The calculate() function is NEVER called for filtered logs!

Best Practices

βœ… DO: Use compile-time levels in production

if(CMAKE_BUILD_TYPE STREQUAL "Release")
    add_compile_definitions(LOGLEVEL=Error)
endif()

βœ… DO: Use .once() in loops

for (int i = 0; i < 1000000; i++) {
    echo::warn("Warning").once();  // Prints once, then ~60ns overhead
}

βœ… DO: Enable only needed sinks

#define ECHO_ENABLE_FILE_SINK  // Only enable what you need
#include <echo/echo.hpp>

❌ DON'T: Use echo() in performance-critical loops

// BAD - echo() always prints (no filtering)
for (int i = 0; i < 1000000; i++) {
    echo("iteration");  // Prints 1M times
}

// GOOD - Use log levels with compile-time filtering
for (int i = 0; i < 1000000; i++) {
    echo::debug("iteration").once();  // Compile-time filtered
}

See misc/PERFORMANCE.md for detailed benchmarks and optimization guide.

Testing

# Run all tests
make test

# Run specific test
make test TEST=test_category

# Run with sanitizers
make test-asan    # AddressSanitizer
make test-tsan    # ThreadSanitizer
make test-ubsan   # UndefinedBehaviorSanitizer
make test-sanitizers  # All sanitizers

# Build and run examples
make build
./build/color_demo
./build/advanced_progress_demo

Test Coverage:

  • 17 test files (150+ test cases)
  • Core features: levels, sinks, formatters, categories
  • Edge cases: concurrency, invalid inputs, long messages, Unicode
  • Integration: multi-sink, real-world scenarios
  • Thread safety: up to 20 concurrent threads

Build System

Echo supports three build systems with automatic detection:

Make (Unified Interface)

make build        # Build project (auto-detects cmake/xmake)
make config       # Configure build
make reconfig     # Full reconfigure (cleans cache)
make test         # Run tests
make clean        # Clean build directory
make help         # Show all targets

CMake

mkdir build && cd build
cmake -DECHO_BUILD_EXAMPLES=ON -DECHO_ENABLE_TESTS=ON ..
make -j$(nproc)
ctest

CMake Options:

  • -DECHO_BUILD_EXAMPLES=ON - Build examples
  • -DECHO_ENABLE_TESTS=ON - Enable tests
  • -DECHO_BUILD_BENCHMARKS=ON - Build benchmarks
  • -DCOMPILER=gcc|clang - Compiler selection
  • -DECHO_ENABLE_SIMD=ON - SIMD optimizations (default: ON)

XMake

xmake f --examples=y --tests=y
xmake
xmake test

XMake Options:

  • --examples=y - Build examples
  • --tests=y - Enable tests
  • --toolchain=gcc|clang - Compiler selection
  • --simd=true - SIMD optimizations (default: true)

Compiler Support

  • C++20 or later (required)
  • GCC 10+ (recommended)
  • Clang 12+ (recommended)
  • MSVC 19.29+ (Visual Studio 2019 16.11+)

SIMD Support:

  • x86_64: AVX, AVX2, FMA
  • ARM64: NEON (enabled by default)
  • ARM32: NEON with hard float

Requirements

  • C++20 compiler
  • Standard library only (no external dependencies)
  • Unix/Linux/macOS for full terminal features
  • Optional: doctest for tests

Documentation

Generate API documentation:

make docs TYPE=doxygen
# Open docs/doxygen/html/index.html

Examples

The project includes 36 comprehensive examples:

Core Logging:

  • main.cpp - Basic logging
  • performance_demo.cpp - Performance characteristics
  • test_once.cpp, test_every_when.cpp - Rate limiting

Colors:

  • color_demo.cpp - HEX color support
  • color_manipulation_demo.cpp - RGB utilities
  • format_demo.cpp - format::String colors
  • format_text_demo.cpp - Text manipulation

Formatters:

  • formatter_demo.cpp - Custom formatters
  • formatter_basic_demo.cpp - Direct formatter usage
  • global_formatter_demo.cpp - Global formatter API

Sinks:

  • sink_demo.cpp - Sink system
  • null_sink_demo.cpp - NullSink for testing
  • file_logging_demo.cpp - File logging

Widgets:

  • wait_demo.cpp - Progress bars, spinners, steps
  • advanced_progress_demo.cpp - Advanced progress features
  • separator_demo.cpp - Banners and separators
  • fullwidth_demo.cpp - Auto-sizing progress bars

Benchmarks (11 files):

  • bench_basic.cpp, bench_levels.cpp, bench_sinks.cpp
  • bench_formatters.cpp, bench_categories.cpp
  • bench_compile_time.cpp, bench_once.cpp
  • bench_threading.cpp, bench_memory.cpp
  • bench_latency.cpp, bench_vs_spdlog.cpp

License

MIT License - see LICENSE for details.

Acknowledgments

Made possible thanks to these amazing projects.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Changelog

See CHANGELOG.md for version history.

About

super simple C++ header olnly logging lib

Resources

License

Stars

Watchers

Forks

Packages

No packages published