Есть ли рекомендуемый способ получить весеннюю загрузку в журналы формата 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>
Другие вопросы по тегам