Heim  >  Artikel  >  Java  >  Java-Codebeispiel für das regelmäßige Drucken von Log4j-Protokollen und das Hinzufügen der Modulnamenkonfiguration

Java-Codebeispiel für das regelmäßige Drucken von Log4j-Protokollen und das Hinzufügen der Modulnamenkonfiguration

高洛峰
高洛峰Original
2017-01-18 12:54:211285Durchsuche

Konfigurieren Sie das Intervall und drucken Sie Protokolle regelmäßig
Ich habe über log4j eine Anfrage zum regelmäßigen Drucken von Protokollen erhalten. Die Anforderung wird wie folgt beschrieben: Protokolle müssen regelmäßig gedruckt werden, und das Zeitintervall ist konfigurierbar. Apropos Timing, das erste, was mir in den Sinn kommt, ist die DailyRollingFileAppender-Klasse. Laut datePattern kann sich dies auf die SimpleDateFormat-Klasse beziehen:

'.'yyyy- MM: Monatlich

'.'yyyy-ww: wöchentlich

'.'yyyy-MM-dd: täglich

'.'yyyy-MM-dd-a: zweimal täglich

'.'yyyy-MM-dd-HH: jede Stunde

'.'yyyy-MM-dd-HH-mm: jede Minute

Durch Bei der Beobachtung wurde festgestellt, dass es kein ähnliches n-Minuten-Datumsformat gibt. Schreiben Sie daher eine benutzerdefinierte Klasse basierend auf der DailyRollingFileAppender-Klasse. Der Vorgang ist wie folgt:

1) Kopieren Sie den Quellcode der DailyRollingFileAppender-Klasse und benennen Sie ihn in MinuteRollingAppender um. Fügen Sie das Konfigurationselement „intervallTime“ hinzu und fügen Sie die Methoden „set“ und „get“ hinzu

private int intervalTime = 10;

2) Da die DailyRollingFileAppender-Klasse die RollingCalendar-Klasse verwendet, um die nächste Intervallzeit zu berechnen, und der Parameter „intervalTime“ übergeben werden muss, wird die RollingCalendar-Klasse seitdem als interne Klasse geändert Methode besteht darin, die Zeit der nächsten RollOver-Aktion basierend auf datePattern zu berechnen. Es sind keine anderen Zeitmodi erforderlich. Die Änderungsmethode ist wie folgt:

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

3) Wann Die Zeit kann nach Minuten konfiguriert werden. Der Zeitmodus muss deaktiviert, in „Static Final“ geändert und reagiert werden. Entfernen Sie die datePattern-Parameter in den get-, set-Methoden und dem MinuteRollingAppender-Konstruktor

private static String DATEPATTERN = "'.'yyyy-MM-dd-HH-mm'.log'";

Ebenso kann die Methode „computeCheckPeriod()“, die mehrere datePatterns bedient, nun auch gelöscht werden, nachdem die Transformation abgeschlossen ist. Nun sind die fertigen Produkte wie folgt:

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

Der Test Die Konfigurationsdatei lautet wie folgt:

<?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>

Informationen zur Timing-Implementierung: Sie können sie auch mit dem von Java bereitgestellten Timer implementieren, sodass keine Berechnungen erforderlich sind und vergleichen Sie die Zeit jedes Mal, wenn Sie sich anmelden. Der Unterschied besteht darin, selbst einen Thread zu starten und die rollOver-Methode aufzurufen. Die Implementierung ist wie folgt:

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

Es gibt jedoch zwei Probleme mit der obigen Implementierung:

1) Parallelität

Ein Ort, an dem Parallelitätsprobleme auftreten können, ist nach dem Aufruf von closeFile(); in run(), genau Die subAppend()-Methode schreibt Protokolle Wenn die Datei zu diesem Zeitpunkt geschlossen ist, wird der folgende Fehler gemeldet:

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)
..............................

Die Lösung ist relativ einfach: Machen Sie einfach die gesamte run()-Methode synchron und fügen Sie einfach die synchronisierte hinzu Schlüsselwort; Der Autor hat jedoch derzeit das Problem des Protokollverlusts nicht gelöst, wenn Sie wirklich schreiben möchten und die Schreibgeschwindigkeit hoch genug ist.

2) Leistung

Die Verwendung von Timer ist relativ einfach. Wenn die Ausführungszeit der Aufgabe im Timer jedoch zu lang ist, wird das Timer-Objekt monopolisiert, sodass nachfolgende Aufgaben nicht ausgeführt werden können. Die Lösung ist relativ einfach. Verwenden Sie die Thread-Pool-Version der Timer-Klasse ScheduledExecutorService wie folgt:

/**
 *
 */
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.");
   }
  }
 }
}


Was die Implementierung des Timings angeht, ist das fast alles. Standardmäßig wird alle 10 Minuten eine neue Protokolldatei generiert. Sie können es während der Konfiguration selbst festlegen, es besteht jedoch eine versteckte Gefahr: Wenn die Person, die konfiguriert, das Zeitintervall nicht kennt, wenn sie denkt, dass es sich um Sekunden handelt, weist sie 600 zu und aktiviert das Debuggen, wodurch eine Protokolldatei von G generiert wird Größe, dies wird definitiv eine Katastrophe sein. Die folgende Änderung besteht darin, RollingFileAppender zu kombinieren. Die maximale Größe und die maximale Anzahl von Sicherungsdateien können erneut konfiguriert werden und der Transformationsprozess wird beim nächsten Mal weiter beschrieben.

Modulnamenkonfiguration hinzufügen

Wie bereits erwähnt, haben wir über die benutzerdefinierte Klassenimplementierung des geplanten Druckens von log4j gesprochen. Wir werden nicht über die Angabe der Größe und Anzahl der Sicherungsdateien sprechen RollingFileAppender-Klasse und fügen Sie sie der vorherigen benutzerdefinierten Klasse hinzu. Das einzige, was gelöst werden muss, ist das Parallelitätsproblem, das heißt, wenn die Datei geschlossen und die Umbenennungsdatei geschlossen wird und ein Protokollereignis auftritt Es wird ein Fehler beim Schließen des Streams gemeldet.

Nun gibt es ein solches Anwendungsszenario, und es kommt häufig vor:

1. Das Projekt enthält mehrere verschiedene Projekte

2. Das gleiche Projekt enthält verschiedene Module.

Für den ersten Fall können Sie log4j73d3276cca4423eeeaf44766d19395f2 konfigurieren und dann beim Generieren des Loggers eine Methode ähnlich der folgenden verwenden:

Logger logger=Logger.getLogger("Test");

Für den zweiten Fall hoffen wir, verschiedene Module in derselben Protokolldatei drucken zu können, hoffen jedoch, dass wir den Modulnamen im Protokoll drucken können, damit wir das Problem lokalisieren können, wenn ein Problem auftritt , wir müssen die Konfiguration in der Appender-Klasse nach Bedarf für diesen Artikel hinzufügen. Beginnen wir mit der Transformation. Anders als beim geplanten Drucken verwenden wir die RollingFileAppender-Klasse als Basisklasse für die Transformation.

Fügen Sie zunächst das Konfigurationselement moduleName hinzu und fügen Sie get- und set-Methoden hinzu.

Da es von RollingFileAppender geerbt wird, müssen Sie nur die Daten in LoggingEvent in subAppend() formatieren und hinzufügen formatInfo-Methodenformat Der Code wird weggelassen;

Die endgültige Produktkategorie lautet wie folgt:

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

Weitere Java-Codebeispiele für den geplanten Protokolldruck von Log4j und das Hinzufügen der Modulnamenkonfiguration finden Sie auf der chinesischen PHP-Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn