Log4j Logging for custom Log levels in Java - java

I have written class for custom logging level i.e. INIT
import org.apache.log4j.Logger;
import org.apache.log4j.Level;
public class InitLoggingLevel extends Level {
public static final String INITLOGGING_LEVEL = "INITLOGGING";
public static final Level INIT_LOGGING = new InitLoggingLevel(
DEBUG_INT - 4, INITLOGGING_LEVEL, 7);
protected InitLoggingLevel(int level, String levelStr, int syslogEquivalent){
super(level, levelStr, syslogEquivalent);
}
}
Now what are changes I need to make in log4j.properties and how I am going to use this INIT logging level in My Java class?

You have to:
Override the method toLevel(String logArgument) like this:
public static Level toLevel(String logArgument) {
if (logArgument != null && logArgument.toUpperCase().equals("INITLOGGING")) {
return INIT_LOGGING;
}
return (Level) toLevel(logArgument);
}
Write a configuration line in the log4j.properties like this:
log4j.logger.[MY_CATEGORY]=INITLOGGING#your-package.InitLoggingLevel
That's all!
P.S.: The '#' syntax is described in org.apache.log4j.helpers.OptionConverter source class.

You can try this
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %c:%L - %m%n
log4j.category.YOUR_PACKAGE=INFO,YOUR_PACKAGE.InitLoggingLevel
or can view log4j category
or http://veerasundar.com/blog/2009/08/log4j-tutorial-how-to-send-log-messages-to-different-log-files/

It can be like this :
<category name="xxx" additivity="false">
<priority class="your class" value="info"/>
<appender-ref ref="xxxlog"/>
</category>

Don't know if you already figured it out, but I found the following article, which helped me.
https://www.owasp.org/index.php/How_to_add_a_security_log_level_in_log4j
The new level is printed with the expected log tag, but log4j will not recognize the new level in my property file. I will see, what I can do about that.

Related

create a custom log4j2 rolling file appender

