- Arawn's Dev Blog
- Outsider's Dev Story
- Toby's Epril
- Benelog
- NHN 개발자 블로그
- SK 플래닛 기술 블로그
- OLC CENTER
- 소프트웨어 경영/공학 블로그
- 모바일 컨버전스
- KOSR - Korea Operating System …
- 넥스트리 블로그
- 리버스코어 ReverseCore
- SLiPP
- 개발자를 위하여... (Nextree 임병인 수석)
- "트위터 부트스트랩: 디자이너도 놀라워할 매끈하고 직관…
- Learning English - The English…
- real-english.com
- 'DataScience/Deep Learning' 카테…
- Deep Learning Summer School, M…
- Deep Learning Courses
민서네집
log 내용을 DB에 저장하기 본문
log 내용을 DB의 테이블에 저장하기 위해서는 log4j에서 제공해주는 JDBCAppender 를 사용하면 되는데, 그대로 사용하면 안되고, JDBCAppender 클래스를 상속해서 (') 문자를 치환해줘야지 된다고 한다.
http://stackoverflow.com/questions/6734044/log4j-jdbcappender-to-log-stacktraces
< How to create logs in database using JDBCAppender in log4j >
http://howtodoinjava.com/2013/04/08/how-to-create-logs-in-database-using-jdbcappender-in-log4j/
아니면 non official 한 Log4j JDBCAppender 를 사용하라고 되어 있었다.
http://stackoverflow.com/questions/6734044/log4j-jdbcappender-to-log-stacktraces
< Log4j JDBCAppender 홈페이지 >
http://www.dankomannhaupt.de/projects/index.html
< JDBCAppender >
http://logging.apache.org/log4j/2.x/manual/appenders.html#JDBCAppender
< log4j 를 이용하여 DB에 로그 쌓기 >
< log4j.properties 설정 >
log4j.rootLogger = WARN, DB, myapp ${optional.appender} log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = %d %5p {%t} [%C.%M()] (%F:%L) - %m%n log4j.appender.stdout.Encoding = UTF-8 log4j.appender.DB=com.myapp.home.config.MyJdbcAppender log4j.appender.DB.BufferSize = 1 log4j.appender.DB.URL=jdbc:mysql://localhost:3306/myapp log4j.appender.DB.driver=com.mysql.jdbc.Driver log4j.appender.DB.user=username log4j.appender.DB.password=password log4j.appender.DB.layout=org.apache.log4j.PatternLayout log4j.appender.DB.sql=INSERT INTO ERROR_LOG(CONTEXT_PATH, ERROR_TIME, LEVEL, THREAD, CATEGORY, CLASS, FILE, MESSAGE) VALUES('/${context.root}','%d{ISO8601}','%p','%t','%c','%C.%M()','%F:%L', '%m%n') log4j.appender.DB.Encoding=UTF-8 log4j.appender.DB.ignoreExceptions=false log4j.appender.myapp = org.apache.log4j.DailyRollingFileAppender log4j.appender.myapp.file = ${TOMCAT_LOG_PATH}/myapp_${context.root}.log log4j.appender.myapp.datePattern = '.'yyyy-MM-dd log4j.appender.myapp.layout = org.apache.log4j.PatternLayout log4j.appender.myapp.layout.ConversionPattern=%d %5p {%t} [%C.%M()] (%F:%L) - %m%n log4j.appender.myapp.Encoding=UTF-8 log4j.logger.org.thymeleaf=WARN log4j.logger.org.thymeleaf.TemplateEngine.CONFIG=WARN log4j.logger.org.thymeleaf.TemplateEngine.TIMER=WARN log4j.logger.org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE=WARN log4j.logger.org.thymeleaf.TemplateEngine.cache.FRAGMENT_CACHE=WARN log4j.logger.org.thymeleaf.TemplateEngine.cache.MESSAGE_CACHE=WARN log4j.logger.org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE=WARN log4j.logger.thymeleafexamples=DEBUG log4j.logger.jdbc.audit=OFF log4j.logger.jdbc.sqlonly=OFF
com.myapp.home.config.MyJdbcAppender 클래스
package com.myapp.home.config; import org.apache.log4j.LogManager; import org.apache.log4j.jdbc.JDBCAppender; import org.apache.log4j.spi.LoggingEvent; public class MyJdbcAppender extends JDBCAppender { @Override protected String getLogStatement(LoggingEvent event) { Object eventMsgObj = event.getMessage(); String eventMessage = ""; if( eventMsgObj != null && eventMsgObj.toString() != null ) { // DB에 입력하기 위해서는 (') 를 ('')로 치환해야지 에러없이 제대로 입력된다. eventMessage = eventMsgObj.toString().replaceAll("'", "''").replaceAll("(?<!\r)\n", "\r\n"); } if (null != event.getThrowableInformation() ) { // DB에 저장할 때는 "\r\n" 이 아닌 "\n"만 "\r\n"으로 바꾼다. // 부정형 후방탐색 정규표현식 이용 => replaceAll("(?<!\r)\n", "\r\n") Throwable throwable = event.getThrowableInformation().getThrowable(); String message = ""; if( throwable != null ) { message = throwable.getMessage(); if( message != null ) { message = message.replaceAll("'", "''").replaceAll("(?<!\r)\n", "\r\n"); } } Exception exception = new Exception(message, throwable); exception.setStackTrace(throwable.getStackTrace()); LoggingEvent clone = new LoggingEvent(event.fqnOfCategoryClass, LogManager.getLogger(event.getLoggerName()), event.getLevel(), eventMessage, exception); return getLayout().format(clone); } else { LoggingEvent clone = new LoggingEvent(event.fqnOfCategoryClass, LogManager.getLogger(event.getLoggerName()), event.getLevel(), eventMessage, null); return getLayout().format(clone); } } }
Stack trace를 DB에 넣기 위해 EnhancedPatternLayout 사용하기
[참조] http://stackoverflow.com/questions/6734044/log4j-jdbcappender-to-log-stacktraces
위 웹사이트의 내용과 달라서 주의할 점은
log4j.properties 파일에서 conversionPattern 앞에 layout. 을 붙여야 하고, pom.xml 파일에서
<dependency>
<groupId>log4j</groupId>
<artifactId>apache-log4j-extras</artifactId>
<version>1.2.17</version>
</dependency>
를 넣어줄 필요가 없다는 것이다.
( 나는 log4j 1.2.17 버전을 사용했음. )
log4j.appender.DB=com.myapp.home.config.MyJdbcAppender log4j.appender.DB.BufferSize = 1 log4j.appender.DB.URL=jdbc:mysql://localhost:3306/myapp log4j.appender.DB.driver=com.mysql.jdbc.Driver log4j.appender.DB.user=username log4j.appender.DB.password=password log4j.appender.DB.layout=org.apache.log4j.EnhancedPatternLayout log4j.appender.DB.layout.conversionPattern=INSERT INTO ERROR_LOG(CONTEXT_PATH, ERROR_TIME, LEVEL, THREAD, CATEGORY, CLASS, FILE, MESSAGE) VALUES('/${context.root}','%d{ISO8601}','%p','%t','%c','%C.%M()','%F:%L', '%m %throwable %n') log4j.appender.DB.Encoding=UTF-8 log4j.appender.DB.ignoreExceptions=false
그런데 이렇게 하면 HeidiSQL로 보면 Stack Trace가 줄바꿈이 안되서 보이기 때문에 그냥 예전처럼 PatternLayout 을 사용하고, MyJdbcAppender 클래스의 getLogStatement() 메서드에서 Stack Trace를 message String 에 추가해서 넣어서 해결하려고 함.
HeidiSQL 에서 줄바꿈이 안되서 보이는 원인을 알았다.
Java 에서는 줄바꿈 문자로 \n 를 사용하는데, log4j.properties 파일에서의 %n는 운영체제에 맞는 줄바꿈 문자이다.
Windows 에서는 줄바꿈 문자가 \r\n 따라서 2개가 섞여 있어서 제대로 줄바꿈이 안보였던 것이다.
log4j.properties 파일에서 %n 을 없애고, MyJdbcAppender 파일에서 \n 를 \r\n 으로 replace 해주는 코드를 없애버림.
이렇게 하면 줄바꿈 문자가 \n 로 통일되기 때문에 HeidiSQL에서 줄바꿈 되서 제대로 보인다.
나의 최종 log4j.properties 파일과 MyJdbcAppender 파일은 다음과 같다.
log4j.appender.DB=com.myapp.home.config.MyJdbcAppender log4j.appender.DB.BufferSize = 1 log4j.appender.DB.URL=jdbc:mysql://localhost:3306/myapp log4j.appender.DB.driver=com.mysql.jdbc.Driver log4j.appender.DB.user=username log4j.appender.DB.password=password log4j.appender.DB.layout=org.apache.log4j.EnhancedPatternLayout log4j.appender.DB.layout.conversionPattern=INSERT INTO ERROR_LOG(CONTEXT_PATH, ERROR_TIME, LEVEL, THREAD, CATEGORY, CLASS, FILE, MESSAGE, STACK_TRACE) VALUES(left('/${context.root}',20),left('%d{ISO8601}',30),left('%p',5),left('%t',50),left('%c',100),left('%C.%M()',100),left('%F:%L',50), left('%m',1000), left('%throwable',20000)) log4j.appender.DB.Encoding=UTF-8 log4j.appender.DB.ignoreExceptions=false
package com.myapp.home.config; import java.lang.reflect.Field; import org.apache.log4j.jdbc.JDBCAppender; import org.apache.log4j.spi.LoggingEvent; public class MyJdbcAppender extends JDBCAppender { @Override protected String getLogStatement(LoggingEvent event) { Object eventMsgObj = event.getMessage(); if( eventMsgObj != null && eventMsgObj.toString() != null ) { if( eventMsgObj.toString().contains("'") ) { replace(event, "message"); replace(event, "renderedMessage"); } } if (null != event.getThrowableInformation() ) { Throwable throwable = event.getThrowableInformation().getThrowable(); replace(new Throwable().getClass(), throwable, "detailMessage"); replace(throwable, "message"); // stack trace를 출력할 때는 rep 멤버변수의 문자열이 출력되서 rep도 '를 ''으로 치환해야 한다. replace(event.getThrowableInformation(), "rep"); } // 이 문자열이 올바른 SQL문이 되는지 console로 출력해서 확인해 본다. String format = getLayout().format(event); //System.out.println("##############################"); //System.out.println(format); return format; } private void replace(Object object, String field) { replace(object.getClass(), object, field); } // DB에 저장하기 위해서는 (') 를 ('')로 치환해야지 에러없이 제대로 입력된다. private void replace(Class<?> clazz, Object object, String field) { try { Field f = clazz.getDeclaredField(field); f.setAccessible(true); if( f.get(object) != null ) { if( f.get(object) instanceof String[] ) { String[] stringArr = (String[]) f.get(object); for (int i = 0; i < stringArr.length; i++) { stringArr[i] = stringArr[i].replaceAll("'", "''"); } } else { String value = f.get(object).toString(); if( value.contains("'") ) { f.set(object, value.replaceAll("'", "''")); } } } } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { //e.printStackTrace(); } } }
'Java' 카테고리의 다른 글
상속 대신 Composition 으로 다른 객체의 기능 사용하는 예제 (0) | 2015.01.07 |
---|---|
Interface로 다중상속을 대체할 수 있음을 보여주는 예제 (1) | 2015.01.07 |
코딩으로 log4j 설정 파일 읽어들여 설정 바꾸기 (PropertyConfigurator 이용) (0) | 2014.12.11 |
Tomcat 7 서버 - hotdeploy 시 memory leak 방지 (0) | 2014.10.10 |
Eclipse Scrapbook page 이용하기 (api 간단 테스트) (0) | 2014.10.01 |