Skip to content

haipome/dlog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dlog

A high-performance C logging library with buffered I/O, log rotation, log levels, and remote logging support.

Features

  • Buffered writes — logs are accumulated in a 64 KB in-memory buffer and flushed every 10 ms or when the buffer reaches 32 KB, drastically reducing I/O syscalls compared to line-by-line file writes
  • Log rotation — rotate log files by size, minute, hour, or day; old files are renamed with index suffixes automatically
  • Log expiration — automatically delete log files older than a configurable retention period
  • Nine log levelsFATAL, ERROR, WARN, INFO, NOTICE, DEBUG, TRACE, USER1, USER2; levels can be toggled at runtime
  • Remote UDP logging — forward log messages to a remote dlog_server over UDP
  • Thread-safe — every log write is protected by a per-instance pthread_mutex
  • Fork offload — optionally fork a child process to perform file rotation and deletion, keeping the main process latency low
  • Stack backtracedlog_backtrace() captures and logs a full call stack using backtrace_symbols
  • Fatal callback — register a dlog_on_fatal callback to be invoked on every log_fatal call (e.g. to send an alert)
  • Automatic cleanup — registers an atexit handler that flushes and closes all open log instances on normal exit
  • Standalone log serverdlog_server is a ready-to-use UDP daemon that receives and persists remote log messages

Log Levels

Macro Flag Description
log_vip always on Critical messages, bypasses the level flag
log_fatal DLOG_FATAL Fatal errors; also triggers dlog_on_fatal callback
log_error DLOG_ERROR Non-fatal errors
log_warn DLOG_WARN Warnings
log_info DLOG_INFO Informational messages
log_notice DLOG_NOTICE Notable events
log_debug DLOG_DEBUG Debug output
log_trace DLOG_TRACE Fine-grained tracing
log_user1 DLOG_USER1 User-defined level 1
log_user2 DLOG_USER2 User-defined level 2
log_exception DLOG_FATAL Fatal + automatic stack backtrace

Each macro prepends the source file, line number, and function name automatically:

[2024-01-15 12:34:56.789012] [error]main.c:42(process_request): connection refused

Building

# Build the test binary and the log server
make

# Build only the test binary
gcc -o dlog_test -g -Wall dlog.c test.c -D DEBUG -rdynamic

# Build only the log server
gcc -o dlog_server dlog_server.c dlog.c -D DLOG_SERVER -g -Wall -O2

Quick Start

#include "dlog.h"

int main(void)
{
    // Create a log handler: rotate daily, no size limit, keep 30 days
    dlog_t *log = dlog_init("logs/app", DLOG_SHIFT_BY_DAY, 0, 0, 30);
    if (log == NULL) {
        fprintf(stderr, "dlog_init failed\n");
        return 1;
    }

    // Set as the default log and enable desired levels
    default_dlog = log;
    default_dlog_flag = DLOG_FATAL | DLOG_ERROR | DLOG_WARN | DLOG_INFO;

    log_info("server started");
    log_error("something went wrong: %s", strerror(errno));
    log_fatal("unrecoverable error, shutting down");

    // Flush remaining buffer on exit (also done automatically via atexit)
    dlog_flush_all();
    return 0;
}

API Reference

Initialization

dlog_t *dlog_init(const char *base_name, int flag, size_t max_size,
                  int log_num, int keep_time);
Parameter Description
base_name Path prefix for log files, e.g. "logs/app"
flag Shift type combined with optional flags (see below)
max_size Maximum file size in bytes before rotation (0 = unlimited)
log_num Maximum number of rotated files to keep (0 = unlimited)
keep_time Number of time units (days/hours/minutes) to retain old files

Shift types (mutually exclusive, required):

Constant Description
DLOG_SHIFT_BY_SIZE Rotate when file reaches max_size
DLOG_SHIFT_BY_MIN Rotate every minute
DLOG_SHIFT_BY_HOUR Rotate every hour
DLOG_SHIFT_BY_DAY Rotate every day

Optional flags (OR-combined with shift type):

Flag Description
DLOG_USE_FORK Fork a child process for rotation and deletion
DLOG_NO_CACHE Write to disk immediately on every log call
DLOG_REMOTE_LOG Send logs to a remote UDP server (pass struct sockaddr_in * as base_name)
DLOG_NO_TIMESTAMP Omit the timestamp prefix from each line
// Examples
dlog_init("app", DLOG_SHIFT_BY_DAY, 0, 0, 30);
dlog_init("app", DLOG_SHIFT_BY_SIZE | DLOG_USE_FORK, 500*1024*1024, 10, 0);
dlog_init("app", DLOG_SHIFT_BY_HOUR | DLOG_NO_CACHE, 0, 24, 7);

