Java开发网 Java开发网
注册 | 登录 | 帮助 | 搜索 | 排行榜 | 发帖统计  

您没有登录

» Java开发网 » Servlet/JSP/JSF/JavaFX Script  

按打印兼容模式打印这个话题 打印话题    把这个话题寄给朋友 寄给朋友    该主题的所有更新都将Email到你的邮箱 订阅主题
reply to topicflat modethreaded modego to previous topicgo to next topicgo to back
作者 log4j使用大全
chengbd



版主


发贴: 687
于 2004-11-27 00:21 user profilesend a private message to usersend email to chengbdreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
一:配置
A:)在Web Application 中使用log4j
Step 1:
配置log4j配置文件
=========================
# Set root logger level to DEBUG and its only appender to A1
#log4j中有五级logger
#FATAL0
#ERROR3
#WARN4
#INFO6
#DEBUG7
log4j.rootLogger=DEBUG, A1
# A1 is set to be a ConsoleAppender.
#log4j中Appender有几层
#ConsoleAppender输出到控制台
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
#输出格式 具体查看log4j javadoc org.apache.log4j.PatternLayout
#d 时间 ....
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

================================另外一种配置
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
#R 输出到文件
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

Step 2:写启动的Servlet

public class InitServlet extends HttpServlet {

public void init() {
ServletContext sct = getServletContext();
String prefix = sct.getRealPath("/");
//log4j 配置文件存放目录
System.out.println("[....Log4j]: The Root Path: " + prefix);
System.out.println("[....Log4j]: InitServlet init start...");
String file = getInitParameter("log4j");
//log4j 配置文件
if (file != null) {
PropertyConfigurator.configure(prefix + file);
//根据配置初始化log4j
}

System.out.println("[....Log4j]: InitServlet Init Sucess...");

}

public void doGet(HttpServletRequest req, HttpServletResponse res) {

}

public void doPost(HttpServletRequest req, HttpServletResponse res) {

}
}

B:在一般的IDE中使用log4j,因为使用log4j需要做一些初始化,在Web Application
中这一部分初始化是有AppServer 启动是加载的。在IDE中,则我们在使用log4j
之前需要自己配置一下。
如下文件:
public final class Lo4jTest {
private static String CLASS_NAME = "com.bingo.test.Lo4jTest";
private static Logger log = Logger.getLogger(CLASS_NAME);
//在Web Application 中下面的pattern是在log4j的配置文件中配置的。
//现在我们手动配置
private static String pattern="%-4r [%-t] [%p] [%c] [%x] - [%m]%n";
private static String pattern2="%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n";
//设置输出层
//
private static ConsoleAppender consAppender =
new ConsoleAppender(
new PatternLayout(pattern2));
public static void main(String[] args) {
//配置log4j
BasicConfigurator.configure(consAppender);
// void configure(Appender appender);
// void configure();
//配置时log4j提供了两种方式
//后一种比较简单,输出的信息不够详细
//我们可以使用前一种输出我们希望的格式
log.debug("Log4j Debug.");
log.error("Log4j Error.");
log.info("Log4j Info.");
log.fatal("Log4j Fatal.");
}
}
//使用pattern2上面的输出结果如下
2002-08-30 13:49:09 [com.bingo.test.Lo4jTest]-[DEBUG] Log4j Debug.
2002-08-30 13:49:09 [com.bingo.test.Lo4jTest]-[ERROR] Log4j Error.
2002-08-30 13:49:09 [com.bingo.test.Lo4jTest]-[INFO] Log4j Info.
2002-08-30 13:49:09 [com.bingo.test.Lo4jTest]-[FATAL] Log4j Fatal.
//假如我们使用pattern输出如下:
0 [main] [DEBUG] [com.bingo.test.Lo4jTest] [] - [Log4j Debug.]
0 [main] [ERROR] [com.bingo.test.Lo4jTest] [] - [Log4j Error.]
0 [main] [INFO] [com.bingo.test.Lo4jTest] [] - [Log4j Info.]
0 [main] [FATAL] [com.bingo.test.Lo4jTest] [] - [Log4j Fatal.]

二:使用log4j
在实际使用过程中其实非常简单

1:)如下定义log,在log4j1.2以前使用Category,log4j1.2以后使用Logger代替
private static String CLASS_NAME = "com.bingo.test.Lo4jTest";
//log4j1.2
private static Logger log = Logger.getLogger(CLASS_NAME);
//lo4j1.2以前
private static Category log =Category.getInstance(CLASS_NAME);
//取得一个Category or Logger有几种方式,根据自己需要使用

2:)写日志
log.debug("Log4j Debug.");
log.error("Log4j Error.");
log.info("Log4j Info.");
log.fatal("Log4j Fatal.");
//void debug(Object message)
//Log a message object with the DEBUG level.
//void debug(Object message, Throwable t)
//Log a message object with the DEBUG level including the
//stack trace of the Throwable t passed as parameter.

三:注意事项
1:在具体使用中注意不同的信息使用不同的log方式,选择debug,error,
info,fatal中的一种,以后可以根据需要屏蔽部分输出
2:开发过程中尽量输出到控制台,运行过程中则修改配置使其输出到文件.
3:定义log尽量使用文件名作为参数,这样容易查找错误.

出处:http://www.javaunion.org/bbs/cgi-bin/topic.cgi?forum=36&topic=20&show=25'



作者 Re:log4j使用大全 [Re:chengbd]
chengbd



版主


发贴: 687
于 2004-11-27 00:22 user profilesend a private message to usersend email to chengbdreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
以下是与Tomcat交互的:
log4j的介绍(一)——log4j与tomcat结合的简单配置

