/* * Copyright (C) 2004-2008 Jive Software, 2017-2023 Ignite Realtime Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.util; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; /** * Openfire makes use of a logging facade (slf4j) to manage its log output. The * facade is backed up by Log4j by default. This class provides utility methods. *

* Additionally, this class provides methods that can be used to record logging * statements. These methods are exact duplicates of the previous Log * implementation of Openfire and are kept for backwards-compatibility (the are * deprecated). These methods will delegate logging functionality to slf4j. * Instead of these methods, slf4j logging functionality should be used * directly. * * @author Guus der Kinderen, guus.der.kinderen@gmail.com * @see http://www.slf4j.org/ */ public final class Log { private static final org.slf4j.Logger Logger = org.slf4j.LoggerFactory.getLogger(Log.class); public static final SystemProperty DEBUG_ENABLED = SystemProperty.Builder.ofType(Boolean.class) .setKey("log.debug.enabled") .setDefaultValue(false) .setDynamic(true) .addListener(Log::setDebugEnabled) .build(); public static final SystemProperty 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) { 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; } } 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 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); } } } } }