Veritable Lasagna
An Allocator & Data Structure Library for C.
Loading...
Searching...
No Matches
vl_log.c File Reference
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vl/vl_async_queue.h>
#include <vl/vl_atomic.h>
#include <vl/vl_log.h>
#include <vl/vl_memory.h>
#include <vl/vl_mutex.h>
#include <vl/vl_semaphore.h>
#include <vl/vl_stream.h>
#include <vl/vl_thread.h>
+ Include dependency graph for vl_log.c:

Data Structures

struct  vl_log_record
 
struct  vl_log_sink_node
 
struct  vl_logger
 Opaque logger instance type. More...
 
struct  vl_log_sink_stdout_data
 
struct  vl_log_sink_stream_data
 

Functions

vl_logger * vlLoggerNew (const vl_log_config *config)
 Create a new logger instance.
 
void vlLoggerDelete (vl_logger *logger)
 Destroy a logger instance and release all associated resources.
 
vl_bool_t vlLoggerAddSink (vl_logger *logger, vl_log_sink sink)
 Attach a sink to a logger instance.
 
void vlLoggerMessage (vl_logger *logger, const char *msg)
 Write a preformatted message through a specific logger.
 
void vlLoggerMessageF (vl_logger *logger, const char *msgFormat,...)
 Write a formatted message through a specific logger.
 
void vlLoggerFlush (vl_logger *logger)
 Flush pending messages for a specific logger.
 
vl_log_sink vlLogSinkStdout (void)
 Create a sink that writes to standard output.
 
vl_log_sink vlLogSinkStream (vl_stream *stream)
 Create a sink that writes to an existing vl_stream.
 
void vlLogInit (const vl_log_config *config)
 Initialize the global logger.
 
void vlLogFlush (void)
 Flush the global logger.
 
void vlLogShutdown (void)
 Shut down and destroy the global logger.
 
vl_bool_t vlLogAddSink (vl_log_sink sink)
 Attach a sink to the global logger.
 
vl_bool_t vlLogAddStdoutSink (void)
 Convenience helper that attaches a stdout sink to the global logger.
 
vl_bool_t vlLogAddStreamSink (vl_stream *stream)
 Convenience helper that attaches a vl_stream sink to the global logger.
 
void vlLogMessage (const char *msg)
 Write a preformatted message through the global logger.
 
void vlLogMessageF (const char *msgFormat,...)
 Write a formatted message through the global logger.
 
void vlLogError (const char *msg)
 Write an error message through the global logger.
 
void vlLogErrorF (const char *msgFormat,...)
 Write a formatted error message through the global logger.
 

Data Structure Documentation

◆ vl_log_record

struct vl_log_record
+ Collaboration diagram for vl_log_record:
Data Fields
vl_memsize_t len
vl_ularge_t seq
char * text

◆ vl_log_sink_node

struct vl_log_sink_node
+ Collaboration diagram for vl_log_sink_node:
Data Fields
struct vl_log_sink_node_ * next
vl_log_sink sink

◆ vl_logger_

struct vl_logger_

Opaque logger instance type.

A vl_logger represents an independently configured logging pipeline. Multiple logger instances may coexist at the same time, each with its own sink set and async worker state.

Use vlLoggerNew() to create an instance and vlLoggerDelete() to destroy it.

+ Collaboration diagram for vl_logger:
Data Fields
vl_bool_t async
vl_mutex config_lock
vl_atomic_ularge_t enqueued_seq
vl_bool_t initialized
vl_atomic_ularge_t processed_seq
vl_async_queue queue
vl_bool_t running
vl_semaphore sem_flushed
vl_semaphore sem_items
vl_log_sink_node * sinks
vl_thread worker

◆ vl_log_sink_stdout_data

struct vl_log_sink_stdout_data
+ Collaboration diagram for vl_log_sink_stdout_data:
Data Fields
int unused

◆ vl_log_sink_stream_data

struct vl_log_sink_stream_data
+ Collaboration diagram for vl_log_sink_stream_data:
Data Fields
vl_stream * stream

Function Documentation

◆ vlLogAddSink()

vl_bool_t vlLogAddSink ( vl_log_sink  sink)

Attach a sink to the global logger.

