Log4j最佳实践(原) - Mainz

标签: log4j 最佳实践 mainz | 发表时间:2014-03-27 15:15 | 作者:Mainz
出处:

本文是结合项目中使用 Log4j总结的最佳实践,非转载。网上可以找到的是这一篇《 Log4j最佳实践》。本来 Log4j使用是非常简单的,无需多介绍其用法,这只是在小型项目中;但在 大型的项目中使用 log4j不太一样。 大型项目非常依赖日志,因为解决线上问题必须依靠log,依靠大量的日志!线上出现问题往往不能重现,而且无法调试,log是必须中的必须,解决线上问题全靠它。

大型项目中Log4j的使用注意点

所以,在大型项目中使用Log4j要注意下面几点:

  • 不能因为写log使得系统性能变慢(最好使用 异步
  • log能易于定位问题,log要能体现何时(精确到毫秒级)、在哪(包、机器、类、函数、行、文件等)、发生了什么问题、以及严重性
  • log要易于自动、手工、半自动分析(如记录文件太大则不能打开分析,写数据库等)
  • 能根据模块/包来动态单独配置log级别(FATAL – ERROR – WARNING – INFO – DEBUG - TRACE) 和单独配置输出文件等
  • 可以用grep分析出独立的行,尽量不要分行
  • 有时调试线上问题还需要非常丰富的信息,例如:进入模块和函数的入口参数信息、完成某项操作耗费的时间、SessionID、机器IP地址和端口号、版本号、try{}catch{}里面的StackTrace信息。
  • 大型系统的日志文件应该定期用gzip压缩并移动到一个专门的档案日志服务器。应该每天晚上,或者每小时这样做一次。
  • 不要随便从网上复制一个Log4j的配置文件,你必须深入理解里面的每一个配置项代表的含义!
  • 如果你拼装的动作比较耗资源,请用if ( log.isDebugEnabled() )
  • 千万不要try{}catch{}了异常却没有记录日志
  • 不要仅记录到数据库,记录文件更加可靠,因为记录到数据库可能发生网络和数据库异常,没有记录本地磁盘可靠。

例如下面这个启动日志包含了版本号、耗费时间、userID等等丰富的信息:

http://images.cnblogs.com/cnblogs_com/zhengyun_ustc/255879/o_clipboard%20-013%20%E5%89%AF%E6%9C%AC.png

 

Log4j为性能考虑的注意点

为系统性能考虑,使用Log4j注意下列几点:

  • 避免输出 '%C', '%F', '%L' '%M' 等位置信息
  • 尽量使用 异步
  • 为每个模块设置单独的输出文件

a). 避免输出'%C', '%F', '%L' '%M' 等位置信息

当配置文件中的配置项包含Location信息时候会非常昂贵,因此,需要 避免'C', 'F', 'L' 'M' 等位置信息的记录( 参数配置项详细说明)。

  • %C - 输出类名
  • %F - 输出文件名
  • %L - 输出行号
  • %M - 输出函数名

注意:当配置为异步输出的时候,以上位置信息可能会显示为问号?,因为是在另外一个线程记录的调用信息。此时,我们可以使用下面的方法来获取类名和函数名:

StackTraceElement se = Thread.currentThread().getStackTrace()[2];
String msg = se.getClassName() + "-[" + se.getMethodName() + "] " + errorMessage;

b). 使用异步(异步写文件,异步写数据库)

Log4j异步写可以使用默认的appender: org.apache.log4j.AsyncAppender,配置文件log4j.xml样例:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' debug="false">

<appender name="DAILY_FILE" class="org.apache.log4j.DailyRollingFileAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c %x - %m%n"/>
</layout>
<param name="File" value="log/log4j.log"/>
<param name="DatePattern" value="'.'yyyy-MM-dd"/>
</appender>

<appender name="ASYNC_FILE" class="org.apache.log4j.AsyncAppender">
<param name="BufferSize" value="10000"/>
<param name="Blocking" value="false"/>
<appender-ref ref="DAILY_FILE"/>
</appender>

<appender name="DB_OUT" class="org.apache.log4j.jdbc.JDBCAppender">
<param name="URL" value="jdbc:postgresql://192.168.1.34:5432/myDB" />
<param name="Driver" value="org.postgresql.Driver"/>
<param name="User" value="aaa"/>
<param name="Password" value="bbb"/>
<param name="Sql" value="INSERT INTO tracelog (ModuleName ,LoginID,UserName,Class, Method,createTime,LogLevel,MSG) values ('%c', '','','','','%d{yyyy-MM-dd HH:mm:ss,SSS}','%p','%m')"/>

</appender>

