Logstash Logback Encoder

repository·main·Indexed 25 days ago

https://github.com/logfellow/logstash-logback-encoder

A highly-configurable structured logging mechanism for Logback that allows developers to output logs in JSON and other Jackson-supported formats. It supports both standard LoggingEvents via logback-classic and AccessEvents via logback-access, providing a variety of appenders, encoders, and layouts, including TCP and UDP socket appenders.

Tokens
24.2K
Snippets
60
Records
89
Agent score
32%

What's inside logstash-logback-encoder

  1. Overview of Logstash Logback Encoder

    main

    Logstash Logback Encoder provides Logback encoders, layouts, and appenders to log in JSON and other formats supported by Jackson. It is a highly-configurable, general-purpose, structured logging mechanism.

    It supports two types of events:

    1. LoggingEvents: Logged through a standard Logger.
    2. AccessEvents: Logged via logback-access.
  2. What is a stack hash and why use it?

    main

    A stack hash is a short, stable signature used to identify a specific type of error (Throwable). It is designed to help log aggregation systems like Elasticsearch match distinct occurrences of the same error type, even if the exact stack trace varies slightly.

    Using stack hashes allows you to:

    • Count distinct types of errors occurring in your code over time.
    • Monitor the frequency and occurrences of specific error types.
    • Detect new error types (e.g., after a new deployment).
    • Link errors to bug trackers by using the hash as a unique error ID.
  3. Configure TCP connection strategies

    main

    The LogstashTcpSocketAppender supports multiple connection strategies to determine the order in which destination connections are attempted and when to reestablish connections. Logs are sent to only one destination at a time.

    Available Strategies:

    1. preferPrimary (Default): Prefers the first configured destination. If it is down, it attempts the next. If a connection succeeds but closes before minConnectionTimeBeforePrimary (default 10s) has elapsed, it tries the next destination to avoid 'connection storms'. If it stays open longer than that threshold, it will eventually attempt to return to the primary destination.
    2. roundRobin: Attempts connections in a circular order. If a connection fails, it moves to the next.
    3. random: Attempts connections in a random order.

    You can implement your own strategy by implementing the DestinationConnectionStrategy interface.

    <appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>destination1.domain.com:4560</destination>
        <destination>destination2.domain.com:4560</destination>
        <destination>destination3.domain.com:4560</destination>
        <connectionStrategy>
            <preferPrimary>
                <secondaryConnectionTTL>5 minutes</secondaryConnectionTTL>
            </preferPrimary>
        </connectionStrategy>
    </appender>
  4. Use Async Disruptor Appenders for high-performance logging

    main

    The *AsyncDisruptorAppender family of appenders provides asynchronous logging by using an LMAX Disruptor RingBuffer instead of a standard BlockingQueue. This allows for higher throughput and lower latency. These appenders can delegate to any other underlying Logback appender.

    Note for Logback 1.3+ users: You cannot declare an <appender> inside another <appender>. You must declare the underlying appender separately and refer to it using <appender-ref>.

    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        ...
    </appender>
    
    <appender name="async" class="net.logstash.logback.appender.LoggingEventAsyncDisruptorAppender">
        <appender-ref ref="file" />
    </appender>
  5. Register Jackson Modules

    main

    By default, Jackson modules are automatically discovered and registered via MapperBuilder.findAndAddModules(). To use this, simply add the module (e.g., jackson-datatype-jsr310) to your classpath.

    • To disable automatic discovery: Set <findAndRegisterJacksonModules>false</findAndRegisterJacksonModules> on the encoder or layout.
    • To register a module manually: Implement a MapperBuilderDecorator and add the module within the decorate method.
  6. Use Composite Encoders for flexible JSON output

    main

    For greater flexibility in the JSON format and data included in LoggingEvents and AccessEvents, use LoggingEventCompositeJsonEncoder or AccessEventCompositeJsonEncoder.

    These encoders are composed of one or more JSON providers that contribute to the output. No providers are configured by default; you must explicitly add the ones you want within a <providers> block.

    To optimize performance and reduce garbage collection pressure, you can configure the minBufferSize property. The default is 1024 bytes. It is strongly advised to set this to at least the average size of your encoded events.

    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
        <providers>
            <mdc/>
            <pattern>
                <pattern>
                    {
                      "timestamp": "%date{ISO8601}",
                      "myCustomField": "fieldValue",
                      "relative": "#asLong{%relative}"
                    }
                </pattern>
            </pattern>
            <stackTrace>
                <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
                    <maxDepthPerThrowable>30</maxDepthPerThrowable>
                    <maxLength>2048</maxLength>
                    <shortenedClassNameLength>20</shortenedClassNameLength>
                    <exclude>^sun\.reflect\..*\.invoke</exclude>
                    <exclude>^net\.sf\.cglib\.proxy\.MethodProxy\.invoke</exclude>
                    <evaluator class="myorg.MyCustomEvaluator"/>
                    <rootCauseFirst>true</rootCauseFirst>
                </throwableConverter>
            </stackTrace>
        </providers>
    </encoder>
  7. Identify field values to mask by value

    main

    You can mask specific values using regex or literal strings. When using regex, all matches within a string field will be replaced. To match the entire field value, use ^ and $ anchors.

    Configuration Options:

    • <defaultMask>: Sets the mask string (defaults to ****).
    • <value>: Adds a single value/regex using the default mask.
    • <values>: A comma-separated list of values/regexes using the default mask.
    • <valueMask>: Allows specifying a custom mask for specific values. The mask string can reference regex capturing groups (e.g., $1).
    • <valueMaskSupplier>: Class name for dynamic value masking via MaskingJsonGeneratorDecorator.ValueMaskSupplier.
    • <valueMasker>: Class name for advanced masking via net.logstash.logback.mask.ValueMasker.

    Behavior: Values are passed through every configured masker sequentially. The output of one masker becomes the input to the next.

  8. Identify field values to mask by path

    main

    Paths follow a format similar to JSON Pointer. Use these rules to target specific fields:

    • Absolute paths: Start with / (e.g., /@timestamp).
    • Partial paths: Do not start with /; they match the sequence anywhere in the output.
    • Single token: A path with no / matches all occurrences of that field name.
    • Wildcards: Use * to match any token at that position.
    • Escaping: Use ~1 to escape / and ~0 to escape ~ within a token.

    Configuration Options:

    • <defaultMask>: Sets the mask string (defaults to ****).
    • <path>: Adds a single path using the default mask.
    • <paths>: A comma-separated list of paths using the default mask.
    • <pathMask>: Allows specifying a custom mask for specific paths.
    • <pathMaskSupplier>: Class name for dynamic path masking via MaskingJsonGeneratorDecorator.PathMaskSupplier.
    • <fieldMasker>: Class name for advanced masking via net.logstash.logback.mask.FieldMasker.
  9. How stack hash computation works

    main

    A stack hash is a unique identifier for a stack trace used to group similar errors. To ensure stability (so the hash doesn't change due to minor environmental variations), the computation follows these rules:

    1. Excludes error messages: The hash is not computed using the error message itself.
    2. Includes parent causes: The computation recurses through the parent causes of the throwable.
    3. Uses exclusion patterns: It is highly recommended to exclude non-stable elements (like proxy classes or framework plumbing) to stabilize the hash over time and space.
  10. Use the pattern provider to define JSON templates

    main

    The pattern provider allows you to define a JSON template using Logback's PatternLayout conversion specifiers. The encoder will populate the values within the template.

    Type Conversion Operations

    Because patterns are processed as strings, you can use special operations to ensure correct JSON types:

    • #asLong{...}: Converts resulting string to a Long (or null).
    • #asDouble{...}: Converts resulting string to a Double (or null).
    • #asBoolean{...}: Converts resulting string to a Boolean. Supports true, yes, y, 1 as true.
    • asNullIfEmpty{...}: Converts empty string to null.
    • #asJson{...}: Converts resulting string to a JSON object (or null).
    • #tryJson{...}: Converts to JSON, or keeps as string if conversion fails.
    • #nullNA{...}: (AccessEvents only) Replaces a dash (-) with null.

    Omitting Empty Fields

    Set <omitEmptyFields>true</omitEmptyFields> to remove fields from the output if they are null, empty strings, empty arrays, or empty objects.

    <pattern>
        <pattern>
            {
                "line_str": "%line",
                "line_long": "#asLong{%line}",
                "has_message": "#asBoolean{%mdc{hasMessage}}",
                "json_message": "#asJson{%message}"
            }
        </pattern>
    </pattern>
  11. Avoid memory leaks with ThreadLocals and Asynchronous Appenders

    main

    The encoders and layouts use ThreadLocal internally for performance. In environments with application reloading (like web containers) or high thread counts (like virtual threads), you should follow these two practices to prevent memory leaks:

    1. Use an asynchronous appender: Use LogstashTcpSocketAppender, LoggingEventAsyncDisruptorAppender, or Logback's AsyncAppender. This limits the number of threads using the encoder/layout, thereby limiting the number of ThreadLocal instances and reducing resource contention.
    2. Cleanly shut down Logback: Ensure you call the Logback shutdown process when the application stops or reloads. This stops the asynchronous threads and makes their ThreadLocal values eligible for garbage collection.

    Common Error Symptoms:

    • Tomcat errors indicating a ThreadLocal (specifically net.logstash.logback.util.ThreadLocalHolder.Holder) was not removed during web application stop.
    • Tomcat errors indicating an asynchronous appender thread (e.g., logback-appender-ASYNC-2) failed to stop.
  12. Customize stack traces with ShortenedThrowableConverter

    main

    By default, Logback uses ExtendedThrowableProxyConverter for stack traces. You can replace this by configuring the LogstashEncoder to use net.logstash.logback.stacktrace.ShortenedThrowableConverter. This converter provides advanced features like omitting common frames, truncating via regex, and shortening class names.

    Important: The converter only applies to exceptions passed as an extra argument to the SLF4J log method. Do NOT use structured arguments or markers for exceptions.

    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
        <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
            <!-- configuration options here -->
        </throwableConverter>
    </encoder>