log4j是apache和ibm联合弄得一个应用系统日志管理工具,利用它的api可以方便的管理和操纵日志。在调试程序的时候,是一个非常不错的调试帮手。有关log4j的一些介绍,大家可以参考apache的网站(http://jakarta.apache.org/log4j/docs/index.html)

下面在开始介绍以前,首先推荐一点资料,大家看看,有助于了解。
(1)《Log4j delivers control over logging》http://www-106.ibm.com/developerworks/java/library/jw-log4j/
(2)http://jakarta.apache.org/log4j/docs/api/org/apache/log4j/PatternLayout.html 这里面介绍了有关layout.ConversionPattern中的转意字符的含义。

(一)与tomcat结合的简单配置
首先到 http://jakarta.apache.org/log4j/docs/download.html 下载一
个log4j,目前版本是1.2.5。下载的文件中有详细的介绍和实例、apidoc,可以参考一下。
将log4j-1.2.5.jar的放到系统classpath中。最好在%tomca_home%/lib/也方一份更好哦。(在此注意一个问题,据说log4j-1.2.5.jar这个文件的文件名,weblogic6.1不能识别,需要改一个名字)

1.1描写properties文件。
这个描述文件一般放在可以放在两个地方:(1)WEB-INF/classes目录下,或者放在在/project_root/,也就是你的web_app的根目录下。利用这个控制日志纪录的配置,当然也可以通过xml文件配置,这里我们暂且说说properties的配置。
建立文件名log4j.properties文件。放在%tomca_home%/web_app/fcxtest/目录下。
文件内容如下:(fcxtest为自己建立的web app目录)

#--------------------------------
# 设定logger的root level为DEBUG,指定的输出目的地(appender)为A1
log4j.rootLogger=DEBUG, A1

# 设定调试信息的输出位置,此处设定输出为控制台
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# 设定调试信息的输出位置,此处设定输出为fcxtest.log文件
# log4j.appender.A1=org.apache.log4j.RollingFileAppender
# log4j.appender.A1.File=fcxtest.log
# log4j.appender.A1.MaxFileSize=1000KB

# 设定制定的A1使用的PatternLayout.
# 有关ConversionPattern中的转意字符的含义参考说明
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %-5p [%t] %C{2} (%F:%L) - %m%n
#--------------------------------

1.2建立测试用的Servlet类
这个测试的com.fcxlog.LogShow类主要是显示如何使用log4j。

package com.fcxlog;

import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.PropertyConfigurator;

public class LogShow extends javax.servlet.http.HttpServlet{

protected String configfile = "log4j.properties";

public void init() throws ServletException{
ServletContext sct = getServletContext();
System.out.println("[Log4j]: The Root Path: " + sct.getRealPath("/"));
//指定自己的properties文件
//以前使用的是BasicConfigurator.configure(),现在使用PropertyConfigurator替代
org.apache.log4j.PropertyConfigurator.configure(sct.getRealPath("/") + configfile);
}

public void service(javax.servlet.http.HttpServletRequest req,javax.servlet.http.HttpServletResponse res){

//初始化Logger,以前使用Category,现在改用Logger替代
//org.apache.log4j.Category log = org.apache.log4j.Category.getInstance(LogShow.class);
org.apache.log4j.Logger log = ora.apache.log4j.Logger.getLogger(LogShow.class);
log.info("调试信息");

}
}

1.3 测试了
至于如何测试,发布,就不说了。

在此说明:
(1)本篇可能错误不少,一方面是参考《Short introduction to log4j》着翻译了一点,有诸多言辞不妥之处,还望指正。一方面,凭借自己的理解,所以难免有不全的地方,还望各位补充。
(2)因为时间有限,每天只能写一点,本文主要是介绍有关Logger及Logger level相关概念的
(3)有关Log4j介绍(一),请参阅:http://www.javaren.com/bbs/cgi-bin/topic.cgi?forum=1&topic=12766&show=0#lastviewpost

概述:
本文主要是简要的介绍了Log4j的api,以及其一些特征和设计原理。它本身是一个开源的软件,允许开发者任意的操纵应用系统日志信息。Log4j的使用通过外部配置文件进行配置。

任何大型应用系统都有其自己的系统日志管理或跟踪的API,所以在1996年的时候,E.U. SEMPER项目组(http://www.semper.org/)也开发其自己的日志管理API,后来经过无数次的修改和补充,发展成了现在的log4j,一个给予java的日志管理工具包。有关最新的版本信息和源码,请访问http://jakarta.apache.org/log4j/docs/index.html
把纪录语句放在代码之中,是一种低端的调试方法,不过有时却不得不说是很有效,也是必需的。毕竟很多时候,调试器并不见得很适用。特别是在多线程应用程序(multithread application)或分布式应用程序(distributed application)

经验告诉我们,在软件开发生命周期中,日志系统是一个非常重要的组件。当然,日志系统也有其自身的问题,过多的日志纪录会降低系统运行的速度。

(一)Log4j的三个重要组件—— Loggers, Appenders, Layouts

这三个组件协同的工作,使得开发者能够依据信息类别和级别去纪录信息,并能够运行期间,控制信息记录的方式已经日志存放地点。

(二)记录器层次(Logger hierarchy)

几乎任何纪录日志的API得功能都超越了简单的System.out.print语句。允许有选择控制的输出日志信息,也就是说,某的时候,一些日志信息允许输出,而另一些则不允许输出。这就假设日志纪录信息之间是有分别的,根据开发者自己定义的选择标准,可以对日志信息加以分类。

纪录器的命名是依据实体的。下面有一段有点绕口的解释,我就直抄了,各位可以看看:(Name Hierarchy)A logger is said to be an ancestor of another logger if its name followed by a dot is a prefix of the descendant logger name. A logger is said to be a parent of a child logger if there are no ancestors between itself and the descendant logger.
(形象的解释,比如存在记录器 a.b.c,记录器a.b,记录器a。那么a就是a.b的ancestor,而a.b就是a.b.c的parent,而a.b.c就是a.b的child)

根纪录器(root logger)是记录器层次的顶端。它有两个独特点:(1)总是存在的(2)能够被重新找回。可以通过访问类的静态方法 Logger.getRootLogger 重新得到。其他的纪录器通过访问静态方法 Logger.getLogger 被实例话或被得到,这个方法将希望获得的记录器的名称作为参数。一些Logger类的方法描述如下:
public class Logger {
// Creation & retrieval methods:
public static Logger getRootLogger();
public static Logger getLogger(String name);
// printing methods:
public void debug(Object message);
public void info(Object message);
public void warn(Object message);
public void error(Object message);
public void fatal(Object message);

// generic printing method:
public void log(Level l, Object message);
}

记录器被赋予级别,这里有一套预定的级别标准:DEBUG, INFO, WARN, ERROR and FATAL ,这些是在 org.apache.log4j.Level 定义的。你可以通过继承Level类定义自己的级别标准,虽然并不鼓励这么做。
如果给定的记录器没有被赋予级别,则其会从离其最近的拥有级别的ancestor处继承得到。如果ancestor也没有被赋予级别,那么就从根记录器继承。所以通常情况下,为了让所有的记录器最终都能够被赋予级别,跟记录器都会被预先设定级别的。比如我们在操作properties文件中,会写这么一句:log4j.rootLogger=DEBUG, A1 。实际上就这就指定了root Logger和root Logger level。

Appenders
Log4j允许记录信息被打印到多个输出目的地,一个输出目的地叫做Appender。目前的Log4j存在的输出目的地包括:控制台(Console),文件(File),GUI Componennt,Remote Socket Server,JMS,NT Event Logger,and Remote Unix Syslog daemons。
多个Appender可以绑定到一个记录器上(Logger)。
通过方法 addAppender(Logger.addAppender) 可以将一个Appender附加到一个记录器上。每一个有效的发送到特定的记录器的记录请求都被转送到那个与当前记录器所绑定的Appender上。(Each enabled logging request for a given logger will be forwarded to all the appenders in that logger as well as the appenders higher in the hierarchy),换句话说,Appender的继承层次是附加在记录器继承层次上的。举个例子:如果一个Console Appender被绑定到根记录器(root Logger),那么所有的记录请求都可以至少被打印到Console。另外,把一个file Appender绑定到记录器C,那么针对记录器C(或C的子孙)的记录请求都可以至少发送到Console Appender和file Appender。当然这种默认的行为方式可以跟改,通过设定记录器的additivity flag(Logger.setAdditivity)为false,从而可以使得Appender的不再具有可加性(Additivity)。
下面简要介绍一下Appender Additivity。
Appender Additivity:记录器C所记录的日志信息将被发送到与记录器C以及其祖先(ancestor)所绑定的所有Appender。
但是,如果记录器C的祖先,叫做P,它的additivity flag被设定为false。那么,记录信息仍然被发送到与记录器C及其祖先,但只到达P这一层次,包括P在内的记录器的所有Appender。但不包括P祖先的。
通常,记录器的additivity flag的被设置为true。

Layouts
这一块主要是介绍输出格式的。PatternLayout,Log4j标准的分配器,可以让开发者依照conversion patterns去定义输出格式。Conversion patterns有点像c语言的打印函数。
参看配置文件的java properties,如下面的两行:
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %-5p [%t] %C{2} (%F:%L) - %m%n
第一行就指定了分配器,第二行则指定了输出的格式。
有关输出格式的定义可以参考/org/apache/log4j/PatternLayout.html



作者 Re:log4j使用大全 [Re:chengbd]
littledeer1974

Garbage Collector

CJSDN高级会员


发贴: 963
于 2004-11-27 00:22 user profilesend a private message to userreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
正准备找这样内容的东西呢,感谢斑竹,让我参考一下了 :)
(是原创吗?很厉害呀)



Surround yourself with people who are open to change

作者 Re:log4j使用大全 [Re:chengbd]
chengbd



版主


发贴: 687
于 2004-11-27 00:23 user profilesend a private message to usersend email to chengbdreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
再贴一篇实例:
使用Log4j进行日志操作

1. 概述

1.1. 背景

在应用程序中添加日志记录总的来说基于三个目的:监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析工作;跟踪代码运行时轨迹,作为日后审计的依据;担当集成开发环境中的调试器的作用,向文件或控制台打印代码的调试信息。

最普通的做法就是在代码中嵌入许多的打印语句,这些打印语句可以输出到控制台或文件中,比较好的做法就是构造一个日志操作类来封装此类操作,而不是让一系列的打印语句充斥了代码的主体。

1.2. Log4j简介

在强调可重用组件开发的今天,除了自己从头到尾开发一个可重用的日志操作类外,Apache为我们提供了一个强有力的日志操作包-Log4j。

Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

此外,通过Log4j其他语言接口,您可以在C、C++、.Net、PL/SQL程序中使用Log4j,其语法和用法与在Java程序中一样,使得多语言分布式系统得到一个统一一致的日志组件模块。而且,通过使用各种第三方扩展,您可以很方便地将Log4j集成到J2EE、JINI甚至是SNMP应用中。

本文介绍的Log4j版本是1.2.3。作者试图通过一个简单的客户/服务器Java程序例子对比使用与不使用Log4j 1.2.3的差别,并详细讲解了在实践中最常使用Log4j的方法和步骤。在强调可重用组件开发的今天,相信Log4j将会给广大的设计开发人员带来方便。加入到Log4j的队伍来吧!

2. 一个简单的例子

我们先来看一个简单的例子,它是一个用Java实现的客户/服务器网络程序。刚开始我们不使用Log4j,而是使用了一系列的打印语句,然后我们将使用Log4j来实现它的日志功能。这样,大家就可以清楚地比较出前后两个代码的差别。

2.1. 不使用Log4j

2.1.1. 客户程序

package log4j ;

import java.io.* ;
import java.net.* ;

/**
*
* <p> Client Without Log4j </p>
* <p> Description: a sample with log4j</p>
* @version 1.0
*/
public class ClientWithoutLog4j {

/**
*
* @param args
*/
public static void main ( String args [] ) {

String welcome = null;
String response = null;
BufferedReader reader = null;
PrintWriter writer = null;
InputStream in = null;
OutputStream out = null;
Socket client = null;

try {
client = new Socket ( "localhost", 8001 ) ;
System.out.println ( "info: Client socket: " + client ) ;
in = client.getInputStream () ;
out = client.getOutputStream () ;
} catch ( IOException e ) {
System.out.println ( "error: IOException : " + e ) ;
System.exit ( 0 ) ;
}

try{
reader = new BufferedReader( new InputStreamReader ( in ) ) ;
writer = new PrintWriter ( new OutputStreamWriter ( out ), true ) ;

welcome = reader.readLine () ;
System.out.println ( "debug: Server says: '" + welcome + "'" ) ;

System.out.println ( "debug: HELLO" ) ;
writer.println ( "HELLO" ) ;
response = reader.readLine () ;
System.out.println ( "debug: Server responds: '" + response + "'") ;

System.out.println ( "debug: HELP" ) ;
writer.println ( "HELP" ) ;
response = reader.readLine () ;
System.out.println ( "debug: Server responds: '" + response + "'" ) ;

System.out.println ( "debug: QUIT" ) ;
writer.println ( "QUIT" ) ;
} catch ( IOException e ) {
System.out.println ( "warn: IOException in client.in.readln()" ) ;
System.out.println ( e ) ;
}
try{
Thread.sleep ( 2000 ) ;
} catch ( Exception ignored ) {}
}
}

2.1.2. 服务器程序

package log4j ;

import java.util.* ;
import java.io.* ;
import java.net.* ;

/**
*
* <p> Server Without Log4j </p>
* <p> Description: a sample with log4j</p>
* @version 1.0
*/
public class ServerWithoutLog4j {

final static int SERVER_PORT = 8001 ; // this server's port

/**
*
* @param args
*/
public static void main ( String args [] ) {
String clientRequest = null;
BufferedReader reader = null;
PrintWriter writer = null;
ServerSocket server = null;
Socket socket = null;
InputStream in = null;
OutputStream out = null;

try {
server = new ServerSocket ( SERVER_PORT ) ;
System.out.println ( "info: ServerSocket before accept: " + server ) ;
System.out.println ( "info: Java server without log4j, on-line!" ) ;

// wait for client's connection
socket = server.accept () ;
System.out.println ( "info: ServerSocket after accept: " + server ) ;

in = socket.getInputStream () ;
out = socket.getOutputStream () ;

} catch ( IOException e ) {
System.out.println( "error: Server constructor IOException: " + e ) ;
System.exit ( 0 ) ;
}
reader = new BufferedReader ( new InputStreamReader ( in ) ) ;
writer = new PrintWriter ( new OutputStreamWriter ( out ) , true ) ;

// send welcome string to client
writer.println ( "Java server without log4j, " + new Date () ) ;

while ( true ) {
try {
// read from client
clientRequest = reader.readLine () ;
System.out.println ( "debug: Client says: " + clientRequest ) ;
if ( clientRequest.startsWith ( "HELP" ) ) {
System.out.println ( "debug: OK!" ) ;
writer.println ( "Vocabulary: HELP QUIT" ) ;
}
else {
if ( clientRequest.startsWith ( "QUIT" ) ) {
System.out.println ( "debug: OK!" ) ;
System.exit ( 0 ) ;
}
else{
System.out.println ( "warn: Command '" +
clientRequest + "' not understood." ) ;
writer.println ( "Command '" + clientRequest
+ "' not understood." ) ;
}
}
} catch ( IOException e ) {
System.out.println ( "error: IOException in Server " + e ) ;
System.exit ( 0 ) ;
}
}
}
}

2.2. 迁移到Log4j

2.2.1. 客户程序

package log4j ;

import java.io.* ;
import java.net.* ;

// add for log4j: import some package
import org.apache.log4j.PropertyConfigurator ;
import org.apache.log4j.Logger ;
import org.apache.log4j.Level ;

/**
*
* <p> Client With Log4j </p>
* <p> Description: a sample with log4j</p>
* @version 1.0
*/
public class ClientWithLog4j {

/*
add for log4j: class Logger is the central class in the log4j package.
we can do most logging operations by Logger except configuration.
getLogger(...): retrieve a logger by name, if not then create for it.
*/
static Logger logger = Logger.getLogger
( ClientWithLog4j.class.getName () ) ;

/**
*
* @param args : configuration file name
*/
public static void main ( String args [] ) {

String welcome = null ;
String response = null ;
BufferedReader reader = null ;
PrintWriter writer = null ;
InputStream in = null ;
OutputStream out = null ;
Socket client = null ;

/*
add for log4j: class BasicConfigurator can quickly configure the package.
print the information to console.
*/
PropertyConfigurator.configure ( "ClientWithLog4j.properties" ) ;

// add for log4j: set the level
// logger.setLevel ( ( Level ) Level.DEBUG ) ;

try{
client = new Socket( "localhost" , 8001 ) ;

// add for log4j: log a message with the info level
logger.info ( "Client socket: " + client ) ;

in = client.getInputStream () ;
out = client.getOutputStream () ;
} catch ( IOException e ) {

// add for log4j: log a message with the error level
logger.error ( "IOException : " + e ) ;

System.exit ( 0 ) ;
}

try{
reader = new BufferedReader ( new InputStreamReader ( in ) ) ;
writer = new PrintWriter ( new OutputStreamWriter ( out ), true ) ;

welcome = reader.readLine () ;

// add for log4j: log a message with the debug level
logger.debug ( "Server says: '" + welcome + "'" ) ;

// add for log4j: log a message with the debug level
logger.debug ( "HELLO" ) ;

writer.println ( "HELLO" ) ;
response = reader.readLine () ;

// add for log4j: log a message with the debug level
logger.debug ( "Server responds: '" + response + "'" ) ;

// add for log4j: log a message with the debug level
logger.debug ( "HELP" ) ;

writer.println ( "HELP" ) ;
response = reader.readLine () ;

// add for log4j: log a message with the debug level
logger.debug ( "Server responds: '" + response + "'") ;

// add for log4j: log a message with the debug level
logger.debug ( "QUIT" ) ;

writer.println ( "QUIT" ) ;
} catch ( IOException e ) {

// add for log4j: log a message with the warn level
logger.warn ( "IOException in client.in.readln()" ) ;

System.out.println ( e ) ;
}
try {
Thread.sleep ( 2000 ) ;
} catch ( Exception ignored ) {}
}
}

2.2.2. 服务器程序

package log4j;

import java.util.* ;
import java.io.* ;
import java.net.* ;

// add for log4j: import some package
import org.apache.log4j.PropertyConfigurator ;
import org.apache.log4j.Logger ;
import org.apache.log4j.Level ;

/**
*
* <p> Server With Log4j </p>
* <p> Description: a sample with log4j</p>
* @version 1.0
*/
public class ServerWithLog4j {

final static int SERVER_PORT = 8001 ; // this server's port

/*
add for log4j: class Logger is the central class in the log4j package.
we can do most logging operations by Logger except configuration.
getLogger(...): retrieve a logger by name, if not then create for it.
*/
static Logger logger = Logger.getLogger
( ServerWithLog4j.class.getName () ) ;

/**
*
* @param args
*/
public static void main ( String args[]) {
String clientRequest = null ;
BufferedReader reader = null ;
PrintWriter writer = null ;
ServerSocket server = null ;
Socket socket = null ;

InputStream in = null ;
OutputStream out = null ;

/*
add for log4j: class BasicConfigurator can quickly configure the package.
print the information to console.
*/
PropertyConfigurator.configure ( "ServerWithLog4j.properties" ) ;

// add for log4j: set the level
// logger.setLevel ( ( Level ) Level.DEBUG ) ;

try{
server = new ServerSocket ( SERVER_PORT ) ;

// add for log4j: log a message with the info level
logger.info ( "ServerSocket before accept: " + server ) ;

// add for log4j: log a message with the info level
logger.info ( "Java server with log4j, on-line!" ) ;

// wait for client's connection
socket = server.accept() ;

// add for log4j: log a message with the info level
logger.info ( "ServerSocket after accept: " + server ) ;

in = socket.getInputStream() ;
out = socket.getOutputStream() ;

} catch ( IOException e ) {

// add for log4j: log a message with the error level
logger.error ( "Server constructor IOException: " + e ) ;
System.exit ( 0 ) ;
}
reader = new BufferedReader ( new InputStreamReader ( in ) ) ;
writer = new PrintWriter ( new OutputStreamWriter ( out ), true ) ;

// send welcome string to client
writer.println ( "Java server with log4j, " + new Date () ) ;

while ( true ) {
try {
// read from client
clientRequest = reader.readLine () ;

// add for log4j: log a message with the debug level
logger.debug ( "Client says: " + clientRequest ) ;

if ( clientRequest.startsWith ( "HELP" ) ) {

// add for log4j: log a message with the debug level
logger.debug ( "OK!" ) ;

writer.println ( "Vocabulary: HELP QUIT" ) ;
}
else {
if ( clientRequest.startsWith ( "QUIT" ) ) {

// add for log4j: log a message with the debug level
logger.debug ( "OK!" ) ;

System.exit ( 0 ) ;
}
else {

// add for log4j: log a message with the warn level
logger.warn ( "Command '"
+ clientRequest + "' not understood." ) ;

writer.println ( "Command '"
+ clientRequest + "' not understood." ) ;
}
}
} catch ( IOException e ) {

// add for log4j: log a message with the error level
logger.error( "IOException in Server " + e ) ;

System.exit ( 0 ) ;
}
}
}
}

2.2.3. 配置文件

2.2.3.1. 客户程序配置文件

log4j.rootLogger=INFO, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender

log4j.appender.A1.layout=org.apache.log4j.PatternLayout

log4j.appender.A1.layout.ConversionPattern=%-4r %-5p [%t] %37c %3x - %m%n

2.2.3.2. 服务器程序配置文件

log4j.rootLogger=INFO, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender

log4j.appender.A1.layout=org.apache.log4j.PatternLayout

log4j.appender.A1.layout.ConversionPattern=%-4r %-5p [%t] %37c %3x - %m%n

2.3. 比较

比较这两个应用可以看出,采用Log4j进行日志操作的整个过程相当简单明了,与直接使用System.out.println语句进行日志信息输出的方式相比,基本上没有增加代码量,同时能够清楚地理解每一条日志信息的重要程度。通过控制配置文件,我们还可以灵活地修改日志信息的格式,输出目的地等等方面,而单纯依靠System.out.println语句,显然需要做更多的工作。

下面我们将以前面使用Log4j的应用作为例子,详细讲解使用Log4j的主要步骤。

3. Log4j基本使用方法

Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式。日志信息的优先级从高到低有ERROR、WARN、INFO、DEBUG,分别用来指定这条日志信息的重要程度;日志信息的输出目的地指定了日志将打印到控制台还是文件中;而输出格式则控制了日志信息的显示内容。

3.1.定义配置文件

其实您也可以完全不使用配置文件,而是在代码中配置Log4j环境。但是,使用配置文件将使您的应用程序更加灵活。

Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件(键=值)。下面我们介绍使用Java特性文件做为配置文件的方法:

配置根Logger,其语法为:

log4j.rootLogger = [ level ] , appenderName, appenderName, …
其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。
appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。

配置日志信息输出目的地Appender,其语法为

log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1

log4j.appender.appenderName.option = valueN
其中,Log4j提供的appender有以下几种:
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

配置日志信息的格式(布局),其语法为:

log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1

log4j.appender.appenderName.layout.option = valueN
其中,Log4j提供的layout有以下几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

3.2.在代码中使用Log4j

下面将讲述在程序代码中怎样使用Log4j。

3.2.1.得到记录器

使用Log4j,第一步就是获取日志记录器,这个记录器将负责控制日志信息。其语法为:

public static Logger getLogger( String name),
通过指定的名字获得记录器,如果必要的话,则为这个名字创建一个新的记录器。Name一般取本类的名字,比如:

static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () ) ;
3.2.2.读取配置文件

