Logging

Gestion globale des logs

Il est important qu’une librairie ayant pour objectif de s’intégrer comme brique logicielle à un système existant offre des traces d’exécution précises et concises, adaptées à la situation (développement, qualification, production) tout en s’adaptant au système de log adopté par le logiciel maître sans être intrusif, c’est-à-dire sans nécessiter de librairie dépendante particulière.

Pour respecter ce cahier des charges, MRules a fait le choix de développer une interface de log interne, redirigeant les messages vers une interface de log externe.

Tous les logs générés par les composants logiciels de nos produits utilisent cette interface de log intermédiaire.

Concernant la verbosité des logs, les différents niveaux (DEBUG, INFO, WARN et ERROR) donneront des informations différentes, correspondant aux besoins des équipes devant les interpréter.

Support des systèmes de log externes

Les systèmes de log externes acceptés couvrent la majorité des applications. Il s’agit, dans l’ordre de priorité, de :

  • slf4j
  • Apache commons-logging
  • log4j
  • tinylog
  • la librairie intégrée au jdk : Java Utils Logging (JUL) par défaut

Configuration

Aucune configuration n’est nécessaire : une recherche des Logger disponibles est effectuée à l’initialisation.

Mais, si nécessaire, il est possible de forcer l’utilisation de l’une des librairies ci-dessus, par l’un des deux moyens suivants :

  • Créer un fichier nommé « mrules-log-factory.properties » à la racine du CLASS_PATH. Il contiendra une seule propriété nommée « mrules.log.factory », fournissant le nom de la factory de log à utiliser.
  • Definir une propriété système nommée « mrules.log.factory », fournissant le nom de la factory de log à utiliser.

Le nom de la factory de log est parmi ces valeurs :

  • slf4j: « com.massa.log.LogOverSlf4j »
  • Apache commons-logging: « com.massa.log.LogOverCommonslogging »
  • log4j: « com.massa.log.LogOverLog4j »
  • tinylog: « com.massa.log.LogOverTinylog »
  • JUL: « com.massa.log.DefaultLog »

Les logs du moteurs de règles

Afin de pouvoir retracer aisément les différentes étapes d’une compilation ou d’une exécution, tous les logs d’exécution d’une instance de RuleSet donnée sont par défaut regroupés au sein d’un même logger. Le nom du logger est construit à partir du nom donné au RuleSet.

La compilation, l’optimisation et l’exécution ont trois loggers séparés afin de pouvoir configurer leur niveau de log indépendamment. Les noms des loggers sont construits ainsi par défaut :

  • com.massa.mrules.set.COMPILE.<Nom du RuleSet>
  • com.massa.mrules.set.OPTIMIZE.<Nom du RuleSet>
  • com.massa.mrules.set.EXECUTE.<Nom du RuleSet>

Voici un extrait de configuration log4j permettant de tracer le déroulement de l’exécution d’une instance particulière du moteur de règles en cas de besoin :

#Ruleset instance with logger name « ruleSetName » will have a TRACE log level.
log4j.logger.com.massa.mrules.set.EXECUTE.ruleSetName = TRACE, console
log4j.additivity.com.massa.mrules.set.EXECUTE.ruleSetName = false
log4j.logger.com.massa.mrules.set.OPTIMIZE.ruleSetName = TRACE, console
log4j.additivity.com.massa.mrules.set.OPTIMIZE.ruleSetName = false
log4j.logger.com.massa.mrules.set.COMPILE.ruleSetName = TRACE, console
log4j.additivity.com.massa.mrules.set.COMPILE.ruleSetName = false
#All other Ruleset instances will have a WARN log level.
log4j.logger.com.massa.mrules.set= WARN, console
log4j.additivity.com.massa.mrules.set= false
#All other MRules logs with level INFO and above will be printed.
log4j.logger.com.massa.mrules = INFO, console
log4j.additivity.com.massa.mrules = false
#This deactivates traces for property access and conversion.
log4j.logger.com.massa= ERROR, console
log4j.additivity.com.massa = false

Et une autre configuration similaire pour logback :

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n</pattern>
    </encoder>
  </appender>

  <logger name="com.massa.mrules.set.EXECUTE.rulesetName" level="trace" additivity="false">
    <appender-ref ref="consoleAppender" />
  </logger>

  <logger name="com.massa.mrules" level="WARN" additivity="false">
    <appender-ref ref="STDOUT" />
  </logger>

  <root level="error">
    <appender-ref ref="consoleAppender" />
  </root>
</configuration>

De plus, le logger étant porté par le Contexte, il est parfaitement possible de surcharger son nom par défaut pour une exécution donnée afin de la tracer précisément.
Un identifiant unique d’exécution peut être généré et forcé, programmatiquement, avant chaque lancement. Ceci permettra d’interroger plus facilement un puit de log par exemple pour tracer précisément une exécution donnée.
L’exemple de code suivant génère un identifiant unique basé sur le nom et la date de lancement :

import java.util.Date;

import com.massa.log.Log;
import com.massa.log.LogFactory;
import com.massa.mrules.context.execute.MExecutionContext;
import com.massa.mrules.exception.MExecutionException;
import com.massa.mrules.set.IMruleExecutionSet;

/**
 * Surcharge le no du logger.
 */
public class NomLoggerExemple {
  private static final Log LOG = LogFactory.getLog(NomLoggerExemple.class.getName());

  public void execute() throws MExecutionException {
    IMruleExecutionSet ruleset = buildRuleset();
    final MExecutionContext<RulesetBean> ctx = new MExecutionContext<RulesetBean>(ruleset);
    // Génère le nom du logger
    String loggerName = ruleset.getName() + '.' + new Date().getTime();
    // Positionne le nom du logger. The point indique qu'il s'agit d'un suffixe et non du nom complet.
    ctx.setLoggerName('.' + loggerName);
    LOG.info("Launching ruleset with logger " + loggerName);
    ctx.execute();
  }
}

Intégrer notre interface de log

Cette interface est également utilisable pour vos développements (que ce soit pour étendre MRules ou pour vos développements métier). Cette abstraction vous donnera la possiblité de changer facilement de système de log au sein de votre application.

L’extrait de code suivant vous permettra de l’intégrer à vos classes :

import com.massa.log.Log;
import com.massa.log.LogFactory;

/**
 * Log une info à l'instanciation.
 */
public class LogExemple {
  private static final Log LOG = LogFactory.getLog(LogExemple.class.getName());

  public LogExample() {
    LOG.info("Construit un LogExemple.");
  }
}