Skip to content

Commit

Permalink
Merge d7670c2 into c9fad45
Browse files Browse the repository at this point in the history
  • Loading branch information
musketyr committed Oct 22, 2020
2 parents c9fad45 + d7670c2 commit e54b9b1
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 91 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ coverallsPluginVersion = 2.9.0
gitPublishPluginVersion = 2.1.3

micronautVersion = 1.2.8
sentryVersion = 1.7.30
sentryVersion = 3.1.1
log4jVersion = 2.13.3
awsLog4jVersion = 1.2.0
systemRulesVersion = 1.19.0
3 changes: 3 additions & 0 deletions subprojects/micronaut-log4aws/micronaut-log4aws.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ dependencies {
compile "io.micronaut:micronaut-core"
compileOnly "io.micronaut:micronaut-inject-groovy"

compileOnly 'io.micronaut:micronaut-http'
compileOnly 'io.reactivex.rxjava2:rxjava'
compileOnly "io.sentry:sentry-servlet:$sentryVersion"

testAnnotationProcessor "io.micronaut:micronaut-inject-java"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,25 @@
*/
package com.agorapulse.micronaut.log4aws;

import io.sentry.event.EventBuilder;
import io.sentry.event.helper.EventBuilderHelper;
import io.micronaut.context.annotation.Context;
import io.sentry.EventProcessor;
import io.sentry.SentryEvent;

public class AwsLambdaEventBuildHelper implements EventBuilderHelper {
@Context
public class AwsLambdaEventBuildHelper implements EventProcessor {

@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"));
public SentryEvent process(SentryEvent event, Object hint) {
event.setTag("aws_region", System.getenv("AWS_REGION"));
event.setTag("aws_default_region", System.getenv("AWS_DEFAULT_REGION"));
event.setTag("lambda_function_name", System.getenv("AWS_LAMBDA_FUNCTION_NAME"));
event.setTag("lambda_function_version", System.getenv("AWS_LAMBDA_FUNCTION_VERSION"));
event.setTag("lambda_handler", System.getenv("_HANDLER"));
event.setTag("lambda_execution_environment", System.getenv("AWS_EXECUTION_ENV"));
event.setTag("lambda_log_group_name", System.getenv("AWS_LAMBDA_LOG_GROUP_NAME"));
event.setTag("lambda_log_stream_name", System.getenv("AWS_LAMBDA_LOG_STREAM_NAME"));
event.setTag("lambda_memory_size", System.getenv("AWS_LAMBDA_FUNCTION_MEMORY_SIZE"));
return event;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,124 +20,96 @@
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Context;
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.*;
import io.sentry.config.PropertiesProviderFactory;
import io.sentry.log4j2.SentryAppender;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
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 java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import static io.sentry.DefaultSentryClientFactory.ASYNC_OPTION;
import java.util.Optional;

/**
* Initializes Sentry for Micronaut.
*/
@Factory
public class Log4AwsFactory {

private static final Logger LOGGER = LoggerFactory.getLogger(Log4AwsFactory.class);

/**
* Automatically registers sentry logging during application context startup.
* @return sentry appender
*/
@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'");
}

return initializeAppenderIfMissing(
SentryAppender.class,
Level.WARN,
SentryAppender.APPENDER_NAME,
SentryAppender::new
);
}

/**
* Sentry client to be injected.
*
* Please, use the injection instead of static reference to simplify testing.
*
* @return sentry client
* @return sentry appender
*/
@Bean
@Context
public SentryClient sentryClient() {
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);
}
}
});

return client;
}

