Java-Log4j日志高级

x33g5p2x  于2021-09-28 转载在 Java  
字(9.8k)|赞(0)|评价(0)|浏览(301)

功能 1. 安装时间分割文件 或者安装时间分割文件夹

功能2. 定时清理策略

功能3. 文件大小限制 超过了的文件放入备份区 然后清空原文件内容

特别注意:如果出现文件路径问题那么你去修改 log4jAppender.java 最后一个方法

public synchronized void setFile 这个里面的内容 其他的都不用动

我们直接上代码后在详细讲解

log4jAppender.java

package cn.xh.utils;

import org.apache.log4j.FileAppender;

import java.io.*;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
// log4j 扩展
public class log4jAppender  extends FileAppender {
    // 清除策略 满足条件的 文件 自动会清除空文件
    // 0. 清除5前的 所有文件
    // 1. 清除10天前的 所有文件
    // 2.清除20天前的 所有文件
    // 3.清除30天前的 所有文件
    // 4.清除3天前的 所有文件
    // 其他 长久保存 所有文件
    private int clear = 4;

    //如果文件大小 超过了 规定的值 那么就会将原文件 存放进当前目录下的 备份区域 backups 备份区内文件大小没有限制
    // 然后清空原文件内容 原文件的 限制大小最好不要低于100kb 大概1000行 内容 内容太大了你也看不过来不 (默认就行)
    //注意有的时候发现文件大小 超过的设置的 那是因为 一次性写入的数据太多 在写入过程中是无法 操作的
    //只有当写入完成后 才能判断 文件大小是否超过了设置的值 然后才能进行备份
    // 所以 这个不影响 也就第一次加载 程序的时候会出现这个问题 会将程序的很多信息一次性输入
    //还有一种情况就是 时间到了 没有在写入了 换文件了
    private  int size_file= 1024*300;    //1024*300; //100KB 大概3000行到6000行

    //是否 按照 日期切割文件夹 按照 年 月 日 时 来切割文件夹 true 开始 fasle关闭
    //如果true 那么日志就以 年 月 日 时 来分割文件夹 以分区分文件(可选)
    //如果false 那么 就以年月日 来区分文件 但是不分割文件夹了
    private boolean datefile = true;
    //日志保留后的 文件名称 默认就行 或者自行修改
    private String filem = "log.txt";

    // 2个选择 一个是存放在 服务器的bin目录下 (true)
    // 一个是存放在服务器项目里中根目录内(flase)
    // 如果不在服务器中 那么 以上两个选项 第一个是在当前项目根目录 第二个就在out目录 
    private  boolean  path_xz=false;
    @Override
    public  void setFile(String file) {

        file=file.trim();

        if (!(file.endsWith("\\") || file.endsWith("//"))) {
            file += "\\";
        }
        String substring = file.substring(0, 1);
        if ((file.startsWith("\\") || file.startsWith("//")||substring.equals("/"))) {
            if (file.startsWith("\\")||substring.equals("/")) {
                file = file.substring(1);
            } else {
                file = file.substring(2);
            }
        }

        //清除策略
        cler_file(file);
        Date dNow = new Date();
        // 按照 时间来分割 文件夹 然后 文件夹下的文件以 分切割
        if (datefile) {
            SimpleDateFormat ft1 = new SimpleDateFormat("yyyy-MM-dd");
            file += ft1.format(dNow) + File.separator+ dNow.getHours() + "-Hour"+File.separator;
            //如果不想按照分钟来分割文件可以注释掉
            filem = dNow.getMinutes() + "-Minute" + "_" + filem;
        }
        //当不采用分割文件夹的方式 那么就采用 日期文件分割 按照 年 月 日
        if (!datefile) {
            SimpleDateFormat ft2 = new SimpleDateFormat("yyyy-MM-dd");
            filem = ft2.format(dNow) + "_" + filem;
        }

        file += filem;
        file = Paths.get(file).toString();
        //备份策略
        size_file(file);
        super.setFile(file);
    }


    //清理策略
    public void cler_file(String folder) {
//当前时间
        File file = new File(folder);
        if (file.exists()) {
            der(file.getPath());
        }
    }

