diff --git a/src/main/java/com/tradier/raven/logging/HostnameEventBuilderHelper.java b/src/main/java/com/tradier/raven/logging/HostnameEventBuilderHelper.java new file mode 100644 index 0000000..344cdfa --- /dev/null +++ b/src/main/java/com/tradier/raven/logging/HostnameEventBuilderHelper.java @@ -0,0 +1,45 @@ +package com.tradier.raven.logging; + +import net.kencochrane.raven.event.EventBuilder; +import net.kencochrane.raven.event.helper.EventBuilderHelper; + +/** + * Helper that sets the {@link net.kencochrane.raven.event.EventBuilder#setServerName(String)} from a + * Environment or System property {@value #SYSTEM_PROPERTY_OR_ENV_VAR_FOR_HOSTNAME}. The System Property takes + * precedence. + * + * The System property or Environment variable is sourced in the Constructor (which is created when raven initialises + * itself). + */ +public class HostnameEventBuilderHelper implements EventBuilderHelper { + + public static final String SYSTEM_PROPERTY_OR_ENV_VAR_FOR_HOSTNAME = "RavenLogbackEventHostname"; + + // null so that the default hostname via {@link EventBuilder} does the default action + // At the time of writing this: https://github.com/getsentry/raven-java/issues/139 + public static final String UNAVAILABLE_HOSTNAME_VALUE = null; + + public final String hostname; + + public HostnameEventBuilderHelper() { + hostname = getSystemPropertyOrEnvVar(SYSTEM_PROPERTY_OR_ENV_VAR_FOR_HOSTNAME,UNAVAILABLE_HOSTNAME_VALUE); + } + + public static String getSystemPropertyOrEnvVar(String property,String defaultValue) { + String hostName = System.getProperty(property,System.getenv(property)); + + if(hostName == null || hostName.trim().length()==0) { + return defaultValue; + } else { + return hostName; + } + } + + @Override + public void helpBuildingEvent(EventBuilder eventBuilder) { + eventBuilder.setServerName(hostname); + } + + + +} diff --git a/src/main/java/com/tradier/raven/logging/HostnameRavenFactory.java b/src/main/java/com/tradier/raven/logging/HostnameRavenFactory.java new file mode 100644 index 0000000..0e40193 --- /dev/null +++ b/src/main/java/com/tradier/raven/logging/HostnameRavenFactory.java @@ -0,0 +1,39 @@ +package com.tradier.raven.logging; + + +import net.kencochrane.raven.DefaultRavenFactory; +import net.kencochrane.raven.Raven; +import net.kencochrane.raven.dsn.Dsn; +import net.kencochrane.raven.event.helper.EventBuilderHelper; +import net.kencochrane.raven.event.helper.HttpEventBuilderHelper; + +/** + * RavenFactory in order to add the EventBuilder : {@link com.tradier.raven.logging.HostnameEventBuilderHelper} + * This is so that the Hostname can be source from a environment variable or system property. + */ +public class HostnameRavenFactory extends DefaultRavenFactory { + + + @Override + public Raven createRavenInstance(Dsn dsn) { + Raven raven = super.createRavenInstance(dsn); + try { + Class.forName("javax.servlet.Servlet", false, this.getClass().getClassLoader()); + raven.addBuilderHelper(new HttpEventBuilderHelper()); + } catch (ClassNotFoundException e) { + // + // DO NOT logger anything here. It can cause a recursive logging call to logback + // and deadlock. see: https://github.com/getsentry/raven-java/issues/139 + // + + } + raven.addBuilderHelper(createHostnameEventBuilderHelper()); + return raven; + } + + + public EventBuilderHelper createHostnameEventBuilderHelper() { + return new HostnameEventBuilderHelper(); + } + +} diff --git a/src/main/resources/META-INF/services/net.kencochrane.raven.RavenFactory b/src/main/resources/META-INF/services/net.kencochrane.raven.RavenFactory new file mode 100644 index 0000000..e397947 --- /dev/null +++ b/src/main/resources/META-INF/services/net.kencochrane.raven.RavenFactory @@ -0,0 +1 @@ +com.tradier.raven.logging.HostnameRavenFactory diff --git a/src/test/java/com/tradier/raven/logging/HostnameRavenFactoryTest.java b/src/test/java/com/tradier/raven/logging/HostnameRavenFactoryTest.java new file mode 100644 index 0000000..0876954 --- /dev/null +++ b/src/test/java/com/tradier/raven/logging/HostnameRavenFactoryTest.java @@ -0,0 +1,197 @@ +package com.tradier.raven.logging; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import net.kencochrane.raven.Raven; +import net.kencochrane.raven.RavenFactory; +import net.kencochrane.raven.event.EventBuilder; +import net.kencochrane.raven.logback.SentryAppender; +import org.junit.*; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.UUID; + + +import static org.junit.Assert.*; + +public class HostnameRavenFactoryTest { + + private String previousHostname; + private String currentHostname; + + @BeforeClass + public static void recordEvents() { + MemoryPersistentHostnameEventBuilderHelper.withEventCollecting(); + } + + @AfterClass + public static void stopRecordingEvents() { + MemoryPersistentHostnameEventBuilderHelper.withoutEventCollecting(); + } + + @Before + public void setUp() { + previousHostname = System.getProperty(HostnameEventBuilderHelper.SYSTEM_PROPERTY_OR_ENV_VAR_FOR_HOSTNAME); + currentHostname = UUID.randomUUID().toString(); + System.setProperty(HostnameEventBuilderHelper.SYSTEM_PROPERTY_OR_ENV_VAR_FOR_HOSTNAME,currentHostname); + } + + @After + public void tearDown() { + clearHostnameProperty(); + } + + private void clearHostnameProperty() { + if(previousHostname!=null) { + System.setProperty(HostnameEventBuilderHelper.SYSTEM_PROPERTY_OR_ENV_VAR_FOR_HOSTNAME, previousHostname); + } else { + System.clearProperty(HostnameEventBuilderHelper.SYSTEM_PROPERTY_OR_ENV_VAR_FOR_HOSTNAME); + } + } + + /** + * Check that the HostnameEventBuilderHelper is called + * and the Hostname on the EventBuilder is set by the helper to + * the value of the currentHostname set in the before + */ + @Test + public void checkHostnameIsSetWithinTheEvent() { + final String dsn = "https://user:pass@app.getsentry.com/id"; + try { + SentryAppender appender1 = new OurAppender(RavenFactory.ravenInstance(dsn)); + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + ILoggingEvent event = new LoggingEvent("com.tradier.raven.logback.LogBackRavenFactoryTest", context.getLogger(this.getClass()), Level.ERROR, "hey", null, null); + appender1.start(); + appender1.doAppend(event); + } finally { + assertTrue("Hostname should have been set on event to: " + currentHostname,findEvent(currentHostname)); + } + } + + public static class OurAppender extends SentryAppender { + + + + public OurAppender() { + + } + + /** + * Creates an instance of SentryAppender. + * + * @param raven instance of Raven to use with this appender. + */ + public OurAppender(Raven raven) { + super(raven); + } + /** + * {@inheritDoc} + *

+ * The raven instance is started in this method instead of {@link #start()} in order to avoid substitute loggers + * being generated during the instantiation of {@link net.kencochrane.raven.Raven}.
+ * More on www.slf4j.org/codes.html#substituteLogger + *

+ */ + @Override + protected void append(ILoggingEvent iLoggingEvent) { + System.out.println(iLoggingEvent.getLoggerName().startsWith("net.kencochrane.raven")); + + super.append(iLoggingEvent); + } + } + + /** + * Check that the HostnameEventBuilderHelper is called for each append call + * and that the Hostname on the EventBuilder is set by the helper to + * the value of the currentHostname set in the before + */ + @Test + public void checkHostnameIsSetForEachAppend() { + final String dsn = "https://user:pass@app.getsentry.com/id"; + int numberOfEventsToGenerate = 3; + try { + SentryAppender appender1 = new SentryAppender(RavenFactory.ravenInstance(dsn)); + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + appender1.start(); + + for(int i=0;i events = MemoryPersistentHostnameEventBuilderHelper.getEventsHelped(); + + for(EventBuilder builder : events) { + if(builder instanceof MemoryPersistentHostnameEventBuilderHelper.WrappedEventBuilder) { + String setEventHostname = ((MemoryPersistentHostnameEventBuilderHelper.WrappedEventBuilder)builder).hostname; + if(setEventHostname!=null && setEventHostname.equals(hostname)){ + return true; + } + } + + } + + return false; + } + + /** + * Returns the number of matching events that were found, that had the given hostname set + * @param hostname + * @return The number of EventBuilder objects that had the given hostname + */ + private int countMatchingEvent(String hostname) { + List events = MemoryPersistentHostnameEventBuilderHelper.getEventsHelped(); + + int count = 0; + for(EventBuilder builder : events) { + if(builder instanceof MemoryPersistentHostnameEventBuilderHelper.WrappedEventBuilder) { + String setEventHostname = ((MemoryPersistentHostnameEventBuilderHelper.WrappedEventBuilder)builder).hostname; + if(setEventHostname!=null && setEventHostname.equals(hostname)){ + count++; + } + } + } + + return count; + } + +} \ No newline at end of file diff --git a/src/test/java/com/tradier/raven/logging/MemoryPersistentHostnameEventBuilderHelper.java b/src/test/java/com/tradier/raven/logging/MemoryPersistentHostnameEventBuilderHelper.java new file mode 100644 index 0000000..9d17453 --- /dev/null +++ b/src/test/java/com/tradier/raven/logging/MemoryPersistentHostnameEventBuilderHelper.java @@ -0,0 +1,57 @@ +package com.tradier.raven.logging; + +import net.kencochrane.raven.event.EventBuilder; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * + */ +public class MemoryPersistentHostnameEventBuilderHelper extends HostnameEventBuilderHelper { + + private final static List eventsHelped = new CopyOnWriteArrayList(); + private final static AtomicBoolean collectEvents = new AtomicBoolean(false); + + @Override + public void helpBuildingEvent(EventBuilder eventBuilder) { + EventBuilder eventBuilder1 = new WrappedEventBuilder(eventBuilder); + super.helpBuildingEvent(eventBuilder1); + if(collectEvents.get()) { + eventsHelped.add(eventBuilder1); + } + } + + + public static List getEventsHelped() { + return eventsHelped; + } + + public static void withEventCollecting() { + collectEvents.set(true); + } + + public static void withoutEventCollecting() { + eventsHelped.clear(); + collectEvents.set(false); + } + + + public static class WrappedEventBuilder extends EventBuilder { + public volatile String hostname; + public final EventBuilder wrapped; + + public WrappedEventBuilder(EventBuilder delegate) { + this.wrapped = delegate; + } + + @Override + public EventBuilder setServerName(String name) { + wrapped.setServerName(name); + hostname = name; + return wrapped; + + } + } +} diff --git a/src/test/java/com/tradier/raven/logging/MemoryPersistentHostnameRavenFactory.java b/src/test/java/com/tradier/raven/logging/MemoryPersistentHostnameRavenFactory.java new file mode 100644 index 0000000..acb5cbb --- /dev/null +++ b/src/test/java/com/tradier/raven/logging/MemoryPersistentHostnameRavenFactory.java @@ -0,0 +1,17 @@ +package com.tradier.raven.logging; + + +import net.kencochrane.raven.event.helper.EventBuilderHelper; + +/** + * RavenFactory in order to add the EventBuilder : {@link HostnameEventBuilderHelper} + * This is so that the Hostname can be source from a environment variable or system property. + */ +public class MemoryPersistentHostnameRavenFactory extends HostnameRavenFactory { + + + public EventBuilderHelper createHostnameEventBuilderHelper() { + return new MemoryPersistentHostnameEventBuilderHelper(); + } + +} diff --git a/src/test/resources/META-INF/services/io.dropwizard.logging.AppenderFactory b/src/test/resources/META-INF/services/io.dropwizard.logging.AppenderFactory new file mode 100644 index 0000000..239830d --- /dev/null +++ b/src/test/resources/META-INF/services/io.dropwizard.logging.AppenderFactory @@ -0,0 +1 @@ +com.tradier.raven.logging.RavenAppenderFactory diff --git a/src/test/resources/META-INF/services/net.kencochrane.raven.RavenFactory b/src/test/resources/META-INF/services/net.kencochrane.raven.RavenFactory new file mode 100644 index 0000000..7d43d6e --- /dev/null +++ b/src/test/resources/META-INF/services/net.kencochrane.raven.RavenFactory @@ -0,0 +1 @@ +com.tradier.raven.logging.MemoryPersistentHostnameRavenFactory