当获得了日志记录器之后,第二步将配置Log4j环境,其语法为:
BasicConfigurator.configure (): 自动快速地使用缺省Log4j环境。
PropertyConfigurator.configure ( String configFilename) :读取使用Java的特性文件编写的配置文件。
DOMConfigurator.configure ( String filename ) :读取XML形式的配置文件。

3.2.3.插入记录信息(格式化日志信息)

当上两个必要步骤执行完毕,您就可以轻松地使用不同优先级别的日志记录语句插入到您想记录日志的任何地方,其语法如下:

Logger.debug ( Object message ) ;
Logger.info ( Object message ) ;
Logger.warn ( Object message ) ;
Logger.error ( Object message ) ;

4. 参考资料

如果您想更深入地了解Log4j,请经常访问下面提及的相关链接。
Log4j项目主页------------------------------------------------------www.log4j.org
Log4j FAQ -------------------------------------------------------www.log4j.org/log4j/faq.html



作者 Re:log4j使用大全 [Re:chengbd]
chengbd



版主


发贴: 687
于 2004-11-27 00:26 user profilesend a private message to usersend email to chengbdreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
我也正在看log4j,还没帖完,你老兄就“横插一刀”

在jdk1.4中也提供了log机制,为了方便用户统一使用各种log机制,避免可能的混乱,jakarta提供了一个通用组件包common-logging,通过这个包提供的接口可以很方便的使用log4j等log工具包。
可以在http://jakarta.apache.org/commons/logging.html找到详细信息及下载包。



