Java 项目中基于 Log4j 的日志工具类封装实践
在 Java 企业级开发中,日志是系统调试、监控和排查问题的核心手段。直接调用 Log4j 往往导致耦合度高、日志格式不统一。这里分享一种封装方案,通过接口抽象和工具类辅助,让日志调用更简洁规范。
1. 定义日志接口
首先定义统一的 Logger 接口,屏蔽底层 Log4j 的具体实现细节,方便后续切换或扩展。
public interface Logger {
/** 配置文件路径 */
String LOG4JFILE = "log4j.properties";
/** 日志名称空间 */
String SYSTEMLOG = "Systemlog";
void debug(Object message);
void debug(Object message, Throwable throwable);
void info(Object message);
void info(Object message, Throwable throwable);
void warn(Object message);
void warn(Object message, Throwable throwable);
void error(Object message);
void error(Object message, Throwable throwable);
}
2. 实现 Log4j 适配器
核心在于 LoggerAdapter,负责初始化配置并代理实际日志输出。注意懒加载机制,确保配置只加载一次,避免重复读取文件。
package com.example.log;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class LoggerAdapter implements Logger {
private static Logger log4j = null;
public static void getInstance() {
PropertyConfigurator.configure(LOG4JFILE);
log4j = Logger.getLogger(SYSTEMLOG);
}
@Override
public void debug(Object message) {
if (log4j == null) getInstance();
log4j.debug(message);
}
@Override
public void debug(Object message, Throwable throwable) {
if (log4j == null) getInstance();
log4j.debug(message, throwable);
}
@Override
public void info(Object message) {
if (log4j == null) getInstance();
log4j.info(message);
}
@Override
public void info(Object message, Throwable throwable) {
if (log4j == null) getInstance();
log4j.info(message, throwable);
}
@Override
public void warn(Object message, Throwable throwable) {
if (log4j == null) getInstance();
log4j.warn(message, throwable);
}
@Override
public void warn(Object message) {
if (log4j == null) getInstance();
log4j.warn(message);
}
@Override
public void error(Object message) {
if (log4j == null) getInstance();
log4j.error(message);
}
@Override
public void error(Object message, Throwable throwable) {
if (log4j == null) getInstance();
log4j.error(message, throwable);
}
}
3. 日志消息格式化
为了保持日志一致性,引入 LogUtils 处理类名、业务名、SQL 等信息的拼接。这能让日志内容结构化,便于后续分析。
package com.example.log;
public final class LogUtils {
private final static String SEPARATOR = " ";
/**
* 准备日志内容
*
* @param className 类名
* @param businessName 业务名
* @param message 输出内容
* @param sql SQL 内容
* @return 格式化后的日志缓冲区
*/
public static StringBuffer prepareLogMessage(Class className, String businessName,
String message, String sql) {
StringBuffer logBuffer = new StringBuffer();
String name = null;
if (className != null) {
name = className.getName();
logBuffer.append(name).append(SEPARATOR);
}
if (businessName != null) {
logBuffer.append(businessName).append(SEPARATOR);
}
if (message != null) {
logBuffer.append(message).append(SEPARATOR);
} else {
logBuffer.append(SEPARATOR);
}
if (sql != null) {
logBuffer.append(sql).append(SEPARATOR);
} else {
logBuffer.append(SEPARATOR);
}
return logBuffer;
}
/**
* 非 SQL 用 日志内容准备
*/
public static StringBuffer setTextMessage(Class className, String businessName, String message) {
return prepareLogMessage(className, businessName, message, null);
}
/**
* SQL 用 日志内容准备
*/
public static StringBuffer setTextMessage(Class className, String businessName, String message, String sql) {
return prepareLogMessage(className, businessName, message, sql);
}
/**
* 仅消息日志准备
*/
public static StringBuffer setTextMessage(String message) {
return prepareLogMessage(null, null, message, null);
}
/**
* 业务名 + 消息日志准备
*/
public static StringBuffer setTextMessage(String businessName, String message) {
return prepareLogMessage(null, businessName, message, null);
}
}
4. 配置说明
记得在项目资源目录下放置 log4j.properties 文件,确保 PropertyConfigurator 能找到配置。例如:
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c - %m%n
这种封装方式不仅降低了代码耦合度,还统一了日志输出标准,在实际维护中非常实用。
