EJB 应用中 log4j 日志框架的使用与优化
1. EJB 日志需求与 log4j 简介
Enterprise JavaBeans (EJB) 提供了强大的功能,但在运行时调试和用户跟踪方面存在困难。一个好的日志框架可以通过在整个应用程序中留下编码痕迹来解决大部分复杂性问题。有经验的开发人员将日志记录作为调试工具和跟踪应用程序使用情况的系统。
随着 Java 在企业领域的应用越来越广泛,日志框架对于企业应用程序变得越来越重要。大型 EJB 应用程序可能会超出普通日志框架的极限,因为企业应用程序支持多个客户端,在多台服务器的集群环境中运行,并且包含大量事务。这些因素使得应用程序更加健壮,但也削弱了典型的自制日志记录器的实用性。
log4j 是 Apache 提供的一个开源日志框架,它具有模块化、非侵入性的特点,正越来越受欢迎。它快速、轻量级、可扩展且易于配置。使用 log4j 主要有以下步骤:
1. 从 http://jakarta.apache.org/log4j 下载 log4j。(也可以在该网站找到更多关于 log4j 和日志框架的信息)
2. 将 log4j 文件包含在应用程序类路径中。(具体可参考应用服务器供应商文档)
3. 在代码中添加日志记录器。
4. 在属性文件中,为代码中引用的每个日志记录器设置日志级别并分配一个附加器。
5. 使用配置初始化日志框架。
1.1 添加日志记录器到代码
以一个经纪应用中的 EJB 类
AccountBean
为例:
import javax.ejb.SessionBean;
import org.apache.log4j.Logger;
public class AccountBean implements SessionBean
{
private static final Logger logger = null;
public void ejbCreate()
{
// 实例化一个日志记录器对象
logger = Logger.getLogger( "ejb.messages" );
// 写入一条信息日志
logger.info("Creating instance of AccountBean" );
}
public void ejbActivate()
throws RemoteException, EJBException
{
logger = Logger.getLogger("ejb.messages" );
}
public void buyStock( String symbol, int shares )
throws AccountException
{
// 写入一条调试日志
logger.debug("Buying " + shares + " of " + symbol );
}
public void sellStock( String symbol, int shares )
throws AccountException
{
logger.debug("Buying " + shares + " of " + symbol );
}
// 剩余的 bean 方法如下
}
从代码中可以看出,log4j 使用简单。首先需要导入
Logger
类,它是日志框架中最常用的类,提供了写入日志消息的方法。在
ejbCreate()
和
ejbActivate()
方法中,获取了一个名为
ejb.messages
的新
Logger
实例。
1.2 设置属性文件
以下是
logconfig.properties
配置文件的内容:
# 设置日志记录器级别和附加器
log4j.rootLogger=DEBUG, stdout
log4j.logger.ejb.messages=DEBUG, stdout
# 设置日志附加器
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] %F:%l - %m%n
设置 log4j 时,需要为代码中引用的每个日志记录器设置日志级别并分配一个附加器。在这个例子中,将根日志记录器和
ejb.messages
日志记录器都设置为 DEBUG 级别,并分配了一个名为
stdout
的附加器。根日志记录器是所有日志记录器的父级,如果某个日志记录器没有分配级别,它将使用根日志记录器的级别。配置根日志记录器是可选的。设置好这些值后,需要配置分配给日志记录器的
stdout
附加器。
附加器被分配一个类和一个布局,以及特定类型附加器可用的任何其他属性。
stdout
附加器被分配了
org.apache.log4j.ConsoleAppender
类,它将日志消息路由到控制台窗口。此外,它的布局包含在同一包的
PatternLayout
类中,
PatternLayout
类允许我们为消息分配特定的格式。
1.3 初始化日志框架
在开始通过日志系统发送消息之前,必须使用配置初始化日志框架。例如,使用
org.apache.log4j.PropertyConfigurator
类来初始化经纪应用程序:
Propertyconfigurator.configure( "logconfig.properties" );
只需要调用静态的
configure
方法并传入包含日志配置的属性文件的名称。只需要做一次,通过从 EJB 中调用静态类的初始化方法。此外,如果修改了属性文件,可以使用
configureAndWatch()
方法强制 log4j 框架重新加载配置。
2. log4j 日志消息格式化
2.1 问题与背景
在已经为 EJB 应用程序设置好 log4j 并编写好所有需要的日志消息后,记录日志事件只是一个好的日志系统的一部分。消息的最终形式与消息本身一样重要。无论消息的最终目的地是文件、数据库还是 JMS 队列,都希望以特定的方式格式化它们,而无需更改任何代码。
2.2 解决方案
在为相关日志记录器设置附加器时,使用
PatternLayout
类来格式化消息。例如,设置一个名为
ejb.session
的日志记录器及其附加器:
log4j.logger.ejb.session=DEBUG, myappender
# 设置附加器 myappender
log4j.appender.myappender=org.apache.log4j.ConsoleAppender
log4j.appender.myappender.layout=org.apache.log4j.PatternLayout
log4j.appender.myappender.ConversionPattern=%5p [%t] - %m%n
指定
PatternLayout
类作为格式化器后,还需要为附加器指定
ConversionPattern
属性的值。
ConversionPattern
值告诉
PatternLayout
类如何格式化消息。以下是示例代码及其输出:
public void testMethod(){
Logger ejbSession = Logger.getLogger( "ejb.session" );
ejbSession.debug("Writing statement 1" );
ejbSession.debug("Writing statement 2" );
}
调用
testMethod()
会产生以下输出:
DEBUG [testMethod] – Writing statement 1
DEBUG [testMethod] – Writing statement 2
2.3 转换模式说明
转换模式值由一组转换说明符和任意文字文本组成。转换说明符以
%
开头,后面可以跟一个可选的格式修饰符,然后是一个转换字符。格式修饰符控制间距、对齐方式等,转换字符指定要包含在消息中的数据。以下是
PatternLayout
类可用的转换字符:
| 字符 | 描述 |
| ---- | ---- |
| c | 输出发送消息的日志记录器的名称。可以使用
{}
对日志记录器名称进行子字符串操作。 |
| C | 输出包含消息的完全限定类名。可以使用
{}
对类名进行子字符串操作。 |
| d | 输出日志事件的时间戳。使用
{}
指定格式。默认使用 ISO8601 格式。此外,可以使用 log4j 日期格式化器,如
ABSOLUTE
、
DATE
或
ISO8601
。 |
| f | 输出消息编码所在的文件名。 |
| l | 输出日志事件的位置信息。这因 JVM 实现而异,但通常包括完全限定类名、源文件名和行号。 |
| L | 输出消息编码所在的行号。 |
| m | 输出消息。 |
| M | 输出事件发生的方法名。 |
| n | 输出执行平台的行分隔符。 |
| p | 输出日志事件的优先级。 |
| r | 输出应用程序启动以来的经过时间(以毫秒为单位)。 |
| t | 输出日志事件的线程名。 |
| x | 输出与线程关联的嵌套诊断上下文。 |
| X | 输出与线程关联的映射诊断上下文。必须使用
{}
指定客户端编号。 |
| % | 使用
%%
输出单个
%
字符。 |
以下是一些格式化转换字符的示例(以
m
为例):
| 字符 | 描述 |
| ---- | ---- |
| %5m | 消息(
m
)必须至少 5 个字符长。必要时,格式化器将在左侧填充。 |
| %-5m | 消息(
m
)必须至少 5 个字符长。必要时,格式化器将在右侧填充。 |
| %.5m | 消息(
m
)最多 5 个字符长。必要时,格式化器将截断为 5 个字符。 |
在格式化消息时,可以组合使用格式修饰符。例如,修饰符
%10.50c
会在日志记录器名称少于 10 个字符时在左侧填充,超过 50 个字符时截断。
3. 提高 log4j 日志性能
3.1 问题与背景
在为整个 EJB 应用程序添加日志记录代码后,可能会注意到性能下降。日志记录可能会以多种方式影响应用程序的性能。虽然 log4j 可能是最快的日志记录器之一,但根据使用方式的不同,仍可能损害应用程序性能。影响性能的两个最常见的方面是日志消息的数量和日志消息的构造。
3.2 解决方案
有四种方法可以提高 log4j 的性能:
1.
调整日志记录器级别
:大型应用程序可能有数千条不同级别的日志消息。修改日志配置文件,只将绝对必要的消息输出到日志中。禁用不必要的消息将提高应用程序的整体性能。例如,在生产环境中测试应用程序后,可以关闭不再需要的调试语句。
2.
经济地构造消息
:在输出消息时,性能不仅会受到实际日志记录操作的影响,还会受到消息构造的影响。对比以下两段代码:
// 第一段代码
public String getValue()
{
String returnValue = computeValue();
Logger.debug( "Final value of action: " + action
+ " is " + computeValue() );
return returnValue;
}
// 第二段代码
public String getValue()
{
String returnValue = computeValue();
if( logger.isDebugEnabled() ) // 检查级别是否启用
{
logger.debug("Final value of action " +
action + " is " + returnValue ); // 重用计算的值
}
return returnValue;
}
第一段日志记录语句即使在日志系统的 DEBUG 级别禁用的情况下,也会产生额外的
computeValue()
方法调用和字符串连接的开销。而第二段语句明智地重用了
returnValue
对象,并且仅在 DEBUG 级别启用时才构造日志消息。
3.
谨慎使用格式化
:
PatternLayout
类和
ConversionPattern
属性是 log4j 最常见的配置之一。然而,
PatternLayout
的许多格式化选项可能会减慢应用程序的速度。以下是一些应谨慎使用的转换字符:
| 字符 | 获取的数据 | 性能问题 |
| ---- | ---- | ---- |
| C | 完全限定类名 | 日志框架必须遍历堆栈跟踪才能构建发送消息的对象的类名。 |
| d | 日志事件的日期 | 如果使用此字符,建议使用 log4j 日期格式化器,如
ABSOLUTE
、
DATE
或
ISO8601
。JDK 的
SimpleDateFormat
比 log4j 格式化器慢得多。 |
| F | 日志事件的文件名 | 与使用
C
有相同的问题。 |
| l | 位置信息 | 需要遍历堆栈跟踪以收集类、文件和行号的信息。 |
| L | 行号 | 与
l
有相同的问题。 |
| M | 方法名 | 与
l
有相同的问题。 |
4.
使用快速的附加器布局类
:将布局类更改为
org.apache.log4j.SimpleLayout
类。这个布局类产生的消息只包含日志级别和日志事件的消息,例如:
DEBUG – my message from the application
SimpleLayout
类是所有布局类中最快的。
3.3 讨论
无论 log4j 系统本身有多快,提高日志系统的性能都依赖于良好的编程实践和精心的消息构造。例如,重用已经获取的对象值可以减少构建消息所需的时间。此外,字符串连接、将基本类型转换为字符串等操作应仅在必要时使用。如果必须从各个部分构造消息,至少应该检查日志记录器的级别,以避免不必要的消息构造。在使用
PatternLayout
类格式化消息时,始终要关注某些转换字符可能导致的性能下降。如果性能是首要目标,可以切换到
SimpleLayout
类进行消息格式化,虽然它只提供简单的消息结构,但性能是最快的。
4. 使用日志生成报告
4.1 问题与背景
在许多生产系统中,日志系统用于向生产支持用户报告关键错误。如果日志系统输出到单个文件(甚至多个文件),用户可能难以在复杂的日志文件中查找特定消息。在特定时间(例如每周或每月),支持人员希望将上一周期的错误或关键事件汇总成报告。
4.2 解决方案
为了能够生成日志报告,需要在 log4j 配置文件中为 log4j 设置 JDBC 附加器。以下是在 log4j 属性文件中为 Oracle 数据库设置附加器的示例:
log4j.appender.myJDBC=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.myJDBC.URL=jdbc:oracle:thin:@myhost:1521:mysid
log4j.appender.myJDBC.user=user
log4j.appender.myJDBC.password=password
log4j.appender.myJDBC.sql=INSERT INTO LogTable (date, logger_name, thread, message ) VALUES ('%d', '%c', '%t', '%m')
log4j.appender.myJDBC.driver=oracle.jdbc.driver.OracleDriver
同时,要确保将新的附加器分配给一个可用的日志记录器:
log4j.logger.myLogger=DEBUG, myJDBC
4.3 讨论
通过将日志消息插入到数据库表中,可以方便地生成各种类型的报告。例如,可以记录性能数据,然后收集这些数据以显示应用程序特定组件的性能。此外,log4j 允许创建多个附加器和多个日志记录器,这种灵活的系统可以为多个日志记录器创建单独的数据库表,从而有效地分离消息,以便更好地对报告进行分类。
5. 日志记录到 JMS 目的地
5.1 需求与背景
在一些场景中,需要将日志消息发送到 JMS(Java Message Service)目的地,例如 JMS 队列或主题。这样可以实现日志消息的异步处理,并且可以方便地与其他系统进行集成。
5.2 实现步骤
- 添加依赖 :确保项目中包含了 log4j 和 JMS 相关的依赖库。
- 配置 log4j :在 log4j 配置文件中设置 JMS 附加器。以下是一个示例配置:
log4j.appender.jmsAppender=org.apache.log4j.net.JMSAppender
log4j.appender.jmsAppender.InitialContextFactoryName=org.apache.activemq.jndi.ActiveMQInitialContextFactory
log4j.appender.jmsAppender.ProviderURL=tcp://localhost:61616
log4j.appender.jmsAppender.TopicBindingName=logTopic
log4j.appender.jmsAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.jmsAppender.layout.ConversionPattern=%5p [%t] %F:%l - %m%n
log4j.rootLogger=DEBUG, jmsAppender
- 在代码中使用 :在代码中添加日志记录器并使用它记录消息,日志消息将被发送到配置的 JMS 目的地。
import org.apache.log4j.Logger;
public class JMSLoggingExample {
private static final Logger logger = Logger.getLogger(JMSLoggingExample.class);
public static void main(String[] args) {
logger.info("This is a log message sent to JMS.");
}
}
5.3 注意事项
- 确保 JMS 服务器正常运行,并且配置的连接信息正确。
- 可以根据需要调整 JMS 附加器的配置,例如更改目的地名称、消息格式等。
6. XML 格式日志记录
6.1 背景与优势
XML 格式的日志记录具有良好的可读性和可解析性,适合用于复杂的日志分析和处理。可以方便地使用 XML 解析工具对日志进行解析和处理。
6.2 配置 log4j
在 log4j 配置文件中设置 XML 附加器。以下是一个示例配置:
log4j.appender.xmlAppender=org.apache.log4j.xml.XMLLayout
log4j.appender.xmlAppender.layout=org.apache.log4j.xml.XMLLayout
log4j.rootLogger=DEBUG, xmlAppender
6.3 日志输出示例
使用上述配置后,日志消息将以 XML 格式输出,示例如下:
<log4j:event logger="com.example.MyClass" timestamp="1640995200000" level="INFO" thread="main">
<log4j:message>This is an XML log message.</log4j:message>
</log4j:event>
6.4 处理 XML 日志
可以使用各种 XML 解析工具(如 DOM、SAX、JAXB 等)对 XML 日志进行解析和处理,提取有用的信息。
7. 集群环境下的日志记录
7.1 挑战与需求
在集群环境中,多个服务器实例同时运行,日志记录需要考虑如何统一管理和查看。不同服务器上的日志可能分散在各个节点,需要一种方式将它们集中起来进行分析。
7.2 解决方案
- 集中日志存储 :使用集中式日志存储系统,如 Elasticsearch、Logstash 和 Kibana(ELK 栈)。将各个服务器上的日志发送到 Elasticsearch 进行存储和索引,然后使用 Kibana 进行可视化和查询。
- 配置 log4j :在每个服务器节点的 log4j 配置文件中设置相应的附加器,将日志消息发送到集中式日志存储系统。以下是一个示例配置,将日志发送到 Logstash:
log4j.appender.logstashAppender=org.apache.log4j.net.SocketAppender
log4j.appender.logstashAppender.RemoteHost=logstash-server
log4j.appender.logstashAppender.Port=5000
log4j.appender.logstashAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.logstashAppender.layout.ConversionPattern=%5p [%t] %F:%l - %m%n
log4j.rootLogger=DEBUG, logstashAppender
7.3 流程图
graph LR
A[Server 1] --> B[Logstash]
C[Server 2] --> B
D[Server 3] --> B
B --> E[Elasticsearch]
E --> F[Kibana]
7.4 优势
- 集中管理:可以在一个地方查看和分析所有服务器的日志。
- 可视化:通过 Kibana 可以直观地查看日志信息,进行统计和分析。
- 搜索功能:可以方便地搜索特定的日志消息。
总结
本文详细介绍了在 EJB 应用中使用 log4j 日志框架的各个方面,包括日志配置、消息格式化、性能优化、报告生成等。通过合理使用 log4j 的各种功能,可以有效地解决 EJB 应用中的日志记录和管理问题。同时,针对不同的场景,如 JMS 日志记录、XML 格式日志、集群环境下的日志记录等,也给出了相应的解决方案。在实际应用中,需要根据具体需求选择合适的配置和方法,以提高日志系统的效率和可用性。
超级会员免费看
2万+

被折叠的 条评论
为什么被折叠?



