热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

log4jmysql单引号_log4j写数据库存在单引号问题

项目中要求把日志信息写到文件的同时也把其写入数据库中,log4j.properties版本log4j-1.2.17,配置如下log4j.rootLoggerINFO,wjc,wjf

项目中要求把日志信息写到文件的同时也把其写入数据库中, log4j.properties版本log4j-1.2.17, 配置如下

log4j.rootLogger=INFO,wjc,wjf,wjj

#common

log4j.appender.wjc.Encoding=GB2312

log4j.appender.wjc=org.apache.log4j.ConsoleAppender

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

log4j.appender.wjc.layout.ConversionPattern=%d %5p (%F:%L) - %m%n

log4j.appender.wjf.Encoding=GB2312

log4j.appender.wjf=org.apache.log4j.DailyRollingFileAppender

log4j.appender.wjf.Append=true

log4j.appender.wjf.File.DatePattern='.'yyyy-MM-dd

log4j.appender.wjf.File=${catalina.base}/logs/jin.log

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

log4j.appender.wjf.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss } %t %p -%m%n

log4j.appender.wjj.Threshold=WARN

log4j.appender.wjj=com.common.component.syslog.Log4JdbcAppender

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

log4j.appender.wjj.sql=insert into cp_syslog(log_time, log_level, location, message) values ('%d{yyyyMMddHHmmss }', '%-5p', '%C,%L', '%m')

#special

log4j.logger.org.apache.cxf=WARN

在代码中调用log.warn("abcd");等就可以把信息写入数据库了.错误日志:

log4j:ERROR Failed to excute sql

com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'classes/spring/spring'')' at line 1

at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:936)

at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2870)

at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1573)

at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1665)

at com.mysql.jdbc.Connection.execSQL(Connection.java:3170)

at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1316)

at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1235)

at com.joysim.common.component.syslog.Log4JdbcAppender.execute(Log4JdbcAppender.java:52)

at org.apache.log4j.jdbc.JDBCAppender.flushBuffer(JDBCAppender.java:289)

at org.apache.log4j.jdbc.JDBCAppender.append(JDBCAppender.java:186)

at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)

经调试发现要执行的sql语句里面包含了单引号,而我们知道要插入数据库中单引号是要进行转义处理的.

从错误信息中跟进发现sql是在JDBCAppender.flushBuffer()方法中进行了处理, 源码如下:

public voidflushBuffer() {//Do the actual logging

removes.ensureCapacity(buffer.size());for (Iterator i =buffer.iterator(); i.hasNext();) {

LoggingEvent logEvent=(LoggingEvent)i.next();try{

String sql=getLogStatement(logEvent);

execute(sql);

}catch(SQLException e) {

errorHandler.error("Failed to excute sql", e,

ErrorCode.FLUSH_FAILURE);

}finally{

removes.add(logEvent);

}

}

打印发现是getLogStatement(logEvent)对sql进行了处理, 该方法的代码如下:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

/*** By default getLogStatement sends the event to the required Layout object.

* The layout will format the given pattern into a workable SQL string.

*

* Overriding this provides direct access to the LoggingEvent

* when constructing the logging statement.

**/

protectedString getLogStatement(LoggingEvent event) {returngetLayout().format(event);

}

View Code

搜索setLayout()方法, 发现是在setSql(String s)方法中进行了赋值, 该方法代码如下:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public voidsetSql(String s) {

sqlStatement=s;if (getLayout() == null) {this.setLayout(newPatternLayout(s));

}else{

((PatternLayout)getLayout()).setConversionPattern(s);

}

}

View Code

该方法的意思就是如果log4j配置文件没有为jdbcAppender配置patterLayout, 那么会默认指定一个PatternLayout对象给jdbcAppender. 接下来就看一下PatternLayout的format方法, 代码如下:

/**Produces a formatted string as specified by the conversion pattern.*/

publicString format(LoggingEvent event) {//Reset working stringbuffer

if(sbuf.capacity() >MAX_CAPACITY) {

sbuf= newStringBuffer(BUF_SIZE);

}else{

sbuf.setLength(0);

}

PatternConverter c= head;while(c != null) {

c.format(sbuf, event);

c=c.next;

}returnsbuf.toString();

}

head在其构造方法中进行了赋值, 代码如下:

publicPatternLayout(String pattern) {this.pattern =pattern;

head= createPatternParser((pattern == null) ?DEFAULT_CONVERSION_PATTERN :

pattern).parse();

}

或是在setConversionPattern方法中进行赋值

public voidsetConversionPattern(String conversionPattern) {

pattern=conversionPattern;

head=createPatternParser(conversionPattern).parse();

}

从上面的代码中可以看到在JDBCAppender的setSql方法中对上述两方法进行了调用.

执行parse(),

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

publicorg.apache.log4j.helpers.PatternConverter parse() {return newBridgePatternConverter(pattern);

}

构造方法publicBridgePatternConverter(finalString pattern) {

next= null;

handlesExceptions= false;

List converters= newArrayList();

List fields= newArrayList();

Map converterRegistry= null;

PatternParser.parse(

pattern, converters, fields, converterRegistry,

PatternParser.getPatternLayoutRules());

patternConverters= newLoggingEventPatternConverter[converters.size()];

patternFields= newFormattingInfo[converters.size()];int i = 0;

Iterator converterIter=converters.iterator();

Iterator fieldIter=fields.iterator();while(converterIter.hasNext()) {

Object converter=converterIter.next();if (converter instanceofLoggingEventPatternConverter) {

patternConverters[i]=(LoggingEventPatternConverter) converter;

handlesExceptions|=patternConverters[i].handlesThrowable();

}else{

patternConverters[i]=

new org.apache.log4j.pattern.LiteralPatternConverter("");

}if(fieldIter.hasNext()) {

patternFields[i]=(FormattingInfo) fieldIter.next();

}else{

patternFields[i]=FormattingInfo.getDefault();

}

i++;

}

}

解析public static voidparse(final String pattern, finalList patternConverters,final List formattingInfos, final Map converterRegistry, finalMap rules) {if (pattern == null) {throw new NullPointerException("pattern");

}

StringBuffer currentLiteral= new StringBuffer(32);int patternLength =pattern.length();int state =LITERAL_STATE;charc;int i = 0;

FormattingInfo formattingInfo=FormattingInfo.getDefault();while (i

c= pattern.charAt(i++);switch(state) {caseLITERAL_STATE://In literal state, the last char is always a literal.

if (i ==patternLength) {

currentLiteral.append(c);continue;

}if (c ==ESCAPE_CHAR) {//peek at the next char.

switch(pattern.charAt(i)) {caseESCAPE_CHAR:

currentLiteral.append(c);

i++; //move pointer

break;default:if (currentLiteral.length() != 0) {

patternConverters.add(newLiteralPatternConverter(currentLiteral.toString()));

formattingInfos.add(FormattingInfo.getDefault());

}

currentLiteral.setLength(0);

currentLiteral.append(c);//append %

state =CONVERTER_STATE;

formattingInfo=FormattingInfo.getDefault();

}

}else{

currentLiteral.append(c);

}break;caseCONVERTER_STATE:

currentLiteral.append(c);switch(c) {case '-':

formattingInfo=

newFormattingInfo(true, formattingInfo.getMinLength(),

formattingInfo.getMaxLength());break;case '.':

state&#61;DOT_STATE;break;default:if ((c >&#61; &#39;0&#39;) && (c <&#61; &#39;9&#39;)) {

formattingInfo&#61;

newFormattingInfo(

formattingInfo.isLeftAligned(), c- &#39;0&#39;,

formattingInfo.getMaxLength());

state&#61;MIN_STATE;

}else{

i&#61;finalizeConverter(

c, pattern, i, currentLiteral, formattingInfo,

converterRegistry, rules, patternConverters, formattingInfos);//Next pattern is assumed to be a literal.

state &#61;LITERAL_STATE;

formattingInfo&#61;FormattingInfo.getDefault();

currentLiteral.setLength(0);

}

}//switch

break;caseMIN_STATE:

currentLiteral.append(c);if ((c >&#61; &#39;0&#39;) && (c <&#61; &#39;9&#39;)) {

formattingInfo&#61;

newFormattingInfo(

formattingInfo.isLeftAligned(),

(formattingInfo.getMinLength()* 10) &#43; (c - &#39;0&#39;),

formattingInfo.getMaxLength());

}else if (c &#61;&#61; &#39;.&#39;) {

state&#61;DOT_STATE;

}else{

i&#61;finalizeConverter(

c, pattern, i, currentLiteral, formattingInfo,

converterRegistry, rules, patternConverters, formattingInfos);

state&#61;LITERAL_STATE;

formattingInfo&#61;FormattingInfo.getDefault();

currentLiteral.setLength(0);

}break;caseDOT_STATE:

currentLiteral.append(c);if ((c >&#61; &#39;0&#39;) && (c <&#61; &#39;9&#39;)) {

formattingInfo&#61;

newFormattingInfo(

formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),

c- &#39;0&#39;);

state&#61;MAX_STATE;

}else{

LogLog.error("Error occured in position " &#43;i&#43; ".\n Was expecting digit, instead got char \"" &#43; c &#43; "\".");

state&#61;LITERAL_STATE;

}break;caseMAX_STATE:

currentLiteral.append(c);if ((c >&#61; &#39;0&#39;) && (c <&#61; &#39;9&#39;)) {

formattingInfo&#61;

newFormattingInfo(

formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),

(formattingInfo.getMaxLength()* 10) &#43; (c - &#39;0&#39;));

}else{

i&#61;finalizeConverter(

c, pattern, i, currentLiteral, formattingInfo,

converterRegistry, rules, patternConverters, formattingInfos);

state&#61;LITERAL_STATE;

formattingInfo&#61;FormattingInfo.getDefault();

currentLiteral.setLength(0);

}break;

}//switch

}//while

if (currentLiteral.length() !&#61; 0) {

patternConverters.add(newLiteralPatternConverter(currentLiteral.toString()));

formattingInfos.add(FormattingInfo.getDefault());

}

}

View Code

代码较长, 大概的意思就是把log4j配置文件中的sql语句, insert into cp_syslog(log_time, log_level, location, message) values (&#39;%d{yyyyMMddHHmmss }&#39;, &#39;%-5p&#39;, &#39;%C,%L&#39;, &#39;%m&#39;), 中的p, c, l, m等都转成PatternConverter, 并返回第一个PatternConverter, 说到底就是一个链表, 而this.head是这个链表的头元素. 现在主要就是关注各个PatternConverter的format方法,

static{//We set the global rules in the static initializer of PatternParser class

Map rules &#61; new HashMap(17);

rules.put("c", LoggerPatternConverter.class);

rules.put("logger", LoggerPatternConverter.class);

rules.put("C", ClassNamePatternConverter.class);

rules.put("class", ClassNamePatternConverter.class);

rules.put("d", DatePatternConverter.class);

rules.put("date", DatePatternConverter.class);

rules.put("F", FileLocationPatternConverter.class);

rules.put("file", FileLocationPatternConverter.class);

rules.put("l", FullLocationPatternConverter.class);

rules.put("L", LineLocationPatternConverter.class);

rules.put("line", LineLocationPatternConverter.class);

rules.put("m", MessagePatternConverter.class);

rules.put("message", MessagePatternConverter.class);

rules.put("n", LineSeparatorPatternConverter.class);

rules.put("M", MethodLocationPatternConverter.class);

rules.put("method", MethodLocationPatternConverter.class);

rules.put("p", LevelPatternConverter.class);

rules.put("level", LevelPatternConverter.class);

rules.put("r", RelativeTimePatternConverter.class);

rules.put("relative", RelativeTimePatternConverter.class);

rules.put("t", ThreadPatternConverter.class);

rules.put("thread", ThreadPatternConverter.class);

rules.put("x", NDCPatternConverter.class);

rules.put("ndc", NDCPatternConverter.class);

rules.put("X", PropertiesPatternConverter.class);

rules.put("properties", PropertiesPatternConverter.class);

rules.put("sn", SequenceNumberPatternConverter.class);

rules.put("sequenceNumber", SequenceNumberPatternConverter.class);

rules.put("throwable", ThrowableInformationPatternConverter.class);

PATTERN_LAYOUT_RULES&#61; newReadOnlyMap(rules);

Map fnameRules&#61; new HashMap(4);

fnameRules.put("d", FileDatePatternConverter.class);

fnameRules.put("date", FileDatePatternConverter.class);

fnameRules.put("i", IntegerPatternConverter.class);

fnameRules.put("index", IntegerPatternConverter.class);

FILENAME_PATTERN_RULES&#61; newReadOnlyMap(fnameRules);

}

MessagePatternConverter的format方法如下:

public void format(final LoggingEvent event, finalStringBuffer toAppendTo) {

toAppendTo.append(event.getRenderedMessage());

}

MyLoggingEvent类中有getThreadName和getRenderedMessage两个方法

很奇怪, 跟踪到这里发现源码中是有对单引号进行处理的, 可是为什么还会出现问题呢

ThreadPatternConverter 的format方法如下:

public void format(final LoggingEvent event, finalStringBuffer toAppendTo) {

toAppendTo.append(event.getThreadName());

}

我们可以在event.getThreadName()和event.getRenderedMessage()方法返回前对单引号进行替换, 因此我们可以写一个类扩展LoggingEvent, 重写LoggingEvent的getRenderedMessage和getThreadName方法, 代码如下:

public class MyLoggingEvent extendsLoggingEvent {/** */

private static final long serialVersionUID &#61; -3499094864944744184L;publicMyLoggingEvent(String fqnOfCategoryClass, Category logger, Priority level, Object message, Throwable throwable) {super(fqnOfCategoryClass, logger, level, message, throwable);

}publicString getThreadName() {

String thrdName&#61; super.getThreadName();if (thrdName.indexOf("&#39;") !&#61; -1) {

thrdName&#61; thrdName.replaceAll("&#39;", "&#39;&#39;");

}returnthrdName;

}publicString getRenderedMessage() {

String msg&#61; super.getRenderedMessage();if (msg.indexOf("&#39;") !&#61; -1) {

msg&#61; msg.replaceAll("&#39;", "&#39;&#39;");

}returnmsg;

}

}

最后在我们log4j配置文件中指定的Log4JdbcAppender处理类中, 覆写JDBCAppender的getLogStatement方法, 让程序执行我们重写的MyLoggingEvent类中的相应方法, 代码如下:

public class Log4JdbcAppender extendsJDBCAppender {privateJdbcSupport jdbcSupport;

&#64;Overrideprotected voidcloseConnection(Connection conn) {try{if (conn !&#61; null && !conn.isClosed())

conn.close();

}catch(SQLException e) {

errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE);

}

}

&#64;Overrideprotected Connection getConnection() throwsSQLException {if (jdbcSupport &#61;&#61; null) {

jdbcSupport&#61; SpringUtils.getBean(JdbcSupport.class, "jdbcSupport");

}return jdbcSupport &#61;&#61; null ? null: jdbcSupport.getJt().getDataSource().getConnection();

}

&#64;Overrideprotected void execute(String sql) throwsSQLException {

Connection conn&#61;getConnection();if (conn !&#61; null) {

Statement stmt&#61; null;try{

stmt&#61;conn.createStatement();

stmt.executeUpdate(sql);

}catch(SQLException e) {if (stmt !&#61; null)

stmt.close();throwe;

}

stmt.close();

closeConnection(conn);

}

}

&#64;OverrideprotectedString getLogStatement(LoggingEvent event) {

