seo技术培训泰州站长之家seo综合查询
Android 日志输出模块
本文主要记录下封装的日志输出模块.
1: 主要功能
- 日志模块初始化,并设置日志级别
- 支持将日志写入文件
- 日志文件单个限制200M,按天记录到指定文件,文件达到阈值后,记录新的日志文件.
- 支持导出日志文件zip.
2: 具体实现
-
日志整体初始化使用静态内部类的方式
private Context context;// 单例模式:静态内部类实现 private static class SingletonHolder {private static final LogManager INSTANCE = new LogManager(); }public static LogManager getInstance(Context context) {if (SingletonHolder.INSTANCE.context == null) {SingletonHolder.INSTANCE.context = context.getApplicationContext();}return SingletonHolder.INSTANCE; }private LogManager() { }
-
日志级别在manager中定义枚举级别.
// 定义日志级别枚举 public enum LogLevel {DEBUG, INFO, WARN, ERROR }private LogLevel currentLogLevel = LogLevel.DEBUG; // 默认日志级别为 DEBUG// 设置日志级别方法 public void setLogLevel(LogLevel logLevel) {this.currentLogLevel = logLevel; }
-
记录日志到文件
/*** 记录日志到文件** @param tag 日志标签* @param message 日志内容* @param logLevel 日志级别*/ public void log(String tag, String message, LogLevel logLevel) {String logFilePath = getCurrentLogFilePath();File logFile = new File(logFilePath);if (logFile.exists() && logFile.length() >= MAX_LOG_FILE_SIZE) {// 如果当前文件已满,创建新文件logFilePath = getCurrentLogFilePath();}try (FileOutputStream fos = new FileOutputStream(logFilePath, true);BufferedOutputStream bos = new BufferedOutputStream(fos)) {String logContent = String.format("[%s] [%s] [PID:%d] [TID:%d] [%s] %s\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),tag,android.os.Process.myPid(), // 进程号Thread.currentThread().getId(), // 线程号logLevel.name(), // 打印传入的日志级别message);bos.write(logContent.getBytes());// 根据传入的日志级别输出到控制台switch (logLevel) {case DEBUG:Log.d(tag, logContent.trim());break;case INFO:Log.i(tag, logContent.trim());break;case WARN:Log.w(tag, logContent.trim());break;case ERROR:Log.e(tag, logContent.trim());break;}} catch (IOException e) {Log.e("LogManager", "Failed to write log to file", e);} }
主要判断当前日志的大小,如果达到阈值200M.创建新的文件.
/*** 获取当前日志文件路径** @return 当前日志文件路径*/ private String getCurrentLogFilePath() {String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date());File logDir = new File(context.getExternalFilesDir(null), LOG_DIR);if (!logDir.exists()) {logDir.mkdirs();}int fileIndex = 1;File logFile;do {String fileName = LOG_FILE_PREFIX + currentDate + "_" + String.format("%03d", fileIndex) + LOG_FILE_EXTENSION;logFile = new File(logDir, fileName);fileIndex++;} while (logFile.exists() && logFile.length() >= MAX_LOG_FILE_SIZE);return logFile.getAbsolutePath(); }
4: 导出日志
主要操作为导出所有日志文件为 ZIP 包,获取到文件列表将文件添加到 ZIP 包中.
/*** 导出所有日志文件为 ZIP 包** @return 导出的 ZIP 文件路径*/ public String exportLogsToZip() {File logDir = new File(context.getExternalFilesDir(null), LOG_DIR);File exportDir = new File(context.getExternalFilesDir(null), EXPORT_DIR);if (!exportDir.exists()) {exportDir.mkdirs();}File zipFile = new File(exportDir, EXPORT_FILE_NAME);try (FileOutputStream fos = new FileOutputStream(zipFile);ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos))) {File[] logFiles = logDir.listFiles();if (logFiles != null) {int totalFiles = logFiles.length;for (int i = 0; i < totalFiles; i++) {addToZipFile(logFiles[i], zos);if (exportProgressListener != null) {int progress = (int) (((double) (i + 1) / totalFiles) * 100);exportProgressListener.onProgress(progress);}}}} catch (IOException e) {Log.e("LogManager", "Failed to export logs to ZIP", e);return null;}if (exportProgressListener != null) {exportProgressListener.onComplete(zipFile.getAbsolutePath());}return zipFile.getAbsolutePath(); }
/*** 将文件添加到 ZIP 包中** @param file 文件* @param zos ZIP 输出流* @throws IOException 如果发生 I/O 错误*/ private void addToZipFile(File file, ZipOutputStream zos) throws IOException {try (FileInputStream fis = new FileInputStream(file);BufferedInputStream bis = new BufferedInputStream(fis)) {ZipEntry zipEntry = new ZipEntry(file.getName());zos.putNextEntry(zipEntry);byte[] buffer = new byte[1024];int length;while ((length = bis.read(buffer)) > 0) {zos.write(buffer, 0, length);}zos.closeEntry();} }
添加导出日志的回调.可根据回调进度自定义进度条等效果.
public interface OnExportProgressListener {void onProgress(int progress);void onComplete(String exportPath); }private OnExportProgressListener exportProgressListener;public void setOnExportProgressListener(OnExportProgressListener listener) {this.exportProgressListener = listener; }
下面附上LogManager的代码:
package com.zh.logmanager;import android.content.Context; import android.util.Log;import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream;/*** @Author: zh* @Time: 25-4-5.* @Email:* @Describe:*/ public class LogManager {private static final String LOG_DIR = "logs";private static final String EXPORT_DIR = "exports";private static final String LOG_FILE_PREFIX = "app_log_";private static final String LOG_FILE_EXTENSION = ".txt";private static final String EXPORT_FILE_NAME = "logs_export.zip";private static final long MAX_LOG_FILE_SIZE = 200 * 1024 * 1024; // 200MBprivate Context context;// 单例模式:静态内部类实现private static class SingletonHolder {private static final LogManager INSTANCE = new LogManager();}public static LogManager getInstance(Context context) {if (SingletonHolder.INSTANCE.context == null) {SingletonHolder.INSTANCE.context = context.getApplicationContext();}return SingletonHolder.INSTANCE;}private LogManager() {}/*** 获取当前日志文件路径** @return 当前日志文件路径*/private String getCurrentLogFilePath() {String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date());File logDir = new File(context.getExternalFilesDir(null), LOG_DIR);if (!logDir.exists()) {logDir.mkdirs();}int fileIndex = 1;File logFile;do {String fileName = LOG_FILE_PREFIX + currentDate + "_" + String.format("%03d", fileIndex) + LOG_FILE_EXTENSION;logFile = new File(logDir, fileName);fileIndex++;} while (logFile.exists() && logFile.length() >= MAX_LOG_FILE_SIZE);return logFile.getAbsolutePath();}// 定义日志级别枚举public enum LogLevel {DEBUG, INFO, WARN, ERROR}private LogLevel currentLogLevel = LogLevel.DEBUG; // 默认日志级别为 DEBUG// 设置日志级别方法public void setLogLevel(LogLevel logLevel) {this.currentLogLevel = logLevel;}public void d(String tag, String message){this.log(tag, message,LogLevel.DEBUG);}public void i(String tag, String message){this.log(tag, message,LogLevel.INFO);}public void w(String tag, String message){this.log(tag, message,LogLevel.WARN);}public void e(String tag, String message){this.log(tag, message,LogLevel.ERROR);}public void log(String tag, String message){this.log(tag, message,LogLevel.DEBUG);}/*** 记录日志到文件** @param tag 日志标签* @param message 日志内容* @param logLevel 日志级别*/public void log(String tag, String message, LogLevel logLevel) {String logFilePath = getCurrentLogFilePath();File logFile = new File(logFilePath);if (logFile.exists() && logFile.length() >= MAX_LOG_FILE_SIZE) {// 如果当前文件已满,创建新文件logFilePath = getCurrentLogFilePath();}try (FileOutputStream fos = new FileOutputStream(logFilePath, true);BufferedOutputStream bos = new BufferedOutputStream(fos)) {String logContent = String.format("[%s] [%s] [PID:%d] [TID:%d] [%s] %s\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),tag,android.os.Process.myPid(), // 进程号Thread.currentThread().getId(), // 线程号logLevel.name(), // 打印传入的日志级别message);bos.write(logContent.getBytes());// 根据传入的日志级别输出到控制台switch (logLevel) {case DEBUG:Log.d(tag, logContent.trim());break;case INFO:Log.i(tag, logContent.trim());break;case WARN:Log.w(tag, logContent.trim());break;case ERROR:Log.e(tag, logContent.trim());break;}} catch (IOException e) {Log.e("LogManager", "Failed to write log to file", e);}}public interface OnExportProgressListener {void onProgress(int progress);void onComplete(String exportPath);}private OnExportProgressListener exportProgressListener;public void setOnExportProgressListener(OnExportProgressListener listener) {this.exportProgressListener = listener;}/*** 导出所有日志文件为 ZIP 包** @return 导出的 ZIP 文件路径*/public String exportLogsToZip() {File logDir = new File(context.getExternalFilesDir(null), LOG_DIR);File exportDir = new File(context.getExternalFilesDir(null), EXPORT_DIR);if (!exportDir.exists()) {exportDir.mkdirs();}File zipFile = new File(exportDir, EXPORT_FILE_NAME);try (FileOutputStream fos = new FileOutputStream(zipFile);ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos))) {File[] logFiles = logDir.listFiles();if (logFiles != null) {int totalFiles = logFiles.length;for (int i = 0; i < totalFiles; i++) {addToZipFile(logFiles[i], zos);if (exportProgressListener != null) {int progress = (int) (((double) (i + 1) / totalFiles) * 100);exportProgressListener.onProgress(progress);}}}} catch (IOException e) {Log.e("LogManager", "Failed to export logs to ZIP", e);return null;}if (exportProgressListener != null) {exportProgressListener.onComplete(zipFile.getAbsolutePath());}return zipFile.getAbsolutePath();}/*** 将文件添加到 ZIP 包中** @param file 文件* @param zos ZIP 输出流* @throws IOException 如果发生 I/O 错误*/private void addToZipFile(File file, ZipOutputStream zos) throws IOException {try (FileInputStream fis = new FileInputStream(file);BufferedInputStream bis = new BufferedInputStream(fis)) {ZipEntry zipEntry = new ZipEntry(file.getName());zos.putNextEntry(zipEntry);byte[] buffer = new byte[1024];int length;while ((length = bis.read(buffer)) > 0) {zos.write(buffer, 0, length);}zos.closeEntry();}} }
3: 调用+结果
初始化很简单,如下:
LogManager logManager = LogManager.getInstance(this);
Button logButton = findViewById(R.id.log_button);
Button exportButton = findViewById(R.id.export_button);logButton.setOnClickListener(v -> {logManager.log("MainActivity", "This is a test log message");logManager.i("MainActivity", "This is a test log message");logManager.w("MainActivity", "This is a test log message");logManager.e("MainActivity", "This is a test log message");
});
可以看到/storage/emulated/0/Android/data/com.zh.logmanager/files/logs/app_log_20250405_001.txt 路径下产生了新的日志文件.
文件内容:
[2025-04-05 17:21:50] [MainActivity] [PID:29019] [TID:2] [DEBUG] This is a test log message
[2025-04-05 17:21:50] [MainActivity] [PID:29019] [TID:2] [INFO] This is a test log message
[2025-04-05 17:21:50] [MainActivity] [PID:29019] [TID:2] [WARN] This is a test log message
[2025-04-05 17:21:50] [MainActivity] [PID:29019] [TID:2] [ERROR] This is a test log message
导出日志如下: /storage/emulated/0/Android/data/com.zh.logmanager/files/exports/logs_export.zip