Parameters
sinkSink descriptor to attach.
Returns
VL_TRUE on success, VL_FALSE on failure.

If the global logger is not initialized yet, it may be initialized lazily before the sink is attached.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ vlLogAddStdoutSink()

vl_bool_t vlLogAddStdoutSink ( void  )

Convenience helper that attaches a stdout sink to the global logger.

Returns
VL_TRUE on success, VL_FALSE on failure.
+ Here is the call graph for this function:

◆ vlLogAddStreamSink()

vl_bool_t vlLogAddStreamSink ( vl_stream stream)

Convenience helper that attaches a vl_stream sink to the global logger.

Parameters
streamStream to attach as a sink target.
Returns
VL_TRUE on success, VL_FALSE on failure.
+ Here is the call graph for this function:

◆ vlLogError()

void vlLogError ( const char *  msg)

Write an error message through the global logger.

Parameters
msgNUL-terminated message string.

This currently behaves like a semantic alias of vlLogMessage(). It exists to make call sites more expressive and to leave room for future severity-aware routing or formatting.

+ Here is the call graph for this function:

◆ vlLogErrorF()

void vlLogErrorF ( const char *  msgFormat,
  ... 
)

Write a formatted error message through the global logger.

Parameters
msgFormatprintf-style format string.
...Format arguments.

This currently behaves like a semantic alias of vlLogMessageF().

+ Here is the call graph for this function:

◆ vlLogFlush()

void vlLogFlush ( void  )

Flush the global logger.

If the global logger has not been initialized, this function does nothing.

See also
vlLoggerFlush
+ Here is the call graph for this function:

◆ vlLoggerAddSink()

vl_bool_t vlLoggerAddSink ( vl_logger *  logger,
vl_log_sink  sink 
)

Attach a sink to a logger instance.

Parameters
loggerLogger receiving the sink.
sinkSink descriptor to attach.
Returns
VL_TRUE on success, VL_FALSE on failure.

On success, the logger stores a copy of sink and assumes ownership of sink.sink_data.

On failure, ownership remains with the caller unless the implementation documents otherwise.

Note
Attaching the same underlying sink state to multiple loggers is unsafe unless that sink implementation explicitly supports shared ownership.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ vlLoggerDelete()

void vlLoggerDelete ( vl_logger *  logger)

Destroy a logger instance and release all associated resources.

Contract

  • Ownership: Releases ownership of the logger and all its associated sinks and internal resources.
  • Lifetime: The logger handle becomes invalid immediately after this call.
  • Thread Safety: Safe to call from any thread, provided no other thread is currently using the logger.
  • Nullability: Safe to call with NULL (no-op).
  • Error Conditions: None.
  • Undefined Behavior: Double deletion. Deleting a logger that is being used by another thread.
  • Memory Allocation Expectations: Deallocates all heap-allocated resources associated with the logger.
  • Return-value Semantics: None (void).
Parameters
loggerLogger to destroy. NULL is ignored.

Destruction performs an implicit flush of pending messages before tearing down worker state and attached sinks.

After this call returns, logger must not be used again.

See also
vlLoggerFlush
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ vlLoggerFlush()

void vlLoggerFlush ( vl_logger *  logger)

Flush pending messages for a specific logger.

Parameters
loggerLogger instance to flush. NULL is ignored.

Behavior depends on the logger mode:

  • async mode: blocks until all messages enqueued before the call have been processed, then flushes sinks
  • sync mode: messages are already written by the time logging calls return, so this primarily flushes sink buffers

This function is useful as a synchronization barrier before shutdown, abnormal termination, or assertions in tests.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ vlLoggerMessage()

void vlLoggerMessage ( vl_logger *  logger,
const char *  msg 
)

Write a preformatted message through a specific logger.

Contract

  • Ownership: The logger copies the msg internally. The caller retains ownership of the msg pointer.
  • Lifetime: Unchanged.
  • Thread Safety: Thread-safe (atomic enqueue).
  • Nullability: Safe to call with NULL for msg (treated as empty). logger should not be NULL.
  • Error Conditions: None.
  • Undefined Behavior: Passing NULL for logger.
  • Memory Allocation Expectations: Allocates memory on the heap to store the message record.
  • Return-value Semantics: None (void).
