From 8c5dff842f6f40cc35272450bf19b1bd34e5ace2 Mon Sep 17 00:00:00 2001 From: musketyr Date: Mon, 27 Jun 2022 09:30:35 +0200 Subject: [PATCH 1/4] Logging Function Initializer --- docs/guide/src/docs/asciidoc/usage.adoc | 4 ++ .../micronaut-log4aws.gradle | 1 + .../function/LoggingFunctionInitializer.java | 54 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java diff --git a/docs/guide/src/docs/asciidoc/usage.adoc b/docs/guide/src/docs/asciidoc/usage.adoc index 94868da..038931e 100644 --- a/docs/guide/src/docs/asciidoc/usage.adoc +++ b/docs/guide/src/docs/asciidoc/usage.adoc @@ -61,3 +61,7 @@ class ReportsService implements RequestHandler { ---- WARNING: The annotation applied to the function class itself has no effect as the function class is executed by AWS Lambda container. + +== Ensure Exception Being Logged with LoggingFunctionInitializer + +You can extend `LoggingFunctionInitializer` instead of `FunctionInitializer` to ensure the problems with function initialization are logged. diff --git a/subprojects/micronaut-log4aws/micronaut-log4aws.gradle b/subprojects/micronaut-log4aws/micronaut-log4aws.gradle index e58e0f8..337183a 100644 --- a/subprojects/micronaut-log4aws/micronaut-log4aws.gradle +++ b/subprojects/micronaut-log4aws/micronaut-log4aws.gradle @@ -48,6 +48,7 @@ dependencies { implementation "io.micronaut:micronaut-core" + compileOnly 'io.micronaut:micronaut-function' compileOnly 'io.micronaut:micronaut-http' implementation 'io.micronaut:micronaut-aop' implementation 'io.reactivex.rxjava2:rxjava' diff --git a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java new file mode 100644 index 0000000..5b635d0 --- /dev/null +++ b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java @@ -0,0 +1,54 @@ +package com.agorapulse.micronaut.log4aws.function; + +import io.micronaut.context.ApplicationContext; +import io.micronaut.function.executor.FunctionInitializer; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; + +public class LoggingFunctionInitializer extends FunctionInitializer { + + public LoggingFunctionInitializer() { + // for AWS startup + super(); + } + + public LoggingFunctionInitializer(ApplicationContext applicationContext) { + super(applicationContext); + } + + public LoggingFunctionInitializer(ApplicationContext applicationContext, boolean inject) { + super(applicationContext, inject); + } + + @Override + protected void startThis(ApplicationContext applicationContext) { + try { + super.startThis(applicationContext); + } catch (Exception e) { + LoggerFactory.getLogger(getClass()).error("Exception starting the application context", e); + throw e; + } + } + + @Override + @SuppressWarnings("unchecked") + protected ApplicationContext buildApplicationContext(@Nullable Object context) { + try { + return super.buildApplicationContext(context); + } catch (Exception e) { + LoggerFactory.getLogger(getClass()).error("Exception building the application context", e); + throw e; + } + } + + @Override + protected void injectThis(ApplicationContext applicationContext) { + try { + super.injectThis(applicationContext); + } catch (Exception e) { + LoggerFactory.getLogger(getClass()).error("Exception injecting the function handler", e); + throw e; + } + } +} From 07d59320678315ff6af8c49e838a6640e56e2ace Mon Sep 17 00:00:00 2001 From: musketyr Date: Mon, 27 Jun 2022 10:21:15 +0200 Subject: [PATCH 2/4] new base classes and interfaces to avoid loosing exception logging --- docs/guide/src/docs/asciidoc/usage.adoc | 11 ++++- .../micronaut/log4aws/function/Logging.java | 47 +++++++++++++++++++ .../log4aws/function/LoggingConsumer.java | 31 ++++++++++++ .../log4aws/function/LoggingFunction.java | 31 ++++++++++++ .../function/LoggingFunctionInitializer.java | 40 ++++++++-------- .../function/LoggingRequestHandler.java | 31 ++++++++++++ .../function/LoggingRequestStreamHandler.java | 34 ++++++++++++++ 7 files changed, 204 insertions(+), 21 deletions(-) create mode 100644 subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/Logging.java create mode 100644 subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingConsumer.java create mode 100644 subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunction.java create mode 100644 subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestHandler.java create mode 100644 subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestStreamHandler.java diff --git a/docs/guide/src/docs/asciidoc/usage.adoc b/docs/guide/src/docs/asciidoc/usage.adoc index 038931e..fb12cc4 100644 --- a/docs/guide/src/docs/asciidoc/usage.adoc +++ b/docs/guide/src/docs/asciidoc/usage.adoc @@ -24,7 +24,9 @@ Sentry events are enriched with the following information if available: * `req.parameters` * `req.headers` -== Ensure Exception Being Logged with @LogError +== Ensure Exception Being Logged + +=== 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: @@ -62,6 +64,11 @@ class ReportsService implements RequestHandler { WARNING: The annotation applied to the function class itself has no effect as the function class is executed by AWS Lambda container. -== Ensure Exception Being Logged with LoggingFunctionInitializer +=== Using LoggingFunctionInitializer You can extend `LoggingFunctionInitializer` instead of `FunctionInitializer` to ensure the problems with function initialization are logged. + +=== Using Logging Request Handlers + +Yuu can extend either `LoggingRequestHandler` or `LoggingRequestStreamHandler` to ensure the error is logged from the `handleRequest` methods. +You can use `LoggingFunction` and `LoggingConsumer` in case of using the interfaces from `java.util.function` package instead of AWS specific ones. diff --git a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/Logging.java b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/Logging.java new file mode 100644 index 0000000..c4be217 --- /dev/null +++ b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/Logging.java @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 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. + */ +package com.agorapulse.micronaut.log4aws.function; + +import io.sentry.Sentry; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Callable; + +public class Logging { + + public static void runAndRethrow(Class referenceClass, String message, Runnable action) { + try { + action.run(); + } catch (Exception e) { + LoggerFactory.getLogger(referenceClass).error(message, e); + Sentry.flush(1000); + throw new IllegalStateException(message, e); + } + } + + public static T callAndRethrow(Class referenceClass, String message, Callable action) { + try { + return action.call(); + } catch (Exception e) { + LoggerFactory.getLogger(referenceClass).error(message, e); + Sentry.flush(1000); + throw new IllegalStateException(message, e); + } + } + +} diff --git a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingConsumer.java b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingConsumer.java new file mode 100644 index 0000000..2ef6fef --- /dev/null +++ b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingConsumer.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 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. + */ +package com.agorapulse.micronaut.log4aws.function; + +import java.util.function.Consumer; + +public interface LoggingConsumer extends Consumer { + + default void accept(T t) { + Logging.runAndRethrow(getClass(), "Exception running handler", () -> doAccept(t)); + } + + void doAccept(T t); + + +} diff --git a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunction.java b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunction.java new file mode 100644 index 0000000..f18d8ba --- /dev/null +++ b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunction.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 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. + */ +package com.agorapulse.micronaut.log4aws.function; + +import java.util.function.Function; + +public interface LoggingFunction extends Function { + + default R apply(T t) { + return Logging.callAndRethrow(getClass(), "Exception running handler", () -> doApply(t)); + } + + R doApply(T t); + + +} diff --git a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java index 5b635d0..bcf7acc 100644 --- a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java +++ b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java @@ -1,8 +1,24 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 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. + */ package com.agorapulse.micronaut.log4aws.function; import io.micronaut.context.ApplicationContext; import io.micronaut.function.executor.FunctionInitializer; -import org.slf4j.LoggerFactory; import javax.annotation.Nullable; @@ -23,32 +39,18 @@ public LoggingFunctionInitializer(ApplicationContext applicationContext, boolean @Override protected void startThis(ApplicationContext applicationContext) { - try { - super.startThis(applicationContext); - } catch (Exception e) { - LoggerFactory.getLogger(getClass()).error("Exception starting the application context", e); - throw e; - } + Logging.runAndRethrow(getClass(), "Exception starting the application context", () -> super.startThis(applicationContext)); } @Override @SuppressWarnings("unchecked") protected ApplicationContext buildApplicationContext(@Nullable Object context) { - try { - return super.buildApplicationContext(context); - } catch (Exception e) { - LoggerFactory.getLogger(getClass()).error("Exception building the application context", e); - throw e; - } + return Logging.callAndRethrow(getClass(), "Exception building the application context", () -> super.buildApplicationContext(context)); } @Override protected void injectThis(ApplicationContext applicationContext) { - try { - super.injectThis(applicationContext); - } catch (Exception e) { - LoggerFactory.getLogger(getClass()).error("Exception injecting the function handler", e); - throw e; - } + Logging.runAndRethrow(getClass(), "Exception injecting the function handler", () -> super.injectThis(applicationContext)); } + } diff --git a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestHandler.java b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestHandler.java new file mode 100644 index 0000000..192e3c5 --- /dev/null +++ b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestHandler.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 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. + */ +package com.agorapulse.micronaut.log4aws.function; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +public interface LoggingRequestHandler extends RequestHandler { + + default O handleRequest(I input, Context context) { + return Logging.callAndRethrow(getClass(), "Exception running handler", () -> doHandleRequest(input, context)); + } + + O doHandleRequest(I input, Context context); + +} diff --git a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestStreamHandler.java b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestStreamHandler.java new file mode 100644 index 0000000..fca41e3 --- /dev/null +++ b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestStreamHandler.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 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. + */ +package com.agorapulse.micronaut.log4aws.function; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; + +import java.io.InputStream; +import java.io.OutputStream; + +public interface LoggingRequestStreamHandler extends RequestStreamHandler { + + default void handleRequest(InputStream input, OutputStream output, Context context) { + Logging.runAndRethrow(getClass(), "Exception running handler", () -> doHandleRequest(input, output, context)); + } + + T doHandleRequest(InputStream input, OutputStream output, Context context); + +} From f72e0183691ad08c6d0586d6cf364cf90d504b38 Mon Sep 17 00:00:00 2001 From: musketyr Date: Mon, 27 Jun 2022 10:26:28 +0200 Subject: [PATCH 3/4] skip the Nullable annotation to ensure compatibility with MN 3.x --- .../log4aws/function/LoggingFunctionInitializer.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java index bcf7acc..251fea1 100644 --- a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java +++ b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingFunctionInitializer.java @@ -20,8 +20,6 @@ import io.micronaut.context.ApplicationContext; import io.micronaut.function.executor.FunctionInitializer; -import javax.annotation.Nullable; - public class LoggingFunctionInitializer extends FunctionInitializer { public LoggingFunctionInitializer() { @@ -44,7 +42,7 @@ protected void startThis(ApplicationContext applicationContext) { @Override @SuppressWarnings("unchecked") - protected ApplicationContext buildApplicationContext(@Nullable Object context) { + protected ApplicationContext buildApplicationContext(Object context) { return Logging.callAndRethrow(getClass(), "Exception building the application context", () -> super.buildApplicationContext(context)); } From 5a788f6651b2672159ab07f9f49e2ae824e51dc1 Mon Sep 17 00:00:00 2001 From: musketyr Date: Mon, 27 Jun 2022 10:49:40 +0200 Subject: [PATCH 4/4] coverage, removed unnecessary generics --- .../function/LoggingRequestStreamHandler.java | 2 +- .../log4aws/function/LoggingSpec.groovy | 94 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/function/LoggingSpec.groovy diff --git a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestStreamHandler.java b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestStreamHandler.java index fca41e3..930f6de 100644 --- a/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestStreamHandler.java +++ b/subprojects/micronaut-log4aws/src/main/java/com/agorapulse/micronaut/log4aws/function/LoggingRequestStreamHandler.java @@ -23,7 +23,7 @@ import java.io.InputStream; import java.io.OutputStream; -public interface LoggingRequestStreamHandler extends RequestStreamHandler { +public interface LoggingRequestStreamHandler extends RequestStreamHandler { default void handleRequest(InputStream input, OutputStream output, Context context) { Logging.runAndRethrow(getClass(), "Exception running handler", () -> doHandleRequest(input, output, context)); diff --git a/subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/function/LoggingSpec.groovy b/subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/function/LoggingSpec.groovy new file mode 100644 index 0000000..9d99265 --- /dev/null +++ b/subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/function/LoggingSpec.groovy @@ -0,0 +1,94 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 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. + */ +package com.agorapulse.micronaut.log4aws.function + +import com.amazonaws.services.lambda.runtime.Context +import groovy.transform.CompileStatic +import spock.lang.Specification + +class LoggingSpec extends Specification { + + void 'consumer test'() { + when: + new TestConsumer().accept('something') + then: + thrown(IllegalStateException) + } + + void 'function test'() { + when: + new TestFunction().apply('something') + then: + thrown(IllegalStateException) + } + + void 'request handler test'() { + when: + new TestRequestHandler().handleRequest('something', Mock(Context)) + then: + thrown(IllegalStateException) + } + + void 'request stream handler test'() { + when: + new TestRequestStreamHandler().handleRequest(Mock(InputStream), Mock(OutputStream), Mock(Context)) + then: + thrown(IllegalStateException) + } + +} + +@CompileStatic +class TestConsumer implements LoggingConsumer { + + @Override + void doAccept(String s) { + throw new UnsupportedOperationException('not implemented') + } + +} + +@CompileStatic +class TestFunction implements LoggingFunction { + + @Override + String doApply(String s) { + throw new UnsupportedOperationException('not implemented') + } + +} + +@CompileStatic +class TestRequestHandler implements LoggingRequestHandler { + + @Override + String doHandleRequest(String input, Context context) { + throw new UnsupportedOperationException('not implemented') + } + +} + +@CompileStatic +class TestRequestStreamHandler implements LoggingRequestStreamHandler { + + @Override + public T doHandleRequest(InputStream input, OutputStream output, Context context) { + throw new UnsupportedOperationException('not implemented') + } + +}