Блоги

Навигационные полоски

Логирование AWS Lambda на Java

При реализации проекта для AWS Lambda на языке Java я в какой-то момент столкнулся с тем, что в логи (которые для AWS Lambda доступны через сервис CloudWatch) сыпется все что угодно и в каком разном виде. По факту - оказывалось что уровень логирования почему-то TRACE, в результате чего мне в логи выводился в том числе контент документов, скачиваемых с S3, и как итог пользоваться логами было невозможно.

Официальная документация по логированию на Java для AWS Lambda доступен на сайте Amazon.  Вроде все понятно - Log4j2 + Custom Appender - но даже после того как сделал все как написано (добавил необходимый artifact в maven dependencies) ничего не заработало как надо.

Как оказалось - проблема в том, что в моем проекте использовалась целая куча библиотек, каждая из которых тянула за собой свой логгер - в итоге у меня в classpath были и log4j2 (который я честно добавил), и первый log4j, и commons-logging, и slf4j, и logback....мне кажется - был полный набор всего, чего только можно...

В итоге задача сводилась к тому, чтобы через exclusions убрать все лишнее, и там где можно настроить, чтобы финальное логирование шло в итоге через log4j2. В этом, кстати, очень помогает вкладка Dependency Hierarhy на pom-файле в Eclipse. Итак, в моем случае я сделал следующее:

Как и положено добавил в проект  Custom Appender для логирования в CloudWatch:

        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-log4j2</artifactId>
            <version>1.1.0</version>
        </dependency>

Положил файл конфигурации log4j2.xml в ресурсы:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration packages="com.amazonaws.services.lambda.runtime.log4j2">
  <Appenders>
    <Lambda name="Lambda">
      <PatternLayout>
          <pattern>%X{AWSRequestId} %-5p %c{1}:%L - %m%n</pattern>
      </PatternLayout>
    </Lambda>
  </Appenders>
  <Loggers>
    <Root level="info">
      <AppenderRef ref="Lambda" />
    </Root>
  </Loggers>
</Configuration>

Далее добавил log4j-jcl и log4j-slf4j-impl чтобы commons-logging и slf4j выводили логи через log4j2:

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-jcl</artifactId>
            <version>2.12.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.12.1</version>
        </dependency>

Дальше из spring-boot-starter-web я убрал log4j-to-slf4j (то есть изначально все работало так - что log4j выводил через slf4j а мне надо наоборот, и logback-classic - иначе slf4j находил его и делал вывод через него

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.logging.log4j</groupId>
                    <artifactId>log4j-to-slf4j</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

В итоге получилось все как надо:

  • Те библиотеки, которые использовали commons-loggins продолжали его использовать, но благодаря log4j-jcl вывод осуществлялся через log4j2
  • Те библиотеки, которые использовали Java Utils Loggins за счет того что в classpath был jul-slf4j (как транзитивная зависимость в spring-boot-starter-web) выводили через slf4j.
  • За счет того, что был подключен log4j-slf4j-impl вывод из него осуществлялся через тот же log4j2
  • На я для него у нас был подключен специальный Appender

Итого все пути ведут в Рим - то есть в log4j2, уровнями логирования мы теперь управляем в одном файле, как и форматом вывода.

14.09.2019