web-dev-qa-db-fra.com

Enregistrement conditionnel avec log4j

L'application Web sur laquelle je travaille génère parfois des problèmes d'intégrité des données pour certains utilisateurs. J'aimerais activer la journalisation au niveau de la trace, mais comme nous traitons avec une centaine de demandes par seconde, la journalisation de la trace ne pose plus aucun problème.

Y a-t-il un moyen avec log4j de pouvoir se connecter de manière conditionnelle? En d'autres termes, j'aimerais pouvoir obtenir des journaux de suivi uniquement lorsque des utilisateurs spécifiques en font la demande. Comme je ne sais pas à l’avance quels utilisateurs seront affectés, je ne peux pas simplement coder temporairement les noms d’utilisateurs.

Modifier:

Je pense que j'ai besoin d'être un peu plus clair. Je peux facilement ajouter des conditions à mes relevés de journal. Par exemple

Logger logger = Logger.getLogger("foo");
String usernameFilter = "piglet";
String username = request.getParameter("username");
logger.setLevel(usernameFilter.equals(username) ? Level.TRACE : Level.INFO);
if (logger.isTraceEnabled()) {
   logger.trace("blah blah blah");
}

La difficulté est de modifier dynamiquement la condition qui définit le niveau de journalisation. En d'autres termes, dans l'exemple ci-dessus, comment définir la valeur de nomutilisateurFilter, autre que la coder en dur.

18
Dave Isaacs

Vous souhaitez consulter Contextes de diagnostic imbriqués ou Contextes de diagnostic mappés dans log4j ou slf4j. Un NDC/MDC vous permet d’insérer dans votre session des données pouvant être filtrées par log4j.

Vous définissez donc le nom d'utilisateur sur le NDC, puis vous pouvez modifier le fichier log4j.properties pour modifier le niveau de journalisation pour des utilisateurs spécifiques.

Un MDC utilise une carte, tandis qu'un NDC est basé sur le principe de la pile. Si vous utilisez slf4j , vous pouvez même créer des fichiers journaux séparés en fonction des informations contenues dans votre MDC.

Par exemple, nous l'avons fait lorsque les utilisateurs se sont connectés à un site Web. Nous voulions tracer ce que faisait un utilisateur particulier (de manière rétrospective), nous avons donc ajouté le nom d'utilisateur et l'identifiant de session au NDC, pour pouvoir ensuite filtrer sur ceux-ci.

Le code était semblable au suivant:

public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        MDC.put("username", session.getParameter("username")); // or where ever t is stored
        chain.doFilter(request, response);
    }
}

Dans votre log4j.xml, cela filtre en fonction de l'utilisateur:

  <appender name="UserDebug" class="org.Apache.log4j.RollingFileAppender">
    <param name="File" value="userdebug.log"/>
    <param name="Append" value="true"/>
    <param name="MaxFileSize" value="5000KB"/>
    <param name="maxBackupIndex" value="5"/> 
          <layout class="org.Apache.log4j.PatternLayout">
                  <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] user:%X{username} %-5p - %m%n" />
          </layout>

          <filter class="org.Apache.log4j.varia.StringMatchFilter">
                  <param name="StringToMatch" value="user:fred" />
                  <param name="AcceptOnMatch" value="true" />
          </filter>

      <filter class="org.Apache.log4j.varia.DenyAllFilter"/>
  </appender>

% X {clé} renvoie la valeur de MDC.get (clé) dans un MDC. Si vous souhaitez un filtre plus complexe, vous pouvez l'étendre vous-même et consulter vous-même les valeurs du MDC.

19
Matthew Farwell

La réponse de Matthew Farwell (utiliser MDC) est celle qui a fonctionné pour moi et mentionne l'écriture de votre propre filtre. Mon besoin était de cacher les messages de journalisation dans certains cas. En particulier, nous avons un appel de vérification de l’état de santé qui est touché beaucoup plus souvent que les utilisateurs habituels et qui remplissait inutilement les journaux. La solution de Matthew ne correspond pas à ma situation car elle nécessite l'ajout du MDC à la sortie du journal. Je souhaite uniquement utiliser le MDC pour le filtrage. J'ai donc étendu org.Apache.log4j.spi.Filter avec la classe suivante:

/**
 * Log4J filter that stops certain log messages from being logged, based on a
 * value in the MDC (See Log4J docs).
 */
public class Log4JMDCFilter extends Filter
{

private String keyToMatch;
private String valueToMatch;
private boolean denyOnMatch = true;

/**
 * {@inheritDoc}
 */
public int decide(LoggingEvent event)
{
    if (keyToMatch != null && valueToMatch != null
        && valueToMatch.equals(event.getMDC(keyToMatch)))
    {
        return denyOnMatch ? DENY : ACCEPT;
    }

    return denyOnMatch ? ACCEPT : DENY;
}

/**
 * The key on which to filter.
 * 
 * @return key on which to filter
 */
public String getKeyToMatch()
{
    return keyToMatch;
}

/**
 * Sets the key on which to filter.
 * 
 * @param keyToMatch key on which to filter
 */
public void setKeyToMatch(String keyToMatch)
{
    this.keyToMatch = keyToMatch;
}

/**
 * Gets the value to match.
 * 
 * @return the value to match.
 */
public String getValueToMatch()
{
    return valueToMatch;
}

/**
 * Sets the value to match.
 * 
 * @param valueToMatch the value to match.
 */
public void setValueToMatch(String valueToMatch)
{
    this.valueToMatch = valueToMatch;
}

/**
 * Returns true if the log message should not be logged if a match is found.
 * 
 * @return true if the log message should not be logged if a match is found.
 */
public boolean isDenyOnMatch()
{
    return denyOnMatch;
}

/**
 * Set this to "true" if you do not want log messages that match the given
 * key/value to be logged. False if you only want messages that match to be
 * logged.
 * 
 * @param denyOnMatch "true" if you do not want log messages that match the
 *        given key/value to be logged. False if you only want messages that
 *        match to be logged.
 */
public void setDenyOnMatch(String denyOnMatch)
{
    this.denyOnMatch = Boolean.valueOf(denyOnMatch).booleanValue();
}

}

Utilisez l'extrait de journal log4j.xml suivant pour activer le filtre ("HEALTHCHECK" est la clé et "true" est la valeur sur laquelle je filtre):

    <filter class="com.copart.hh.core.utils.Log4JMDCFilter">
        <param name="keyToMatch" value="HEALTHCHECK" />
        <param name="valueToMatch" value="true" />
        <param name="denyOnMatch" value="true" />
    </filter>

Ensuite, partout où vous voulez marquer pour le filtrage, mettez le code comme ceci:

MDC.put("HEALTHCHECK", "true");
try
{    
      // do healthcheck stuff that generates unnecessary logs
}
finally
{
    MDC.remove("HEALTHCHECK"); // not sure this is strictly necessary
}
7
KC Baltz

J'ai trouvé ceci article de blog très utile. Cela peut vous aider à créer des conditions de journalisation pour vos utilisateurs.

1
Chris Aldrich

Je fais une solution de contournement comme suit

Configurez votre cible de journal dans log4j.xml comme suit:

    <appender name="file" class="org.Apache.log4j.RollingFileAppender">
    <param name="file" value="${catalina.home}/logs/uploader.log" />
    <param name="append" value="false" />
    <param name="threshold" value="info" />
    <layout class="org.Apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{1}] - %m%n" />
    </layout>

    <filter class="org.Apache.log4j.varia.StringMatchFilter">
        <param name="StringToMatch" value="trackfile" />
        <param name="AcceptOnMatch" value="true" />
    </filter>

     <filter class="org.Apache.log4j.varia.DenyAllFilter" />   
</appender>

créer une méthode de consignateur client qui ajoute votre balise cible [ trackfile ]

private void logfile(String msg) {
    logger.info("trackfile: " + msg);
}

enregistrez vos informations en utilisant la méthode ci-dessus:

logfile("your log message")

 enter image description here

0
RAY