<appender name="ASYNC_DB" class="org.apache.log4j.AsyncAppender">
<param name="BufferSize" value="10000"/>
<param name="Blocking" value="false"/>
<appender-ref ref="DB_OUT"/>
</appender>

<root>
<level value="info"/>
<appender-ref ref="ASYNC_DB" />
<appender-ref ref="ASYNC_FILE" />
</root>

<logger name="PACKAGE_1" additivity="false">
<level value="info"/>
<appender-ref ref="ASYNC_DB" />
<appender-ref ref="ASYNC_FILE" />
</logger>

<logger name="PACKAGE_2" additivity="false">
<level value="info"/>
<appender-ref ref="ASYNC_DB" />
<appender-ref ref="ASYNC_FILE" />
</logger>


</log4j:configuration>

上面的配置文件包含异步写文件和异步写入postgreSQL数据库的配置,默认是root,也有各个Package的配置。用的时候可以写一个logUtil的类来初始化这个log4j.xml:

package com.ibm;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;

public class LogUtil implements ILogUtil {

public static LogUtil getInstance() {
if (instance == null)
instance = new LogUtil();
return instance;
}

@Override
public void init() {
//PropertyConfigurator.configure("conf/log4j.properties");
DOMConfigurator.configure("conf/log4j.xml");
}

@Override
public void close() {
}

@Override
public void logError(String errorMessage) {
logger.error(errorMessage.replace("'", "''"));
}

@Override
public void logWarn(String warnMessage) {
logger.warn(warnMessage.replace("'", "''"));
}

@Override
public void logInfo(String infoMessage) {
logger.info(infoMessage.replace("'", "''"));
}

@Override
public void logDebug(String debugMessage) {
logger.debug(debugMessage.replace("'", "''"));
}

@Override
public void logFatal(String fatalMessage) {
logger.fatal(fatalMessage.replace("'", "''"));
}

private LogUtil() {
}

private static Logger getPackageLogger(String packageName){
if(packageName.equals(PackageName.PACKAGE_1.toString()))
return Logger.getLogger(PackageName.PACKAGE_1.toString());
else if(packageName.equals(PackageName.PACKAGE_2.toString()))
return Logger.getLogger(PackageName.PACKAGE_2.toString());
else
return Logger.getRootLogger();
}

@Override
public void logError(String packageName, String errorMessage) {
getPackageLogger(packageName).error(errorMessage.replace("'", "''"));
}

@Override
public void logError(String packageName, String errorMessage,
Throwable exception) {
getPackageLogger(packageName).error(errorMessage.replace("'", "''"), exception);
}

@Override
public void logWarn(String packageName, String warnMessage) {
getPackageLogger(packageName).warn(warnMessage.replace("'", "''"));
}

@Override
public void logWarn(String packageName, String warnMessage,
Throwable exception) {
getPackageLogger(packageName).warn(warnMessage.replace("'", "''"), exception);
}

@Override
public void logInfo(String packageName, String infoMessage) {
getPackageLogger(packageName).info(infoMessage.replace("'", "''"));
}

@Override
public void logInfo(String packageName, String infoMessage,
Throwable exception) {
getPackageLogger(packageName).info(infoMessage.replace("'", "''"), exception);
}

@Override
public void logDebug(String packageName, String debugMessage) {
getPackageLogger(packageName).debug(debugMessage.replace("'", "''"));
}

@Override
public void logDebug(String packageName, String debugMessage,
Throwable exception) {
getPackageLogger(packageName).debug(debugMessage.replace("'", "''"), exception);
}

@Override
public void logFatal(String packageName, String fatalMessage) {
getPackageLogger(packageName).fatal(fatalMessage.replace("'", "''"));
}

@Override
public void logFatal(String packageName, String fatalMessage,
Throwable exception) {
getPackageLogger(packageName).fatal(fatalMessage.replace("'", "''"), exception);
}

private static Logger logger = Logger.getRootLogger();
private static LogUtil instance;
}

具体各个Package可以调用:

LogUtil.getInstance().logError("PACKAGE_1", "error message....", e);

注意:写数据库的时候配置文件log4j.xml里面有一菊SQL,这个SQL在写的message包含单引号或双引号的时候会爆异常,所以需要把 单引号或双引号转义为 两个单引号;我们自己的log可以控制,如果是例如Tomcat/jBoss写的log的message包含单引号或双引号的时候会写数据库异常,具体做法可以自定义JDBCAppender,参考 这一片文章。自定义字段可以使用MDC和%X,参考 这一片文章。)

上面的配置文件已经根据各个Package配置单独的log输出,可以配置为写某个文件,或单独写数据库,或是组合,都可以灵活根据自己的需要配置。

在基类写log4j日志要注意的问题

