0x00 需求
目前,公司有个需求,希望能把我们项目中,用户的登录日志、操作日志记录到日志文件中。由于是Multi-Tenant,所以每个实例都有自己的用户。那么实现思路和步奏应该就是:1. 自定义Logger(自定义级别和过滤器)。 2. 自定义过滤器。3. 配置文件。
0x01 自定义Logger
package net.wechance.onlinelog.log;
import java.io.IOException;
import org.apache.log4j.Appender;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.net.SyslogAppender;
public class UserOnlineLogger {
private static final Logger logger = Logger.getLogger(UserOnlineLogger.class);
private static class SeriousLevel extends Level {
private SeriousLevel(int level, String name, int sysLogLevel) {
super(level, name, sysLogLevel);
}
}
/**
* 自定义级别
* 此处的level是自定义的,可以参考下面自定义过滤器的级别
*/
private static final Level USER_ONLINE = new SeriousLevel(20050000, "USER_ONLINE", SyslogAppender.LOG_LOCAL0);
public static void log(Object pm_objLogInfo, String tenantId) {
// 样式
Layout layout = new PatternLayout("%m%n");
// Appender
Appender appender = null;
// 根据传进来的tenantId动态生成path
// 注意生成的文件是userOnline
String path = tenantId + "/logs/userOnlineLog/userOnline";
try {
appender = new DailyRollingFileAppender(layout, path, "'.'yyyy-MM-dd'.log'");
} catch (IOException e) {
e.printStackTrace();
}
// 指定Appender
logger.addAppender(appender);
logger.log(USER_ONLINE, pm_objLogInfo);
}
}
仔细看看,和我们在配置文件中定义一个Appender很相似。
0x02 自定义过滤器
package net.wechance.onlinelog.log;
import org.apache.log4j.spi.Filter;
import org.apache.log4j.spi.LoggingEvent;
public class CustomerLogFilter extends Filter {
boolean acceptOnMatch = false;
private String levelMin;
private String levelMax;
public String getLevelMin() {
return levelMin;
}
public void setLevelMin(String levelMin) {
this.levelMin = levelMin;
}
public String getLevelMax() {
return levelMax;
}
public void setLevelMax(String levelMax) {
this.levelMax = levelMax;
}
public boolean isAcceptOnMatch() {
return acceptOnMatch;
}
public void setAcceptOnMatch(boolean acceptOnMatch) {
this.acceptOnMatch = acceptOnMatch;
}
@Override
public int decide(LoggingEvent lgEvent) {
int inputLevel = lgEvent.getLevel().toInt();
if (inputLevel >= getLevel(levelMin) && inputLevel <= getLevel(levelMax)) {
return 0;
}
return -1;
}
private int getLevel(String level) {
level = level.toUpperCase();
// 用户操作日志级别
if (level.equals("USER_OPT")) {
return LevelType.USER_OPT.getType();
}
// 用户在线日志级别
if (level.equals("USER_ONLINE")) {
return LevelType.USER_ONLINE.getType();
}
if (level.equals("OFF")) {
return LevelType.OFF.getType();
}
if (level.equals("FATAL")) {
return LevelType.FATAL.getType();
}
if (level.equals("ERROR")) {
return LevelType.ERROR.getType();
}
if (level.equals("INFO")) {
return LevelType.INFO.getType();
}
if (level.equals("WARN")) {
return LevelType.WARN.getType();
}
if (level.equals("DEBUG")) {
return LevelType.DEBUG.getType();
}
if (level.equals("ALL")) {
return LevelType.ALL.getType();
}
return LevelType.OFF.getType();
}
private static enum LevelType {
OFF(2147483647),
FATAL(50000),
ERROR(40000),
WARN(30000),
INFO(20000),
DEBUG(10000),
ALL(-2147483648),
// 用户操作日志级别
USER_OPT(20050001),
// 用户在线日志级别
USER_ONLINE(20050000);
int type;
public int getType() {
return type;
}
private LevelType(int type) {
this.type = type;
}
}
}
0x03 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' >
<appender name="userOnline" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="userOnlineLog/userOnline"></param>
<param name="Append" value="true" />
<param name="DatePattern" value="'.'yyyy-MM-dd'.log'" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%m%n" />
</layout>
<filter class="net.wechance.onlinelog.log.CustomerLogFilter">
<param name="LevelMin" value="USER_ONLINE" />
<param name="LevelMax" value="USER_ONLINE" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
<appender name="ERR" class="org.apache.log4j.DailyRollingFileAppender">
<!-- 设置日志输出文件名 -->
<param name="File" value="/logs/error" />
<!-- 设置是否在重新启动服务时,在原有日志的基础添加新日志 -->
<param name="Append" value="true" />
<param name="DatePattern" value="'.'yyyy-MM-dd'.html'" />
<layout class="org.apache.log4j.HTMLLayout" />
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="ERROR" />
<param name="LevelMax" value="ERROR" />
</filter>
</appender>
<!-- 定义各个包中的类日志appender -->
<category name="com.opensymphony.sitemesh">
<priority value="ERROR"/>
<appender-ref ref="ERR"/>
</category>
<category name="org.apache.struts2">
<priority value="ERROR"/>
<appender-ref ref="ERR"/>
</category>
<category name="org.apache">
<priority value="ERROR"/>
<appender-ref ref="ERR"/>
</category>
<category name="org.drools">
<priority value="ERROR"/>
<appender-ref ref="ERR"/>
</category>
<category name="catalia">
<priority value="ERROR"/>
<appender-ref ref="ERR"/>
</category>
<!-- 根logger的设置-->
<root>
<priority value ="debug"/>
<appender-ref ref="userOnline" />
<appender-ref ref="ERR"/>
</root>
</log4j:configuration>
0x04 日志记录
/**
* 记录用户在线日志
* @param dto 用户对象
* @param flag 上下线标识符
*/
private void log(String userName, String flag) {
StringBuffer strBuff = new StringBuffer();
// id
strBuff.append(UUID.randomUUID()).append(",")
// 用户
.append(userName).append(",")
// 时间
.append(new Date()).append(",")
// 在线标志
.append(flag);
UserOnlineLogger.log(strBuff.toString(), appctx.getCurrentTenantId());
}
0x05 完成
Okey, 那么到此使用log4j自定义级别、自定义过滤器、动态路径就已经完成啦。赶紧试试吧。