String fqnOfCategoryClass&#61;event.fqnOfCategoryClass;

Category logger&#61;event.getLogger();

Priority level&#61;event.getLevel();

Object message&#61;event.getMessage();

Throwable throwable&#61;null;

MyLoggingEvent bEvent&#61;new MyLoggingEvent(fqnOfCategoryClass,logger,level,message,throwable);

return super.getLogStatement(bEvent);

}

}

大功告成, 日志成功插入到数据库

5b8970f669d999f330cb7c6eba0b57d4.png



推荐阅读
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 本文介绍了一款名为TimeSelector的Android日期时间选择器,采用了Material Design风格,可以在Android Studio中通过gradle添加依赖来使用,也可以在Eclipse中下载源码使用。文章详细介绍了TimeSelector的构造方法和参数说明,以及如何使用回调函数来处理选取时间后的操作。同时还提供了示例代码和可选的起始时间和结束时间设置。 ... [详细]
  • 合并列值-合并为一列问题需求:createtabletab(Aint,Bint,Cint)inserttabselect1,2,3unionallsel ... [详细]
  • 微信小程序导航跟随的实现方法
    本文介绍了在微信小程序中实现导航跟随的方法。通过设置导航的position属性和绑定滚动事件,可以实现页面向下滚动到导航位置时,导航固定在页面最上方;页面向上滚动到导航位置时,导航恢复到原始位置;点击导航可以平滑跳转到相应位置。代码示例也给出了具体实现方法。 ... [详细]
  • 今天就跟大家聊聊有关怎么在Android应用中实现一个换肤功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根 ... [详细]
  • python3 logging
    python3logginghttps:docs.python.org3.5librarylogging.html,先3.5是因为我当前的python版本是3.5之所 ... [详细]
  • android:EditText属性去边框EditText继承关系:View--TextView--EditTextEditText的属性很多,这里介绍几个:android:h ... [详细]
  • 涉及的知识点-ViewGroup的测量与布局-View的测量与布局-滑动冲突的处理-VelocityTracker滑动速率跟踪-Scroller实现弹性滑动-屏幕宽高的获取等实现步 ... [详细]
  • 前记上周利用空余时间,做了一下0ctf,感觉这道bypassdisablefunction的题目比较有趣,于是分析一下,有了 ... [详细]
author-avatar
大笨猫的男人
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有