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

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

Перейти к содержанию

Логирование

Модуль для декларативного логирования аргументов и результата методов с помощью аннотаций аспектов.

Подключение

Скорее всего уже транзитивно подключен из других зависимостей либо из Logback, в противном случае требуется подключить:

Зависимость build.gradle:

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

Модуль:

@KoraApp
public interface Application extends LoggingModule { }

Зависимость build.gradle.kts:

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

Модуль:

@KoraApp
interface Application : LoggingModule

Логирование

Предполагается использовать специальные комбинации аннотаций для настройки логирование методов.

Аргументов

@Log.in
public String doWork(@Log.off String strParam, int numParam) {
    return "testResult";
}
@Log.`in`
fun doWork(@Log.off strParam: String?, numParam: Int): String {
    return "testResult"
}
Уровень логгирования Лог
TRACE, DEBUG

DEBUG [main] r.t.e.e.Example.doWork: > {data: {numParam: "4"}}

INFO

INFO [main] r.t.e.e.Example.doWork: >

Результата

@Log.out
public String doWork(String strParam, int numParam) {
    return "testResult";
}
@Log.out
fun doWork(strParam: String, numParam: Int): String {
    return "testResult"
}
Уровень логгирования Лог
TRACE, DEBUG

DEBUG [main] r.t.e.e.Example.doWork: < {data: {out: "testResult"}}

INFO

INFO [main] r.t.e.e.Example.doWork: <

Аргументов и результата

@Log
public String doWork(String strParam, int numParam) {
    return "testResult";
}
@Log
fun doWork(strParam: String, numParam: Int): String {
    return "testResult"
}
Уровень логгирования Лог
TRACE, DEBUG

DEBUG [main] r.t.e.e.Example.doWork: > {data: {strParam: "s", numParam: "4"}}

DEBUG [main] r.t.e.e.Example.doWork: < {data: {out: "testResult"}}

INFO

INFO [main] r.t.e.e.Example.doWork: >

INFO [main] r.t.e.e.Example.doWork: <

Выборочное логирование

@Log.out
@Log.off
public String doWork(String strParam, int numParam) {
    return "testResult";
}
@Log.out
@Log.off
fun doWork(strParam: String, numParam: Int): String {
    return "testResult"
}
Уровень логгирования Лог
TRACE, DEBUG

INFO [main] r.t.e.e.Example.doWork: <

INFO

INFO [main] r.t.e.e.Example.doWork: <

Структурированный параметр

В случае если представление параметра как строкой не является желаемым поведением, его можно реализовать интерфейс StructuredArgument и параметр научится логировать себя сам:

public record Entity(String name, String code) implements StructuredArgument {

    @Override
    public String fieldName() {
        return "name";
    }

    @Override
    public void writeTo(JsonGenerator generator) throws IOException {
        generator.writeString(name);
    }
}

@Log.in
public String doWork(Entity entity) {
    return "testResult";
}
data class Entity(val name: String, val code: String) : StructuredArgument {

    override fun writeTo(generator: JsonGenerator) = generator.writeString(name)

    override fun fieldName(): String = "name"
}

@Log.`in`
fun doWork(entity: Entity): String {
    return "testResult"
}
Уровень логгирования Лог
TRACE, DEBUG

INFO [main] r.t.e.e.Example.doWork: >

     data={"entity":"Bob"}

INFO

INFO [main] r.t.e.e.Example.doWork: >

     data={"entity":"Bob"}

Конвертация параметров

В случае если представление параметра как строкой не является желаемым поведением, его можно переназначить через указание StructuredArgumentMapper напротив желаемого аргумента:

public record Entity(String name, String code) { }

public final class EntityLogMapper implements StructuredArgumentMapper<Entity> {
    public void write(JsonGenerator gen, Entity value) throws IOException {
        gen.writeString(value.name());
    }
}

@Log.in
public String doWork(@Mapping(EntityLogMapper.class) Entity entity) {
    return "testResult";
}
data class Entity(val name: String, val code: String)

class EntityLogMapper : StructuredArgumentMapper<Entity> {

    @Throws(IOException::class)
    override fun write(gen: JsonGenerator, value: Entity) = gen.writeString(value.name)
}

@Log.`in`
fun doWork(@Mapping(EntityLogMapper::class) entity: Entity): String {
    return "testResult"
}
Уровень логгирования Лог
TRACE, DEBUG

INFO [main] r.t.e.e.Example.doWork: >

     data={"entity":"Bob"}

INFO

INFO [main] r.t.e.e.Example.doWork: >

     data={"entity":"Bob"}

MDC (Mapped Diagnostic Context)

Аннотация @Mdc позволяет добавлять пары ключ-значение в MDC (Mapped Diagnostic Context) для структурированного логирования. MDC позволяет добавлять контекстную информацию к каждому лог-сообщению.

Аннотация может применяться к методам и параметрам методов. Поддерживается множественное применение.

Параметры аннотации @Mdc:

  • key() - Ключ для MDC записи. Если не указано, используется имя аннотированного параметра.
  • value() - Значение для MDC записи. Если не указано, используется значение аннотированного параметра.
  • global() - Если true, MDC значение будет доступно глобально в рамках потока, а не только во время выполнения метода.

Аннотация параметра

public String test(@Mdc String s) {
    return "1";
}
fun test(@Mdc s: String): String {
    return "1"
}

В этом случае ключ MDC будет совпадать с именем параметра ("s"), а значением будет значение параметра.

Аннотация параметра с ключом

public String test(@Mdc(key = "123") String s) {
    return "1";
}
fun test(@Mdc(key = "123") s: String): String {
    return "1"
}

Здесь ключ MDC будет "123", а значением - значение параметра "s".

Аннотация метода

@Mdc(key = "key1", value = "value2")
public String test(String s) {
    return "1";
}
@Mdc(key = "key1", value = "value2")
fun test(s: String): String {
    return "1"
}

В этом примере демонстрируется: - Аннотация метода с локальным MDC значением

Комбинированное

@Mdc(key = "key", value = "value", global = true)
@Mdc(key = "key1", value = "value2")
public String test(@Mdc(key = "123") String s) {
    return "1";
}
@Mdc(key = "key", value = "value", global = true)
@Mdc(key = "key1", value = "value2")
fun test(@Mdc(key = "123") s: String): String {
    return "1"
}

В этом примере к методу применены две аннотации MDC, а к параметру одна аннотация.

Генерация значения из кода

@Mdc(key = "key", value = "${java.util.UUID.randomUUID().toString()}")
public String test(String s) {
    return "1";
}
@Mdc(key = "key", value = "\${java.util.UUID.randomUUID().toString()}")
fun test(s: String): String {
    return "1";
}

При вызове метода в MDC будет добавлена запись с ключом "key" и в данном случае значением будет случайный UUID.

Пример лога с MDC:

INFO [main] r.t.e.e.Example.test: > {data: {s: "testValue"}} key=some-uuid-value key1=value2 123=testValue

Сигнатуры

Доступные сигнатуры для методов которые поддерживают аннотации из коробки:

Класс не должен быть final, чтобы аспекты работали.

Под T подразумевается тип возвращаемого значения, либо Void.

Класс должен быть open, чтобы аспекты работали.

Под T подразумевается тип возвращаемого значения, либо T?, либо Unit.