131

When using log4j, the Logger.log(Priority p, Object message) method is available and can be used to log a message at a log level determined at runtime. We're using this fact and this tip to redirect stderr to a logger at a specific log level.

slf4j doesn't have a generic log() method that I can find. Does that mean there's no way to implement the above?

Update: As of SLF4J version 2.0, it is possible to use the Logger.atLevel() method. For example:

import org.slf4j.event.Level;
import org.slf4j.Logger;
...
Logger logger = ... // some slf4j logger of choice
logger.atLevel(Level.Info).log("hello world");
Ceki
  • 26,753
  • 7
  • 62
  • 71
Edward Dale
  • 29,597
  • 13
  • 90
  • 129
  • 6
    It looks like there's some discussion of adding this to slf4j 2.0 on the dev mailing list: http://www.qos.ch/pipermail/slf4j-dev/2010-March/002865.html – Edward Dale Apr 12 '10 at 12:42
  • 1
    take a look at Marker, this is custom data you can pass to log chain. – tuxSlayer Jul 15 '13 at 11:43
  • 1
    @tuxSlayer can you please elaborate how to use Marker in this case? – Miserable Variable Sep 05 '13 at 02:16
  • Probably its not the best idea for "logging" but you can use several markers for log entry "priority" (high|low|normal, info|warn|fatal) and use filtering in logback or custom appender to consume markers and drive log entries into separate channels (log info, email fatal etc). However, the more straight way is to have a facade for this as was pointed in answers below. – tuxSlayer Sep 09 '13 at 10:06
  • 2
    This feature is supposed to be part of `slf4j 2.0`. https://jira.qos.ch/browse/SLF4J-124 See my answer for details and for a possible `slf4j 1.x`-workaround. – slartidan May 23 '19 at 06:52
  • The Logger.atLevel() introduced in SLF4J 2.0 provides the desired functionality. – Ceki Nov 29 '22 at 22:34

18 Answers18

61

There is no way to do this with slf4j 1.x.

I imagine that the reason that this functionality is missing is that it is next to impossible to construct a Level type for slf4j that can be efficiently mapped to the Level (or equivalent) type used in all of the possible logging implementations behind the facade. Alternatively, the designers decided that your use-case is too unusual to justify the overheads of supporting it.

Concerning @ripper234's use-case (unit testing), I think the pragmatic solution is modify the unit test(s) to hard-wire knowledge of what logging system is behind the slf4j facade ... when running the unit tests.


UPDATE

They intend to implement piecemeal construction of logging events (with dynamic logging levels) in slf4j 2.0; see https://jira.qos.ch/browse/SLF4J-124. According to @Ceki (see comments), it is implemented in the 2.0.0-alpha2 release.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 14
    There's no mapping necessary really. There are five levels already implicitly defined by the methods in `org.slf4j.Logger`: debug, error, info, trace, warn. – Edward Dale Apr 12 '10 at 12:45
  • That is a mapping, and it costs CPU cycles to support it. – Stephen C Apr 12 '10 at 12:49
  • 1
    And the issues got closed as Invalid. As far as I understand this, it is a deliberate design choice. – ripper234 Nov 30 '10 at 13:28
  • @ripper234 - which supports what I said in my answer, IMO. – Stephen C Nov 30 '10 at 21:29
  • 11
    @ripper234 - I don't think your bug addressed the same issue as scompt.com's original question. You asked about configuring the level of the *underlying logging system* via the SLF4J API. What scompt.com was after was a generic 'log' method in the SLF4J API, that takes the logging level *of the message* as a parameter. – Richard Fearn May 25 '11 at 21:24
  • 1
    +1 @RichardFearn And one cannot undo the comment upvote after 60 seconds, *meh*. Feature request is existing meanwhile: http://bugzilla.slf4j.org/show_bug.cgi?id=133 – jan Mar 28 '14 at 10:40
  • 3
    The RFE links don't resolve any more. The relevant links are now: http://jira.qos.ch/browse/SLF4J-124 and http://jira.qos.ch/browse/SLF4J-197 ... and both have been closed. Read the comments for the rationale. – Stephen C Apr 23 '15 at 12:20
  • SLFJ does one thing very good, it routs all other logging framework calls to one logging implementation, i suggest you write your implementation (its a simple one class to do whatever you need) ever since i did it, i have no issues with logging. – vach Jul 12 '18 at 04:59
  • 1
    Fixed in SLF4J 2.0.0-alpha2. See also my below. https://stackoverflow.com/a/68185612/100970 – Ceki Jun 29 '21 at 22:36
  • I am the one-billionth person to look up how to set the logging level in slf4j, and like everyone else, so far unable to find a clear and simple answer. – Gerry May 30 '22 at 21:10
  • @Gerry - Clear simple answer. Update to SLF4J 2.0.0. As of right now, the latest is alpha7. – Stephen C May 30 '22 at 22:57
34

Richard Fearn has the right idea, so I wrote up the full class based on his skeleton code. It's hopefully short enough to post here. Copy & paste for enjoyment. I should probably add some magic incantation, too: "This code is released to the public domain"

import org.slf4j.Logger;

public class LogLevel {

    /**
     * Allowed levels, as an enum. Import using "import [package].LogLevel.Level"
     * Every logging implementation has something like this except SLF4J.
     */

    public static enum Level {
        TRACE, DEBUG, INFO, WARN, ERROR
    }

    /**
     * This class cannot be instantiated, why would you want to?
     */

    private LogLevel() {
        // Unreachable
    }

    /**
     * Log at the specified level. If the "logger" is null, nothing is logged.
     * If the "level" is null, nothing is logged. If the "txt" is null,
     * behaviour depends on the SLF4J implementation.
     */

    public static void log(Logger logger, Level level, String txt) {
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                logger.trace(txt);
                break;
            case DEBUG:
                logger.debug(txt);
                break;
            case INFO:
                logger.info(txt);
                break;
            case WARN:
                logger.warn(txt);
                break;
            case ERROR:
                logger.error(txt);
                break;
            }
        }
    }

    /**
     * Log at the specified level. If the "logger" is null, nothing is logged.
     * If the "level" is null, nothing is logged. If the "format" or the "argArray"
     * are null, behaviour depends on the SLF4J-backing implementation.
     */

    public static void log(Logger logger, Level level, String format, Object[] argArray) {
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                logger.trace(format, argArray);
                break;
            case DEBUG:
                logger.debug(format, argArray);
                break;
            case INFO:
                logger.info(format, argArray);
                break;
            case WARN:
                logger.warn(format, argArray);
                break;
            case ERROR:
                logger.error(format, argArray);
                break;
            }
        }
    }

    /**
     * Log at the specified level, with a Throwable on top. If the "logger" is null,
     * nothing is logged. If the "level" is null, nothing is logged. If the "format" or
     * the "argArray" or the "throwable" are null, behaviour depends on the SLF4J-backing
     * implementation.
     */

    public static void log(Logger logger, Level level, String txt, Throwable throwable) {
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                logger.trace(txt, throwable);
                break;
            case DEBUG:
                logger.debug(txt, throwable);
                break;
            case INFO:
                logger.info(txt, throwable);
                break;
            case WARN:
                logger.warn(txt, throwable);
                break;
            case ERROR:
                logger.error(txt, throwable);
                break;
            }
        }
    }

    /**
     * Check whether a SLF4J logger is enabled for a certain loglevel. 
     * If the "logger" or the "level" is null, false is returned.
     */

    public static boolean isEnabledFor(Logger logger, Level level) {
        boolean res = false;
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                res = logger.isTraceEnabled();
                break;
            case DEBUG:
                res = logger.isDebugEnabled();
                break;
            case INFO:
                res = logger.isInfoEnabled();
                break;
            case WARN:
                res = logger.isWarnEnabled();
                break;
            case ERROR:
                res = logger.isErrorEnabled();
                break;
            }
        }
        return res;
    }
}
David Tonhofer
  • 14,559
  • 5
  • 55
  • 51
  • This would be easier to use with a variadic (Object...) args parameter. – Anonymoose Jun 27 '12 at 21:54
  • "org.slf4j.Logger" has quite a few logging method signatures that are not handled in the above class, so an extension is probably warranted: http://www.slf4j.org/api/org/slf4j/Logger.html – David Tonhofer Dec 20 '13 at 17:17
  • 2
    I think that this implementation will add a non desired change. When you use the call logger.info(...) the logger has access to the caller class and method and it could be added to the log entry automatically. Now, with this implementation, the call log(logger, level, txt) will produce a log entry which will have always the same caller: Loglevel.log. Am I right? – Domin Aug 12 '15 at 06:51
  • @Domin Hi, you mean, the logger could examine the current call stack, then extract the last entry for automatic logging, which is not the case here? In principle yes, but in fact, the stack will grove a bit more even after this until the actual message is written out (in particular, logback has to be called at some point, an then the actual appender). I think it should be the appender's role to throw away non-interesting stack lines, so you could adapt it to throw everything away up to and including the call to this Loglevel class. – David Tonhofer Aug 12 '15 at 18:07
  • @David, Yes, you are right :-). I'm not sure that it is a task for the appender because on that case you are defining a hard dependency between the appender and the logger... but... it is a solution. Thanks David – Domin Aug 14 '15 at 14:10
  • The trouble with this is you are not logging with slf4j anymore. You are logging with a custom logging facade for the slf4j logging facade for a real logger. You have to modify your test / application / whatever code to do this. – Stephen C May 24 '19 at 13:55
  • @StephenC ... but only when needed, i.e. probably just in a few classes. This new "logging facade" is after all just a thin shim that can even be code-duplicated. (Maybe slf4j has this facility now? I haven't seriously coded in some time) – David Tonhofer May 24 '19 at 14:29
  • @DavidTonhofer actually the entry point into the logging facade should remove stack frames. The appender can't know what logging framework is calling it - could be SLF4J, could be just raw Logback, could be log4j2 with its own Logback adapter, could be a newer j.u.l with Logback, could be some homebrew logging. – toolforger Jul 05 '22 at 13:53
21

Try switching to Logback and use

ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.toLevel("info"));

I believe this will be the only call to Logback and the rest of your code will remain unchanged. Logback uses SLF4J and the migration will be painless, just the xml config files will have to be changed.

Remember to set the log level back after you're done.

Ondrej Skopek
  • 778
  • 10
  • 27
agelbess
  • 4,249
  • 3
  • 20
  • 21
  • I was already using Logback-backed slf4j, and this instantly allowed me to clean up my unit tests. Thanks! – Lambart Mar 18 '14 at 23:51
  • 2
    This was my first -1, thanks. I believe you are wrong. Logback uses SLF4J, so the answer IS relevant. – agelbess Apr 02 '14 at 08:18
  • 6
    @AlexandrosGelbessis You should reread the question. It was asked for a method that could log programmatically one log message at any level. You are changing the level of the root logger for all messages, not only for one. – jan Apr 03 '14 at 07:16
14

You can implement this using Java 8 lambdas.

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;

public class LevelLogger {
    private static final Logger LOGGER = LoggerFactory.getLogger(LevelLogger.class);
    private static final Map<Level, LoggingFunction> map;

    static {
        map = new HashMap<>();
        map.put(Level.TRACE, (o) -> LOGGER.trace(o));
        map.put(Level.DEBUG, (o) -> LOGGER.debug(o));
        map.put(Level.INFO, (o) -> LOGGER.info(o));
        map.put(Level.WARN, (o) -> LOGGER.warn(o));
        map.put(Level.ERROR, (o) -> LOGGER.error(o));
    }

    public static void log(Level level, String s) {
        map.get(level).log(s);
    }

    @FunctionalInterface
    private interface LoggingFunction {
        public void log(String arg);
    }
}
Paul Croarkin
  • 14,496
  • 14
  • 79
  • 118
  • 1
    Well yea ... but now you need to modify your codebase to use this API as well as or instead of slf4j. If you use it instead of slf4j 1) it probably needs to be richer, 2) lots of (at least) imports need to be changed, and 3) this new layer in front of slf4j adds an extra logging overhead. – Stephen C Oct 07 '18 at 23:17
  • 5
    Be also aware that when you go for this solution, the class that does the actual logging will not be logged (because the logger is initialized with `LevelLogger`), which is not a good thing because it's generally very useful information. – Dormouse Oct 19 '18 at 11:32
8

This can be done with an enum and a helper method:

enum LogLevel {
    TRACE,
    DEBUG,
    INFO,
    WARN,
    ERROR,
}

public static void log(Logger logger, LogLevel level, String format, Object[] argArray) {
    switch (level) {
        case TRACE:
            logger.trace(format, argArray);
            break;
        case DEBUG:
            logger.debug(format, argArray);
            break;
        case INFO:
            logger.info(format, argArray);
            break;
        case WARN:
            logger.warn(format, argArray);
            break;
        case ERROR:
            logger.error(format, argArray);
            break;
    }
}

// example usage:
private static final Logger logger = ...
final LogLevel level = ...
log(logger, level, "Something bad happened", ...);

You could add other variants of log, say if you wanted generic equivalents of SLF4J's 1-parameter or 2-parameter warn/error/etc. methods.

Richard Fearn
  • 25,073
  • 7
  • 56
  • 55
  • 5
    True, but slf4j purpose is not to have to write log wrappers. – djjeck Jun 20 '14 at 00:35
  • 6
    The purpose of SLF4J is to provide an abstraction for different logging frameworks. If that abstraction doesn't provide exactly what you need, you have no choice but to write a helper method. The only other alternative is to contribute a method like the one in my answer to the SLF4J project. – Richard Fearn Jun 20 '14 at 08:49
  • 1
    I agree, but in this case there are caveats, like that you wouldn't be able to provide file and line number anymore, unless you implemented yet another workaround for that. In this case I would have stuck with log4j, until the framework supported the feature - which eventually happened through an extension, see Robert Elliot's more recent answer. – djjeck Jun 20 '14 at 18:46
7

It is not possible to specify a log level in sjf4j 1.x out of the box. But there is hope for slf4j 2.0 to fix the issue. In 2.0 it might look like this:

// POTENTIAL 2.0 SOLUTION
import org.slf4j.helpers.Util;
import static org.slf4j.spi.LocationAwareLogger.*;

// does not work with slf4j 1.x
Util.log(logger, DEBUG_INT, "hello world!");

In the meanwhile, for slf4j 1.x, you can use this workaround:

Copy this class into your classpath:

import org.slf4j.Logger;
import java.util.function.Function;

public enum LogLevel {

    TRACE(l -> l::trace, Logger::isTraceEnabled),
    DEBUG(l -> l::debug, Logger::isDebugEnabled),
    INFO(l -> l::info, Logger::isInfoEnabled),
    WARN(l -> l::warn, Logger::isWarnEnabled),
    ERROR(l -> l::error, Logger::isErrorEnabled);

    interface LogMethod {
        void log(String format, Object... arguments);
    }

    private final Function<Logger, LogMethod> logMethod;
    private final Function<Logger, Boolean> isEnabledMethod;

    LogLevel(Function<Logger, LogMethod> logMethod, Function<Logger, Boolean> isEnabledMethod) {
        this.logMethod = logMethod;
        this.isEnabledMethod = isEnabledMethod;
    }

    public LogMethod prepare(Logger logger) {
        return logMethod.apply(logger);
    }

    public boolean isEnabled(Logger logger) {
        return isEnabledMethod.apply(logger);
    }
}

Then you can use it like this:

Logger logger = LoggerFactory.getLogger(Application.class);

LogLevel level = LogLevel.ERROR;
level.prepare(logger).log("It works!"); // just message, without parameter
level.prepare(logger).log("Hello {}!", "world"); // with slf4j's parameter replacing

try {
    throw new RuntimeException("Oops");
} catch (Throwable t) {
    level.prepare(logger).log("Exception", t);
}

if (level.isEnabled(logger)) {
    level.prepare(logger).log("logging is enabled");
}

This will output a log like this:

[main] ERROR Application - It works!
[main] ERROR Application - Hello world!
[main] ERROR Application - Exception
java.lang.RuntimeException: Oops
    at Application.main(Application.java:14)
[main] ERROR Application - logging is enabled

Is it worth it?

  • Pro It keeps the source code location (class names, method names, line numbers will point to your code)
  • Pro You can easily define variables, parameters and return types as LogLevel
  • Pro Your business code stays short and easy to read, and no additional dependencies required.

The source code as minimal example is hosted on GitHub.

slartidan
  • 20,403
  • 15
  • 83
  • 131
  • Note: the `LogMethod` interface needs to be public for it to work with classes outside of its package. Other than that, it works as intended. Thanks! – andrebrait Mar 11 '20 at 12:57
5

Anyone wanting a drop-in fully SLF4J compatible solution to this problem might want to check out Lidalia SLF4J Extensions - it's on Maven Central.

Robert Elliot
  • 1,372
  • 13
  • 20
5

I've just needed something like that and came up with:

@RequiredArgsConstructor //lombok annotation
public enum LogLevel{

    TRACE(l -> l::trace),
    INFO (l -> l::info),
    WARN (l -> l::warn),
    ERROR(l -> l::error);

    private final Function<Logger, Consumer<String>> function;

    public void log(Logger logger, String message) {
        function.apply(logger).accept(message);
    }
}

usage:

    LogLevel level = LogLevel.TRACE;
    level.log(logger, "message");

Logger is passed during invocation, so the class info should be ok, and it works nicely with @Slf4j lombok annotation.

Kamil Nowak
  • 113
  • 2
  • 6
5

Confirm answer Ondrej Skopek

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;

var rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.TRACE);

You will get result:

2020-05-14 14:01:16,644 TRACE [] [o.a.k.c.m.Metrics] Test worker Registered metric named MetricName [name=bufferpool-wait-time-total, group=producer-metrics, description=The total time an appender waits for space allocation., tags={client-id=producer-2}]

Torino
  • 445
  • 5
  • 12
4

The fluent API in SLF4J v2.0 introduces a new method, namely Logger.atLevel(Level) which an be used to accomplish the desired outcome.

Example code:

public void logAMessageAtGivenLevel(Level aLevel, String aMessage) {
  Logger logger = .. // some slf4j logger of choice
  logger.atLevel(aLevel).log(aMessage);
}

The default implementation will return the singleton instance of NOPLoggingEventBuilder if the logger is disabled for the given Level. This implementation of the LoggingEventBuilder interface, as the name NOP indicates, does nothing, preserving nanosecond execution time for disabled log messages.

Ceki
  • 26,753
  • 7
  • 62
  • 71
  • oh the naming is terrible :( – Antony Stubbs May 12 '22 at 14:39
  • 3
    boom! https://jira.qos.ch/browse/SLF4J-124?focusedCommentId=20911&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-20911 `atLevel` :) thank gosh! – Antony Stubbs May 12 '22 at 14:46
  • 1
    `logger.atLevel` is the right one. Per the javadoc `logAMessageAtGivenLevel` is intended to be used by logging systems implementing the SLF4J API and not by end users. – Andrey Nov 27 '22 at 00:03
  • Changed my answer to use "atLevel()" method instead of "makeLoggingEventBuilder()" method. The latter is intended for use by logging systems and not end users. – Ceki Nov 29 '22 at 22:28
3

The method I use is to import the ch.qos.logback modules and then type-cast the slf4j Logger instance to a ch.qos.logback.classic.Logger. This instance includes a setLevel() method.

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;

Logger levelSet = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);

// Now you can set the desired logging-level
levelSet.setLevel( Level.OFF );

To find out the possible Logging-levels, you can explode the ch.qos.logback class to see all the possible values for Level:

prompt$ javap -cp logback-classic-1.2.3.jar ch.qos.logback.classic.Level

The results are the following:

{
   // ...skipping
   public static final ch.qos.logback.classic.Level OFF;
   public static final ch.qos.logback.classic.Level ERROR;
   public static final ch.qos.logback.classic.Level WARN;
   public static final ch.qos.logback.classic.Level INFO;
   public static final ch.qos.logback.classic.Level DEBUG;
   public static final ch.qos.logback.classic.Level TRACE;
   public static final ch.qos.logback.classic.Level ALL;
}
Glenn Inn
  • 41
  • 1
  • 4
1

It is not possible with slf4j API to dynamically change log level but you can configure logback (if you use this) by your own. In that case create factory class for your logger and implement root logger with configuration that you need.

LoggerContext loggerContext = new LoggerContext();
ch.qos.logback.classic.Logger root = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

// Configure appender
final TTLLLayout layout = new TTLLLayout();
layout.start(); // default layout of logging messages (the form that message displays 
// e.g. 10:26:49.113 [main] INFO com.yourpackage.YourClazz - log message

final LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<>();
encoder.setCharset(StandardCharsets.UTF_8);
encoder.setLayout(layout);

final ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
appender.setContext(loggerContext);
appender.setEncoder(encoder);
appender.setName("console");
appender.start();

root.addAppender(appender);

After you configure root logger (only once is enough) you can delegate getting new logger by

final ch.qos.logback.classic.Logger logger = loggerContext.getLogger(clazz);

Remember to use the same loggerContext.

Changing log level is easy to do with root logger given from loggerContext.

root.setLevel(Level.DEBUG);
pablo127
  • 33
  • 1
  • 4
0

I have just encountered a similar need. In my case, slf4j is configured with the java logging adapter (the jdk14 one). Using the following code snippet I have managed to change the debug level at runtime:

Logger logger = LoggerFactory.getLogger("testing");
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger("testing");
julLogger.setLevel(java.util.logging.Level.FINE);
logger.debug("hello world");
Yair Zaslavsky
  • 4,091
  • 4
  • 20
  • 27
  • 1
    Like other answers, this does not address the original question, it's a different problem. – E-Riz Oct 28 '16 at 13:06
0

Based on the answer of massimo virgilio, I've also managed to do it with slf4j-log4j using introspection. HTH.

Logger LOG = LoggerFactory.getLogger(MyOwnClass.class);

org.apache.logging.slf4j.Log4jLogger LOGGER = (org.apache.logging.slf4j.Log4jLogger) LOG;

try {
    Class loggerIntrospected = LOGGER.getClass();
    Field fields[] = loggerIntrospected.getDeclaredFields();
    for (int i = 0; i < fields.length; i++) {
        String fieldName = fields[i].getName();
        if (fieldName.equals("logger")) {
            fields[i].setAccessible(true);
            org.apache.logging.log4j.core.Logger loggerImpl = (org.apache.logging.log4j.core.Logger) fields[i].get(LOGGER);
            loggerImpl.setLevel(Level.DEBUG);
        }
    }
} catch (Exception e) {
    System.out.println("ERROR :" + e.getMessage());
}
Guido
  • 46,642
  • 28
  • 120
  • 174
0

Here's a lambda solution not as user-friendly as @Paul Croarkin's in one way (the level is effectively passed twice). But I think (a) the user should pass the Logger; and (b) AFAIU the original question was not asking for a convenient way for everywhere in the application, only a situation with few usages inside a library.

package test.lambda;
import java.util.function.*;
import org.slf4j.*;

public class LoggerLambda {
    private static final Logger LOG = LoggerFactory.getLogger(LoggerLambda.class);

    private LoggerLambda() {}

    public static void log(BiConsumer<? super String, ? super Object[]> logFunc, Supplier<Boolean> logEnabledPredicate, 
            String format, Object... args) {
        if (logEnabledPredicate.get()) {
            logFunc.accept(format, args);
        }
    }

    public static void main(String[] args) {
        int a = 1, b = 2, c = 3;
        Throwable e = new Exception("something went wrong", new IllegalArgumentException());
        log(LOG::info, LOG::isInfoEnabled, "a = {}, b = {}, c = {}", a, b, c);

        // warn(String, Object...) instead of warn(String, Throwable), but prints stacktrace nevertheless
        log(LOG::warn, LOG::isWarnEnabled, "error doing something: {}", e, e);
    }
}

Since slf4j allows a Throwable (whose stack trace should be logged) inside the varargs param, I think there is no need for overloading the log helper method for other consumers than (String, Object[]).

EndlosSchleife
  • 515
  • 7
  • 19
0

I was able to do this for the JDK14 binding by first requesting the SLF4J Logger instance and then setting the level on the binding -- you may try this for the Log4J binding.

private void setLevel(Class loggerClass, java.util.logging.Level level) {
  org.slf4j.LoggerFactory.getLogger(loggerClass);
  java.util.logging.Logger.getLogger(loggerClass.getName()).setLevel(level);
}
youurayy
  • 1,635
  • 1
  • 18
  • 11
-2

using java introspection you can do it, for example:

private void changeRootLoggerLevel(int level) {

    if (logger instanceof org.slf4j.impl.Log4jLoggerAdapter) {
        try {
            Class loggerIntrospected = logger.getClass();
            Field fields[] = loggerIntrospected.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                String fieldName = fields[i].getName();
                if (fieldName.equals("logger")) {
                    fields[i].setAccessible(true);
                    org.apache.log4j.Logger loggerImpl = (org.apache.log4j.Logger) fields[i]
                            .get(logger);

                    if (level == DIAGNOSTIC_LEVEL) {
                        loggerImpl.setLevel(Level.DEBUG);
                    } else {
                        loggerImpl.setLevel(org.apache.log4j.Logger.getRootLogger().getLevel());
                    }

                    // fields[i].setAccessible(false);
                }
            }
        } catch (Exception e) {
            org.apache.log4j.Logger.getLogger(LoggerSLF4JImpl.class).error("An error was thrown while changing the Logger level", e);
        }
    }

}
-6

no, it has a number of methods, info(), debug(), warn(), etc (this replaces the priority field)

have a look at http://www.slf4j.org/api/org/slf4j/Logger.html for the full Logger api.

chris
  • 9,745
  • 1
  • 27
  • 27
  • sorry, i see what you're asking now. no, there's no generic way to change log level at runtime, but you could easily implement a helper method with a switch statment. – chris Apr 12 '10 at 11:47
  • Yes, but then you have to do that once for each overloaded version of the "log" method. – Andrew Swan Dec 04 '14 at 04:23