我有log4j的Struts应用程序显示有关应用程序的信息。
对格式的日志的输出模式如下:
log4j.appender.RALL.layout.ConversionPattern=[%p] %d{dd/MM/yyyy HH:mm:ss} [THREAD ID=%t] [CLASS=(%C{1}:%L)] %m%n
我需要显示线程ID,而不是在日志中的线程名 。 该显示线程名转换字符为%吨。 我不log4j文档中见得到它的方式。
谁能帮我??
我有log4j的Struts应用程序显示有关应用程序的信息。
对格式的日志的输出模式如下:
log4j.appender.RALL.layout.ConversionPattern=[%p] %d{dd/MM/yyyy HH:mm:ss} [THREAD ID=%t] [CLASS=(%C{1}:%L)] %m%n
我需要显示线程ID,而不是在日志中的线程名 。 该显示线程名转换字符为%吨。 我不log4j文档中见得到它的方式。
谁能帮我??
这是可能的,但不是那么容易的只是使用一些预配置模式。
Log4j的1.x和2.x的Log4j中没有打印线程ID预配置的模式,但你总是可以使用一些“魔术”。
PatternLayout
使用PatternParser
类,这是标记作为final
类和具有“模式”作为键和静态地图Converters
类作为值。 每次当使用解析用于记录图案格式发现图案开始%
,它使用与在地图中该模式键匹配转换器。
你不能自己的规则添加到地图,但你仍然可以编写自己的MyOwnPatternLayout:
public class MyOwnPatternLayout extends PatternLayout
这将在它的format
方法做这样的伎俩:
public String format(LoggingEvent event) {
String log = super.format(event);
/*
Now you just have to replace with regex all occurences of %i or
any mark you would like to use as mark to represent Thread ID
with Thread ID value.
Only thing you have to be sure to not use any mark as your Thread ID
that already is defined by PatterParser class
*/
return log.replaceAll("%i", someThreadID);
}
唯一的问题是,你必须得在某种程度上是线程ID。 有时候,你所要做的就是解析线程名称可你轻松收集:
String threadName = event.getThreadName();
例如Apache的Tomcat的放线程ID在线程名HTTP-nio- / 127.0.0.1-8084" -exec-41的端部。
为了确保线程ID是正确的,你也可以让自己的LogginEvent和记录器(MyLoggingEvent和MyLogger)的子类,里面MyLogger创建MyLoggingEvent魔女也将作为参数线程ID不仅线程名。 然后,你可以伊斯利其收集在上面的代码。
对不起,长的答案,我希望这至少可以给你一些帮助。
你能做到这一点的方法之一是它自己使用log4j的MDC增加。 我们用它来添加用户名的Web请求。 我们在每个请求的开始做这一个过滤器。 例如。
import org.apache.log4j.MDC;
...
// Add username to MDC
String username = ...;
MDC.put("user", username);
然后添加[%X{user}]
到您的变换图案。
我实现了线程ID和线程的优先级为即将到来的2.6。 追踪位置: https://issues.apache.org/jira/browse/LOG4J2-1299
您可以从Apache快照库拿起一块2.6快照生成: https://repository.apache.org/content/repositories/snapshots/
您可以使用ThreadContext地图元数据提供给log4j2。 这是,你可以通过正常的格式添加值的字符串映射。
String threadId = String.valueOf(Thread.currentThread().getId());
ThreadContext.put("TId", threadId);
而一个更合理的模式:
<PatternLayout pattern="%d{yyyyMMdd}T%d{HHmmss.SSS} %-5level [%t] [%5X{TId}] %15c{1} - %msg%n"/>
在“鱼标注”全Log4j2文档
我认为这是不可能的,以显示与标准的log4j格式线程ID。 我还通过代码调查PatterParser
类的什么也没有发现它是有用的。 我发现了一些定制的解决方案,但仅限于具有IBM服务器%i
的选择:
%I:插入线程ID。 不同于螺纹名(%T表示),这是线程的数字ID。 请注意,这个参数是特别启动,而在这里列出的其他参数与log4j的标准。
请参阅此链接
扩展PatternLayout
如下,然后指定MyPatternLayout
与$X{threadId}
格式字符串中。
此实现使用ThreadLocal
,以尽量减少计算线程ID对性能的影响:
MyPatternLayout extends PatternLayout {
private final ThreadLocal<String> threadId = new ThreadLocal<String>() {
@Override
protected String initialValue() {
String t = Long.toString(Thread.currentThread().getId());
MDC.put("threadId", t);
return t;
}
};
@Override
public String format(LoggingEvent event) {
this.threadId.get();
return super.format(event);
}
}
一个可能的解决方案是创建你自己的类,它位于你的代码和Log4J的之间并追加线程ID的每一个日志消息:
public class ThreadLogger
{
// Constructor declared private to prevent instantiation. Use static methods instead.
private ThreadLogger() {}
private static enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARN,
ERROR
}
public static void trace(String message)
{
logMessage(message, LogLevel.ERROR);
}
public static void debug(String message)
{
logMessage(message, LogLevel.ERROR);
}
public static void info(String message)
{
logMessage(message, LogLevel.ERROR);
}
public static void warn(String message)
{
logMessage(message, LogLevel.WARN);
}
public static void error(String message)
{
logMessage(message, LogLevel.ERROR);
}
private static void logMessage(String message, LogLevel logLevel)
{
// Get the Log4J logger for the class that originally wanted to log the message
String callingClassName = Thread.currentThread().getStackTrace()[3].getClassName();
Class callingClass;
try
{
callingClass = Class.forName(callingClassName);
}
catch(ClassNotFoundException e)
{
String errorMessage = String.format("Could not reference class [%s]. Unable to log call!", callingClassName);
throw new RuntimeException(errorMessage);
}
Logger logger = Logger.getLogger(callingClass);
// Get the thread ID and place it in front of the logged message
long threadId = Thread.currentThread().getId();
String formattedMessage = String.format("[%s] %s", threadId, message);
// Log the message
switch(logLevel)
{
case TRACE:
logger.trace(formattedMessage);
break;
case DEBUG:
logger.debug(formattedMessage);
break;
case INFO:
logger.info(formattedMessage);
break;
case WARN:
logger.warn(formattedMessage);
break;
case ERROR:
logger.error(formattedMessage);
break;
}
}
}
缺点:
1234 [main] INFO com.foo.bar.Baz - [1] Hello world on thread #1! 1234 [main] INFO com.foo.bar.Baz - [2] Hello world on thread #2!
创建我自己的appender并设置Thread.currentThread()。的getId()到MDC属性。 %X {}的threadId应该给我的线程ID。 因为1.2.15此解决方案的工作。 然后,您可以将AsyncAppender这一点。
public class CurrentThreadIdAppender extends AppenderSkeleton implements AppenderAttachable {
private final AppenderAttachableImpl appenders = new AppenderAttachableImpl();
...
@Override
protected void append(LoggingEvent event) {
synchronized (appenders) {
event.setProperty("threadId", String.valueOf(Thread.currentThread().getId()));
appenders.appendLoopOnAppenders(event);
}
}
...
}
与log4j2另一种优雅的解决方案是使用org.apache.logging.log4j.core.pattern.LogEventPatternConverter
。
你可以写这样的一类
@Plugin(name = "ThreadIdConverter", category = "Converter")
@ConverterKeys({ "tid" })
public class ThreadIdConverter extends LogEventPatternConverter {
protected ThreadIdConverter(String name, String style) {
super(name, style);
}
@Override
public void format(LogEvent event, StringBuilder toAppendTo) {
toAppendTo.append(getThreadId());
}
protected String getThreadId() {
long id = Thread.currentThread().getId();
return Long.toHexString(id);
}
public static ThreadIdConverter newInstance(String[] options) {
return new ThreadIdConverter("tid", "tid");
}
}
通过这种方式,你要创建一个新的格局tid
,当你定义的appender的布局,你可以用它
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout>
<Pattern>%d{dd-MMM HH:mm:ss.SSS} %-7level [%5tid] %logger - %message%n</Pattern>
</PatternLayout>
</Console>
</Appenders>
要记住的最后重要的是如何激活您的log4j2插件。 要做到这一点,你必须添加使用包含log4j2的配置文件中的插件包package
上的属性Configuration
节点
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE Configuration>
<Configuration status="warn"
packages="my.package.logging.plugins">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout>
<Pattern>%d{dd-MMM HH:mm:ss.SSS} %-7level [%5tid] %logger - %message%n</Pattern>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Root level="warn">
<AppenderRef ref="console" />
</Root>
<Logger name="my.package" level="trace" />
</Loggers>
</Configuration>