Skip to content
Permalink
Browse files

[COLAB-2921] Add sentry error monitoring

  • Loading branch information...
jobachhu committed Oct 3, 2018
1 parent f9fa1a3 commit 6e72816f28b1c02b3ee8bad9b03ef2b0c5cfa5aa
@@ -101,4 +101,14 @@ private PlatformAttributeKey() {
.defaultValue(TransformedAttribute.of(GOOGLE_RECAPTCHA_SITE_KEY,
StringUtils::isNotEmpty))
.build();

public static final AttributeGetter<String> SENTRY_BACKEND_DSN =
PlatformAttributes.newStringAttribute("sentry.backend.dsn")
.defaultValue("")
.build();

public static final AttributeGetter<String> SENTRY_FRONTEND_DSN_PUBLIC =
PlatformAttributes.newStringAttribute("sentry.frontend.dsn-public")
.defaultValue("")
.build();
}
19 pom.xml
@@ -69,6 +69,9 @@
<!-- Test dependencies -->
<powermock.version>1.7.3</powermock.version>
<maven-surefire-plugin.version>2.20.1</maven-surefire-plugin.version>

<!-- Error monitoring -->
<sentry.version>1.7.10</sentry.version>
</properties>

<repositories>
@@ -263,6 +266,22 @@
<artifactId>autolink</artifactId>
<version>${autolink.version}</version>
</dependency>

<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry</artifactId>
<version>${sentry.version}</version>
</dependency>
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-logback</artifactId>
<version>${sentry.version}</version>
</dependency>
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-spring</artifactId>
<version>${sentry.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

@@ -235,6 +235,18 @@
<groupId>org.coursera</groupId>
<artifactId>metrics-datadog</artifactId>
</dependency>
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry</artifactId>
</dependency>
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-logback</artifactId>
</dependency>
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-spring</artifactId>
</dependency>
</dependencies>

<build>
@@ -1,5 +1,8 @@
package org.xcolab.view.auth.tracking;

import io.sentry.Sentry;
import io.sentry.event.User;
import io.sentry.event.UserBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
@@ -68,16 +71,29 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
// that is being generated
try {
final TrackedVisit trackedVisit = trackedVisitFuture.get();
setSentryUser(trackedVisit.getVisitorUuid());
userTrackingCookie = new Cookie(COOKIE_NAME, trackedVisit.getVisitorUuid());
userTrackingCookie.setHttpOnly(true);
userTrackingCookie.setMaxAge((int) COOKIE_MAX_AGE);
response.addCookie(userTrackingCookie);
} catch (InterruptedException | ExecutionException e) {
log.error("Error while retrieving new TrackedVisit: ", e);
}
} else if (userTrackingCookie.getMaxAge() < COOKIE_MIN_AGE) {
userTrackingCookie.setMaxAge((int) COOKIE_MAX_AGE);
response.addCookie(userTrackingCookie);
} else {
setSentryUser(userTrackingCookie.getValue());
if (userTrackingCookie.getMaxAge() < COOKIE_MIN_AGE) {
userTrackingCookie.setMaxAge((int) COOKIE_MAX_AGE);
response.addCookie(userTrackingCookie);
}
}
}

private void setSentryUser(String userTrackingId) {
final User sentryUser = Sentry.getContext().getUser();
if (sentryUser == null) {
Sentry.getContext().setUser(new UserBuilder()
.setId(userTrackingId)
.build());
}
}
}
@@ -0,0 +1,58 @@
package org.xcolab.view.config.spring.beans;

import io.sentry.Sentry;
import io.sentry.SentryClient;
import io.sentry.spring.SentryServletContextInitializer;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import org.xcolab.client.admin.attributes.platform.PlatformAttributeKey;
import org.xcolab.client.admin.enums.ServerEnvironment;
import org.xcolab.commons.servlet.ManifestUtil;
import org.xcolab.view.config.spring.filters.SentryUserInfoFilter;

import java.util.Optional;

import javax.servlet.ServletContext;

@Configuration
public class SentryConfig {

@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new SentryServletContextInitializer();
}

@Bean
public SentryUserInfoFilter sentryUserInfoFilter() {
return new SentryUserInfoFilter();
}

