HytaleSentryHandler.java
package com.hypixel.hytale.logger.sentry;
import io.sentry.Breadcrumb;
import io.sentry.Hint;
import io.sentry.IScopes;
import io.sentry.ScopesAdapter;
import io.sentry.Sentry;
import io.sentry.SentryAttribute;
import io.sentry.SentryAttributes;
import io.sentry.SentryEvent;
import io.sentry.SentryIntegrationPackageStorage;
import io.sentry.SentryLevel;
import io.sentry.SentryLogLevel;
import io.sentry.exception.ExceptionMechanismException;
import io.sentry.logger.SentryLogParameters;
import io.sentry.protocol.Mechanism;
import io.sentry.protocol.Message;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Filter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class HytaleSentryHandler extends Handler {
public static final String MECHANISM_TYPE = "JulSentryHandler";
public static final String THREAD_ID = "thread_id";
private final IScopes scope;
private boolean printfStyle;
@Nonnull
private Level minimumBreadcrumbLevel;
@Nonnull
private Level minimumEventLevel;
@Nonnull
private Level minimumLevel;
public HytaleSentryHandler(@Nonnull IScopes scope) {
this(scope, true);
}
HytaleSentryHandler(@Nonnull IScopes scope, boolean configureFromLogManager) {
this.minimumBreadcrumbLevel = Level.INFO;
this.minimumEventLevel = Level.SEVERE;
this.minimumLevel = Level.INFO;
this.setFilter(new DropSentryFilter());
if (configureFromLogManager) {
this.retrieveProperties();
}
this.scope = scope;
}
public void publish(@Nonnull LogRecord record) {
if (this.isLoggable(record)) {
try {
if (ScopesAdapter.getInstance().getOptions().getLogs().isEnabled() && record.getLevel().intValue() >= this.minimumLevel.intValue()) {
this.captureLog(record);
}
if (record.getLevel().intValue() >= this.minimumEventLevel.intValue()) {
Hint hint = new Hint();
hint.set("syntheticException", record);
this.scope.captureEvent(this.createEvent(record), hint);
}
if (record.getLevel().intValue() >= this.minimumBreadcrumbLevel.intValue()) {
Hint hint = new Hint();
hint.set("jul:logRecord", record);
Sentry.addBreadcrumb(this.createBreadcrumb(record), hint);
}
} catch (RuntimeException e) {
this.reportError("An exception occurred while creating a new event in Sentry", e, 1);
}
}
}
protected void captureLog(@Nonnull LogRecord loggingEvent) {
SentryLogLevel sentryLevel = toSentryLogLevel(loggingEvent.getLevel());
Object[] arguments = loggingEvent.getParameters();
SentryAttributes attributes = SentryAttributes.of();
String message = loggingEvent.getMessage();
if (loggingEvent.getResourceBundle() != null && loggingEvent.getResourceBundle().containsKey(loggingEvent.getMessage())) {
message = loggingEvent.getResourceBundle().getString(loggingEvent.getMessage());
}
String formattedMessage = this.maybeFormatted(arguments, message);
if (!formattedMessage.equals(message)) {
attributes.add(SentryAttribute.stringAttribute("sentry.message.template", message));
}
SentryLogParameters params = SentryLogParameters.create(attributes);
params.setOrigin("auto.log.jul.hytale");
Sentry.logger().log(sentryLevel, params, formattedMessage, arguments);
}
@Nonnull
private String maybeFormatted(@Nonnull Object[] arguments, @Nonnull String message) {
if (arguments != null) {
try {
return this.formatMessage(message, arguments);
} catch (RuntimeException var4) {
}
}
return message;
}
private void retrieveProperties() {
LogManager manager = LogManager.getLogManager();
String className = HytaleSentryHandler.class.getName();
this.setPrintfStyle(Boolean.parseBoolean(manager.getProperty(className + ".printfStyle")));
this.setLevel(this.parseLevelOrDefault(manager.getProperty(className + ".level")));
String minimumBreadCrumbLevel = manager.getProperty(className + ".minimumBreadcrumbLevel");
if (minimumBreadCrumbLevel != null) {
this.setMinimumBreadcrumbLevel(this.parseLevelOrDefault(minimumBreadCrumbLevel));
}
String minimumEventLevel = manager.getProperty(className + ".minimumEventLevel");
if (minimumEventLevel != null) {
this.setMinimumEventLevel(this.parseLevelOrDefault(minimumEventLevel));
}
String minimumLevel = manager.getProperty(className + ".minimumLevel");
if (minimumLevel != null) {
this.setMinimumLevel(this.parseLevelOrDefault(minimumLevel));
}
}
@Nullable
private static SentryLevel formatLevel(@Nonnull Level level) {
if (level.intValue() >= Level.SEVERE.intValue()) {
return SentryLevel.ERROR;
} else if (level.intValue() >= Level.WARNING.intValue()) {
return SentryLevel.WARNING;
} else if (level.intValue() >= Level.INFO.intValue()) {
return SentryLevel.INFO;
} else {
return level.intValue() >= Level.ALL.intValue() ? SentryLevel.DEBUG : null;
}
}
@Nonnull
private static SentryLogLevel toSentryLogLevel(@Nonnull Level level) {
if (level.intValue() >= Level.SEVERE.intValue()) {
return SentryLogLevel.ERROR;
} else if (level.intValue() >= Level.WARNING.intValue()) {
return SentryLogLevel.WARN;
} else if (level.intValue() >= Level.INFO.intValue()) {
return SentryLogLevel.INFO;
} else {
return level.intValue() >= Level.FINE.intValue() ? SentryLogLevel.DEBUG : SentryLogLevel.TRACE;
}
}
@Nonnull
private Level parseLevelOrDefault(@Nonnull String levelName) {
try {
return Level.parse(levelName.trim());
} catch (RuntimeException var3) {
return Level.WARNING;
}
}
@Nonnull
private Breadcrumb createBreadcrumb(@Nonnull LogRecord record) {
Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setLevel(formatLevel(record.getLevel()));
breadcrumb.setCategory(record.getLoggerName());
if (record.getParameters() != null) {
try {
breadcrumb.setMessage(this.formatMessage(record.getMessage(), record.getParameters()));
} catch (RuntimeException var4) {
breadcrumb.setMessage(record.getMessage());
}
} else {
breadcrumb.setMessage(record.getMessage());
}
return breadcrumb;
}
@Nonnull
SentryEvent createEvent(@Nonnull LogRecord record) {
SentryEvent event = new SentryEvent(new Date(record.getMillis()));
event.setLevel(formatLevel(record.getLevel()));
event.setLogger(record.getLoggerName());
Message sentryMessage = new Message();
sentryMessage.setParams(this.toParams(record.getParameters()));
String message = record.getMessage();
if (record.getResourceBundle() != null && record.getResourceBundle().containsKey(record.getMessage())) {
message = record.getResourceBundle().getString(record.getMessage());
}
sentryMessage.setMessage(message);
if (record.getParameters() != null) {
try {
sentryMessage.setFormatted(this.formatMessage(message, record.getParameters()));
} catch (RuntimeException var8) {
}
}
event.setMessage(sentryMessage);
Throwable throwable = record.getThrown();
if (throwable != null) {
Mechanism mechanism = new Mechanism();
mechanism.setType("JulSentryHandler");
Throwable mechanismException = new ExceptionMechanismException(mechanism, throwable, Thread.currentThread());
event.setThrowable(mechanismException);
}
event.setExtra("thread_id", String.valueOf(record.getLongThreadID()));
return event;
}
@Nonnull
private List<String> toParams(@Nullable Object[] arguments) {
List<String> result = new ArrayList();
if (arguments != null) {
for(Object argument : arguments) {
if (argument != null) {
result.add(argument.toString());
}
}
}
return result;
}
@Nonnull
private String formatMessage(@Nonnull String message, @Nullable Object[] parameters) {
String formatted;
if (this.printfStyle) {
formatted = String.format(message, parameters);
} else {
formatted = MessageFormat.format(message, parameters);
}
return formatted;
}
public void flush() {
}
public void close() throws SecurityException {
try {
Sentry.close();
} catch (RuntimeException e) {
this.reportError("An exception occurred while closing the Sentry connection", e, 3);
}
}
public void setPrintfStyle(boolean printfStyle) {
this.printfStyle = printfStyle;
}
public void setMinimumBreadcrumbLevel(@Nullable Level minimumBreadcrumbLevel) {
if (minimumBreadcrumbLevel != null) {
this.minimumBreadcrumbLevel = minimumBreadcrumbLevel;
}
}
@Nonnull
public Level getMinimumBreadcrumbLevel() {
return this.minimumBreadcrumbLevel;
}
public void setMinimumEventLevel(@Nullable Level minimumEventLevel) {
if (minimumEventLevel != null) {
this.minimumEventLevel = minimumEventLevel;
}
}
@Nonnull
public Level getMinimumEventLevel() {
return this.minimumEventLevel;
}
public void setMinimumLevel(@Nullable Level minimumLevel) {
if (minimumLevel != null) {
this.minimumLevel = minimumLevel;
}
}
@Nonnull
public Level getMinimumLevel() {
return this.minimumLevel;
}
public boolean isPrintfStyle() {
return this.printfStyle;
}
static {
SentryIntegrationPackageStorage.getInstance().addPackage("maven:io.sentry:sentry-jul", "8.29.0");
}
private static final class DropSentryFilter implements Filter {
private DropSentryFilter() {
}
public boolean isLoggable(@Nonnull LogRecord record) {
String loggerName = record.getLoggerName();
return loggerName == null || !loggerName.startsWith("server.io.sentry") || loggerName.startsWith("server.io.sentry.samples");
}
}
}