    //计算时间差
        private long getDatePoor (Date endDate, Date nowDate){
            long nd = 1000 * 24 * 60 * 60;
            // 获得两个时间的毫秒时间差异
            long diff = endDate.getTime() - nowDate.getTime();
            // 计算差多少天
            long day = diff / nd;

            return day;
        }

//清理 时间
        private  boolean switch_day(long day){
        switch (clear){
            case 0:
                if (day>5){
                    return true;
                }
                break;
            case 1:
                if (day>10){
                    return true;
                }
                break;
            case 2:
                if (day>20){
                    return true;
                }
                break;
            case 3:
                if (day>30){
                    return true;
                }
                break;
            case 4:
                if (day>40){
                    return true;
                }
                break;
        }
        return false;

    }

//删除 超过时间的文件
    private    boolean der(String path){
        File delfile = new File(path);
        try {
            File[] files=delfile.listFiles();
            for(int i=0;i<files.length;i++){
                if (files[i].isDirectory()){
                    der(files[i].getPath());
                }
                Date date1 = new Date();
                Date date = new Date(files[i].lastModified());
                long datePoor = getDatePoor(date1,date);
                boolean b = switch_day(datePoor);
                if(b){
                boolean result=files[i].delete();
                    //如果文件被java占用 导致无法删除 使用强制删除
                    if (!result) {
                        System.gc();    //回收资源
                        files[i].delete();
                    }
                }
             if (files[i].isFile()){
            //当文件内容为空的时候也删除
                BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(files[i].getPath()))));
                if (br.readLine()==null){
                    br.close();
                    boolean result=files[i].delete();
                    //如果文件被java占用 导致无法删除 使用强制删除
                    if (!result) {
                        System.gc();    //回收资源
                        files[i].delete();
                    }
                }
        }

            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        //将文件夹也 一起删除
        delfile.delete();
        return true;
    }

