class Log {

    private static final org.slf4j.Logger Logger = org.slf4j.LoggerFactory.getLogger(Log.class);
    public static final SystemProperty<Boolean> DEBUG_ENABLED = SystemProperty.Builder.ofType(Boolean.class)
        .setKey("log.debug.enabled")
        .setDefaultValue(false)
        .setDynamic(true)
        .addListener(Log::setDebugEnabled)
        .build();

    public static final SystemProperty<Boolean> TRACE_ENABLED = SystemProperty.Builder.ofType(Boolean.class)
        .setKey("log.trace.enabled")
        .setDefaultValue(false)
        .setDynamic(true)
        .addListener(Log::setTraceEnabled)
        .build();

    private static Level lastLogLevel = getRootLogLevel();

    public static void setDebugEnabled(final boolean enabled) {<FILL_FUNCTION_BODY>}
    
    public static void setTraceEnabled(final boolean enabled) {
        if (enabled && getRootLogLevel().isMoreSpecificThan(Level.TRACE)) {
            lastLogLevel = getRootLogLevel();
            setLogLevel(Level.TRACE);
        } else if (!enabled && getRootLogLevel().isLessSpecificThan(Level.TRACE)) {
            setLogLevel(lastLogLevel != Level.TRACE ? lastLogLevel : Level.INFO);
            lastLogLevel = Level.TRACE;
        }
    }

    public static Level getRootLogLevel() {
        // SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
        final LoggerContext ctx = (LoggerContext) LogManager.getContext( false );
        final Configuration config = ctx.getConfiguration();
        final LoggerConfig loggerConfig = config.getRootLogger();
        return loggerConfig.getLevel();
    }

    private static void setLogLevel(Level newLevel) {
        // SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
        final LoggerContext ctx = (LoggerContext) LogManager.getContext( false );
        final Configuration config = ctx.getConfiguration();
        final LoggerConfig loggerConfig = config.getRootLogger();
        loggerConfig.setLevel( newLevel );
        ctx.updateLoggers();  // This causes all Loggers to refetch information from their LoggerConfig.
    }

    public static void rotateOpenfireLogFile() {
        // SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
        File logFile = new File(Log.getLogDirectory(), "openfire.log");
        emptyFile(logFile);
    }

    public static void markOpenfireLogFile(String username) {
        String message = getMarkMessage(username);
        File logFile = new File(Log.getLogDirectory(), "openfire.log");

        try(FileWriter fw = new FileWriter(logFile, true);
            PrintWriter out = new PrintWriter(fw))
        {
            out.println(message);
        } catch (IOException e) {
            e.printStackTrace(); // Writing it to the logfile feels wrong, as we're processing the logfile here.
        }
    }
    
    /**
     * Returns the directory that log files exist in. The directory name will
     * have a File.separator as the last character in the string.
     *
     * @return the directory that log files exist in.
     */
    public static String getLogDirectory() {
        // SLF4J doesn't provide a hook into the logging implementation. We'll have to do this 'direct', bypassing slf4j.
        final StringBuilder sb = new StringBuilder();
        sb.append(JiveGlobals.getHomePath());
        if (!sb.substring(sb.length()-1).startsWith(File.separator)) {
            sb.append(File.separator);
        }
        sb.append("logs");
        sb.append(File.separator);
        return sb.toString();
    }

    private static String getMarkMessage(String username) {
        final List<String> args = new ArrayList<>();
        args.add(username);
        args.add(JiveGlobals.formatDateTime(new java.util.Date()));
        return LocaleUtils.getLocalizedString("log.marker_inserted_by", args);
    }
    
    private static void printToStdErr(String s, Throwable throwable) {
        if (s != null) {
            System.err.println(s);
        }
        if (throwable != null) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            throwable.printStackTrace(pw);
            System.err.print(sw.toString());
            System.err.print("\n");
        }
    }

    private static void emptyFile(File logFile) {
        BufferedWriter out = null;
        try {
            out = new BufferedWriter(new FileWriter(logFile));
            out.write("");
        } catch (IOException ex) {
            Logger.warn("Could not empty file " + logFile.getName(), ex);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ex) {
                    Logger.warn("Could not close file.", ex);
                }
            }
        }
    }
}

        if (enabled && getRootLogLevel().isMoreSpecificThan(Level.DEBUG)) {
            lastLogLevel = getRootLogLevel();
            setLogLevel(Level.DEBUG);
        } else if (!enabled && getRootLogLevel().isLessSpecificThan(Level.DEBUG)) {
            setLogLevel(lastLogLevel != Level.DEBUG ? lastLogLevel : Level.INFO);
            lastLogLevel = Level.DEBUG;
        }
    
<no_super_class>