logback-kafka-appender Documentation

repository·master·Indexed 20 days ago

https://github.com/danielwegener/logback-kafka-appender

A Java library that allows Logback logs to be published directly to Apache Kafka topics. It features configurable delivery strategies (Asynchronous and Blocking), customizable keying strategies for partition control, and support for standard or custom Logback encoders. Requires Logback version 1.2 or higher.

Tokens
2.9K
Snippets
6
Records
10
Agent score
21%

What's inside logback-kafka-appender

  1. Configure Kafka partitioning and keying strategies

    master

    Kafka uses partitions to manage scalability and ordering. logback-kafka-appender allows you to control how log messages are distributed across partitions using different keying strategies.

    Key considerations:

    • Ordering: Kafka only guarantees read order within a single partition. To ensure messages from a specific source (like a host or thread) stay in order, use a strategy that uses that source as the message key.
    • Distribution: Using a key (like HostNameKeyingStrategy) can lead to uneven distribution across partitions if there are fewer unique keys than partitions.
    • Fixed Partitioning: You can bypass keying strategies entirely by providing a fixed partition using the partition property.
  2. Configure Fallback Appenders

    master
    If the Kafka producer cannot publish a message, the message is sent to all appenders defined via <appender-ref> within the KafkaAppender configuration. It is recommended to use a fast fallback appender, such as a ConsoleAppender (STDOUT/STDERR), because AsynchronousDeliveryStrategy reuses the Kafka producer's IO thread to write to these fallbacks.
  3. Choose a Delivery Strategy

    master

    Decide whether to prioritize log delivery or application performance by choosing one of the following strategies:

    StrategyDescription
    AsynchronousDeliveryStrategyDispatches logs to the Kafka Producer. It blocks if the producer's buffer is full. To prevent blocking, set the Kafka producer config block.on.buffer.full=false.
    BlockingDeliveryStrategyBlocks the calling thread until the log is delivered. This significantly impacts throughput and should not be used with the linger.ms producer config.

    To implement a custom strategy, extend com.github.danielwegener.logback.kafka.delivery.DeliveryStrategy.

  4. Install logback-kafka-appender

    master

    Add logback-kafka-appender and logback-classic as runtime dependencies to your project.

    Important: Due to breaking changes in the Logback Encoder API, you must use at least Logback version 1.2.

    <!-- Maven pom.xml -->
    <dependency>
        <groupId>com.github.danielwegener</groupId>
        <artifactId>logback-kafka-appender</artifactId>
        <version>0.2.0</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
        <scope>runtime</scope>
    </dependency>
    
    <!-- build.sbt -->
    libraryDependencies += "com.github.danielwegener" % "logback-kafka-appender" % "0.2.0"
    libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"
  5. Prevent application blocking during Kafka outages

    master

    Even with AsynchronousDeliveryStrategy, the appender can block during Kafka metadata exchanges if brokers are unreachable. To ensure the appender never blocks your application, wrap the KafkaAppender with Logback's native AsyncAppender and set <neverBlock>true</neverBlock>.

    <configuration>
        <appender name="kafkaAppender" class="com.github.danielwegener.logback.kafka.KafkaAppender">
            <!-- Kafka Appender configuration -->
        </appender>
    
        <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
            <!-- If true, discards messages when the internal queue is full -->
            <neverBlock>true</neverBlock>  
            <appender-ref ref="kafkaAppender" />
        </appender>
    
        <root level="info">
            <appender-ref ref="ASYNC" />
        </root>
    </configuration>
  6. Configure Kafka Producer settings

    master
    The appender allows fine-tuning the underlying Kafka producer by adding <producerConfig> blocks. Each block follows the key=value format. The bootstrap.servers configuration is mandatory. Common tuning parameters include batch.size, compression.type, and linger.ms.
  7. Configure KafkaAppender in logback.xml

    master

    To use the KafkaAppender, define it in your logback.xml and provide the mandatory bootstrap.servers configuration via a <producerConfig> block. You can also specify a topic, keyingStrategy, deliveryStrategy, and optional parameters like partition or appendTimestamp.

    <appender name="kafkaAppender" class="com.github.danielwegener.logback.kafka.KafkaAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
        <topic>logs</topic>
        <keyingStrategy class="com.github.danielwegener.logback.kafka.keying.NoKeyKeyingStrategy" />
        <deliveryStrategy class="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy" />
        
        <!-- Optional: <partition>0</partition> -->
        <!-- Optional: <appendTimestamp>true</appendTimestamp> -->
    
        <!-- Mandatory: bootstrap.servers -->
        <producerConfig>bootstrap.servers=localhost:9092</producerConfig>
    
        <!-- Fallback appender if Kafka is unavailable -->
        <appender-ref ref="STDOUT" />
    </appender>
  8. Implement a custom KeyingStrategy

    master

    If the built-in strategies do not meet your requirements (for example, if you want to use Kafka's log compaction facility), you can implement the KeyingStrategy<T> interface. The createKey method should return a byte[] representing the key.

    package foo;
    import com.github.danielwegener.logback.kafka.keying.KeyingStrategy;
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import java.nio.ByteBuffer;
    
    /* This is a valid example but does not really make much sense */
    public class LevelKeyingStrategy implements KeyingStrategy<ILoggingEvent> {
        @Override
        public byte[] createKey(ILoggingEvent e) {
            return ByteBuffer.allocate(4).putInt(e.getLevel()).array();
        }
    }
  9. Use custom serialization with KafkaAppender

    master

    The appender supports any ch.qos.logback.core.encoder.Encoder. You can use standard encoders like PatternLayoutEncoder or third-party ones like LogstashEncoder.

    To implement custom serialization (e.g., for BSON or Avro), extend ch.qos.logback.core.encoder.Encoder<ILoggingEvent>. Note that the appender will not call headerBytes() or footerBytes() methods.

    public class MyEncoder extends ch.qos.logback.core.encoder.Encoder<ILoggingEvent> {
        // Implementation details...
    }
  10. Reference of available KeyingStrategies

    master

    The following keying strategies are available to control how messages are partitioned in Kafka. Note that some strategies only work with ILoggingEvent objects.

    | Strategy | Description |
    |---|---| 
    | `NoKeyKeyingStrategy` (default) | Does not generate a message key. Results in round robin distribution across partition if no fixed partition is provided. |
    | `HostNameKeyingStrategy` | This strategy uses the HOSTNAME as message key. This is useful because it ensures that all log messages issued by this host will remain in the correct order for any consumer. But this strategy can lead to uneven log distribution for a small number of hosts (compared to the number of partitions). |
    | `ContextNameKeyingStrategy` | This strategy uses logback's CONTEXT_NAME as message key. This is ensures that all log messages logged by the same logging context will remain in the correct order for any consumer. But this strategy can lead to uneven log distribution for a small number of hosts (compared to the number of partitions). This strategy only works for `ILoggingEvents`. |
    | `ThreadNameKeyingStrategy` | This strategy uses the calling threads name as message key. This ensures that all messages logged by the same thread will remain in the correct order for any consumer. But this strategy can lead to uneven log distribution for a small number of thread(-names) (compared to the number of partitions). This strategy only works for `ILoggingEvents`. |
    | `LoggerNameKeyingStrategy` | This strategy uses the logger name as message key. This ensures that all messages logged by the same logger will remain in the correct order for any consumer. But this strategy can lead to uneven log distribution for a small number of distinct loggers (compared to the number of partitions). This strategy only works for `ILoggingEvents`. |