Définition du niveau de journalisation du message à l’exécution dans slf4j

Lorsque vous utilisez log4j, la Logger.log(Priority p, Object message) est disponible et peut être utilisée pour consigner un message à un niveau de journal déterminé au moment de l’exécution. Nous utilisons ce fait et cette astuce pour redirect stderr vers un enregistreur à un niveau de journalisation spécifique.

slf4j n’a pas de méthode log() générique que je puisse trouver. Est-ce que cela signifie qu’il n’y a aucun moyen de mettre en œuvre ce qui précède?

Il n’y a aucun moyen de faire cela avec slf4j .

J’imagine que la raison pour laquelle cette fonctionnalité est manquante est qu’il est presque impossible de construire un type de Level pour slf4j qui peut être mappé efficacement au type Level (ou équivalent) utilisé dans toutes les implémentations de journalisation possibles derrière la façade. Alternativement, les concepteurs ont décidé que votre cas d’utilisation est trop inhabituel pour justifier les coûts de support.

En ce qui concerne le cas d’ utilisation de @ ripper234 (tests unitaires), je pense que la solution pragmatique consiste à modifier le ou les tests unitaires pour savoir quel système de journalisation se trouve derrière la façade slf4j … lors des tests unitaires.

Richard Fearn a la bonne idée, alors j’ai écrit le cours complet en fonction de son code squelette. J’espère que c’est assez court pour poster ici. Copiez et collez pour le plaisir. Je devrais probablement append une incantation magique aussi: “Ce code est publié dans le domaine public”

 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, Ssortingng 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, Ssortingng 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, Ssortingng 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; } } 

Vous pouvez l’implémenter en utilisant 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 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, Ssortingng s) { map.get(level).log(s); } @FunctionalInterface private interface LoggingFunction { public void log(Ssortingng arg); } } 

Essayez de passer en mode de connexion et d’utiliser

 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")); 

Je crois que ce sera le seul appel à Logback et le rest de votre code restra inchangé. Logback utilise SLF4J et la migration sera sans problème, seuls les fichiers de configuration xml devront être modifiés.

N’oubliez pas de rétablir le niveau de journalisation une fois que vous avez terminé.

Cela peut être fait avec un enum et une méthode d’assistance:

 enum LogLevel { TRACE, DEBUG, INFO, WARN, ERROR, } public static void log(Logger logger, LogLevel level, Ssortingng 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", ...); 

Vous pouvez append d’autres variantes de log , par exemple si vous voulez des équivalents génériques du paramètre 1 ou 2 parameters / error / etc de SLF4J. méthodes.

Toute personne souhaitant une solution intégrale compatible SLF4J à ce problème peut vouloir vérifier les extensions Lidalia SLF4J – elles sont sur Maven Central.

Je viens de rencontrer un besoin similaire. Dans mon cas, slf4j est configuré avec l’adaptateur de journalisation Java (celui de jdk14). En utilisant l’extrait de code suivant, j’ai réussi à modifier le niveau de débogage à l’exécution:

 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"); 

Sur la base de la réponse de massimo virgilio, j’ai également réussi à le faire avec slf4j-log4j en utilisant l’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()); } 

Voici une solution lambda moins conviviale que @Paul Croarkin dans un sens (le niveau est effectivement passé deux fois). Mais je pense que (a) l’utilisateur doit passer le Logger; et (b) AFAIU la question originale ne demandait pas un moyen commode pour partout dans l’application, seulement une situation avec peu d’utilisations dans une bibliothèque.

 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 logEnabledPredicate, Ssortingng format, Object... args) { if (logEnabledPredicate.get()) { logFunc.accept(format, args); } } public static void main(Ssortingng[] 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(Ssortingng, Object...) instead of warn(Ssortingng, Throwable), but prints stacktrace nevertheless log(LOG::warn, LOG::isWarnEnabled, "error doing something: {}", e, e); } } 

Puisque slf4j autorise un Throwable (dont la trace de stack doit être enregistrée) dans le paramètre varargs , je pense qu’il n’est pas nécessaire de surcharger la méthode d’assistance de log pour d’autres consommateurs que (Ssortingng, Object[]) .

J’ai été en mesure de le faire pour la liaison JDK14 en demandant d’abord l’instance SLF4J Logger, puis en définissant le niveau sur la liaison – vous pouvez essayer ceci pour la liaison Log4J.

 private void setLevel(Class loggerClass, java.util.logging.Level level) { org.slf4j.LoggerFactory.getLogger(loggerClass); java.util.logging.Logger.getLogger(loggerClass.getName()).setLevel(level); } 

en utilisant l’introspection Java, vous pouvez le faire, par exemple:

 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); } } } 

non, il a un certain nombre de méthodes, info (), debug (), warn (), etc (cela remplace le champ de priorité)

Jetez un coup d’œil à http://www.slf4j.org/api/org/slf4j/Logger.html pour l’application complète de Logger.