I want to create a custom log4j2 rolling file appender. I need to create this custom appender because I want to wrap the file name with current thread name. We are trying to migrate log4j 1.x to recent log4j2 version and previously we had used DailyRollingFileAppender to log all activities of our application.
please find the below code.
Here we are trying to append the log to a file on daily basis with help of DailyRollingFileAppender based on threadName.
Since DailyRollingFileAppender is deprecated in recent version -so, how to create custom rolling file appender with incorporating our thread based logic.?
Find the below log4j.properties file
log4j.logger.***=INFO, FileLogger
# log4j.appender.FileLogger=org.apache.log4j.DailyRollingFileAppender
# Custom Appendar which will redirect the logs based on thread names configured using
# log4j.appender.FileLogger.threadNameMapping property below
log4j.appender.FileLogger=********.framework.log4j.appender.ThreadNamePatternAppender
log4j.appender.FileLogger.DatePattern='.'yyyy-MM-dd
log4j.appender.FileLogger.file=/logs/fileName.log
log4j.appender.FileLogger.layout=org.apache.log4j.PatternLayout
log4j.appender.FileLogger.layout.ConversionPattern=%d [%-5p] [%t] [%c{1}] [%M] - %m%n
# Custom property to hold mapping between thread names and log file for plug-in
# Beware - ThreadNamePatternAppender class inherits DailyRollingFileAppender hence it will not work for any other type of appender
# This can be distuingished using - ThreadName1>ThreadName1.log|ThreadName2>ThreadName2.log|.....|ThreadNameN>ThreadNameN.log
# Note - If there is no mapping for a particular thread then logs will be written to default log file
log4j.appender.FileLogger.threadNameMapping=********/logs/fileName-fileName.log
Thanks!
import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
public class ThreadNamePatternAppender extends DailyRollingFileAppender {
private Map<String, DailyRollingFileAppender> threadBasedSubAppenders = new HashMap<String, DailyRollingFileAppender>();
private String threadNameMapping;
public String getThreadNameMapping() {
return threadNameMapping;
}
public void setThreadNameMapping(String threadNameMapping) {
this.threadNameMapping = threadNameMapping;
}
#Override
public void activateOptions() {
super.activateOptions();
if (threadNameMapping != null && threadNameMapping.trim().length() > 0) {
DailyRollingFileAppender tempAppender;
String[] threadNames = threadNameMapping.split("\\|");
for (String threadName : threadNames) {
if (threadName != null && threadName.length() > 0) {
try {
LogLog.debug(String.format("Creating new appender for thread %s", threadName));
tempAppender = new DailyRollingFileAppender(getLayout(), threadName.split(">")[1],
getDatePattern());
threadBasedSubAppenders.put(threadName.split(">")[0], tempAppender);
} catch (Exception ex) {
LogLog.error("Failed to create appender", ex);
}
}
}
}
}
#Override
public void append(LoggingEvent event) {
String threadName = event.getThreadName().split(" ")[0];
if (threadBasedSubAppenders.containsKey(threadName)) {
threadBasedSubAppenders.get(threadName).append(event);
} else {
super.append(event);
}
}
#Override
public synchronized void close() {
LogLog.debug("Calling Close on ThreadNamePatternAppender" + getName());
for (DailyRollingFileAppender appender : threadBasedSubAppenders.values()) {
appender.close();
}
this.closed = true;
}
}
The RollingFileAppender in Log4j 2.x is final, so you can not extend it. However you can obtain the functionality of your custom Log4j 1.x appender using:
A RoutingAppender, which can create appenders on demand,
Multiple RollingFileAppender that will write and rotate your files,
The EventLookup to retrieve the current thread name.
For a simple logfile-per-thread appender you can use:
<Routing name="Routing">
<Routes pattern="$${event:ThreadName}">
<Route>
<RollingFile name="Rolling-${event:ThreadName}"
fileName="logs/thread-${event:ThreadName}.log"
filePattern="logs/thread-${event:ThreadName}.log.%d{yyyy-MM-dd}">
<PatternLayout pattern="%d [%-5p] [%t] [%c{1}] [%M] - %m%n" />
<TimeBasedTriggeringPolicy />
</RollingFile>
</Route>
</Routes>
</Routing>
For a more complex configuration both the <Routing> appender and the <Routes> can contain a <Script> (cf. documentation):
the script in the <Routing> appender can initialize the staticVariables map and return a default route,
the script in the <Routes> component chooses the appropriate route based on staticVariables and the logging event.

Setting root log level to a custom not working

My requirement is to define a log level which is in between INFO and DEBUG. In other words INFO plus something but less than debug. The level needs to be enabled in the root level of log4j.xml file in the tomcat configuration directory. I have done the following :
Set the root log level to custom one STAT like :
<root>
<priority value="STAT"/>
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
<appender-ref ref="LOGSTASH"/>
</root>
Defined a log level class :
package com.log4j.logger;
import org.apache.log4j.Level;
/**
* #author shantha
*
*/
#SuppressWarnings("serial")
public class StatLogLevel extends Level {
public static final int STAT_INT = 15000;
public static final Level STAT = new StatLogLevel(STAT_INT, "STAT", 6);
public StatLogLevel(int level, String levelStr, int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
}
public static Level toLevel(String logArgument) {
if (logArgument != null && logArgument.toUpperCase().equals("STAT")) {
return STAT;
}
return (Level) toLevel(logArgument);
}
public static Level toLevel(int val) {
if (val == STAT_INT) {
return STAT;
}
return (Level) toLevel(val, Level.DEBUG);
}
public static Level toLevel(int val, Level defaultLevel) {
if (val == STAT_INT) {
return STAT;
}
return Level.toLevel(val, defaultLevel);
}
public static Level toLevel(String logArgument, Level defaultLevel) {
if (logArgument != null && logArgument.toUpperCase().equals("STAT")) {
return STAT;
}
return Level.toLevel(logArgument, defaultLevel);
}
}
In the code, logs are sent using the code :
logger.log(StatLogLevel.STAT, "Custom log message");
And also added an entry to the log4j.xml :
<category name="stats">
<priority value="STAT" class="com.log4j.logger.StatLogLevel" />
<appender-ref ref="CONSOLE" />
</category>
The result that I am getting is :
...
log4j: Level value for root is [STAT].
log4j: root level set to DEBUG
log4j: Adding appender named [CONSOLE] to category [root].
...
and all the debug messages are printed.
Appreciate if someone could help on this.
The environment is java 1.8, tomcat 8.5.15 and log4j-1.2.17.jar.

Logback file per logger dynamicaly

I have "special" requirement on loggin - I need every logger in separate file.
Java
Logger log1 = LoggerFactory.getLogger("dynamic.log1");
Logger log2 = LoggerFactory.getLogger("dynamic.log2");
//...
Then I want logback any output from log1 to be written to file log1.log and so on. Is it possible to "dynamicaly" create new appender's like that with logback?
Can some other logging framework be used to solve this use-case?
I could configure appenders manualy but this is what I want to avoid. Like whenever I add dynamic logger, new appender/file is accordingly created.
EDIT:
I implemented custom discriminator:
public class LoggerBasedDiscriminator extends AbstractDiscriminator<ILoggingEvent> {
private static final String LOGGER_NAME = "loggerName";
#Override
public String getDiscriminatingValue(ILoggingEvent e) {
return e.getLoggerName();
}
#Override
public String getKey() {
return LOGGER_NAME;
}
}
And then my appender config looks like this:
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator class="cz.svobol.logging.LoggerBasedDiscriminator">
<key>loggerName</key>
<defaultValue>root</defaultValue>
</discriminator>
<sift>
<appender name="FILE-${loggerName}" class="ch.qos.logback.core.FileAppender">
<file>${loggerName}.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
</sift>
</appender>
You can use a SiftingAppender.
This way you have one appender that can divide the log into different files dynamically.

How to Change log level for particular users/threads at runtime

I'm using slf4j with either log4j 2.0 or logback as the implementation. For example, my servlet has a logger with level ERROR, and my server spawns 100 threads of the servlet. I will get a list of special users at runtime. When I detect some of the special users connected in. I want to change the log level for those special users/threads to DEBUG, and leave other threads' log level unaffected (still ERROR).
I know the TurboFilter in logback and DynamicThresholdFilter in log4j 2.0, but since I will only get the special users list at runtime, I cannot use them.
Here is my application:
package com.example.logging;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServlet;
import org.slf4j.*;
public class App extends HttpServlet {
private final Logger Logger = LoggerFactory.getLogger(App.class);
Map<String, String> map = new HashMap<String, String>();
public App() {
map.put("user1", "DEBUG");
map.put("user2", "DEBUG");
map.put("user3", "ERROR");
}
public void writeToLogFile(String userName) {
if (map.containsKey(userName)) {
// do something so that I can change the logger to the corresponding log level
}
Logger.error(userName + " error message");
// the logger is of level ERROR, so by default, this log event will not happen
// but I want it to happen for special users
if (Logger.isDebugEnabled()) {
Logger.debug(userName + " debug message");
}
}
}
Here is my log configuration in log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%-5level %class{36} %M %msg%xEx%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="com.example.logging.App" level="ERROR" additivity="false">
<AppenderRef ref="Console" />
</Logger>
<Root level="DEBUG">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
If I call the methods below:
App myApp = new App();
// assume the below 4 methods are called concurrently
myApp.writeToLogFile("user1");
myApp.writeToLogFile("user2");
myApp.writeToLogFile("user3");
myApp.writeToLogFile("user4");
The expected output should be:
ERROR com.example.logging.App writeToLogFile - user1 error message
DEBUG com.example.logging.App writeToLogFile - user1 debug message
ERROR com.example.logging.App writeToLogFile - user2 error message
DEBUG com.example.logging.App writeToLogFile - user2 debug message
ERROR com.example.logging.App writeToLogFile - user3 error message
ERROR com.example.logging.App writeToLogFile - user4 error message
I've met the same problem, and I end up using my own filter by making changes to DynamicThresholdFilter
Changes to the application:
package com.example.logging;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServlet;
import org.slf4j.*;
public class App extends HttpServlet {
private final Logger Logger = LoggerFactory.getLogger(App.class);
Map<String, String> map = new HashMap<String, String>();
public App() {
map.put("user1", "Debug");
map.put("user2", "Debug");
map.put("user3", "Error");
}
public void writeToLogFile(String userName) {
// if the user is in the map, we put it into ThreadConext for filtering
if (map.containsKey(userName)) {
MDC.put("level", map.get(userName));
}
Logger.error(userName + " error message");
if (Logger.isDebugEnabled()) {
Logger.debug(userName + " debug message");
}
// remember to remove it
MDC.remove("level");
}
}
Here is the newly defined filter based on DynamicThresholdFilter, let's call it DynamicThresholdUserFilter, you can compare it to the source code of DynamicThresholdFilter
package com.example.logging.log4j2.plugin;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message;
/**
* Compare against a log level that is associated with an MDC value.
*/
#Plugin(name = "DynamicThresholdUserFilter", category = "Core", elementType = "filter", printObject = true)
public final class DynamicThresholdUserFilter extends AbstractFilter {
private Level defaultThreshold = Level.ERROR;
private final String key;
private DynamicThresholdUserFilter(final String key, final Level defaultLevel,
final Result onMatch, final Result onMismatch) {
super(onMatch, onMismatch);
if (key == null) {
throw new NullPointerException("key cannot be null");
}
this.key = key;
this.defaultThreshold = defaultLevel;
}
public String getKey() {
return this.key;
}
#Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object... params) {
return filter(level);
}
#Override
public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
final Throwable t) {
return filter(level);
}
#Override
public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
final Throwable t) {
return filter(level);
}
#Override
public Result filter(final LogEvent event) {
return filter(event.getLevel());
}
/* biggest change here */
private Result filter(final Level level) {
final String value = ThreadContext.get(key);
if (value != null) {
Level ctxLevel = Level.toLevel(value);
if (ctxLevel == null) {
// in case the level is invalid
ctxLevel = defaultThreshold;
}
return level.isAtLeastAsSpecificAs(ctxLevel) ? onMatch : onMismatch;
}
return Result.NEUTRAL;
}
#Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("key=").append(key);
sb.append(", default=").append(defaultThreshold);
return sb.toString();
}
/**
* Create the DynamicThresholdFilter.
* #param key The name of the key to compare.
* #param pairs An array of value and Level pairs.
* #param levelName The default Level.
* #param match The action to perform if a match occurs.
* #param mismatch The action to perform if no match occurs.
* #return The DynamicThresholdFilter.
*/
#PluginFactory
public static DynamicThresholdUserFilter createFilter(
#PluginAttribute("key") final String key,
#PluginAttribute("defaultThreshold") final String levelName,
#PluginAttribute("onMatch") final String match,
#PluginAttribute("onMismatch") final String mismatch) {
final Result onMatch = Result.toResult(match);
final Result onMismatch = Result.toResult(mismatch);
final Level level = Level.toLevel(levelName, Level.ERROR);
return new DynamicThresholdUserFilter(key, level, onMatch, onMismatch);
}
}
Add the DynamicThresholdUserFilter and package name to your configuration file
<?xml version="1.0" encoding="UTF-8"?>
<!-- add the package name of the filter-->
<Configuration status="ERROR" packages="com.example.logging.plugin">
<!-- configuration of the new defined filter -->
<DynamicThresholdUserFilter key="level" defaultThreshold="ERROR" onMatch="ACCEPT" onMismatch="NEUTRAL" />
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%-5level %class{36} %M %msg%xEx%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="com.example.logging.App" level="ERROR" additivity="false">
<AppenderRef ref="Console" />
</Logger>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
The newly defined filter is pretty similar to DynamicThresholdFilter. The difference is DynamicThresholdFilter uses the predefined level in configuration file as the dynamic threshold, while this filter uses the level programmatically defined in the map.
While the already existing answer might work (haven't tried it personally), after intensive searching, I found a very easy and neat trick to do what you are requesting.
The DynamicThresholdFilter can be used with conditions to switch the log level at run time. This, combined with log4j2's ThreadContext, you can do quite nifty things.
You would have to populate a particular key in the ThreadContext at the beginning of a server call processing (somewhere in doFilter method of your HttpServlet class) based on your custom logic of user names. This would look something like:
ThreadContext.put("customLogLevel", "debug");
Then in your log4j2.xml file, you put this as a global filter, right below the root Configuration tag:
<DynamicThresholdFilter key="customLogLevel" onMatch="ACCEPT" onMismatch="NEUTRAL">
<KeyValuePair key="debug" value="DEBUG"/>
<KeyValuePair key="error" value="ERROR"/>
<KeyValuePair key="info" value="INFO"/>
</DynamicThresholdFilter>
Now based on the value of the key customLogLevel in the ThreadContext that you set at the beginning of a call, all the log calls in that thread will have log level corresponding to the matching KeyValuePair line. So in the example above, all log calls in the thread would have level as DEBUG.

How to change root logging level programmatically for logback

I have the following logback.xml file:
<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Now, upon the occurrence of a specific event, I want to programmatically change the level of the root logger from debug to error. I can't use variable substitution, it is mandatory that I do this within the code.
How can it be done ? Thanks.
Try this:
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
Logger root = (Logger)LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.INFO);
Note that you can also tell logback to periodically scan your config file like this:
<configuration scan="true" scanPeriod="30 seconds" >
...
</configuration>
using logback 1.1.3 I had to do the following (Scala code):
import ch.qos.logback.classic.Logger
import org.slf4j.LoggerFactory
...
val root: Logger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).asInstanceOf[Logger]
I assume you are using logback (from the configuration file).
From logback manual, I see
Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
Perhaps this can help you change the value?
As pointed out by others, you simply create mockAppender and then create a LoggingEvent instance which essentially listens to the logging event registered/happens inside mockAppender.
Here is how it looks like in test:
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
#RunWith(MockitoJUnitRunner.class)
public class TestLogEvent {
// your Logger
private Logger log = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// here we mock the appender
#Mock
private Appender<ILoggingEvent> mockAppender;
// Captor is generic-ised with ch.qos.logback.classic.spi.LoggingEvent
#Captor
private ArgumentCaptor<LoggingEvent> captorLoggingEvent;
/**
* set up the test, runs before each test
*/
#Before
public void setUp() {
log.addAppender(mockAppender);
}
/**
* Always have this teardown otherwise we can stuff up our expectations.
* Besides, it's good coding practise
*/
#After
public void teardown() {
log.detachAppender(mockAppender);
}
// Assuming this is your method
public void yourMethod() {
log.info("hello world");
}
#Test
public void testYourLoggingEvent() {
//invoke your method
yourMethod();
// now verify our logging interaction
// essentially appending the event to mockAppender
verify(mockAppender, times(1)).doAppend(captorLoggingEvent.capture());
// Having a generic captor means we don't need to cast
final LoggingEvent loggingEvent = captorLoggingEvent.getValue();
// verify that info log level is called
assertThat(loggingEvent.getLevel(), is(Level.INFO));
// Check the message being logged is correct
assertThat(loggingEvent.getFormattedMessage(), containsString("hello world"));
}
}
I think you can use MDC to change logging level programmatically. The code below is an example to change logging level on current thread. This approach does not create dependency to logback implementation (SLF4J API contains MDC).
<configuration>
<turboFilter class="ch.qos.logback.classic.turbo.DynamicThresholdFilter">
<Key>LOG_LEVEL</Key>
<DefaultThreshold>DEBUG</DefaultThreshold>
<MDCValueLevelPair>
<value>TRACE</value>
<level>TRACE</level>
</MDCValueLevelPair>
<MDCValueLevelPair>
<value>DEBUG</value>
<level>DEBUG</level>
</MDCValueLevelPair>
<MDCValueLevelPair>
<value>INFO</value>
<level>INFO</level>
</MDCValueLevelPair>
<MDCValueLevelPair>
<value>WARN</value>
<level>WARN</level>
</MDCValueLevelPair>
<MDCValueLevelPair>
<value>ERROR</value>
<level>ERROR</level>
</MDCValueLevelPair>
</turboFilter>
......
</configuration>
MDC.put("LOG_LEVEL", "INFO");
You may intercept logging configuration via the LogManager.getLogManager().updateConfiguration() method. Just check for configuration properties which contains .level suffix, and replace default value with Level.ALL value.
public class Application {
// Static initializer is executed when class is loaded by a class loader
static {
try {
java.util.logging.LogManager.getLogManager().updateConfiguration(propertyName -> {
// Check for the log level property
if (propertyName.contains(".level")) {
// Level = ALL => logs all messages
return (oldValue, newValue) -> java.util.logging.Level.ALL.getName();
} else {
// Identity mapper for other propeties
return (oldValue, newValue) -> newValue;
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
So I mostly agree with the top answer but found it to be slightly different in 2023. I found that the following works
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
final Logger logger = loggerContext.exists(org.slf4j.Logger.ROOT_LOGGER_NAME); // give it your logger name
final Level newLevel = Level.toLevel("ERROR", null); // give it your log level
logger.setLevel(newLevel);
The primary difference of note is instead of getLogger I had to use getILoggerFactory. To see additional related posts to this see Programmatically change log level in Log4j2 or if you want to be able to this per request see Change priority level in log4j per request
I seem to be having success doing
org.jboss.logmanager.Logger logger = org.jboss.logmanager.Logger.getLogger("");
logger.setLevel(java.util.logging.Level.ALL);
Then to get detailed logging from netty, the following has done it
org.slf4j.impl.SimpleLogger.setLevel(org.slf4j.impl.SimpleLogger.TRACE);
Here's a controller
#RestController
#RequestMapping("/loggers")
public class LoggerConfigController {
private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(PetController.class);
#GetMapping()
public List<LoggerDto> getAllLoggers() throws CoreException {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
List<Logger> loggers = loggerContext.getLoggerList();
List<LoggerDto> loggerDtos = new ArrayList<>();
for (Logger logger : loggers) {
if (Objects.isNull(logger.getLevel())) {
continue;
}
LoggerDto dto = new LoggerDto(logger.getName(), logger.getLevel().levelStr);
loggerDtos.add(dto);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("All loggers retrieved. Total of {} loggers found", loggerDtos.size());
}
return loggerDtos;
}
#PutMapping
public boolean updateLoggerLevel(
#RequestParam String name,
#RequestParam String level
)throws CoreException {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger logger = loggerContext.getLogger(name);
if (Objects.nonNull(logger) && StringUtils.isNotBlank(level)) {
switch (level) {
case "INFO":
logger.setLevel(Level.INFO);
LOGGER.info("Logger [{}] updated to [{}]", name, level);
break;
case "DEBUG":
logger.setLevel(Level.DEBUG);
LOGGER.info("Logger [{}] updated to [{}]", name, level);
break;
case "ALL":
logger.setLevel(Level.ALL);
LOGGER.info("Logger [{}] updated to [{}]", name, level);
break;
case "OFF":
default:
logger.setLevel(Level.OFF);
LOGGER.info("Logger [{}] updated to [{}]", name, level);
}
}
return true;
}
}

Categories

Resources