設計目標
- 零依賴 (僅 JDK),輕量級嵌入任意專案
- 線程安全:
synchronized 方法級鎖
- 自動創建目錄/文件
- 支援單行與批量多行寫入
- 統一時間戳格式:
yyyy-MM-dd HH:mm:ss
完整實現代碼
public final class AndyLogger {
// 配置
private static final String ROOT_PATH = "C:\\temp2"; // 建議外部化配置
private static final String LINE_SEP = System.lineSeparator();
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 單行記錄
public static synchronized void log(String fileName, String message) {
write(fileName, Collections.singletonList(message));
}
// 批量記錄
public static synchronized void log(String fileName, String[] messages) {
write(fileName, Arrays.asList(messages));
}
// 核心寫入邏輯
private static void write(String fileName, List<String> messages) {
File dir = new File(ROOT_PATH);
if (!dir.exists()) dir.mkdirs();
File logFile = new File(dir, fileName + ".log");
if (!logFile.exists()) {
try { logFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); return; }
}
// 讀取現有內容 (小文件適用,大文件建議追加模式)
StringBuilder content = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader(logFile))) {
String line;
while ((line = br.readLine()) != null) {
content.append(line).append(LINE_SEP);
}
} catch (IOException e) { e.printStackTrace(); return; }
// 追加新記錄
String timestamp = SDF.format(new Date());
for (String msg : messages) {
content.append(timestamp).append(' ').append(msg).append(LINE_SEP);
}
// 覆寫文件 (注意:非追加模式,大文件性能差)
try (BufferedWriter bw = new BufferedWriter(new FileWriter(logFile))) {
bw.write(content.toString());
} catch (IOException e) { e.printStackTrace(); }
}
// 測試
public static void main(String[] args) {
AndyLogger.log("bug223", "timeisjjja");
AndyLogger.log("bug223", new String[]{"111", "222", "333"});
}
}
關鍵設計點分析
| 點 | 實現 | 優缺點 |
| 線程安全 | synchronized 方法鎖 | 簡單可靠,高併發下成瓶頸 |
| 文件模式 | 讀全量 → 內存拼接 → 覆寫 | ✅ 保證順序;❌ 大文件 OOM 風險、性能差 |
| 時間戳 | SimpleDateFormat (非線程安全) | 鎖內使用安全,高併發建議 DateTimeFormatter (Java 8+) |
| 編碼 | 平台默認編碼 | 建議顯式指定 StandardCharsets.UTF_8 |
| 配置 | 硬編碼路徑 | 建議外部化 (Properties/YAML/環境變量) |
生產級優化建議 (漸進式)
- 追加模式:
new FileWriter(file, true) 直接追加,無需讀舊內容,O(1) 寫入
- 異步寫入:
BlockingQueue + 專用寫入線程 或 Log4j2 AsyncAppender,業務線程無阻塞
- 滾動策略:按大小/時間分割文件,避免單文件過大
- 結構化日誌:JSON 格式,便於 ELK/Splunk 採集分析
- 門面模式:對接 SLF4J,底層可切換 Logback/Log4j2/JUL
現代替代方案對比
| 方案 | 優點 | 缺點 | 適用場景 |
| Logback + SLF4J | 業界標準、高性能、豐富策略、原生異步 | 學習曲線、依賴體積 | 所有生產項目 |
| Log4j2 | 無 GC、配置熱加載、插件化 | 歷史安全漏洞 (已修復) | 高性能/低延遲系統 |
| JUL (java.util.logging) | 零依賴、JDK 內建 | 功能弱、配置繁瑣、性能一般 | 極簡項目/受限環境 |
| 自研 (如本類) | 完全可控、零依賴、適合教學 | 缺乏企業級特性、維護成本 | 教學、嵌入式、臨時腳本 |
總結
本工具類展示了日誌框架的核心骨架:文件管理、線程同步、格式化、寫入路徑。理解其原理有助於深入掌握成熟框架的內部機制。實際工程中,請直接使用 Logback/Log4j2,僅在極度受限環境或教學場景考慮自研。若必須自研,請務必實現追加寫入、異步落盤、滾動分割三大核心能力。