作者 Re:log4j使用大全 [Re:chengbd]
chengbd



版主


发贴: 687
于 2004-11-27 00:29 user profilesend a private message to usersend email to chengbdreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
不原创,“拿来主义”

Log4j有三个主要的组件:Loggers,Appenders和Layouts,这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出。综合使用这三个组件可以轻松的记录信息的类型和级别,并可以在运行时控制日志输出的样式和位置。下面对三个组件分别进行说明:

1、 Loggers

Loggers组件在此系统中被分为五个级别:DEBUG、INFO、WARN、ERROR和FATAL。这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR < FATAL,明白这一点很重要,这里Log4j有一个规则:假设Loggers级别为P,如果在Loggers中发生了一个级别Q比P高,则可以启动,否则屏蔽掉。

Java程序举例来说:

//建立Logger的一个实例,命名为“com.foo”

Logger logger = Logger.getLogger("com.foo");

//设置logger的级别。通常不在程序中设置logger的级别。一般在配置文件中设置。

logger.setLevel(Level.INFO);

Logger barlogger = Logger.getLogger("com.foo.Bar");

//下面这个请求可用,因为WARN >= INFO

logger.warn("Low fuel level.");

//下面这个请求不可用,因为DEBUG < INFO

logger.debug("Starting search for nearest gas station.");

//命名为“com.foo.bar”的实例barlogger会继承实例“com.foo”的级别。因此,下面这个请求可用,因为INFO >= INFO

barlogger.info("Located nearest gas station.");

//下面这个请求不可用,因为DEBUG < INFO

barlogger.debug("Exiting gas station search");

这里“是否可用”的意思是能否输出Logger信息。

在对Logger实例进行命名时,没有限制,可以取任意自己感兴趣的名字。一般情况下建议以类的所在位置来命名Logger实例,这是目前来讲比较有效的Logger命名方式。这样可以使得每个类建立自己的日志信息,便于管理。比如:

static Logger logger = Logger.getLogger(ClientWithLog4j.class.getName());

2、 Appenders

禁用与使用日志请求只是Log4j其中的一个小小的地方,Log4j日志系统允许把日志输出到不同的地方,如控制台(Console)、文件(Files)、根据天数或者文件大小产生新的文件、以流的形式发送到其它地方等等。

其语法表示为:

org.apache.log4j.ConsoleAppender(控制台),

org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

配置时使用方式为:

log4j.appender.appenderName = fully.qualified.name.of.appender.class

log4j.appender.appenderName.option1 = value1



log4j.appender.appenderName.option = valueN

这样就为日志的输出提供了相当大的便利。

3、 Layouts