@Component
public static class SentryConfigurer implements ApplicationRunner {

private final ServletContext servletContext;

public SentryConfigurer(ServletContext servletContext) {
this.servletContext = servletContext;
}

@Override
public void run(ApplicationArguments applicationArguments) {

final SentryClient sentryClient = Sentry.init(PlatformAttributeKey.SENTRY_BACKEND_DSN.get());
final ServerEnvironment serverEnvironment = PlatformAttributeKey.SERVER_ENVIRONMENT.get();
sentryClient.setEnvironment(serverEnvironment.name().toLowerCase());

final Package runtimePackage = Runtime.class.getPackage();
sentryClient.addTag("javaVersion", runtimePackage.getImplementationVersion());

final Optional<String> buildCommitOpt = ManifestUtil.getBuildCommit(servletContext);
buildCommitOpt.ifPresent(sentryClient::setRelease);
}
}
}
@@ -0,0 +1,40 @@
package org.xcolab.view.config.spring.filters;

import io.sentry.Sentry;
import io.sentry.event.UserBuilder;
import org.springframework.web.filter.GenericFilterBean;

import org.xcolab.client.members.pojo.Member;
import org.xcolab.view.auth.AuthenticationContext;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class SentryUserInfoFilter extends GenericFilterBean {

private final AuthenticationContext authenticationContext = new AuthenticationContext();

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("SentryUserInfoFilter just supports HTTP requests");
}

final Member realMemberOrNull = authenticationContext.getRealMemberOrNull();
if (realMemberOrNull != null) {
//noinspection UnnecessaryLocalVariable
final Member member = realMemberOrNull;
Sentry.getContext().setUser(new UserBuilder()
.setId(Long.toString(member.getId()))
.build());
}

chain.doFilter(request, response);
}
}
@@ -1,18 +1,21 @@
package org.xcolab.view.theme;

import org.xcolab.client.admin.attributes.configuration.ConfigurationAttributeKey;
import org.xcolab.client.admin.attributes.platform.PlatformAttributeKey;

public class CredentialVariables {
private final String googleAnalyticsKey;
private final String pingdomRumId;
private final String typekitId;
private final String typekitIdLocal;
private final String sentryDsn;

public CredentialVariables() {
this.googleAnalyticsKey = ConfigurationAttributeKey.GOOGLE_ANALYTICS_KEY.get();
this.pingdomRumId = ConfigurationAttributeKey.PINGDOM_RUM_ID.get();
this.typekitId = ConfigurationAttributeKey.TYPEKIT_KIT_ID.get();
this.typekitIdLocal = ConfigurationAttributeKey.TYPEKIT_KIT_ID_LOCALHOST.get();
this.sentryDsn = PlatformAttributeKey.SENTRY_FRONTEND_DSN_PUBLIC.get();
}

public String getGoogleAnalyticsKey() {
@@ -30,4 +33,8 @@ public String getTypekitId() {
public String getTypekitIdLocal() {
return typekitIdLocal;
}

public String getSentryDsn() {
return sentryDsn;
}
}
@@ -0,0 +1,18 @@
<configuration>

<!-- Include basic configuration from Spring Boot-->
<include resource="org/springframework/boot/logging/logback/base.xml"/>


<!-- Report errors directly to sentry -->
<appender name="Sentry" class="io.sentry.logback.SentryAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>

<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="Sentry" />
</root>
</configuration>
@@ -0,0 +1 @@
stacktrace.app.packages=org.xcolab
@@ -157,6 +157,10 @@
analyticsAttribute="${_themeContext.messageVariables.analyticsAttribute}"
httpSession="${request.session}" member="${_themeContext.authenticationVariables.member}" />
</c:if>

<c:if test="${not empty _themeContext.credentialVariables.sentryDsn}">
<script src="https://js.sentry-cdn.com/${_themeContext.credentialVariables.sentryDsn}.min.js" crossorigin="anonymous"><!-- --></script>
</c:if>
</head>

<body class="${_themeContext.themeVariables.isHomePage ? 'p-Homepage' : ''}">
@@ -408,6 +412,25 @@
<!-- Show update notification to outdated browsers -->
<xcolab-scripts:browserUpdate />

<c:if test="${not empty _themeContext.credentialVariables.sentryDsn}">
<script>
Sentry.onLoad(function() {
Sentry.init({
environment: '${_themeContext.serverEnvironment.name().toLowerCase()}',
// release: ''
});

Sentry.configureScope(function(scope) {
scope.setTag("language", "${_themeContext.i18NVariables.language}");
if (_isLoggedIn) {
scope.setUser({"id": "${_themeContext.authenticationVariables.member.id}"});
}
});
});
</script>
</c:if>


<!-- ============ Structured data (schema.org) ============ -->

<script type="application/ld+json">

0 comments on commit 6e72816

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.