Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,26 @@ public class BugsnagSpringConfiguration implements InitializingBean {
Callback springVersionErrorCallback() {
Callback callback = new Callback() {
@Override
public void beforeNotify(Report report) {
public boolean onError(Report report) {
addSpringRuntimeVersion(report.getDevice());
return true;
}
};
bugsnag.addCallback(callback);
return callback;
}

@Bean
BeforeSendSession springVersionSessionCallback() {
BeforeSendSession beforeSendSession = new BeforeSendSession() {
OnSession springVersionSessionCallback() {
OnSession onSession = new OnSession() {
@Override
public void beforeSendSession(SessionPayload payload) {
public boolean onSession(SessionPayload payload) {
addSpringRuntimeVersion(payload.getDevice());
return true;
}
};
bugsnag.addBeforeSendSession(beforeSendSession);
return beforeSendSession;
bugsnag.addOnSession(onSession);
return onSession;
}

private void addSpringRuntimeVersion(Map<String, Object> device) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,15 @@ class ExceptionClassCallback implements Callback {
}

@Override
public void beforeNotify(Report report) {
public boolean onError(Report report) {

HandledState handledState = report.getHandledState();

// A manually-set severity takes precedence
SeverityReasonType severityReasonType = handledState.calculateSeverityReasonType();
if (severityReasonType == SeverityReasonType.REASON_USER_SPECIFIED
|| severityReasonType == SeverityReasonType.REASON_CALLBACK_SPECIFIED) {
return;
return true; // do not change delivery decision
}

Class exceptionClass = report.getException().getClass();
Expand All @@ -133,5 +133,6 @@ public void beforeNotify(Report report) {
severity,
handledState.isUnhandled()));
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,26 @@ public class SpringBootConfiguration {
Callback springBootVersionErrorCallback() {
Callback callback = new Callback() {
@Override
public void beforeNotify(Report report) {
public boolean onError(Report report) {
addSpringRuntimeVersion(report.getDevice());
return true;
}
};
bugsnag.addCallback(callback);
return callback;
}

@Bean
BeforeSendSession springBootVersionSessionCallback() {
BeforeSendSession beforeSendSession = new BeforeSendSession() {
OnSession springBootVersionSessionCallback() {
OnSession onSession = new OnSession() {
@Override
public void beforeSendSession(SessionPayload payload) {
public boolean onSession(SessionPayload payload) {
addSpringRuntimeVersion(payload.getDevice());
return true;
}
};
bugsnag.addBeforeSendSession(beforeSendSession);
return beforeSendSession;
bugsnag.addOnSession(onSession);
return onSession;
}

private void addSpringRuntimeVersion(Map<String, Object> device) {
Expand Down
3 changes: 2 additions & 1 deletion bugsnag-spring/src/test/java/com/bugsnag/SpringMvcTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,9 @@ public void unhandledTypeMismatchExceptionCallbackSeverity()
Report report;
Callback callback = new Callback() {
@Override
public void beforeNotify(Report report) {
public boolean onError(Report report) {
report.setSeverity(Severity.WARNING);
return true;
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ public void handledTypeMismatchExceptionCallbackSeverity() {
} catch (TypeMismatchException ex) {
bugsnag.notify(ex, new Callback() {
@Override
public void beforeNotify(Report report) {
public boolean onError(Report report) {
report.setSeverity(Severity.WARNING);
return true;
}
});
}
Expand Down
5 changes: 0 additions & 5 deletions bugsnag/src/main/java/com/bugsnag/BeforeSendSession.java

This file was deleted.

28 changes: 10 additions & 18 deletions bugsnag/src/main/java/com/bugsnag/Bugsnag.java
Original file line number Diff line number Diff line change
Expand Up @@ -452,16 +452,12 @@ public boolean notify(Report report, Callback reportCallback) {
return false;
}

// Run all client-wide beforeNotify callbacks
// Run all client-wide onError callbacks
for (Callback callback : config.callbacks) {
try {
// Run the callback
callback.beforeNotify(report);

// Check if callback cancelled delivery
if (report.getShouldCancel()) {
LOGGER.debug("Error not reported to Bugsnag - "
+ "cancelled by a client-wide beforeNotify callback");
boolean proceed = callback.onError(report);
if (!proceed || report.getShouldCancel()) {
LOGGER.debug("Error not reported to Bugsnag - cancelled by a client-wide onError callback");
return false;
}
} catch (Throwable ex) {
Expand All @@ -472,16 +468,12 @@ public boolean notify(Report report, Callback reportCallback) {
// Add thread metadata to the report
report.mergeMetadata(THREAD_METADATA.get());

// Run the report-specific beforeNotify callback, if given
// Run the report-specific onError callback, if given
if (reportCallback != null) {
try {
// Run the callback
reportCallback.beforeNotify(report);

// Check if callback cancelled delivery
if (report.getShouldCancel()) {
LOGGER.debug(
"Error not reported to Bugsnag - cancelled by a report-specific callback");
boolean proceed = reportCallback.onError(report);
if (!proceed || report.getShouldCancel()) {
LOGGER.debug("Error not reported to Bugsnag - cancelled by a report-specific callback");
return false;
}
} catch (Throwable ex) {
Expand Down Expand Up @@ -676,7 +668,7 @@ public static Set<Bugsnag> uncaughtExceptionClients() {
return Collections.emptySet();
}

void addBeforeSendSession(BeforeSendSession beforeSendSession) {
sessionTracker.addBeforeSendSession(beforeSendSession);
void addOnSession(OnSession onSession) {
sessionTracker.addOnSession(onSession);
}
}
11 changes: 8 additions & 3 deletions bugsnag/src/main/java/com/bugsnag/BugsnagAppender.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ protected void append(final ILoggingEvent event) {
calculateSeverity(event),
new Callback() {
@Override
public void beforeNotify(Report report) {
public boolean onError(Report report) {

// Add some data from the logging event
report.addToTab("Log event data",
Expand All @@ -146,8 +146,12 @@ public void beforeNotify(Report report) {
populateContextData(report, event);

if (reportCallback != null) {
reportCallback.beforeNotify(report);
boolean proceed = reportCallback.onError(report);
if (!proceed) {
return false; // suppress delivery
}
}
return true;
}
});
}
Expand Down Expand Up @@ -270,7 +274,7 @@ private Bugsnag createBugsnag() {
// Add a callback to put global metadata on every report
bugsnag.addCallback(new Callback() {
@Override
public void beforeNotify(Report report) {
public boolean onError(Report report) {

for (LogbackMetadata metadata : globalMetadata) {
for (LogbackMetadataTab tab : metadata.getTabs()) {
Expand All @@ -282,6 +286,7 @@ public void beforeNotify(Report report) {
}

}
return true;
}
});

Expand Down
6 changes: 6 additions & 0 deletions bugsnag/src/main/java/com/bugsnag/OnSession.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.bugsnag;

@FunctionalInterface
interface OnSession {
boolean onSession(SessionPayload payload);
}
15 changes: 13 additions & 2 deletions bugsnag/src/main/java/com/bugsnag/SessionPayload.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,21 @@ final class SessionPayload {

private final Collection<SessionCount> sessionCounts;
private final Diagnostics diagnostics;
private final Map<String, Object> device;
private final Map<String, Object> app;

SessionPayload(Collection<SessionCount> sessionCounts, Configuration configuration) {
this.sessionCounts = sessionCounts;
diagnostics = new Diagnostics(configuration);
this.device = null;
this.app = null;
}

SessionPayload(Collection<SessionCount> sessionCounts, Map<String, Object> device, Map<String, Object> app) {
this.sessionCounts = sessionCounts;
this.diagnostics = null;
this.device = device;
this.app = app;
}

@Expose
Expand All @@ -22,12 +33,12 @@ Notifier getNotifier() {

@Expose
Map<String, Object> getDevice() {
return diagnostics.device;
return device != null ? device : diagnostics.device;
}

@Expose
Map<String, Object> getApp() {
return diagnostics.app;
return app != null ? app : diagnostics.app;
}

@Expose
Expand Down
47 changes: 35 additions & 12 deletions bugsnag/src/main/java/com/bugsnag/SessionTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
Expand All @@ -16,12 +17,11 @@ class SessionTracker {
private final Configuration config;
private final ThreadLocal<Session> session = new ThreadLocal<Session>();
private final AtomicReference<SessionCount> batchCount = new AtomicReference<SessionCount>();
private final Collection<SessionCount>
enqueuedSessionCounts = new ConcurrentLinkedQueue<SessionCount>();
private final Collection<SessionCount> enqueuedSessionCounts = new ConcurrentLinkedQueue<SessionCount>();

private final Semaphore flushingRequest = new Semaphore(1);
private final AtomicBoolean shuttingDown = new AtomicBoolean();
private final Collection<BeforeSendSession> sessionCallbacks = new ConcurrentLinkedQueue<BeforeSendSession>();
private final Collection<OnSession> sessionCallbacks = new ConcurrentLinkedQueue<OnSession>();

SessionTracker(Configuration configuration) {
this.config = configuration;
Expand Down Expand Up @@ -86,17 +86,40 @@ private void sendSessions(Date now) {

if (!enqueuedSessionCounts.isEmpty() && flushingRequest.tryAcquire(1)) {
try {
Collection<SessionCount> requestValues
= new ArrayList<SessionCount>(enqueuedSessionCounts);
SessionPayload payload = new SessionPayload(requestValues, config);
Collection<SessionCount> requestValues = new ArrayList<SessionCount>(enqueuedSessionCounts);
Collection<SessionCount> approvedSessions = new ArrayList<SessionCount>();
SessionPayload firstPayload = null;

for (SessionCount sessionCount : requestValues) {
SessionPayload payload = new SessionPayload(Collections.singleton(sessionCount), config);

boolean sendThisSession = true;
for (OnSession callback : sessionCallbacks) {
if (!callback.onSession(payload)) {
sendThisSession = false;
break;
}
}

for (BeforeSendSession callback : sessionCallbacks) {
callback.beforeSendSession(payload);
if (sendThisSession) {
approvedSessions.add(sessionCount);
if (firstPayload == null) {
firstPayload = payload;
}
}
}

if (!approvedSessions.isEmpty()) {
// Reuse the device/app from the first approved payload to preserve runtime
// versions
SessionPayload batchPayload = new SessionPayload(approvedSessions, firstPayload.getDevice(),
firstPayload.getApp());
Delivery delivery = config.sessionDelivery;
delivery.deliver(config.serializer, batchPayload, config.getSessionApiHeaders());
}

Delivery delivery = config.sessionDelivery;
delivery.deliver(config.serializer, payload, config.getSessionApiHeaders());
enqueuedSessionCounts.removeAll(requestValues);

} finally {
flushingRequest.release(1);
}
Expand All @@ -109,7 +132,7 @@ void shutdown() {
}
}

void addBeforeSendSession(BeforeSendSession beforeSendSession) {
sessionCallbacks.add(beforeSendSession);
void addOnSession(OnSession onSession) {
sessionCallbacks.add(onSession);
}
}
3 changes: 2 additions & 1 deletion bugsnag/src/main/java/com/bugsnag/callbacks/AppCallback.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public AppCallback(Configuration config) {
}

@Override
public void beforeNotify(Report report) {
public boolean onError(Report report) {
if (config.appType != null) {
report.setAppInfo("type", config.appType);
}
Expand All @@ -23,5 +23,6 @@ public void beforeNotify(Report report) {
if (config.releaseStage != null) {
report.setAppInfo("releaseStage", config.releaseStage);
}
return true;
}
}
6 changes: 5 additions & 1 deletion bugsnag/src/main/java/com/bugsnag/callbacks/Callback.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
public interface Callback {
/**
* Perform changes to the report before delivery.
* Return {@code true} to continue sending, or {@code false} to cancel delivery.
* Implementations may also call {@code report.cancel()} for backward
* compatibility.
*
* @param report the report to perform changes on.
* @return true to send, false to suppress delivery
*/
void beforeNotify(Report report);
boolean onError(Report report);
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,13 @@ public void run() {
}

@Override
public void beforeNotify(Report report) {
public boolean onError(Report report) {
report
.addToTab("device", "osArch", System.getProperty("os.arch"))
.addToTab("device", "locale", Locale.getDefault())
.setDeviceInfo("hostname", getHostnameValue())
.setDeviceInfo("osName", System.getProperty("os.name"))
.setDeviceInfo("osVersion", System.getProperty("os.version"));
return true;
}
}
Loading