package com.hypixel.hytale.logger.backend;
import com.google.common.flogger.backend.LogData;
import com.google.common.flogger.backend.LoggerBackend;
import com.google.common.flogger.backend.system.SimpleLogRecord;
import com.hypixel.hytale.logger.sentry.HytaleSentryHandler;
import com.hypixel.hytale.logger.sentry.SkipSentryException;
import io.sentry.IScopes;
import java.io.PrintStream;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class HytaleLoggerBackend extends LoggerBackend {
public static Function<String, Level> LOG_LEVEL_LOADER;
public static final PrintStream REAL_SOUT;
public static final PrintStream REAL_SERR;
private static final Map<String, HytaleLoggerBackend> CACHE;
private static final HytaleLoggerBackend ROOT_LOGGER;
private static final int OFF_VALUE;
private final String name;
private final HytaleLoggerBackend parent;
@Nonnull
private Level level;
private BiConsumer<Level, Level> onLevelChange;
@Nullable
private HytaleSentryHandler sentryHandler;
private boolean propagateSentryToParent;
@Nonnull
private CopyOnWriteArrayList<CopyOnWriteArrayList<LogRecord>> subscribers;
protected HytaleLoggerBackend(String name) {
this.level = Level.INFO;
this.propagateSentryToParent = true;
this.subscribers = new CopyOnWriteArrayList();
this.name = name;
this.parent = ROOT_LOGGER;
}
protected HytaleLoggerBackend(String name, HytaleLoggerBackend parent) {
this.level = Level.INFO;
this.propagateSentryToParent = true;
this.subscribers = new CopyOnWriteArrayList();
this.name = name;
this.parent = parent;
}
public String getLoggerName() {
return this.name;
}
@Nonnull
public Level getLevel() {
return this.level;
}
public boolean isLoggable(@Nonnull Level lvl) {
int levelValue = this.level.intValue();
return lvl.intValue() >= levelValue && levelValue != OFF_VALUE;
}
public void log(@Nonnull LogData data) {
this.log((LogRecord)SimpleLogRecord.create(data));
}
public void handleError(@Nonnull RuntimeException error, @Nonnull LogData badData) {
this.log((LogRecord)SimpleLogRecord.error(error, badData));
}
public void log(@Nonnull LogRecord logRecord) {
this.log(logRecord, false);
}
public void log(@Nonnull LogRecord logRecord, boolean sentryHandled) {
if (this.sentryHandler != null && !sentryHandled && logRecord.getThrown() != null && !SkipSentryException.hasSkipSentry(logRecord.getThrown())) {
this.sentryHandler.publish(logRecord);
sentryHandled = true;
}
if (!this.propagateSentryToParent && !sentryHandled && logRecord.getThrown() != null) {
sentryHandled = true;
}
if (this.parent != null) {
this.parent.log(logRecord, sentryHandled);
} else {
HytaleFileHandler.INSTANCE.log(logRecord);
HytaleConsole.INSTANCE.publish(logRecord);
for(int i = 0; i < this.subscribers.size(); ++i) {
((CopyOnWriteArrayList)this.subscribers.get(i)).add(logRecord);
}
}
}
public static void subscribe(CopyOnWriteArrayList<LogRecord> subscriber) {
if (!ROOT_LOGGER.subscribers.contains(subscriber)) {
ROOT_LOGGER.subscribers.add(subscriber);
}
}
public static void unsubscribe(CopyOnWriteArrayList<LogRecord> subscriber) {
if (ROOT_LOGGER.subscribers.contains(subscriber)) {
ROOT_LOGGER.subscribers.remove(subscriber);
}
}
@Nonnull
public HytaleLoggerBackend getSubLogger(String name) {
HytaleLoggerBackend hytaleLoggerBackend = new HytaleLoggerBackend(this.name + "][" + name, this);
hytaleLoggerBackend.loadLogLevel();
return hytaleLoggerBackend;
}
public void setSentryClient(@Nullable IScopes scope) {
if (scope != null) {
this.sentryHandler = new HytaleSentryHandler(scope);
this.sentryHandler.setLevel(Level.ALL);
} else {
this.sentryHandler = null;
}
}
public void setPropagatesSentryToParent(boolean propagate) {
this.propagateSentryToParent = propagate;
}
public void setOnLevelChange(BiConsumer<Level, Level> onLevelChange) {
this.onLevelChange = onLevelChange;
}
public void setLevel(@Nonnull Level newLevel) {
Level old = this.level;
this.level = newLevel;
if (this.onLevelChange != null && !Objects.equals(old, newLevel)) {
this.onLevelChange.accept(old, newLevel);
}
}
public void loadLogLevel() {
if (this.name != null && LOG_LEVEL_LOADER != null) {
Level level = (Level)LOG_LEVEL_LOADER.apply(this.name);
if (level != null) {
this.setLevel(level);
}
}
}
public static void loadLevels(@Nonnull List<Map.Entry<String, Level>> list) {
for(Map.Entry<String, Level> e : list) {
getLogger((String)e.getKey()).setLevel((Level)e.getValue());
}
}
public static void reloadLogLevels() {
CACHE.values().forEach(HytaleLoggerBackend::loadLogLevel);
}
public static HytaleLoggerBackend getLogger() {
return ROOT_LOGGER;
}
public static HytaleLoggerBackend getLogger(@Nonnull String name) {
if (name.isEmpty()) {
return getLogger();
} else {
HytaleLoggerBackend logger = (HytaleLoggerBackend)CACHE.computeIfAbsent(name, HytaleLoggerBackend::new);
logger.loadLogLevel();
return logger;
}
}
@Nonnull
public static HytaleLoggerBackend getLogger(String name, BiConsumer<Level, Level> onLevelChange) {
HytaleLoggerBackend logger = (HytaleLoggerBackend)CACHE.computeIfAbsent(name, HytaleLoggerBackend::new);
logger.setOnLevelChange(onLevelChange);
logger.loadLogLevel();
return logger;
}
public static void setIndent(int indent) {
HytaleConsole.INSTANCE.getFormatter().maxModuleName = indent;
FileHandler fileHandler = HytaleFileHandler.INSTANCE.getFileHandler();
if (fileHandler != null) {
((HytaleLogFormatter)fileHandler.getFormatter()).maxModuleName = indent;
}
}
public static boolean isJunitTest() {
for(StackTraceElement element : Thread.currentThread().getStackTrace()) {
if (element.getClassName().startsWith("org.junit.")) {
return true;
}
}
return false;
}
public static void rawLog(String message) {
ROOT_LOGGER.log((LogRecord)(new RawLogRecord(Level.ALL, message)));
}
static {
REAL_SOUT = System.out;
REAL_SERR = System.err;
CACHE = new ConcurrentHashMap();
ROOT_LOGGER = new HytaleLoggerBackend("Hytale", (HytaleLoggerBackend)null);
OFF_VALUE = Level.OFF.intValue();
}
public static class RawLogRecord extends LogRecord {
public RawLogRecord(@Nonnull Level level, String msg) {
super(level, msg);
}
}
}