package tc.oc.api.minecraft.logging; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; import com.google.common.base.Strings; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.NestedCommand; import com.sk89q.minecraft.util.commands.SuggestException; import net.md_5.bungee.api.ChatColor; import org.apache.logging.log4j.spi.LoggerContext; import tc.oc.api.util.Permissions; import tc.oc.commons.core.commands.Commands; import tc.oc.commons.core.commands.NestedCommands; import tc.oc.commons.core.logging.Logging; import tc.oc.commons.core.logging.LoggingConfig; import tc.oc.minecraft.api.command.CommandSender; public class LoggingCommands implements NestedCommands { private final LoggingConfig loggingConfig; @Inject LoggingCommands(LoggingConfig loggingConfig) { this.loggingConfig = loggingConfig; } @Override public void enable() { // Verify this reflection magic works for(LoggerContext context : Logging.L4J.getContexts()) { Logging.L4J.getLoggers(context); } } private static String levelName(Level level) { if(level == Level.WARNING) { return "WARN"; } else if(level == null) { return "parent"; } else { return level.getName(); } } private static String colorLevelName(Level level) { return Logging.levelColor(level) + levelName(level) + ChatColor.RESET; } private static String colorLevelName(org.apache.logging.log4j.Level level) { return Logging.levelColor(level) + level.name() + ChatColor.RESET; } private static String paddedLevelName(Level level) { return Logging.levelColor(level) + Strings.padEnd(levelName(level), 6, ' ') + ChatColor.RESET; } private static String paddedLevelName(org.apache.logging.log4j.Level level) { return Logging.levelColor(level) + Strings.padEnd(level.name(), 6, ' ') + ChatColor.RESET; } private static String loggerName(String literal) { if(literal == null || literal.length() == 0) { return "<root>"; } else { return literal; } } private static String loggerName(Logger logger) { return loggerName(logger.getName()); } private String loggerNameArg(CommandContext args, int index) throws CommandException, SuggestException { return args.string(index, Stream.concat(Logging.loggerNames(), Logging.L4J.loggerNames()) .sorted() .collect(Collectors.toList())); } public static class Parent implements Commands { @Command( aliases = "log", desc = "Commands related to logging", min = 1, max = -1 ) @NestedCommand(LoggingCommands.class) @CommandPermissions(Permissions.DEVELOPER) public void log(CommandContext args, CommandSender sender) throws CommandException {} } @Command( aliases = "list", desc = "List all registered loggers", usage = "[prefix]", min = 0, max = 1 ) public void list(CommandContext args, CommandSender sender) throws CommandException { String prefix = args.getString(0, ""); for(LoggerContext context : Logging.L4J.getContexts()) { Map<String, org.apache.logging.log4j.Logger> loggers = Logging.L4J.getLoggers(context); if(!loggers.isEmpty()) { List<String> names = new ArrayList<>(loggers.keySet()); Collections.sort(names); boolean first = true; for(String name : names) { if(name.startsWith(prefix)) { if(first) { first = false; sender.sendMessage(ChatColor.YELLOW + "log4j Loggers (" + Logging.L4J.getContextName(context) + "):"); } org.apache.logging.log4j.Logger logger = loggers.get(name); sender.sendMessage("[" + paddedLevelName(Logging.L4J.getLevel(logger)) + "] [" + paddedLevelName(Logging.L4J.getEffectiveLevel(logger)) + "] " + loggerName(logger.getName())); } } } } LogManager lm = LogManager.getLogManager(); List<String> names = Collections.list(lm.getLoggerNames()); if(!names.isEmpty()) { sender.sendMessage(ChatColor.YELLOW + "java.util.logging Loggers:"); Collections.sort(names); for(String name : names) { if(name.startsWith(prefix)) { Logger logger = lm.getLogger(name); if(logger != null) { sender.sendMessage("[" + paddedLevelName(logger.getLevel()) + "] [" + paddedLevelName(Logging.getEffectiveLevel(logger)) + "] " + loggerName(name)); } } } } } @Command( aliases = "level", desc = "Set or reset the level of a logger, or all loggers", usage = "<reset|off|severe|warning|info|config|fine|finer|finest|all> [jul|l4j] [root | <logger name>]", min = 1, max = 3 ) public void level(CommandContext args, CommandSender sender) throws CommandException, SuggestException { String levelName = args.getString(0).toUpperCase(); boolean jul = true, l4j = true; if(args.argsLength() >= 2) { String subsystem = args.getString(1).toLowerCase(); if("jul".equals(subsystem)) { l4j = false; } else if("l4j".equals(subsystem)) { jul = false; } } String loggerName = loggerNameArg(args, jul && l4j ? 1 : 2); if(jul) { Logger julLogger = Logging.findLogger(loggerName); if(julLogger != null) { Level level; if("RESET".equals(levelName)) { level = null; } else { level = Level.parse(levelName); } julLogger.setLevel(level); sender.sendMessage(ChatColor.WHITE + "Logger " + loggerName(julLogger) + " level " + (level == null ? "reset" : "set to " + colorLevelName(level))); return; } } if(l4j) { org.apache.logging.log4j.Logger l4jLogger = Logging.L4J.findLogger(loggerName); if(l4jLogger != null) { org.apache.logging.log4j.Level level = org.apache.logging.log4j.Level.valueOf(levelName); Logging.L4J.setLevel(l4jLogger, level); sender.sendMessage(ChatColor.WHITE + "Logger " + l4jLogger.getName() + " level " + (level == null ? "reset" : "set to " + colorLevelName(level))); return; } } throw new CommandException("No logger named '" + loggerName + "'"); } @Command( aliases = "props", desc = "Dump the current JUL logging properties", min = 0, max = 0 ) public void props(CommandContext args, CommandSender sender) throws CommandException { try { for(Map.Entry<Object, Object> entry : Logging.getLoggingProperties().entrySet()) { sender.sendMessage(entry.getKey() + "=" + entry.getValue()); } } catch(IllegalAccessException | NoSuchFieldException e) { throw new CommandException("Failed to get JUL logging properties", e); } } @Command( aliases = "load", desc = "Reload the logging configuration", min = 0, max = 0 ) public void load(CommandContext args, CommandSender sender) throws CommandException { loggingConfig.load(); sender.sendMessage("Logging configuration reloaded"); } }