Kora фреймворк для написания Java / Kotlin приложений с упором на производительность, эффективность, прозрачность сделанный разработчиками Т-Банк / Тинькофф

Kora is a framework for writing Java / Kotlin applications with a focus on performance, efficiency, transparency made by T-Bank / Tinkoff developers

Skip to content

Logging

Kora uses slf4j-api as the logging engine for the entire framework, it is expected that an implementation based on Logback will be used.

Usage

Loggers are required to be provided through the SLF4J factory.

Logger logger = LoggerFactory.getLogger(SomeService.class)
val logger = LoggerFactory.getLogger(SomeService::class.java);

Configuration

Logging levels described in the LoggingConfig class:

logging {
  levels {  //(1)!
    "ru.tinkoff.kora.http.server.common.telemetry": "INFO"
    "ru.tinkoff.kora.http.client.common.telemetry.DefaultHttpClientTelemetry": "INFO"
  }
}
  1. Logging levels for classes and packages are specified
logging:
  levels: #(1)!
    ru.tinkoff.kora.http.server.common.telemetry: "INFO"
    ru.tinkoff.kora.http.client.common.telemetry.DefaultHttpClientTelemetry: "INFO"
}
  1. Logging levels for classes and packages are specified

Logback configuration parameters are described in the modules that include logback, e.g. HTTP server, HTTP client, etc.

Module

Enabling/disabling logging of certain modules is specified in the configuration of the modules themselves.

Logging of all modules is disabled by default, for convenience below is a separate configuration to enable logging of most modules.

db.telemetry.logging.enabled = true //(1)!
cassandra.telemetry.logging.enabled = true //(2)!
grpcServer.telemetry.logging.enabled = true //(3)!
httpServer.telemetry.logging.enabled = true //(4)!
scheduling.telemetry.logging.enabled = true //(5)!
grpcClient.SomeGrpcServiceName.telemetry.logging.enabled = true //(6)!
soapClient.SomeSoapServiceName.telemetry.logging.enabled = true //(7)!
SomePathToConfigHttpClient.telemetry.logging.enabled = true //(8)!
SomePathToConfigKafkaConsumer.telemetry.logging.enabled = true //(9)!
SomePathToConfigKafkaProducer.telemetry.logging.enabled = true //(10)!
  1. Database JDBC / R2DBC / Vertx
  2. Database Cassandra
  3. gRPC server
  4. HTTP server
  5. Scheduler
  6. gRPC client (Specified for a specific service)
  7. SOAP client (Specified for a specific service)
  8. HTTP client (Specified for a specific client)
  9. Kafka consumer (Specified for a specific consumer)
  10. Kafka producer (Specified for a specific producer)
db.telemetry.logging.enabled: true #(1)!
cassandra.telemetry.logging.enabled: true #(2)!
grpcServer.telemetry.logging.enabled: true #(3)!
httpServer.telemetry.logging.enabled: true #(4)!
scheduling.telemetry.logging.enabled: true #(5)!
grpcClient.SomeGrpcServiceName.telemetry.logging.enabled: true #(6)!
soapClient.SomeSoapServiceName.telemetry.logging.enabled: true #(7)!
SomePathToConfigHttpClient.telemetry.logging.enabled: true #(8)!
SomePathToConfigKafkaConsumer.telemetry.logging.enabled: true #(9)!
SomePathToConfigKafkaProducer.telemetry.logging.enabled: true #(10)!
  1. Database JDBC / R2DBC / Vertx
  2. Database Cassandra
  3. gRPC server
  4. HTTP server
  5. Scheduler
  6. gRPC client (Specified for a specific service)
  7. SOAP client (Specified for a specific service)
  8. HTTP client (Specified for a specific client)
  9. Kafka consumer (Specified for a specific consumer)
  10. Kafka producer (Specified for a specific producer)

Logback

The module provides a logging implementation based on Logback, adds support for structured logs and the ability to configure logging levels via config file.

Dependency

Dependency build.gradle:

implementation "ru.tinkoff.kora:logging-logback"

Module:

@KoraApp
public interface Application extends LogbackModule { }

Dependency build.gradle.kts:

implementation("ru.tinkoff.kora:logging-logback")

Module:

@KoraApp
interface Application : LogbackModule

Configuration

It is assumed that Logback will be configured via logback.xml, and only logging levels will be specified in the Kora configuration, example logback.xml:

<configuration debug="false">
    <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <charset>UTF-8</charset>
            <pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="ASYNC" class="ru.tinkoff.kora.logging.logback.KoraAsyncAppender">
        <appender-ref ref="STDOUT"/>
    </appender>

    <root level="WARN">
        <appender-ref ref="ASYNC"/>
    </root>
</configuration>

Other implementation

Kora uses slf4j-api as the logging engine, you can plug in any custom compatible implementation. The base module adds support for structured logs and the ability to configure logging levels via config file.

Dependency

A generic logging implementation will need to be connected:

Dependency build.gradle:

implementation "ru.tinkoff.kora:logging-common"

Module:

@KoraApp
public interface Application extends LoggingModule { }

Dependency build.gradle.kts:

implementation("ru.tinkoff.kora:logging-common")

Module:

@KoraApp
interface Application : LoggingModule

Usage

When using your custom implementation, you would need to provide an implementation of LoggingLevelApplier that implements the setting the logging level and resetting it.

It will also be necessary for the implementation to independently support StructuredArgument, StructuredArgumentWriter and MDC if they are to be used.

Structured Logs

You can pass structured data to a log record in two ways via:

  • Marker
  • Parameter

The marker and parameter methods also take Long, Integer, String, Boolean and Map<String, String> as arguments.

Marker

You can pass structured data to the log via a marker:

var logger = LoggerFactory.getLogger(getClass());
var marker = StructuredArgument.marker("key", gen -> {
   gen.writeString("value");
});
logger.info(marker, "message");
val logger = LoggerFactory.getLogger(javaClass)
val marker = StructuredArgument.marker("key") { it.writeString("value") }
logger.info(marker, "message")

Parameter

You can transfer structured data to the log via parameters:

var logger = LoggerFactory.getLogger(getClass());
var parameter = StructuredArgument.arg("key", gen -> {
  gen.writeString("value");
});
log.info("message", parameter);
val logger = LoggerFactory.getLogger(javaClass)
val parameter = StructuredArgument.arg("key") { it.writeString("value") }
logger.info("message", parameter)

MDC

Structured data can be attached to all records within a context using the ru.tinkoff.kora.logging.common.MDC class:

MDC.put("key", gen -> gen.writeString("value"));

`kotlin MDC.put("key") { it.writeString("value") }

If you are using AsyncAppender to send logs, you need to use ru.tinkoff.kora.logging.logback.KoraAsyncAppender to correctly pass MDC parameters, which will pass to the delegate ru.tinkoff.kora.kora.logging.logging.logback.KoraLoggingEvent containing, among other things, a structured MDC.