ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
Logging System

📝 Logging API with multiple log levels and terminal output control More...

Files

file  logging.c
 📝 Multi-level logging with terminal color support, file rotation, and async output
 
file  logging.h
 📝 Logging API with multiple log levels and terminal output control
 
file  mmap.c
 Lock-free memory-mapped text logging implementation.
 
file  mmap.h
 Lock-free memory-mapped text logging with crash safety.
 
file  logging.h
 Network logging macros and remote log direction enumeration.
 

Data Structures

struct  log_buffer_entry_t
 A single buffered log entry. More...
 

Macros

#define LOG_ATOMIC_UINT64   _Atomic uint64_t
 
#define LOG_ATOMIC_UINT64_INIT(val)   val
 
#define DEFAULT_LOG_LEVEL   LOG_DEBUG
 Default log level for debug builds (DEBUG and above)
 
#define MAX_LOG_SIZE   (3 * 1024 * 1024)
 Maximum log file size in bytes (3MB) before rotation.
 
#define MAX_TERMINAL_BUFFER_SIZE   (64 * 1024)
 Maximum size of terminal output buffer (64KB)
 
#define MAX_TERMINAL_BUFFER_ENTRIES   256
 Maximum number of buffered log entries.
 
#define LOG_MSG_BUFFER_SIZE   4096
 Maximum size of a single log message (including formatting)
 
#define LOG_MMAP_MSG_BUFFER_SIZE   1024
 Maximum size of a log message in mmap mode.
 
#define LOG_HEADER_BUFFER_SIZE   512
 Maximum size of a log header (timestamp, level, file:line:func)
 
#define LOG_TIMESTAMP_BUFFER_SIZE   32
 Maximum size of a timestamp string.
 
#define LOG_COMPILE_LEVEL   LOG_DEV
 Compile-time minimum log level (DEV keeps all, allowing runtime -vvv)
 
#define log_every(log_level, interval_us, fmt, ...)
 Rate-limited logging macro (thread-safe)
 
#define LOG_CLIENT_IMPL(client, level, fmt, ...)
 
#define log_debug_client(client, fmt, ...)   LOG_CLIENT_IMPL(client, LOG_DEBUG, fmt, ##__VA_ARGS__)
 Server sends DEBUG log message to client.
 
#define log_info_client(client, fmt, ...)   LOG_CLIENT_IMPL(client, LOG_INFO, fmt, ##__VA_ARGS__)
 Server sends INFO log message to client.
 
#define log_warn_client(client, fmt, ...)   LOG_CLIENT_IMPL(client, LOG_WARN, fmt, ##__VA_ARGS__)
 Server sends WARN log message to client.
 
#define log_error_client(client, fmt, ...)   LOG_CLIENT_IMPL(client, LOG_ERROR, fmt, ##__VA_ARGS__)
 Server sends ERROR log message to client.
 
#define log_fatal_client(client, fmt, ...)   LOG_CLIENT_IMPL(client, LOG_FATAL, fmt, ##__VA_ARGS__)
 Server sends FATAL log message to client.
 
#define LOG_SERVER_IMPL(sockfd, crypto_ctx, level, fmt, ...)
 
#define log_debug_server(sockfd, crypto_ctx, fmt, ...)    LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_DEBUG, fmt, ##__VA_ARGS__)
 Client sends DEBUG log message to server.
 
#define log_info_server(sockfd, crypto_ctx, fmt, ...)   LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_INFO, fmt, ##__VA_ARGS__)
 Client sends INFO log message to server.
 
#define log_warn_server(sockfd, crypto_ctx, fmt, ...)   LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_WARN, fmt, ##__VA_ARGS__)
 Client sends WARN log message to server.
 
#define log_error_server(sockfd, crypto_ctx, fmt, ...)    LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_ERROR, fmt, ##__VA_ARGS__)
 Client sends ERROR log message to server.
 
#define log_fatal_server(sockfd, crypto_ctx, fmt, ...)    LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_FATAL, fmt, ##__VA_ARGS__)
 Client sends FATAL log message to server.
 

Typedefs

typedef struct log_mmap_config log_mmap_config_t
 
typedef enum remote_log_direction remote_log_direction_t
 Remote log packet direction enumeration.
 

Enumerations

enum  log_level_t {
  LOG_DEV = 0 , LOG_DEBUG , LOG_INFO , LOG_WARN ,
  LOG_ERROR , LOG_FATAL
}
 Logging levels enumeration. More...
 
enum  log_color_t {
  LOG_COLOR_DEV = 0 , LOG_COLOR_DEBUG = 1 , LOG_COLOR_INFO = 2 , LOG_COLOR_WARN = 3 ,
  LOG_COLOR_ERROR = 4 , LOG_COLOR_FATAL = 5 , LOG_COLOR_RESET = 6
}
 Color enum for logging - indexes into color arrays. More...
 
enum  remote_log_direction { REMOTE_LOG_DIRECTION_UNKNOWN = 0 , REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT = 1 , REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER = 2 }
 Remote log packet direction enumeration. More...
 

Functions

void log_init (const char *filename, log_level_t level, bool force_stderr, bool use_mmap)
 Initialize the logging system.
 
void log_destroy (void)
 Destroy the logging system and close log file.
 
void log_set_level (log_level_t level)
 Set the minimum log level.
 
log_level_t log_get_level (void)
 Get the current minimum log level.
 
void log_set_terminal_output (bool enabled)
 Control stderr output to terminal.
 
bool log_get_terminal_output (void)
 Get current terminal output setting.
 
void log_set_force_stderr (bool enabled)
 Force all terminal log output to stderr.
 
bool log_get_force_stderr (void)
 Get current force_stderr setting.
 
void log_truncate_if_large (void)
 Manually truncate large log files.
 
void log_msg (log_level_t level, const char *file, int line, const char *func, const char *fmt,...)
 Log a message at a specific level.
 
void log_plain_msg (const char *fmt,...)
 Plain logging without timestamps or levels.
 
void log_plain_stderr_msg (const char *fmt,...)
 Plain logging to stderr with newline.
 
void log_plain_stderr_nonewline_msg (const char *fmt,...)
 Plain logging to stderr without trailing newline.
 
void log_file_msg (const char *fmt,...)
 Log to file only, no stderr output.
 
void log_labeled (const char *label, log_color_t color, const char *message,...)
 Print a labeled message with color.
 
const char * log_level_color (log_color_t color)
 Get color string for a given color enum.
 
const char ** log_get_color_array (void)
 Get the appropriate color array based on terminal capabilities.
 
void log_redetect_terminal_capabilities (void)
 Re-detect terminal capabilities after logging is initialized.
 
bool log_lock_terminal (void)
 Lock terminal output for exclusive access by the calling thread.
 
void log_unlock_terminal (bool previous_state)
 Release terminal lock and flush buffered messages.
 
void log_set_flush_delay (unsigned int delay_ms)
 Set the delay between flushing buffered log entries.
 
char * format_message (const char *format, va_list args)
 Format a message using va_list.
 
size_t get_current_time_formatted (char *time_buf)
 Get current time as formatted string.
 
asciichat_error_t log_network_message (socket_t sockfd, const struct crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, const char *fmt,...)
 Send a formatted log message over the network.
 
asciichat_error_t log_net_message (socket_t sockfd, const struct crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, const char *file, int line, const char *func, const char *fmt,...)
 Log a message to all destinations (network, file, and terminal).
 
asciichat_error_t log_enable_mmap (const char *log_path)
 Enable lock-free mmap-based logging.
 
asciichat_error_t log_enable_mmap_sized (const char *log_path, size_t max_size)
 Enable lock-free mmap logging with custom file size.
 
void log_disable_mmap (void)
 Disable mmap logging and return to mutex-based logging.
 
void log_shutdown_begin (void)
 Begin shutdown phase - disable console logging but keep file logging.
 
void log_shutdown_end (void)
 End shutdown phase - restore previous logging settings.
 

Logging Macros

Note
Compile-time log level stripping: In release builds, log_dev() and log_debug() are compiled out completely (no runtime overhead). Override with LOG_COMPILE_LEVEL.
#define log_dev(...)   log_msg(LOG_DEV, __FILE__, __LINE__, __func__, __VA_ARGS__)
 Log a DEV message (most verbose, development only)
 
#define log_debug(...)   log_msg(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)
 Log a DEBUG message.
 
#define log_info(...)   log_msg(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)
 Log an INFO message.
 
#define log_warn(...)   log_msg(LOG_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__)
 Log a WARN message.
 
#define log_error(...)   log_msg(LOG_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__)
 Log an ERROR message.
 
#define log_fatal(...)   log_msg(LOG_FATAL, __FILE__, __LINE__, __func__, __VA_ARGS__)
 Log a FATAL message.
 
#define log_plain(...)   log_plain_msg(__VA_ARGS__)
 Plain logging - writes to both log file and stderr without timestamps or log levels.
 
#define log_plain_stderr(...)   log_plain_stderr_msg(__VA_ARGS__)
 Plain logging to stderr with newline.
 
#define log_plain_stderr_nonewline(...)   log_plain_stderr_nonewline_msg(__VA_ARGS__)
 Plain logging to stderr without newline - for interactive prompts.
 
#define log_file(...)   log_file_msg(__VA_ARGS__)
 File-only logging - writes to log file only, no stderr output.
 

Rate-Limited Logging Macros

