log4j日志扩展---PatternLayout
目前扩展log4j的日志一般使用扩展adaper的方法,这里使用一种扩展PatternLayout方法。
log4j.rootLogger=debug, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
这是log4j官网上的配置
请注意:
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.R.layout=org.apache.log4j.PatternLayout
注意到其实这是两个类
那么org.apache.log4j.ConsoleAppender可以自定义,思考是否可以自定义log4j.appender.R.layout=org.apache.log4j.PatternLayout
下载官方文件发现有这样两个类
package examples;
import org.apache.log4j.*;
import org.apache.log4j.helpers.PatternParser;
public class MyPatternLayout extends PatternLayout {
public
MyPatternLayout() {
this(DEFAULT_CONVERSION_PATTERN);
}
public
MyPatternLayout(String pattern) {
super(pattern);
}
public
PatternParser createPatternParser(String pattern) {
return new MyPatternParser(
pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern);
}
public
static void main(String[] args) {
Layout layout = new MyPatternLayout("[counter=%.10#] - %m%n");
Logger logger = Logger.getLogger("some.cat");
logger.addAppender(new ConsoleAppender(layout, ConsoleAppender.SYSTEM_OUT));
logger.debug("Hello, log");
logger.info("Hello again...");
}
}
package examples;
import org.apache.log4j.helpers.FormattingInfo;
import org.apache.log4j.helpers.PatternConverter;
import org.apache.log4j.helpers.PatternParser;
import org.apache.log4j.spi.LoggingEvent;
public class MyPatternParser extends PatternParser {
int counter = 0;
public
MyPatternParser(String pattern) {
super(pattern);
}
public
void finalizeConverter(char c) {
if (c == '#') {
addConverter(new UserDirPatternConverter(formattingInfo));
currentLiteral.setLength(0);
} else {
super.finalizeConverter(c);
}
}
private class UserDirPatternConverter extends PatternConverter {
UserDirPatternConverter(FormattingInfo formattingInfo) {
super(formattingInfo);
}
public
String convert(LoggingEvent event) {
return String.valueOf(++counter);
}
}
}
直接粘过去找个类测试一下发现是可以使用的,显示打印日志的行数
发现MyPatternLayout基本调用的是父类的方法
继续扒
public
void finalizeConverter(char c) {
if (c == '#') {
addConverter(new UserDirPatternConverter(formattingInfo));
currentLiteral.setLength(0);
} else {
super.finalizeConverter(c);
}
}
注意看这段代码
发现字符为"#"的时候,创建了UserDirPatternConverter
推荐大家去看PatternParser这里面有答案
给大家举个例子
%#{MMMM}
使用extractOption()可以获得MMMM
有了MMMM是不是就可以处理很多问题.
想一下,比如我们要在日志里面打印公司的编号 orgId
那么首先配置
log4j.appender.R.layout.ConversionPattern= %#{orgId} %p %t %c - %m%n
这样的话
if (c == '#') {
String exs = super.extractOption(); //获取orgId
addConverter(new ExrPatternConverter(formattingInfo, exs));
currentLiteral.setLength(0);
} else {
super.finalizeConverter(c);
}
考虑orgId的赋值问题
使用ThreadLocal
故完整的代码
package com.yogapay.core;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.helpers.FormattingInfo;
import org.apache.log4j.helpers.PatternConverter;
import org.apache.log4j.helpers.PatternParser;
import org.apache.log4j.spi.LoggingEvent;
public class ExPatternParser extends PatternParser {
static final ThreadLocal<Map<String, Object>> TH_LOCAL = new ThreadLocal<Map<String, Object>>(){
@Override
protected HashMap<String, Object> initialValue() {
return new HashMap<String, Object>();
}
};
public static void setCurrentValue(String key, Object value) {
Map<String, Object> map = TH_LOCAL.get();
map.put(key, value);
}
public ExPatternParser(String pattern) {
super(pattern);
}
public void finalizeConverter(char c) {
if (c == '#') {
String exs = super.extractOption();
addConverter(new ExrPatternConverter(formattingInfo, exs));
currentLiteral.setLength(0);
} else {
super.finalizeConverter(c);
}
}
private class ExrPatternConverter extends PatternConverter {
private String cfg;
ExrPatternConverter(FormattingInfo formattingInfo, String cfg) {
super(formattingInfo);
this.cfg = cfg;
}
public String convert(LoggingEvent event) {
Map<String, Object> valueMap = TH_LOCAL.get();
if (valueMap != null) {
Object value = valueMap.get(cfg);
if (value != null) {
return String.valueOf(value);
}
}
return "";
}
}
}
package com.yogapay.core;
import org.apache.log4j.*;
import org.apache.log4j.helpers.PatternParser;
public class ExPatternLayout extends PatternLayout {
public ExPatternLayout() {
this(DEFAULT_CONVERSION_PATTERN);
}
public ExPatternLayout(String pattern) {
super(pattern);
}
@Override
public PatternParser createPatternParser(String pattern) {
return new ExPatternParser(pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern);
}
}
### set log levels ###
log4j.rootLogger = info,stdout
### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = com.yogapay.core.ExPatternLayout
log4j.appender.stdout.layout.ConversionPattern = lgcNo:%#{orgId} %d{yyyy-MM-dd HH:mm:ss} [%t] %p [%c] - %m%n
到此扩展就完成呢!
扩展log4j---自定义日志文件创建
之前使用过logstash,发现它的文件写入功能比log4j更加完善
logstash允许动态创建文件夹,我只使用了按照日期创建日志文件
如果配置日志路径如下
/home/log/log/%{yyyy}/%{MM}/%{dd}-log.log
那么就会创建路径为/home/log/log/2018/06/12-log.log的文件
log4j有个DailyRollingFileAppender,支持按一定时间规则写入日志,但是创建规则是在原有的日志文件之后加上时间后缀,这样所有日志文件都在一个文件夹,非常不友好
因此,我自定义了appender,允许创建文件夹的方式存储日志文件,每天凌晨写入新的日志文件
内容如下
import org.apache.log4j.spi.LoggingEvent;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EveryDayRollingFileAppender extends FileAppender {
private SimpleDateFormat format;
private String fileName;
private Level level;
private static final String SEPERTOR = "-";
private String regex = "\\{([^\\{\\}]+)\\}";//
private long next;
private Date now;
public Level getLevel() {
return level;
}
public void setLevel(Level level) {
this.level = level;
}
public EveryDayRollingFileAppender() {
now = new Date();
level = Level.WARN;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
//TODO 18-6-16 该方法被调用了两次
@Override
public void activateOptions() {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(fileName);
StringBuilder builder = new StringBuilder();
while (matcher.find()) {
String temp = matcher.group(1);
builder.append(temp).append(SEPERTOR);
}
if (builder.length() != 0) {
format = new SimpleDateFormat(builder.deleteCharAt(builder.length() - 1).toString());
changeFile();
setNextTime();日志采集小工具
} else {
format = null;
try {
setFile(fileName, getAppend(), getBufferedIO(), getBufferSize());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void changeFile() {
if (format != null) {
try {
setFile(createFile(), getAppend(), getBufferedIO(), getBufferSize());
} catch (IOException e) {
e.printStackTrace();
}
}
}
protected void setNextTime() {
Calendar c = Calendar.getInstance();
c.setTime(now);
c.add(Calendar.DAY_OF_YEAR, 1);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.HOUR, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
next = c.getTimeInMillis();
System.out.println(new Date(next));
}
@Override
protected void subAppend(LoggingEvent event) {
if (event.getLevel().syslogEquivalent <= level.syslogEquivalent) {
long n = System.currentTimeMillis();
if (n >= next) {
now.setTime(n);
changeFile();
setNextTime();
}
super.subAppend(event);
}
}
public String createFile() {
String file = format.format(now);
String s[] = file.split(SEPERTOR);
String temp = fileName;
for (int i = 0; i < s.length; i++) {
temp = temp.replaceFirst(regex, s[i]);
}
File f = new File(temp);
if (!f.getParentFile().exists())
f.getParentFile().mkdirs();
else if (f.exists())
f.delete();
return f.getAbsolutePath();
}
}
log4j配置如下
log4j.appender.logfile=org.apache.log4j.EveryDayRollingFileAppender
# 需要写入的日志级别
log4j.appender.logfile.level=INFO
# 日志路径
log4j.appender.logfile.fileName=/home/inkbox/log/log/{yyyy}/{MM}/{dd}_logSocket.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%m%n
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。