Java手寫一個(gè)日志框架的示例代碼
什么是日志框架
日志框架是一種用于記錄和管理應(yīng)用程序運(yùn)行時(shí)信息的軟件組件。它通常提供了一套API(Application Programming Interface),讓開發(fā)人員能夠在代碼中插入日志語(yǔ)句,以便在應(yīng)用程序運(yùn)行時(shí)生成有關(guān)其狀態(tài)和執(zhí)行流的信息。
一個(gè)優(yōu)秀的日志框架應(yīng)該具備的功能:
- 級(jí)別控制: 日志框架通常支持多個(gè)日志級(jí)別,如 DEBUG、INFO、WARN、ERROR 等。開發(fā)人員可以根據(jù)需要選擇記錄的信息級(jí)別,以便在不同場(chǎng)景中控制日志輸出。
- 輸出目標(biāo): 日志框架支持將日志信息輸出到不同的目標(biāo),如控制臺(tái)、文件、數(shù)據(jù)庫(kù)、遠(yuǎn)程服務(wù)器等。這使得開發(fā)人員能夠根據(jù)實(shí)際需求選擇合適的輸出方式。
- 性能優(yōu)化: 一些日志框架支持異步日志記錄,以減少對(duì)應(yīng)用程序性能的影響。它們可能使用緩沖機(jī)制、線程池等技術(shù),以提高日志記錄的效率。
- 靈活性: 日志框架提供了靈活的配置選項(xiàng),使得開發(fā)人員能夠根據(jù)實(shí)際需求進(jìn)行定制和調(diào)整。
常見的Java日志框架包括 Log4j、Logback、java.util.logging 等。
手寫一個(gè)簡(jiǎn)單的日志框架應(yīng)該怎么做
首先,我們需要明確我們要做的功能。
從日漸成熟的日志框架中,我們總計(jì)日志框架的核心功能有;
- 級(jí)別控制(DEBUG、INFO、WARN、ERROR)
- 輸出(控制臺(tái)輸出、文件輸出)
- 性能優(yōu)化(做到不浪費(fèi)性能)
我寫了一個(gè)簡(jiǎn)單的源碼
import java.io.*; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * LeLog類用于記錄不同日志級(jí)別的消息。 */ public class LeLog { /** * 日志級(jí)別的枚舉: INFO, WARN, ERROR */ public enum LogLevel { INFO, WARN, ERROR } private static Class<?> defaultClass; // 用于日志記錄的默認(rèn)類 private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); // 日志記錄的日期格式 private static Map<Class<?>, LeLog> instances = new HashMap<>(); // 存儲(chǔ)LeLog實(shí)例的映射 private Class<?> clazz; // 與日志關(guān)聯(lián)的類 /** * 構(gòu)造函數(shù),設(shè)置默認(rèn)日志記錄的類。 * @param clazz 與日志關(guān)聯(lián)的類 */ public LeLog(Class<?> clazz) { this.clazz = clazz; defaultClass = clazz; // 設(shè)置默認(rèn)日志記錄的類 } /** * 多例模式創(chuàng)建實(shí)例 * @param clazz 類 * @return LeLog實(shí)例 */ public static synchronized LeLog getLeLog(Class<?> clazz) { if (!instances.containsKey(clazz)) { LeLog instance = new LeLog(clazz); instances.put(clazz, instance); } return instances.get(clazz); } /** * 將日志消息寫入控制臺(tái)和文件。 * @param level 日志級(jí)別 * @param logName 日志名稱 * @param message 日志消息 */ private static void write(LogLevel level, String logName, String message) { // 創(chuàng)建并格式化日志消息 String timeStamp = dateFormat.format(new Date()); String threadName = Thread.currentThread().getName(); long threadId = Thread.currentThread().getId(); String logMessage = timeStamp + " [" + level + "] " + "Thread: " + threadName + " (ID: " + threadId + ") " + logName + " - " + message; // 輸出日志消息到控制臺(tái) System.out.println(logMessage); // 將日志消息寫入文件 writeToFile(logMessage); } /** * 獲取日志文件路徑。 * @return 日志文件路徑 */ private static String getLogFile() { Properties prop = new Properties(); try (FileInputStream input = new FileInputStream("le.log.properties")) { prop.load(input); String logFilePath = prop.getProperty("logFilePath"); File logFolder = new File(logFilePath); if (!logFolder.exists()) { logFolder.mkdirs(); } String logFileName = prop.getProperty("logFileName"); return logFilePath + "/" + logFileName + "_" + LocalDate.now() + ".log"; } catch (IOException e) { e.printStackTrace(); } return null; } /** * 將日志消息寫入文件。 * @param message 日志消息 */ private static void writeToFile(String message) { String logFile = getLogFile(); if (logFile != null) { try (PrintWriter out = new PrintWriter(new FileWriter(logFile, true))) { out.println(message); } catch (IOException e) { e.printStackTrace(); } } } /** * 記錄INFO級(jí)別的日志消息。 * @param message 日志消息 */ public static void info(String message){ write(LeLog.LogLevel.INFO,defaultClass.getName(),message); } /** * 記錄WARN級(jí)別的日志消息。 * @param message 日志消息 */ public static void warn(String message){ write(LogLevel.WARN,defaultClass.getName(),message); } /** * 記錄ERROR級(jí)別的日志消息。 * @param message 日志消息 */ public static void error(String message){ write(LogLevel.ERROR,defaultClass.getName(),message); } }
代碼結(jié)構(gòu)和核心思路
LeLog
類是一個(gè)用于記錄不同日志級(jí)別消息的工具類。以下是它的核心思路和代碼結(jié)構(gòu):
日志級(jí)別枚舉: LeLog
定義了一個(gè)LogLevel
枚舉,包括 INFO、WARN 和 ERROR 三個(gè)級(jí)別,分別表示信息、警告和錯(cuò)誤。
/** * 日志級(jí)別的枚舉: INFO, WARN, ERROR */ public enum LogLevel { INFO, WARN, ERROR }
類與實(shí)例關(guān)聯(lián): LeLog
通過(guò)多例模式創(chuàng)建實(shí)例,每個(gè)實(shí)例與一個(gè)特定的類關(guān)聯(lián)。這種設(shè)計(jì)使得每個(gè)類都有自己的日志記錄器,方便了解來(lái)自不同類的日志信息。
private static Map<Class<?>, LeLog> instances = new HashMap<>(); // 存儲(chǔ)LeLog實(shí)例的映射 private Class<?> clazz; // 與日志關(guān)聯(lián)的類 /** * 構(gòu)造函數(shù),設(shè)置默認(rèn)日志記錄的類。 * @param clazz 與日志關(guān)聯(lián)的類 */ public LeLog(Class<?> clazz) { this.clazz = clazz; defaultClass = clazz; // 設(shè)置默認(rèn)日志記錄的類 } /** * 多例模式創(chuàng)建實(shí)例 * @param clazz 類 * @return LeLog實(shí)例 */ public static synchronized LeLog getLeLog(Class<?> clazz) { if (!instances.containsKey(clazz)) { LeLog instance = new LeLog(clazz); instances.put(clazz, instance); } return instances.get(clazz); }
日志消息格式化: 在 write 方法中,使用 SimpleDateFormat
對(duì)日期進(jìn)行格式化,同時(shí)獲取線程信息和日志級(jí)別,將這些信息拼接成格式化的日志消息。
/** * 將日志消息寫入控制臺(tái)和文件。 * @param level 日志級(jí)別 * @param logName 日志名稱 * @param message 日志消息 */ private static void write(LogLevel level, String logName, String message) { // 創(chuàng)建并格式化日志消息 String timeStamp = dateFormat.format(new Date()); String threadName = Thread.currentThread().getName(); long threadId = Thread.currentThread().getId(); String logMessage = timeStamp + " [" + level + "] " + "Thread: " + threadName + " (ID: " + threadId + ") " + logName + " - " + message; // 輸出日志消息到控制臺(tái) System.out.println(logMessage); // 將日志消息寫入文件 writeToFile(logMessage); }
輸出到控制臺(tái)和文件: LeLog
將日志消息輸出到控制臺(tái),并通過(guò) writeToFile
方法將消息寫入到文件。日志文件路徑和文件名從配置文件中讀取,提高了靈活性。日志文件名中包含當(dāng)前日期,每天生成一個(gè)新的日志文件,方便按日期查看日志。
# 日志輸出路徑 logFilePath=logs/xxx/xx # 日志輸出名稱 logFileName=xia_le
/** * 獲取日志文件路徑。 * @return 日志文件路徑 */ private static String getLogFile() { Properties prop = new Properties(); try (FileInputStream input = new FileInputStream("le.log.properties")) { prop.load(input); String logFilePath = prop.getProperty("logFilePath"); File logFolder = new File(logFilePath); if (!logFolder.exists()) { logFolder.mkdirs(); } String logFileName = prop.getProperty("logFileName"); return logFilePath + "/" + logFileName + "_" + LocalDate.now() + ".log"; } catch (IOException e) { e.printStackTrace(); } return null; } /** * 將日志消息寫入文件。 * @param message 日志消息 */ private static void writeToFile(String message) { String logFile = getLogFile(); if (logFile != null) { try (PrintWriter out = new PrintWriter(new FileWriter(logFile, true))) { out.println(message); } catch (IOException e) { e.printStackTrace(); } } }
使用方法
使用 LeLog
記錄日志非常簡(jiǎn)單。首先,通過(guò) LeLog.getLeLog(Class clazz)
方法獲取 LeLog
實(shí)例,然后通過(guò)該實(shí)例的 info
、warn
、error
方法記錄不同級(jí)別的日志消息。以下是一個(gè)簡(jiǎn)單的示例:
public class TestOne { private static final LeLog leLog = LeLog.getLeLog(TestOne.class); public void test(){ leLog.info("This is an information message."); leLog.warn("This is a warning message."); leLog.error("This is an error message."); } } public class TestTwo { private static final LeLog leLog = LeLog.getLeLog(TestTwo.class); public void test(){ leLog.info("This is an information message."); leLog.warn("This is a warning message."); leLog.error("This is an error message."); } } public class Main { public static void main(String[] args) { new TestOne().test(); new TestTwo().test(); } }
控制臺(tái)輸出如下
文件輸出如下:
后期優(yōu)化
這個(gè)類可以說(shuō)是漏洞百出,我寫出來(lái)不過(guò)是給大伙提供一個(gè)思路和一個(gè)敢于手寫框架造輪子的程序員桀驁不馴的心。
那這個(gè)類來(lái)說(shuō),值得噴的點(diǎn)有哪些:
- 異常處理你是一點(diǎn)沒(méi)做?。?/li>
- 文件輸出類型太單一,我只做了windows的日志路徑,Linux上的你是一點(diǎn)沒(méi)做??!
- 文件的格式問(wèn)題,現(xiàn)在日志都流行是json格式的,以便于是以后在NoSql數(shù)據(jù)庫(kù)展示和分析。
- 過(guò)濾我也是一個(gè)都么看見。
- 異常追蹤呢?
- 最重要的是一個(gè)框架的靈活性,你這個(gè)你不覺(jué)得笨重嗎?
- 字符編碼和亂碼問(wèn)題呢,
System.out.println
很耗費(fèi)性能的好吧。 - ..........太多了,懶得吐槽。
以上就是Java手寫一個(gè)日志框架的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Java日志框架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
分布式系統(tǒng)下調(diào)用鏈追蹤技術(shù)面試題
這篇文章主要為大家介紹了分布式系統(tǒng)下調(diào)用鏈追蹤技術(shù)面試問(wèn)題合集,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-03-03詳解java并發(fā)之重入鎖-ReentrantLock
這篇文章主要介紹了java并發(fā)之重入鎖-ReentrantLock,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03springboot?sleuth?日志跟蹤問(wèn)題記錄
Spring?Cloud?Sleuth是一個(gè)在應(yīng)用中實(shí)現(xiàn)日志跟蹤的強(qiáng)有力的工具,使用Sleuth庫(kù)可以應(yīng)用于計(jì)劃任務(wù)?、多線程服務(wù)或復(fù)雜的Web請(qǐng)求,尤其是在一個(gè)由多個(gè)服務(wù)組成的系統(tǒng)中,這篇文章主要介紹了springboot?sleuth?日志跟蹤,需要的朋友可以參考下2023-07-07Mybatis批量更新對(duì)象數(shù)據(jù)的兩種實(shí)現(xiàn)方式
這篇文章主要介紹了Mybatis批量更新對(duì)象數(shù)據(jù)的兩種實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08JavaWeb實(shí)現(xiàn)顯示mysql數(shù)據(jù)庫(kù)數(shù)據(jù)
MySQL是最流行的關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),在WEB應(yīng)用方面MySQL是最好的。本文將利用JavaWeb實(shí)現(xiàn)顯示mysql數(shù)據(jù)庫(kù)數(shù)據(jù)功能,需要的可以參考一下2022-03-03一篇文章帶你了解jdk1.8新特性--為什么使用lambda表達(dá)式
Lambda是一個(gè)匿名函數(shù),我們可以把Lambda表達(dá)式理解為是一段可以傳遞的代碼,本篇文章就帶你了解,希望能給你帶來(lái)幫助2021-08-08