diff --git a/examples/micronaut-log4aws-demo/micronaut-log4aws-demo.gradle b/examples/micronaut-log4aws-demo/micronaut-log4aws-demo.gradle new file mode 100644 index 0000000..58fbcfd --- /dev/null +++ b/examples/micronaut-log4aws-demo/micronaut-log4aws-demo.gradle @@ -0,0 +1,83 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020 Agorapulse. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +plugins { + id 'groovy' + id "jp.classmethod.aws.lambda" + id "com.github.johnrengelman.shadow" +} + +config { + bintray { + enabled = true + } +} + +dependencies { + compile project(':micronaut-log4aws') + + annotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion") + compile platform("io.micronaut:micronaut-bom:$micronautVersion") + testAnnotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion") + testCompile platform("io.micronaut:micronaut-bom:$micronautVersion") + + annotationProcessor "io.micronaut:micronaut-inject-java" + + compile "io.sentry:sentry-log4j2:$sentryVersion" + compile group: 'com.amazonaws', name: 'aws-lambda-java-log4j2', version: awsLog4jVersion + compile "org.apache.logging.log4j:log4j-slf4j18-impl:$log4jVersion" + + compile "io.micronaut:micronaut-core" + compile "io.micronaut:micronaut-function" + + compileOnly "io.micronaut:micronaut-inject-groovy" + + + + testAnnotationProcessor "io.micronaut:micronaut-inject-java" + + testCompile "io.micronaut:micronaut-inject-groovy" + testCompile "com.github.stefanbirkner:system-rules:$systemRulesVersion" + testCompile("org.spockframework:spock-core") { + exclude group: "org.codehaus.groovy", module: "groovy-all" + } + +} + + +shadowJar { + mergeServiceFiles() + mergeGroovyExtensionModules() + transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer) +} + +aws { + profileName = 'beta' + region = 'eu-west-1' +} + +task deploy(type: jp.classmethod.aws.gradle.lambda.AWSLambdaMigrateFunctionTask, dependsOn: shadowJar) { + functionName = "MicronautLog4AwsDemo" + handler = "com.agorapulse.micronaut.log4aws.demo.LoggingDemo::apply" + role = "arn:aws:iam::${aws.accountId}:role/lambda-basic-role" + runtime = com.amazonaws.services.lambda.model.Runtime.Java8 + zipFile = shadowJar.archivePath + memorySize = 1024 + timeout = 300 + publish = true +} + diff --git a/examples/micronaut-log4aws-demo/src/main/java/com/agorapulse/micronaut/log4aws/demo/LoggingDemo.java b/examples/micronaut-log4aws-demo/src/main/java/com/agorapulse/micronaut/log4aws/demo/LoggingDemo.java new file mode 100644 index 0000000..0f5192d --- /dev/null +++ b/examples/micronaut-log4aws-demo/src/main/java/com/agorapulse/micronaut/log4aws/demo/LoggingDemo.java @@ -0,0 +1,23 @@ +package com.agorapulse.micronaut.log4aws.demo; + +import io.micronaut.function.FunctionBean; +import io.micronaut.function.executor.FunctionInitializer; + +import javax.inject.Inject; +import java.util.Map; +import java.util.function.Function; + +@FunctionBean( + method = "apply", + name = "logging-demo" +) +public class LoggingDemo extends FunctionInitializer implements Function, String> { + + @Inject private LoggingService loggingService; + + @Override + public String apply(Map payload) { + return loggingService.apply(payload); + } + +} diff --git a/examples/micronaut-log4aws-demo/src/main/java/com/agorapulse/micronaut/log4aws/demo/LoggingService.java b/examples/micronaut-log4aws-demo/src/main/java/com/agorapulse/micronaut/log4aws/demo/LoggingService.java new file mode 100644 index 0000000..8d1a531 --- /dev/null +++ b/examples/micronaut-log4aws-demo/src/main/java/com/agorapulse/micronaut/log4aws/demo/LoggingService.java @@ -0,0 +1,44 @@ +package com.agorapulse.micronaut.log4aws.demo; + +import com.agorapulse.micronaut.log4aws.LogError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Singleton; +import java.util.Map; +import java.util.stream.Collectors; + +@Singleton +public class LoggingService { + + private static final Logger LOGGER = LoggerFactory.getLogger(LoggingService.class); + + @LogError + String apply(Map payload) { + if (payload.containsKey("trace")) { + LOGGER.trace(payload.get("trace")); + } + + if (payload.containsKey("debug")) { + LOGGER.debug(payload.get("debug")); + } + + if (payload.containsKey("warn")) { + LOGGER.warn(payload.get("warn")); + } + + if (payload.containsKey("error")) { + LOGGER.error(payload.get("error"), new RuntimeException(payload.get("error"))); + } + + if (payload.containsKey("env")) { + LOGGER.debug(System.getenv().entrySet().stream().map(e -> e.getKey() + ": " + e.getValue()).collect(Collectors.joining("\n"))); + } + + if (payload.containsKey("throw")) { + throw new RuntimeException(payload.get("throw")); + } + return "nothing to do"; + } + +} diff --git a/examples/micronaut-log4aws-demo/src/main/resources/log4j2.xml b/examples/micronaut-log4aws-demo/src/main/resources/log4j2.xml new file mode 100644 index 0000000..c86f367 --- /dev/null +++ b/examples/micronaut-log4aws-demo/src/main/resources/log4j2.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/micronaut-log4aws-demo/src/main/resources/sentry.properties b/examples/micronaut-log4aws-demo/src/main/resources/sentry.properties new file mode 100644 index 0000000..91a4f8f --- /dev/null +++ b/examples/micronaut-log4aws-demo/src/main/resources/sentry.properties @@ -0,0 +1,21 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright 2020 Agorapulse. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +async=false + +stacktrace.app.packages=com.agorapulse diff --git a/examples/micronaut-log4aws-demo/src/test/groovy/com/agorapulse/micronaut/log4aws/demo/LoggingServiceSpec.groovy b/examples/micronaut-log4aws-demo/src/test/groovy/com/agorapulse/micronaut/log4aws/demo/LoggingServiceSpec.groovy new file mode 100644 index 0000000..26b5b1f --- /dev/null +++ b/examples/micronaut-log4aws-demo/src/test/groovy/com/agorapulse/micronaut/log4aws/demo/LoggingServiceSpec.groovy @@ -0,0 +1,26 @@ +package com.agorapulse.micronaut.log4aws.demo + +import io.micronaut.context.ApplicationContext +import spock.lang.AutoCleanup +import spock.lang.Specification + +class LoggingServiceSpec extends Specification { + + @AutoCleanup ApplicationContext context = ApplicationContext.build().build().start() + + LoggingService service = context.getBean(LoggingService) + + void 'exception thrown'() { + when: + service.apply( + trace: 'Testing Trace', + debug: 'Testing Debug', + warn: 'Testing Warning', + error: 'Testing Error', + throw: 'Testing Exception' + ) + then: + thrown(RuntimeException) + } + +} diff --git a/gradle.properties b/gradle.properties index 6ec2bda..a98df4f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,10 +16,15 @@ # limitations under the License. # -# TODO: replace -slug=agorapulse/micronaut-log4aws -group=com.agorapulse -kordampPluginVersion=0.31.2 -coverallsPluginVersion=2.9.0 -gitPublishPluginVersion=2.1.3 +slug = agorapulse/micronaut-log4aws +group = com.agorapulse +kordampPluginVersion = 0.31.2 +coverallsPluginVersion = 2.9.0 +gitPublishPluginVersion = 2.1.3 + +micronautVersion = 1.3.7 +sentryVersion = 1.7.30 +log4jVersion = 2.13.3 +awsLog4jVersion = 1.2.0 +systemRulesVersion = 1.19.0 diff --git a/settings.gradle b/settings.gradle index 6e481fa..6898f4f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -43,13 +43,15 @@ buildscript { force = true } classpath 'org.ajoberstar.grgit:grgit-core:4.0.1' + classpath 'jp.classmethod.aws:gradle-aws-plugin:0.41' + classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0' } } apply plugin: 'org.kordamp.gradle.settings' projects { - directories = ['subprojects', 'docs'] + directories = ['subprojects', 'docs', 'examples'] } rootProject.name = 'micronaut-log4aws-root' diff --git a/subprojects/micronaut-log4aws/micronaut-log4aws.gradle b/subprojects/micronaut-log4aws/micronaut-log4aws.gradle index 392edbb..97aa8e0 100644 --- a/subprojects/micronaut-log4aws/micronaut-log4aws.gradle +++ b/subprojects/micronaut-log4aws/micronaut-log4aws.gradle @@ -22,23 +22,25 @@ config { } dependencies { - annotationProcessor platform('io.micronaut:micronaut-bom:1.2.8') + annotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion") + compile platform("io.micronaut:micronaut-bom:$micronautVersion") + testAnnotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion") + testCompile platform("io.micronaut:micronaut-bom:$micronautVersion") + annotationProcessor "io.micronaut:micronaut-inject-java" - compile platform('io.micronaut:micronaut-bom:1.2.8') - compile 'io.sentry:sentry-log4j2:1.7.29' - compile group: 'com.amazonaws', name: 'aws-lambda-java-log4j2', version: '1.2.0' - compile 'org.apache.logging.log4j:log4j-slf4j18-impl:2.13.3' + compile "io.sentry:sentry-log4j2:$sentryVersion" + compile group: 'com.amazonaws', name: 'aws-lambda-java-log4j2', version: awsLog4jVersion + compile "org.apache.logging.log4j:log4j-slf4j18-impl:$log4jVersion" compile "io.micronaut:micronaut-core" compileOnly "io.micronaut:micronaut-inject-groovy" - testAnnotationProcessor platform('io.micronaut:micronaut-bom:1.2.8') + testAnnotationProcessor "io.micronaut:micronaut-inject-java" - testCompile platform('io.micronaut:micronaut-bom:1.2.8') testCompile "io.micronaut:micronaut-inject-groovy" - testCompile 'com.github.stefanbirkner:system-rules:1.19.0' + testCompile "com.github.stefanbirkner:system-rules:$systemRulesVersion" testCompile("org.spockframework:spock-core") { exclude group: "org.codehaus.groovy", module: "groovy-all" } diff --git a/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/AwsLambdaEventBuildHelper.java b/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/AwsLambdaEventBuildHelper.java new file mode 100644 index 0000000..0a8f456 --- /dev/null +++ b/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/AwsLambdaEventBuildHelper.java @@ -0,0 +1,21 @@ +package com.agorapulse.micronaut.log4aws; + +import io.sentry.event.EventBuilder; +import io.sentry.event.helper.EventBuilderHelper; + +public class AwsLambdaEventBuildHelper implements EventBuilderHelper { + + @Override + public void helpBuildingEvent(EventBuilder eventBuilder) { + eventBuilder.withTag("aws_region", System.getenv("AWS_REGION")); + eventBuilder.withTag("aws_default_region", System.getenv("AWS_DEFAULT_REGION")); + eventBuilder.withTag("lambda_function_name", System.getenv("AWS_LAMBDA_FUNCTION_NAME")); + eventBuilder.withTag("lambda_function_version", System.getenv("AWS_LAMBDA_FUNCTION_VERSION")); + eventBuilder.withTag("lambda_handler", System.getenv("_HANDLER")); + eventBuilder.withTag("lambda_execution_environment", System.getenv("AWS_EXECUTION_ENV")); + eventBuilder.withTag("lambda_log_group_name", System.getenv("AWS_LAMBDA_LOG_GROUP_NAME")); + eventBuilder.withTag("lambda_log_stream_name", System.getenv("AWS_LAMBDA_LOG_STREAM_NAME")); + eventBuilder.withTag("lambda_memory_size", System.getenv("AWS_LAMBDA_FUNCTION_MEMORY_SIZE")); + } + +} diff --git a/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/SentryClientFactory.java b/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/SentryClientFactory.java index b7f5f0f..cc318b3 100644 --- a/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/SentryClientFactory.java +++ b/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/SentryClientFactory.java @@ -22,21 +22,32 @@ import io.micronaut.context.annotation.Factory; import io.sentry.Sentry; import io.sentry.SentryClient; +import io.sentry.config.Lookup; +import io.sentry.connection.EventSendCallback; +import io.sentry.dsn.Dsn; +import io.sentry.event.Event; import io.sentry.log4j2.SentryAppender; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Singleton; +import static io.sentry.DefaultSentryClientFactory.ASYNC_OPTION; + /** * Initializes Sentry for Micronaut. */ @Factory public class SentryClientFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(SentryClientFactory.class); + private static final String APPENDER_NAME = "Sentry"; + /** * Automatically registers sentry logging during application context startup. * @return sentry appender @@ -44,18 +55,47 @@ public class SentryClientFactory { @Bean @Context public SentryAppender sentryAppender() { + boolean sync = !Boolean.FALSE.toString().equals(Lookup.getDefault().get(ASYNC_OPTION)); + boolean dsnProvided = !Dsn.DEFAULT_DSN.equals(Dsn.dsnLookup()); + + if (sync && dsnProvided) { + // in future releases + // throw new IllegalStateException("Sentry not configured correctly for synchornous calls! Please, create file 'sentry.properties' and add there a line 'async=false'"); + LOGGER.error("Sentry not configured correctly for synchornous calls! Please, create file 'sentry.properties' and add there a line 'async=false'"); + } + Sentry.init(); + SentryClient client = Sentry.getStoredClient(); + client.addBuilderHelper(new AwsLambdaEventBuildHelper()); + client.addEventSendCallback(new EventSendCallback() { + @Override + public void onFailure(Event event, Exception exception) { + LOGGER.error("Failed to send event to Sentry: " + event, exception); + } + + @Override + public void onSuccess(Event event) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Event sent to Sentry: " + event); + } + } + }); + + LoggerContext lc = (LoggerContext) LogManager.getContext(false); Configuration configuration = lc.getConfiguration(); - SentryAppender appender = new SentryAppender(); - appender.start(); + SentryAppender appender = configuration.getAppender(APPENDER_NAME); + if (appender == null) { + appender = new SentryAppender(); + appender.start(); + } configuration.addAppender(appender); LoggerConfig rootLoggerConfig = configuration.getRootLogger(); - rootLoggerConfig.removeAppender(SentryAppender.APPENDER_NAME); - rootLoggerConfig.addAppender(configuration.getAppender(SentryAppender.APPENDER_NAME), Level.WARN, null); + rootLoggerConfig.removeAppender(APPENDER_NAME); + rootLoggerConfig.addAppender(configuration.getAppender(APPENDER_NAME), Level.WARN, null); lc.updateLoggers(); return appender;