Java手寫一個(gè)日志框架的示例代碼
什么是日志框架
日志框架是一種用于記錄和管理應(yīng)用程序運(yùn)行時(shí)信息的軟件組件。它通常提供了一套API(Application Programming Interface),讓開發(fā)人員能夠在代碼中插入日志語句,以便在應(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ù)庫、遠(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 通過多例模式創(chuàng)建實(shí)例,每個(gè)實(shí)例與一個(gè)特定的類關(guān)聯(lián)。這種設(shè)計(jì)使得每個(gè)類都有自己的日志記錄器,方便了解來自不同類的日志信息。
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),并通過 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)單。首先,通過 LeLog.getLeLog(Class clazz) 方法獲取 LeLog 實(shí)例,然后通過該實(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è)類可以說是漏洞百出,我寫出來不過是給大伙提供一個(gè)思路和一個(gè)敢于手寫框架造輪子的程序員桀驁不馴的心。
那這個(gè)類來說,值得噴的點(diǎn)有哪些:
- 異常處理你是一點(diǎn)沒做啊!
- 文件輸出類型太單一,我只做了windows的日志路徑,Linux上的你是一點(diǎn)沒做啊!
- 文件的格式問題,現(xiàn)在日志都流行是json格式的,以便于是以后在NoSql數(shù)據(jù)庫展示和分析。
- 過濾我也是一個(gè)都么看見。
- 異常追蹤呢?
- 最重要的是一個(gè)框架的靈活性,你這個(gè)你不覺得笨重嗎?
- 字符編碼和亂碼問題呢,
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ù)面試問題合集,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-03-03
詳解java并發(fā)之重入鎖-ReentrantLock
這篇文章主要介紹了java并發(fā)之重入鎖-ReentrantLock,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Mybatis批量更新對(duì)象數(shù)據(jù)的兩種實(shí)現(xiàn)方式
這篇文章主要介紹了Mybatis批量更新對(duì)象數(shù)據(jù)的兩種實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
JavaWeb實(shí)現(xiàn)顯示mysql數(shù)據(jù)庫數(shù)據(jù)
MySQL是最流行的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),在WEB應(yīng)用方面MySQL是最好的。本文將利用JavaWeb實(shí)現(xiàn)顯示mysql數(shù)據(jù)庫數(shù)據(jù)功能,需要的可以參考一下2022-03-03
一篇文章帶你了解jdk1.8新特性--為什么使用lambda表達(dá)式
Lambda是一個(gè)匿名函數(shù),我們可以把Lambda表達(dá)式理解為是一段可以傳遞的代碼,本篇文章就帶你了解,希望能給你帶來幫助2021-08-08

