@@ -1,5 +1,7 @@
package org.cobbzilla.wizard.server.listener;
import airbrake.AirbrakeNoticeBuilder;
import airbrake.AirbrakeNotifier;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.buffer.CircularFifoBuffer;
import org.cobbzilla.util.daemon.ErrorApi;
@@ -8,13 +10,13 @@ import org.cobbzilla.wizard.server.RestServer;
import org.cobbzilla.wizard.server.RestServerBase;
import org.cobbzilla.wizard.server.RestServerLifecycleListenerBase;
import org.cobbzilla.wizard.server.config.ErrorApiConfiguration;
import org.cobbzilla.wizard.server.config.RestServerConfiguration;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace;
import static org.cobbzilla.util.daemon.ZillaRuntime.shortError;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.string.StringUtil.ellipsis;
import static org.cobbzilla.util.system.Sleep.sleep;
@Slf4j
@@ -25,7 +27,7 @@ public class ErrbitConfigListener extends RestServerLifecycleListenerBase {
log.warn("onStart: no error API configured, not starting ErrbitApi error sender");
return;
}
final ErrbitApi errorApi = new ErrbitApi(server.getConfiguration());
final ErrbitApi errorApi = new ErrbitApi(server.getConfiguration().getErrorApi() );
RestServerBase.setErrorApi(errorApi);
ZillaRuntime.setErrorApi(errorApi);
errorApi.start();
@@ -37,34 +39,50 @@ public class ErrbitConfigListener extends RestServerLifecycleListenerBase {
private static final String SLEEP_MESSAGE = ErrbitApi.class.getName()+": waiting for more errors";
private final RestServerConfiguration config;
private final CircularFifoBuffer fifo;
private final String key;
private String env;
private long sendInterval;
ErrbitApi(RestServerConfiguration config) {
this.config = config;
this.fifo = new CircularFifoBuffer(config.getErrorApi().getBufferSize());
private final CircularFifoBuffer dupCache;
private final CircularFifoBuffer fifo;
private AirbrakeNotifier notifier;
ErrbitApi(ErrorApiConfiguration errorApi) {
if (errorApi != null && errorApi.isValid()) {
this.key = errorApi.getKey();
this.env = errorApi.getEnv();
this.sendInterval = errorApi.getSendInterval();
this.dupCache = new CircularFifoBuffer(errorApi.getDupCacheSize());
this.fifo = new CircularFifoBuffer(errorApi.getBufferSize());
this.notifier = new AirbrakeNotifier(errorApi.getUrl());
} else {
log.warn("ErrbitApi: errorApi is null or invalid, not running");
this.key = null;
this.fifo = null;
this.dupCache = null;
}
}
@Override public void report(Exception e) {
if (config.hasErrorApi()) {
if (key != null ) {
log.error(e.toString());
synchronized (fifo) { fifo.add(e); }
} else {
log.warn("report: could not send exception to error reporting API: "+e, e);
if (log.isWarnEnabled()) log.warn("report: could not send exception to error reporting API: "+e, e);
}
}
@Override public void report(String s) {
if (config.hasErrorApi()) {
log.error(s);
if (key != null ) {
if (log.isErrorEnabled()) log.error(s);
synchronized (fifo) { fifo.add(s); }
} else {
log.warn("report: could not send exception to error reporting API: "+s);
if (log.isWarnEnabled()) log.warn("report: could not send exception to error reporting API: "+s);
}
}
@Override public void report(String s, Exception e) {
report(s+"\nException: "+e+"\n"+getStackTrace(e));
report(s+"\nException: "+shortError( e) +"\n"+getStackTrace(e));
}
public void start() {
@@ -75,8 +93,6 @@ public class ErrbitConfigListener extends RestServerLifecycleListenerBase {
}
@Override public void run() {
final ErrorApiConfiguration errorApi = config.getErrorApi();
final long sendInterval = errorApi.getSendInterval();
while (true) {
try {
final List<Object> reports;
@@ -94,27 +110,50 @@ public class ErrbitConfigListener extends RestServerLifecycleListenerBase {
}
for (Object o : reports) {
if (o == null) {
log.error("ErrbitApi.run: NOT reporting null object"); // should never happen
if (log.isErrorEnabled()) log.error("ErrbitApi.run: NOT reporting null object"); // should never happen
} else if (o instanceof Exception) {
log.debug("ErrbitApi.run: reporting Exception: "+shortError((Exception) o));
errorApi.r eport((Exception) o);
if (log.isDebugEnabled()) log.debug("ErrbitApi.run: reporting Exception: "+shortError((Exception) o));
sendR eport((Exception) o);
} else if (o instanceof String) {
log.debug("ErrbitApi.run: reporting String: "+o);
errorApi.r eport((String) o);
if (log.isDebugEnabled()) log.debug("ErrbitApi.run: reporting String: "+o);
sendR eport((String) o);
} else {
final String val = o.toString();
log.warn("ErrbitApi.run: reporting object that is neither Exception nor String (" + o.getClass().getName() + ") as String: " + val);
errorApi.r eport(val);
if (log.isWarnEnabled()) log.warn("ErrbitApi.run: reporting object that is neither Exception nor String (" + o.getClass().getName() + ") as String: " + val);
sendR eport(val);
}
}
} catch (Exception e) {
log.error("ErrbitApi.run: unexpected exception: "+shortError(e));
if (log.isErrorEnabled()) log.error("ErrbitApi.run: unexpected exception: "+shortError(e));
}
}
}
public void sendReport(Exception e) { sendReport(fullError(e)); }
public void sendReport(String s) {
if (inCache(s)) {
if (log.isWarnEnabled()) log.warn("sendReport(" + ellipsis(s, 100) + "): already reported recently, not resending");
return;
}
final AirbrakeNoticeBuilder builder = new AirbrakeNoticeBuilder(key, s, env);
final int responseCode = notifier.notify(builder.newNotice());
if (responseCode != 200) {
if (log.isWarnEnabled()) log.warn("sendReport("+ellipsis(s, 100)+"): notifier API returned "+responseCode);
}
}
public boolean inCache(String s) {
synchronized (dupCache) {
if (dupCache.contains(s)) return true;
dupCache.add(s);
}
return false;
}
}
}