Maison > Java > javaDidacticiel > le corps du texte

Exemple de code Java de Log4j imprimant régulièrement des journaux et ajoutant la configuration du nom du module

高洛峰
Libérer: 2017-01-18 12:54:21
original
1315 Les gens l'ont consulté

Configurer l'intervalle et imprimer les journaux régulièrement
J'ai reçu une demande d'impression régulière des journaux via log4j. La demande est décrite comme suit : les journaux doivent être imprimés régulièrement et l'intervalle de temps est configurable. En parlant de timing, la première chose qui me vient à l'esprit est la classe DailyRollingFileAppender. Divers timings Selon datePattern, cela peut faire référence à la classe SimpleDateFormat. Certains paramètres de timing courants sont les suivants :

'.'yyyy-. MM : Mensuel

'.'aaaa-ww : hebdomadaire

'.'aaaa-MM-jj : quotidien

'.'aaaa-MM-jj-a : deux fois par jour

'.'aaaa-MM-jj-HH : toutes les heures

'.'aaaa-MM-jj-HH-mm : toutes les minutes

À observation, il s'avère qu'il n'existe pas de format de date de n minutes similaire, par conséquent, écrivez une classe personnalisée basée sur la classe DailyRollingFileAppender. Le processus est le suivant :

1) Copiez le code source de la classe DailyRollingFileAppender et renommez-le MinuteRollingAppender Afin de le configurer dans log4j.xml, ajoutez l'élément de configuration intervalTime et ajoutez les méthodes set et get

.
private int intervalTime = 10;
Copier après la connexion

2) Étant donné que la classe DailyRollingFileAppender utilise la classe RollingCalendar pour calculer le prochain intervalle de temps et que le paramètre intervalTime doit être passé, la classe RollingCalendar est modifiée en tant que classe interne depuis sa création ; La méthode consiste à calculer l'heure de la prochaine action rollOver en fonction de datePattern, cela. Il n'y a pas besoin d'autres modes de temps. La méthode de modification est la suivante :

public Date getNextCheckDate(Date now)
{
 this.setTime(now);
 this.set(Calendar.SECOND, 0);
 this.set(Calendar.MILLISECOND, 0);
 this.add(Calendar.MINUTE, intervalTime);
 return getTime();
}
Copier après la connexion

3) Quand. l'heure peut être configurée en fonction des minutes, le mode heure doit être désactivé, le changer en final statique et répondre Supprimer les paramètres datePattern dans ses méthodes get, set et son constructeur MinuteRollingAppender

private static String DATEPATTERN = "'.'yyyy-MM-dd-HH-mm'.log'";
Copier après la connexion

De même, la méthode calculateCheckPeriod() qui sert plusieurs datePatterns peut également être supprimée ; maintenant la transformation est terminée. Les catégories de produits finis sont les suivantes :

package net.csdn.blog;
  
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
  
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
  
/**
 * 按分钟可配置定时appender
 *
 * @author coder_xia
 *
 */
public class MinuteRollingAppender extends FileAppender
{
 /**
  * The date pattern. By default, the pattern is set to "'.'yyyy-MM-dd"
  * meaning daily rollover.
  */
 private static String DATEPATTERN = "'.'yyyy-MM-dd-HH-mm'.log'";
 /**
  * 间隔时间,单位:分钟
  */
 private int intervalTime = 10;
  
 /**
  * The log file will be renamed to the value of the scheduledFilename
  * variable when the next interval is entered. For example, if the rollover
  * period is one hour, the log file will be renamed to the value of
  * "scheduledFilename" at the beginning of the next hour.
  *
  * The precise time when a rollover occurs depends on logging activity.
  */
 private String scheduledFilename;
  
 /**
  * The next time we estimate a rollover should occur.
  */
 private long nextCheck = System.currentTimeMillis() - 1;
  
 Date now = new Date();
  
 SimpleDateFormat sdf;
  
 RollingCalendar rc = new RollingCalendar();
  
 /**
  * The default constructor does nothing.
  */
 public MinuteRollingAppender()
 {
 }
  
 /**
  * Instantiate a MinuteRollingAppender and open the file
  * designated by filename. The opened filename will become the
  * ouput destination for this appender.
  */
 public MinuteRollingAppender(Layout layout, String filename)
   throws IOException
 {
  super(layout, filename, true);
  activateOptions();
 }
  
 /**
  * @return the intervalTime
  */
 public int getIntervalTime()
 {
  return intervalTime;
 }
  
