1. Introduction
Micronaut Log4AWS simplifies integration of AWS Lambda functions written with Micronaut into Sentry service.
2. Installation
First, add this dependency to your build file.
repositories {
mavenCentral()
}
dependencies {
implementation "com.agorapulse:micronaut-log4aws:4.0.1"
}
There are variants for older Micronaut versions as well. You can use micronaut-1.0 or micronaut-2.0 suffix for
different versions of Micronaut.
|
Also be sure that your build file does not contain any reference to Logback:
dependencies {
// remove following line from your build file if present
runtime "ch.qos.logback:logback-classic:1.2.3"
}
Then remove Logback configuration file from src/main/resources/logback.groovy
and replace it with a new file log4j2.xml
with following content. Feel free to update the file to fit your needs.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration packages="org.apache.logging.log4j.core,io.sentry.log4j2,com.amazonaws.services.lambda.runtime.log4j2">
<Appenders>
<Lambda name="Lambda">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %X{AWSRequestId} %-5p %c{1}:%L - %m%n"/>
</Lambda>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<Sentry name="Sentry"/>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Lambda"/>
<!-- Note that the Sentry logging threshold is overridden to the WARN level -->
<AppenderRef ref="Sentry" level="warn"/>
</Root>
<Logger name="io.sentry" level="warn" additivity="false">
<AppenderRef ref="Console" />
</Logger>
<Logger name="software.amazon" level="warn" additivity="false">
<AppenderRef ref="Console" />
</Logger>
</Loggers>
</Configuration>
The library can configure Sentry appender for you if you don’t specify it in the configuration file.
|
Finally, create src/main/resources/sentry.properties
for sentry configuration:
# consider adding your packages here to enable filtering of stack traces in Sentry
# in-app-includes=agorapulse,com.agorapulse
3. Usage
3.1. Sentry Integration
Sentry logger is initialised automatically at application startup. Sentry’s IHub
bean is available for injection to access the lower-level API.
Sentry events are enriched with the following information if available:
-
aws_region
-
aws_default_region
-
lambda_function_name
-
lambda_function_version
-
lambda_handler
-
lambda_execution_environment
-
lambda_log_group_name
-
lambda_log_stream_name
-
lambda_memory_size
-
req.path
-
req.method
-
req.remoteHost
-
req.serverHost
-
req.parameters
-
req.headers
3.2. Ensure Exception Being Logged
3.2.1. Using @LogError
All the errors have to be logged to be propagated to Sentry. You can use LogErrors
around advice
with your entry-point method to simplify the logging boilerplate:
/**
* This class is an entrypoint called by AWS Lambda. Keep the code to minimum.
* Method handleRequest is called directly by the AWS Lambda container.
*/
class ReportsExporter extends FunctionInitializer implements RequestHandler<SQSEvent, Void> {
@Inject private ReportsService reportsService;
Void handleRequest(SQSEvent event, Context context) {
return reportsService.handlerRequest(event);
}
}
/**
* This class is handled by Micronaut and can use the advice.
*/
@Singleton
class ReportsService implements RequestHandler<SQSEvent, Void> {
@LogError
Void handleRequest(SQSEvent event, Context context) {
return reportsService.handlerRequest(event);
}
}
The annotation applied to the function class itself has no effect as the function class is executed by AWS Lambda container. |
3.2.2. Using LoggingFunctionInitializer
You can extend LoggingFunctionInitializer
instead of FunctionInitializer
to ensure the problems with function initialization are logged.
3.2.3. Using Logging Request Handlers
You can use Logging
class to ensure the errors are logged properly:
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class LoggingRequestHandler extends RequestHandler<Input, Output> {
@Override
public Output handleRequest(Input input, Context context) {
return Logging.callAndRethrow(getClass(), "Exception running handler", () -> doHandleRequest(input, context));
}
private Output doHandleRequest(Input input, Context context) {
// ...
}
}