Home Java javaTutorial Java code example of Log4j regularly printing logs and adding module name configuration

Java code example of Log4j regularly printing logs and adding module name configuration

Jan 18, 2017 pm 12:54 PM
log4j

Configure interval time, print logs regularly
I received a requirement to print logs regularly through log4j. The requirement is described as follows: logs need to be printed regularly, and the time interval is configurable. Speaking of timing, the first thing that comes to mind is the DailyRollingFileAppender class. Various timings. According to datePattern, this can refer to the SimpleDateFormat class. Some common timing settings are as follows:

'.'yyyy-MM: Monthly

'.'yyyy-ww: weekly

'.'yyyy-MM-dd: daily

'.'yyyy-MM-dd-a: twice a day

'.'yyyy-MM-dd-HH: every hour

'.'yyyy-MM-dd-HH-mm: every minute

Through observation, it is found that there are no similar n minutes date format, therefore, write a custom class based on the DailyRollingFileAppender class. The process is as follows:

1) Copy the DailyRollingFileAppender class source code and rename it MinuteRollingAppender. In order to configure it in log4j.xml, add the configuration item intervalTime and add set and get methods;

private int intervalTime = 10;
Copy after login

2) Since the DailyRollingFileAppender class uses the RollingCalendar class to calculate the next interval time, and needs to pass the parameter intervalTime, the RollingCalendar class is modified as an internal class; since its method is to calculate the time of the next rollOver action based on datePattern, there is no need to If you need other time modes, the modification method is as follows:

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

3) If the time can be configured according to minutes, the time mode needs to be disabled, change it to static final, and remove its get accordingly , the datePattern parameter in the set method and the MinuteRollingAppender constructor

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

Similarly, the method computeCheckPeriod() that serves multiple datePatterns can also be deleted; At this point, the transformation is complete, and the finished class is as follows:

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

Copy after login

The test configuration file is as follows:

<?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>
Copy after login

Regarding timing implementation, you can also use the Timer implementation provided by java. This eliminates the need to calculate and compare the time each time the log is recorded. The difference is actually starting a thread yourself and calling the rollOver method. The implementation is as follows:

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

However, there are 2 problems with the above implementation. Questions:

1) Concurrency

One place where concurrency problems may occur is after calling closeFile(); in run(), the subAppend() method happens to write the log, and the file is closed at this moment. The following error will be reported:

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)
..............................
Copy after login

The solution is relatively simple, just make the entire run() method synchronous and add the synchronized keyword; however, the poster has not yet solved the problem. To write, and the writing speed is fast enough, the log may be lost;
2) Performance

It is relatively simple to use Timer to implement, but if the execution time of the task in Timer is too long, it will monopolize Timer object, making it impossible to execute subsequent tasks. The solution is relatively simple. Use the thread pool version of the timer class ScheduledExecutorService, which is implemented as follows:

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


About the implementation of timing, that’s almost it. The default is to generate a new log file every 10 minutes. You can set it yourself during configuration. However, there is a hidden danger in case the person who configures does not know that the time interval is minutes. , if you think it is seconds, allocate 600, turn on debug, and generate a log file of GB, this is definitely a disaster. The following modification is to combine the maximum size of RollingFileAppender and the maximum number of backup files that can be configured, and improve it again. The description of the transformation process will continue next time.

Add module name configuration
I talked about the custom class implementation of log4j scheduled printing earlier. I will not talk about specifying the size and the number of backup files. I copy the code from the RollingFileAppender class and add it to the previous custom class. That’s it. The only thing that needs to be solved is the concurrency problem, that is, when the file is closed and the rename file is closed, and a log event occurs, an output stream closed error will be reported.

Now there is such an application scenario, and it often happens:

1. The project contains multiple different projects;

2. The same project contains different modules.

For the first case, you can configure log4j, and then use a method similar to the following when generating Logger:

Logger logger=Logger.getLogger("Test");
Copy after login

For the second case In the two cases, we hope to be able to print different modules to the same log file, but we hope to be able to print the module name in the log so that we can locate the problem when there is a problem. Therefore, we need to add the configuration ModuleName to the Appender class as needed in this article. Let's start the transformation. Different from scheduled printing, we use the RollingFileAppender class as the base class for transformation.

First, add the configuration item moduleName, and add get and set methods;

Since it is inherited from RollingFileAppender, you only need to format the data in LoggingEvent in subAppend() and add the formatInfo method format Transformed data, the code is omitted;

The final product category is as follows:

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));
 }
}
Copy after login

For more Java code examples of Log4j scheduled log printing and adding module name configuration, please pay attention to the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to simplify field mapping issues in system docking using MapStruct? How to simplify field mapping issues in system docking using MapStruct? Apr 19, 2025 pm 06:21 PM

Field mapping processing in system docking often encounters a difficult problem when performing system docking: how to effectively map the interface fields of system A...

How to elegantly obtain entity class variable names to build database query conditions? How to elegantly obtain entity class variable names to build database query conditions? Apr 19, 2025 pm 11:42 PM

When using MyBatis-Plus or other ORM frameworks for database operations, it is often necessary to construct query conditions based on the attribute name of the entity class. If you manually every time...

Is the company's security software causing the application to fail to run? How to troubleshoot and solve it? Is the company's security software causing the application to fail to run? How to troubleshoot and solve it? Apr 19, 2025 pm 04:51 PM

Troubleshooting and solutions to the company's security software that causes some applications to not function properly. Many companies will deploy security software in order to ensure internal network security. ...

How to safely convert Java objects to arrays? How to safely convert Java objects to arrays? Apr 19, 2025 pm 11:33 PM

Conversion of Java Objects and Arrays: In-depth discussion of the risks and correct methods of cast type conversion Many Java beginners will encounter the conversion of an object into an array...

In back-end development, how to distinguish the responsibilities of the service layer and the dao layer? In back-end development, how to distinguish the responsibilities of the service layer and the dao layer? Apr 19, 2025 pm 01:51 PM

Discussing the hierarchical architecture in back-end development. In back-end development, hierarchical architecture is a common design pattern, usually including controller, service and dao three layers...

How to restrict access to specific interfaces of nested H5 pages through OAuth2.0's scope mechanism? How to restrict access to specific interfaces of nested H5 pages through OAuth2.0's scope mechanism? Apr 19, 2025 pm 02:30 PM

How to use OAuth2.0's access_token to achieve control of interface access permissions? In the application of OAuth2.0, how to ensure that the...

How to correctly divide business logic and non-business logic in hierarchical architecture in back-end development? How to correctly divide business logic and non-business logic in hierarchical architecture in back-end development? Apr 19, 2025 pm 07:15 PM

Discussing the hierarchical architecture problem in back-end development. In back-end development, common hierarchical architectures include controller, service and dao...

How do I convert names to numbers to implement sorting and maintain consistency in groups? How do I convert names to numbers to implement sorting and maintain consistency in groups? Apr 19, 2025 pm 11:30 PM

Solutions to convert names to numbers to implement sorting In many application scenarios, users may need to sort in groups, especially in one...

See all articles