 /**
  * @param intervalTime
  *   the intervalTime to set
  */
 public void setIntervalTime(int intervalTime)
 {
  this.intervalTime = intervalTime;
 }
  
 @Override
 public void activateOptions()
 {
  super.activateOptions();
  if (fileName != null)
  {
   now.setTime(System.currentTimeMillis());
   sdf = new SimpleDateFormat(DATEPATTERN);
   File file = new File(fileName);
   scheduledFilename = fileName
     + sdf.format(new Date(file.lastModified()));
  
  }
  else
  {
   LogLog
     .error("Either File or DatePattern options are not set for appender ["
       + name + "].");
  }
 }
  
 /**
  * Rollover the current file to a new file.
  */
 void rollOver() throws IOException
 {
  String datedFilename = fileName + sdf.format(now);
  // It is too early to roll over because we are still within the
  // bounds of the current interval. Rollover will occur once the
  // next interval is reached.
  if (scheduledFilename.equals(datedFilename))
  {
   return;
  }
  
  // close current file, and rename it to datedFilename
  this.closeFile();
  
  File target = new File(scheduledFilename);
  if (target.exists())
  {
   target.delete();
  }
  
  File file = new File(fileName);
  boolean result = file.renameTo(target);
  if (result)
  {
   LogLog.debug(fileName + " -> " + scheduledFilename);
  }
  else
  {
   LogLog.error("Failed to rename [" + fileName + "] to ["
     + scheduledFilename + "].");
  }
  
  try
  {
   // This will also close the file. This is OK since multiple
   // close operations are safe.
   this.setFile(fileName, true, this.bufferedIO, this.bufferSize);
  }
  catch (IOException e)
  {
   errorHandler.error("setFile(" + fileName + ", true) call failed.");
  }
  scheduledFilename = datedFilename;
 }
  
 /**
  * This method differentiates MinuteRollingAppender from its super class.
  *
  * 

* Before actually logging, this method will check whether it is time to do * a rollover. If it is, it will schedule the next rollover time and then * rollover. * */ @Override protected void subAppend(LoggingEvent event) { long n = System.currentTimeMillis(); if (n >= nextCheck) { now.setTime(n); nextCheck = rc.getNextCheckMillis(now); try { rollOver(); } catch (IOException ioe) { if (ioe instanceof InterruptedIOException) { Thread.currentThread().interrupt(); } LogLog.error("rollOver() failed.", ioe); } } super.subAppend(event); } /** * RollingCalendar is a helper class to MinuteRollingAppender. Given a * periodicity type and the current time, it computes the start of the next * interval. * */ class RollingCalendar extends GregorianCalendar { private static final long serialVersionUID = -3560331770601814177L; RollingCalendar() { super(); } public long getNextCheckMillis(Date now) { return getNextCheckDate(now).getTime(); } public Date getNextCheckDate(Date now) { this.setTime(now); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); this.add(Calendar.MINUTE, intervalTime); return getTime(); } } }

Copier après la connexion

La configuration du test Le fichier est le suivant :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
  
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  
 <appender name="myFile" class="net.csdn.blog.MinuteRollingAppender"> 
  <param name="File" value="log4jTest.log" />
  <param name="Append" value="true" />
  <param name="intervalTime" value="2"/>
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="%p %d (%c:%L)- %m%n" />
  </layout>
 </appender>
  
 <root>
  <priority value="debug"/>
  <appender-ref ref="myFile"/> 
 </root>
  
</log4j:configuration>
Copier après la connexion

À propos de l'implémentation du timing, vous pouvez également utiliser le Timer fourni par Java pour l'implémenter, ce qui élimine le besoin de calculer et comparez l'heure à chaque fois que vous enregistrez le journal. La différence est en fait de démarrer votre propre fil de discussion et d'appeler la méthode rollOver. L'implémentation est la suivante :

package net.csdn.blog;
  
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
  
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
  
public class TimerTaskRollingAppender extends FileAppender
{
 /**
  * The date pattern. By default, the pattern is set to "&#39;.&#39;yyyy-MM-dd"
  * meaning daily rollover.
  */
 private static final String DATEPATTERN = "&#39;.&#39;yyyy-MM-dd-HH-mm&#39;.log&#39;";
  
 /**
  * 间隔时间,单位:分钟
  */
 private int intervalTime = 10;
  
 SimpleDateFormat sdf = new SimpleDateFormat(DATEPATTERN);
  
 /**
  * The default constructor does nothing.
  */
 public TimerTaskRollingAppender()
 {
 }
  