最后要注意的是,如果你把写日志这部分封装到一个独立的jar包模块里面(在基类或者静态类里面写日志),就会导致输出的类名、函数名都是基类的类名和函数名,这将是重大的错误。因为下面的这行:

private static Logger log = Logger.getLogger( MyClass.class );

如果你获得的是基类的logger那就永远是基类的logger。这一点需要注意.


本文链接: http://www.cnblogs.com/Mainz/p/3628414.html,转载请注明。

相关 [log4j 最佳实践 mainz] 推荐:

Log4j最佳实践(原) - Mainz

- - 博客园_Mainz's Blog
本文是结合项目中使用 Log4j总结的最佳实践,非转载. 网上可以找到的是这一篇《 Log4j最佳实践》. 本来 Log4j使用是非常简单的,无需多介绍其用法,这只是在小型项目中;但在 大型的项目中使用 log4j不太一样. 大型项目非常依赖日志,因为解决线上问题必须依靠log,依靠大量的日志.

log4j的MDC

- - 行业应用 - ITeye博客
原文地址: http://blog.csdn.net/huxin1/article/details/5736227. NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)是log4j种非常有用的两个类,它们用于存储应用程序的上下文信息(context infomation),从而便于在log中使用这些上下文信息.

Apache Log4j 2.0介绍

- - CSDN博客推荐文章
Apache Log4j 2.0介绍. 作者:chszs,转载需注明. 作者博客主页:http://blog.csdn.net/chszs. Apache Log4j是著名的Java日志框架之一,在早些年应用最广. 但近两年来,随着SLF4J和LogBack的兴起,很多流行的开源框架在日志模块方面逐步转移到SLF4J+LogBack上,Log4j日渐衰落.

log4j实用配置

- - CSDN博客架构设计推荐文章
第一步:加入log4j-1.2.8.jar到lib下. 第二步:在CLASSPATH下建立log4j.properties. 此句为将等级为INFO的日志信息输出到stdout和R这两个目的地,stdout和R的定义在下面的代码,可以任意起名. 等级可分为OFF、 FATAL、ERROR、WARN、INFO、DEBUG、ALL,如果配置OFF则不打出任何信息,如果配置为INFO这样只显示INFO, WARN, ERROR的log信息,而DEBUG信息不会被显示,具体讲解可参照第三部分定义配置文件中的logger.

jQuery最佳实践

- andi - 阮一峰的网络日志
上周,我整理了《jQuery设计思想》. 那篇文章是一篇入门教程,从设计思想的角度,讲解"怎么使用jQuery". 今天的文章则是更进一步,讲解"如何用好jQuery". 我主要参考了Addy Osmani的PPT《提高jQuery性能的诀窍》(jQuery Proven Performance Tips And Tricks).

PHP最佳实践

- xiangqian - 阮一峰的网络日志
虽然名字叫《PHP最佳实践》,但是它主要谈的不是编程规则,而是PHP应用程序的合理架构. 它提供了一种逻辑和数据分离的架构模式,属于MVC模式的一种实践. 我觉得,这是很有参考价值的学习资料,类似的文章网上并不多,所以一边学习,一边就把它翻译了出来. 根据自己的理解,我总结了它的MVC模式的实现方式(详细解释见译文):.

MongoDB最佳实践

- - NoSQLFan
将 MongoDB加入到我们的服务支持列表中,是整个团队年初工作计划中的首要任务. 但我们感觉如果先添加一项对NoSQL存储的支持,而不是先升级已支持的关系型数据库,可能对用户不太好,毕竟目前的用户都使用关系型数据库. 所以我们决定将引入MongoDB这项工作放到升级MySQL和PostgreSQL之后来做.

Dockerfile 最佳实践

- - DockOne.io
在容器领域,Docker 公司提出的容器镜像已经成为目前容器打包交付的事实标准. 构建镜像需要编写 Dockerfile,如何编写一个优雅的 Dockerfile 呢. 在 Docker 公司的官方文档中给出了一篇:《 Best practices for writing Dockerfiles》.

Log4j详细配置(转)

- - Web前端 - ITeye博客
原文地址:Log4j.properties配置详解作者:大海巨浪. Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局). 这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出. 综合使用这三个组件可以轻松地记录信息的类型和级别,并可以在运行时控制日志输出的样式和位置.

log4j日志性能优化

- - 编程语言 - ITeye博客
       在软件系统中,打日志几乎是每个系统都会使用的行为. 不管是用来记录系统健康状态,辅助问题定位,还是收集数据,以便后续数据分析等,日志都起着举足轻重的作用. 但是IO的阻塞行为和磁盘的读写速度低下意味着写日志并非是没有代价的.           在很多系统中,日志模块用的都是log4j,打日志用的都是同步方法,基本配置如下:.