JAVA中自定义日志输出到指定文件



虽然JAVA日志包提供的功能已经很方便,但是假如我们有新的需求如:将日志文件保存到我们希望的位置并在日志文件名中添加日期且保存指定时间内的日志文件;按照自己希望的格式输出日志内容。对于这些需求我们只要扩展java.util.logging.StreamHandler(Handler的子类),java.util.logging.Formatter创建自定义的处理器及格式化器即可以实现。下面是个例子,它分别创建了Handler及Formatter的子类,以便实现将日志文件保存到我们需要的位置,及在日志文件名中保存日志的生成日期,允许你设置需要保存几天内的日志文件,允许设置每个日志文件的大小。

(1)自定义文件处理器

package com.bsk.log;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.StreamHandler;

public class CustomFileStreamHandler extends StreamHandler{
    //输出流
    private MeteredStream msOut;
    //日志玩家的写入的字节数, limit 为0 表示没有限制
    private int limit = 50000;
    //是否添加的玩家末尾
    private boolean append;
    //保存存在的日志文件
    private LinkedList<File> files;
    //希望写入的日志文件
    private File file;
    //希望写入的日志路径
    private String fileUrl;
  //希望写入的当天日志路径
    private String fileUrlDay;
    //保存几天之内的日志文件
    private int dateInter = 1;
    //索引文件,用于记录当前日志记录信息,请不要认为的修改
    private File indexFile;
    
    /**
     * 初始化自定义文件流处理器
     * @param fileUrl 文件路径, 可以是个目录或希望的日志名称,如果是个目录则日志为“未命名” 
     * 指定日志名称时不需要包括日期,程序会自动生成日志文件的生成日期及相应的编号
     * @param limit 每个日志希望写入的最大字节数,如果日志达到最大字节数则当天日期的一个新的编 号的日志文件将被创建,最新的日志记录在最大编号的文件中
     * @param dateInter 事务保存的日期间隔
     * @param append 是否将日志写入已存在的日志文件中
     * @throws java.lang.Exception
     */
    
    public CustomFileStreamHandler(String fileUrl, int limit, int dateInter, boolean append) throws Exception {
        super();
        this.fileUrl = fileUrl;
        if (dateInter <= 0) {
            throw new IllegalArgumentException("时间间隔必须大于0");
        }
        if (limit < 0) {
            throw new IllegalArgumentException("写入日志文件的最大字节数必须大于0");
        }
        this.limit = limit;
        this.dateInter = dateInter;
        this.append = append;
        openWriteFiles();
    }

    public CustomFileStreamHandler(String fileUrl, boolean append) throws Exception {
        super();
        this.fileUrl = fileUrl;
        this.append = append;
        openWriteFiles();
    }
    
    /**
     * 获得将要写入的文件
     */
    private synchronized void openWriteFiles() throws Exception {
        if (fileUrl == null) {
            throw new IllegalArgumentException("文件路径不能为null");
        }
        //files = getWritedLog();
        //checkLogFile();
        getWriteFile();
        if (append) {
            //openFile(files.getLast(), append);
            openFile(file, append);
        } else {
            getLastFile();
        }
    }
    
    private void getWriteFile()throws Exception{
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String trace = sdf.format(new Date().getTime());
        fileUrlDay = fileUrl+"_"+trace+".log";
        file = new File(fileUrlDay);
        if (!file.exists()) {
            file.createNewFile();
        }
    }
    
    /**
     * 打开需要写入的文件
     * @param file 需要打开的文件
     * @param append 是否将内容添加到文件末尾
     */
    private void openFile(File file, boolean append) throws Exception {
        //System.out.println("***opend = true " + file.toString());
        int len = 0;
        if (append) {
            len = (int) file.length();
        }
        FileOutputStream fout = new FileOutputStream(file.toString(), append);
        BufferedOutputStream bout = new BufferedOutputStream(fout);
        msOut = new MeteredStream(bout, len);
        setOutputStream(msOut);
    }
    