 /**
  * Instantiate a <code>TimerTaskRollingAppender</code> and open the file
  * designated by <code>filename</code>. The opened filename will become the
  * ouput destination for this appender.
  */
 public TimerTaskRollingAppender(Layout layout, String filename)
   throws IOException
 {
  super(layout, filename, true);
  activateOptions();
 }
  
 /**
  * @return the intervalTime
  */
 public int getIntervalTime()
 {
  return intervalTime;
 }
  
 /**
  * @param intervalTime
  *   the intervalTime to set
  */
 public void setIntervalTime(int intervalTime)
 {
  this.intervalTime = intervalTime;
 }
  
 @Override
 public void activateOptions()
 {
  super.activateOptions();
  Timer timer = new Timer();
  timer.schedule(new LogTimerTask(), 1000, intervalTime * 60000);
 }
  
 class LogTimerTask extends TimerTask
 {
  @Override
  public void run()
  {
   String datedFilename = fileName + sdf.format(new Date());
   closeFile();
   File target = new File(datedFilename);
   if (target.exists())
    target.delete();
   File file = new File(fileName);
   boolean result = file.renameTo(target);
   if (result)
    LogLog.debug(fileName + " -> " + datedFilename);
   else
    LogLog.error("Failed to rename [" + fileName + "] to ["
      + datedFilename + "].");
   try
   {
    setFile(fileName, true, bufferedIO, bufferSize);
   }
   catch (IOException e)
   {
    errorHandler.error("setFile(" + fileName
      + ", true) call failed.");
   }
  }
 }
}
Copier après la connexion

Cependant, il y en a deux. problèmes avec l'implémentation ci-dessus :

1) Concurrence

Un endroit où des problèmes de concurrence peuvent survenir est après avoir appelé closeFile(); le fichier est fermé à ce moment, l'erreur suivante sera signalée :

java.io.IOException: Stream closed
 at sun.nio.cs.StreamEncoder.ensureOpen(Unknown Source)
 at sun.nio.cs.StreamEncoder.write(Unknown Source)
 at sun.nio.cs.StreamEncoder.write(Unknown Source)
 at java.io.OutputStreamWriter.write(Unknown Source)
 at java.io.Writer.write(Unknown Source)
..............................
Copier après la connexion

La solution est relativement simple, il suffit de rendre toute la méthode run() synchrone, d'ajouter Il suffit d'ajouter le mot-clé synchronisé ; cependant, l'auteur n'a actuellement pas résolu la situation où le journal peut être perdu si vous voulez vraiment écrire et que la vitesse d'écriture est suffisamment rapide
2) Performances

L'utilisation de Timer est relativement simple ; à implémenter.Cependant, si le temps d'exécution de la tâche dans Timer est trop long, cela monopolisera l'objet Timer, rendant les tâches suivantes incapables d'être exécutées. La solution est relativement simple. Utilisez la version pool de threads de la classe timer ScheduledExecutorService, qui s'implémente comme suit :

/**
 *
 */
package net.csdn.blog;
  
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
  
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
  
/**
 * @author coder_xia
 *   <p>
 *   采用ScheduledExecutorService实现定时配置打印日志
 *   <p>
 *
 */
public class ScheduledExecutorServiceAppender extends FileAppender
{
 /**
  * The date pattern. By default, the pattern is set to "&#39;.&#39;yyyy-MM-dd"
  * meaning daily rollover.
  */
 private static final String DATEPATTERN = "&#39;.&#39;yyyy-MM-dd-HH-mm&#39;.log&#39;";
  
 /**
  * 间隔时间,单位:分钟
  */
 private int intervalTime = 10;
  
 SimpleDateFormat sdf = new SimpleDateFormat(DATEPATTERN);
  
 /**
  * The default constructor does nothing.
  */
 public ScheduledExecutorServiceAppender()
 {
 }
  
 /**
  * Instantiate a <code>ScheduledExecutorServiceAppender</code> and open the
  * file designated by <code>filename</code>. The opened filename will become
  * the ouput destination for this appender.
  */
 public ScheduledExecutorServiceAppender(Layout layout, String filename)
   throws IOException
 {
  super(layout, filename, true);
  activateOptions();
 }
  
 /**
  * @return the intervalTime
  */
 public int getIntervalTime()
 {
  return intervalTime;
 }
  
 /**
  * @param intervalTime
  *   the intervalTime to set
  */
 public void setIntervalTime(int intervalTime)
 {
  this.intervalTime = intervalTime;
 }
  