// 超过设置的文件大小 备份文件
    public  void size_file(String file){
        File file1=new File(file);
        if (!file1.isAbsolute()){
            file1=new File(file1.getAbsolutePath());
        }
        if(file1.exists()){
            if (file1.length()>=size_file){
                String newPaht=file1.getParent()+ File.separator+"backups"+File.separator;
                File file2 = new File(newPaht);
                if (! file2.exists()){
                    file2.mkdirs();
                   }
                   newPaht+=file1.getName();
                String yuan_path=file1.getPath();
                copyfile(yuan_path, newPaht );
                //将原文件清空
                try {
                    BufferedWriter   bw = new BufferedWriter(new OutputStreamWriter(
                            new FileOutputStream(new File(file))));
                    bw.write("");
                    bw.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }



    }

// 将备份的内容 赋值到 备份区
    private void copyfile(String oldPath,String newPaht ){

        if (!new File(oldPath).exists()){
            System.out.println("文件路径不存在");
            return;
        }

        File file = new File(oldPath);
        if (file.isFile()) {
            try(
                    BufferedReader   br = new BufferedReader(new InputStreamReader(new FileInputStream(
                            new File(oldPath))));
                    BufferedWriter   bw = new BufferedWriter(new OutputStreamWriter(
                            new FileOutputStream(new File(newPaht),true)));
            ) {

                char[] c = new char[4096];
                int len = 0;
                while ((len = br.read(c)) != -1) {
                    bw.write(c, 0, len);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }else{
            System.out.println("这不是文件类型 ");
        }
    }

    //多线程调度
    @Override
    public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException {
        File file=new File(fileName);
        String path=fileName;
        if (!file.isAbsolute()){ //判断是否是绝对路径F

            if ( path_xz){
                path=System.getProperty("user.dir")+File.separator+fileName;
            }else{
                path=Paths.get(new File(new File(log4jAppender.class.getResource("/").getPath()).getParent()).getParent() +File.separator+fileName).toString();
            }
        }

        System.out.println("\n\n"+ "日志文件存放位置"+path+"\n\n");
        super.setFile(path, append, bufferedIO, bufferSize);
    }
}

log4jutils.java

package cn.xh.utils;

import org.apache.log4j.Logger;

import java.io.*;

//log4j 工具类
public class log4jutils {

    public static Logger logger = Logger.getLogger(log4jutils.class);

    // 将指定的日志文件打印到 控制台中
    public static void   showLog(String  path){
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(path))));
            String lin = br.readLine();
            while (lin != null) {
                System.out.println(lin);
                lin = br.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

log4j.properties

## 设置Logger输出级别和输出目的地(可以指定多个目的地) ###
### 一般在开发的时候使用debug,开发完成后使用error ###
### 他们对应的是输出信息的级别,级别越低信息输出越详细,使用debug级别的时候,info中的信息也能输出,使用info的时候,debug对应的信息显示不出来 ###
### 日志记录器输出级别:fatal>error>warn>info>debug ###
### 后面的两个对应下方的两处 一处打印在控制台 另一处打印在日志文件里
log4j.rootLogger=debug,console,logFile
#log4j.rootLogger=error,logFile
#############################################################################################
### 把日志信息输出到控制台 ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
### 信息打印到System.err上,红色 ###
log4j.appender.console.Target=System.out
### 指定日志在控制台上输出的布局类型  采用指定格式的类型 ###
log4j.appender.console.layout=org.apache.log4j.PatternLayout
### %r:输出自应用启动到输出该 log信息耗费的毫秒数    %x表示信息输出时左对齐 
### %5p:%p表示输出日志信息优先级,即 DEBUG, INFO, WARN, ERROR, FATAL 中间的5控制最小的宽度为5
### %F:%L %F:输出日志消息产生时所在的文件名称   %L:输出代码中的行号
### %l:输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及行数
### %m:输出代码中指定的消息,产生的日志具体信息  %n:输出一个回车换行符, Windows 平台为"\r\n", Unix 平台为"\n"输出日志信息换行
log4j.appender.console.layout.ConversionPattern=-> (%r ms) -  [%-d{yyyy-MM-dd HH:mm:ss}]  %x[%5p] %M %l %m%n
#############################################################################################
#############################################################################################
### 把日志信息输出到文件:log.log 注意:如果有路径\符号一定要写成\\ 否则会被转义 路径不能有中文(否则有可能找不到) ###  cn.xh.utils.log4jAppender
log4j.appender.logFile=cn.xh.utils.log4jAppender
### 指定日志输出的路径   绝对路径相对路径都行  相对路径是在当前项目
### 相对路径 比如:logs会在当前项目 根目录创建 logs目录  然后日志都保存在里面  或者这样/src/main/resources/logs/都行
### 如果是多工程 项目那么 他的根路径的最顶层的父级    如果想要在父级里的工程内创建那么 子级工程名称/logs  自己测试下 在junit中和main中都不同
###  或者使用绝对路径   比如 C:\\Users\\12841\\Desktop\\log\\   会在这个目录下 存放日志
log4j.appender.logFile.File=logs
### 指定转换模式 ###
log4j.appender.logFile.layout=org.apache.log4j.PatternLayout
### 指定日志布局类型 ###
###log4j.appender.logFile.layout.ConversionPattern=-> %d{yyyy-MM-dd HH:mm:ss}%l %F %p %m%n
###log4j.appender.logFile.layout.ConversionPattern=-> (%r ms) - %d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n
log4j.appender.logFile.layout.ConversionPattern=-> (%r ms) - [%-d{yyyy-MM-dd HH:mm:ss}]  %x[%5p] %M %l %m%n
#############################################################################################

这个文件里的log4j.appender.logFile.File=logs 不要动 一般 都会 将日志保留在当前项目下 logs文件里 自动会创建的

在服务器上也一样 测试过了

我们先介绍下 log4jAppender.java 和 log4jutils.java是什么

log4jAppender.java 继承 FileAppender 类 来 自定义一些 日志处理的

主要介绍 几个属性

log4jAppender.java 介绍

清理策略

private int clear = 1;

清除策略 清除满足条件的文件 会自动会清除空文件 主要是防止日志堆积
// 0. 清除5前的 所有文件
// 1. 清除10天前的 所有文件
// 2.清除20天前的 所有文件
// 3.清除30天前的 所有文件
// 4.清除3天前的 所有文件
// 其他 长久保存 所有文件

控制文件大小

private int size_file=1024/*300; //KB

如果文件大小 超过了 规定的值 那么就会将原文件 存放进当前目录下的 备份区域 backups 备份区内文件大小没有限制 然后清空原文件内容

有可能会发生几种情况

当一次写入的内容大于 设置的大小 不会立马 备份 而是等待下次 (防止内容丢失)

当满足第一条的 同时 此时 时间超过文件规定的写入时间 那么也不会备份因为日志的 执行目标切换了 当然是可以解决的但是会降低速度 如果解决上面问题 那么就需要每次插入的时候 都遍历全部的日志文件 来判断大小然后备份 这样不太理想

切割方式

private boolean datefile = true;

是否 按照 日期切割文件夹 按照 年 月 日 时 来切割 文件夹

true 开始 fasle关闭
如果true 那么日志就以 年 月 日 时 来分割文件夹 以分区分文件(可选)
如果false 那么 就以年月日 来区分文件 但是不分割文件夹了

日志名称

日志保留后的 文件名称 默认就行 或者自行修改
private String filem = “log.txt”;

存放位置

2个选择 一个是存放在 服务器的bin目录下 (true)
一个是存放在服务器项目里中根目录内(flase)
如果不在服务器中 那么 以上两个选项 第一个是在当前项目根目录 第二个就在out目录 所以选择true
private boolean path_xz=false;

以上全部属性的效果图

清理策略效果图

我们先运行log 添加几条日志 然后将本地时间修改

修改本地时间前:

我这里使用的是默认1 清除10天后的日志 以23日为基础 将时间调整10天后

也就是 11月4日 他会将 10月22 和 10月23 里的文件全部删除 包括他本身

控制文件大小效果图

当写入文件超过了 设置的值默认的是300kb 那么他就会发生切割 在当目录下创建一个backups文件夹存储备份的内容 原来的文件将被清空 然后如果后面还有日志将会继续在原文件里写 直到在次超过了设置的值 备份里的文件和原文件名一样 备份的文件没有大小限制 使用的是追加的方式 原文件只要超过了设置的值内容就会被追加到备份文件里

第一次创建的

当内容超过了 设置的值 那么 就会在backups文件夹里创建一个 然后……上面有介绍

切割方式

默认的是false 用于在 开发阶段好找 但是在项目上线后 尽量改为true

这样的话知道什么时候发生的错误就能在对应的文件夹里找到

设置为false 效果图

设置为true 的效果图

Hour (小时) Minute(分钟)

日志名称

这就不用介绍了 就是文件名称

log4g使用方法

在log4j.properties 的配置文件里找到

​ 两种配置

​ 第一种开发阶段 log4j.rootLogger= debug,console,logFile

​ 第二种上线阶段log4j.rootLogger=error,logFile

日志级别fatal>error>warn>info>debug 只会输出他本身和大于他级别的的日志

console 是将日志打印在控制台

logFile将日志写入到文件中

log4j.appender.logFile= log4jAppender文件的地址 (必须修改 为你当前的位置)

log4j.appender.logFile.File =logs 你要将日志存放的位置 如果不修改 默认是在当前项目下创建一个logs文件夹存放日志

然后将log4jutils文件添加在项目中就可以使用了

log4jutils.logger.fatal(“aaaa”);
log4jutils.logger.error(“aaaa”);
log4jutils.logger.warn(“aaaaa”);
log4jutils.logger.info(“aaaa”);
log4jutils.logger.debug(“aaaa”);

log4jutils文件介绍

这个文件主要就是针对日志的使用 和 将日志打印到控制台中

在IDEA 控制台中找 问题比较好找 有类路径导航 和颜色区分 不像是在文本文件中看得头疼

你可以能会想 我做一个日志分割把 如果分好的话还行 分不好的话 缺胳膊少腿的 导致日志都看不懂了

而且不同级别显示出来的日志排版也不同 很乱的 所以就别做分割了 把日志文件内的信息显示在 IDEA控制台中看还是比较简单的

相关文章