Skip to content

Commit

Permalink
Add RavenContext and Breadcrumbs helper. (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
bretthoerner committed Jun 10, 2016
1 parent deb7a83 commit 34798e1
Show file tree
Hide file tree
Showing 9 changed files with 726 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -3,6 +3,8 @@ Version 7.2.4

- Add (manual) support for breadcrumbs to event objects.
- Add ``sendEvent(EventBuilder)`` method which calls builder helpers before building and sending the ``Event``.
- Add ``RavenContext`` which tracks thread-local state.
- Add ``Breadcrumbs`` helper to log breadcrumbs from anywhere without manually passing context around.

Version 7.2.3
-------------
Expand Down
Expand Up @@ -2,6 +2,7 @@

import com.getsentry.raven.connection.*;
import com.getsentry.raven.dsn.Dsn;
import com.getsentry.raven.event.helper.ContextBuilderHelper;
import com.getsentry.raven.event.helper.HttpEventBuilderHelper;
import com.getsentry.raven.event.interfaces.*;
import com.getsentry.raven.marshaller.Marshaller;
Expand Down Expand Up @@ -79,6 +80,7 @@ public Raven createRavenInstance(Dsn dsn) {
logger.debug("The current environment doesn't provide access to servlets,"
+ "or provides an unsupported version.");
}
raven.addBuilderHelper(new ContextBuilderHelper(raven));
return raven;
}

Expand Down
12 changes: 12 additions & 0 deletions raven/src/main/java/com/getsentry/raven/Raven.java
Expand Up @@ -25,6 +25,14 @@ public class Raven {
private static final Logger logger = LoggerFactory.getLogger(Raven.class);
private final Set<EventBuilderHelper> builderHelpers = new HashSet<>();
private Connection connection;
private ThreadLocal<RavenContext> context = new ThreadLocal<RavenContext>() {
@Override
protected RavenContext initialValue() {
RavenContext ctx = new RavenContext();
ctx.activate();
return ctx;
}
};

/**
* Runs the {@link EventBuilderHelper} against the {@link EventBuilder} to obtain additional information with a
Expand Down Expand Up @@ -129,6 +137,10 @@ public void setConnection(Connection connection) {
this.connection = connection;
}

public RavenContext getContext() {
return context.get();
}

@Override
public String toString() {
return "Raven{"
Expand Down
125 changes: 125 additions & 0 deletions raven/src/main/java/com/getsentry/raven/RavenContext.java
@@ -0,0 +1,125 @@
package com.getsentry.raven;

import com.getsentry.raven.event.Breadcrumb;
import com.getsentry.raven.util.CircularFifoQueue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;

/**
* RavenContext is used to hold {@link ThreadLocal} context data (such as
* {@link Breadcrumb}s) so that data may be collected in different parts
* of an application and then sent together when an exception occurs.
*/
public class RavenContext implements AutoCloseable {

/**
* Thread local set of active context objects. Note that an {@link IdentityHashMap}
* is used instead of a Set because there is no identity-set in the Java
* standard library.
*
* A set of active contexts is required in order to support running multiple Raven
* clients within a single process. In *most* cases this set will contain a single
* active context object.
*
* This must be static and {@link ThreadLocal} so that users can retrieve any active
* context objects globally, without passing context objects all the way down their
* stacks. See {@link com.getsentry.raven.event.Breadcrumbs} for an example of how this may be used.
*/
private static ThreadLocal<IdentityHashMap<RavenContext, RavenContext>> activeContexts =
new ThreadLocal<IdentityHashMap<RavenContext, RavenContext>>() {
@Override
protected IdentityHashMap<RavenContext, RavenContext> initialValue() {
return new IdentityHashMap<>();
}
};

/**
* The number of {@link Breadcrumb}s to keep in the ring buffer by default.
*/
private static final int DEFAULT_BREADCRUMB_LIMIT = 100;

/**
* Ring buffer of {@link Breadcrumb} objects.
*/
private CircularFifoQueue<Breadcrumb> breadcrumbs;

/**
* Create a new (empty) RavenContext object with the default Breadcrumb limit.
*/
public RavenContext() {
this(DEFAULT_BREADCRUMB_LIMIT);
}

/**
* Create a new (empty) RavenContext object with the specified Breadcrumb limit.
*
* @param breadcrumbLimit Number of Breadcrumb objects to retain in ring buffer.
*/
public RavenContext(int breadcrumbLimit) {
breadcrumbs = new CircularFifoQueue<>(breadcrumbLimit);
}

/**
* Add this context to the active contexts for this thread.
*/
public void activate() {
activeContexts.get().put(this, this);
}

/**
* Remove this context from the active contexts for this thread.
*/
public void deactivate() {
activeContexts.get().remove(this);
}

/**
* Clear state from this context.
*/
public void clear() {
breadcrumbs.clear();
}

/**
* Calls deactivate, used by try-with-resources ({@link AutoCloseable}).
*/
@Override
public void close() {
deactivate();
}

/**
* Returns all active contexts for the current thread.
*
* @return List of active {@link RavenContext} objects.
*/
public static List<RavenContext> getActiveContexts() {
Collection<RavenContext> ravenContexts = activeContexts.get().values();
List<RavenContext> list = new ArrayList<>(ravenContexts.size());
list.addAll(ravenContexts);
return list;
}

/**
* Return {@link Breadcrumb}s attached to this RavenContext.
*
* @return Iterator of {@link Breadcrumb}s.
*/
public Iterator<Breadcrumb> getBreadcrumbs() {
return breadcrumbs.iterator();
}

/**
* Record a single {@link Breadcrumb} into this context.
*
* @param breadcrumb Breadcrumb object to record
*/
public void recordBreadcrumb(Breadcrumb breadcrumb) {
breadcrumbs.add(breadcrumb);
}

}
28 changes: 28 additions & 0 deletions raven/src/main/java/com/getsentry/raven/event/Breadcrumbs.java
@@ -0,0 +1,28 @@
package com.getsentry.raven.event;

import com.getsentry.raven.RavenContext;

/**
* Helpers for dealing with {@link Breadcrumb}s.
*/
public final class Breadcrumbs {

/**
* Private constructor because this is a utility class.
*/
private Breadcrumbs() {

}

/**
* Record a {@link Breadcrumb} into all of this thread's active contexts.
*
* @param breadcrumb Breadcrumb to record
*/
public static void record(Breadcrumb breadcrumb) {
for (RavenContext context : RavenContext.getActiveContexts()) {
context.recordBreadcrumb(breadcrumb);
}
}

}
@@ -0,0 +1,41 @@
package com.getsentry.raven.event.helper;

import com.getsentry.raven.Raven;
import com.getsentry.raven.event.Breadcrumb;
import com.getsentry.raven.event.EventBuilder;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* {@link EventBuilderHelper} that extracts and sends any data attached to the
* provided {@link Raven}'s {@link com.getsentry.raven.RavenContext}.
*/
public class ContextBuilderHelper implements EventBuilderHelper {

/**
* Raven object where the RavenContext comes from.
*/
private Raven raven;

/**
* {@link EventBuilderHelper} that extracts context data from the provided {@link Raven} client.
*
* @param raven Raven client which holds RavenContext to be used.
*/
public ContextBuilderHelper(Raven raven) {
this.raven = raven;
}

@Override
public void helpBuildingEvent(EventBuilder eventBuilder) {
List<Breadcrumb> breadcrumbs = new ArrayList<>();
Iterator<Breadcrumb> iter = raven.getContext().getBreadcrumbs();
while (iter.hasNext()) {
breadcrumbs.add(iter.next());
}
eventBuilder.withBreadcrumbs(breadcrumbs);
}

}

0 comments on commit 34798e1

Please sign in to comment.