 @Override
 public void activateOptions()
 {
  super.activateOptions();
  Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
    new LogTimerTask(), 1, intervalTime * 60000,
    TimeUnit.MILLISECONDS);
 }
  
 class LogTimerTask implements Runnable
 {
  @Override
  public void run()
  {
   String datedFilename = fileName + sdf.format(new Date());
   closeFile();
   File target = new File(datedFilename);
   if (target.exists())
    target.delete();
   File file = new File(fileName);
   boolean result = file.renameTo(target);
   if (result)
    LogLog.debug(fileName + " -> " + datedFilename);
   else
    LogLog.error("Failed to rename [" + fileName + "] to ["
      + datedFilename + "].");
   try
   {
    setFile(fileName, true, bufferedIO, bufferSize);
   }
   catch (IOException e)
   {
    errorHandler.error("setFile(" + fileName
      + ", true) call failed.");
   }
  }
 }
}
Copier après la connexion


Concernant la mise en place du timing, c'est presque tout. La valeur par défaut est de générer un nouveau fichier log chaque. 10 minutes. Vous pouvez le définir vous-même lors de la configuration, mais il existe un danger caché. Si la personne qui configure ne connaît pas l'intervalle de temps, si elle pense qu'il s'agit de secondes, elle en alloue 600 et active le débogage, générant un journal. fichier de taille G, ce sera certainement un désastre. La modification suivante consiste à combiner RollingFileAppender La taille maximale et le nombre maximum de fichiers de sauvegarde peuvent être configurés. Améliorez-le à nouveau et continuez à décrire le processus de transformation la prochaine fois.

Ajouter la configuration du nom du module
Comme mentionné précédemment, nous avons parlé de l'implémentation de la classe personnalisée de l'impression programmée log4j. Nous ne parlerons pas de la spécification de la taille et du nombre de fichiers de sauvegarde. Nous copions le code du. RollingFileAppender et ajoutez-le à la classe personnalisée précédente. C'est tout. La seule chose qui doit être résolue est le problème de concurrence, c'est-à-dire que lorsque le fichier est fermé et que le fichier renommé est fermé, et qu'un événement de journal se produit, une sortie. Une erreur de fermeture du flux sera signalée.

Il existe désormais un tel scénario d'application, et cela arrive souvent :

1. Le projet contient plusieurs projets différents

2. Le même projet contient différents modules ;

Pour le premier cas, vous pouvez configurer log4j, puis utiliser une méthode similaire à la suivante lors de la génération de Logger :

Logger logger=Logger.getLogger("Test");
Copier après la connexion

Pour le deuxième cas, nous espérons pouvoir imprimer différents modules dans le même fichier journal, mais nous espérons pouvoir imprimer le nom du module dans le journal afin de pouvoir localiser le problème en cas de problème. Par conséquent, nous devons ajouter la configuration dans la classe Appender requise pour cet article, commençons la transformation. Contrairement à l'impression planifiée, nous utilisons la classe RollingFileAppender comme classe de base pour la transformation.

Tout d'abord, ajoutez l'élément de configuration moduleName et ajoutez les méthodes get et set

Puisqu'il est hérité de RollingFileAppender, il vous suffit de formater les données dans LoggingEvent dans subAppend() et d'ajouter le formatInfo méthode format Les données sont transformées, le code est omis

La catégorie du produit final est la suivante :

package net.csdn.blog;
  
import org.apache.log4j.Category;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.spi.LoggingEvent;
  
/**
 * @author coder_xia
 *
 */
public class ModuleAppender extends RollingFileAppender
{
 private String moduleName;
  
 /**
  * @return the moduleName
  */
 public String getModuleName()
 {
  return moduleName;
 }
  
 /**
  * @param moduleName
  *   the moduleName to set
  */
 public void setModuleName(String moduleName)
 {
  this.moduleName = moduleName;
 }
  
 /**
  * 格式化打印内容
  *
  * @param event
  *   event
  * @return msg
  */
 private String formatInfo(LoggingEvent event)
 {
  StringBuilder sb = new StringBuilder();
  if (moduleName != null)
  {
   sb.append(moduleName).append("|");
   sb.append(event.getMessage());
  }
  return sb.toString();
 }
  
 @Override
 public void subAppend(LoggingEvent event)
 {
  String msg = formatInfo(event);
  super.subAppend(new LoggingEvent(Category.class.getName(), event
    .getLogger(), event.getLevel(), msg, null));
 }
}
Copier après la connexion

Pour plus d'exemples de code Java d'impression programmée de journaux Log4j et d'ajout de configuration de nom de module, veuillez faire attention au site Web PHP chinois !

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!