    /**
     * 将离现在最近的文件作为写入文件的文件 
     * 例如 D:logmylog_30_2008-02-19.log
     * mylog表示自定义的日志文件名,2008-02-19表示日志文件的生成日期,30 表示此日期的第30个日志文件 
     */
    private void getLastFile() {
        try {
            super.close();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String trace = sdf.format(new Date().getTime());
            int maxLogCount = 0; //获得当前最大的日志文件编号
            //System.out.println("********共有文件**********");
            for (File file : files) {
                //System.out.println(file.toString());
                String fileName = file.toString();
                //获得相同同日期日志文件的最大编号
                if (fileName.indexOf(trace) != -1) {
                    int last = fileName.lastIndexOf('_');
                    int beforeLast = fileName.lastIndexOf('_', last - 1);
                    maxLogCount = Integer.valueOf(fileName.substring(beforeLast + 1, last));
                }
            }
            
            //System.out.println("********");
            //System.out.println("maxLogCount == " + maxLogCount);
            File file = new File(fileUrl);
            String logIndex = (maxLogCount + 1) + "_" + trace;
            if (file.isDirectory()) { //是个目录
                files.add(new File(fileUrl + File.separator + "未命名_" + logIndex + ".log"));
            } else {
                files.add(new File(fileUrl + "_" + logIndex + ".log"));
            }
            writeLogIndex(logIndex, true);
            openFile(files.getLast(), false);
        } catch (Exception ex) {
            Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * 读取已经记录的日志的时间信息
     */
    private LinkedList<File> getWritedLog() {
        LinkedList<File> fileList = new LinkedList<File>();
        BufferedReader br = null;
        try {
            File file = new File(fileUrl);
            if (fileUrl.endsWith("/") || fileUrl.endsWith("/")) {  //是个目录
                if (!file.exists()) {
                    file.mkdirs();
                }
            }
            if (file.isDirectory()) { //只有指定file存在且是个目录     
                indexFile = new File(file.toString() + File.separator + "logindex");
            } else {
                indexFile = new File(file.getParent() + File.separator + "logindex");
            }
            if (!indexFile.exists()) {
                indexFile.createNewFile();
            }     
            
            FileReader fr = null;
            fr = new FileReader(indexFile);
            br = new BufferedReader(fr);
            String line = null;
            while ((line = br.readLine()) != null) {
                if (line.trim().length() != 0) {
                    if (file.isDirectory()) { //是个目录
                        fileList.add(new File(fileUrl + File.separator + "未命名" + "_" + line + ".log"));
                    } else {
                        fileList.add(new File(fileUrl + "_" + line + ".log"));
                    }
                System.out.println("line == " + line);
                }
            }
            return fileList;
            
        } catch (Exception ex) {
            Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                br.close();
            } catch (IOException ex) {
                Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }
    
    /**
     * 写入日志索引
     * @param logIndex 日志所以
     * @param isAppend 是否添加到索引文件中
     */
    private void writeLogIndex(String logIndex, boolean isAppend) {
        File file = new File(fileUrl);
        BufferedWriter bw = null;
        try {
            FileWriter fw = null;
            if (file.isDirectory()) { //是个目录     
                //是个目录
                fw = new FileWriter(new File(file.toString() + File.separator + "logindex"), isAppend);
            } else {
                fw = new FileWriter(new File(file.getParent() + File.separator + "logindex"), isAppend);
            }
            bw = new BufferedWriter(fw);
            bw.newLine();
            bw.write(logIndex, 0, logIndex.length());
            bw.flush();
            
        } catch (Exception ex) {
            Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                bw.close();
            } catch (IOException ex) {
                Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    /**
     * 检查当前日志时间
     * 删除过期日志,并检查日志索引中是否包含了现在日期
     */
    private void checkLogFile() {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String trace = sdf.format(new Date().getTime());
            boolean isIncludeNow = false;
            LinkedList<File> overdueLog = new LinkedList<File>(); //过期的日志文件
            long nowDate = sdf.parse(trace).getTime();
            for (File file : files) {
                if (file.toString().indexOf(trace) != -1) {
                    isIncludeNow = true;
                }
                if ((nowDate - sdf.parse(file.toString(), new ParsePosition(file.toString().lastIndexOf('_') + 1)).getTime()) / (86400 * 1000) > 5) {
                    overdueLog.add(file);
                //System.err.println("将被删除的日志为 " + file);
                }
            }
            
            //删除过期日志记录, 并重写日志索引文件
            if (overdueLog.size() != 0) {
                files.removeAll(overdueLog);
                indexFile.delete();
                indexFile.createNewFile();
                String fileStr = null;
                for (File file : files) {
                    fileStr = file.toString();
                    writeLogIndex(fileStr.substring(fileStr.lastIndexOf('_') - 1, fileStr.lastIndexOf('.')), true);
                }
                //删除过期文件
                for (File file : overdueLog) {
                    file.delete();
                }
            }

            //如果没有包含当天的日期同时日志需要添加到文件末尾,则添加当天日期的日志文件
            if (!isIncludeNow && append) {
                File file = new File(fileUrl);
                String logIndex = 1 + "_" + trace;
                if (file.isDirectory()) {
                    //是个目录
                    files.add(new File(fileUrl + File.separator + "未命名_" + logIndex + ".log"));
                } else {
                    files.add(new File(fileUrl + "_" + logIndex + ".log"));
                }
                writeLogIndex(logIndex, true);
            }
        } catch (Exception ex) {
            Logger.getLogger(CustomFileStreamHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * 发布日志信息
     */
    @Override
    public synchronized void publish(LogRecord record) {
        super.publish(record);
        super.flush();
        if (limit > 0 && msOut.written >= limit) {
            getLastFile();
        }
    }

    /**
     * 抄自FileHandler的实现,用于跟踪写入文件的字节数
     * 这样以便提高效率
     */
    private class MeteredStream extends OutputStream {

        private OutputStream out;
        //记录当前写入字节数
        private int written;

        MeteredStream(OutputStream out, int written) {
            this.out = out;
            this.written = written;
        }

        public void write(int b) throws IOException {
            out.write(b);
            written++;
        }

        @Override
        public void write(byte buff[]) throws IOException {
            out.write(buff);
            written += buff.length;
        }

        @Override
        public void write(byte buff[], int off, int len) throws IOException {
            out.write(buff, off, len);
            written += len;
        }

        @Override
        public void flush() throws IOException {
            out.flush();
        }

        @Override
        public void close() throws IOException {
            out.close();
        }
    }

    
}

(2)自定义日志输出格式化器

package com.bsk.log;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.AccessController;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;

public class CustomFormatter extends Formatter {
    
    //当前是第几条记录
    private int logCount;
    //时间
    private Date dat = new Date();
    //参数
    private Object[] args = new Object[1];
    //消息格式化器
    private MessageFormat formatter;
    //时间参数
    private String format = "日期 {0,date} 时间{0,time}";
    //行分格符
    private String lineSeparator = AccessController.doPrivileged(new sun.security.action.GetPropertyAction("line.separator"));

    /**
     * @param 日志记录器
     * @return 返回格式化好的日志内容
     */
    @Override
    public String format(LogRecord record) {
        StringBuffer sb = new StringBuffer();
        dat.setTime(record.getMillis());
        args[0] = dat;
        StringBuffer text = new StringBuffer();
        if (formatter == null) {
            formatter = new MessageFormat(format);
        }
        formatter.format(args, text, null);
        //sb.append("【第 " + (logCount++) + " 条记录】" + lineSeparator);
        //sb.append(text);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String trace = sdf.format(new Date().getTime());
        sb.append("[--|--][DEBUG] [" +trace).append("]");
        sb.append(" ");
        /*if (record.getSourceClassName() != null) {
            sb.append("源文件名 " + record.getSourceClassName());
        } else {
            sb.append("日志名 " + record.getLoggerName());
        }
        if (record.getSourceMethodName() != null) {
            sb.append(" ");
            sb.append("方法名 " + record.getSourceMethodName());
        }*/
        sb.append(" ");
        String message = formatMessage(record);
        sb.append(record.getLevel().getLocalizedName());
        sb.append(": ");
        sb.append(message);
        sb.append(lineSeparator);
        if (record.getThrown() != null) {
            try {
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                record.getThrown().printStackTrace(pw);
                pw.close();
                sb.append(sw.toString());
            } catch (Exception ex) {
            }
        }
        return sb.toString();
    }

}

(3) 测试代码

package com.bsk.doctor.log;

import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

public class HuanXinLog {
    
     private static Logger fileLogger;
     
     static {
            fileLogger = Logger.getLogger("com.bsk.doctor.util");
            fileLogger.setLevel(Level.INFO);
            Handler[] hs = fileLogger.getHandlers();
            for (Handler h : hs) {
                h.close();
                fileLogger.removeHandler(h);
            }
            try {
                //文件 日志文件名为mylog 日志最大写入为4000个字节 保存5天内的日志文件 如果文件没有达到规定大小则将日志文件添加到已有文件
                CustomFileStreamHandler fh = new CustomFileStreamHandler("e:/log/huanxinlog", 0, 1000, true);
                fh.setEncoding("UTF-8");
                fh.setFormatter(new CustomFormatter());
                fileLogger.setUseParentHandlers(false);  
                fileLogger.addHandler(fh);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
     
     /**
         * Creates a new instance of MyLog
         */
        private HuanXinLog() {
        }

        /**
         * 返回一个文件记录实例
         */
        public static synchronized Logger getFileLogger() {
            return fileLogger;
        }
        
        public static void writeFileLogger(String info){
            getFileLogger().log(Level.INFO, info);
            //getFileLogger().logp(Level.INFO, sourceClass, sourceMethod, info);
            
        }
        
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                fileLogger.logp(Level.INFO, "ewrwerwer", "werwerw", "记录log?");
            }
            /*String name = "insert into doc_huanxin_chatting_message (content,messageTimestamp,sender,addressee,timeStr,fromPhone,toPhone,questionId)";
            String sqlTemp = name.substring(0,50);
            System.out.println(sqlTemp);*/
        }

}

优质内容筛选与推荐>>
1、初探 iOS8 中的 Size Class
2、设置报表的 条形码 Barcode 字体 生产参数
3、视频作品《springboot基础篇》上线了
4、js判断浏览器版本
5、【CF】110 Div.1 B. Suspects


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn