From 9216512f83461323489e6534c4001b6f988a29bf Mon Sep 17 00:00:00 2001 From: Vladimir Orany Date: Fri, 23 Oct 2020 11:18:39 +0200 Subject: [PATCH] added support for HTTP --- gradle.properties | 3 + .../micronaut-log4aws.gradle | 9 ++ .../http/MicronautRequestBuildHelper.java | 73 +++++++++++++++ .../micronaut/log4aws/http/SentryFilter.java | 55 +++++++++++ .../log4aws/http/SentryFilterSpec.groovy | 91 +++++++++++++++++++ .../log4aws/http/TestController.groovy | 42 +++++++++ 6 files changed, 273 insertions(+) create mode 100644 subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/http/MicronautRequestBuildHelper.java create mode 100644 subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/http/SentryFilter.java create mode 100644 subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/http/SentryFilterSpec.groovy create mode 100644 subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/http/TestController.groovy diff --git a/gradle.properties b/gradle.properties index a47deae..583e657 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,3 +28,6 @@ sentryVersion = 1.7.30 log4jVersion = 2.13.3 awsLog4jVersion = 1.2.0 systemRulesVersion = 1.19.0 +gruVersion = 0.8.4 +bytebuddyVersion = 1.10.17 +objenesisVersion = 3.1 diff --git a/subprojects/micronaut-log4aws/micronaut-log4aws.gradle b/subprojects/micronaut-log4aws/micronaut-log4aws.gradle index 97aa8e0..f8bd13b 100644 --- a/subprojects/micronaut-log4aws/micronaut-log4aws.gradle +++ b/subprojects/micronaut-log4aws/micronaut-log4aws.gradle @@ -36,14 +36,23 @@ dependencies { compile "io.micronaut:micronaut-core" compileOnly "io.micronaut:micronaut-inject-groovy" + compileOnly 'io.micronaut:micronaut-http' + compileOnly 'io.reactivex.rxjava2:rxjava' testAnnotationProcessor "io.micronaut:micronaut-inject-java" testCompile "io.micronaut:micronaut-inject-groovy" + testCompile "io.micronaut:micronaut-http-server-netty" + testCompile "com.github.stefanbirkner:system-rules:$systemRulesVersion" + testCompile("org.spockframework:spock-core") { exclude group: "org.codehaus.groovy", module: "groovy-all" } + testCompile "com.agorapulse:gru-http:$gruVersion" + testCompile "net.bytebuddy:byte-buddy:$bytebuddyVersion" + testCompile "org.objenesis:objenesis:$objenesisVersion" + } diff --git a/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/http/MicronautRequestBuildHelper.java b/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/http/MicronautRequestBuildHelper.java new file mode 100644 index 0000000..e467397 --- /dev/null +++ b/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/http/MicronautRequestBuildHelper.java @@ -0,0 +1,73 @@ +/* + * 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. + */ +package com.agorapulse.micronaut.log4aws.http; + +import io.micronaut.http.HttpRequest; +import io.sentry.event.EventBuilder; +import io.sentry.event.helper.EventBuilderHelper; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class MicronautRequestBuildHelper implements EventBuilderHelper { + + private static final List SENSITIVE_HEADERS = Arrays.asList("X-FORWARDED-FOR", "AUTHORIZATION", "COOKIE"); + + private static final List SENSITIVE_PARAMS = Arrays.asList("TOKEN"); + + private final HttpRequest request; + + public MicronautRequestBuildHelper(HttpRequest request) { + this.request = request; + } + + @Override + public void helpBuildingEvent(EventBuilder eventBuilder) { + eventBuilder.withTag("req.path", request.getPath()); + eventBuilder.withTag("req.method", request.getMethod().toString()); + eventBuilder.withTag("req.remoteHost", request.getRemoteAddress().getHostString()); + eventBuilder.withTag("req.serverHost", request.getServerAddress().getHostString()); + eventBuilder.withExtra("req.parameters", resolveParameters(request)); + eventBuilder.withExtra("req.headers", resolveHeaders(request)); + } + + private static Map resolveParameters(final HttpRequest request) { + return StreamSupport.stream(request.getParameters().spliterator(), false) + .filter(e -> !SENSITIVE_PARAMS.contains(e.getKey().toUpperCase())) + .collect(Collectors.toMap(Map.Entry::getKey, e -> String.join(",", e.getValue()))); + } + + private Map resolveHeaders(final HttpRequest request) { + final Map headersMap = new HashMap<>(); + for (String headerName : request.getHeaders().names()) { + if (!SENSITIVE_HEADERS.contains(headerName.toUpperCase())) { + headersMap.put(headerName, String.join(",", request.getHeaders().getAll(headerName))); + } + } + return headersMap; + } + + @Override + public String toString() { + return String.format("MicronautRequestBuildHelper{ request = %s }", request); + } +} diff --git a/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/http/SentryFilter.java b/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/http/SentryFilter.java new file mode 100644 index 0000000..ebefa49 --- /dev/null +++ b/subprojects/micronaut-log4aws/src/main/groovy/com/agorapulse/micronaut/log4aws/http/SentryFilter.java @@ -0,0 +1,55 @@ +/* + * 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. + */ +package com.agorapulse.micronaut.log4aws.http; + +import io.micronaut.http.HttpRequest; +import io.micronaut.http.MutableHttpResponse; +import io.micronaut.http.annotation.Filter; +import io.micronaut.http.filter.HttpServerFilter; +import io.micronaut.http.filter.ServerFilterChain; +import io.reactivex.Flowable; +import io.sentry.SentryClient; +import io.sentry.event.helper.EventBuilderHelper; +import org.reactivestreams.Publisher; + +import java.util.concurrent.atomic.AtomicReference; + +@Filter("/**") +public class SentryFilter implements HttpServerFilter { + + private final SentryClient client; + + public SentryFilter(SentryClient client) { + this.client = client; + } + + @Override + public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { + AtomicReference helperReference = new AtomicReference<>(new MicronautRequestBuildHelper(request)); + return Flowable + .just(request) + .doOnNext(r -> { + client.addBuilderHelper(helperReference.get()); + }) + .switchMap(chain::proceed) + .doOnNext(res -> { + client.removeBuilderHelper(helperReference.get()); + }); + } + +} diff --git a/subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/http/SentryFilterSpec.groovy b/subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/http/SentryFilterSpec.groovy new file mode 100644 index 0000000..c19cc1a --- /dev/null +++ b/subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/http/SentryFilterSpec.groovy @@ -0,0 +1,91 @@ +/* + * 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. + */ +package com.agorapulse.micronaut.log4aws.http + +import com.agorapulse.gru.Gru +import com.agorapulse.gru.http.Http +import io.micronaut.context.ApplicationContext +import io.micronaut.runtime.server.EmbeddedServer +import io.sentry.SentryClient +import io.sentry.event.helper.EventBuilderHelper +import org.junit.Rule +import spock.lang.AutoCleanup +import spock.lang.Specification + +class SentryFilterSpec extends Specification { + + @Rule Gru gru = Gru.equip(Http.steal(this)) + + @AutoCleanup ApplicationContext context + @AutoCleanup EmbeddedServer server + + SentryClient client = Mock() + + void setup() { + context = ApplicationContext.build().build() + context.registerSingleton(SentryClient, client) + + context.start() + + server = context.getBean(EmbeddedServer) + server.start() + + gru.prepare(server.URL.toString()) + } + + void 'try ok message'() { + when: + gru.test { + get('/test/parameter') + expect { + text inline('parameter') + } + } + then: + gru.verify() + + 1 * client.addBuilderHelper(_) + 1 * client.removeBuilderHelper(_) + + 0 * _._ + } + + @SuppressWarnings('Println') + void 'try error message'() { + when: + gru.test { + post('/test/parameter') + expect { + status INTERNAL_SERVER_ERROR + } + } + then: + gru.verify() + + _ * client.addBuilderHelper(_) >> { EventBuilderHelper helper -> + println "adding $helper" + } + + _ * client.removeBuilderHelper(_) >> { EventBuilderHelper helper -> + println "removing $helper" + } + + 0 * _._ + } + +} diff --git a/subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/http/TestController.groovy b/subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/http/TestController.groovy new file mode 100644 index 0000000..542f0e8 --- /dev/null +++ b/subprojects/micronaut-log4aws/src/test/groovy/com/agorapulse/micronaut/log4aws/http/TestController.groovy @@ -0,0 +1,42 @@ +/* + * 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. + */ +package com.agorapulse.micronaut.log4aws.http + +import groovy.transform.CompileStatic +import io.micronaut.http.HttpResponse +import io.micronaut.http.annotation.Controller +import io.micronaut.http.annotation.Get +import io.micronaut.http.annotation.PathVariable +import io.micronaut.http.annotation.Post + +@CompileStatic +@Controller('/test') +class TestController { + + @Get('/{someparam}') + HttpResponse someparam(@PathVariable String someparam) { + HttpResponse.ok(someparam) + } + + @Post('/{someerror}') + @SuppressWarnings('ThrowRuntimeException') + HttpResponse someerror(@PathVariable String someerror) { + throw new RuntimeException(someerror) + } + +}