From d5e07b526dfa716d795b520e0fea66c1db88d997 Mon Sep 17 00:00:00 2001 From: dominictootell Date: Wed, 16 Sep 2015 11:25:57 +0100 Subject: [PATCH] Allow the hostname to be specified by environment or system properties Using the "RavenLogbackEventHostname" environment variable or system property you can set the hostname that Raven uses to send along with the other event data rather than looking it up from the first interface it finds on the host. --- .../logging/HostnameEventBuilderHelper.java | 45 ++++ .../raven/logging/HostnameRavenFactory.java | 39 ++++ .../net.kencochrane.raven.RavenFactory | 1 + .../logging/HostnameRavenFactoryTest.java | 197 ++++++++++++++++++ ...yPersistentHostnameEventBuilderHelper.java | 57 +++++ .../MemoryPersistentHostnameRavenFactory.java | 17 ++ .../io.dropwizard.logging.AppenderFactory | 1 + .../net.kencochrane.raven.RavenFactory | 1 + 8 files changed, 358 insertions(+) create mode 100644 src/main/java/com/tradier/raven/logging/HostnameEventBuilderHelper.java create mode 100644 src/main/java/com/tradier/raven/logging/HostnameRavenFactory.java create mode 100644 src/main/resources/META-INF/services/net.kencochrane.raven.RavenFactory create mode 100644 src/test/java/com/tradier/raven/logging/HostnameRavenFactoryTest.java create mode 100644 src/test/java/com/tradier/raven/logging/MemoryPersistentHostnameEventBuilderHelper.java create mode 100644 src/test/java/com/tradier/raven/logging/MemoryPersistentHostnameRavenFactory.java create mode 100644 src/test/resources/META-INF/services/io.dropwizard.logging.AppenderFactory create mode 100644 src/test/resources/META-INF/services/net.kencochrane.raven.RavenFactory 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