我正在尝试将日志消息保存到中央数据库。为此,我在log4j的xml配置中配置了以下Appender:
<appender name="DB" class="org.apache.log4j.jdbc.JDBCAppender">
<param name="URL" value="jdbc:postgresql://localhost/logging_test" />
<param name="user" value="test_user" />
<param name="password" value="test_password" />
<param name="sql" value="INSERT INTO log_messages ( log_level, message, log_date ) VALUES ( '%p', '%m', '%d{yyyy-MM-dd HH:mm:ss}' )" />
</appender>
这样可以正常工作,除了一些消息包含',然后appender失败。
有一种简单的方法吗?
答案 0 :(得分:6)
我建议您创建一个自定义appender并覆盖flushBuffer
和execute
方法,您可以在这些方法中转义字符串或使用PreparedStatement
:
public class MyJDBCAppender extends JDBCAppender {
}
要解释为什么需要覆盖flushBuffer
- appender将LogEvent
个对象放入缓冲区,稍后将其刷回目标(在本例中为数据库)。此处,flushBuffer
方法使用getLogStatement
和(通过execute
)正常Statement
。您可以完全替换该行为。看看the current source code
然后注册你的appender而不是JDBCAppender
。
答案 1 :(得分:3)
我不熟悉log4j或JDBC,但我知道JDBC支持预处理语句。也许有一种方法可以将它与JDBCAppender一起使用
答案 2 :(得分:3)
看看这个解决此问题的非官方Log4J JDBCAppender,并在Apache 2.0许可下发布。在与org.apache.log4j.jdbc.JDBCAppender
:
- 登录(关系)数据库
- 灵活的连接处理(尚不支持DataSource)
- 用于执行实际记录的灵活sql命令
- 支持预备语句和存储过程(J2SDK 1.4+)
- 启用具有特殊字符的消息记录,例如'(单个 引用)和(逗号)
- 灵活的表格和列结构
- 灵活的ID生成
- 允许多个PatternLayout应用程序;在一个或多个 列
- 支持J2SDK 1.3,1.4和1.5
- 支持Log4j 1.2.9和当前开发
或者,你应该认真考虑这个选项,从log4j切换到它的继任者logback(这是发生的事情),DBAppender
使用PreparedStatement
(参见sources),可以使用JNDI数据源,连接池(这是一个很大的优点),等等。有关此appender的更多信息,请参阅在线手册http://logback.qos.ch/manual/appenders.html#DBAppender
答案 3 :(得分:3)
我用以下方式解决了问题:
复制了名为JDBCAppender
ACMEJDBCAppender
的源代码
覆盖getLogStatement(LoggingEvent event)
方法,克隆旧事件并为新事件提供转义邮件。
从oop的角度来看,这不是最干净的解决方案,但它可以完成工作。希望它有所帮助。
protected String getLogStatement(LoggingEvent event) {
LoggingEvent clone = new LoggingEvent(
event.fqnOfCategoryClass,
LogManager.getLogger(event.getLoggerName()),
event.getLevel(),
AidaUtils.sqlEscape(event.getMessage().toString()),
event.getThrowableInformation()!=null ? event.getThrowableInformation().getThrowable() : null
);
return getLayout().format(clone);
}
答案 4 :(得分:1)
根据Javadocs,官方JDBCAppender非常有限,特别是没有办法处理这个问题。
另一种方法是使用替代的appender,例如this one,其目的是在功能上与Log4J兼容,除此之外,你工作。
答案 5 :(得分:1)
要解决此问题,请记录到Oracle,您可以使用Oracle的报价运算符。
将报价运算符包裹在%m附近(即q#'%m'#)
例如:
INSERT INTO log_messages ( log_level, message, log_date )
VALUES ( '%p', q#'%m'#, '%d{yyyy-MM-dd HH:mm:ss}' )
答案 6 :(得分:0)
Joao,抱歉迟到了,但现在是:
<appender name="DB" class="org.apache.log4j.db.DBAppender">
<connectionSource class="org.apache.log4j.db.DriverManagerConnectionSource">
<param name="driverClass" value="org.postgresql.Driver" />
<param name="url" value="jdbc:postgresql://localhost/database" />
<param name="user" value="user" />
<param name="password" value="password" />
</connectionSource>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [%t] %c - %m%n" />
</layout>
</appender>
希望它有所帮助!
答案 7 :(得分:0)
如果您使用的是SQL Server,可以使用以下
SET QUOTED_IDENTIFIER OFF;
INSERT INTO log_messages ( log_level, message, log_date ) VALUES ( "%p", ":%m", "%d{yyyy-MM-dd HH:mm:ss}" )'
SET QUOTED_IDENTIFIER OFF;
将问题转换为双引号。如果您的消息中没有双引号,则可以解决问题。
答案 8 :(得分:0)
对于postgresql,请使用$$
示例:$$%m $$