1. 程式人生 > >簡易Java日誌(Log)輸出工具的封裝

簡易Java日誌(Log)輸出工具的封裝

平時 Java 專案的開發通常需要統一管理日誌(Log)的輸出,例如控制日誌資訊輸送的目的地(控制檯、檔案等),控制每一條日誌的輸出格式,把日誌分為不同的級別等。常用的比較成熟的 Java 日誌管理工具有 Apache 的 Log4j 等。但有時我們平時一時興趣想寫個小Dmeo或小工具,想較好的控制日誌的輸出,引入專業的日誌管理庫又顯得比較繁瑣,下面我就自己封裝一個簡單的日誌工具類(LogUtils.java),需要用時拷貝即用。

LogUtils工具類支援 4 種日誌級別(debug、info、warn、error),可以把日誌資訊格式化輸出到 控制檯 或 檔案。

使用方法如下:

package
com.xiets.log; import java.io.File; public class Main { private static final String TAG = "Main"; public static void main(String[] args) throws Exception { // (可選) 設定日誌輸出級別, 預設為 INFO 級別 LogUtils.setLogOutLevel(LogUtils.Level.DEBUG); // (可選) 設定日誌輸出檔案(追加到檔案尾部) LogUtils.setLogOutFile(new
File("MyLog.log")); // (可選) 設定日誌輸出位置(是否輸出到控制檯 和 是否輸出到檔案), 預設只輸出到控制檯, 不輸出到檔案 LogUtils.setLogOutTarget(true, true); // 輸出日誌 LogUtils.debug(TAG, "The debug log."); LogUtils.info(TAG, "The info log."); LogUtils.warn(TAG, "The warn log."); LogUtils.error(TAG, "The error log."
); } }

LogUtils.java日誌工具類的封裝:

package com.xiets.log;

import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 日誌輸出工具 <br/><br/>
 *
 * 可以輸出到控制檯和指定的檔案中, 分為4個級別, 由低到高分別為: debug, info, warn, error
 *
 * <br/><br/>
 *
 * 輸出級別:
 *
 * <ul>
 *     <li> debug: 輸出 debug, info, warn, error </li>
 *     <li> info: 輸出 info, warn, error </li>
 *     <li> warn: 輸出 warn, error </li>
 *     <li> error: 輸出 error </li>
 * </ul>
 *
 * 預設為 info 輸出級別
 *
 * <p/>
 *
 * Demo:
 *
 * <pre>{@code
 *     // (可選) 設定日誌輸出級別, 預設為 INFO 級別
 *     LogUtils.setLogOutLevel(LogUtils.Level.DEBUG);
 *
 *     // (可選) 設定日誌輸出檔案(追加到檔案尾部)
 *     LogUtils.setLogOutFile(new File("MyLog.log"));
 *
 *     // (可選) 設定日誌輸出位置(是否輸出到控制檯 和 是否輸出到檔案), 預設只輸出到控制檯, 不輸出到檔案
 *     LogUtils.setLogOutTarget(true, true);
 *
 *     // 輸出日誌
 *     LogUtils.debug("TAG", "The debug log.");
 *     LogUtils.info("TAG", "The info log.");
 *     LogUtils.warn("TAG", "The warn log.");
 *     LogUtils.error("TAG", "The error log.");
 * }</pre>
 *
 * @author xietansheng
 */
public class LogUtils {

    /** 每條 Log 的 tag 輸出的最大長度, 超過部分將被截斷 */
    private static final int TAG_MAX_LENGTH = 20;

    /** 每條 Log 的 message 輸出的最大長度, 超過部分將被截斷 */
    private static final int MESSAGE_MAX_LENGTH = 1024;

    /** 日期字首格式化 */
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd HH:mm:ss.SSS");

    /** 日誌當前的輸出級別, 預設為 INFO 級別 */
    private static Level logOutLevel = Level.INFO;

    /** 是否輸出到控制檯, 預設輸出 */
    private static boolean isOutToConsole = true;

    /** 是否輸出到檔案 */
    private static boolean isOutToFile = false;

    /** 日誌輸出檔案, 追加到檔案尾 */
    private static File logOutFile;

    /** 日誌檔案輸出流, 追加到檔案尾  */
    private static RandomAccessFile logOutFileStream;

    public static void setLogOutLevel(Level currentLevel) {
        if (currentLevel == null) {
            currentLevel = Level.INFO;
        }
        LogUtils.logOutLevel = currentLevel;
    }

    public static synchronized void setLogOutFile(File logOutFile) throws IOException {
        LogUtils.logOutFile = logOutFile;

        if (logOutFileStream != null) {
            closeStream(logOutFileStream);
            logOutFileStream = null;
        }

        if (LogUtils.logOutFile != null) {
            try {
                logOutFileStream = new RandomAccessFile(LogUtils.logOutFile, "rw");
                logOutFileStream.seek(LogUtils.logOutFile.length());
            } catch (IOException e) {
                closeStream(logOutFileStream);
                logOutFileStream = null;
                throw e;
            }
        }
    }

    public static void setLogOutTarget(boolean isOutToConsole, boolean isOutToFile) {
        LogUtils.isOutToConsole = isOutToConsole;
        LogUtils.isOutToFile = isOutToFile;
    }

    public static void debug(String tag, String message) {
        printLog(Level.DEBUG, tag, message, false);
    }

    public static void info(String tag, String message) {
        printLog(Level.INFO, tag, message, false);
    }

    public static void warn(String tag, String message) {
        printLog(Level.WARN, tag, message, false);
    }

    public static void error(String tag, String message) {
        printLog(Level.ERROR, tag, message, true);
    }

    public static void error(String tag, Exception e) {
        if (e == null) {
            error(tag, (String) null);
            return;
        }

        PrintStream printOut = null;

        try {
            ByteArrayOutputStream bytesBufOut = new ByteArrayOutputStream();
            printOut = new PrintStream(bytesBufOut);
            e.printStackTrace(printOut);
            printOut.flush();
            error(tag, new String(bytesBufOut.toByteArray(), "UTF-8"));

        } catch (Exception e1) {
            e1.printStackTrace();

        } finally {
            closeStream(printOut);
        }
    }

    private static void printLog(Level level, String tag, String message, boolean isOutToErr) {
        if (level.getLevelValue() >= logOutLevel.getLevelValue()) {
            String log = DATE_FORMAT.format(new Date()) +
                    " " +
                    level.getTag() +
                    "/" +
                    checkTextLengthLimit(tag, TAG_MAX_LENGTH) +
                    ": " +
                    checkTextLengthLimit(message, MESSAGE_MAX_LENGTH);

            if (isOutToConsole) {
                outLogToConsole(isOutToErr, log);
            }
            if (isOutToFile) {
                outLogToFile(log);
            }
        }
    }

    private static void outLogToConsole(boolean isOutToErr, String log) {
        if (isOutToErr) {
            // System.err 和 System.out 是兩個不同的輸出流通道, 如果極短時間內連
            // 續輸出 log 到 err 和 out, 控制檯上的列印順序可能會不完全按時序列印.
            System.err.println(log);
        } else {
            System.out.println(log);
        }
    }

    private static synchronized void outLogToFile(String log) {
        if (logOutFileStream != null) {
            try {
                logOutFileStream.write((log + "\n").getBytes("UTF-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static String checkTextLengthLimit(String text, int maxLength) {
        if ((text != null) && (text.length() >  maxLength)) {
            text = text.substring(0, maxLength - 3) + "...";
        }
        return text;
    }

    private static void closeStream(Closeable stream) {
        if (stream != null) {
            try {
                stream.close();
            } catch (Exception e) {
                // nothing
            }
        }
    }

    public static enum Level {
        DEBUG("D", 1), INFO("I", 2), WARN("W", 3), ERROR("E", 4);

        private String tag;

        private int levelValue;

        private Level(String tag, int levelValue) {
            this.tag = tag;
            this.levelValue = levelValue;
        }

        public String getTag() {
            return tag;
        }

        public int getLevelValue() {
            return levelValue;
        }
    }

}

控制檯輸出結果:

result.png

日誌檔案輸出結果:

result-file.png