How to get a list of all the appender names which have been set in a List in Java while using logback.
The following code will gather all appenders in the current LoggerContext:
private Map<String, Appender<ILoggingEvent>> getAppendersMap() {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Map<String, Appender<ILoggingEvent>> appendersMap = new HashMap<>();
for (Logger logger : loggerContext.getLoggerList()) {
Iterator<Appender<ILoggingEvent>> appenderIterator = logger.iteratorForAppenders();
while (appenderIterator.hasNext()) {
Appender<ILoggingEvent> appender = appenderIterator.next();
if (!appendersMap.containsKey(appender.getName())) {
appendersMap.put(appender.getName(), appender);
}
}
}
return appendersMap;
}
You can then list the names like so:
Map<String, Appender<ILoggingEvent>> appendersMap = getAppendersMap();
for (String key : appendersMap.keySet()) {
logger.info("appender name = {}", key);
}
Thanks #glytching.
I found a shorter answer:
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
for (ch.qos.logback.classic.Logger logger : context.getLoggerList()) {
for (Iterator<Appender<ILoggingEvent>> index = logger.iteratorForAppenders(); index.hasNext();) {
Appender<ILoggingEvent> appender = index.next();
}
}
Related
I'm using log4j2.
I want to create a RollingFileAppender which rotates the log file on a daily basis.
The name of the logfile is unknown until the application has started (the logfile name is assembled from the application config).
That is why I need to add a RollingFileAppender at runtime.
I have the following code:
public static final ConfigurationBuilder<BuiltConfiguration> BUILDER = ConfigurationBuilderFactory.newConfigurationBuilder
public void initFileLoggerWithFilePattern(final String pattern) {
final LoggerComponentBuilder logger = BUILDER.newLogger("FileLogger", Level.DEBUG);
final AppenderComponentBuilder appender = createFileAppenderWithFilePattern(pattern);
BUILDER.add(appender);
logger.add(BUILDER.newAppenderRef("RollingFileAppender"));
BUILDER.add(logger);
Configurator.initialize(BUILDER.build());
}
public AppenderComponentBuilder createFileAppenderWithFilePattern(final String pattern) {
final AppenderComponentBuilder acb = BUILDER.newAppender("RollingFileAppender", "RollingFile");
acb.addAttribute("fileName", pattern);
acb.addAttribute("filePattern", pattern);
acb.addComponent(createPatternLayout());
acb.addComponent(createTimeBasedTriggeringPolicy());
return acb;
}
public LayoutComponentBuilder createPatternLayout() {
final LayoutComponentBuilder lcb = BUILDER.newLayout("PatternLayout");
lcb.addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss.SSS}{GMT}Z %m");
return lcb;
}
public ComponentBuilder createTimeBasedTriggeringPolicy() {
final ComponentBuilder policies = BUILDER.newComponent("Policies");
final ComponentBuilder policy = BUILDER.newComponent("TimeBasedTriggeringPolicy");
policies.addComponent(policy);
return policies;
}
The problem is that this code changes absolutely nothing. No appender as well as no Logger is being added to the configuration. The "FileLogger" that was created programmatically is not available.
I used this code to print the loggers and Appenders after executing the code above.
private void printLog4jConfig() {
final LoggerContext context = (LoggerContext) LogManager.getContext(false);
final Configuration config = context.getConfiguration();
// Print appenders
for(Appender app : config.getAppenders().values()) {
System.out.println(app.getName());
}
// Print Loggers and their Appenders
for(LoggerConfig lc : config.getLoggers().values()) {
System.out.println(lc);
for(Appender app : lc.getAppenders().values()) {
System.out.println(" " + app);
}
}
}
Output:
Appenders
-------------
STDOUT
Loggers
-------------
root
STDOUT
Console
STDOUT
My Question:
What is wrong with my code? Why is my Appender as well as my Logger not added? Respectively why is the configuration not being refreshed / updated ?
How can I add a RollingFileAppender as well as a logger to the log4j2 configuration during runtime?
I found the solution to my problem.
Instead using
Configurator.initialize(BUILDER.build())
I had to use
Configurator.reconfigure(BUILDER.build());
This reloaded the configuration and i was able to see and use my appender and logger.
i have a question regarding log4j2 with version 2.9.
Basically I want to do the same as described here (log4j), only with 2.9:
Sample log4j v1.x
I need a logger that can be called in any method in the class. This is to collect all subsequent logs recursively from a certain starting point. The collection should be able to be read out in any form later on.
Logger logger = LogManager.getLogger();
public void meth1(){
StringWriter/ List/ String or whatever
logger.add(Collector);
logger.info("Start");
this.meth2();
this.meht3();
logger.info("Stop");
=> do something with the collected logs
}
public void meht2(){
logger.info("meth2: add Collection");
}
public void meth3(){
logger.info("meth3: add Collection");
}
public void meht4(){
logger.info("foo");
}
as soon as the end is set in a form, the following logs should be included in the collection:
Start
meth2: add Collection
meth3: add Collection
Stop
thanks for your help
After a while, I came up with the following solution which works for me using Log4J 2.8. I've added some comments to the code to explain the different steps necessary.
It's important that the logger is requested with the same name (line marked with /* 1 */ as for which the configuration is stored (line marked with /* 2 */. This implies, that the class name cannot be used to get the logger, or that MyClass.class.getName() should be used at /*2*/.
public class LoggingTest {
public static void main(String[] args) {
// define the logger, could also be static in class
final String loggerName = "myCollectingLogger";
Logger logger = LogManager.getLogger(loggerName); /* 1 */
// the log message collector
StringWriter writer = new StringWriter();
// start adapting the logger configuration
LoggerContext ctx = LoggerContext.getContext(false);
Configuration config = ctx.getConfiguration();
// create our appender
PatternLayout layout = PatternLayout.newBuilder().withPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} %level [%t] [%c] [%M] [%l] - %msg%n").build();
WriterAppender writerAppender = WriterAppender.newBuilder().setName("writerAppender").setTarget(writer).setLayout(layout).build();
// add the appender to a LoggerConfig
AppenderRef ref = AppenderRef.createAppenderRef("writerAppender", null, null);
AppenderRef[] refs = new AppenderRef[] { ref };
LoggerConfig loggerConfig = LoggerConfig.createLogger(false, Level.INFO, "example", null, refs, null, config, null);
loggerConfig.addAppender(writerAppender, null, null);
// enable the LoggerConfig in the LoggerContext
config.addLogger(loggerName, loggerConfig); /* 2 */
ctx.updateLoggers();
// use the logger:
logger.info("Start");
logger.warn("foo bar");
logger.error("relax, it's just a test");
logger.info("Stop");
System.out.println("--- the collected log messages: ---");
System.out.println(writer.toString());
}
}
thanks, I've adjusted it to my needs:
public static StringWriter createStringWriter(String classname){
StringWriter stringWriter = new StringWriter();
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
PatternLayout layout = PatternLayout.newBuilder()
.withPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} %level [%t] [%c] [%M] [%l] - %msg%n").build();
WriterAppender writerAppender = WriterAppender.newBuilder()
.setName(classname + "writeLogger")
.setTarget(stringWriter)
.setLayout(layout)
.build();
writerAppender.start();
config.addAppender(writerAppender);
LoggerConfig loggerConfig = config.getLoggerConfig(classname);
loggerConfig.addAppender(writerAppender, null, null);
ctx.updateLoggers();
return stringWriter;
}
public static void removeStringWriter(String classname){
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
LoggerConfig loggerConfig = config.getLoggerConfig(classname);
loggerConfig.removeAppender(classname + "writeLogger");
ctx.updateLoggers();
}
I'm trying to configure and set up Log4j2 only through using ConfigurationFactory and this reference. The code I'm using is as follows:
public class LoggingConfiguration {
public static final String PATTERN_LAYOUT = "[%d] [%t] [%-5level] - %msg (%logger{1}:%L) %n%throwable";
public static final String LOG_FILE_NAME = "app.log";
public static final String LOG_FILE_NAME_PATTERN = LOG_FILE_NAME + "-yyyy.MM.dd";
static {
ConfigurationFactory.setConfigurationFactory(new Log4j2ConfigurationFactory());
}
/**
* Just to make JVM visit this class to initialize the static parts.
*/
public static void configure() {
}
#Plugin(category = "ConfigurationFactory", name = "Log4j2ConfigurationFactory")
#Order(0)
public static class Log4j2ConfigurationFactory extends ConfigurationFactory {
#Override
protected String[] getSupportedTypes() {
return null;
}
#Override
public Configuration getConfiguration(ConfigurationSource source) {
return new Log4j2Configuration();
}
#Override
public Configuration getConfiguration(String name, URI configLocation) {
return new Log4j2Configuration();
}
}
private static class Log4j2Configuration extends DefaultConfiguration {
public Log4j2Configuration() {
setName("app-log4j2");
String root = System.getProperty("APP_ROOT", "/tmp");
if (!root.endsWith("/")) {
root += "/";
}
// MARKER
Layout<? extends Serializable> layout = PatternLayout.createLayout(PATTERN_LAYOUT, null, null, null, null);
String oneDay = TimeUnit.DAYS.toMillis(1) + "";
String oneMB = (1024 * 1024) + "";
final TimeBasedTriggeringPolicy timeBasedTriggeringPolicy = TimeBasedTriggeringPolicy.createPolicy(oneDay,
"true");
final SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy = SizeBasedTriggeringPolicy.createPolicy(oneMB);
final CompositeTriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(timeBasedTriggeringPolicy,
sizeBasedTriggeringPolicy);
final DefaultRolloverStrategy strategy = DefaultRolloverStrategy.createStrategy("7", "1", null,
Deflater.DEFAULT_COMPRESSION + "", this);
Appender appender = RollingFileAppender.createAppender(root + LOG_FILE_NAME, LOG_FILE_NAME_PATTERN, "true",
"app-log-file-appender", "true", "true", policy, strategy, layout, null, null, null, null, null);
addAppender(appender);
getRootLogger().addAppender(appender, Level.INFO, null);
}
}
}
Note that
it extends BaseConfiguration that already configures console by default
it tries to add a rolling file appender to the root logger
I get the following exception:
Exception in thread "main" java.lang.IllegalStateException: Pattern does not contain a date
at org.apache.logging.log4j.core.appender.rolling.PatternProcessor.getNextTime(PatternProcessor.java:91)
at org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy.initialize(TimeBasedTriggeringPolicy.java:49)
at org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy.initialize(CompositeTriggeringPolicy.java:43)
at org.apache.logging.log4j.core.appender.rolling.RollingFileManager.<init>(RollingFileManager.java:58)
at org.apache.logging.log4j.core.appender.rolling.RollingFileManager$RollingFileManagerFactory.createManager(RollingFileManager.java:297)
at org.apache.logging.log4j.core.appender.rolling.RollingFileManager$RollingFileManagerFactory.createManager(RollingFileManager.java:267)
at org.apache.logging.log4j.core.appender.AbstractManager.getManager(AbstractManager.java:71)
at org.apache.logging.log4j.core.appender.OutputStreamManager.getManager(OutputStreamManager.java:65)
at org.apache.logging.log4j.core.appender.rolling.RollingFileManager.getFileManager(RollingFileManager.java:78)
at org.apache.logging.log4j.core.appender.RollingFileAppender.createAppender(RollingFileAppender.java:175)
at com.narmnevis.papyrus.LoggingConfiguration$Log4j2Configuration.<init>(LoggingConfiguration.java:79)
at com.narmnevis.papyrus.LoggingConfiguration$Log4j2ConfigurationFactory.getConfiguration(LoggingConfiguration.java:55)
at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:377)
at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:149)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:85)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:34)
at org.apache.logging.log4j.LogManager.getContext(LogManager.java:200)
at org.slf4j.helpers.Log4jLoggerFactory$PrivateManager.getContext(Log4jLoggerFactory.java:104)
at org.slf4j.helpers.Log4jLoggerFactory.getContext(Log4jLoggerFactory.java:90)
at org.slf4j.helpers.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:46)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:270)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:281)
at com.narmnevis.papyrus.Main.<init>(Main.java:12)
at com.narmnevis.papyrus.Main.main(Main.java:21)
If I comment out the code after MARKER in above code, it works but it seems that I'm missing something to configure a rolling file appender. What should I do to fix this?
In log4j 2.x you have to specify the date format in this way
public static final String LOG_FILE_NAME_PATTERN = LOG_FILE_NAME + "-%d{dd-MM-yyy}";
% marks the beginning of a format
d means that it is a date format (you can also use date)
within the curly braces {} you define the formatter's options. In this case the date format. You can use everything that a SimpleDateFormat would accept.
In addition you can also use:
%d{ABSOLUTE} -> HH:mm:ss,SSS
%d{COMPACT} -> yyyyMMddHHmmssSSS
%d{DATE} -> dd MMM yyyy HH:mm:ss,SSS
%d{ISO8601_BASIC} -> yyyyMMdd HHmmss,SSS
%d{ISO8601} -> yyyy-MM-dd HH:mm:ss,SSS
Note: This information is based on log4j 2.0-beta9 (the current release). Since it is a beta version it might change slightly.
Where log4j2.xml should be placed for use in applet? Can it log both to Java Console and to files on user computer?
I placed in applet resources conf/log4j2.xml and read it from applet. Applet loads it incorrectly, so i fix the fields from the applet code:
public static Logger getLogger(Class className) {
//get logger configuration
LoggerContext loggerContext = Configurator.initialize("client", className.getClassLoader(), className.getClassLoader().getResource("conf/log4j2.xml").getFile());
Configuration configuration = loggerContext.getConfiguration();
//set root logger to desired level
LoggerConfig loggerConfig = configuration.getLoggerConfig("");
loggerConfig.setLevel(Level.INFO);
//obtain appender
Appender appender = obtainAppender(configuration);
//get logger for required class
org.apache.logging.log4j.core.Logger loggerForClass = loggerContext.getLogger(className.getName());
//associate logger for required class with just created appender
configuration.addLoggerAppender(loggerForClass, appender);
return loggerForClass;
}
private static Appender obtainAppender(Configuration configuration) {
//create appender
TriggeringPolicy[] triggeringPolicies = {OnStartupTriggeringPolicy.createPolicy(), TimeBasedTriggeringPolicy.createPolicy("5", "true"), SizeBasedTriggeringPolicy.createPolicy("5 MB")};
TriggeringPolicy triggeringPolicy = CompositeTriggeringPolicy.createPolicy(triggeringPolicies);
return RollingFileAppender.createAppender(CLIENT_LOG_PATH + FileUtils.FILE_SEPARATOR + "my_client.log",
CLIENT_LOG_PATH + FileUtils.FILE_SEPARATOR + "/$${date:yyyy-MM}/my_client-%d{MM-dd-yyyy-HH-mm}-%i.log",
"", APPLET_APPENDER,
"true", "true",
triggeringPolicy, null,
PatternLayout.createLayout("%d{dd/MM/yyyy HH:mm:ss} %-5p [%t] [%c{1}] %m%n", configuration, null, "UTF-8"),
null, "true", configuration);
}
I want to use default SLF4J + Logback configuration except setting org.springframework.data.document.mongodb logging level to DEBUG.
How can I do it with Java code?
I'm not using XML, and this decision made at runtime.
The following works for me but generally this is not a good idea. Your code will depend on Logback (you can't choose another logging framework behind SLF4J).
final org.slf4j.Logger logger =
org.slf4j.LoggerFactory.getLogger("test.package");
if (!(logger instanceof ch.qos.logback.classic.Logger)) {
return;
}
ch.qos.logback.classic.Logger logbackLogger =
(ch.qos.logback.classic.Logger) logger;
logbackLogger.setLevel(ch.qos.logback.classic.Level.TRACE);
logger.trace("some log");
Depending to logback-classic is not a good idea as #palacsint stated. You can achieve what you want using Java's Reflection API. Note that this approach puts some overhead to your program because of use of reflection.
Usage:
LogbackUtils.setLogLevel("com.stackoverflow.sample", "DEBUG")
Code:
public static final String LOGBACK_CLASSIC = "ch.qos.logback.classic";
public static final String LOGBACK_CLASSIC_LOGGER = "ch.qos.logback.classic.Logger";
public static final String LOGBACK_CLASSIC_LEVEL = "ch.qos.logback.classic.Level";
private static final Logger logger = LoggerFactory.getLogger(LogbackUtils.class);
/**
* Dynamically sets the logback log level for the given class to the specified level.
*
* #param loggerName Name of the logger to set its log level. If blank, root logger will be used.
* #param logLevel One of the supported log levels: TRACE, DEBUG, INFO, WARN, ERROR, FATAL,
* OFF. {#code null} value is considered as 'OFF'.
*/
public static boolean setLogLevel(String loggerName, String logLevel)
{
String logLevelUpper = (logLevel == null) ? "OFF" : logLevel.toUpperCase();
try
{
Package logbackPackage = Package.getPackage(LOGBACK_CLASSIC);
if (logbackPackage == null)
{
logger.info("Logback is not in the classpath!");
return false;
}
// Use ROOT logger if given logger name is blank.
if ((loggerName == null) || loggerName.trim().isEmpty())
{
loggerName = (String) getFieldValue(LOGBACK_CLASSIC_LOGGER, "ROOT_LOGGER_NAME");
}
// Obtain logger by the name
Logger loggerObtained = LoggerFactory.getLogger(loggerName);
if (loggerObtained == null)
{
// I don't know if this case occurs
logger.warn("No logger for the name: {}", loggerName);
return false;
}
Object logLevelObj = getFieldValue(LOGBACK_CLASSIC_LEVEL, logLevelUpper);
if (logLevelObj == null)
{
logger.warn("No such log level: {}", logLevelUpper);
return false;
}
Class<?>[] paramTypes = { logLevelObj.getClass() };
Object[] params = { logLevelObj };
Class<?> clz = Class.forName(LOGBACK_CLASSIC_LOGGER);
Method method = clz.getMethod("setLevel", paramTypes);
method.invoke(loggerObtained, params);
logger.debug("Log level set to {} for the logger '{}'", logLevelUpper, loggerName);
return true;
}
catch (Exception e)
{
logger.warn("Couldn't set log level to {} for the logger '{}'", logLevelUpper, loggerName, e);
return false;
}
}
// getFieldValue() method omitted for bravity from here,
// but available at GitHub link below.
Full code including tests: Github Gist.