J'ai un appender logback défini dans le logback.xml, c'est un appender de base de données, mais je suis curieux de savoir s'il est possible de configurer l'appender dans Java en utilisant mon propre pool de connexion défini comme un haricot.
Je trouve des choses similaires, mais jamais la réponse réelle.
Voici un exemple simple qui fonctionne pour moi (notez que j'utilise FileAppender dans cet exemple)
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;
public class Loggerutils {
public static void main(String[] args) {
Logger foo = createLoggerFor("foo", "foo.log");
Logger bar = createLoggerFor("bar", "bar.log");
foo.info("test");
bar.info("bar");
}
private static Logger createLoggerFor(String string, String file) {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
PatternLayoutEncoder ple = new PatternLayoutEncoder();
ple.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
ple.setContext(lc);
ple.start();
FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>();
fileAppender.setFile(file);
fileAppender.setEncoder(ple);
fileAppender.setContext(lc);
fileAppender.start();
Logger logger = (Logger) LoggerFactory.getLogger(string);
logger.addAppender(fileAppender);
logger.setLevel(Level.DEBUG);
logger.setAdditive(false); /* set to true if root should log too */
return logger;
}
}
Vous pouvez configurer les appenders par programmation. Presque tous les appenders sont testés à l'aide d'une configuration par programme. Il s'ensuit qu'il existe de nombreux exemples de configuration par programmeur d'appender dans le code source du projet logback. Pour un appender logback-core, regardez sous logback-core/src/test/Java
, et pour un journal classique d'appender, regardez sous logback-classic/src/test/Java
.
À titre de référence, lorsque vous essayez de modifier le code responsable de la création d'enregistreurs, un ensemble de règles doivent être satisfaites pour qu'un enregistreur fonctionne.
Ces règles ont été décrites dans un article formidable et utile Configuration par programme de slf4j/logback :
Maintenant, j'ai de l'expérience dans la configuration par programme de slf4j/logback.
Tâche
Un programme doit ouvrir un fichier journal séparé pour chaque fichier d'entrée traité.
Solution pour la tâche
Au lieu de configurer la consignation via XML, il faut instancier "manuellement" les encodeurs, les appenders et les enregistreurs, puis les configurer et les relier.
Mise en garde 1
Logback devient fou lors d'une tentative de partage d'encodeur (c'est-à-dire PatternLayoutEncoder) entre les appenders.
Solution pour la mise en garde 1
Créez un encodeur séparé pour chaque utilisateur.
Mise en garde 2
Logback refuse de consigner quoi que ce soit si les encodeurs et les appenders ne sont pas associés au contexte de journalisation.
Solution pour la mise en garde 2
Appelez setContext sur chaque encodeur et appender, en transmettant LoggerFactory en tant que paramètre.
Mise en garde 3
Logback refuse de consigner quoi que ce soit si les encodeurs et les appenders ne sont pas démarrés.
Solution pour la mise en garde 3
les encodeurs et les appenders doivent être démarrés dans le bon ordre, c’est-à-dire d’abord les encodeurs, puis les appenders.
Mise en garde 4
Les objets RollingPolicy (c'est-à-dire TimeBasedRollingPolicy) produisent des messages d'erreur étranges du type "format de date non reconnu", lorsqu'ils ne sont pas attachés au même contexte que appender.
Solution pour la mise en garde 4
appelez setContext sur RollingPolicy de la même manière que sur les encodeurs et les appenders.
Voici un exemple concret de configuration de la journalisation "manuelle":
package testpackage
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.LoggerContext
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy
import org.slf4j.LoggerFactory
class TestLogConfig {
public static void main(String[] args) {
LoggerContext logCtx = LoggerFactory.getILoggerFactory();
PatternLayoutEncoder logEncoder = new PatternLayoutEncoder();
logEncoder.setContext(logCtx);
logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n");
logEncoder.start();
ConsoleAppender logConsoleAppender = new ConsoleAppender();
logConsoleAppender.setContext(logCtx);
logConsoleAppender.setName("console");
logConsoleAppender.setEncoder(logEncoder);
logConsoleAppender.start();
logEncoder = new PatternLayoutEncoder();
logEncoder.setContext(logCtx);
logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n");
logEncoder.start();
RollingFileAppender logFileAppender = new RollingFileAppender();
logFileAppender.setContext(logCtx);
logFileAppender.setName("logFile");
logFileAppender.setEncoder(logEncoder);
logFileAppender.setAppend(true);
logFileAppender.setFile("logs/logfile.log");
TimeBasedRollingPolicy logFilePolicy = new TimeBasedRollingPolicy();
logFilePolicy.setContext(logCtx);
logFilePolicy.setParent(logFileAppender);
logFilePolicy.setFileNamePattern("logs/logfile-%d{yyyy-MM-dd_HH}.log");
logFilePolicy.setMaxHistory(7);
logFilePolicy.start();
logFileAppender.setRollingPolicy(logFilePolicy);
logFileAppender.start();
Logger log = logCtx.getLogger("Main");
log.additive = false;
log.level = Level.INFO;
log.addAppender(logConsoleAppender);
log.addAppender(logFileAppender);
}
}
Juste si quelqu'un cherchait un exemple concret de configuration programmatique.
Ici, j'ai configuré le jeu de caractères de ConsoleAppender:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
ConsoleAppender<ILoggingEvent> appender =
(ConsoleAppender) lc.getLogger("appconsole").getAppender("STDOUT");
LayoutWrappingEncoder<ILoggingEvent> enc =
(LayoutWrappingEncoder<ILoggingEvent>) appender.getEncoder();
enc.setCharset(Charset.forName("utf-8"));
Et mon logback.xml:
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>866</charset>
<pattern>[%level] %msg%n</pattern>
</encoder>
</appender>
<logger name="appconsole">
<appender-ref ref="STDOUT" />
</logger>
Pourquoi ai-je besoin de configurer par programme un enregistreur? Parce que je compresse mon application (Spring Boot) dans un fichier jar. Par conséquent, le fichier Logback.xml semble être masqué dans un fichier jar. Bien que ce ne soit pas pratique de décompresser et de le changer. Et je ne pas besoin n'importe quel fichier logback.xml à côté de mon app.jar. Je n'ai qu'un fichier app.yaml qui contient toutes les propriétés de configuration pour app.
Pas le droit de commenter (encore?), Je voudrais juste ajouter trois astuces;
en ce qui concerne les mises en garde ci-dessus, si vous avez des problèmes, ajoutez simplement un appel à
StatusPrinter.print(context);
après que tout soit configuré, c’est-à-dire, après avoir ajouté vos ajouts, le root/"principal" appender: it will vous dit ce qui ne va pas.
J'aime beaucoup séparer les niveaux de journalisation dans différents fichiers; quand je cherche des erreurs, je commence par regarder dans le fichier d’erreurs et ainsi de suite, en les configurant comme
tot_[app name].log : Level.INFO deb_[app name].log : Level.DEBUG err_[app name].log : Level.ERROR
routage au moyen d’une simple classe de filtres privés telle que
private static class ThresholdLoggerFilter extends Filter<ILoggingEvent> {
private final Level level;
private ThresholdLoggerFilter(Level level){
this.level = level;
}
@Override
public FilterReply decide(ILoggingEvent event) {
if (event.getLevel().isGreaterOrEqual(level)) {
return FilterReply.NEUTRAL;
} else {
return FilterReply.DENY;
}
}
}
puis appelez simplement myFilter.start()
et myAppender.addFilter(myFilter);
.
Enfin, je souhaite généralement pouvoir modifier les niveaux de journalisation de manière dynamique, la configuration implémentant une interface simple, telle que
public interface LoggingService {
void setRootLogLevel(Level level);
}
garder le niveau de journalisation racine dans un fichier de propriétés qui est surveillé de sorte que chaque fois qu'il y a une entrée valide, j'appelle ce service implémenté comme
@Override
public void setRootLogLevel(Level level) {
if (context != null && context.isStarted()) {
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(level);
}
}
avec mon nouveau niveau de logger racine.