Есть ли рекомендуемый способ получить весеннюю загрузку в журналы формата JSON с logback
Использование пружинной загрузки 2.1.1.RELEASE
можно, казалось бы, отформатировать журналы как JSON, предоставив logback-spring.xml
файл следующим образом:
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.contrib.json.classic.JsonLayout">
<timestampFormat>yyyy-MM-dd'T'HH:mm:ss.SSSX</timestampFormat>
<timestampFormatTimezoneId>Etc/UTC</timestampFormatTimezoneId>
<jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter">
<prettyPrint>true</prettyPrint>
</jsonFormatter>
</layout>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout" />
</root>
и добавление к pom.xml
<dependency>
<groupId>ch.qos.logback.contrib</groupId>
<artifactId>logback-json-classic</artifactId>
<version>0.1.5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback.contrib</groupId>
<artifactId>logback-jackson</artifactId>
<version>0.1.5</version>
</dependency>
действительно приводит к таким сообщениям, как:
{
"timestamp" : "2018-12-11T18:20:25.641Z",
"level" : "INFO",
"thread" : "main",
"logger" : "com.netflix.config.sources.URLConfigurationSource",
"message" : "To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.",
"context" : "default"
}
Зачем?
Я пробую https://logz.io/, который, кажется, ведет себя более благоприятно, когда журналы отформатированы в формате JSON, некоторые грузоотправители борются с многострочными журналами, как мы видим в трассировках стека Java, и при форматировании в JSON он может автоматически анализировать поля, такие как level
а также message
и если есть данные MDC, он автоматически получает их.
У меня был небольшой опыт использования нескольких методов доставки журналов в logzio, таких как их образ докера и использование rsyslog без использования сообщений журнала в формате JSON.
Проблемы с этим подходом
Это нормально работает для добавления консоли, но весенняя загрузка обеспечивает как logging.file=test.log
, logging.level.com.example=WARN
, logging.pattern.console
, Я действительно могу импортировать управляемую конфигурацию из spring-boot-2.1.1.RELEASE.jar!/org/springframework/boot/logging/logback/base.xml
который в свою очередь импортирует console-appender.xml and
файл-appender.xml`.
Пример консоли-аппендера
<included>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
</included>
Пример файла appender
<included>
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
<maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize>
<maxHistory>${LOG_FILE_MAX_HISTORY:-0}</maxHistory>
</rollingPolicy>
</appender>
</included>
Эти два как раз то, что мне нужно для поддержки весенней конфигурации свойств, но они не включают кодировщик / макет, который мне понадобится.
В моих первоначальных тестах оказалось, что я не могу просто назвать моего аппендера такими же, как те, и предоставить свои макеты. Например:
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.contrib.json.classic.JsonLayout">
<timestampFormat>yyyy-MM-dd'T'HH:mm:ss.SSSX</timestampFormat>
<timestampFormatTimezoneId>Etc/UTC</timestampFormatTimezoneId>
<jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter">
<prettyPrint>true</prettyPrint>
</jsonFormatter>
</layout>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
приводит к тому, что сообщение регистрируется как в формате JSON, так и в формате обычного текста.
Я действительно могу просто скопировать и вставить содержимое этих 3 файлов в мою пользовательскую конфигурацию, а не импортировать их вообще. Тогда я могу переопределить то, что хочу настроить.
Тем не менее, по мере развития Spring и выпуска новых выпусков, которые могут добавлять новые функции, я бы навсегда заставлял себя следить, копировать и вставлять новые файлы, вносить изменения и тестировать их.
Есть ли лучший способ, которым я могу либо:
- Просто вносите аддитивные изменения в appenders, а не переопределяйте их полностью, например, сохраняйте конфигурацию весной, но предоставьте мой собственный кодер или макет для использования этими appenders.
- Настроить Spring в JSON log через свойства полностью без каких-либо настроек - я сомневаюсь в этом:S
Сноска: logzio действительно обеспечивает зависимость, которую можно импортировать, но мне не нравится идея напрямую связывать провайдера журналирования с кодом. Я чувствую, что, если серво генерирует журналы JSON для stdout или файла, любой провайдер может легко их обработать и отправить в какой-либо пункт назначения.
5 ответов
Я не использую никаких зависимостей. Просто сделайте это через application.yml, и все. Это решение также решает проблему с многострочным журналом.
logging:
pattern:
console: "{\"time\": \"%d\", \"level\": \"%p\", \"correlation-id\": \"%X{X-Correlation-Id}\", \"source\": \"%logger{63}:%L\", \"message\": \"%replace(%m%wEx{6}){'[\r\n]+', '\\n'}%nopex\"}%n"
Я использую что-то вроде следующего, всегда работало нормально.
Рекомендация Spring Boot - назвать файл logback-spring.xml
и поместите его под src/main/resources/
, это позволяет нам использовать пружинные профили при входе в систему. Итак, в файле ниже вы увидите, что дляLOCAL
profile вы можете войти в стандартный режим, но для развертываний на сервере или в контейнере вы можете использовать другую стратегию ведения журнала.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [YourApp:%thread:%X{X-B3-TraceId}:%X{X-B3-SpanId}] %logger{40} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="jsonstdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<providers>
<timestamp>
<timeZone>EST</timeZone>
</timestamp>
<pattern>
<pattern>
{
"level": "%level",
"service": "YourApp",
"traceId": "%X{X-B3-TraceId:-}",
"spanId": "%X{X-B3-SpanId:-}",
"thread": "%thread",
"class": "%logger{40}",
"message": "%message"
}
</pattern>
</pattern>
<stackTrace>
<throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
<maxDepthPerThrowable>30</maxDepthPerThrowable>
<maxLength>2048</maxLength>
<shortenedClassNameLength>20</shortenedClassNameLength>
<rootCauseFirst>true</rootCauseFirst>
</throwableConverter>
</stackTrace>
</providers>
</encoder>
</appender>
<root level="info">
<springProfile name="LOCAL">
<appender-ref ref="stdout" />
</springProfile>
<springProfile name="!LOCAL">
<appender-ref ref="jsonstdout" />
</springProfile>
</root>
</configuration>
Похоже, вам нужно скопировать пасту с модификациями 3 из 4 файлов отсюда https://github.com/spring-projects/spring-boot/tree/v2.1.1.RELEASE/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback в вашу конфигурацию.
Хорошая новость в том, что вам не нужно копировать пасту https://github.com/spring-projects/spring-boot/blob/v2.1.1.RELEASE/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml
Это можно включить так <include resource="org/springframework/boot/logging/logback/default.xml"/>
Это даст вам некоторую конфигурацию Spring по умолчанию без копирования и вставки
Если вы переключитесь на log4j2, используя метод, указанный в документации Spring Boot
implementation "org.springframework.boot:spring-boot-starter-log4j2"
modules {
module("org.springframework.boot:spring-boot-starter-logging") {
replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback")
}
}
У вас может быть файл log4j2.properties, содержащий следующее.
appender.stdout.type=Console
appender.stdout.name=json
appender.stdout.json.type=JsonTemplateLayout
appender.stdout.json.eventTemplateUri=classpath:LogstashJsonEventLayoutV1.json
appender.console.type=Console
appender.console.name=console
rootLogger.appenderRef.stdout.ref=${env:LOGGING_APPENDER:-json}
я использовалLOGGING_APPENDER
чтобы соответствовать моим переопределениям уровня среды.
Обратите внимание на один странный недостаток... это не работает, когда вы используете JDK17.
Этот подход предназначен для использования Logback с одной дополнительной зависимостью.
runtimeOnly("net.logstash.logback:logstash-logback-encoder:7.4")
Иlogback-spring.xml
который содержит
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="warn">
<appender-ref ref="${LOGGING_APPENDER:-JSON}"/>
</root>
<!-- this silences warnings when the appenders are not active -->
<logger name="nil">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="JSON"/>
</logger>
</configuration>