28

I am using a lambda function and writing it in Java. I was looking up logging for Lambda functions when I read the docs and they support log4j - http://docs.aws.amazon.com/lambda/latest/dg/java-logging.html#java-wt-logging-using-log4j.

I was wondering if we could use logging using the Slf4j annotation as well since Slf4j is only a binding annotation. Has anybody tried using Slf4j before with lambda?

chrisrhyno2003
  • 3,906
  • 8
  • 53
  • 102

5 Answers5

19

Yes, you can. Just add the following dependencies to your project:

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.7.25</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.25</version>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-log4j</artifactId>
        <version>1.0.0</version>
    </dependency>

and create correct log4j.properties in /src/main/resources/ of your project, e.g.

log = .
log4j.rootLogger = DEBUG, LAMBDA

# Define the LAMBDA appender
log4j.appender.LAMBDA=com.amazonaws.services.lambda.runtime.log4j.LambdaAppender
log4j.appender.LAMBDA.layout=org.apache.log4j.PatternLayout
log4j.appender.LAMBDA.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} <%X{AWSRequestId}> %-5p %c{1}:%m%n
Dmitriy Popov
  • 2,150
  • 3
  • 25
  • 34
foal
  • 693
  • 7
  • 20
  • 4
    Can anyone confirm that this works in SAM Local? No matter what I do with dependencies and shade, I keep getting "SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder"." – Tremelune Apr 25 '18 at 21:24
  • 2
    Hi, what does %-5p %c{1}:%m%n signify here? – Manthan Jamdagni Jul 05 '18 at 08:24
  • 1
    @ManthanJamdagni it just specifies the format of the log message: `%-5p` — 5 characters for log priority, `%c{1}` — category of the event, `%m` — log message, `%n` — new line character (line separator). See https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html for details. – Dmitriy Popov Feb 19 '20 at 09:07
16

The jlib AWS Lambda Logback Appender allows you to use SLF4J with Logback from your AWS Lambda functions.

Simply add these dependencies:

Gradle (build.gradle)

dependencies {
    implementation 'org.slf4j:slf4j-api:1.8.0-beta2'
    runtimeOnly 'org.jlib:jlib-awslambda-logback:1.0.0'
}

Maven (pom.xml)

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.8.0-beta2</version>
</dependency>
<dependency>
    <groupId>org.jlib</groupId>
    <artifactId>jlib-awslambda-logback</artifactId>
    <version>1.0.0</version>
    <scope>runtime</scope>
</dependency>

Then use the AwsLambdaAppender in your logging configuration:

Example XML configuration (src/main/resources/logback.xml)

<configuration>

    <appender name="awslambda" class="org.jlib.cloud.aws.lambda.logback.AwsLambdaAppender">
        <encoder type="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] &lt;%-36X{AWSRequestId:-request-id-not-set-by-lambda-runtime}&gt;
%-5level %logger{10} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="awslambda" />
    </root>

</configuration>

Unlike other solutions, this Logback Appender correctly handles multi-line log messages, especially stack traces, and produces only a single CloudWatch Logs event per message.

The library also allows you to include the AWSRequestId, provided by the AWS Lambda runtime, in every single log message for better tracing.

While log4j2 requires additional handling in the build when you create an uber-jar, this solution works out of the box.

Disclaimer: I'm the developer of jlib

Brooks
  • 7,099
  • 6
  • 51
  • 82