Parameters
loggerLogger instance to receive the message.
msgNUL-terminated UTF-8 message string. If NULL, it is treated as an empty string.

The message is copied internally so the caller retains ownership of msg. No newline is appended automatically.

In async mode, this call typically enqueues the message and returns before sink I/O completes.

See also
vlLoggerFlush
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ vlLoggerMessageF()

void vlLoggerMessageF ( vl_logger *  logger,
const char *  msgFormat,
  ... 
)

Write a formatted message through a specific logger.

Parameters
loggerLogger instance to receive the message.
msgFormatprintf-style format string.
...Format arguments.

The formatted message is materialized into logger-owned memory before being enqueued or written.

No newline is appended automatically.

◆ vlLoggerNew()

vl_logger * vlLoggerNew ( const vl_log_config config)

Create a new logger instance.

Contract

  • Ownership: The caller owns the returned vl_logger handle and is responsible for calling vlLoggerDelete.
  • Lifetime: The logger remains valid until vlLoggerDelete.
  • Thread Safety: This function is thread-safe.
  • Nullability: Returns NULL if the logger could not be created.
  • Error Conditions: Returns NULL if heap allocation fails or if internal synchronization primitives/worker threads cannot be initialized.
  • Undefined Behavior: None.
  • Memory Allocation Expectations: Allocates the logger structure, internal message queue, mutexes, and semaphores on the heap.
  • Return-value Semantics: Returns an opaque handle to the new logger, or NULL on failure.
Parameters
configOptional logger configuration. If NULL, implementation-defined defaults are used.
Returns
A newly allocated logger instance, or NULL on allocation or initialization failure.

The returned logger initially has no sinks attached. Messages sent to such a logger are accepted but may produce no externally visible output until one or more sinks are added.

See also
vlLoggerAddSink
vlLoggerDelete
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ vlLogInit()

void vlLogInit ( const vl_log_config config)

Initialize the global logger.

Parameters
configOptional configuration for the global logger. If NULL, implementation defaults are used.

This function initializes the process-wide convenience logger used by the vlLog*() functions.

The global logger is intended for applications that want a simple shared logging facility without manually managing a vl_logger*.

Typical usage:

  • call vlLogInit() once during startup
  • attach one or more sinks
  • use vlLogMessage*() during runtime
  • call vlLogShutdown() during shutdown
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ vlLogMessage()

void vlLogMessage ( const char *  msg)

Write a preformatted message through the global logger.

Parameters
msgNUL-terminated message string. If NULL, treated as an empty string.

If the global logger has not yet been initialized, it may be initialized lazily using default behavior.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ vlLogMessageF()

void vlLogMessageF ( const char *  msgFormat,
  ... 
)

Write a formatted message through the global logger.

Parameters
msgFormatprintf-style format string.
...Format arguments.

If the global logger has not yet been initialized, it may be initialized lazily using default behavior.

+ Here is the call graph for this function:

◆ vlLogShutdown()

void vlLogShutdown ( void  )

Shut down and destroy the global logger.

If the global logger is active, this performs an implicit flush, destroys all attached sinks, stops background worker state if present, and resets the global logger to an uninitialized state.

Reinitialization after shutdown is permitted by calling vlLogInit() again.

+ Here is the call graph for this function:

◆ vlLogSinkStdout()

vl_log_sink vlLogSinkStdout ( void  )

Create a sink that writes to standard output.

Returns
A sink descriptor suitable for vlLoggerAddSink().

This sink writes to stdout and flushes it when requested by the logger.

The returned sink is intended to be attached to exactly one logger.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ vlLogSinkStream()

vl_log_sink vlLogSinkStream ( vl_stream stream)

Create a sink that writes to an existing vl_stream.

Parameters
streamStream to write to.
Returns
A sink descriptor suitable for vlLoggerAddSink().

The sink retains the stream when created and releases it when the sink is destroyed. This allows the stream to remain valid for as long as the logger needs it.

Note
The stream pointer should be non-NULL.
Whether stream writes are buffered depends on the underlying stream implementation.
+ Here is the call graph for this function:
+ Here is the caller graph for this function: