最近做项目的时候,遇到了日志的输出问题。我们想按小时输出日志,同时最多保存7天的日志。log4j本身自带的appender如下:
org.apache.log4j.ConsoleAppender 输出到控制台
org.apache.log4j.FileAppender 输出到文件
org.apache.log4j.DailyRollingFileAppender
org.apache.log4j.RollingFileAppender 文件大小到达指定尺寸的时候产生一个新的文件
按小时输出可以使用DailRollingFileAppender,但是这个类没法限制文件的数量。如果继承该类,该类输出日志的方法rollover是default,无法重写。参考了其他博主的思路,DailRollingFileAppender继承了FileAppender,我们可以自己写一个类,继承FileAppender,代码还是用DailRollingFileAppender的代码,在其中添加限制文件保存天数的方法。
CustomLogAppender 类代码如下:
import org.apache.log4j.FileAppender;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.text.SimpleDateFormat;
import java.util.*;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
* @author
* @date 2018/10/30 16:41
* 按小时滚动日志,指定保存的时间,删除超出时间的日志
public class CustomLogAppender extends FileAppender {
static final int TOP_OF_TROUBLE = -1;
static final int TOP_OF_MINUTE = 0;
static final int TOP_OF_HOUR = 1;
static final int HALF_DAY = 2;
static final int TOP_OF_DAY = 3;
static final int TOP_OF_WEEK = 4;
static final int TOP_OF_MONTH = 5;
private String datePattern = "'.'yyyy-MM-dd";
private String scheduledFilename;
/** 最多保存天数,默认七天 */
private int maxBackupIndex = 7;
private long nextCheck = System.currentTimeMillis() - 1L;
Date now = new Date();
SimpleDateFormat sdf;
SimpleDateFormat newSdf = new SimpleDateFormat("'.'yyyy-MM-dd");
RollingCalendar rc = new RollingCalendar();
int checkPeriod = -1;
static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
public CustomLogAppender() {
public CustomLogAppender(Layout layout, String filename, String datePattern) throws IOException {
super(layout, filename, true);
this.datePattern = datePattern;
this.activateOptions();
public void setDatePattern(String pattern) {
this.datePattern = pattern;
public String getDatePattern() {
return this.datePattern;
public void activateOptions() {
super.activateOptions();
if (this.datePattern != null && this.fileName != null) {
this.now.setTime(System.currentTimeMillis());
this.sdf = new SimpleDateFormat(this.datePattern);
int type = this.computeCheckPeriod();
this.printPeriodicity(type);
this.rc.setType(type);
File file = new File(this.fileName);
this.scheduledFilename = this.fileName + this.sdf.format(new Date(file.lastModified()));
} else {
LogLog.error("Either File or DatePattern options are not set for appender [" + this.name + "].");
void printPeriodicity(int type) {
switch(type) {
case 0:
LogLog.debug("Appender [" + this.name + "] to be rolled every minute.");
break;
case 1:
LogLog.debug("Appender [" + this.name + "] to be rolled on top of every hour.");
break;
case 2:
LogLog.debug("Appender [" + this.name + "] to be rolled at midday and midnight.");
break;
case 3:
LogLog.debug("Appender [" + this.name + "] to be rolled at midnight.");
break;
case 4:
LogLog.debug("Appender [" + this.name + "] to be rolled at start of week.");
break;
case 5:
LogLog.debug("Appender [" + this.name + "] to be rolled at start of every month.");
break;
default:
LogLog.warn("Unknown periodicity for appender [" + this.name + "].");
int computeCheckPeriod() {
RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.getDefault());
Date epoch = new Date(0L);
if (this.datePattern != null) {
for(int i = 0; i <= 5; ++i) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(this.datePattern);
simpleDateFormat.setTimeZone(gmtTimeZone);
String r0 = simpleDateFormat.format(epoch);
rollingCalendar.setType(i);
Date next = new Date(rollingCalendar.getNextCheckMillis(epoch));
String r1 = simpleDateFormat.format(next);
if (r0 != null && r1 != null && !r0.equals(r1)) {
return i;
return -1;
void rollOver() throws IOException {
if (this.datePattern == null) {
this.errorHandler.error("Missing DatePattern option in rollOver().");
} else {
String datedFilename = this.fileName + this.sdf.format(this.now);
if (!this.scheduledFilename.equals(datedFilename)) {
this.closeFile();
File target = new File(this.scheduledFilename);
if (target.exists()) {
target.delete();
File file = new File(this.fileName);
boolean result = file.renameTo(target);
if (result) {
LogLog.debug(this.fileName + " -> " + this.scheduledFilename);
} else {
LogLog.error("Failed to rename [" + this.fileName + "] to [" + this.scheduledFilename + "].");
// 删除过期文件
if (maxBackupIndex > 0) {
File folder = new File(file.getParent());
List<String> maxBackupIndexDates = getMaxBackupIndexDates();
for (File ff : folder.listFiles()) {
// 遍历目录,将日期不在备份范围内的日志删掉
if (ff.getName().startsWith(file.getName()) && !ff.getName().equals(file.getName())) {
// 获取文件名带的日期时间戳
String marked = ff.getName().substring( file.getName().length());
String markedDate = marked.substring(0,marked.lastIndexOf("-"));
if (!maxBackupIndexDates.contains(markedDate)) {
result = ff.delete();
if (result) {
LogLog.debug(ff.getName() + " -> deleted ");
} else {
LogLog.error("Failed to deleted old DayRollingFileAppender file :" + ff.getName());
try {
this.setFile(this.fileName, true, this.bufferedIO, this.bufferSize);
} catch (IOException var6) {
this.errorHandler.error("setFile(" + this.fileName + ", true) call failed.");
this.scheduledFilename = datedFilename;
* 根据maxBackupIndex配置的保存天数,获取要保留log文件的日期范围集合
* @return list<'fileName+yyyy-MM-dd'>
List<String> getMaxBackupIndexDates() {
List<String> result = new ArrayList<String>();
if (maxBackupIndex > 0) {
for (int i = 0; i < maxBackupIndex; i++) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(System.currentTimeMillis()));
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
// 注意MILLISECOND,毫秒也要置0...否则错了也找不出来的
calendar.add(Calendar.DATE, -i);
result.add(newSdf.format(calendar.getTime()));
return result;
protected void subAppend(LoggingEvent event) {
long n = System.currentTimeMillis();
if (n >= this.nextCheck) {
this.now.setTime(n);
this.nextCheck = this.rc.getNextCheckMillis(this.now);
try {
this.rollOver();
} catch (IOException var5) {
if (var5 instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
LogLog.error("rollOver() failed.", var5);
super.subAppend(event);
public int getMaxBackupIndex() {
return maxBackupIndex;
public void setMaxBackupIndex(int maxBackupIndex) {
this.maxBackupIndex = maxBackupIndex;
//这个类是org.apache.log4j里的私有类,外类无法引用,所以我在这里完全copy了出来
class RollingCalendar extends GregorianCalendar {
//private static final long serialVersionUID = -3560331770601814177L;
int type = -1;
RollingCalendar() {
RollingCalendar(TimeZone tz, Locale locale) {
super(tz, locale);
void setType(int type) {
this.type = type;
public long getNextCheckMillis(Date now) {
return this.getNextCheckDate(now).getTime();
public Date getNextCheckDate(Date now) {
this.setTime(now);
switch(this.type) {
case 0:
this.set(13, 0);
this.set(14, 0);
this.add(12, 1);
break;
case 1:
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
this.add(11, 1);
break;
case 2:
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
int hour = this.get(11);
if (hour < 12) {
this.set(11, 12);
} else {
this.set(11, 0);
this.add(5, 1);
break;
case 3:
this.set(11, 0);
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
this.add(5, 1);
break;
case 4:
this.set(7, this.getFirstDayOfWeek());
this.set(11, 0);
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
this.add(3, 1);
break;
case 5:
this.set(5, 1);
this.set(11, 0);
this.set(12, 0);
this.set(13, 0);
this.set(14, 0);
this.add(2, 1);
break;
default:
throw new IllegalStateException("Unknown periodicity type.");
return this.getTime();
然后在log4j.properties里使用这个appender。
log4j.rootLogger=INFO,CONSOLE,day
log4j.appender.day=改类所在路径.CustomLogAppender
log4j.appender.day.File=日志输出路径/日志名.log
log4j.appender.day.Threshold=INFO
#日志的后缀名格式,按小时
log4j.appender.day.DatePattern='.'yyyy-MM-dd-HH
#最多保存7天
log4j.appender.day.MaxBackupIndex=7
log4j.appender.day.Append=true
log4j.appender.day.layout=org.apache.log4j.PatternLayout
log4j.appender.day.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}][%l] %m%n
原文地址:http://www.shanhh.com/set_max_count_of_log_files_for_log4j/
每天生成一个log4j日志文件,如果只需要将最近一段时间内的日志文件保留,以前或更早的文件不用保留。例如只保留最近一周的日志,日志文件保留3天等等这些。。。通过这个jar包就可以实现。
log4j.properties文件在包中,拷贝出来用就可以了
在开发中,需要调用日志记录的方法时, 不应该直接调用日志实现类,而是调用日志抽象层定义的方法接口
SLF4J+默认实现logback的简单使用//导入SLF4J,lo...
当log4j使用DailyRollingFileAppender进行日志归档时,需要对日志的个数进行控制。
此时需要对DailyRollingFileAppender进行重写。
package org.apache.log4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
因为现场的定时调度框架是前辈写的,前辈已高就,现场使用因为涉及到一天几亿数据需要提取出来,转换为其他表数据,所以日志量特别大,1天10个G,长此以往肯定不行的,找了很多网上的教程都不行,最后发现,这样就可以删除3天的日志,目的达到,备忘分享
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="console" tar
@[TOC]完整且可用log4j2.xml文件配置
1.文件生成目录为:本项目的logfiles自定义目录,完整xml中是按没小时生成。
<?xml version="1.0" encoding="UTF-8"?>
<!-- status=debug 可以查看log4j的装配过程 -->
<configuration status="off" monitorInterval="1800">
<!-- Log4j 2 包含了基于LMAX 分离库的下一代的异步日志系
一、之前的文章中有log4j的相关配置以及属性的介绍,下面我们先把配置列出来:log4j.rootLogger=INFO,file2,file3#file2----------log4j.appender.file2=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.file2.Append=truelog4j.appender.fil...
https://www.cnblogs.com/xishuai/p/spring-boot-log4j2.html
https://blog.csdn.net/shope9/article/details/87379255
<dependency>
<groupId>org.springframew...
一般来说,log4j.jar中提供的日志都是按天保存,并且当天第一次启动项目,去生成前一天的日志,并且Info和error日志信息都保存在一起,对于日志都没有分类,不好管理与查看;
现如今重写 log4j.jar中的DailyRollingFileAppender.java,让日志按照自己约定的方式,满足客户的需求;
重写的类名更改为:CustomDailyRollingFileAppender.java
日志生成要求:
第一种方式: 日志按天生成,按月保存到一个文件夹中,日志...
发现一个令人大吃一惊的事情,每台机器是40G硬盘,日志文件占了32G,-,-
至于为什么部分请求成功呢?因为20台还没有所有机器都满。
首先肯定是删除一波日志文件解决问题,发现info的日志有部分几天高达有2G的大小,这谁顶的住,应该是线上出了bug,然后疯狂打日志(内网环
import java.io.IOException;
import java.io.InterruptedIOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Ma
日志文件大小1MB,每天最多保留10个文件,只保留7天的日志
rollingRandomAccessFile: # 日志文件Appender,将日志信息输出到日志文件
- name: ROLLING_FILE
fileName: ${logPath}/${projectName}.log
filePattern: "${logPath}/historyLogs/$${date:yyyy-MM}/${projectName}-%d{yyyy-MM-dd}-%i.log.gz"
log4j案例5—每小时生成一个日志文件创建项目项目名称:006-log4j-demo项目的pom.xml文件xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0....
由于日志服务一般都在ApplicationContext创建前就初始化了,它并不是必须通过Spring的配置文件控制。因此通过系统属性和传统的SpringBoot外部配置文件依然可以很好的支持日志控制和管理。在类路径下放置自定义日志配置xml文件,SpringBoot就不会使用它本身的默认日志配置了。下图是SpringBoot官方文档的提示内容,意思是根据您的日志记录系统,将加载相应的文件使用。Appender是负责写日志的组件,设置日志信息的去向,常用的有以下几个(1)loggerlevel。...
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.LoggingEvent;
import java.io.*;
import java.
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="log" />
在做项目中基本上都用到了日志log,那么自然就会遇到问题,比方说,日志太大了怎么办?日志很乱怎么办?
这时我们就会想到如果能够把日志分为定时的生成即:Log4j每天、每小时、每分钟定时生成日志文件这样多好,既可以方便存储亦利于查看。针对这个一想法,本人做了下测试,发现效果还是可以的,特把代码以及配置贴出以便初学者参考:
一、首先添加必须的jar文件,如:commons-logging.jar、