#define log_dev_every(interval_us, fmt, ...)   log_every(DEV, interval_us, fmt, ##__VA_ARGS__)
 Rate-limited DEV logging.
 
#define log_debug_every(interval_us, fmt, ...)   log_every(DEBUG, interval_us, fmt, ##__VA_ARGS__)
 Rate-limited DEBUG logging.
 
#define log_info_every(interval_us, fmt, ...)   log_every(INFO, interval_us, fmt, ##__VA_ARGS__)
 Rate-limited INFO logging.
 
#define log_warn_every(interval_us, fmt, ...)   log_every(WARN, interval_us, fmt, ##__VA_ARGS__)
 Rate-limited WARN logging.
 
#define log_error_every(interval_us, fmt, ...)   log_every(ERROR, interval_us, fmt, ##__VA_ARGS__)
 Rate-limited ERROR logging.
 
#define log_fatal_every(interval_us, fmt, ...)   log_every(FATAL, interval_us, fmt, ##__VA_ARGS__)
 Rate-limited FATAL logging.
 

Detailed Description

📝 Logging API with multiple log levels and terminal output control

This header provides a comprehensive logging system with:

Note
In debug builds, log macros include file/line/function information. In release builds, this information is omitted.

Logging README

Overview

Welcome to the logging system! If you've ever tried to debug a complex program without good logging, you know the pain. The logging system is your debugging best friend—it helps you understand what's happening in your application, track down bugs, and monitor behavior in production.

Think of logging as your application's diary. It writes down everything that happens: when clients connect, when errors occur, when packets are processed. You can configure how chatty it should be (from super verbose DEBUG mode to quiet ERROR-only mode), and you can send the output to the console, to a file, or both!

Implementation: lib/logging.h

What makes this logging system helpful?

  • Six log levels: DEV, DEBUG, INFO, WARN, ERROR, FATAL (choose your verbosity)
  • Rate limiting: Prevents log spam in tight loops (your disk will thank you)
  • Flexible output: Console, file, or both simultaneously
  • Thread-safe: Multiple threads can log without stepping on each other
  • Context tracking: Automatic timestamps and source location (file:line)
  • Pretty colors: Colored console output so errors jump out at you
  • Runtime configuration: Change log level on the fly via environment variable

Log Levels

The logging system has six levels, from most verbose to least:

DEV (0) - The super chatty one:

  • Use this for development-only messages that are too verbose for normal debugging
  • Useful for tracing code paths during active development
  • Usually disabled even in debug builds unless you need extreme detail

DEBUG (1) - The chatty one:

  • Use this when you need detailed diagnostic information
  • Only available in debug builds (stripped out in release for performance)
  • Perfect for understanding exactly what's happening step by step

INFO (2) - The reporter:

  • General informational messages about normal operations
  • "Client connected", "Frame processed", that sort of thing
  • What you'd want to see during normal operation

WARN (3) - The concerned citizen:

  • Something's not quite right, but we can keep going
  • "Buffer is getting full", "Retrying connection"
  • Pay attention, but don't panic

ERROR (4) - Houston, we have a problem:

  • Operation failures that are recoverable
  • "Failed to send packet", "Client disconnected unexpectedly"
  • Things went wrong, but we're handling it

FATAL (5) - The showstopper:

  • Unrecoverable errors that mean we can't continue
  • "Cannot initialize crypto", "Out of memory"
  • Time to gracefully shut down

Basic Usage

Basic Logging

Log Messages:

log_debug("Processing frame %d", frame_number);
log_info("Client connected: %s:%d", ip, port);
log_warn("Buffer nearly full: %zu/%zu", used, capacity);
log_error("Failed to send packet: %s", error_msg);
log_fatal("Cannot initialize crypto: %s", reason);
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
#define log_fatal(...)
Log a FATAL message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.

Format Specifiers:

log_info("Integer: %d", 42);
log_info("String: %s", "hello");
log_info("Pointer: %p", ptr);
log_info("Size: %zu bytes", size);
log_info("Hex: 0x%08x", value);

Rate-Limited Logging

Rate Limiting (prevents log spam in tight loops):

// Log at most once per second (1,000,000 microseconds)
for (int i = 0; i < 1000000; i++) {
log_debug_every(1000000, "Processing item %d", i);
}
// Log at most once per 5 seconds
while (video_running) {
log_info_every(5000000, "Video frames processed: %d", frame_count);
process_frame();
}
#define log_info_every(interval_us, fmt,...)
Rate-limited INFO logging.
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.

Available Rate-Limited Macros:

Configuration

Setting Log Level

Runtime Configuration:

// Set log level to DEBUG
set_log_level(LOG_LEVEL_DEBUG);
// Set log level to INFO (hide debug messages)
set_log_level(LOG_LEVEL_INFO);
// Set log level to ERROR (only errors and fatal)
set_log_level(LOG_LEVEL_ERROR);

Environment Variable:

# Set log level via environment
export LOG_LEVEL=0 # DEV
export LOG_LEVEL=1 # DEBUG
export LOG_LEVEL=2 # INFO
export LOG_LEVEL=3 # WARN
export LOG_LEVEL=4 # ERROR
export LOG_LEVEL=5 # FATAL
# Run with debug logging
LOG_LEVEL=1 ./bin/ascii-chat server

File Output

Configure File Logging:

// Enable logging to file
set_log_file("/tmp/ascii-chat-server.log");
// Logs now go to both console and file
log_info("This appears in both console and file");
// Close log file
close_log_file();

Command-line Option:

# Log to file via command line
./bin/ascii-chat server --log-file=/tmp/server.log
./bin/ascii-chat client --log-file=/tmp/client.log

Output Format

Log messages have a consistent format that makes them easy to read and understand. Every message includes a timestamp, level, source location, and the actual message text. Let's look at the format:

What does a log message look like?

Log Message Format:

[TIMESTAMP] LEVEL [file:line] message

The format is simple and consistent—timestamp first (so you can see when things happened), level next (so you know how important it is), source location (so you know where in the code it came from), and the message itself.

Example Output:

[2025-01-15 14:23:45] DEBUG [server.c:234] Starting video capture thread
[2025-01-15 14:23:45] INFO [network.c:456] Client connected: 192.168.1.100:54321
[2025-01-15 14:23:46] WARN [audio.c:123] Audio buffer 80% full
[2025-01-15 14:23:47] ERROR [crypto.c:789] Handshake failed: invalid signature

Notice how you can see exactly when things happened, what level they are, where in the code they came from, and what the message is. This makes debugging much easier!

What about colors?

Console Colors (ANSI terminals):

  • DEV: Dim Gray (even more subtle, for development tracing)
  • DEBUG: Gray (subtle, so it doesn't distract from important messages)
  • INFO: White (default color, for normal messages)
  • WARN: Yellow (gets your attention, but not too alarming)
  • ERROR: Red (you should pay attention to this!)
  • FATAL: Bold Red (this is serious!)

Colors make it easy to scan logs—errors jump out in red, warnings in yellow. If you're in a terminal that supports colors, this makes debugging much easier.

Thread Safety

Mutex Protection:

  • All logging operations are protected by a global mutex
  • Safe for concurrent logging from multiple threads
  • No output interleaving

Thread Identification:

  • Thread ID automatically captured (debug builds)
  • Helps trace multi-threaded execution

Example Multi-threaded Logging:

void* worker_thread(void *arg) {
log_info("Worker thread started"); // Thread-safe
for (int i = 0; i < 1000; i++) {
// Rate-limited, thread-safe
log_debug_every(1000000, "Processing item %d", i);
}
log_info("Worker thread finished");
return NULL;
}

Performance Considerations

Logging performance is important—you don't want logging to slow down your application. We've designed the system to be efficient, but there are some things to keep in mind:

How do we keep logging fast?

Rate Limiting:

  • Use log_*_every() in high-frequency code paths: If you're logging in a tight loop, use rate limiting so you don't flood the logs
  • Prevents log spam and performance degradation: Without rate limiting, logging can slow down your application significantly
  • Minimal overhead when rate limit is active: Once the rate limit is active, logging is essentially free (we just check if enough time has passed, then skip logging)

Rate limiting is your friend in tight loops—you can log every iteration without actually logging every iteration!

How about file I/O?

File I/O:

  • File logging adds I/O overhead: Writing to a file is slower than writing to console, but it's still pretty fast for most use cases
  • Consider async file writing for high-throughput: If you're logging a lot (thousands of messages per second), you might want async file writing
  • Use buffered output for better performance: We use buffered I/O, so multiple log messages can be written in one operation

File logging is pretty fast—we use buffered I/O, so most of the time you won't notice the overhead. If you're logging hundreds of messages per second, you might want to consider rate limiting or async logging.

What about string formatting?

String Formatting:

  • Avoid expensive formatting in hot paths: String formatting (like sprintf()) can be slow if you're doing it thousands of times per second
  • Use rate limiting for frequent messages: If you're logging frequently, rate limiting reduces the number of format operations
  • Consider conditional compilation for debug logs: Debug logs can be compiled out entirely in release builds, so they have zero overhead

String formatting is usually pretty fast, but if you're doing it millions of times per second, it can add up. Use rate limiting for frequent messages, and remember that debug logs are compiled out in release builds!

Best Practices

DO:

  • Use appropriate log levels
  • Use rate limiting in tight loops
  • Include relevant context (IDs, values)
  • Use structured messages
  • Log errors with error codes

DON'T:

  • Don't use printf() directly (use log_*() instead)
  • Don't log in tight loops without rate limiting
  • Don't log sensitive data (passwords, keys)
  • Don't use DEBUG level for production
  • Don't log every iteration of a loop

Examples:

// ✅ GOOD - Rate limited
for (int i = 0; i < 1000000; i++) {
log_debug_every(1000000, "Processed %d items", i);
}
// ❌ BAD - Logs 1 million times!
for (int i = 0; i < 1000000; i++) {
log_debug("Processing item %d", i);
}
// ✅ GOOD - Contextual error message
log_error("Failed to send packet to client %u: %s", client_id, error_msg);
// ❌ BAD - Generic message
log_error("Send failed");

Integration with Error System

Automatic Error Logging:

  • SET_ERRNO() macros automatically log errors
  • Error context included in log messages
  • File/line/function captured automatically

Example:

if (socket_fd < 0) {
// Automatically logs: "ERROR: Failed to create socket"
return SET_ERRNO(ERROR_NETWORK_SOCKET, "Failed to create socket");
}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
See also
logging.h
asciichat_errno.h
common.h

Macro Definition Documentation

◆ DEFAULT_LOG_LEVEL

#define DEFAULT_LOG_LEVEL   LOG_DEBUG

#include <logging.h>

Default log level for debug builds (DEBUG and above)

Definition at line 75 of file log/logging.h.

◆ LOG_ATOMIC_UINT64

#define LOG_ATOMIC_UINT64   _Atomic uint64_t

#include <logging.h>

Definition at line 42 of file log/logging.h.

◆ LOG_ATOMIC_UINT64_INIT

#define LOG_ATOMIC_UINT64_INIT (   val)    val

#include <logging.h>

Definition at line 44 of file log/logging.h.

◆ LOG_CLIENT_IMPL

#define LOG_CLIENT_IMPL (   client,
  level,
  fmt,
  ... 
)

#include <logging.h>

Value:
do { \
if ((client)->crypto_initialized) { \
const struct crypto_context_t *_ctx = crypto_handshake_get_context(&(client)->crypto_handshake_ctx); \
log_net_message((client)->socket, _ctx, level, REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT, __FILE__, __LINE__, \
__func__, fmt, ##__VA_ARGS__); \
} \
} while (0)
const crypto_context_t * crypto_handshake_get_context(const crypto_handshake_context_t *ctx)
Get the crypto context for encryption/decryption.
@ REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT
Cryptographic context structure.

Definition at line 55 of file network/logging.h.

56 { \
57 if ((client)->crypto_initialized) { \
58 const struct crypto_context_t *_ctx = crypto_handshake_get_context(&(client)->crypto_handshake_ctx); \
59 log_net_message((client)->socket, _ctx, level, REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT, __FILE__, __LINE__, \
60 __func__, fmt, ##__VA_ARGS__); \
61 } \
62 } while (0)

◆ LOG_COMPILE_LEVEL

#define LOG_COMPILE_LEVEL   LOG_DEV

#include <logging.h>

Compile-time minimum log level (DEV keeps all, allowing runtime -vvv)

Definition at line 119 of file log/logging.h.

◆ log_debug

#define log_debug (   ...)    log_msg(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)

#include <logging.h>

Log a DEBUG message.

Parameters
...Format string and arguments (printf-style)
Note
DEBUG messages are stripped at compile-time in release builds.
In debug builds, includes file/line/function.

Definition at line 450 of file log/logging.h.

◆ log_debug_client

#define log_debug_client (   client,
  fmt,
  ... 
)    LOG_CLIENT_IMPL(client, LOG_DEBUG, fmt, ##__VA_ARGS__)

#include <logging.h>

Server sends DEBUG log message to client.

Definition at line 66 of file network/logging.h.

◆ log_debug_every

#define log_debug_every (   interval_us,
  fmt,
  ... 
)    log_every(DEBUG, interval_us, fmt, ##__VA_ARGS__)

#include <logging.h>

Rate-limited DEBUG logging.

Definition at line 604 of file log/logging.h.

◆ log_debug_server

#define log_debug_server (   sockfd,
  crypto_ctx,
  fmt,
  ... 
)     LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_DEBUG, fmt, ##__VA_ARGS__)

#include <logging.h>

Client sends DEBUG log message to server.

Definition at line 92 of file network/logging.h.

◆ log_dev

#define log_dev (   ...)    log_msg(LOG_DEV, __FILE__, __LINE__, __func__, __VA_ARGS__)

#include <logging.h>

Log a DEV message (most verbose, development only)

Parameters
...Format string and arguments (printf-style)
Note
DEV messages are stripped at compile-time in release builds.
In debug builds, includes file/line/function.

Definition at line 432 of file log/logging.h.

◆ log_dev_every

#define log_dev_every (   interval_us,
  fmt,
  ... 
)    log_every(DEV, interval_us, fmt, ##__VA_ARGS__)

#include <logging.h>

Rate-limited DEV logging.

Definition at line 601 of file log/logging.h.

◆ log_error

#define log_error (   ...)    log_msg(LOG_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__)

#include <logging.h>

Log an ERROR message.

Parameters
...Format string and arguments (printf-style)
Note
ERROR messages can be stripped via LOG_COMPILE_LEVEL=LOG_FATAL.

Definition at line 501 of file log/logging.h.

◆ log_error_client

#define log_error_client (   client,
  fmt,
  ... 
)    LOG_CLIENT_IMPL(client, LOG_ERROR, fmt, ##__VA_ARGS__)

#include <logging.h>

Server sends ERROR log message to client.

Definition at line 75 of file network/logging.h.

◆ log_error_every

#define log_error_every (   interval_us,
  fmt,
  ... 
)    log_every(ERROR, interval_us, fmt, ##__VA_ARGS__)

#include <logging.h>

Rate-limited ERROR logging.

Definition at line 613 of file log/logging.h.

◆ log_error_server

#define log_error_server (   sockfd,
  crypto_ctx,
  fmt,
  ... 
)     LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_ERROR, fmt, ##__VA_ARGS__)

#include <logging.h>

Client sends ERROR log message to server.

Definition at line 102 of file network/logging.h.

◆ log_every

#define log_every (   log_level,
  interval_us,
  fmt,
  ... 
)

#include <logging.h>

Value:
do { \
static LOG_ATOMIC_UINT64 _log_every_last_time = LOG_ATOMIC_UINT64_INIT(0); \
uint64_t _log_every_last = atomic_load_explicit(&_log_every_last_time, memory_order_relaxed); \
if (_log_every_now - _log_every_last >= (uint64_t)(interval_us)) { \
if (atomic_compare_exchange_weak_explicit(&_log_every_last_time, &_log_every_last, _log_every_now, \
memory_order_relaxed, memory_order_relaxed)) { \
log_msg(LOG_##log_level, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__); \
} \
} \
} while (0)
unsigned long long uint64_t
Definition common.h:59
#define LOG_ATOMIC_UINT64
Definition log/logging.h:42
#define LOG_ATOMIC_UINT64_INIT(val)
Definition log/logging.h:44
uint64_t platform_get_monotonic_time_us(void)
Get monotonic time in microseconds.

Rate-limited logging macro (thread-safe)

Logs at most once per specified time interval. Useful for threads that have an FPS and functions they call to prevent spammy logs.

Parameters
log_levelLog level (DEV, DEBUG, INFO, WARN, ERROR, FATAL)
interval_usMinimum microseconds between log messages
fmtFormat string (printf-style)
...Format arguments
Note
Each call site maintains its own static atomic timer, so different call sites can log independently. Thread-safe via atomic compare-exchange.
Uses platform_get_monotonic_time_us() for cross-platform time.

Definition at line 581 of file log/logging.h.

582 { \
583 static LOG_ATOMIC_UINT64 _log_every_last_time = LOG_ATOMIC_UINT64_INIT(0); \
584 uint64_t _log_every_now = platform_get_monotonic_time_us(); \
585 uint64_t _log_every_last = atomic_load_explicit(&_log_every_last_time, memory_order_relaxed); \
586 if (_log_every_now - _log_every_last >= (uint64_t)(interval_us)) { \
587 if (atomic_compare_exchange_weak_explicit(&_log_every_last_time, &_log_every_last, _log_every_now, \
588 memory_order_relaxed, memory_order_relaxed)) { \
589 log_msg(LOG_##log_level, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__); \
590 } \
591 } \
592 } while (0)

◆ log_fatal

#define log_fatal (   ...)    log_msg(LOG_FATAL, __FILE__, __LINE__, __func__, __VA_ARGS__)

#include <logging.h>

Log a FATAL message.

Parameters
...Format string and arguments (printf-style)
Note
FATAL messages are never stripped (always compiled in).

Definition at line 517 of file log/logging.h.

◆ log_fatal_client

#define log_fatal_client (   client,
  fmt,
  ... 
)    LOG_CLIENT_IMPL(client, LOG_FATAL, fmt, ##__VA_ARGS__)

#include <logging.h>

Server sends FATAL log message to client.

Definition at line 78 of file network/logging.h.

◆ log_fatal_every

#define log_fatal_every (   interval_us,
  fmt,
  ... 
)    log_every(FATAL, interval_us, fmt, ##__VA_ARGS__)

#include <logging.h>

Rate-limited FATAL logging.

Definition at line 616 of file log/logging.h.

◆ log_fatal_server

#define log_fatal_server (   sockfd,
  crypto_ctx,
  fmt,
  ... 
)     LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_FATAL, fmt, ##__VA_ARGS__)

#include <logging.h>

Client sends FATAL log message to server.

Definition at line 106 of file network/logging.h.

◆ log_file

#define log_file (   ...)    log_file_msg(__VA_ARGS__)

#include <logging.h>

File-only logging - writes to log file only, no stderr output.

Parameters
...Format string and arguments (printf-style)

Definition at line 546 of file log/logging.h.

◆ LOG_HEADER_BUFFER_SIZE

#define LOG_HEADER_BUFFER_SIZE   512

#include <logging.h>

Maximum size of a log header (timestamp, level, file:line:func)

Definition at line 101 of file log/logging.h.

◆ log_info

#define log_info (   ...)    log_msg(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)

#include <logging.h>

Log an INFO message.

Parameters
...Format string and arguments (printf-style)
Note
INFO messages can be stripped via LOG_COMPILE_LEVEL=LOG_WARN.

Definition at line 467 of file log/logging.h.

◆ log_info_client

#define log_info_client (   client,
  fmt,
  ... 
)    LOG_CLIENT_IMPL(client, LOG_INFO, fmt, ##__VA_ARGS__)

#include <logging.h>

Server sends INFO log message to client.

Definition at line 69 of file network/logging.h.

◆ log_info_every

#define log_info_every (   interval_us,
  fmt,
  ... 
)    log_every(INFO, interval_us, fmt, ##__VA_ARGS__)

#include <logging.h>

Rate-limited INFO logging.

Definition at line 607 of file log/logging.h.

◆ log_info_server

#define log_info_server (   sockfd,
  crypto_ctx,
  fmt,
  ... 
)    LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_INFO, fmt, ##__VA_ARGS__)

#include <logging.h>

Client sends INFO log message to server.

Definition at line 96 of file network/logging.h.

◆ LOG_MMAP_MSG_BUFFER_SIZE

#define LOG_MMAP_MSG_BUFFER_SIZE   1024

#include <logging.h>

Maximum size of a log message in mmap mode.

Definition at line 98 of file log/logging.h.

◆ LOG_MSG_BUFFER_SIZE

#define LOG_MSG_BUFFER_SIZE   4096

#include <logging.h>

Maximum size of a single log message (including formatting)

Definition at line 95 of file log/logging.h.

◆ log_plain

#define log_plain (   ...)    log_plain_msg(__VA_ARGS__)

#include <logging.h>

Plain logging - writes to both log file and stderr without timestamps or log levels.

Parameters
...Format string and arguments (printf-style)

Definition at line 525 of file log/logging.h.

◆ log_plain_stderr

#define log_plain_stderr (   ...)    log_plain_stderr_msg(__VA_ARGS__)

#include <logging.h>

Plain logging to stderr with newline.

Parameters
...Format string and arguments (printf-style)

Definition at line 532 of file log/logging.h.

◆ log_plain_stderr_nonewline

#define log_plain_stderr_nonewline (   ...)    log_plain_stderr_nonewline_msg(__VA_ARGS__)

#include <logging.h>

Plain logging to stderr without newline - for interactive prompts.

Parameters
...Format string and arguments (printf-style)

Definition at line 539 of file log/logging.h.

◆ LOG_SERVER_IMPL

#define LOG_SERVER_IMPL (   sockfd,
  crypto_ctx,
  level,
  fmt,
  ... 
)

#include <logging.h>

Value:
log_net_message(sockfd, (const struct crypto_context_t *)(crypto_ctx), level, REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER, \
__FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)
asciichat_error_t log_net_message(socket_t sockfd, const struct crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, const char *file, int line, const char *func, const char *fmt,...)
Log a message to all destinations (network, file, and terminal).
@ REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER

Definition at line 86 of file network/logging.h.

◆ LOG_TIMESTAMP_BUFFER_SIZE

#define LOG_TIMESTAMP_BUFFER_SIZE   32

#include <logging.h>

Maximum size of a timestamp string.

Definition at line 104 of file log/logging.h.

◆ log_warn

#define log_warn (   ...)    log_msg(LOG_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__)

#include <logging.h>

Log a WARN message.

Parameters
...Format string and arguments (printf-style)
Note
WARN messages can be stripped via LOG_COMPILE_LEVEL=LOG_ERROR.

Definition at line 484 of file log/logging.h.

◆ log_warn_client

#define log_warn_client (   client,
  fmt,
  ... 
)    LOG_CLIENT_IMPL(client, LOG_WARN, fmt, ##__VA_ARGS__)

#include <logging.h>

Server sends WARN log message to client.

Definition at line 72 of file network/logging.h.

◆ log_warn_every

#define log_warn_every (   interval_us,
  fmt,
  ... 
)    log_every(WARN, interval_us, fmt, ##__VA_ARGS__)

#include <logging.h>

Rate-limited WARN logging.

Definition at line 610 of file log/logging.h.

◆ log_warn_server

#define log_warn_server (   sockfd,
  crypto_ctx,
  fmt,
  ... 
)    LOG_SERVER_IMPL(sockfd, crypto_ctx, LOG_WARN, fmt, ##__VA_ARGS__)

#include <logging.h>

Client sends WARN log message to server.

Definition at line 99 of file network/logging.h.

◆ MAX_LOG_SIZE

#define MAX_LOG_SIZE   (3 * 1024 * 1024)

#include <logging.h>

Maximum log file size in bytes (3MB) before rotation.

Definition at line 79 of file log/logging.h.

◆ MAX_TERMINAL_BUFFER_ENTRIES

#define MAX_TERMINAL_BUFFER_ENTRIES   256

#include <logging.h>

Maximum number of buffered log entries.

Definition at line 85 of file log/logging.h.

◆ MAX_TERMINAL_BUFFER_SIZE

#define MAX_TERMINAL_BUFFER_SIZE   (64 * 1024)

#include <logging.h>

Maximum size of terminal output buffer (64KB)

Definition at line 82 of file log/logging.h.

Typedef Documentation

◆ log_mmap_config_t

#include <logging.h>

Definition at line 632 of file log/logging.h.

◆ remote_log_direction_t

#include <logging.h>

Remote log packet direction enumeration.

Indicates the originator of a remote log message so receivers can annotate logs clearly.

Note
This typedef MUST be defined before includes to avoid circular dependency issues with packet.h

Enumeration Type Documentation

◆ log_color_t

#include <logging.h>

Color enum for logging - indexes into color arrays.

These values directly index into level_colors arrays. Order matches DEV, DEBUG, INFO, WARN, ERROR, FATAL, RESET.

Enumerator
LOG_COLOR_DEV 

Blue - DEV messages

LOG_COLOR_DEBUG 

Cyan - DEBUG messages

LOG_COLOR_INFO 

Green - INFO messages

LOG_COLOR_WARN 

Yellow - WARN messages

LOG_COLOR_ERROR 

Red - ERROR messages

LOG_COLOR_FATAL 

Magenta - FATAL messages

LOG_COLOR_RESET 

Reset to default

Definition at line 137 of file log/logging.h.

137 {
138 LOG_COLOR_DEV = 0,
139 LOG_COLOR_DEBUG = 1,
140 LOG_COLOR_INFO = 2,
141 LOG_COLOR_WARN = 3,
142 LOG_COLOR_ERROR = 4,
143 LOG_COLOR_FATAL = 5,
144 LOG_COLOR_RESET = 6
log_color_t
Color enum for logging - indexes into color arrays.
@ LOG_COLOR_RESET
@ LOG_COLOR_FATAL
@ LOG_COLOR_ERROR
@ LOG_COLOR_DEV
@ LOG_COLOR_DEBUG
@ LOG_COLOR_INFO
@ LOG_COLOR_WARN

◆ log_level_t

#include <logging.h>

Logging levels enumeration.

Note
This typedef MUST be defined before #include "network/logging.h" to avoid circular dependency issues with packet.h
Enumerator
LOG_DEV 

Development messages (most verbose)

LOG_DEBUG 

Debug messages

LOG_INFO 

Informational messages

LOG_WARN 

Warning messages

LOG_ERROR 

Error messages

LOG_FATAL 

Fatal error messages (most severe)

Definition at line 59 of file log/logging.h.

59 {
60 LOG_DEV = 0,
61 LOG_DEBUG,
62 LOG_INFO,
63 LOG_WARN,
64 LOG_ERROR,
log_level_t
Logging levels enumeration.
Definition log/logging.h:59
@ LOG_ERROR
Definition log/logging.h:64
@ LOG_INFO
Definition log/logging.h:62
@ LOG_DEBUG
Definition log/logging.h:61
@ LOG_FATAL
Definition log/logging.h:65
@ LOG_WARN
Definition log/logging.h:63
@ LOG_DEV
Definition log/logging.h:60

◆ remote_log_direction

#include <logging.h>

Remote log packet direction enumeration.

Indicates the originator of a remote log message so receivers can annotate logs clearly.

Note
This typedef MUST be defined before includes to avoid circular dependency issues with packet.h
Enumerator
REMOTE_LOG_DIRECTION_UNKNOWN 
REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT 
REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER 

Definition at line 20 of file network/logging.h.

20 {
enum remote_log_direction remote_log_direction_t
Remote log packet direction enumeration.
@ REMOTE_LOG_DIRECTION_UNKNOWN

Function Documentation

◆ format_message()

char * format_message ( const char *  format,
va_list  args 
)

#include <logging.h>

Format a message using va_list.

Parameters
formatFormat string
argsVariable arguments list
Returns
Formatted message string (must be freed by caller)

Definition at line 157 of file log/logging.c.

157 {
158 if (!format) {
159 return NULL;
160 }
161
162 // First, determine the size needed
163 va_list args_copy;
164 va_copy(args_copy, args);
165 int size = vsnprintf(NULL, 0, format, args_copy);
166 va_end(args_copy);
167
168 if (size < 0) {
169 LOGGING_INTERNAL_ERROR(ERROR_INVALID_STATE, "Failed to format context message");
170 return NULL;
171 }
172
173 // Allocate and format the message
174 char *message = SAFE_MALLOC(size + 1, char *);
175 int result = vsnprintf(message, (size_t)size + 1, format, args);
176 if (result < 0) {
177 SAFE_FREE(message);
178 LOGGING_INTERNAL_ERROR(ERROR_INVALID_STATE, "Failed to format context message");
179 return NULL;
180 }
181
182 return message;
183}
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
@ ERROR_INVALID_STATE
#define LOGGING_INTERNAL_ERROR(error, message,...)

References ERROR_INVALID_STATE, LOGGING_INTERNAL_ERROR, SAFE_FREE, and SAFE_MALLOC.

Referenced by asciichat_fatal_with_context(), asciichat_set_errno_with_message(), asciichat_set_errno_with_system_error_and_message(), and log_labeled().

◆ get_current_time_formatted()

size_t get_current_time_formatted ( char *  time_buf)

#include <logging.h>

Get current time as formatted string.

Parameters
time_bufOutput buffer for formatted time
Returns
Number of characters written (excluding null terminator)

Definition at line 126 of file log/logging.c.

126 {
127 /* Log the rotation event */
128 struct timespec ts;
129 (void)clock_gettime(CLOCK_REALTIME, &ts);
130 struct tm tm_info;
131 platform_localtime(&ts.tv_sec, &tm_info);
132
133 // Format the time part first
134 // strftime returns 0 on error, not negative (and len is size_t/unsigned)
135 size_t len = strftime(time_buf, 32, "%H:%M:%S", &tm_info);
136 if (len == 0 || len >= 32) {
137 LOGGING_INTERNAL_ERROR(ERROR_INVALID_STATE, "Failed to format time");
138 return 0;
139 }
140
141 // Add microseconds manually
142 long microseconds = ts.tv_nsec / 1000;
143 if (microseconds < 0)
144 microseconds = 0;
145 if (microseconds > 999999)
146 microseconds = 999999;
147
148 int result = snprintf(time_buf + len, 32 - len, ".%06ld", microseconds);
149 if (result < 0 || result >= (int)(32 - len)) {
150 LOGGING_INTERNAL_ERROR(ERROR_INVALID_STATE, "Failed to format microseconds");
151 return 0;
152 }
153
154 return len + (size_t)result;
155}
asciichat_error_t platform_localtime(const time_t *timer, struct tm *result)
Platform-safe localtime wrapper.

References ERROR_INVALID_STATE, LOGGING_INTERNAL_ERROR, and platform_localtime().

Referenced by log_msg().

◆ log_destroy()

void log_destroy ( void  )

#include <logging.h>

Destroy the logging system and close log file.

Definition at line 459 of file log/logging.c.

459 {
460 // Destroy mmap logging first (if active)
461 if (log_mmap_is_active()) {
463 }
464
465 // Lock-free cleanup using atomic operations
466 int old_file = atomic_load(&g_log.file);
467 if (old_file >= 0 && old_file != STDERR_FILENO) {
468 platform_close(old_file);
469 }
470 atomic_store(&g_log.file, -1);
471 atomic_store(&g_log.initialized, false);
472
473 // Destroy rotation mutex
474 if (atomic_load(&g_log.rotation_mutex_initialized)) {
475 mutex_destroy(&g_log.rotation_mutex);
476 atomic_store(&g_log.rotation_mutex_initialized, false);
477 }
478}
int platform_close(int fd)
Safe file close (close replacement)
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
bool log_mmap_is_active(void)
Check if mmap logging is active.
Definition mmap.c:375
void log_mmap_destroy(void)
Shutdown mmap logging.
Definition mmap.c:257

References log_mmap_destroy(), log_mmap_is_active(), mutex_destroy(), and platform_close().

Referenced by main(), and server_main().

◆ log_disable_mmap()

void log_disable_mmap ( void  )

#include <logging.h>

Disable mmap logging and return to mutex-based logging.

Flushes remaining entries and closes the mmap file.

Definition at line 1129 of file log/logging.c.

1129 {
1130 if (log_mmap_is_active()) {
1132 log_info("Lock-free mmap logging disabled");
1133 }
1134}

References log_info, log_mmap_destroy(), and log_mmap_is_active().

◆ log_enable_mmap()

asciichat_error_t log_enable_mmap ( const char *  log_path)

#include <logging.h>

Enable lock-free mmap-based logging.

When enabled, log messages bypass the mutex and use atomic operations to write directly to a memory-mapped log file as human-readable text.

Benefits:

  • No mutex contention between logging threads
  • Crash-safe: text is written directly to mmap'd file, readable after crash
  • Fast path uses atomic fetch_add, no locks
  • ERROR/FATAL messages sync immediately for visibility
  • Simple: log file IS the mmap file (no separate binary format)
Parameters
log_pathPath to the log file (will be memory-mapped)
Returns
ASCIICHAT_OK on success, error code on failure
Note
Call log_init() first, then log_enable_mmap() to upgrade to lock-free

Definition at line 1110 of file log/logging.c.

1110 {
1111 return log_enable_mmap_sized(log_path, 0); /* Use default size */
1112}
asciichat_error_t log_enable_mmap_sized(const char *log_path, size_t max_size)
Enable lock-free mmap logging with custom file size.

References log_enable_mmap_sized().

◆ log_enable_mmap_sized()

asciichat_error_t log_enable_mmap_sized ( const char *  log_path,
size_t  max_size 
)

#include <logging.h>

Enable lock-free mmap logging with custom file size.

Parameters
log_pathPath to the log file
max_sizeMaximum file size in bytes (0 = default 4MB)
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 1114 of file log/logging.c.

1114 {
1115 if (!log_path) {
1116 return SET_ERRNO(ERROR_INVALID_PARAM, "log_path is required");
1117 }
1118
1119 // Initialize mmap logging - text is written directly to the mmap'd file
1120 asciichat_error_t result = log_mmap_init_simple(log_path, max_size);
1121 if (result != ASCIICHAT_OK) {
1122 return result;
1123 }
1124
1125 log_info("Lock-free mmap logging enabled: %s", log_path);
1126 return ASCIICHAT_OK;
1127}
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_INVALID_PARAM
asciichat_error_t log_mmap_init_simple(const char *log_path, size_t max_size)
Initialize mmap logging with simple parameters.
Definition mmap.c:249

References ASCIICHAT_OK, ERROR_INVALID_PARAM, log_info, log_mmap_init_simple(), and SET_ERRNO.

Referenced by log_enable_mmap().

◆ log_file_msg()

void log_file_msg ( const char *  fmt,
  ... 
)

#include <logging.h>

Log to file only, no stderr output.

Parameters
fmtFormat string (printf-style)
...Format arguments

Writes to log file only, without terminal output.

Definition at line 923 of file log/logging.c.

923 {
924 if (!atomic_load(&g_log.initialized)) {
925 return;
926 }
927
928 char log_buffer[LOG_MSG_BUFFER_SIZE];
929 va_list args;
930 va_start(args, fmt);
931 int msg_len = vsnprintf(log_buffer, sizeof(log_buffer), fmt, args);
932 va_end(args);
933
934 if (msg_len <= 0 || msg_len >= (int)sizeof(log_buffer)) {
935 return;
936 }
937
938 // Write to mmap if active, else to file
939 if (log_mmap_is_active()) {
940 log_mmap_write(LOG_INFO, NULL, 0, NULL, "%s", log_buffer);
941 } else {
942 int file_fd = atomic_load(&g_log.file);
943 if (file_fd >= 0 && file_fd != STDERR_FILENO) {
944 write_to_log_file_atomic(log_buffer, msg_len);
945 write_to_log_file_atomic("\n", 1);
946 }
947 }
948}
#define LOG_MSG_BUFFER_SIZE
Maximum size of a single log message (including formatting)
Definition log/logging.h:95
void log_mmap_write(int level, const char *file, int line, const char *func, const char *fmt,...)
Write a log entry directly to the mmap'd file (lock-free)
Definition mmap.c:309

References LOG_INFO, log_mmap_is_active(), log_mmap_write(), and LOG_MSG_BUFFER_SIZE.

Referenced by client_main().

◆ log_get_color_array()

const char ** log_get_color_array ( void  )

#include <logging.h>

Get the appropriate color array based on terminal capabilities.

Returns
Pointer to color array (16-color, 256-color, or truecolor)

Automatically detects terminal capabilities and returns the appropriate color array.

Definition at line 1079 of file log/logging.c.

1079 {
1080 init_terminal_capabilities();
1081
1082 log_color_mode_t mode;
1083 if (g_terminal_caps.color_level >= TERM_COLOR_TRUECOLOR) {
1084 mode = LOG_CMODE_TRUECOLOR;
1085 } else if (g_terminal_caps.color_level >= TERM_COLOR_256) {
1086 mode = LOG_CMODE_256;
1087 } else {
1088 mode = LOG_CMODE_16;
1089 }
1090
1091 /* Cast away const for the pointer-to-pointer return - the array is still const */
1092 return (const char **)level_colors[mode];
1093}
@ TERM_COLOR_256
256-color support (extended ANSI palette)
Definition terminal.h:432
@ TERM_COLOR_TRUECOLOR
24-bit truecolor support (RGB colors)
Definition terminal.h:434
log_color_mode_t
Definition log/logging.c:75
@ LOG_CMODE_256
Definition log/logging.c:75
@ LOG_CMODE_TRUECOLOR
Definition log/logging.c:75
@ LOG_CMODE_16
Definition log/logging.c:75
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
Definition terminal.h:487

References terminal_capabilities_t::color_level, LOG_CMODE_16, LOG_CMODE_256, LOG_CMODE_TRUECOLOR, TERM_COLOR_256, and TERM_COLOR_TRUECOLOR.

Referenced by log_level_color(), log_msg(), and platform_is_binary_in_path().

◆ log_get_force_stderr()

bool log_get_force_stderr ( void  )

#include <logging.h>

Get current force_stderr setting.

Returns
true if all logs are forced to stderr, false otherwise

Definition at line 510 of file log/logging.c.

510 {
511 return atomic_load(&g_log.force_stderr);
512}

◆ log_get_level()

log_level_t log_get_level ( void  )

#include <logging.h>

Get the current minimum log level.

Returns
Current log level

Definition at line 485 of file log/logging.c.

485 {
486 return (log_level_t)atomic_load(&g_log.level);
487}

Referenced by options_init().

◆ log_get_terminal_output()

bool log_get_terminal_output ( void  )

#include <logging.h>

Get current terminal output setting.

Returns
true if terminal output is enabled, false otherwise

Definition at line 502 of file log/logging.c.

502 {
503 return atomic_load(&g_log.terminal_output_enabled);
504}

◆ log_init()

void log_init ( const char *  filename,
log_level_t  level,
bool  force_stderr,
bool  use_mmap 
)

#include <logging.h>

Initialize the logging system.

Parameters
filenameLog file path (or NULL for no file logging)
levelMinimum log level to output
force_stderrIf true, route ALL logs to stderr (for client mode to keep stdout clean)
use_mmapIf true, use fully lock-free mmap logging (recommended). If mmap fails, uses stderr only (no mutex fallback).
Note
When use_mmap=true, the entire logging path is lock-free:
  • File output uses atomic operations on mmap'd memory
  • Terminal output uses atomic fprintf/fwrite to fd
  • No mutex is ever acquired in the hot path

Definition at line 386 of file log/logging.c.

386 {
387 // Initialize rotation mutex (only operation that uses a mutex)
388 if (!atomic_load(&g_log.rotation_mutex_initialized)) {
389 mutex_init(&g_log.rotation_mutex);
390 atomic_store(&g_log.rotation_mutex_initialized, true);
391 }
392
393 // Set basic config using atomic stores
394 atomic_store(&g_log.force_stderr, force_stderr);
395 bool preserve_terminal_output = atomic_load(&g_log.terminal_output_enabled);
396
397 // Close any existing file (atomic load/store)
398 int old_file = atomic_load(&g_log.file);
399 if (atomic_load(&g_log.initialized) && old_file >= 0 && old_file != STDERR_FILENO) {
400 platform_close(old_file);
401 atomic_store(&g_log.file, -1);
402 }
403
404 // Check LOG_LEVEL environment variable
405 const char *env_level_str = SAFE_GETENV("LOG_LEVEL");
406 if (env_level_str) {
407 atomic_store(&g_log.level, (int)parse_log_level_from_env());
408 } else {
409 atomic_store(&g_log.level, (int)level);
410 }
411
412 atomic_store(&g_log.level_manually_set, false);
413 atomic_store(&g_log.current_size, 0);
414
415 if (filename) {
416 SAFE_STRNCPY(g_log.filename, filename, sizeof(g_log.filename) - 1);
417
418 if (use_mmap) {
419 // Lock-free mmap path - writes go to mmap'd file
420 asciichat_error_t mmap_result = log_mmap_init_simple(filename, 0);
421 if (mmap_result == ASCIICHAT_OK) {
422 atomic_store(&g_log.file, -1); // No regular fd - using mmap for file output
423 } else {
424 // Mmap failed - use stderr only (atomic writes, lock-free)
425 if (preserve_terminal_output) {
426 safe_fprintf(stderr, "Mmap logging failed for %s, using stderr only (lock-free)\n", filename);
427 }
428 atomic_store(&g_log.file, STDERR_FILENO);
429 g_log.filename[0] = '\0';
430 }
431 } else {
432 // Lock-free file I/O path - uses atomic write() syscalls
433 int fd = platform_open(filename, O_CREAT | O_RDWR | O_TRUNC, FILE_PERM_PRIVATE);
434 atomic_store(&g_log.file, (fd >= 0) ? fd : STDERR_FILENO);
435 if (fd < 0) {
436 if (preserve_terminal_output) {
437 safe_fprintf(stderr, "Failed to open log file: %s\n", filename);
438 }
439 g_log.filename[0] = '\0';
440 }
441 }
442 } else {
443 atomic_store(&g_log.file, STDERR_FILENO);
444 g_log.filename[0] = '\0';
445 }
446
447 atomic_store(&g_log.initialized, true);
448 atomic_store(&g_log.terminal_output_enabled, preserve_terminal_output);
449
450 // Reset terminal detection if needed
451 if (g_terminal_caps_initialized && !g_terminal_caps.detection_reliable) {
452 g_terminal_caps_initialized = false;
453 }
454
455 // Detect terminal capabilities
457}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
#define SAFE_GETENV(name)
Definition common.h:378
void log_redetect_terminal_capabilities(void)
Re-detect terminal capabilities after logging is initialized.
int safe_fprintf(FILE *stream, const char *format,...)
Safe version of fprintf.
int mutex_init(mutex_t *mutex)
Initialize a mutex.
int platform_open(const char *pathname, int flags,...)
Safe file open (open replacement)
#define FILE_PERM_PRIVATE
File permission: Private (owner read/write only)
Definition system.h:637
bool detection_reliable
True if detection is confident (reliable detection)
Definition terminal.h:495

References ASCIICHAT_OK, terminal_capabilities_t::detection_reliable, FILE_PERM_PRIVATE, log_mmap_init_simple(), log_redetect_terminal_capabilities(), mutex_init(), platform_close(), platform_open(), safe_fprintf(), SAFE_GETENV, and SAFE_STRNCPY.

Referenced by asciichat_shared_init(), and main().

◆ log_labeled()

void log_labeled ( const char *  label,
log_color_t  color,
const char *  message,
  ... 
)

#include <logging.h>

Print a labeled message with color.

Parameters
labelThe label text to print (appears before the message)
colorColor for the label (from log_color_t enum)
messageFormat string (printf-style) for the message
...Format arguments

Used for consistent formatting of section headers and labeled output. The label is colored, followed by the message content. Output goes to both stderr and log file.

Definition at line 98 of file asciichat_errno.c.

98 {
99 va_list args;
100 va_start(args, message);
101 char *formatted_message = format_message(message, args);
102 va_end(args);
103
104 safe_fprintf(stderr, "%s%s%s: %s\n", log_level_color(color), label, log_level_color(LOG_COLOR_RESET),
105 formatted_message);
106
107 log_file("%s: %s", label, formatted_message);
108
109 SAFE_FREE(formatted_message);
110}
const char * log_level_color(log_color_t color)
Get color string for a given color enum.
char * format_message(const char *format, va_list args)
Format a message using va_list.
#define log_file(...)
File-only logging - writes to log file only, no stderr output.

References format_message(), LOG_COLOR_RESET, log_file, log_level_color(), safe_fprintf(), and SAFE_FREE.

Referenced by asciichat_fatal_with_context().

◆ log_level_color()

const char * log_level_color ( log_color_t  color)

#include <logging.h>

Get color string for a given color enum.

Parameters
colorColor enum value
Returns
ANSI color code string

Definition at line 1095 of file log/logging.c.

1095 {
1096 const char **colors = log_get_color_array();
1097 if (colors == NULL) {
1098 return ""; /* Return empty string if colors not available */
1099 }
1100 if (color >= 0 && color <= LOG_COLOR_RESET) {
1101 return colors[color];
1102 }
1103 return colors[LOG_COLOR_RESET]; /* Return reset color if invalid */
1104}
const char ** log_get_color_array(void)
Get the appropriate color array based on terminal capabilities.

References LOG_COLOR_RESET, and log_get_color_array().

Referenced by asciichat_print_error_context(), and log_labeled().

◆ log_lock_terminal()

bool log_lock_terminal ( void  )

#include <logging.h>

Lock terminal output for exclusive access by the calling thread.

Call this before interactive prompts (like password entry, yes/no questions) to ensure only the calling thread can output to the terminal. Other threads' log messages will be buffered and flushed when the terminal is unlocked.

While locked:

  • The locking thread can use log_plain() to write to terminal
  • Other threads' log messages go to log file and are buffered
  • Buffered messages are flushed to terminal on unlock

Must be paired with log_unlock_terminal().

Returns
The previous terminal lock state (for nested calls)

Definition at line 514 of file log/logging.c.

514 {
515 bool previous_state = atomic_exchange(&g_log.terminal_locked, true);
516 atomic_store(&g_log.terminal_owner_thread, (uint64_t)asciichat_thread_self());
517 return previous_state;
518}
thread_id_t asciichat_thread_self(void)
Get the current thread's ID.

References asciichat_thread_self().

Referenced by client_crypto_handshake(), client_main(), discovery_tui_select(), prompt_password(), and prompt_unknown_host().

◆ log_msg()

void log_msg ( log_level_t  level,
const char *  file,
int  line,
const char *  func,
const char *  fmt,
  ... 
)

#include <logging.h>

Log a message at a specific level.

Parameters
levelLog level (LOG_DEV, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL)
fileSource file name (or NULL to omit)
lineSource line number (or 0 to omit)
funcFunction name (or NULL to omit)
fmtFormat string (printf-style)
...Format arguments

Definition at line 695 of file log/logging.c.

695 {
696 // All state access uses atomic operations - fully lock-free
697 if (!atomic_load(&g_log.initialized)) {
698 return;
699 }
700
701 if (level < (log_level_t)atomic_load(&g_log.level)) {
702 return;
703 }
704
705 /* =========================================================================
706 * MMAP PATH: When mmap logging is active, writes go to mmap'd file
707 * ========================================================================= */
708 if (log_mmap_is_active()) {
709 maybe_rotate_log();
710
711 va_list args;
712 va_start(args, fmt);
713 char msg_buffer[LOG_MMAP_MSG_BUFFER_SIZE];
714 vsnprintf(msg_buffer, sizeof(msg_buffer), fmt, args);
715 va_end(args);
716
717 log_mmap_write(level, file, line, func, "%s", msg_buffer);
718
719 // Terminal output (check with atomic loads)
720 if (atomic_load(&g_log.terminal_output_enabled) && !atomic_load(&g_log.terminal_locked)) {
721 char time_buf[LOG_TIMESTAMP_BUFFER_SIZE];
723
724 FILE *output_stream;
725 if (atomic_load(&g_log.force_stderr)) {
726 output_stream = stderr;
727 } else {
728 output_stream = (level == LOG_ERROR || level == LOG_WARN || level == LOG_FATAL) ? stderr : stdout;
729 }
730 int fd = output_stream == stderr ? STDERR_FILENO : STDOUT_FILENO;
731 bool use_colors = isatty(fd);
732
733 char header_buffer[512];
734 int header_len =
735 format_log_header(header_buffer, sizeof(header_buffer), level, time_buf, file, line, func, use_colors, false);
736
737 if (header_len > 0 && header_len < (int)sizeof(header_buffer)) {
738 if (use_colors) {
739 const char **colors = log_get_color_array();
740 if (colors) {
741 safe_fprintf(output_stream, "%s%s%s%s\n", header_buffer, colors[LOG_COLOR_RESET], msg_buffer,
742 colors[LOG_COLOR_RESET]);
743 } else {
744 safe_fprintf(output_stream, "%s%s\n", header_buffer, msg_buffer);
745 }
746 } else {
747 safe_fprintf(output_stream, "%s%s\n", header_buffer, msg_buffer);
748 }
749 (void)fflush(output_stream);
750 }
751 }
752 return;
753 }
754
755 /* =========================================================================
756 * FILE I/O PATH: Lock-free using atomic write() syscalls
757 * ========================================================================= */
758 char time_buf[LOG_TIMESTAMP_BUFFER_SIZE];
760
761 // Format message for file output
762 char log_buffer[LOG_MSG_BUFFER_SIZE];
763 va_list args;
764 va_start(args, fmt);
765
766 int header_len = format_log_header(log_buffer, sizeof(log_buffer), level, time_buf, file, line, func, false, false);
767 if (header_len <= 0 || header_len >= (int)sizeof(log_buffer)) {
768 LOGGING_INTERNAL_ERROR(ERROR_INVALID_STATE, "Failed to format log header");
769 va_end(args);
770 return;
771 }
772
773 int msg_len = header_len;
774 int formatted_len = vsnprintf(log_buffer + header_len, sizeof(log_buffer) - (size_t)header_len, fmt, args);
775 if (formatted_len < 0) {
776 LOGGING_INTERNAL_ERROR(ERROR_INVALID_STATE, "Failed to format log message");
777 va_end(args);
778 return;
779 }
780
781 msg_len += formatted_len;
782 if (msg_len >= (int)sizeof(log_buffer)) {
783 msg_len = sizeof(log_buffer) - 1;
784 log_buffer[msg_len] = '\0';
785 }
786
787 if (msg_len > 0 && msg_len < (int)sizeof(log_buffer) - 1) {
788 log_buffer[msg_len++] = '\n';
789 log_buffer[msg_len] = '\0';
790 }
791
792 va_end(args);
793
794 // Write to file (atomic write syscall)
795 int file_fd = atomic_load(&g_log.file);
796 if (file_fd >= 0 && file_fd != STDERR_FILENO) {
797 write_to_log_file_atomic(log_buffer, msg_len);
798 }
799
800 // Write to terminal (atomic state checks)
801 va_list args_terminal;
802 va_start(args_terminal, fmt);
803 write_to_terminal_atomic(level, time_buf, file, line, func, fmt, args_terminal);
804 va_end(args_terminal);
805}
size_t get_current_time_formatted(char *time_buf)
Get current time as formatted string.
#define LOG_TIMESTAMP_BUFFER_SIZE
Maximum size of a timestamp string.
#define LOG_MMAP_MSG_BUFFER_SIZE
Maximum size of a log message in mmap mode.
Definition log/logging.h:98

References ERROR_INVALID_STATE, get_current_time_formatted(), LOG_COLOR_RESET, LOG_ERROR, LOG_FATAL, log_get_color_array(), log_mmap_is_active(), LOG_MMAP_MSG_BUFFER_SIZE, log_mmap_write(), LOG_MSG_BUFFER_SIZE, LOG_TIMESTAMP_BUFFER_SIZE, LOG_WARN, LOGGING_INTERNAL_ERROR, and safe_fprintf().

Referenced by handle_remote_log_packet_from_client().

◆ log_net_message()

asciichat_error_t log_net_message ( socket_t  sockfd,
const struct crypto_context_t crypto_ctx,
log_level_t  level,
remote_log_direction_t  direction,
const char *  file,
int  line,
const char *  func,
const char *  fmt,
  ... 
)

#include <logging.h>

Log a message to all destinations (network, file, and terminal).

Parameters
sockfdDestination socket
crypto_ctxOptional crypto context for encryption (NULL if not ready)
levelLog severity used for remote and local logging
directionRemote log direction metadata
fileSource file name (or NULL to omit)
lineSource line number (or 0 to omit)
funcFunction name (or NULL to omit)
fmtFormat string (printf-style)
...Format arguments
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 1010 of file log/logging.c.

1012 {
1013 va_list args;
1014 va_start(args, fmt);
1015 asciichat_error_t result =
1016 log_network_message_internal(sockfd, crypto_ctx, level, direction, file, line, func, fmt, args);
1017 va_end(args);
1018 return result;
1019}

◆ log_network_message()

asciichat_error_t log_network_message ( socket_t  sockfd,
const struct crypto_context_t crypto_ctx,
log_level_t  level,
remote_log_direction_t  direction,
const char *  fmt,
  ... 
)

#include <logging.h>

Send a formatted log message over the network.

Parameters
sockfdDestination socket
crypto_ctxOptional crypto context for encryption (NULL if not ready)
levelLog severity used for remote and local logging
directionRemote log direction metadata
fmtFormat string (printf-style)
...Format arguments
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 1000 of file log/logging.c.

1001 {
1002 va_list args;
1003 va_start(args, fmt);
1004 asciichat_error_t result =
1005 log_network_message_internal(sockfd, crypto_ctx, level, direction, NULL, 0, NULL, fmt, args);
1006 va_end(args);
1007 return result;
1008}

Referenced by disconnect_client_for_bad_data(), tcp_client_send_join(), and threaded_send_client_join_packet().

◆ log_plain_msg()

void log_plain_msg ( const char *  fmt,
  ... 
)

#include <logging.h>

Plain logging without timestamps or levels.

Parameters
fmtFormat string (printf-style)
...Format arguments

Writes to both log file and stderr without timestamps or log levels.

Definition at line 807 of file log/logging.c.

807 {
808 if (!atomic_load(&g_log.initialized)) {
809 return;
810 }
811
812 if (shutdown_is_requested()) {
813 return;
814 }
815
816 char log_buffer[LOG_MSG_BUFFER_SIZE];
817 va_list args;
818 va_start(args, fmt);
819 int msg_len = vsnprintf(log_buffer, sizeof(log_buffer), fmt, args);
820 va_end(args);
821
822 if (msg_len <= 0 || msg_len >= (int)sizeof(log_buffer)) {
823 return;
824 }
825
826 // Write to mmap if active
827 if (log_mmap_is_active()) {
828 log_mmap_write(LOG_INFO, NULL, 0, NULL, "%s", log_buffer);
829 } else {
830 // Write to file (atomic write syscall)
831 int file_fd = atomic_load(&g_log.file);
832 if (file_fd >= 0 && file_fd != STDERR_FILENO) {
833 write_to_log_file_atomic(log_buffer, msg_len);
834 write_to_log_file_atomic("\n", 1);
835 }
836 }
837
838 // Terminal output (atomic state checks)
839 if (!atomic_load(&g_log.terminal_output_enabled)) {
840 return;
841 }
842 if (atomic_load(&g_log.terminal_locked)) {
843 uint64_t owner = atomic_load(&g_log.terminal_owner_thread);
844 if (owner != (uint64_t)asciichat_thread_self()) {
845 return;
846 }
847 }
848
849 safe_fprintf(stdout, "%s\n", log_buffer);
850 (void)fflush(stdout);
851}
bool shutdown_is_requested(void)
Check if shutdown has been requested.
Definition common.c:45

References asciichat_thread_self(), LOG_INFO, log_mmap_is_active(), log_mmap_write(), LOG_MSG_BUFFER_SIZE, safe_fprintf(), and shutdown_is_requested().

◆ log_plain_stderr_msg()

void log_plain_stderr_msg ( const char *  fmt,
  ... 
)

#include <logging.h>

Plain logging to stderr with newline.

Parameters
fmtFormat string (printf-style)
...Format arguments

Writes to both log file and stderr without timestamps or log levels, with trailing newline.

Definition at line 895 of file log/logging.c.

895 {
896 if (!atomic_load(&g_log.initialized)) {
897 return;
898 }
899 if (shutdown_is_requested()) {
900 return;
901 }
902
903 va_list args;
904 va_start(args, fmt);
905 log_plain_stderr_internal_atomic(fmt, args, true);
906 va_end(args);
907}

References shutdown_is_requested().

◆ log_plain_stderr_nonewline_msg()

void log_plain_stderr_nonewline_msg ( const char *  fmt,
  ... 
)

#include <logging.h>

Plain logging to stderr without trailing newline.

Parameters
fmtFormat string (printf-style)
...Format arguments

Writes to both log file and stderr without timestamps, log levels, or trailing newline. Useful for interactive prompts where the user's response should be on the same line.

Definition at line 909 of file log/logging.c.

909 {
910 if (!atomic_load(&g_log.initialized)) {
911 return;
912 }
913 if (shutdown_is_requested()) {
914 return;
915 }
916
917 va_list args;
918 va_start(args, fmt);
919 log_plain_stderr_internal_atomic(fmt, args, false);
920 va_end(args);
921}

References shutdown_is_requested().

◆ log_redetect_terminal_capabilities()

void log_redetect_terminal_capabilities ( void  )

#include <logging.h>

Re-detect terminal capabilities after logging is initialized.

Useful when terminal capabilities change or need to be refreshed.

Definition at line 1054 of file log/logging.c.

1054 {
1055 // Guard against recursion
1056 if (g_terminal_caps_detecting) {
1057 return;
1058 }
1059
1060 // Detect if not initialized, or if we're using defaults (not reliably detected)
1061 // This ensures we get proper detection after logging is ready, replacing any defaults
1062 // Once we have reliable detection, never re-detect to keep colors consistent
1063 if (!g_terminal_caps_initialized || !g_terminal_caps.detection_reliable) {
1064 g_terminal_caps_detecting = true;
1065 g_terminal_caps = detect_terminal_capabilities();
1066 g_terminal_caps_detecting = false;
1067 g_terminal_caps_initialized = true;
1068
1069 // Now log the capabilities AFTER colors are set, so this log uses the correct colors
1070 log_debug("Terminal capabilities: color_level=%d, capabilities=0x%x, utf8=%s, fps=%d", g_terminal_caps.color_level,
1071 g_terminal_caps.capabilities, g_terminal_caps.utf8_support ? "yes" : "no", g_terminal_caps.desired_fps);
1072
1073 // Now that we've detected once with reliable results, keep these colors consistent for all future logs
1074 }
1075 // Once initialized with reliable detection, never re-detect to keep colors consistent
1076}
terminal_capabilities_t detect_terminal_capabilities(void)
Detect terminal capabilities.
bool utf8_support
True if terminal supports UTF-8 encoding.
Definition terminal.h:493
uint32_t capabilities
Capability flags bitmask (terminal_capability_flags_t)
Definition terminal.h:489

References terminal_capabilities_t::capabilities, terminal_capabilities_t::color_level, terminal_capabilities_t::desired_fps, detect_terminal_capabilities(), terminal_capabilities_t::detection_reliable, log_debug, and terminal_capabilities_t::utf8_support.

Referenced by log_init().

◆ log_set_flush_delay()

void log_set_flush_delay ( unsigned int  delay_ms)

#include <logging.h>

Set the delay between flushing buffered log entries.

When terminal output is re-enabled after an interactive prompt, buffered log entries are flushed to the terminal. This setting adds a delay between each entry for a visual animation effect.

Parameters
delay_msDelay in milliseconds between each log entry (0 = no delay)

Definition at line 527 of file log/logging.c.

527 {
528 atomic_store(&g_log.flush_delay_ms, delay_ms);
529}

◆ log_set_force_stderr()

void log_set_force_stderr ( bool  enabled)

#include <logging.h>

Force all terminal log output to stderr.

Parameters
enabledtrue to force all logs to stderr, false for normal routing

When enabled, all log messages (including INFO, DEBUG, DEV) go to stderr instead of the default behavior where INFO/DEBUG/DEV go to stdout and WARN/ERROR/FATAL go to stderr. This is used by the client to keep stdout clean for ASCII art output.

Definition at line 506 of file log/logging.c.

506 {
507 atomic_store(&g_log.force_stderr, enabled);
508}

◆ log_set_level()

void log_set_level ( log_level_t  level)

#include <logging.h>

Set the minimum log level.

Parameters
levelMinimum log level to output

Definition at line 480 of file log/logging.c.

480 {
481 atomic_store(&g_log.level, (int)level);
482 atomic_store(&g_log.level_manually_set, true);
483}

Referenced by options_init().

◆ log_set_terminal_output()

void log_set_terminal_output ( bool  enabled)

#include <logging.h>

Control stderr output to terminal.

Parameters
enabledtrue to enable terminal output, false to disable

Definition at line 489 of file log/logging.c.

489 {
490 // Respect --quiet flag: if quiet is set, never enable terminal output
491 const options_t *opts = options_get();
492 if (!opts) {
493 log_error("Options not initialized");
494 return;
495 }
496 if (enabled && opts && opts->quiet) {
497 return; // Silently ignore attempts to enable terminal output when --quiet is set
498 }
499 atomic_store(&g_log.terminal_output_enabled, enabled);
500}
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:222
Consolidated options structure.
Definition options.h:439
unsigned short int quiet
Quiet mode (suppress logs)
Definition options.h:530

References log_error, options_get(), and options_state::quiet.

Referenced by asciichat_shared_init(), client_main(), display_disable_logging_for_first_frame(), mirror_main(), server_connection_cleanup(), server_connection_close(), server_connection_establish(), server_connection_lost(), and server_main().

◆ log_shutdown_begin()

void log_shutdown_begin ( void  )

#include <logging.h>

Begin shutdown phase - disable console logging but keep file logging.

Call this before logging shutdown messages. Disables console output but keeps file logging so messages are recorded for debugging.

Useful when you want final messages (like "no servers found") to go to the log file only, not to stdout where it might interfere with output.

Definition at line 1140 of file log/logging.c.

1140 {
1141 if (g_shutdown_in_progress) {
1142 return; /* Already in shutdown phase */
1143 }
1144
1145 /* Save current terminal output state and disable console output */
1146 g_shutdown_saved_terminal_output = atomic_load(&g_log.terminal_output_enabled);
1147 atomic_store(&g_log.terminal_output_enabled, false);
1148 g_shutdown_in_progress = true;
1149}

◆ log_shutdown_end()

void log_shutdown_end ( void  )

#include <logging.h>

End shutdown phase - restore previous logging settings.

Call after shutdown messages have been logged to restore console output.

Definition at line 1151 of file log/logging.c.

1151 {
1152 if (!g_shutdown_in_progress) {
1153 return; /* Not in shutdown phase */
1154 }
1155
1156 /* Restore previous terminal output state */
1157 atomic_store(&g_log.terminal_output_enabled, g_shutdown_saved_terminal_output);
1158 g_shutdown_in_progress = false;
1159}

◆ log_truncate_if_large()

void log_truncate_if_large ( void  )

#include <logging.h>

Manually truncate large log files.

Checks if the log file exceeds MAX_LOG_SIZE and truncates it if necessary.

Definition at line 531 of file log/logging.c.

531 {
532 // Log rotation is inherently racy without locks - best-effort only
533 // For reliable rotation, use mmap mode or external logrotate
534 int file = atomic_load(&g_log.file);
535 if (file >= 0 && file != STDERR_FILENO && strlen(g_log.filename) > 0) {
536 struct stat st;
537 if (fstat(file, &st) == 0 && st.st_size > MAX_LOG_SIZE) {
538 atomic_store(&g_log.current_size, (size_t)st.st_size);
539 }
540 }
541}
#define MAX_LOG_SIZE
Maximum log file size in bytes (3MB) before rotation.
Definition log/logging.h:79

References MAX_LOG_SIZE.

Referenced by asciichat_shared_init().

◆ log_unlock_terminal()

void log_unlock_terminal ( bool  previous_state)

#include <logging.h>

Release terminal lock and flush buffered messages.

Call this after interactive prompts complete to release the terminal lock. Buffered log messages from other threads will be flushed to terminal.

Parameters
previous_stateThe value returned by log_lock_terminal()

Definition at line 520 of file log/logging.c.

520 {
521 atomic_store(&g_log.terminal_locked, previous_state);
522 if (!previous_state) {
523 atomic_store(&g_log.terminal_owner_thread, 0);
524 }
525}

Referenced by client_crypto_handshake(), discovery_tui_select(), prompt_password(), and prompt_unknown_host().