stumpless

repository·latest·Indexed 19 days ago

https://github.com/goatshriek/stumpless

A high-performance C logging library providing a consistent interface for targets such as Splunk, rsyslog, journald, and the Windows Event Log. It supports structured logging based on RFC 5424, hierarchical target chaining, and provides object-oriented C++ bindings via Wrapture.

Tokens
32.8K
Snippets
96
Records
159
Agent score
63%

What's inside stumpless

  1. Use the Check Headers Tool to manage C/C++ include statements

    latest

    The Check Headers Tool is used to audit C/C++ source and header files to ensure two specific properties are met:

    1. Completeness: All necessary headers are explicitly included in the file, rather than relying on transitive includes (where one header includes another).
    2. Cleanliness: There are no unnecessary header includes present in the file.

    Maintaining these properties helps keep compile times manageable and prevents complicated header interdependencies.

    The tool operates using manifest files (e.g., standard_library.yml) which map specific code terms to their required headers. For example, if a file uses NULL, the tool checks the manifest to ensure either stddef.h or cstddef is included.

  2. Use Stumpless C++ Bindings

    latest
    Stumpless provides an object-oriented C++ API built on top of the core C library using Wrapture. This API allows you to use target classes, entries, elements, and parameters as objects rather than calling raw C functions. The C++ bindings offer the same performance benefits as the C library while providing a more ergonomic interface.
  3. Continuous Integration (CI) and Quality Tools

    latest

    Stumpless utilizes several tools to maintain code quality and portability. Developers should be aware of these gates:

    • GitHub Actions: Builds the library in various environments. The build workflow must pass before any change is merged.
    • Codecov: Analyzes code coverage from the test suite. It monitors diff and total coverage in pull requests.
    • Sonarcloud: Provides static analysis and code quality reviews. Avoid introducing new issues here.
    • CodeQL: GitHub's static code scanning service. Unlike Sonarcloud, it runs on pull requests from forks. Avoid introducing new issues detected by CodeQL.

    Tip: If making non-code changes (like documentation), you can temporarily remove .github/workflows/build.yml to conserve build resources, but ensure it is restored before creating a pull request.

  4. Stumpless Memory Management Patterns

    latest

    Stumpless follows specific naming patterns for object lifecycle management. Ensure you pair constructors with their corresponding destructors:

    Dynamic Allocation (Heap)

    • new (Constructors): Functions like stumpless_new_entry allocate memory for a new structure and return it.
    • destroy (Destructors): Use these to deallocate memory for structures created with new (e.g., stumpless_destroy_entry_and_contents).

    Static/Provided Allocation

    • load (Constructors): Functions like stumpless_load_entry populate a structure provided as a parameter rather than allocating one. This is often faster due to fewer dynamic allocations.
    • unload (Destructors): Use these to clean up structures initialized via load (e.g., stumpless_unload_entry).

    State-based Initialization

    • open (Constructors): Similar to new, but also transitions the object to an "opened" state. This is currently primarily used for network targets to manage connection states.
  5. Use `private/config.h` for build environment details

    latest

    While stumpless/config.h describes features, private/config.h contains information about the specific build environment and available system headers. This header is used for internal code decisions.

    Example symbol:

    • HAVE_WINSOCK2_H: Defined if winsock2.h was available during the CMake build process.
  6. Use Stumpless single-file dropins

    latest

    The single-file build target generates stumpless.c and include/single_file/stumpless.h. These can be used as drop-in files to provide Stumpless functionality without needing static linking or dynamic loading.

    Warning: These files are relatively large, contain duplicate code, and only include functionality enabled by the specific configuration used during generation.

  7. Use Atomic Types for high-performance synchronization

    latest

    Atomic types allow single fields to be read and written without a lock. This is the most performant synchronization method but requires careful implementation to avoid logic errors (e.g., memory leaks during compare-and-exchange operations).

    Atomic types are provided for various data types and include specific functions for:

    • Reading the value
    • Writing the value
    • Performing a compare-and-exchange operation

    Note: You must initialize atomic variables using their appropriate initializer when they are defined.

  8. How error handling works in Stumpless

    latest

    Stumpless uses a centralized error handling framework.

    Defining Errors

    All possible errors are defined in stumpless/error.h within the STUMPLESS_FOREACH_ERROR macro. To add a new error, append it to this list; it will be assigned the next available integer ID.

    Raising Errors

    Each error has an internal raise function (declared in include/private/error.h and defined in src/error.c). When an operation fails, call the appropriate raise_<error_name> function. These functions set the error ID and localized messages.

    Pattern: If a function detects a failure from an internal function that already raises an error (like alloc_mem), simply return without raising a new error to avoid overwriting the original context.

    Clearing Errors

    Public functions must clear the error state if they succeed so users can distinguish between success and failure. Use clear_error before returning on the non-error path. Destructors and error-handling functions do not need to call this.

    Consuming Errors

    Users can check for and retrieve errors using:

    • stumpless_has_error: Returns true if the last call failed.
    • stumpless_get_error: Retrieves the current error struct.
    • stumpless_perror: Prints the current error.
  9. Create a custom Function Target

    latest

    Function targets allow you to inject custom logic into the stumpless workflow. Instead of logging to a standard output or file, you can provide a callback function that is executed every time an entry is added to that target. This is useful for real-time data processing, such as incrementing counters, filtering events, or transforming data based on specific criteria.

    To implement a function target, you must:

    1. Define a callback function with the signature int func(const struct stumpless_target *target, const struct stumpless_entry *entry).
    2. Register the function using stumpless_open_function_target.
    3. Send entries to the target using stumpless_add_entry.
    /* 1. Define the callback */
    int my_custom_logic(const struct stumpless_target *target, const struct stumpless_entry *entry) {
      // Custom logic here
      return 0;
    }
    
    /* 2. Open the target */
    target = stumpless_open_function_target("my-target-name", my_custom_logic);
    
    /* 3. Use the target */
    stumpless_add_entry(target, entry);
  10. Implement custom runtime filters

    latest

    For logic more complex than severity masking (e.g., filtering based on entry content), you can define a custom filter function and apply it to a target using stumpless_set_filter.

    A filter function must match the signature: bool function_name(const struct stumpless_target *target, const struct stumpless_entry *entry).

    Note: When you set a custom filter, it replaces the default mask-based filter. If you want to keep the severity mask behavior alongside your custom logic, you must explicitly call stumpless_mask_filter(target, entry) within your custom function.

    // 1. Define a filter function
    bool
    ignore_element_filter( const struct stumpless_target *target, 
                           const struct stumpless_entry *entry ) {
      // Example: filter out entries that contain an element named "ignore"
      return !stumpless_get_element_by_name( entry, "ignore" );
    }
    
    // 2. Apply the filter to a target
    stumpless_set_filter( target, ignore_element_filter );
    
    // 3. To combine custom logic with the default mask filtering:
    bool
    combined_filter( const struct stumpless_target *target, 
                     const struct stumpless_entry *entry ) {
      return !stumpless_get_element_by_name( entry, "ignore" )
               && stumpless_mask_filter( target, entry );
    }
  11. Use the builder pattern to create network targets

    latest

    If you need to configure a target before attempting to connect (for example, if the destination server is not yet available), use the stumpless_new_* family of functions. This creates a target in a 'paused' state without attempting an immediate connection. You can then use setter functions to configure the destination and port before calling stumpless_open_target.

    new_target = stumpless_new_network_target( "new-tcp4-target",
                                               STUMPLESS_IPV4_NETWORK_PROTOCOL,
                                               STUMPLESS_TCP_TRANSPORT_PROTOCOL );
    
    stumpless_target_is_open( new_target ); // returns false
    stumpless_set_destination( new_target, "example.com" );
    stumpless_set_transport_port( new_target, "6514" );
    stumpless_open_target( new_target );
    stumpless_target_is_open( new_target ); // returns true if successful
  12. Understand the design goals of Stumpless

    latest

    Stumpless is a lightweight, performant C logging library designed to minimize the overhead of high-verbosity logging. Its primary goals are:

    • Efficiency and Performance: Uses multi-threading, multi-process support, and strong compression to keep logging overhead at an absolute minimum.
    • Unobtrusive Interface: Provides a rich, intuitive interface that supports everything from simple string logging to structured data logging.
    • Integrated Generation and Transmission: Allows applications to handle both log generation and transmission directly within the application, removing the need for a separate logging daemon and reducing integration points.
    • Interoperability: Complies with existing standards to ensure compatibility with established collection and analysis tools.