310 lines
10 KiB
Java
310 lines
10 KiB
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 = Level.INFO;
|
|
@Nonnull
|
|
private Level minimumEventLevel = Level.SEVERE;
|
|
@Nonnull
|
|
private Level minimumLevel = Level.INFO;
|
|
|
|
public HytaleSentryHandler(@Nonnull IScopes scope) {
|
|
this(scope, true);
|
|
}
|
|
|
|
HytaleSentryHandler(@Nonnull IScopes scope, boolean configureFromLogManager) {
|
|
this.setFilter(new HytaleSentryHandler.DropSentryFilter());
|
|
if (configureFromLogManager) {
|
|
this.retrieveProperties();
|
|
}
|
|
|
|
this.scope = scope;
|
|
}
|
|
|
|
@Override
|
|
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 var3) {
|
|
this.reportError("An exception occurred while creating a new event in Sentry", var3, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void captureLog(@Nonnull LogRecord loggingEvent) {
|
|
SentryLogLevel sentryLevel = toSentryLogLevel(loggingEvent.getLevel());
|
|
Object[] arguments = loggingEvent.getParameters();
|
|
SentryAttributes attributes = SentryAttributes.of(new SentryAttribute[0]);
|
|
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;
|
|
}
|
|
|
|
@Override
|
|
public void flush() {
|
|
}
|
|
|
|
@Override
|
|
public void close() throws SecurityException {
|
|
try {
|
|
Sentry.close();
|
|
} catch (RuntimeException var2) {
|
|
this.reportError("An exception occurred while closing the Sentry connection", var2, 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 {
|
|
@Override
|
|
public boolean isLoggable(@Nonnull LogRecord record) {
|
|
String loggerName = record.getLoggerName();
|
|
return loggerName == null || !loggerName.startsWith("server.io.sentry") || loggerName.startsWith("server.io.sentry.samples");
|
|
}
|
|
}
|
|
}
|