package com.hubspot.singularity.sentry; import java.util.Collections; import java.util.Map; import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.name.Named; import com.hubspot.singularity.SingularityMainModule; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.config.SentryConfiguration; import net.kencochrane.raven.Raven; import net.kencochrane.raven.RavenFactory; import net.kencochrane.raven.event.Event; import net.kencochrane.raven.event.EventBuilder; import net.kencochrane.raven.event.interfaces.ExceptionInterface; import net.kencochrane.raven.event.interfaces.HttpInterface; import net.kencochrane.raven.event.interfaces.UserInterface; @Singleton public class SingularityExceptionNotifier { private final Optional<Raven> raven; private final Optional<SentryConfiguration> sentryConfiguration; private final Provider<Optional<HttpServletRequest>> requestProvider; private final Provider<Optional<SingularityUser>> userProvider; @Inject public SingularityExceptionNotifier(Optional<SentryConfiguration> sentryConfiguration, @Named(SingularityMainModule.CURRENT_HTTP_REQUEST) Provider<Optional<HttpServletRequest>> requestProvider, Provider<Optional<SingularityUser>> userProvider) { this.sentryConfiguration = sentryConfiguration; this.requestProvider = requestProvider; this.userProvider = userProvider; if (sentryConfiguration.isPresent()) { this.raven = Optional.of(RavenFactory.ravenInstance(sentryConfiguration.get().getDsn())); } else { this.raven = Optional.absent(); } } private String getPrefix() { if (!sentryConfiguration.isPresent() || Strings.isNullOrEmpty(sentryConfiguration.get().getPrefix())) { return ""; } return sentryConfiguration.get().getPrefix() + " "; } private String getCallingClassName(StackTraceElement[] stackTrace) { if (stackTrace != null && stackTrace.length > 2) { return stackTrace[2].getClassName(); } else { return "(unknown)"; } } private void sendEvent(Raven raven, final EventBuilder eventBuilder) { raven.runBuilderHelpers(eventBuilder); raven.sendEvent(eventBuilder.build()); } public void notify(String message, Throwable t) { notify(message, t, Collections.<String, String>emptyMap()); } public void notify(String message, Throwable t, Map<String, String> extraData) { if (!raven.isPresent()) { return; } final StackTraceElement[] currentThreadStackTrace = Thread.currentThread().getStackTrace(); final EventBuilder eventBuilder = new EventBuilder() .withCulprit(getPrefix() + message) .withMessage(message) .withLevel(Event.Level.ERROR) .withLogger(getCallingClassName(currentThreadStackTrace)) .withSentryInterface(new ExceptionInterface(t)); final Optional<HttpServletRequest> maybeRequest = requestProvider.get(); if (maybeRequest.isPresent()) { eventBuilder.withSentryInterface(new HttpInterface(maybeRequest.get())); } final Optional<SingularityUser> maybeUser = userProvider.get(); if (maybeUser.isPresent()) { eventBuilder.withSentryInterface(new UserInterface(maybeUser.get().getId(), maybeUser.get().getId(), "", maybeUser.get().getEmail().or(""))); } if (extraData != null && !extraData.isEmpty()) { for (Map.Entry<String, String> entry : extraData.entrySet()) { eventBuilder.withExtra(entry.getKey(), entry.getValue()); } } sendEvent(raven.get(), eventBuilder); } public void notify(String subject) { notify(subject, Collections.<String, String>emptyMap()); } public void notify(String subject, Map<String, String> extraData) { if (!raven.isPresent()) { return; } final StackTraceElement[] currentThreadStackTrace = Thread.currentThread().getStackTrace(); final EventBuilder eventBuilder = new EventBuilder() .withMessage(getPrefix() + subject) .withLevel(Event.Level.ERROR) .withLogger(getCallingClassName(currentThreadStackTrace)); final Optional<HttpServletRequest> maybeRequest = requestProvider.get(); if (maybeRequest.isPresent()) { eventBuilder.withSentryInterface(new HttpInterface(maybeRequest.get())); } if (extraData != null && !extraData.isEmpty()) { for (Map.Entry<String, String> entry : extraData.entrySet()) { eventBuilder.withExtra(entry.getKey(), entry.getValue()); } } sendEvent(raven.get(), eventBuilder); } }