Igor Akkerman
  • 2,348
  • 1
  • 21
  • 24
  • 3
    Thanks for the help. Though your solution did not solve my issue but it actually help me to figure out my issue and fix :) – privatejava Aug 30 '18 at 07:48
  • I noticed this only works in the class of your Lambda handler, any external class/package in the same project (so with the same logback.xml) will NOT print to cloudwatch with this. Did anybody manage to work around this? – Ghilteras Feb 06 '19 at 18:34
  • @Ghilteras Could you please open an issue on GitHub and provide an example? I'd be very interested to understand and fix any potential issue. – Igor Akkerman Feb 06 '19 at 18:40
  • @IgorAkkerman I fixed it by defining the logger as static in my main Lambda class and then importing it in my external classes that are part of my Lambda – Ghilteras Feb 21 '19 at 21:57
  • @Ghilteras Thanks for your feedback. It would be very helpful if you could provide a skeletal code example to reproduce the original issue? We should fix the cause, even if a workaround is possible. – Igor Akkerman Feb 21 '19 at 22:04
  • 1
    This provider would be great to work. Unfortunately, I tried the given configuration, and I got `SLF4J: No SLF4J providers were found.` `SLF4J: Defaulting to no-operation (NOP) logger implementation` `SLF4J: See http://www.slf4j.org/codes.html#noProviders for further details.` in log of the lambda function. `System.out` and `LambdaLogger` logs are working. Probably I have to add `slf-log4j logger` to dependencies as well? – Dmitriy Popov Feb 18 '20 at 17:36
  • @DmitriyPopov which Java version are you using? – Igor Akkerman Apr 05 '20 at 22:03
  • @DmitriyPopov I was not able to reproduce the behavior. Would you please provide your configuration details on GitHub? https://github.com/jlib-framework/jlib-awslambda-logback/issues/2 – Igor Akkerman Apr 09 '20 at 22:06
9

Just include following dependency:

<dependency>
    <groupId>io.symphonia</groupId>
    <artifactId>lambda-logging</artifactId>
    <version>1.0.0</version>
</dependency>

Source of lambda-logging code available at https://github.com/symphoniacloud/lambda-monitoring/tree/master/lambda-logging.

Background information available at: https://blog.symphonia.io/a-love-letter-to-lambda-logging-974b0eb49273

koppor
  • 19,079
  • 15
  • 119
  • 161
5

Ever since the AWS guide has been updated with details on Log4j 2 and SLF4J integration. The code snippets below are based on the guide with slf4j and log4j versions updated to the more recent ones.

pom.xml:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-bom</artifactId>
            <version>2.19.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-log4j2</artifactId>
        <version>1.5.1</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j2-impl</artifactId>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.6</version>
    </dependency>
</dependencies>

And src/main/resources/log4j2.xml:

<Configuration status="WARN">
    <Appenders>
        <Lambda name="Lambda">
            <PatternLayout>
                <pattern>%d{yyyy-MM-dd HH:mm:ss} %X{AWSRequestId} %-5p %c{1} - %m%n</pattern>
            </PatternLayout>
        </Lambda>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="Lambda"/>
        </Root>
        <Logger name="software.amazon.awssdk" level="WARN"/>
        <Logger name="software.amazon.awssdk.request" level="DEBUG"/>
    </Loggers>
</Configuration>
Andrey
  • 6,526
  • 3
  • 39
  • 58
  • This may have worked 10 years ago, but now this config/dependencies gives: java.lang.NoSuchMethodError: com.amazonaws.services.lambda.runtime.LambdaLogger.log([B)V at com.amazonaws.services.lambda.runtime.log4j2.LambdaAppender.append(LambdaAppender.java:74) at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156) – Chris Wolf May 30 '21 at 15:03
  • 1
    @ChrisWolf works like a charm today as well on all latest versions of the libs. Most likely you have old version of aws-lambda-java-core lib somewhere. Make sure it is 1.2.1, as mandated by aws-lambda-java-log4j2. – Andrey May 31 '21 at 11:37
  • it's not my code and their version of aws-lambda-java-core is 1.1.0, but I got it working by downgrading aws-lambda-java-log4j2 to 1.0.0. I will work with the maintainer to upgrade both. – Chris Wolf Jun 24 '21 at 15:38
  • https://logging.apache.org/log4j/2.x/log4j-slf4j-impl/ "As of release 2.19.0 the log4j-slf4j18-impl module targetting the unreleased SLF4J 1.8.x series has been removed." Used log4j-slf4j2-impl instead with added bonus: "Applications that take advantage of the Java Module System should use SLF4J 2.0.x and log4j-slf4j2-impl." – Chris Dec 29 '22 at 20:29
  • Thanks @Chris, updated the answer for the latest versions of slf4j and log4j. – Andrey Jan 03 '23 at 04:11
0

Be aware that lambda requires the use of a shadowJar (shaded jar) which does not work correctly with log4j2 without an additional transformation, this is known albeit somewhat hard to grok issue...

To try to bring all these posts together and to clarify in order to enable faster resolution of this issue see below

Rhineb
  • 305
  • 3
  • 12