Logging

// Write a formatted message (thread-safe)
int dlog(dlog_t *log, const char *fmt, ...);
int dlogv(dlog_t *log, const char *fmt, va_list ap);

// Write to stderr (unbuffered, no log object needed)
void dlog_stderr(const char *fmt, ...);

// Write to syslog
void dlog_syslog(const char *fmt, ...);

// Log a stack backtrace
void dlog_backtrace(dlog_t *log);

Level flags

// Parse a comma/space-separated level string into a bitmask
// Accepts: "fatal", "error", "warn", "info", "notice", "debug", "trace", "user1", "user2"
int dlog_read_flag(char *str);

// Example
default_dlog_flag = dlog_read_flag("fatal, error, warn, info");

// Dynamically raise or lower the active log level by one step
void dlog_level_up(void);
void dlog_level_down(void);

Flushing and checking

// Flush a single log to disk now
void dlog_flush(dlog_t *log);

// Flush all open logs to disk (safe to call from a signal handler)
void dlog_flush_all(void);

// Check whether a log needs flushing (call from main loop or timer)
void dlog_check(dlog_t *log, struct timeval *tv);
void dlog_check_all(void);

Cleanup

// Close and free a log instance
int dlog_fini(dlog_t *log);

// Number of currently open log instances
int dlog_opened_num(void);

Signal handling

To prevent log loss on crashes, catch SIGSEGV with SA_RESETHAND and call dlog_flush_all:

#include <signal.h>

void sigsegv_handler(int signo)
{
    dlog_flush_all();
}

struct sigaction sa = { .sa_handler = sigsegv_handler, .sa_flags = SA_RESETHAND };
sigaction(SIGSEGV, &sa, NULL);

Fatal callback

// Called on every log_fatal invocation, e.g. to send an alert or notify a monitor
extern dlog_on_fatal_cb dlog_on_fatal;

int my_alert(const char *fmt, ...)
{
    // send alert...
    return 0;
}

dlog_on_fatal = my_alert;

Remote Logging

dlog supports forwarding log messages to a remote host over UDP.

Option 1 — IP:port string

// Pass "host:port" as base_name; dlog detects the colon and parses it
dlog_t *log = dlog_init("192.168.1.100:9000", DLOG_SHIFT_BY_DAY, 0, 0, 0);

Option 2 — DLOG_REMOTE_LOG flag

struct sockaddr_in addr = { 0 };
addr.sin_family = AF_INET;
inet_aton("192.168.1.100", &addr.sin_addr);
addr.sin_port = htons(9000);

dlog_t *log = dlog_init((const char *)&addr, DLOG_REMOTE_LOG | DLOG_SHIFT_BY_DAY, 0, 0, 0);

Option 3 — Supply your own UDP socket

int fd = socket(AF_INET, SOCK_DGRAM, 0);
// configure fd ...
dlog_set_sockfd(log, fd);

Running the log server

Usage:
    -h --help
    -b --base base_name      Log file path prefix (required)
    -t --type shift_type     size / day / hour / min  (default: day)
    -s --size max_size       Max file size in bytes
    -n --num  log_num        Max number of rotated files
    -k --keep keep_time      Retention period
    -p --port port           UDP port to listen on (required)
    -a --addr                Prepend sender IP:port to each message
    -d --daemon              Run as a background daemon
# Listen on port 9000, rotate daily, keep 30 days
./dlog_server -b logs/remote -t day -p 9000 -k 30 -d

Log File Naming

Log files are named <base_name><suffix> where the suffix encodes both the time period and the rotation index:

Shift type Example filenames
By size app.log, app.log.1, app.log.2
By day app.20240115.log, app.20240114.log.1
By hour app.2024011512.log
By minute app.202401151234.log

Performance Notes

  • The 64 KB write buffer and 10 ms flush interval keep log overhead minimal for high-throughput services.
  • Use DLOG_USE_FORK to offload file rotation and deletion to a child process, eliminating blocking I/O in the main thread.
  • Avoid DLOG_NO_CACHE in latency-sensitive paths; use dlog_check_all() in your event loop instead.

About

a log lib with cache

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors