All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.chandre.admintool.log4j2.AdminToolLog4j2Util Maven / Gradle / Ivy

The newest version!
package de.chandre.admintool.log4j2;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.OutputStreamAppender;
import org.apache.logging.log4j.core.config.AppenderRef;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.spi.StandardLevel;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.StringUtils;

/**
 * service for log4j2 manipulation
 * @author Andre
 * @since 1.0.0
 */
@Service("adminToolLog4j2Util")
public class AdminToolLog4j2Util 
{
	private static List LEVELS = new ArrayList<>(7);
	static {
		LEVELS.add(Level.OFF);
		LEVELS.add(Level.TRACE);
		LEVELS.add(Level.DEBUG);
		LEVELS.add(Level.INFO);
		LEVELS.add(Level.WARN);
		LEVELS.add(Level.ERROR);
		LEVELS.add(Level.FATAL);
	}
	
	private static final Comparator LOGGER_COMP = new Comparator() {
		@Override
		public int compare(Logger o1, Logger o2) {
			return o1.getName().compareTo(o2.getName());
		}
	};
	
	private static final String DEFAULT_PATTERN = "%d{dd.MM.yyyy HH:mm:ss.SSS} %X{sessionId} [%t] %-5level %logger{36} : %msg%n";
	
	public static final String SESSION_APPENDER_NAME = "log4j2AppenderName";
	
	private Map customLoggers = new ConcurrentReferenceHashMap<>();
	private Map customParentLoggers = new ConcurrentReferenceHashMap<>();
	
	private Map outputStreams = new ConcurrentReferenceHashMap<>();
	
	public int getCustomLoggerSize() {
		return customLoggers.size();
	}
	
	public int getCustomParentLoggerSize() {
		return customParentLoggers.size();
	}
	
	public boolean isCustom(String name) {
		return customLoggers.containsValue(name) || customParentLoggers.containsValue(name);
	}
	
	/**
	 * returns all parent loggers 
	 * @return
	 */
	public Collection getParentLoggers() {
		LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
		List loggers = new ArrayList<>(ctx.getLoggers());
		Map parentMap = new HashMap<>();
		try {
			for (Logger logger : loggers) {
				if (null != logger.getParent() && parentMap.get(logger.getParent().getName()) == null) {
					parentMap.put(logger.getParent().getName(), logger.getParent());
				}
			}
			List parents = new ArrayList<>(parentMap.values());
			Collections.sort(parents, LOGGER_COMP);
			return parents;
		} finally {
			loggers.clear();
			parentMap.clear();
		}
	}
	
	public Collection getParentLoggerNames() {
		List loggerNames = new ArrayList<>();
		for (Logger logger : getParentLoggers()) {
			loggerNames.add(logger.getName());
		}
		return loggerNames;
	}
	
	/**
	 * returns all loggers 
	 * @return
	 */
	public Collection getLoggers() {
		LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
		List loggers = new ArrayList<>(ctx.getLoggers());
		Collections.sort(loggers, LOGGER_COMP);
		return loggers;
	}
	
	/**
	 * returns all logger names including custom loggers
	 * 
	 * @since 1.1.1
	 * @return
	 */
	public Collection getAllLoggerNames() {
		Set loggerNames = new TreeSet<>();
		for (Logger logger : getParentLoggers()) {
			loggerNames.add(logger.getName());
		}
		for (Logger logger : getLoggers()) {
			loggerNames.add(logger.getName());
		}
		if (!customLoggers.isEmpty()) {
			for (Entry entry : customLoggers.entrySet()) {
				loggerNames.add(entry.getKey().getName());
			}
		}
		if (!customParentLoggers.isEmpty()) {
			for (Entry entry : customParentLoggers.entrySet()) {
				loggerNames.add(entry.getKey().getName());
			}
		}
		return loggerNames;
	}
	
	/**
	 * returns the a css class with optional prefix for the particular log level.
* if prefix is set result will be <prefix>-<css-class> * * @param prefix (optional) a prefic * @param level the log level * @return */ public String getLoggerLevelCss(String prefix, Level level) { if (null == prefix) { prefix = ""; } else { prefix += "-"; } if (level.intLevel() == StandardLevel.TRACE.intLevel()) { return prefix + "info"; } if (level.intLevel() == StandardLevel.DEBUG.intLevel()) { return prefix + "primary"; } if (level.intLevel() == StandardLevel.INFO.intLevel()) { return prefix + "success"; } if (level.intLevel() == StandardLevel.WARN.intLevel()) { return prefix + "warning"; } if (level.intLevel() == StandardLevel.ERROR.intLevel()) { return prefix + "danger"; } if (level.intLevel() == StandardLevel.FATAL.intLevel()) { return prefix + "muted"; } if (level.intLevel() == StandardLevel.OFF.intLevel()) { return prefix + "muted"; } return ""; } /** * returns fix amount of logger levels * @return */ public Collection getLevels() { return LEVELS; } private Level getLevel(final String levelStr) { Level level = Level.getLevel(levelStr); if (null == level || !LEVELS.contains(level)) { throw new IllegalArgumentException("wrong logger level: " + String.valueOf(levelStr)); } return level; } /** * changes the level of an logger * * @param name logger name * @param levelStr level as string * @param parent if the logger is a parent logger * @throws IllegalArgumentException */ public void changeLogger(final String name, final String levelStr, boolean parent) throws IllegalArgumentException { Level level = getLevel(levelStr); changeLogger(name, level, parent); } /** * * @param name * @param level * @param parent * @throws IllegalArgumentException * @see {@link #changeLogger(String, String, boolean)} */ public void changeLogger(final String name, final Level level, boolean parent) throws IllegalArgumentException { if (null == name) { throw new IllegalArgumentException("logger name must not null"); } String loggerName = name; if (name.equals("ROOT")) { loggerName = LogManager.ROOT_LOGGER_NAME; } LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); LoggerConfig loggerConfig = config.getLoggerConfig(loggerName); if (null == loggerConfig) { throw new IllegalArgumentException("no logger config found for: " + String.valueOf(loggerName)); } if (customLoggers.containsValue(loggerName)) { setLevelOnExistingCustomLogger(this.customLoggers, loggerName, level); } else if (customParentLoggers.containsValue(loggerName)) { setLevelOnExistingCustomLogger(this.customParentLoggers, loggerName, level); } else if (!loggerConfig.getName().equals(loggerName)) { // LoggerConfig loggerConfigNew = new LoggerConfig(); // loggerConfigNew.setLevel(level); // config.addLogger(loggerName, loggerConfigNew); // if (parent) { // customParentLoggers.put(loggerConfigNew, loggerName); // } else { // customLoggers.put(loggerConfigNew, loggerName); // } addCustomParentLogger(false, level, loggerName, Arrays.asList("Console")); } else { loggerConfig.setLevel(level); } ctx.updateLoggers(); } private void setLevelOnExistingCustomLogger(Map customLoggers, String loggerName, Level level) { for (Entry entry : customLoggers.entrySet()) { if (entry.getValue().equals(loggerName)) { entry.getKey().setLevel(level); } } } public Collection getAppenderNames() { final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); final Configuration config = ctx.getConfiguration(); return config.getAppenders().keySet(); } public String getAppendersForLogger(String loggerName) { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); LoggerConfig loggerConfig = config.getLoggerConfig(loggerName); List appenderRefs = loggerConfig.getAppenderRefs(); return StringUtils.collectionToCommaDelimitedString(appenderRefs.parallelStream().map(ar -> ar.getRef()).collect(Collectors.toSet())); } /** * * @param additivity * @param level * @param loggerName * @param appenderNames * @param recursive * * @since 1.1.6.4 */ public void addCustomParentLogger(boolean additivity, String levelStr, String loggerName, Collection appenderNames) { Level level = getLevel(levelStr); addCustomParentLogger(additivity, level, loggerName, appenderNames); } /** * * @param additivity * @param level * @param loggerName * @param appenderNames * @param recursive * * @since 1.1.6.4 */ public void addCustomParentLogger(boolean additivity, Level level, String loggerName, Collection appenderNames) { if (StringUtils.isEmpty(loggerName)) { throw new IllegalArgumentException("loggerName should not be null"); } if (null == level) { throw new IllegalArgumentException("level should not be null"); } LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); if (null == appenderNames) { appenderNames = Collections.emptyList(); } List appenderRefs = appenderNames.stream() .map(name -> AppenderRef.createAppenderRef(name, null, null)) .collect(Collectors.toList()); LoggerConfig loggerConfigForTest = config.getLoggerConfig(loggerName); List appenderRefsToAdd = new ArrayList<>(appenderNames); final LoggerConfig loggerConfig; if (null == loggerConfigForTest || StringUtils.isEmpty(loggerConfigForTest.getName()) || !loggerConfigForTest.getName().equals(loggerName)) { //create a new Logger loggerConfig = LoggerConfig.createLogger(additivity, level, loggerName, "true", appenderRefs.toArray(new AppenderRef[]{}), null, config, null); customParentLoggers.put(loggerConfig, loggerName); } else { //manage a existing logger loggerConfig = config.getLoggerConfig(loggerName); //remove appenderRef which are not selected anymore List currentRefs = loggerConfig.getAppenderRefs(); Iterator refIter = currentRefs.iterator(); while (refIter.hasNext()) { AppenderRef ref = (AppenderRef) refIter.next(); if (!appenderNames.contains(ref.getRef())) { refIter.remove(); loggerConfig.removeAppender(ref.getRef()); } else { appenderRefsToAdd.remove(ref.getRef()); } } //add appendersRefs if (!CollectionUtils.isEmpty(appenderRefsToAdd)) { appenderRefsToAdd.forEach(appenderRefName -> { loggerConfig.getAppenderRefs().add(AppenderRef.createAppenderRef(appenderRefName, null, null)); }); } } config.getAppenders().entrySet().stream() .filter(entry -> appenderRefsToAdd.contains(entry.getKey())) .forEach(appenderEntry -> loggerConfig.addAppender(appenderEntry.getValue(), null, null)); config.addLogger(loggerName, loggerConfig); ctx.updateLoggers(); } /** * removes all custom loggers (without parents) * * @throws IllegalArgumentException */ public void removeCustomLoggers() throws IllegalArgumentException { removeCustomLoggers(customLoggers); } /** * removes all custom parent loggers * @throws IllegalArgumentException */ public void removeCustomParentLoggers() throws IllegalArgumentException { removeCustomLoggers(customParentLoggers); } public void removeCustomLoggers(Map customMap) throws IllegalArgumentException { if (customMap.isEmpty()) { return; } LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); for (Entry entry : customMap.entrySet()) { config.removeLogger(entry.getValue()); } ctx.updateLoggers(); customMap.clear(); } /** * returns the default log message pattern (used in template) * @return * @since 1.1.1 */ public String getDefaultPattern() { return DEFAULT_PATTERN; } /** * creates the custom output steam appender and returns the name * * @param name * @param pattern * @param encoding * @param loggerNames * @param levelStr * @return * @since 1.1.1 */ public String createOutputStreamAppender(String name, String pattern, String encoding, Collection loggerNames, String levelStr, boolean recursive, boolean overrideLogLevel) { Level level = getLevel(levelStr); String encodingToUse = StringUtils.isEmpty(encoding) ? "UTF-8" : encoding; PatternLayout layout = PatternLayout.newBuilder() .withPattern(StringUtils.isEmpty(pattern) ? DEFAULT_PATTERN : pattern) .withCharset(Charset.forName(encodingToUse)) .build(); String appenderName = StringUtils.isEmpty(name) ? UUID.randomUUID().toString() : name; AdminToolLog4j2OutputStream baos = new AdminToolLog4j2OutputStream(4096, encodingToUse); outputStreams.put(appenderName, baos); OutputStreamAppender appender = OutputStreamAppender.createAppender(layout, null, baos, appenderName, false, true); appender.start(); final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); final Configuration config = ctx.getConfiguration(); config.addAppender(appender); Collection parentLoggerNames = getParentLoggerNames(); Map configs = getRecursiveLoggerConfigs(loggerNames, recursive, config); configs.entrySet().forEach(configEntry ->{ configEntry.getValue().addAppender(appender, level, null); if (overrideLogLevel) { baos.addOriginalLevel(configEntry.getKey(), configEntry.getValue().getLevel()); changeLogger(configEntry.getKey(), level, parentLoggerNames.contains(configEntry.getKey())); } }); ctx.updateLoggers(); return appenderName; } /** * * @param loggerNames * @param recursive * @param config * @return */ private Map getRecursiveLoggerConfigs(Collection loggerNames, boolean recursive, final Configuration config) { Map configs = new HashMap<>(); for (String configuredLoggerName : getAllLoggerNames()) { for (String loggerNameToApply : loggerNames) { boolean apply = (recursive && configuredLoggerName.startsWith(loggerNameToApply)) || (!recursive && configuredLoggerName.equalsIgnoreCase(loggerNameToApply)); if (apply) { configs.put(configuredLoggerName, config.getLoggerConfig(configuredLoggerName)); } } } return configs; } /** * returns the log messages from custom appenders output stream * * @param appenderName * @param encoding * @return * @throws UnsupportedEncodingException * @since 1.1.1 */ public String getStringOutput(String appenderName, String encoding) throws UnsupportedEncodingException { AdminToolLog4j2OutputStream baos = outputStreams.get(appenderName); String output = ""; if (null != baos) { output = baos.getAndReset(encoding); } return output.trim().isEmpty() ? null : output; } /** * closes output stream and removes appender from loggers * @param appenderName * @throws IOException * @since 1.1.1 */ public void closeOutputStreamAppender(String appenderName) throws IOException { if (null == appenderName) { return; } final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); final Configuration config = ctx.getConfiguration(); AdminToolLog4j2OutputStream baos = outputStreams.get(appenderName); if (null != config && null != config.getAppenders()) { OutputStreamAppender appender = config.getAppender(appenderName); if (null != appender) { appender.stop(); Collection parentLoggerNames = getParentLoggerNames(); for (String configuredLoggerName : getAllLoggerNames()) { LoggerConfig loggerConfig = config.getLoggerConfig(configuredLoggerName); loggerConfig.removeAppender(appender.getName()); if (null != baos.getOriginalLevel(configuredLoggerName)) { changeLogger(configuredLoggerName, baos.getOriginalLevel(configuredLoggerName), parentLoggerNames.contains(configuredLoggerName)); } } //unsure about, if removing the appender from logger config if it gets also removed from logger instance too... removeAppender(appender, getParentLoggers()); removeAppender(appender, getLoggers()); appender.getManager().getByteBuffer().clear(); ctx.updateLoggers(); } } if (null != baos) { try { baos.close(); baos.clearOriginalLevels(); } catch (Exception ignore) { } finally { outputStreams.remove(appenderName); } } } private void removeAppender(Appender appender, Collection appenders) { for (Logger logger : appenders) { logger.removeAppender(appender); } } public Map getAppenders() { final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); final Configuration config = ctx.getConfiguration(); return config.getAppenders(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy