• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Shenzhen Kaihong Digital.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package utils;
17 
18 import java.io.BufferedReader;
19 import java.io.File;
20 import java.io.FileWriter;
21 import java.io.IOException;
22 import java.nio.charset.StandardCharsets;
23 import java.nio.file.Files;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Date;
28 import java.util.List;
29 
30 /**
31  * <h3>类名:该类用于xxx</h3>
32  * description ${description}
33  *
34  * @author ${USER}
35  * date 2025-02-28
36  * @since 2025-02-28
37  * @version 1.0
38  */
39 public class LogUtils {
40     // 日志级别常量
41     /**
42      * 调试级别
43      */
44     public static final int DEBUG = 1;
45 
46     /**
47      * 信息级别
48      */
49     public static final int INFO = 2;
50 
51     /**
52      * 告警级别
53      */
54     public static final int WARN = 3;
55 
56     /**
57      * 错误级别
58      */
59     public static final int ERROR = 4;
60 
61     // 全局日志级别(默认 INFO)
62     private static int currentLevel = INFO;
63 
64     // 其他原有配置(路径、文件大小限制等)
65     private static final String LOG_DIR = "logs";
66     private static final String LOG_FILE = "app.log";
67     private static final String DEFAULT_LOG_PATH = "./logs/app.log";
68     private static final int DEFAULT_LOG_LEVEL = INFO;
69     private static final long MAX_SIZE = 10 * 1024 * 1024; // 10MB
70     private static final int MAX_FILES = 5;
71     private static FileWriter writer;
72     private static String currentLogPath = DEFAULT_LOG_PATH;
73     private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
74 
75     static {
initLogDir()76         initLogDir();
initWriter()77         initWriter();
78     }
79 
80     // 初始化日志目录
initLogDir()81     private static void initLogDir() {
82         File dir = new File(LOG_DIR);
83         if (!dir.exists()) {
84             boolean res = dir.mkdir();
85             if (!res) {
86                 System.out.println("mkdir " + LOG_DIR + " failed!");
87             }
88         }
89     }
90 
91     // 初始化写入器
initWriter()92     private static void initWriter() {
93         try {
94             File logFile = new File(LOG_DIR, LOG_FILE);
95             writer = new FileWriter(logFile, true); // 追加模式
96         } catch (IOException e) {
97             System.out.println("initWriter error: " + e.getMessage());
98         }
99     }
100 
101     /**
102      * 改名字
103      */
doRename(File src, int i)104     private static synchronized void doRename(File src, int i) {
105         if (src.exists()) {
106             File dest = new File(LOG_DIR, "app." + (i + 1) + ".log");
107             boolean res = src.renameTo(dest);
108             if (!res) {
109                 System.out.println("rollOver " + LOG_DIR + " failed!");
110             }
111         }
112     }
113 
114     /**
115      * 日志滚动
116      */
rollOver()117     private static synchronized void rollOver() {
118         try {
119             writer.close();
120             for (int i = MAX_FILES - 1; i >= 1; i--) {
121                 File src = new File(LOG_DIR, "app." + i + ".log");
122                 doRename(src, i);
123             }
124             boolean res = new File(LOG_DIR, LOG_FILE).renameTo(new File(LOG_DIR, "app.1.log"));
125             if (!res) {
126                 System.out.println("rollOver " + LOG_DIR + " failed!");
127             }
128             initWriter(); // 重新初始化写入器
129         } catch (IOException e) {
130             System.out.println("initWriter error: " + e.getMessage());
131         }
132     }
133 
134     /**
135      * 重新初始化
136      */
reinit()137     public static void reinit() {
138         // 1. 关闭现有日志流‌:ml-citation{ref="1,3" data="citationList"}
139         if (writer != null) {
140             try {
141                 writer.close();
142             } catch (IOException e) {
143                 System.err.println("日志流关闭失败: " + e.getMessage());
144             }
145         }
146 
147         // 2. 恢复初始配置参数‌:ml-citation{ref="5,8" data="citationList"}
148         currentLogPath = DEFAULT_LOG_PATH;
149         currentLevel = DEFAULT_LOG_LEVEL;
150 
151         // 3. 重新初始化日志文件‌:ml-citation{ref="1,3" data="citationList"}
152         try {
153             File logFile = new File(LOG_DIR, LOG_FILE);
154             writer = new FileWriter(logFile, true);
155         } catch (IOException e) {
156             System.err.println("日志文件初始化失败: " + e.getMessage());
157         }
158     }
159 
160     /**
161      * 清除日志
162      */
clear()163     public static void clear() {
164         if (writer != null) {
165             try {
166                 writer.close();
167             } catch (IOException e) {
168                 System.err.println("日志流关闭失败: " + e.getMessage());
169             }
170         }
171 
172         File logFile = new File(LOG_DIR, LOG_FILE);
173 
174         // 1. 处理单文件模式‌:ml-citation{ref="4,5" data="citationList"}
175         if (logFile.isFile()) {
176             if (logFile.delete()) {
177                 System.out.println("日志文件已删除: " + currentLogPath);
178             } else {
179                 System.err.println("文件删除失败: dir-" + LOG_DIR + " ,file-" + LOG_FILE);
180             }
181             return;
182         }
183 
184         // 2. 处理目录模式‌:ml-citation{ref="3,4" data="citationList"}
185         if (logFile.isDirectory()) {
186             File[] logFiles = logFile.listFiles((dir, name) -> name.endsWith(".log"));
187             if (logFiles == null) {
188                 return;
189             }
190 
191             for (File file : logFiles) {
192                 if (file.delete()) {
193                     System.out.println("已删除: " + file.getAbsolutePath());
194                 } else {
195                     System.err.println("删除失败: " + file.getAbsolutePath());
196                 }
197             }
198         }
199     }
200 
201 
202     // 设置日志级别(静态方法)
setLevel(int level)203     public static synchronized void setLevel(int level) {
204         currentLevel = level;
205     }
206 
207     // 读取日志级别(静态方法)
getLevel()208     public static synchronized int getLevel() {
209         return currentLevel;
210     }
211 
212     /**
213      * 写调试日志
214      *
215      * @param message 调试日志
216      */
debug(String message)217     public static void debug(String message) {
218         log(DEBUG, message);
219     }
220 
221     /**
222      * 写信息日志
223      *
224      * @param message 信息日志
225      */
info(String message)226     public static void info(String message) {
227         log(INFO, message);
228     }
229 
230     /**
231      * 写告警日志
232      *
233      * @param message 告警日志
234      */
warn(String message)235     public static void warn(String message) {
236         log(WARN, message);
237     }
238 
239     /**
240      * 写错误日志
241      *
242      * @param message 错误日志
243      */
error(String message)244     public static void error(String message) {
245         log(ERROR, message);
246     }
247 
248     // 核心日志记录逻辑(含分级判断)
249     /**
250      * 写日志
251      *
252      * @param level 日志等级
253      * @param message 告警日志
254      */
log(int level, String message)255     private static synchronized void log(int level, String message) {
256         if (level < currentLevel) {
257             // 低于当前级别则不记录‌:ml-citation{ref="3,6" data="citationList"}
258             return;
259         }
260 
261         try {
262             File logFile = new File(LOG_DIR, LOG_FILE);
263             if (logFile.length() >= MAX_SIZE) {
264                 rollOver();
265             }
266 
267             String timestamp = SDF.format(new Date());
268             String levelName = getLevelName(level);
269             String logLine = String.format("%s [%s] %s%n", timestamp, levelName, message);
270             writer.write(logLine);
271             writer.flush();
272         } catch (IOException e) {
273             System.out.println("log write error: " + e.getMessage());
274         }
275     }
276 
277     // 将级别数值转换为名称
278     /**
279      * 获取日志界壁
280      *
281      * @param level 日志等级
282      * @return 日志级别名称
283      */
getLevelName(int level)284     private static String getLevelName(int level) {
285         return switch (level) {
286             case DEBUG -> "DEBUG";
287             case INFO -> "INFO";
288             case WARN -> "WARN";
289             case ERROR -> "ERROR";
290             default -> "UNKNOWN";
291         };
292     }
293 
294     /**
295      * 读取日志文件内容(支持分页)
296      *
297      * @param pageNum    页码(从1开始)
298      * @param pageSize   每页行数
299      * @return 按时间倒序排列的日志列表(最近日志在前)
300      */
readLog(int pageNum, int pageSize)301     public static List<String> readLog(int pageNum, int pageSize) {
302         return readLog(currentLogPath, pageNum, pageSize);
303     }
304 
305     /**
306      * 指定路径,大小读取日志文件内容
307      *
308      * @param logPath    日志文件路径
309      * @param pageNum    页码
310      * @param pageSize   每页行数
311      * @return 文件内容列表
312      */
readLog(String logPath, int pageNum, int pageSize)313     public static synchronized List<String> readLog(String logPath, int pageNum, int pageSize) {
314         File logFile = new File(logPath);
315         List<String> allLines = new ArrayList<>();
316 
317         try (BufferedReader reader = Files.newBufferedReader(logFile.toPath(), StandardCharsets.UTF_8)) {
318             String line;
319             while ((line = reader.readLine()) != null) {
320                 allLines.add(line);
321             }
322         } catch (IOException e) {
323             return Collections.singletonList("日志读取失败: " + e.getMessage());
324         }
325 
326         // 倒序处理(最近日志在前)
327         Collections.reverse(allLines);
328 
329         // 分页计算
330         int start = (pageNum - 1) * pageSize;
331         int end = Math.min(start + pageSize, allLines.size());
332         return start >= allLines.size() ? new ArrayList<>() : allLines.subList(start, end);
333     }
334 
335     /**
336      * 全量读取日志文件
337      *
338      * @return 返回日志读取内容
339      */
readLog()340     public static List<String> readLog() {
341         return readLog(currentLogPath, 1, Integer.MAX_VALUE);
342     }
343 }
344