#log4j2
Log4j 2 API提供应用程序应该编写的接口,并提供实现者创建日志记录实现所需的适配器组件。虽然Log4j 2的API和实现是分开的,但是这样做的主要目的是不允许多个实现,尽管这当然是可行的,但是要明确定义什么类和方法可以在“正常”应用程序代码中使用是安全的。
替代参数:
Logger logger = LogManager.getLogger(MyApp.class.getName());
if (logger.isDebugEnabled()) {
logger.debug("Logging in user " + user.getName() + " with birthday " + user.getBirthdayCalendar());
}
Log4j2中建议下面的写法:
logger.debug("Logging in user {} with birthday {}", user.getName(), user.getBirthdayCalendar());
因为第一种写法会对logging级别进行2次检查,使用上面建议的代码,只会检查一次记录级别,只有当启用调试日志记录时才会发生字符串构造。
格式化参数
为了方便格式化,您可以使用与Java的Formatter相同的格式字符串 。要使用格式化日志记录器,必须调用其中一个LogManager的getFormatterLogger方法。
Logger logger = LogManager.getFormatterLogger(MyApp.class.getName());
logger.debug("Logging in user %s with birthday %s", "ruyi", "1990-04-02");
logger.debug("Logging in user %1$s with birthday %2$tm %2$te,%2$tY", "name", new Date());
logger.debug("Integer.MAX_VALUE = %,d", Integer.MAX_VALUE);
logger.debug("Long.MAX_VALUE = %,d", Long.MAX_VALUE);
打印结果:
2017-06-03 22:17:14 DEBUG com.foo.MyApp [main] -main-24- Logging in user ruyi with birthday 1990-04-02
2017-06-03 22:17:14 DEBUG com.foo.MyApp [main] -main-25- Logging in user name with birthday 06 3,2017
2017-06-03 22:17:14 DEBUG com.foo.MyApp [main] -main-26- Integer.MAX_VALUE = 2,147,483,647
2017-06-03 22:17:14 DEBUG com.foo.MyApp [main] -main-27- Long.MAX_VALUE = 9,223,372,036,854,775,807
注意:使用LogManager的getFormatterLogger方法时上面的替代参数
就不起作用了。
混合记录器和格式化记录器(Mixing Loggers with Formatter Loggers)
格式化记录器对输出格式进行了细粒度的控制,但是缺点是必须指定正确的参数类型(例如,给%d格式传递参数的类型非十进制整数时就会发生异常)。如果你的主要使用代替参数{},但有时你需要对输出格式进行细粒度控制,则可以使用printf方法:
public static Logger logger = LogManager.getLogger(MyApp.class.getName());
logger.debug("Opening connection to {}...", "someDataSource");
logger.printf(Level.INFO, "Logging in user %1$s with birthday %2$tm %2$te,%2$tY", "userName", new Date());
打印结果:
2017-06-03 22:34:26 DEBUG com.foo.MyApp [main] -main-30- Opening connection to someDataSource...
2017-06-03 22:34:26 INFO com.foo.MyApp [main] -main-31- Logging in user userName with birthday 06 3,2017
Java 8 lambda支持懒惰日志记录
在版本2.4中,Logger接口增加了对lambda表达式的支持。这允许客户端代码在不显式检查所请求的日志级别是否启用的情况下延迟日志记录。
if (logger.isTraceEnabled()) { // java8之前
logger.trace("Some long-running operation returned {}", expensiveOperation());
}
// java8就可以这样写
logger.trace("Some long-running operation returned {}", () -> expensiveOperation());
记录器名称(Logger Names)
大多数日志记录实现使用分层方案来将日志记录名与日志配置相匹配。在记录器名称中,与用于Java包名称的层次结构非常相似。 例如,org.apache.logging.appender和org.apache.logging.filter都有org.apache.logging作为其父级。 在大多数情况下,应用程序通过将当前类的名称传递给LogManager.getLogger来命名其记录器。 因为这种用法很常见,Log4j 2规定当logger name参数被省略或为null时,作为默认值。 例如,在下面的所有示例中,Logger指定名称“org.apache.test.MyTest”。
package org.apache.test;
// 方式1:
public class MyTest {
private static final Logger logger = LogManager.getLogger(MyTest.class);
}
// 方式2:
public class MyTest {
private static final Logger logger = LogManager.getLogger(MyTest.class.getName());
}
// 方式3:
public class MyTest {
private static final Logger logger = LogManager.getLogger();
}
方式2是最常用的方式。
Logger类提供了对遵循应用程序执行路径非常有用的日志记录方法。这些方法生成的logging events可以过滤分离来自其他的debug logging。
最常用的方法是entry()
或traceEntry()
和exit()
或traceExit()
方法。
entry()或traceEntry()
应该放在方法体的开头,除了简单的getter和setter之外。这两个方法的logging level是trace,都是使用'Enter'
作为标记,这个标记也是FLOW
标记。所有消息字符串都以“event”开头,即使用的要么是格式化字符串要么是Message。
entry()参数个数是0-多个,通常这些参数是方法体的参数。traceEntry()
可以传递一个格式化的字符串和一个变量的参数列表,或者一个Message。这两个方法的区别在于,entry()
方法接收的参数都是是来自方法体的参数列表,traceEntry()
方法接收的参数第一个是格式化字符串,第二个是来自方法体的参数列表。
public void doSomething(String foo, int bar) {
LOGGER.entry(foo, bar);
// do something
}
public void doSomething(String foo, int bar) {
LOGGER.traceEntry("Parameters: {} and {}", foo, bar);
// do something
}
or:
public int doSomething(String foo, int bar) {
Message m = LOGGER.traceEntry("doSomething(foo={}, bar={})", foo, bar);
// do something
return traceExit(m, value);
}
一个exit()
或traceExit()
方法应该放在任何返回语句之前,或放在一个没有return语句的方法体的最后。调用exit()
或traceExit()
时可以传参也可以不传参。一般,当方法体返回void时使用exit()
或traceExit()
,当方法体返回Object obj时使用exit(Object obj)
或者traceExit(object, new SomeMessage(object))
。输出的日志级别也是TRACE,使用的标记是“EXIT”,也是一种“FLOW”标记,所有消息字符串都以“exit”开头,即使用的要么是格式化字符串要么是Message。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<!-- Flow tracing is most useful with a pattern that shows location.
Below pattern outputs class, line number and method name. -->
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</Console>
<File name="log" fileName="target/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="log"/>
</Root>
</Loggers>
</Configuration>
日志记录框架的主要目的之一是仅在需要时提供生成调试和诊断信息的方法,并允许对该信息进行过滤,使其不会压倒系统或个人需要使用它。作为示例,应用程序希望将其条目,退出和其他操作与正在执行的SQL语句分开记录,并希望能够将查询与更新分开记录。
import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
public class MyApp {
private Logger logger = LogManager.getLogger(MyApp.class.getName());
private static final Marker SQL_MARKER = MarkerManager.getMarker("SQL");
private static final Marker UPDATE_MARKER = MarkerManager.getMarker("SQL_UPDATE").setParents(SQL_MARKER);
private static final Marker QUERY_MARKER = MarkerManager.getMarker("SQL_QUERY").setParents(SQL_MARKER);
@SuppressWarnings("deprecation")
public String doQuery(String table) {
logger.entry(table);
logger.debug(QUERY_MARKER, "SELECT * FROM {}", table);
return logger.exit(table);
}
@SuppressWarnings("deprecation")
public String doUpdate(String table, Map<String, String> params) {
logger.entry(table,params);
if (logger.isDebugEnabled()) {
logger.debug(UPDATE_MARKER, "UPDATE {} SET {}", table, formatCols(params));
}
return logger.exit(table);
}
private String formatCols(Map<String, String> cols) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Map.Entry<String, String> entry : cols.entrySet()) {
if (!first) {
sb.append(", ");
}
sb.append(entry.getKey()).append("=").append(entry.getValue());
first = false;
}
return sb.toString();
}
public static void main(String[] args) {
MyApp app = new MyApp();
Map<String, String> params = new HashMap<>();
params.put("name", "ry");
params.put("age", "27");
app.doUpdate("ry",params);
}
}
在上面的示例中,现在可以添加MarkerFilters以仅允许记录SQL更新操作,要记录的所有SQL更新或记录MyApp中的所有内容。
在使用标记时,必须考虑一些关于标记的重要规则。
EventLogger类提供了一种简单的机制用于应用程序中发生的logging events。 虽然EventLogger作为启动事件通过审核日志记录系统处理的方式是很有用,但本身不会实现审核日志记录系统需要的任何功能,例如保证传递。详细看官网
在一个典型的web应用程序中使用EventLogger推荐的方式是将请求的整个生命周期相关的数据都存放在org.apache.logging.log4j.ThreadContext
Map中,例如用户的ID,用户的IP地址,产品名称等。这在servlet的filter中很容易实现,ThreadContext
Map也可以在请求结束后清除。当一个事件(event)需要被记录时,应当创建一个StructuredDataMessage
对象并且填充该对象。然后调用EventLogger.logEvent(msg)
其中msg是对StructuredDataMessage
对象的引用。
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.logging.log4j.ThreadContext;
public class RequestFilter implements Filter {
private FilterConfig filterConfig;
private static String TZ_NAME = "timezoneOffset";
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
ThreadContext.put("ipAddress", request.getRemoteAddr());
HttpSession session = request.getSession(false);
if (session != null) {
// Something should set this after authentication completes
String loginId = (String)session.getAttribute("LoginId");
if (loginId != null) {
ThreadContext.put("loginId", loginId);
}
// This assumes there is some javascript on the user's page to create the cookie.
if (session.getAttribute(TZ_NAME) == null) {
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (TZ_NAME.equals(cookie.getName())) {
int tzOffsetMinutes = Integer.parseInt(cookie.getValue());
request.getSession().setAttribute(TZ_NAME, tzOffsetMinutes);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
}
}
}
ThreadContext.put("hostname", servletRequest.getServerName());
ThreadContext.put("productName", filterConfig.getInitParameter("ProductName"));
// 将请求转发给过滤器链上下一个filter对象,如果没有filter那就转发到你请求的资源。
filterChain.doFilter(servletRequest, servletResponse);
// Clears the context map and stack.
ThreadContext.clearAll();;
}
public void destroy() {
}
}
EventLogger的使用示例:
String confirm = UUID.randomUUID().toString().substring(0, 32);
StructuredDataMessage msg = new StructuredDataMessage(confirm, null, "transfer");
msg.put("toAccount", "username1");
msg.put("fromAccount", "username2");
msg.put("amount", "username3");
EventLogger.logEvent(msg);
EventLogger类使用名为“EventLogger”的记录器。EventLogger使用日志记录级别OFF作为默认值,表示无法进行过滤。使用StructuredDataLayout
可以将这些事件格式化打印。(但是在我使用的2.8.1版本中我并没有找到。)
虽然Log4j 2提供了接受字符串和对象的记录器方法,但所有这些都最终被捕获在与对象事件相关联的消息对象中。应用程序可以自由构建自己的消息,并将它们传递给记录器。尽管看起来似乎比将消息格式和参数直接传递给事件更昂贵,但测试表明,使用现代JVM,创建和销毁事件的成本很小,特别是当复杂任务封装在Message而不是应用程序中时。另外,当使用接受字符串和参数的方法时,只有在配置的全局过滤器或记录器的日志级别允许处理消息的情况下,才会创建基础的Message对象。
一个程序中有一个Map对象,其中包含{"Name" = "John Doe", "Address" = "123 Main St.", "Phone" = "(999) 555-1212"}和一个用户对象,用户对象拥有一个getId方法返回”jdoe”。开发者想要增加一条报告信息“User John Doe has logged in using id jdoe”,可以通过下面方式实现:
logger.info("User {} has logged in using id {}", map.get("Name"), user.getId());
虽然没有什么内在的错误,因为物体的复杂性和期望的输出增加这种技术变得更难使用。作为替代,使用消息允许:
logger.info(new LoggedInMessage(map, user));
在这种替代方案中,格式化被委派给LoggedInMessage对象的getFormattedMessage方法。 虽然在此替代方案中创建了一个新对象,但是在LoggedInMessage被格式化之前,不会调用传递给LoggedInMessage的对象上的任何方法。 当Object的toString方法不会产生要在日志中显示的信息时,这一点尤其有用。(LoggedInMessage是实现org.apache.logging.log4j.message.Message
接口的类)
消息的另一个优点是它们简化了编写布局。在其他日志记录框架中,布局必须单独循环参数,并根据遇到的对象确定要执行的操作。使用消息,布局可以根据所遇到的消息类型,将格式委托给消息或执行其格式化。
FormattedMessage
首先检查 传递给FormattedMessage的消息模式 ,看看它是否是一个有效的java.text.MessageFormat模式。如果是,则使用MessageFormatMessage进行格式化。如果不是,请检查它是否包含任何用于String.format()的有效格式说明符的令牌。如果是,则使用StringFormattedMessage进行格式化。最后,如果模式不匹配,那么使用ParameterizedMessage进行格式化。
LocalizedMessage
LocalizedMessage 主要提供与Log4j 1.x的兼容性。通常,本地化的最佳方法是让客户端UI在客户端的区域设置中呈现事件。 LocalizedMessage包含一个ResourceBundle,并允许message pattern参数作为bundle中消息模式的关键。如果未指定bundle,LocalizedMessage将尝试使用用于记录事件的Logger的名称来查找bundle。从包中检索的信息将使用FormattedMessage进行格式化。
LoggerNameAwareMessage
LoggerNameAwareMessage是一个带有setLoggerName方法的接口。在事件构造期间将调用此方法,以使消息具有用于在消息被格式化时记录事件的记录器的名称。
MapMessage消息
MapMessage包含一个字符串键和值的映射。MapMessage实现FormattedMessage并接受“XML”的格式说明符,在这种情况下,Map将被格式化为XML。否则,Map将格式化为“key1 = value1 key2 = value2 ...”。
MessageFormatMessage
MessageFormatMessage 使用转换格式java.text.MessageFormat处理消息。因此处理消息具有比ParameterizedMessage更多的灵活性,但是它也慢了两倍。
MultiformatMessage
一个MultiformatMessage将有一个getFormats方法和一个getFormattedMessage方法,它接受格式化的字符串和数组。可以通过布局调用getFormats方法来向其提供有关Message支持哪些格式化选项的信息。布局然后可以使用一个或多个格式的getFormattedMessage调用。如果消息不能识别格式名称,它将使用其默认格式简单地格式化数据。一个例子是StructuredDataMessage,它接受格式为“XML”的字符串,这将导致它将事件数据格式化为XML而不是RFC 5424格式。
ObjectMessage
通过调用其toString方法来格式化对象。从Log4j 2.6版本开始,尝试低垃圾或无垃圾的Layouts将调用formatTo(StringBuilder)方法。
ParameterizedMessage
ParameterizedMessage 以格式来处理包含“{}”的消息,以表示可替换令牌和替换参数。
ReusableObjectMessage
在无垃圾模式下,此消息用于将记录的对象传递到布局和Appender。功能上等同于ObjectMessage。
ReusableObjectMessage
在无垃圾模式下,该消息用于处理包含“{}”的消息,以表示可替换令牌和替换参数。功能上等同于ParameterizedMessage。
ReusableSimpleMessage
在无垃圾模式下,此消息用于将已记录的字符串和CharSequences传递到Layout和Appender。功能上相当于SimpleMessage。
SimpleMessage中
SimpleMessage包含不需要格式化的String或CharSequence。
StringFormattedMessage
StringFormattedMessage 处理消息时使用符合java.lang.String.format的转换格式。虽然此消息具有比ParameterizedMessage更多的灵活性,但它也是慢了5到10倍。
StructuredDataMessage
StructuredDataMessage允许应用程序向Map添加项目,并设置id以允许根据RFC 5424将消息格式化为结构化数据元素。
ThreadDumpMessage
ThreadDumpMessage(如果记录)将为所有线程生成堆栈跟踪。如果在Java 6+上运行,则堆栈跟踪将包括所保存的任何锁。
TimestampMessage
一个TimestampMessage将提供在事件构造期间调用的getTimestamp方法。消息中的时间戳将用于代替当前时间戳。
Log4j 2包含一个易用的Logger API Scala包装器。Log4j 2 Scala API依赖于Log4j 2 API,Scala运行时库和反射。它目前支持Scala 2.10和2.11。请参阅详细其包含在SBT,Maven,Ivy或Gradle项目中的说明。
maven依赖时:
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api-scala_2.11</artifactId>
<version>2.8.2</version>
</dependency>
</dependencies>
例子:
import org.apache.logging.log4j.scala.Logging
import org.apache.logging.log4j.Level
class MyClass extends BaseClass with Logging {
def doStuff(): Unit = {
logger.info("Doing stuff")
}
def doStuffWithLevel(level: Level): Unit = {
logger(level, "Doing stuff with arbitrary level")
}
}
代替参数
通常,记录的目的是提供有关系统中正在发生的情况的信息,这需要包括有关被处理对象的信息。在Scala中,您可以使用 字符串插值 来实现:
logger.debug(s"Logging in user ${user.getName} with birthday ${user.calcBirthday}")
由于Scala Logger是用宏实现的,所以String结构和方法调用只会在启用调试日志记录时发生。
大多数日志记录实现使用分层方案来将日志记录名与日志配置相匹配。在该方案中,记录器名称层次结构由“。”表示。记录器名称中的字符,非常类似于用于Java / Scala包名称的层次结构。该记录特质都会自动命名记录仪它是正在使用的类。
ScalaDoc
将日志请求插入到应用程序代码中需要大量的规划和努力。 观察显示,约4%的代码专用于记录。 因此,甚至中等大小的应用程序将在其代码中嵌入数千个日志记录语句。 给定它们的数量,管理这些日志语句是必不可少的,而不需要手动修改它们。
Log4j 2的配置可以通过以下4种方式之一完成:
请注意,与Log4j 1.x不同,公共Log4j 2 API不会以任何方式公开添加,修改或删除追加器和过滤器或操作配置的方法。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug">
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n" />
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
以Configuration为根节点,有一个status属性,这个属性表示log4j2本身的日志信息打印级别。如果把status改为TRACE再执行测试代码,可以看到控制台中打印了一些log4j加载插件、组装logger等调试信息。
日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出。additivity
属性设为false时,该Logger对象的事件event不会传递给任何父类的Logger对象包括根对象(root),配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="com.foo.Bar" level="trace" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Java代码:
package com.foo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class MyApp {
private static final Logger logger = LogManager.getLogger(MyApp.class);
public static void main(final String... args) {
// Set up a simple configuration that logs on the console.
logger.trace("Entering application.");
Bar bar = new Bar();
if (!bar.doIt()) {
logger.error("Didn't do it.");
}
logger.trace("Exiting application.");
}
}
package com.foo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Bar {
static final Logger logger = LogManager.getLogger(Bar.class.getName());
public boolean doIt() {
logger.entry();
logger.error("Did it again!");
return logger.exit(false);
}
}
使用Log4j 2 API的应用程序请求时将从LogManager指定一个特殊名称的Logger。 LogManager将找到相应的LoggerContext,然后从中获取Logger。如果Logger必须创建,Logger将关联LoggerConfig,包括a)与Logger相同名称 b)父包的名称 c)root LoggerConfig。LoggerConfig与实际提供LogEvent的Appender相关联。
Log4j包含四个ConfigurationFactory实现:一个用于JSON,一个用于YAML,一个用于properties,一个用于XML。
如果找不到本地配置文件,Log4j将使用提供的默认配置。默认配置由DefaultConfiguration类中提供将设置:
pattern
属性设置为%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
,并追加到上面的ConsoleAppender中。请注意,默认情况下,Log4j会将根记录器分配给Level.ERROR。
当有配置文件时,Log4j能够自动检测配置文件的更改并重新配置自身。如果在configuration
元素上指定了monitorInterval
属性,并将其设置为非零值,则在下次评估和/或 记录日志事件 并自上次检查以来已经过了monitorInterval时,将检查该配置文件。下面的配置,使配置文件至少隔30秒后会检查配置文件是否更改。最小间隔为5秒。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="30">
...
</Configuration>
Log4j允许您轻松地重新定义日志记录行为,而无需修改应用程序。可以对应用程序的某些部分禁用日志记录,仅在满足特定条件时进行日志记录,例如对特定用户执行的操作,到Flume或日志报告系统的路由输出等。能够做到这一点需要理解配置文件的语法。
XML文件中的configuration元素接受几个属性:
属性名称 | 描述 |
advertiser | (可选)将用于通知单独的FileAppender或SocketAppender配置的Advertiser插件名称。提供的唯一的Advertiser插件是“multicastdns”。 |
dest | “err”,将输出到stderr,或文件路径或URL。 |
monitorInterval | 检查更改文件配置之前经过的最短时间(以秒为单位)。 |
name | 配置的名称。 |
packages | 逗号分隔的包名称列表用来搜索插件。每个类加载器只加载一次插件,所以更改此值可能不会对重新配置产生任何影响。 |
schema | 标识类加载器定位用于验证配置的XML模式的位置。仅当strict设置为true时有效。如果未设置,则不会执行模式验证。 |
shutdownHook | 指定当JVM关闭时Log4j是否应自动关闭。关闭挂钩默认启用,但可以通过将此属性设置为“disable”来禁用 |
shutdownTimeout | 指定当JVM关闭时,追加器和后台任务将被关闭几毫秒。默认值为零,这意味着每个appender都使用其默认超时,并且不要等待后台任务。并不是所有的追加者都会尊重这一点,这是一个提示,而不是一个绝对的保证,关机过程不会花费更长时间。将此值设置得太低会增加未写入最终目的地的未完成日志事件的风险。请参阅LoggerContext.stop(long,java.util.concurrent.TimeUnit)。(如果shutdownHook设置为“disable” ,则不使用。) |
status | 应该记录到控制台的内部Log4j事件的级别。此属性的有效值为“trace”,“debug”,“info”,“warn”,“error”和“fatal”。Log4j将向状态记录器记录有关初始化,翻转和其他内部操作的详细信息。如果您需要对log4j进行故障排除,则设置status="trace"是你可用的第一个工具之一。 |
strict | 允许使用严格的XML格式。在JSON配置中不支持。 |
verbose | 加载插件时启用诊断信息。 |
Log4j可以使用两种XML风格进行配置,concise和strict。concise格式使配置非常容易,因为元素名称与它们所代表的组件匹配,但是它不能用XML模式验证。例如,通过在其父appenders元素下声明名为Console的XML元素来配置ConsoleAppender。但是,元素和属性名称不区分大小写。另外,属性可以被指定为XML属性,也可以被指定为没有属性并具有文本值的XML元素。如:
<PatternLayout pattern="%m%n"/>
和
<PatternLayout>
<Pattern>%m%n</Pattern>
</PatternLayout>
是等同的。
<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
<Properties>
<Property name="name1">value</property>
<Property name="name2" value="value2"/>
</Properties>
<*filter* ... />
<Appenders>
<appender ... >
<*filter* ... />
</appender>
...
</Appenders>
<Loggers>
<Logger name="name1">
<*filter* ... />
</Logger>
...
<Root level="level">
<AppenderRef ref="name"/>
</Root>
</Loggers>
</Configuration>
严格的XML
除了上述concise格式的XML格式之外,Log4j允许以更“normal”的XML方式指定配置,可以使用XML模式进行验证。这是通过用下面的对象类型替换上面的友好元素名称来实现的。例如,代替ConsoleAppender使用名为Console的元素进行配置,而是将其配置为type属性包含“Console”属性值的appender元素。
<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
<Properties>
<Property name="name1">value</property>
<Property name="name2" value="value2"/>
</Properties>
<Filter type="type" ... />
<Appenders>
<Appender type="type" name="name">
<Filter type="type" ... />
</Appender>
...
</Appenders>
<Loggers>
<Logger name="name1">
<Filter type="type" ... />
</Logger>
...
<Root level="level">
<AppenderRef ref="name"/>
</Root>
</Loggers>
</Configuration>
以下是使用严格格式的示例配置。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" strict="true" name="XMLConfigTest"
packages="org.apache.logging.log4j.test">
<Properties>
<Property name="filename">target/test.log</Property>
</Properties>
<Filter type="ThresholdFilter" level="trace"/>
<Appenders>
<Appender type="Console" name="STDOUT">
<Layout type="PatternLayout" pattern="%m MDC%X%n"/>
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="DENY" onMismatch="ACCEPT"/>
</Filters>
</Appender>
<Appender type="Console" name="FLOW">
<Layout type="PatternLayout" pattern="%C{1}.%M %m %ex%n"/><!-- class and line number -->
<Filters>
<Filter type="MarkerFilter" marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</Appender>
<Appender type="File" name="File" fileName="${filename}">
<Layout type="PatternLayout">
<Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
</Layout>
</Appender>
<Appender type="List" name="List">
</Appender>
</Appenders>
<Loggers>
<Logger name="org.apache.logging.log4j.test1" level="debug" additivity="false">
<Filter type="ThreadContextMapFilter">
<KeyValuePair key="test" value="123"/>
</Filter>
<AppenderRef ref="STDOUT"/>
</Logger>
<Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false">
<AppenderRef ref="File"/>
</Logger>
<Root level="trace">
<AppenderRef ref="List"/>
</Root>
</Loggers>
</Configuration>
Configuration with JSON
除了XML之外,Log4j也可以使用JSON进行配置。JSON格式与concise的XML格式非常相似。每个键代表一个插件的名称,与它相关联的键/值对是它的属性。一个键包含多于一个简单的值,它本身就是一个从属的插件。在下面的示例中,ThresholdFilter,Console和PatternLayout都是插件,而Console插件将为其名称属性分配一个STDOUT值,并为ThresholdFilter分配一个调试级别。
{ "configuration": { "status": "error", "name": "RoutingTest",
"packages": "org.apache.logging.log4j.test",
"properties": {
"property": { "name": "filename",
"value" : "target/rolling1/rollingtest-$${sd:type}.log" }
},
"ThresholdFilter": { "level": "debug" },
"appenders": {
"Console": { "name": "STDOUT",
"PatternLayout": { "pattern": "%m%n" }
},
"List": { "name": "List",
"ThresholdFilter": { "level": "debug" }
},
"Routing": { "name": "Routing",
"Routes": { "pattern": "$${sd:type}",
"Route": [
{
"RollingFile": {
"name": "Rolling-${sd:type}", "fileName": "${filename}",
"filePattern": "target/rolling1/test1-${sd:type}.%i.log.gz",
"PatternLayout": {"pattern": "%d %p %c{1.} [%t] %m%n"},
"SizeBasedTriggeringPolicy": { "size": "500" }
}
},
{ "AppenderRef": "STDOUT", "key": "Audit"},
{ "AppenderRef": "List", "key": "Service"}
]
}
}
},
"loggers": {
"logger": { "name": "EventLogger", "level": "info", "additivity": "false",
"AppenderRef": { "ref": "Routing" }},
"root": { "level": "error", "AppenderRef": { "ref": "STDOUT" }}
}
}
}
请注意,在RoutingAppender中,Route元素已被声明为数组。这是有效的,因为每个数组元素都将是一个Route组件。这对于appenders和filters元素将不起作用,其中每个元素的concise格式都有不同的名称。如果每个appender或filter使用了名为“type”的属性,它的属性值包含appender,Appenders和filters就可以被定义为数组元素。下面示例说明了如何将多个记录器声明为数组。
{ "configuration": { "status": "debug", "name": "RoutingTest",
"packages": "org.apache.logging.log4j.test",
"properties": {
"property": { "name": "filename",
"value" : "target/rolling1/rollingtest-$${sd:type}.log" }
},
"ThresholdFilter": { "level": "debug" },
"appenders": {
"appender": [
{ "type": "Console", "name": "STDOUT", "PatternLayout": { "pattern": "%m%n" }},
{ "type": "List", "name": "List", "ThresholdFilter": { "level": "debug" }},
{ "type": "Routing", "name": "Routing",
"Routes": { "pattern": "$${sd:type}",
"Route": [
{
"RollingFile": {
"name": "Rolling-${sd:type}", "fileName": "${filename}",
"filePattern": "target/rolling1/test1-${sd:type}.%i.log.gz",
"PatternLayout": {"pattern": "%d %p %c{1.} [%t] %m%n"},
"SizeBasedTriggeringPolicy": { "size": "500" }
}
},
{ "AppenderRef": "STDOUT", "key": "Audit"},
{ "AppenderRef": "List", "key": "Service"}
]
}
}
]
},
"loggers": {
"logger": [
{ "name": "EventLogger", "level": "info", "additivity": "false",
"AppenderRef": { "ref": "Routing" }},
{ "name": "com.foo.bar", "level": "error", "additivity": "false",
"AppenderRef": { "ref": "Console" }}
],
"root": { "level": "error", "AppenderRef": { "ref": "STDOUT" }}
}
}
}
JSON支持使用Jackson数据处理器来解析JSON文件。这些依赖项必须添加到要使用JSON进行配置的项目中:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.8.7</version>
</dependency>
配置记录器(Configuring loggers)
在对Log4j进行操作之前,了解Logger的工作原理至关重要。如果需要更多信息,请参考Log4j架构。尝试配置Log4j而不了解这些概念将导致失败。
使用logger
元素来配置一个LoggerConfig,logger
元素必须要指定name
属性的值,一般也会指定level
属性的值,也许会指定additivity
属性的值。该级别level
属性可以配置为TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF之一。如果没有指定级别,它将默认为ERROR。可以将additivity
属性赋值为true或false,如果省略该属性,将使用默认值false。
可以复制被添加到ThreadContextMap的属性来配置LoggerConfig(包括根LoggerConfig)。这些属性来自 Appenders, Filters, Layouts等的引用,就像它们是ThreadContext Map的一部分一样。属性可以包含在每次事件被记录时解析配置或动态解析的变量。有关使用变量的更多信息,请参阅下面的[属性替换]。
LoggerConfig也可以配置一个或多个AppenderRef元素。引用的每个appender将与指定的LoggerConfig关联。如果在LoggerConfig中配置了多个appender,那么在处理日志记录事件时每个appender会被调用。
每个配置都必须有根记录器。如果没有配置默认根LoggerConfig,那么将使用一个级别为ERROR并是一个Console appender来管理日志。根记录器和其他记录器之间的主要区别是:
配置Appenders
配置appender 使用指定appender插件的名称或者一个appender元素且拥有type属性,属性值包含appender插件的名称。此外,每个appender必须指定唯一一个name属性值。记录器将使用该名称来引用上一个部分所述的appender。
大多数追加者还支持配置布局(可以使用特定的布局插件的名称作为元素或使用“布局”作为元素名称以及包含布局插件的名称的类型属性来指定布局。包含他们正常运行所需的其他属性或元素。
配置Filters
og4j允许在4个位置中指定过滤器:
appenders, loggers and properties
元素处于同一级别。这些过滤器可以在事件传递到LoggerConfig之前接受或拒绝事件。logger
元素中,这些过滤器可以为指定的记录器接受或者拒绝事件。虽然只能配置一个filter
元素,但该元素可以是表示复合过滤器的filters
元素。filters元素中允许配置多个filter
元素。以下示例显示如何在ConsoleAppender上配置多个过滤器。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" name="XMLConfigTest" packages="org.apache.logging.log4j.test">
<Properties>
<Property name="filename">target/test.log</Property>
</Properties>
<ThresholdFilter level="trace"/>
<Appenders>
<Console name="STDOUT">
<PatternLayout pattern="%m MDC%X%n"/>
</Console>
<Console name="FLOW">
<!-- this pattern outputs class name and line number -->
<PatternLayout pattern="%C{1}.%M %m %ex%n"/>
<filters>
<MarkerFilter marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
<MarkerFilter marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
</filters>
</Console>
<File name="File" fileName="${filename}">
<PatternLayout>
<pattern>%d %p %C{1.} [%t] %m%n</pattern>
</PatternLayout>
</File>
<List name="List">
</List>
</Appenders>
<Loggers>
<Logger name="org.apache.logging.log4j.test1" level="debug" additivity="false">
<ThreadContextMapFilter>
<KeyValuePair key="test" value="123"/>
</ThreadContextMapFilter>
<AppenderRef ref="STDOUT"/>
</Logger>
<Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false">
<Property name="user">${sys:user.name}</Property>
<AppenderRef ref="File">
<ThreadContextMapFilter>
<KeyValuePair key="test" value="123"/>
</ThreadContextMapFilter>
</AppenderRef>
<AppenderRef ref="STDOUT" level="error"/>
</Logger>
<Root level="trace">
<AppenderRef ref="List"/>
</Root>
</Loggers>
</Configuration>
使用Properties配置
从2.4版本起,Log4j支持通过属性文件进行配置。请注意,属性语法与Log4j 1中使用的语法不同。像XML和JSON配置一样,属性配置根据插件和插件的属性定义配置。
在版本2.6之前,属性配置要求你在具有这些名称的属性中以逗号分隔的列表列出appenders,filters和 loggers的标识符。那么这些组件中的每一个将被预期在以组件.<.identifier>..开头的属性集中定义。标识符不必与定义的组件的名称相匹配,但必须唯一标识作为组件一部分的所有属性和子组件。如果标识符列表不存在,则标识符不能包含'.'。每个单独的组件必须有一个“type”属性。
从版本2.6开始,不再需要这个标识符列表,因为在首次使用时推断出名称,但是如果您希望使用更复杂的标识符,那么您仍然必须使用该列表。如果列表存在,它将被使用。与基础组件不同,创建子组件时,不能指定包含标识符列表的元素。相反,必须按照下面的滚动文件追加器的策略定义中所示的类型定义包装器元素。然后,在该包装器元素下面定义每个子组件,因为下面定义了TimeBasedTriggeringPolicy和SizeBasedTriggeringPolicy。
属性配置文件支持advertiser, monitorInterval, name, packages, shutdownHook, shutdownTimeout, status, verbose, 和 dest 属性,具体的可以参照上面的配置语法.
log4j2.properties
status = error
dest = err
name = PropertiesConfig
property.filename = target/rolling/rollingtest.log
filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %m%n
appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = ${filename}
appender.rolling.filePattern = target/rolling2/test1-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d %p %C{1.} [%t] %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 2
appender.rolling.policies.time.modulate = true
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=100MB
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 5
appender.list.type = List
appender.list.name = List
appender.list.filter.threshold.type = ThresholdFilter
appender.list.filter.threshold.level = error
logger.rolling.name = com.example.my.app
logger.rolling.level = debug
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = RollingFile
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT
属性替代
Log4j 2支持将配置中的令牌指定在其他地方定义的属性的引用。引用.properties文件,为了实现这一点,Log4j使用Apache Commons Lang的 StrSubstitutor 和StrLookup 类的变体。这允许配置本身声明的属性使用解析声明为$ {name}的变量。例如:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" name="RoutingTest" packages="org.apache.logging.log4j.test">
<Properties>
<Property name="filename">target/rolling1/rollingtest-$${sd:type}.log</Property>
</Properties>
<ThresholdFilter level="debug"/>
<Appenders>
<Console name="STDOUT">
<PatternLayout pattern="%m%n"/>
</Console>
<List name="List">
<ThresholdFilter level="debug"/>
</List>
<Routing name="Routing">
<Routes pattern="$${sd:type}">
<Route>
<RollingFile name="Rolling-${sd:type}" fileName="${filename}"
filePattern="target/rolling1/test1-${sd:type}.%i.log.gz">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
</PatternLayout>
<SizeBasedTriggeringPolicy size="500" />
</RollingFile>
</Route>
<Route ref="STDOUT" key="Audit"/>
<Route ref="List" key="Service"/>
</Routes>
</Routing>
</Appenders>
<Loggers>
<Logger name="EventLogger" level="info" additivity="false">
<AppenderRef ref="Routing"/>
</Logger>
<Root level="error">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
虽然这是有用的,但还有更多来源的地方。为了适应这一点,Log4j还支持语法${prefix:name}
,其中前缀标识告诉Log4j应在特定上下文中使用的变量名称。请参阅详细信息,内置于Logj4的上下文如下边:
前缀 | 上下文 |
bundle | 资源束。格式为$ {bundle:BundleName:BundleKey}。捆绑包名称遵循包命名约定,例如: $ {bundle:com.domain.Messages:MyKey}。 |
ctx | 线程上下文映射(MDC) |
date | 使用指定的格式插入当前日期和/或时间 |
env | 系统环境变量 |
jndi | 在默认的JNDI上下文中设置的值。 |
jvmrunargs | 通过JMX访问的JVM输入参数,但不是主要参数; 请参阅[RuntimeMXBean.getInputArguments](http://docs.oracle.com/javase/6/docs/api/java/lang/management/RuntimeMXBean.html#getInputArguments--)在Android上不可用 |
log4j | Log4j配置属性。表达式```${log4j:configLocation}```和```${log4j:configParentLocation}```分别提供给log4j的配置文件和它的父文件夹的绝对路径。 |
main | 使用 MapLookup.setMainArguments(String[])设置的值 |
map | 来自MapMessage的值 |
sd | 来自StructuredDataMessage的值。“id”将返回没有企业号的StructuredDataId的名称。“type”将返回消息类型。其他键将从Map中取回单个元素。 |
sys | 系统属性 |
默认属性映射可以在配置文件中声明。如果该值不能位于指定的查找中,则将使用默认属性映射中的值。默认映射预先填充有当前系统的主机名或IP地址的“hostName”值,“contextName”与当前日志上下文的值相对应。
XInclude
XML配置文件可以使用XInclude包含其他文件。这是一个包含两个其他文件的log4j2.xml文件示例:
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns:xi="http://www.w3.org/2001/XInclude"
status="warn" name="XIncludeDemo">
<properties>
<property name="filename">xinclude-demo.log</property>
</properties>
<ThresholdFilter level="debug"/>
<xi:include href="log4j-xinclude-appenders.xml" />
<xi:include href="log4j-xinclude-loggers.xml" />
</configuration>
log4j-xinclude-appenders.xml:
<?xml version="1.0" encoding="UTF-8"?>
<appenders>
<Console name="STDOUT">
<PatternLayout pattern="%m%n" />
</Console>
<File name="File" fileName="${filename}" bufferedIO="true" immediateFlush="true">
<PatternLayout>
<pattern>%d %p %C{1.} [%t] %m%n</pattern>
</PatternLayout>
</File>
</appenders>
log4j-xinclude-loggers.xml:
<?xml version="1.0" encoding="UTF-8"?>
<loggers>
<logger name="org.apache.logging.log4j.test1" level="debug" additivity="false">
<ThreadContextMapFilter>
<KeyValuePair key="test" value="123" />
</ThreadContextMapFilter>
<AppenderRef ref="STDOUT" />
</logger>
<logger name="org.apache.logging.log4j.test2" level="debug" additivity="false">
<AppenderRef ref="File" />
</logger>
<root level="error">
<AppenderRef ref="STDOUT" />
</root>
</loggers>
复合配置
Log4j允许通过在log4j.configurationFile上将它们指定为逗号分隔的文件路径列表来使用多个配置文件。可以通过在log4j.mergeStrategy属性上指定实现MergeStrategy接口的类来控制合并逻辑。默认合并策略将使用以下规则合并文件:
状态消息(Status Messages)
org.apache.logging.log4j.simplelog.StatusLogger.level
来控制状态记录器级别。<Configuration status ="trace">
。正如希望能够诊断应用程序中的问题一样,通常需要能够在日志记录配置或配置的组件中诊断问题。由于未配置日志记录,因此在初始化期间无法使用“正常”日志记录。另外,appender内部的正常日志记录可能会产生无限的递归,Log4j会检测到并导致递归事件被忽略。为了满足这一需求,Log4j 2 API包括一个 StatusLogger。组件声明StatusLogger的一个实例:
protected final static Logger logger = StatusLogger.getLogger();
由于StatusLogger实现了Log4j2 API的Logger接口,因此可以使用所有正常的Logger方法。配置Log4j时,有时需要查看生成的状态事件。这可以通过将status属性添加到配置元素来实现,也可以通过设置“Log4jDefaultStatusLevel”系统属性来提供默认值。status属性的有效值“trace”,“debug”,“info”,“warn”,“error”和“fatal”。以下配置的status属性设置为debug。
<?xml version="1.0" encoding="UTF-8"?>;
<Configuration status="debug" name="RoutingTest">
<Properties>
<Property name="filename">target/rolling1/rollingtest-$${sd:type}.log</Property>
</Properties>
<ThresholdFilter level="debug"/>
<Appenders>
<Console name="STDOUT">
<PatternLayout pattern="%m%n"/>
</Console>
<List name="List">
<ThresholdFilter level="debug"/>
</List>
<Routing name="Routing">
<Routes pattern="$${sd:type}">
<Route>
<RollingFile name="Rolling-${sd:type}" fileName="${filename}"
filePattern="target/rolling1/test1-${sd:type}.%i.log.gz">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
</PatternLayout>
<SizeBasedTriggeringPolicy size="500" />
</RollingFile>
</Route>
<Route ref="STDOUT" key="Audit"/>
<Route ref="List" key="Service"/>
</Routes>
</Routing>
</Appenders>
<Loggers>
<Logger name="EventLogger" level="info" additivity="false">
<AppenderRef ref="Routing"/>
</Logger>
<Root level="error">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
如果status属性设置为error,则只会将错误消息写入控制台。这使得故障排除配置错误成为可能。例如,如果上面的配置被更改为将状态设置为error,并且记录器声明是
<logger name="EventLogger" level="info" additivity="false">
<AppenderRef ref="Routing"/>
</logger>
将产生以下错误消息:
2011-11-24 23:21:25,517 ERROR Unable to locate appender Routng for logger EventLogger
用程序可能希望将状态输出指向其他目的地。这可以通过将dest属性设置为“err”来将输出发送到stderr或文件位置或URL来实现。这也可以通过确保配置的状态设置为OFF,然后以编程方式配置应用程序来完成,例如:
StatusConsoleListener listener = new StatusConsoleListener(Level.ERROR);
StatusLogger.getLogger().registerListener(listener);
System Properties(系统属性)
在Java EE Web应用程序中使用Log4j或任何其他日志记录框架时,您必须特别小心。当容器关闭或Web应用程序被取消部署时,将资源正确清理(数据库连接关闭,文件关闭等)非常重要。由于Web应用程序中的类加载器的性质,Log4j资源无法通过正常方式进行清理。当Web应用程序取消配置时,Web应用程序部署并“关闭”时,Log4j必须“启动”。这是如何起作用的,这取决于您的应用程序是Servlet 3.0还是更新版本或 Servlet 2.5 Web应用程序。
在这两种情况下,需要将log4j-web
模块jar添加到部署中 。
为了避免问题,当包含log4j-web
jar时,Log4j关闭钩子将自动被禁用。
配置
Log4j允许使用log4jConfiguration
上下文参数在web.xml中指定配置文件。Log4j将通过以下方式搜索配置文件:
Servlet 3.0和最新的Web应用程序
Servlet 3.0或最新的Web应用程序是其版本version
属性值为“3.0”或更高版本的任何。当然,应用程序也必须在兼容的Web容器中运行。一些示例是:Tomcat 7.0及更高版本,GlassFish 3.0及更高版本,JBoss 7.0及更高版本,Oracle WebLogic 12c及更高版本,以及IBM WebSphere 8.0及更高版本。
Log4j 2“只是工作”在Servlet 3.0和更新的Web应用程序。当应用程序未部署时,应用程序部署和关闭时,它能够自动启动。感谢ServletContainerInitializer API添加到Servlet 3.0中,相关的Filter
和ServletContextListener
类可以在Web应用程序启动时动态注册。
重要的提示! 出于性能原因,容器经常忽略已知不包含TLD或ServletContainerInitializer
的某些JAR ,并且不扫描它们以进行网页片段和初始化。重要的是,Tomcat 7 <7.0.43将忽略名为log4j * .jar的所有JAR文件,从而阻止此功能的工作。这在Tomcat 7.0.43,Tomcat 8和更高版本中已经修复。在Tomcat 7 < 7.0.43中,您将需要更改catalina.properties,并从jarsToSkip 属性中删除“log4j * .jar” 。如果他们跳过扫描Log4j JAR文件,您可能需要在其他容器上执行类似的操作。
Log4j 2 Web JAR文件是配置为在应用程序中的任何其他Web片段之前订购的Web片段。它包含容器自动发现和初始化的ServletContainerInitializer (Log4jServletContainerInitializer)。这将Log4jServletContextListener和 Log4jServletFilter添加到ServletContext中。这些类可以正确地初始化并初始化Log4j配置。
对于某些用户,自动启动Log4j是有问题的或不合需要的。您可以使用isLog4jAutoInitializationDisabled
上下文参数轻松禁用此功能。只需将其添加到您的部署描述符,值为“true”即可禁用自动初始化。您必须在web.xml中定义context参数。如果以编程方式设置,Log4j将检测到该设置为时已晚。
<context-param>
<param-name>isLog4jAutoInitializationDisabled</param-name>
<param-value>true</param-value>
</context-param>
一旦禁用自动初始化,您必须像Servlet 2.5 Web应用程序一样初始化Log4j 。您必须这样做,以便在执行任何其他应用程序代码(如Spring Framework启动代码)之前进行此初始化。
您可以自定义侦听器的行为并使用log4jContextName
,log4jConfiguration
和/或isLog4jContextSelectorNamed
上下文参数进行过滤。在下面的上下文参数部分中阅读更多信息。您不得在部署描述符(web.xml)中或在Servlet 3.0或更新应用程序中的其他初始化程序或侦听器中 手动配置Log4jServletContextListener
或Log4jServletFilter
,除非禁用了使用 isLog4jAutoInitializationDisabled
自动初始化。
Servlet 2.5 Web应用程序
一个Servlet 2.5 web 应用程序是任何,其version属性的值为“2.5”。该版本属性是唯一重要的事情; 即使Web应用程序在Servlet 3.0或更新的容器中运行,如果版本属性为“2.5” ,它也是一个Servlet 2.5 Web应用程序 。请注意,Log4j 2不支持Servlet 2.4和较旧的Web应用程序。
如果您在Servlet 2.5 Web应用程序中使用Log4j,或者使用isLog4jAutoInitializationDisabled
上下文参数禁用自动初始化,则必须在 部署描述符中配置Log4jServletContextListener
和 Log4jServletFilter
,或以编程方式配置。过滤器应该匹配任何类型的所有请求。监听器应该是应用程序中定义的第一个监听器,过滤器应该是在应用程序中定义和映射的第一个过滤器。这可以使用以下web.xml代码轻松完成:
<listener>
<listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
<filter>
<filter-name>log4jServletFilter</filter-name>
<filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>log4jServletFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>ASYNC</dispatcher><!-- Servlet 3.0 w/ disabled auto-initialization only; not supported in 2.5 -->
</filter-mapping>
您可以自定义侦听器的行为并使用log4jContextName, log4jConfiguration和/或isLog4jContextSelectorNamed上下文参数进行过滤。在下面的“ 上下文参数”部分中阅读更多信息。
上下文参数
默认情况下,Log4j2使用ServletContext
的上下文名称作为LoggerContext名称,并使用标准模式来定位Log4j配置文件。可以使用三个上下文参数来控制此行为。第一个isLog4jContextSelectorNamed
指定是否应使用JndiContextSelector
选择上下文 。
将日志记录上下文名称设置为“myApplication”
<context-param>
<param-name>log4jContextName</param-name>
<param-value>myApplication</param-value>
</context-param>
将配置 Path/File/URI 设置为“/etc/myApp/myLogging.xml”
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>file:///etc/myApp/myLogging.xml</param-value>
</context-param>
使用JndiContextSelector
<context-param>
<param-name>isLog4jContextSelectorNamed</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>log4jContextName</param-name>
<param-value>appWithJndiSelector</param-value>
</context-param>
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>file:///D:/conf/myLogging.xml</param-value>
</context-param>
请注意,在这种情况下,您还必须将“Log4jContextSelector”系统属性设置为“ org.apache.logging.log4j.core.selector.JndiContextSelector”。
Lookups提供了一种在任意位置向Log4j配置添加值的方法。他们是一个特殊类型的插件实现了 StrLookup接口。关于如何使用在配置文件中查找信息可以在找到 属性替代的部分 配置页面。
上下文映射查找(Context Map Lookup)
ContextMapLookup允许应用程序将数据存储在Log4j ThreadContext Map中,然后检索Log4j配置中的值。在下面的示例中,应用程序将使用键“loginId”将当前用户的登录ID存储在ThreadContext Map中。在初始配置过程中,第一个“$”将被删除。PatternLayout支持使用Lookups进行插值,然后解析每个事件的变量。请注意,模式"%X{loginId}"将获得相同的结果。
<File name="Application" fileName="application.log">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] $${ctx:loginId} %m%n</pattern>
</PatternLayout>
</File>
日期查询
DateLookup与其他查找有点不寻常,因为它不使用键来定位项目。相反,该键可用于指定对SimpleDateFormat有效的日期格式字符串 。当前日期或与当前日志事件相关联的日期将按照指定进行格式化。
<RollingFile name="Rolling-${map:type}" fileName="${filename}" filePattern="target/rolling1/test1-$${date:MM-dd-yyyy}.%i.log.gz">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
</PatternLayout>
<SizeBasedTriggeringPolicy size="500" />
</RollingFile>
环境查找
EnvironmentLookup允许系统在全局文件(如/ etc / profile)或应用程序的启动脚本中配置环境变量,然后从日志记录配置中检索这些变量。下面的示例包括应用程序日志中当前登录的用户的名称。
<File name="Application" fileName="application.log">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] $${env:USER} %m%n</pattern>
</PatternLayout>
</File>
Java查找
JavaLookup允许使用java:前缀在方便的预格式化字符串中检索Java环境信息。
key: version,runtime,vm,os,locale,hw
<File name="Application" fileName="application.log">
<PatternLayout header="${java:runtime} - ${java:vm} - ${java:os}">
<Pattern>%d %m%n</Pattern>
</PatternLayout>
</File>
Log4j配置位置查找
og4j配置属性。该表达式 ${log4j:configLocation}
和${log4j:configParentLocation}
分别提供给log4j的配置文件,它的父文件夹的绝对路径。
下面的示例使用此查找将日志文件放在相对于log4j配置文件的目录中。
<File name="Application" fileName="${log4j:configParentLocation}/logs/application.log">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
</PatternLayout>
</File>
主要参数查询(应用)
此查找需要您手动将应用程序的主要参数提供给Log4j:
import org.apache.logging.log4j.core.lookup.MainMapLookup;
public static void main(String args[]) {
MainMapLookup.setMainArguments(args);
...
}
如果已经设置了主参数,则该查找允许应用程序从日志记录配置中检索这些主参数值。main: prefix 后面的键可以是参数列表中的基于0的索引,也可以是一个字符串,其中$ {main:myString}替换为主参数列表中myString之后的值 。
--file foo.txt --verbose -x bar
Expression Result
${main:0}
--file
${main:1}
foo.txt
${main:2}
--verbose
${main:3}
-x
${main:4}
bar
${main:--file}
foo.txt
${main:-x}
bar
${main:bar}
null
Appender负责将LogEvents传送到目的地。每个Appender都必须实现Appender接口。大多数Appender将扩展AbstractAppender,并增加了Lifecycle和Filterable 支持。Lifecycle
允许组件在配置完成后初始化,并在关闭期间执行清理。Filterable
允许组件关联到其上的过滤器,在事件处理期间进行评估。
Appenders通常只负责将事件的数据写入目标目的地。在大多数情况下,他们将事件格式化责任委托给layout。一些appender包装其他appenders,以便他们可以修改LogEvent,处理Appender中的故障,根据过滤器优先级将事件路由到一个下级Appender,或者提供类似的功能,不会直接格式化事件进行查看。
Appenders总是有一个名称,以便它们可以被Loggers引用。
在下表中,“type”列对应于预期的Java类型。对于非JDK类,除非另有说明,否则它们通常应在Log4j Core中。
ConsoleAppender(控制台输出配置)
可预期的,ConsoleAppender
将其输出写入到System.out
或System.err
,System.out
是默认目标。必须提供Layout
以格式化LogEvent。
ConsoleAppender Parameters参数列表
Parameter Name | Type | Description |
filter | Filter | 一个过滤器,用于确定事件是否应由此Appender处理。使用CompositeFilter可以使用多个Filter。 |
layout | Layout | 用于格式化LogEvent的布局。如果没有提供布局,将使用“%m%n”的默认模式布局。 |
follow | boolean | 确定appender是否通过配置后通过System.setOut或System.setErr来重新分配System.out或System.err。请注意,该属性不能与Windows上的Jansi一起使用。不能与【direct】属性一起使用 |
direct | boolean | 直接写入```java.io.FileDescriptor```并绕过java.lang.System.out/.err。当输出重定向到文件或其他进程时,可以放弃10倍的性能提升。在Windows上无法与Jansi一起使用。不能与【follow】使用 |
name | String | Appender的名称。 |
ignoreExceptions | boolean | 默认值true,导致在将事件附加到内部记录然后被忽略时遇到异常。当设置为false异常将被传播到调用者。将此Appender包装在[FailoverAppender]中时, 必须将其设置为false。 |
target | String | SYSTEM_OUT或SYSTEM_ERR。默认值为“SYSTEM_OUT”。 |
典型的控制台配置可能如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
FailoverAppender(故障转移配置)
FailoverAppender用于包装一组appenders。若主要的Appender失败了,下一个appenders将尝试以此类推直到成功或没有更多的次要尝试。
Parameter Name | Type | Description |
filter | Filter | 一个过滤器,用于确定事件是否应由此Appender处理。使用CompositeFilter可以使用多个Filter。 |
primary | String | 要使用的主要Appender的名称。 |
failovers | String[] | 次顺序要使用的Appenders列表 |
name | String | Appender的名称. |
retryIntervalSeconds | integer | 在重试主Appender之前应该通过的秒数。默认值为60。 |
ignoreExceptions | boolean | 默认值为true,导致在将事件附加到内部记录然后被忽略时遇到异常。当设置为false异常将被传播到调用者。 |
target | String | "SYSTEM_OUT"或者"SYSTEM_ERR". 默认是"SYSTEM_ERR". |
故障转移配置如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<RollingFile name="RollingFile" fileName="logs/app.log" filePattern="logs/app-%d{MM-dd-yyyy}.log.gz"
ignoreExceptions="false">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<TimeBasedTriggeringPolicy />
</RollingFile>
<Console name="STDOUT" target="SYSTEM_OUT" ignoreExceptions="false">
<PatternLayout pattern="%m%n"/>
</Console>
<Failover name="Failover" primary="RollingFile">
<Failovers>
<AppenderRef ref="Console"/>
</Failovers>
</Failover>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Failover"/>
</Root>
</Loggers>
</Configuration>
FileAppender(写文件配置)
FileAppender是一个OutputStreamAppender,它写入FileName参数中命名的文件。FileAppender使用FileManager(扩展OutputStreamManager)来实际执行文件I / O。虽然不能共享来自不同配置的FileAppenders,但是如果Manager可访问的话FileManager可以使用。例如,Servlet容器中的两个Web应用程序可以拥有自己的配置,并且如果Log4j位于它们两者共同的ClassLoader中,则可以安全地写入同一个文件。
Parameter Name | Type | Description |
append | boolean | 当为true时,默认情况下,记录将追加到文件的末尾。当设置为false时,文件将在新记录写入之前被清除。 |
bufferedIO | boolean | 当为true时,默认情况下,记录将被写入缓冲区,当缓冲区已满时,数据将被写入磁盘,或者如果immediateFlush置位,写入该记录。文件锁定不能与bufferedIO一起使用。性能测试表明,即使启用immediateFlush,使用缓冲I / O可显着提高性能。 |
bufferSize | int | 当bufferedIO为真时,这是缓冲区大小,默认为8192字节。 |
createOnDemand | boolean | Tappender按需创建文件。当日志事件通过所有过滤器并被路由到该appender时,appender仅创建该文件。默认为false。 |
filter | Filter | 一个过滤器,用于确定事件是否应由此Appender处理。使用CompositeFilter可以使用多个Filter。 |
fileName | String | 要写入的文件的名称。如果文件或其任何父目录不存在,将创建它们。 |
immediateFlush | boolean | 当设置为true - 默认值时,每次写入后都会进行刷新。这将保证数据写入磁盘,但可能会影响性能。每次写入后刷新仅在使用具有同步记录器的该追加器时才有用。异步记录器和追加器将在一批事件结束时自动刷新,即使immediateFlush设置为false。这也保证了数据被写入磁盘,但效率更高。 |
layout | Layout | 用于格式化LogEvent的布局。如果没有提供布局,将使用“%m%n”的默认模式布局。 |
locking | boolean | 当设置为true时,只有在保留文件锁定的情况下才会发生I / O操作,允许多个JVM中的FileAppenders和可能的多个主机同时写入同一个文件。这将显着影响性能,所以应该仔细使用。此外,在许多系统上,文件锁是“咨询”,意味着其他应用程序可以对文件执行操作,而无需获取锁定。默认值为false。 |
name | String | Appender的名称。 |
ignoreExceptions | boolean | 默认值为true,导致在将事件附加到内部记录然后被忽略时遇到异常。当设置为false异常将被传播到调用者。将此Appender包装在FailoverAppender中时, 必须将其设置为false。 |
简单的例子:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<File name="MyFile" fileName="logs/app.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
</File>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="MyFile"/>
</Root>
</Loggers>
</Configuration>
FlumeAppender
这是在单独的jar中提供的可选组件。Apache Flume是一种分布式,可靠和可用的系统,用于从许多不同的源高效收集,聚合和移动大量日志数据到集中式数据存储。FlumeAppender采用LogEvents,并将它们发送到Flume代理,作为序列化的Avro事件消费。
JDBCAppender
JDBCAppender使用标准JDBC将日志事件写入关系数据库表。它可以配置为使用JNDI DataSource
或自定义工厂方法获取JDBC连接。无论采取哪种方式,都必须由连接池进行备份。否则,记录性能将受到很大的损害。如果配置的JDBC驱动程序支持批处理语句,并且将 bufferSize
配置为正数,则日志事件将被批处理。请注意,从Log4j 2.8起,有两种将日志事件配置为列映射的方式:1.原始的ColumnConfig
样式,只允许字符串和时间戳. 2.新的ColumnMapping
插件使用log4j内建的类型进行转换允许更多的数据类型。参见CassandraAppender
JDBCAppender Parameters:
Parameter Name | Type | Description |
name | String | Appender的名称。 Required |
ignoreExceptions | boolean | 默认值为true,导致在将事件附加到内部记录然后被忽略时遇到异常。当设置为false异常将被传播到调用者。将此Appender包装在FailoverAppender中时, 必须将其设置为false。 |
filter | Filter | 一个过滤器,用于确定事件是否应由此Appender处理。使用CompositeFilter可以使用多个Filter。 |
bufferSize | int | 如果一个大于0的整数,这会导致appender缓冲日志事件,并在缓冲区达到此大小时进行刷新。 |
connectionSource | ConnectionSource | 应检索数据库连接的连接源。Required. |
tableName | String | 将日志事件插入的数据库表的名称。Required |
columnConfigs | ColumnConfig[] | 有关记录日志事件数据的列的信息应该如何插入以及如何插入该数据。这由多个元素表示。Required (and/or columnMappings). |
columnMappings | ColumnMapping[] | 列映射配置列表。每列必须指定列名。每列可以具有由其完全限定类名指定的转换类型。默认情况下,转换类型为String。如果配置的类型与 ReadOnlyStringMap / ThreadContextMap 或 ThreadContextStack分配兼容,则该列将分别由MDC或NDC填充(这是特定于数据库的如何处理插入Map或List值)。如果配置的类型与java.util.Date分配兼容,则日志时间戳将转换为配置的日期类型。如果配置的类型与java.sql.Clob 或java.sql.NClob分配兼容,则格式化的事件将分别设置为Clob或NClob(类似于传统的ColumnConfig插件)。如果给出了一个文字属性,那么它的值将按照INSERT查询中的原样使用,而不进行任何转义。否则,指定的布局或模式将被转换为已配置的类型并存储在该列中。日期,则日志时间戳将转换为配置的日期类型。如果配置的类型与java.sql.Clob或java.sql.NClob分配兼容,则格式化的事件将分别设置为Clob或NClob(类似于传统的ColumnConfig插件)。如果给出了一个文字属性,那么它的值将按照INSERT查询中的原样使用,而不进行任何转义。否则,指定的布局或模式将被转换为已配置的类型并存储在该列中。日期,则日志时间戳将转换为配置的日期类型。如果配置的类型与java.sql.Clob或java.sql.NClob分配兼容,则格式化的事件将分别设置为Clob或NClob(类似于传统的ColumnConfig插件)。如果给出了一个文字属性,那么它的值将按照INSERT查询中的原样使用,而不进行任何转义。否则,指定的布局或模式将被转换为已配置的类型并存储在该列中。如果配置的类型与java.sql.Clob或java.sql.NClob分配兼容,则格式化的事件将分别设置为Clob或NClob(类似于传统的ColumnConfig插件)。如果给出了一个文字属性,那么它的值将按照INSERT查询中的原样使用,而不进行任何转义。否则,指定的布局或模式将被转换为已配置的类型并存储在该列中。如果配置的类型与java.sql.Clob或java.sql.NClob分配兼容,则格式化的事件将分别设置为Clob或NClob(类似于传统的ColumnConfig插件)。如果给出了一个文字属性,那么它的值将按照INSERT查询中的原样使用,而不进行任何转义。否则,指定的布局或模式将被转换为已配置的类型并存储在该列中。如果给出了一个文字属性,那么它的值将按照INSERT查询中的原样使用,而不进行任何转义。否则,指定的布局或模式将被转换为已配置的类型并存储在该列中。如果给出了一个文字属性,那么它的值将按照INSERT查询中的原样使用,而不进行任何转义。否则,指定的布局或模式将被转换为已配置的类型并存储在该列中。 |
配置JDBCAppender时,必须指定一个ConnectionSource
实现,Appender从中获取JDBC连接。您必须使用恰好一个<DataSource>
或<ConnectionFactory>
嵌套元素。
DataSource Parameters:
Parameter Name | Type | Description |
jndiName | String | ```javax.sql.DataSource```绑定的完整的前缀JNDI名称,例如```java:/comp/env/jdbc/LoggingDatabase```。该数据源必须由连接池进行备份; 否则,记录将非常慢。Required |
ConnectionFactory Parameters:
Parameter Name | Type | Description |
class | Class | 包含用于获取JDBC连接的静态工厂方法的类的完全限定名称。Required |
method | Method | 用于获取JDBC连接的静态工厂方法的名称。此方法必须没有参数,其返回类型必须是java.sql.Connection或 DataSource。如果方法返回Connection s,它必须从连接池获取它们(当Log4j完成它们时,它们将返回到池中); 否则,记录将非常慢。如果该方法返回一个DataSource,DataSource将仅被检索一次,并且由于相同的原因它必须由连接池支持。 |
配置JDBCAppender时,使用嵌套的<Column>
元素来指定表中应该写入哪些列以及如何写入它们。JDBCAppender使用此信息来制定一个PreparedStatement
来插入没有SQL注入漏洞的记录。
Parameter Name | Type | Description |
name | String | 数据库列的名称。Required |
pattern | String | 使用此属性可使用PatternLayout模式从该列中的日志事件插入值或值 。只需在此属性中指定任何合法模式。必须指定此属性,literal或isEventTimestamp =“true”,但不能超过其中之一。 |
literal | Type | 使用此属性在此列中插入文字值。该值将直接包含在插入SQL中,而不会引用任何引号,这对于不支持标识列的数据库特别有用。例如,如果使用Oracle,您可以指定literal =“NAME_OF_YOUR_SEQUENCE.NEXTVAL”,以在ID列中插入唯一的ID。必须指定此属性,pattern或isEventTimestamp =“true” |
isEventTimestamp | boolean | 使用此属性可以在该列中插入事件时间戳,该列应为SQL datetime。该值将作为java.sql.Types.TIMESTAMP插入。此属性(等于 true),pattern, 或 isEventTimestamp="true"必须指定 |
isUnicode | boolean | 除非指定了pattern,否则忽略此属性。如果为true或省略(默认),该值将作为unicode(setNString或setNClob)插入。否则,该值将被插入为非unicode(setString或setClob)。 |
isClob | boolean | 除非指定了pattern,否则忽略此属性。使用此属性指示列存储字符大对象(CLOB)。如果为true,则该值将作为CLOB(setClob或setNClob)插入。如果为false或省略(默认),则该值将作为VARCHAR或NVARCHAR(setString或setNString)插入。 |
以下是JDBCAppender的几个示例配置,以及使用Commons Pooling和Commons DBCP来池数据库连接的示例工厂实现:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error">
<Appenders>
<JDBC name="databaseAppender" tableName="dbo.application_log">
<DataSource jndiName="java:/comp/env/jdbc/LoggingDataSource" />
<Column name="eventDate" isEventTimestamp="true" />
<Column name="level" pattern="%level" />
<Column name="logger" pattern="%logger" />
<Column name="message" pattern="%message" />
<Column name="exception" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="warn">
<AppenderRef ref="databaseAppender"/>
</Root>
</Loggers>
</Configuration>
示例2:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error">
<Appenders>
<JDBC name="databaseAppender" tableName="LOGGING.APPLICATION_LOG">
<ConnectionFactory class="net.example.db.ConnectionFactory" method="getDatabaseConnection" />
<Column name="EVENT_ID" literal="LOGGING.APPLICATION_LOG_SEQUENCE.NEXTVAL" />
<Column name="EVENT_DATE" isEventTimestamp="true" />
<Column name="LEVEL" pattern="%level" />
<Column name="LOGGER" pattern="%logger" />
<Column name="MESSAGE" pattern="%message" />
<Column name="THROWABLE" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="warn">
<AppenderRef ref="databaseAppender"/>
</Root>
</Loggers>
</Configuration>
Java代码:
package net.example.db;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnection;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
public class ConnectionFactory {
private static interface Singleton {
final ConnectionFactory INSTANCE = new ConnectionFactory();
}
private final DataSource dataSource;
private ConnectionFactory() {
Properties properties = new Properties();
properties.setProperty("user", "logging");
properties.setProperty("password", "abc123"); // or get properties from some configuration file
GenericObjectPool<PoolableConnection> pool = new GenericObjectPool<PoolableConnection>();
DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
"jdbc:mysql://example.org:3306/exampleDb", properties
);
new PoolableConnectionFactory(
connectionFactory, pool, null, "SELECT 1", 3, false, false, Connection.TRANSACTION_READ_COMMITTED
);
this.dataSource = new PoolingDataSource(pool);
}
public static Connection getDatabaseConnection() throws SQLException {
return Singleton.INSTANCE.dataSource.getConnection();
}
}
RollingFileAppender
RollingFileAppender是一个OutputStreamAppender,它写入FileName参数中命名的文件,并根据TriggeringPolicy和RolloverPolicy滚动文件。RollingFileAppender不支持文件锁定。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。