private <A extends Appender> A initializeAppenderIfMissing(
Class<A> appenderClass,
Level defaultLevel,
String defaultName,
Supplier<A> initializer
) {
public SentryAppender sentryAppender(IHub hub) {
LoggerContext lc = (LoggerContext) LogManager.getContext(false);
Configuration configuration = lc.getConfiguration();

String name = defaultName;
A appender = null;
String name = "Sentry";
SentryAppender appender = null;

for (Map.Entry<String, Appender> e : configuration.getAppenders().entrySet()) {
if (appenderClass.isInstance(e.getValue())) {
appender = appenderClass.cast(e.getValue());
if (e.getValue() instanceof SentryAppender) {
appender = (SentryAppender) e.getValue();
name = e.getKey();
}
}

if (appender == null) {
appender = initializer.get();
SentryOptions options = SentryOptions.from(PropertiesProviderFactory.create());
appender = new SentryAppender(
name,
null,
Optional.ofNullable(options.getDsn()).orElse(""),
Level.INFO,
Level.ERROR,
options.getTransport(),
hub);
appender.start();
configuration.addAppender(appender);
}

LoggerConfig rootLoggerConfig = configuration.getRootLogger();
if (rootLoggerConfig.getAppenders().values().stream().noneMatch(appenderClass::isInstance)) {
if (rootLoggerConfig.getAppenders().values().stream().noneMatch(SentryAppender.class::isInstance)) {
rootLoggerConfig.removeAppender(name);
rootLoggerConfig.addAppender(configuration.getAppender(name), defaultLevel, null);
rootLoggerConfig.addAppender(configuration.getAppender(name), Level.WARN, null);
}

lc.updateLoggers();

return appender;
}

/**
* Sentry client to be injected.
* <p>
* Please, use the injection instead of static reference to simplify testing.
*
* @return sentry client
*/
@Bean(preDestroy = "close")
@Context
public IHub sentryHub(
List<EventProcessor> eventProcessors,
List<Integration> integrations,
List<IScopeObserver> scopeObservers
) {
SentryOptions propertiesOptions = SentryOptions.from(PropertiesProviderFactory.create());

Optional.ofNullable(propertiesOptions.getDsn()).ifPresent(dsn -> Sentry.init(options -> {
options.setEnableExternalConfiguration(true);
options.setDsn(dsn);
eventProcessors.forEach(options::addEventProcessor);
integrations.forEach(options::addIntegration);
scopeObservers.forEach(options::addScopeObserver);
}));

return HubAdapter.getInstance();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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.EventProcessor;
import io.sentry.SentryEvent;
import io.sentry.protocol.Request;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class MicronautHttpRequestEventProcessor implements EventProcessor {

private static final List<String> SENSITIVE_HEADERS =
Arrays.asList("X-FORWARDED-FOR", "AUTHORIZATION", "COOKIE");

private static final List<String> SENSITIVE_PARAMS =
Arrays.asList("TOKEN");

private final HttpRequest<?> httpRequest;

public MicronautHttpRequestEventProcessor(HttpRequest<?> httpRequest) {
this.httpRequest = httpRequest;
}

// httpRequest.getRequestURL() returns StringBuffer which is considered an obsolete class.
@SuppressWarnings("JdkObsolete")
@Override
public SentryEvent process(SentryEvent event, Object hint) {
final Request sentryRequest = new Request();

sentryRequest.setMethod(httpRequest.getMethod().toString());
sentryRequest.setQueryString(resolveParameters());
sentryRequest.setUrl(httpRequest.getPath());
sentryRequest.setHeaders(resolveHeadersMap(httpRequest));

event.setRequest(sentryRequest);
return event;
}

private String resolveParameters() {
return StreamSupport.stream(httpRequest.getParameters().spliterator(), false)
.filter(e -> !SENSITIVE_PARAMS.contains(e.getKey().toUpperCase()))
.map(e -> e.getKey() + "=" + String.join(",", e.getValue()))
.collect(Collectors.joining("&"));
}

private Map<String, String> resolveHeadersMap(final HttpRequest<?> request) {
final Map<String, String> headersMap = new HashMap<>();
for (String headerName : request.getHeaders().names()) {
// do not copy personal information identifiable headers
if (!SENSITIVE_HEADERS.contains(headerName.toUpperCase())) {
headersMap.put(headerName, String.join(",", request.getHeaders().getAll(headerName)));
}
}
return headersMap;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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.IHub;
import org.reactivestreams.Publisher;

@Filter("/**")
public class SentryFilter implements HttpServerFilter {

private final IHub hub;

public SentryFilter(IHub hub) {
this.hub = hub;
}

@Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
return Flowable
.just(request)
.doOnNext(r -> {
hub.pushScope();
hub.configureScope(s -> {
s.addEventProcessor(new MicronautHttpRequestEventProcessor(r));
});
})
.switchMap(chain::proceed)
.doOnNext(res -> {
hub.popScope();
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
package com.agorapulse.micronaut.log4aws.test

import io.micronaut.context.ApplicationContext
import io.sentry.SentryClient
import io.sentry.IHub
import io.sentry.log4j2.SentryAppender
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.core.LoggerContext
Expand All @@ -34,7 +34,7 @@ abstract class Log4AwsFactorySpec extends Specification {

void 'sentry appender configured'() {
expect:
context.getBean(SentryClient)
context.getBean(IHub)
context.getBean(SentryAppender)

when:
Expand Down

0 comments on commit e54b9b1

Please sign in to comment.