有时用户希望根据自己的喜好格式化自己的日志输出。Log4j可以在Appenders的后面附加Layouts来完成这个功能。Layouts提供了四种日志输出样式,如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式等等。

其语法表示为:

org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

配置时使用方式为:

log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1

log4j.appender.appenderName.layout.option = valueN

以上是从原理方面说明Log4j的使用方法,在具体Java编程使用Log4j可以参照以下示例:
1、 建立Logger实例:
语法表示:public static Logger getLogger( String name)
实际使用:static Logger logger = Logger.getLogger (ServerWithLog4j.class.getName ()) ;
2、 读取配置文件:
获得了Logger的实例之后,接下来将配置Log4j使用环境:
语法表示:
BasicConfigurator.configure():自动快速地使用缺省Log4j环境。
PropertyConfigurator.configure(String configFilename):读取使用Java的特性文件编写的配置文件。
DOMConfigurator.configure(String filename):读取XML形式的配置文件。
实际使用:PropertyConfigurator.configure("ServerWithLog4j.properties");
3、 插入日志信息
完成了以上连个步骤以后,下面就可以按日志的不同级别插入到你要记录日志的任何地方了。
语法表示:
Logger.debug(Object message);
Logger.info(Object message);
Logger.warn(Object message);
Logger.error(Object message);
实际使用:logger.info("ServerSocket before accept: " + server);

在实际编程时,要使Log4j真正在系统中运行事先还要对配置文件进行定义。定义步骤就是对Logger、Appender及Layout的分别使用,具体如下:
1、 配置根Logger,其语法为:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
这里level指Logger的优先级,appenderName是日志信息的输出地,可以同时指定多个输出地。如:log4j.rootLogger= INFO,A1,A2
2、 配置日志信息输出目的地,其语法为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
可以指定上面所述五个目的地中的一个。
3、 配置日志信息的格式,其语法为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
这里上面三个步骤是对前面Log4j组件说明的一个简化;下面给出一个具体配置例子,在程序中可以参照执行:
log4j.rootLogger=INFO,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=
%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
这里需要说明的就是日志信息格式中几个符号所代表的含义:
-X号: X信息输出时左对齐;
%p: 日志信息级别
%d{}: 日志信息产生时间
%c: 日志信息所在地(类名)
%m: 产生的日志具体信息
%n: 输出日志信息换行

根据上面的日志格式,某一个程序的输出结果如下:
0 INFO 2003-06-13 13:23:46968 ClientWithLog4j Client socket: Socket[addr=localhost/127.0.0.1,port=8002,localport=2014]
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server says: 'Java server with log4j, Fri Jun 13 13:23:46 CST 2003'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j GOOD
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Command 'HELLO' not understood.'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j HELP
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Vocabulary: HELP QUIT'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j QUIT

作者Blog:http://blog.csdn.net/futurelight/



作者 Re:log4j使用大全 [Re:chengbd]
littledeer1974

Garbage Collector

CJSDN高级会员


发贴: 963
于 2004-11-27 00:29 user profilesend a private message to userreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
不好意思啊,我要是总斑竹就给你加分了,谢谢你的[辛苦工作]:)


Surround yourself with people who are open to change

作者 Log4j简明手册(1/3) [Re:chengbd]
chengbd



版主


发贴: 687
于 2004-11-27 02:34 user profilesend a private message to usersend email to chengbdreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Log4j简明手册  作者Blog:http://blog.csdn.net/chensheng913/

1. 概述
本文主要描述Log4j的API的唯一特性和它的设计原理。Log4j是一个基于许多作者的开放源码的项目。它允许开发员以任意的间隔来控制日志的输出。它通过设在外部的配置文件而达到运行时灵活的设置。最重要的是,Log4j有一个平稳的学习曲线。注意:根据来自用户的反馈判断,它很容易使人上瘾。

2. 导言

几乎所有的大型应用程序都包括它的自己的日志和跟踪API。顺应这个规则,E.U. SEMPER 项目决定写它自己的跟踪PAI。这是1996年初。在无数次加强,几次变形和许多工作后,那个API变成了如今的Log4j,一个流行的java日志包。这个包以Apache Software License协议发布,一个成熟的开放源吗协议。最新的Log4j版本,包括全部的源码,class文件和文档,你可以在http://jakarta.apache.org/Log4j/上找到。顺便,Log4j已经给C, C++, C#, Python, Ruby, and Eiffel 语言都提供了接口。

为了调试而插入日志输出到代码里是一个低技术成分的方法,但它可能也是唯一的方法,因为调试器并不是一直可用或者可以适应的,尤其对于多线程的分布使式的大型程序而言。

经验指出调试是软件开发周期中一个重要的组成部分。

Log4j拥有几个优点:

首先,它提供关于运行程序的准确的环境。一旦代码被插入,不需要人工干预就可以产生调试信息。

其次,日志输出可以被有计划的保存在永久媒体中以便日后研究。

另外,除了在开发周期中,一个充分详尽的日志包可以被用来作为以后的统计工具。

Log4j当然还有它的缺点,它可能减慢程序。如果太详细,它可能导致屏幕盲目滚动。排除这些情况,Log4j是可靠的,快速的,可以扩展的。因为日志很少是一个应用程序的主要目的, 设计者们正努力使得Log4j API学习和使用简单化。

3. 日志类别、输出源和布局

Log4j有三个主要的组件:日志类别(Loggers)、输出源( Appenders)和布局(Layouts)。这三种类型的组件一起工作使得开发员可以根据信息的类型和级别记录它们,并且在运行时控制这些信息的输出格式和位置。

3.1 日志类别的层次结构(Loggers)

Log4j首要的相对于简单的使用System.out.println()方法的优点是基于它的在禁止一些特定的信息输出的同时不妨碍其它信息的输出的能力。这个能力源自于日志命名空间,也就是说,所有日志声明的空间,它根据一些开发员选择的公式而分类。以前的观察引导我们选择类别作为包的中心概念。然而,自从Log4j的1.2版本,Logger类被Catalog类所取代,对于那些熟悉Log4j以前版本的人来说,Logger类可以被想象成仅仅是Category 类的别名。

Loggers 被指定为实体,Logger的名字是大小写敏感的,它们遵循以下的命名

规则:

² 命名继承

如果类别的名称(后面加一个点)是其子类别名称的前缀,则它就是另一个类别的祖辈。

如果一个类别(Logger)和它的子类别之间没有其它的继承关系,我们就称之为parent与child的关系。

例如,类别"com.foo"是类别"com.foo.Bar"的parent。相似的,"java"是"java.util"的parent,是"java.util.Vector"的父辈。.这个命名规则应该被大多数的开发员所熟悉。

根(root) 类别位于logger继承结构的最上层。它有两种例外:

1.它一直存在

2.它不能根据名称而获得。

调用类的静态方法Logger.getRootLogger可以得到它。其它所有的Logger可以通过静态方法Logger.getLogger而得到它们自己的实例。这个方法取希望的Logger名作为参数。Logger的一些基本的方法示例如下:

package org.apache.Log4j;

public Logger class {

// Creation & retrieval methods:

public static Logger getRootLogger();

public static Logger getLogger(String name);

// printing methods:

public void debug(Object message);

public void info(Object message);

public void warn(Object message);

public void error(Object message);

// generic printing method:

public void log(Level l, Object message);

}

Loggers可以被分配的级别。所有级别的集合包括:

DEBUG

INFO

WARN

ERROR

FATAL

它们被定义于org.apache.Log4j.Level 类。虽然我们不鼓励,但是你们可以通过继承Level类来定义你们自己的级别。我们随后将介绍一个比较好的方法。

如果一个Logger没有被分配一个级别,那么它将从一个被分配了级别的最接近它的ancestor哪里继承。

正规的说:

² 级别继承

对于一个给定的Logger C,它的继承的级别等于从C开始上溯到的第一个拥有非空级别的Logger的级别。

为了保证所有的Logger最终能够继承到一个级别,根Logger通常有一个已经定义了的级别。

以下四个表中的数据演示了根据以上规则得到的结果。

类别名
分配的级别
继承的级别

root
Proot
Proot

X
none
Proot

X.Y
none
Proot

X.Y.Z
none
Proot

Example 1

在例子1中,只有根Logger定义了一个级别,它的级别的值--"Proot"被所有其它的Loggers X, X.Y, 和X.Y.Z所继承。

类别名
分配的级别
继承的级别

root
Proot
Proot

X
Px
Px

X.Y
Pxy
Pxy

X.Y.Z
Pxyz
Pxyz

Example 2

在例子2中,所有的Logger都有一个被分配的级别值,所以它们不需要级别继承。

类别名
分配的级别
继承的级别

root
Proot
Proot

X
Px
Px

X.Y
none
Px

X.Y.Z
Pxyz
Pxyz

Example 3

在例子3中,根Logger,以及X和X.Y.Z被分别分配了级别Proot,Px和Pxyz。Logger X.Y从它的parent X继承了级别值Px。

类别名
分配的级别
继承的级别

root
Proot
Proot

X
Px
Px

X.Y
none
Px

X.Y.Z
none
Px

Example 4

在例子4中,根Logger和X被分别分配了级别"Proot"和"Px",Logger X.Y 和 X.Y.Z从被分配了级别的最接近它们的ancestor X那里得到继承。

我们需要通过调用Logger的输出的实例方法之一来实现日志请求。这些输出的方法是debug, info, warn, error, fatal 和 log.

通过定义输出方法来区分日志的请求的级别。例如,如果c是一个Logger的实例,那么声明 c.info 就是一个INFO级别的日志请求。

如果一个日志的请求的级别高于或等于日志的级别那么它就能被启用。反之,将被禁用。一个没有被安排级别的Logger将从它的父辈中得到继承。这个规则总结如下。

² 基本的选择规则

假如在一个级别为q的Logger中发生一个级别为p的日志请求,如果p>=q,那么请求将被启用。

这是Log4j的核心原则。它假设级别是有序的。对于标准级别,我们定义DEBUG < INFO < WARN < ERROR < FATAL.

以下是关于这条规则的一个例子。

// get a logger instance named "com.foo"

Logger logger = Logger.getLogger("com.foo");

// Now set its level. Normally you do not need to set the

// level of a logger progamitcally. This is usually done

// in configuration files.

cat.setLevel(Level.INFO);

Logger barlogger = Logger.getLogger("com.foo.Bar");

// This request is enabled, because WARN >= INFO.

logger.warn("Low fuel level.");

// This request is disabled, because DEBUG < INFO.

logger.debug("Starting search for nearest gas station.");

// The logger instance barlogger, named "com.foo.Bar",

// will inherit its level from the logger named

// "com.foo" Thus, the following request is enabled

// because INFO >= INFO.

barlogger.info("Located nearest gas station.");

// This request is disabled, because DEBUG < INFO.

barlogger.debug("Exiting gas station search");

调用getLogger方法将返回一个同名的Logger对象的实例。

例如,

Categoty x = Logger.getLogger("wombat");

Categoty y = Logger.getLogger("wombat");

x和y参照的是同一个Logger对象。

这样我们就可以先定义一个Logger,然后在代码的其它地方不需传参就可以重新得到我们已经定义了的Logger的实例.

同基本的生物学理论--父先于子相反,Log4j 的loggers可以以任何顺序创造和配置。特别是,一个后实例化的"parent"logger能够找到并且连接它的子logger。

配置Log4j的环境通常在一个应用程序被初始化的时候进行,最好的方法是通过读一个配置文件。这个方法我们将简短介绍。

Log4j使得通过软件组件命名logger很容易。我们可以通过Logger的静态的初始化方法在每一个类里定义一个logger,令logger的名字等于类名的全局名,而实现logger的命名。这是一个实效的简单的定义一个logger的方法。因为日志输出带有产生日志的类的名字,这个命名策略使得我们更容易定位到一个日志信息的来源。虽然普通,但却是命名logger的常用策略之一。Log4j没有限制定义logger的可能。开发员可以自由的按照它们的意愿定义logger的名称。

然而,以类的所在位置来命名Logger好象是目前已知的最好方法。

3.2 输出源(Appenders)和布局(Layouts)

有选择的能用或者禁用日志请求仅仅是Log4j的一部分功能。Log4j允许日志请求被输出到多个输出源。用Log4j的话说,一个输出源被称做一个Appender. 。Appender包括console(控制台), files(文件), GUI components(图形的组件), remote socket servers(socket 服务), JMS(java信息服务), NT Event Loggers(NT的事件日志), and remote UNIX Syslog daemons(远程UNIX的后台日志服务)。它也可以做到异步记录。

一个logger可以设置超过一个的appender。

用addAppender 方法添加一个appender到一个给定的logger。对于一个给定的logger它每个生效的日志请求都被转发到该logger所有的appender上和该logger的父辈logger的appender上。换句话说,appender自动从它的父辈获得继承。举例来说,如果一个根logger拥有一个console appender,那么所有生效的日志请求至少会被输出到console上。如果一个名为C的logger有一个file类型的appender,那么它就会对它自己以及所有它的子logger生效。我们也可以通过设置appender的additivity flag 为false,来重载appender的默认行为,以便继承的属性不在生效。

调节输出源(appender)添加性的规则如下。

输出源的可添加性(Appender Additivity )

一个名为C的logger的日志定义的输出将延续到它自身以及它的ancestor logger的appenders。这就是术语"appender additivity"的含义。

然而,logger C的一个ancestor logger P,它的附加标志被设为false,那么C的输出将被定位到所有C的appender,以及从它开始上溯到P的所有ancestor logger的appender。

Loggers的附加标记(additivity flag)默认为true。

下表是一个例子。

Logger
Name
Added
Appenders
Additivity
Flag
Output Targets
Comment

root
A1
not applicable
A1
The root logger is anonymous but can be accessed with the Logger.getRootLogger() method. There is no default appender attached to root.

x
A-x1, A-x2
true
A1, A-x1, A-x2
Appenders of "x" and root.

x.y
none
true
A1, A-x1, A-x2
Appenders of "x" and root.

x.y.z
A-xyz1
true
A1, A-x1, A-x2, A-xyz1
Appenders in "x.y.z", "x" and root.

security
A-sec
false
A-sec
No appender accumulation since the additivity flag is set to false.

security.access
none
true
A-sec
Only appenders of "security" because the additivity flag in "security" is set to false.

经常,用户希望自定义不但输出源,而且定义输出格式。这个是通过在一个appender上附加一个layout来完成的。layout是负责根据用户的希望来格式化日志请求。而appender是负责发送格式化的输出到它的目的地。PatternLayout,作为Log4j标准版中的一部分,让用户指以类似C语言的printf方法的格式来指定日志的输出格式。

例如,转化模式为"%r [%t] %-5p %c - %m%n" 的PatternLayout 将输出类似如下的信息:

176 [main] INFO org.foo.Bar - Located nearest gas station.

第一个栏位是自从程序开始后消逝的毫秒数。

第二个栏位是做出日志的线程。

第三个栏位是log的级别。

第四个栏位是日志请求相关的logger的名字。而"-"后的文字是信息的表述。

Log4j将根据用户定义的公式来修饰日志信息的内容。例如,如果你经常需要记录Oranges,一个在你当前的项目被用到的对象类型,那么你可以注册一个OrangeRenderer ,它将在一个orange需要被记录时被调用。

对象渲染类似的类的结构继承。例如,假设oranges是fruits,如果你注册了一个FruitRenderer,所有的水果包括oranges将被FruitRenderer所渲染。除非你注册了一个orange。

对象渲染必须实现ObjectRenderer接口。


chengbd edited on 2004-11-27 02:50

作者 Re:log4j使用大全 [Re:chengbd]
chengbd



版主


发贴: 687
于 2004-11-27 02:39 user profilesend a private message to usersend email to chengbdreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
4. 配置

插入日志请求到应用程序的代码中需要大量的预先计划和最终努力。观察显示大约4%的代码是用来输出的。

因此,大小适度的程序都被嵌入有成千个日志输出语句。为了以无需手工的方式管理这些日志的输出状态,给日志输出以编号和规范变得势在必行。

Log4j在程序中有充分的可配置性。然而,用配置文件配置Log4j具有更大的弹性。目前,它的配置文件支持xml和java properties(key=value)文件两种格式。

让我们以一个例子来演示它是如何做的。假定有一个用了Log4j的程序MyApp。

import com.foo.Bar;

// Import Log4j classes.

import org.apache.Log4j.Logger;

import org.apache.Log4j.BasicConfigurator;

public class MyApp {

// Define a static logger variable so that it references the

// Logger instance named "MyApp".

static Logger logger = Logger.getLogger(MyApp.class);

public static void main(String[] args) {

// Set up a simple configuration that logs on the console.

BasicConfigurator.configure();

logger.info("Entering application.");

Bar bar = new Bar();

bar.doIt();

logger.info("Exiting application.");

}

}

MyApp以引入Log4j的相关类开始,接着它定义了一个静态logger变量,并给予值为"MyApp"类的全路径名称。

MYApp用了定义在包com.foo中的类Bar.

package com.foo;

import org.apache.Log4j.Logger;

public class Bar {

static Logger logger = Logger.getLogger(Bar.class);

public void doIt() {

logger.debug("Did it again!");

}

}

调用BasicConfigurator.configure()方法创建了一个相当简单的Log4j的设置。它加入一

个ConsoleAppender到根logger。输出将被采用了"%-4r [%t] %-5p %c %x - %m%n"模式

的PatternLayout所格式化。

注意,根logger默认被分配了Level.DEBUG的级别。

MyApp的输出为:

0 [main] INFO MyApp - Entering application.

36 [main] DEBUG com.foo.Bar - Did it again!

51 [main] INFO MyApp - Exiting application.

随后的图形描述了在调用BasicConfigurator.configure()方法后MyApp的对象图。

一边要提醒的是,Log4j的子logger只连接到已经存在的它们的父代。特别的是,名为

com.foo.bar的logger是直接连接到根logger,而不是围绕着没用的com或com.foo

logger。这显著的提高了程序性能并且减少的内存占用。

MyApp类配置Log4j是通过调用BasicConfigurator.configure 方法。其它的类仅仅

需要引入org.apache.Log4j.Logger 类,找到它们希望用的logger,并且用它就行。

以前的例子通常输出同样的日志信息。幸运的是,修改MyApp是容易的,以便日志输

出可以在运行时刻被控制。这里是一个小小修改的版本。

import com.foo.Bar;

import org.apache.Log4j.Logger;

import org.apache.Log4j.PropertyConfigurator;

public class MyApp {

static Logger logger = Logger.getLogger(MyApp.class.getName());

public static void main(String[] args) {

// BasicConfigurator replaced with PropertyConfigurator.

PropertyConfigurator.configure(args[0]);

logger.info("Entering application.");

Bar bar = new Bar();

bar.doIt();

logger.info("Exiting application.");

}

}

修改后的 MyApp通知程序调用PropertyConfigurator()方法解析一个配置文件,并且根

据这个配置文件来设置日志。

这里是一个配置文件的例子,它将产生同以前BasicConfigurator 基本例子一样

的输出结果。

# Set root logger level to DEBUG and its only appender to A1.

Log4j.rootLogger=DEBUG, A1

# A1 is set to be a ConsoleAppender.

Log4j.appender.A1=org.apache.Log4j.ConsoleAppender

# A1 uses PatternLayout.

Log4j.appender.A1.layout=org.apache.Log4j.PatternLayout

Log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

假设我们不在对com.foo包的任何类的输出感兴趣的话,随后的配置文件向我们展示

了实现这个目的的方法之一。

Log4j.rootLogger=DEBUG, A1

Log4j.appender.A1=org.apache.Log4j.ConsoleAppender

Log4j.appender.A1.layout=org.apache.Log4j.PatternLayout

# Print the date in ISO 8601 format

Log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n

# Print only messages of level WARN or above in the package com.foo.

Log4j.logger.com.foo=WARN

以这个配置文件配置好的MyApp将输出如下:

2000-09-07 14:07:41,508 [main] INFO MyApp - Entering application.

2000-09-07 14:07:41,529 [main] INFO MyApp - Exiting application.

当logger com.foo.bar没有被分配一个级别,它将从com.foo继承,在配置文件中

它被设置了WARN的级别。在Bar.doIt方法中定义的log为DEBUG级别,低于WARN,

因此doIt() 方法的日志请求被禁用。

这里是另外一个配置文件,它使用了多个appenders.

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

以这个配置文件调用加强了的MyApp类将输出如下信息.

INFO [main] (MyApp2.java:12) - Entering application.

DEBUG [main] (Bar.java:8) - Doing it again!

INFO [main] (MyApp2.java:15) - Exiting application.

另外,因为根logger有被分配第二个appender,所以输出也将被定向到example.log文件。

这个文件大小达到100kb时将自动备份。备份时老版本的example.log文件自动被移到

文件example.log.1中。

注意我们不需要重新编译代码就可以获得这些不同的日志行为。我们一样可以容易

的使日志输出到UNIX Syslog daemon, 重定向所有的com.foo到NT Event logger,

或者转发日志到一个远程的Log4j服务器,它根据本地server的策略来进行日志输出。例

如转发日志事件到第二个Log4j服务器.

5. 默认的初始化过程
Log4j类库不对它的环境做任何假设。特别是没有默认的Log4j appender。在一些特别

的有着良好定义的环境下,logger的静态inializer将尝试自动的配置Log4j。

java语言的特性保证类的静态initializer当且仅当装载类到内存之时只会被调用一次。

要记住的重要一点是,不同的类装载器可能装载同一个类的完全不同的拷贝。

这些同样类的拷贝被虚拟机认为是完全不相干的。

默认的initialization是非常有用的,特别是在一些应用程序所依靠的运行环境被准确的

定位的情况下。例如,同一样的应用程序可以被用做一个标准的应用程序,或一个

applet,或一个在web-server控制下的servlet。

准确的默认的initialization原理被定义如下:

1.设置系统属性Log4j.defaultInitOverride为"false"以外的其它值,那么Log4j将

跳过默认的initialization过程。

2.设置资源变量字符串给系统属性Log4j.configuration。定义默认initialization

文件的最好的方法是通过系统属性Log4j.configuration。万一系统属性

Log4j.configuration没有被定义,那么设置字符串变量resource 给它的默认值

Log4j.properties。

3.尝试转换resource 变量为一个URL。

4.如果变量resource的值不能被转换为一个URL,例如由于MalformedURLException违

例,那么通过调用

org.apache.Log4j.helpers.Loader.getResource(resource, Logger.class) 方法从

classpath中搜索resource,它将返回一个URL,并通知"Log4j.properties"的值是一个错

误的URL。

看See Loader.getResource(java.lang.String) 查看搜索位置的列表。

5.如果没有URL被发现,那么放弃默认的initialization。否则用URL配置Log4j。

PropertyConfigurator将用来解析URL,配置Log4j,除非URL以".xml"为结尾。

在这种情况下的话DOMConfigurator将被调用。你可以有机会定义一个自定义的

configurator。

系统属性Log4j.configuratorClass 的值取自你的自定义的类名的全路径。

你自定义的configurator必须实现configurator接口。

6. 配置范例
6.1 Tomcat下的初始化
默认的Log4j initialization典型的应用是在web-server 环境下。在tomcat3.x和tomcat4.x

下,你应该将配置文件Log4j.properties放在你的web应用程序的WEB-INF/classes 目录

下。

Log4j将发现属性文件,并且以此初始化。这是使它工作的最容易的方法。

你也可以选择在运行tomcat前设置系统属性Log4j.configuration 。对于tomcat 3.x,

TOMCAT_OPTS 系统变量是用来设置命令行的选项。对于tomcat4.0,用系统环境变

量CATALINA_OPTS 代替了TOMCAT_OPTS。

Example 1

UNIX 命令行

export TOMCAT_OPTS="-DLog4j.configuration=foobar.txt"

告诉Log4j用文件foobar.txt作为默认的配置文件。这个文件应该放在WEB-INF/classes

目录下。这个文件将被PropertyConfigurator所读。每个web-application将用不同的默认

配置文件,因为每个文件是和它的web-application 相关的。

Example 2

UNIX 命令行

export TOMCAT_OPTS="-DLog4j.debug -DLog4j.configuration=foobar.xml"

告诉Log4j输出Log4j-internal的调试信息,并且用foobar.xml作为默认的配置文件。

这个文件应该放在你的web-application的WEB-INF/classes 目录下。因为有.xml的

扩展名,它将被DOMConfigurator所读。每个web-application将用不同的默认

配置文件。因为每个文件都和它所在的web-application 相关的。

Example 3

UNIX 命令行

set TOMCAT_OPTS=-DLog4j.configuration=foobar.lcf -DLog4j.configuratorClass=com.foo.BarConfigurator

告诉Log4j用文件foobar.lcf作为默认的配置文件。这个文件应该放在你的

web-application的WEB-INF/classes 目录下。因为定义了Log4j.configuratorClass 系统属

性,文件将用自定义的com.foo.barconfigurator类来解析。每个web-application将用不

同的默认配置文件。因为每个文件都和它所在的web-application 相关的。

Example 4

UNIX 命令行

set TOMCAT_OPTS=-DLog4j.configuration=file:/c:/foobar.lcf

告诉Log4j用文件foobar.lcf作为默认的配置文件。这个配置文件用URL file:/c:/foobar.lcf

定义了全路径名。这样同样的配置文件将被所有的web-application所用。

不同的web-application将通过它们自己的类装载器来装载Log4j。这样,每个Log4j的环

境将独立的运作,而没有任何的相互同步。例如:在多个web-application中定义了

完全相同的输出源的FileAppenders将尝试写同样的文件。结果好象是缺乏安全性的。

你必须确保每个不同的web-application的Log4j配置没有用到同样的系统资源。

6.2 Servlet 的初始化
用一个特别的servlet来做Log4j的初始化也是可以的。如下是一个例子:

package com.foo;

import org.apache.Log4j.PropertyConfigurator;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.PrintWriter;

import java.io.IOException;

public class Log4jInit extends HttpServlet {

public void init() {

String prefix = getServletContext().getRealPath("/");

String file = getInitParameter("Log4j-init-file");

// if the Log4j-init-file is not set, then no point in trying

if(file != null) {

PropertyConfigurator.configure(prefix+file);

}

}

public void doGet(HttpServletRequest req, HttpServletResponse res) {

}

}

在web.xml中定义随后的servlet为你的web-application。

<servlet>

<servlet-name>Log4j-init</servlet-name>

<servlet-class>com.foo.Log4jInit</servlet-class>

<init-param>

<param-name>Log4j-init-file</param-name>

<param-value>WEB-INF/classes/Log4j.lcf</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

写一个初始化的servlet是最有弹性的初始化Log4j的方法。代码中没有任何限制,你可

以在servlet的init方法中定义它。



作者 Re:log4j使用大全 [Re:chengbd]
chengbd



版主


发贴: 687
于 2004-11-27 02:46 user profilesend a private message to usersend email to chengbdreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
7. Nested Diagnostic Contexts

在现实世界中的系统经常不得不同时处理多个客户端请求。在这样的一个典型的多线程的系统中,不同的线程将处理不同的客户端。Logging特别能够适应这种复杂的分布式的应用程序的调试和跟踪。一个常见的区分每个客户端所输出的Logging的方法是为每个客户端实例化一个新的独立的Logger。这导致Logger的大量产生,管理的成本也超过了logging本身。

唯一标识每个log请求是一个轻量级的技术。Neil Harrison 在名为“Patterns for Logging Diagnostic Messages”的书中描述了这个方法in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997).

为了唯一标识每个请求,用户把上下文信息推入NDC(Nested Diagnostic Context)中。

NDC类示例如下:

public class NDC {

// Used when printing the diagnostic

public static String get();

// Remove the top of the context from the NDC.

public static String pop();

// Add diagnostic context for the current thread.

public static void push(String message);

// Remove the diagnostic context for this thread.

public static void remove();

}

NDC如同一个堆栈样管理每个线程。注意所有the org.apache.log4j.NDC 类的方法都是静态的。假设NDC输出被开启,每次一个log 请求被生成时,适当的log4j组件为将要输出log的线程包含完整的NDC堆栈。这是在没有用户的干预的情况下做到的,用户只负责在NDC中定位正确的信息,通过在代码中正确位置插入很少的push和pop方法就行了。相反的,在代码中per-client实现方法有着很大变化。

为了演示这个点,让我们以一个发送内容到匿名客户端的servlet为例。这个servlet可以在开始执行每个其他代码前的初始化时建立NDC。上下文信息可以是客户主机的名字和其他的请求中固有的信息。

典型的信息包括cookies。因此,即使servlet同时为多个客户同时提供服务,log 被同样的代码初始化,例如属于同一个logger,依然可以被区别,因为每个客户请求将有不同的NDC堆栈。与之相比,Contrast this with the complexity of passing a freshly instantiated logger to all code exercised during the client's request。

不过,一些诡异的程序,例如虚拟主机的web server记录日志,不是一般的依靠虚拟主机的上下文,还要依靠软件的组件发出请求。近来log4j的发布版本支持多层的树形结构。这个增强允许每个虚拟主机可以处理在树型结构中属于它自己的logger。

8. 优化
一个经常引用的依靠于logging的参数是可以计算的花费。这是一个合理的概念,一个适度的应用程序可能产生成千上万个日志请求。许多努力花在测量和调试logging的优化上。Log4j要求快速和弹性:速度最重要,弹性是其次。

用户应该注意随后的优化建议。

1.日志为禁用时,日志的优化。

当日志被彻底的关闭,一个日志请求的花费等于一个方法的调用加上整数的比较时间。

在233mhz的Pentium II 机器上这个花费通常在5-50纳秒之间。

然而,方法调用包括参数构建的隐藏花费。

例如,对于logger cat,

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

引起了构建信息参数的花费,例如,转化整数i和entry[i]到一个string,并且连接中间字符串,不管信息是否被输出。这个参数的构建花费可能是很高,它主要决定于被调用的参数的大小。

避免参数构建的花费应如下,

if(logger

如果logger的debug被关闭这将不会招致参数构建的花费。另一方面,如果logger是debug的话,它将产生两次判断 logger是否能用的花费。一次是在debugenabled,一次是debug。这是无关紧要的,因为判断日志的能用 只占日志实际花费时间的约1%。

在Log4j里,日志请求在Logger 类的实例里。Logger 是一个类,而不是一个接口。这大量的减少了在方法调用上的弹性化的花费。

当然用户采用预处理或编译时间技术去编译出所有的日志声明。这将导致完美的执行成效。然而因为二进制应用程序不包括任何的日志声明的结果,日志不可能对那个二进制程序开启。以我的观点,以这种较大的代价来换取较小的性能优化是不值得的。

2。当日志状态为启用时,日志的优化。

这是本质上的优化logger的层次。当日志状态为开,Log4j依然需要比较请求的级别与logger的级别。然而, logger可能没有被安排一个级别;它们将从它们的father继承。这样,在继承之前,logger可能需要搜索它的ancestor。

这里有一个认真的努力使层次的搜索尽可能的快。例如,子logger仅仅连接到它的存在的father logger。

在先前展示的BasicConfigurator 例子中,名为com.foo.bar 的logger是连接到跟根logger,因此绕过 了不存在的logger com和com.foo。这将显著的改善执行的速度,特别是解析logger的层结构时。

典型的层次结构的解析的花费是logger彻底关闭时的三倍。

3.日志信息的输出时,日志的优化。

这是主要花费在日志输出的格式化和发送它到它的输出源上。这里我们再一次的付出努力以使格式化执行的尽可能快。同appender一样。实际上典型的花费大约是100-300毫秒。

详情看org.apache.log4.performance.Logging。

虽然Log4j有许多特点,但是它的第一个设计目标还是速度。一些Log4j的组件已经被重写过很多次以改善性能。不过,投稿者经常提出了新的优化。你应该满意的知道,以SimpleLayout的配置执行测试已经展示了Log4j的输出同System.out.println一样快。

9. 总结
Log4j是一个用java写成的流行的日志包。一个它与众不同的特点是在logger中的继承的概念。用logger的继承可以以任意的间隔控制日志的状态输出。这个减少了体积和最小化日志的代价。

易管理性是Log4j API的优点之一。只要日志定义被加入到代码中,它们就可以用配置文件来控制。它们可以有选择的被禁用,并且发送到不同的多个输出源上,以用户选择好的格式。

Log4j的包被设计成,不需花费沉重的执行代价就可以保留它们在产品中。



作者 Re:log4j使用大全 [Re:chengbd]
bluepure

pureblue



发贴: 509
于 2004-11-27 19:45 user profilesend a private message to userreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
log4j已经出到1.2.9版本了。

1.3版本将有比较大的改动。



作者 Re:log4j使用大全 [Re:chengbd]
worldcreatxr





发贴: 149
于 2005-01-20 19:10 user profilesend a private message to userreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
请问:如何通知程序使用log4j.xml而不是log4j.properties

我找了好久了,就是找不着相关的东西,只有xml的文件格式

要想在 web 环境中使用, 一定要自个写个东西???????



我是世界的缔造者!

reply to topicflat modethreaded modego to previous topicgo to next topicgo to back
  已读帖子
  新的帖子
  被删除的帖子
Jump to the top of page

   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2020 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号
客服电话 0592-8750026    客服信箱 714923@qq.com    客服QQ 714923