간격 시간과 인쇄 로그를 정기적으로 구성하십시오
요청을받은 후 로그는 log4J를 통해 정기적으로 인쇄됩니다. 요구 사항 설명은 다음과 같습니다. 로그를 정기적으로 인쇄 할 수 있고 시간 간격이 일치 할 수 있습니다. 타이밍에 대해 말하면, 우선, 나는 DailyrollingfileAppender 클래스를 생각합니다. 다양한 타이밍. DatePattern에 따르면 SimpledateFormat 클래스를 참조 할 수 있습니다. 일부 일반적인 타이밍 설정은 다음과 같습니다.
관찰을 통해 N 분과 비슷한 날짜 형식이 없다는 것을 알았으므로 DailyrollingFileAppender 클래스를 기반으로 사용자 정의 클래스를 작성했습니다. 프로세스는 다음과 같습니다.
1) DailyrollingFileAppender 클래스의 소스 코드를 복사하여 MinuterollingAppender의 이름을 바꿉니다. log4j.xml로 구성하려면 구성 항목 간격 시간을 추가하고 세트를 추가하고 메소드를 얻습니다.
개인 int 간격 시간 = 10;
2) DailyrollingFileAppender 클래스는 RollingCalendar 클래스를 사용하여 다음 간격 시간을 계산하고 매개 변수 간격 시간을 전달해야하므로 RollingCalendar 클래스는 내부 클래스로 수정됩니다. 방법은 DatePattern을 기반으로 다음 롤오버 동작의 시간을 계산하는 것이므로이 시점에서 다른 시간 모드가 필요하지 않으므로 수정 방법은 다음과 같습니다.
공개 날짜 getNextCheckDate (지금 날짜) {this.settime (지금); this.set (calendar.second, 0); this.set (calendar.millisecond, 0); this.add (calendar.minute, intervaltime); return gettime (); }3) 시간 모드가 분에 따라 일치하면 시간 모드를 비활성화해야합니다. 정적 최종으로 변경하고 응답에서 Get, Set 메소드 및 MinuterollingAppender 생성자에서 DatePattern 매개 변수를 제거하십시오.
개인 정적 문자열 datepattern = " '.'yyyy-mm-dd-hh-mm'.log'";
마찬가지로, 여러 날짜 패턴을 제공하는 CompuTeCheckPeriod ()도 삭제할 수 있습니다. 변환이 완료되었으며 완제품 카테고리는 다음과 같습니다.
패키지 net.csdn.blog; import java.io.file; import java.io.ioexception; import java.io.interruptedioexception; import java.text.simpledateformat; java.util.calendar 가져 오기; import java.util.date; java.util.GregorianCalendar 가져 오기; import org.apache.log4j.fileappender; import org.apache.log4j.layout; import org.apache.log4j.helpers.loglog; import org.apache.log4j.spi.loggingevent; /** * 미세한 appender의 타이머 이용 * * @author coder_xia * * /public class minuterollingAppender는 fileappender { /** * 날짜 패턴을 확장합니다. 기본적으로 패턴은 매일 롤오버를 의미하는 " '.'yyyy-mm-dd" *로 설정됩니다. */ private static string datePattern = " '.'yyyy-mm-dd-hh-mm'.log'"; / *** 간격 시간, 단위 : ming*/ private int intervaltime = 10; /** * 다음 간격이 입력 될 때 로그 파일의 이름이 scheduledfilename * 변수로 바뀝니다. 예를 들어, 롤오버 * 기간이 1 시간 인 경우 다음 시간의 시작 부분에 로그 파일의 이름이 * "ScheduledFilename"의 이름으로 바뀝니다. * * 로깅 활동에 따라 롤오버가 발생하는 정밀 시간. */ 개인 문자열 scheduledFilename; /*** 다음에 롤오버가 발생한다고 추정 할 때. */ private long nextCheck = System.CurrentTimeMillis () -1; 날짜 지금 = 새 날짜 (); SimpledateFormat SDF; RollingCalendar RC = New RollingCalendar (); /*** 기본 생성자는 아무것도하지 않습니다. */public minuterollingAppender () {}/** * a <code> minuterollingAppender </code>를 인스턴스하고 <code> filename </code>에 의해 디자인 된 파일 *을 엽니 다. 열린 Filename 은이 Appender의 * OUPUT 목적지가됩니다. */ public minuterollingAppender (레이아웃 레이아웃, 문자열 파일 이름) IoException {Super (레이아웃, 파일 이름, true); activateOptions (); } / ** * @return intervaltime * / public int getInterValtime () {return IntervalTime; } / ** * @param intervaltime * set * / public void setInterValtime (int intervaltime) {this.intervaltime = intervaltime; } @Override public void activeAteOptions () {super.ActivateOptions (); if (filename! = null) {now.settime (System.CurrentTimeMillis ()); sdf = 새로운 simpledateformat (datepattern); 파일 = 새 파일 (filename); scheduledfilename = filename + sdf.format (새 날짜 (file.lastModified ())); } else {loglog .Error ( "파일 또는 날짜 패턴 옵션은 Appender [" + name + "]에 대해 설정되지 않습니다."); }} /*** 현재 파일을 새 파일로 롤오버합니다. */ void rollover ()는 ioException {string datedfilename = filename + sdf.format (지금); // 우리는 여전히 현재 간격의 // 경계 내에 있기 때문에 롤오버하기에는 너무 이르다. // 다음 간격에 도달하면 롤오버가 발생합니다. if (scheduledfilename.equals (datedfilename)) {return; } // 현재 파일을 닫고 DatedFilename this.closeFile ()로 이름을 바꿉니다. 파일 target = 새 파일 (ScheduledFilename); if (target.exists ()) {target.delete (); } 파일 파일 = 새 파일 (filename); 부울 결과 = file.renameto (대상); if (result) {loglog.debug (filename + " ->" + scheduledfilename); } else {loglog.error ( "[" + filename + "]를 [" + scheduledFilename + "로 바꾸지 못했습니다."); } try {// 파일도 닫습니다. 여러 // 닫기 작업이 안전하므로 괜찮습니다. this.setfile (filename, true, this.bufferedio, this.buffersize); } catch (ioException e) {ErrorHandler.error ( "setFile (" + filename + ", true) 호출 실패"); } scheduledFilename = DatedFilename; } /***이 메소드는 MinuterollingAppender를 슈퍼 클래스와 차별화합니다. * * <p> * 실제로 로깅하기 전에이 방법은 롤오버를 수행 할 시간인지 확인합니다. 그렇다면 다음 롤오버 시간과 * 롤오버를 예약합니다. */ @override Protected Void SubAppend (LoggingEvent 이벤트) {long n = system.currenttimeMillis (); if (n> = nextCheck) {now.settime (n); NextCheck = rc.getNextCheckMillis (지금); {rollover (); } catch (ioException ioe) {if (ioe instanceof interpruptedioexception) {thread.currentthread (). 인터럽트 (); } loglog.error ( "rollover () 실패.", ioe); }} super.subAppend (이벤트); } /*** RollingCalendar는 MinuterollingAppender의 도우미 클래스입니다. 주기성 유형과 현재 시간이 주어지면 다음 * 간격의 시작을 계산합니다. * */ 클래스 RollingCalendar 확장 GregorianCalendar {private static final long serialversionuid = -3560331770601814177L; RollingCalendar () {super (); } public long getNextCheckMillis (지금 날짜) {return getNextCheckDate (지금) .gettime (); } 공개 날짜 getNextCheckDate (지금 날짜) {this.settime (지금); this.set (calendar.second, 0); this.set (calendar.millisecond, 0); this.add (calendar.minute, intervaltime); return gettime (); }}}테스트 구성 파일은 다음과 같습니다.
<? xml version = "1.0"alcoding = "utf-8"?> <! doctype log4j : 구성 시스템 "log4j.dtd"> <log4j : configuration xmlns : log4j = "http://jakarta.apache.org/log4j/"> <parmile "> <myfile"> value = "log4jtest.log"/> <param name = "append"value = "true"/> <param name = "intervaltime"value = "2"/> <paran name = "converionpattern"value = "%p%d (%c :%l)-%m%n"/> </layout> </accelender> <Priority value = "Applender-re debug"/>/>/>/>/>. ref = "myfile"/> </root> </log4j : configuration>
타이밍 구현과 관련하여 Java가 제공하는 타이머 구현을 사용할 수도있어 로그를 녹음 할 때마다 시간의 계산 및 비교를 제거합니다. 차이점은 실제로 스레드를 설정하고 롤오버 방법을 호출하는 것입니다. 구현은 다음과 같습니다.
패키지 net.csdn.blog; import java.io.file; import java.io.ioexception; import java.text.simpledateformat; import java.util.date; java.util.timer 가져 오기; java.util.timertask 가져 오기; import org.apache.log4j.fileappender; import org.apache.log4j.layout; import org.apache.log4j.helpers.loglog; 공개 클래스 TimertAskRollingAppender는 fileappender { /*** 날짜 패턴을 확장합니다. 기본적으로 패턴은 매일 롤오버를 의미하는 " '.'yyyy-mm-dd" *로 설정됩니다. */ 개인 정적 최종 문자열 datePattern = " '.'yyyy-mm-dd-hh-mm'.log'"; / *** 간격 시간, 단위 : ming*/ private int intervaltime = 10; simpledateformat sdf = new simpledateformat (datepattern); /*** 기본 생성자는 아무것도하지 않습니다. */public timertaskrollingAppender () {}/** * a <code> TimertAskRollingAppender </code>를 인스턴스화하고 <code> filename </code>에 의해 설계된 파일 *을 엽니 다. 열린 Filename 은이 Appender의 * OUPUT 목적지가됩니다. */ public TimertASkRollingAppender (레이아웃 레이아웃, 문자열 파일 이름) IOException {Super (레이아웃, 파일 이름, True); activateOptions (); } / ** * @return intervaltime * / public int getInterValtime () {return IntervalTime; } / ** * @param intervaltime * set * / public void setInterValtime (int intervaltime) {this.intervaltime = intervaltime; } @Override public void activeAteOptions () {super.ActivateOptions (); 타이머 타이머 = 새로운 타이머 (); TIMER.SCEDULE (new logtimertask (), 1000, IntervalTime * 60000); } class logtimertask 확장 TimertASK {@Override public void run () {String datedfilename = filename + sdf.format (new date ()); CloseFile (); 파일 target = 새 파일 (DatedFilename); if (target.exists ()) target.delete (); 파일 = 새 파일 (filename); 부울 결과 = file.renameto (대상); if (result) loglog.debug (filename + " ->" + datedfilename); else loglog.error ( "" " + filename +"] 이름을 [ " + datedfilename +"]로 바꾸지 못했습니다. "); try {setfile (filename, true, bufferedio, buffersize); } catch (ioException e) {ErrorHandler.error ( "setFile (" + filename + ", true) 호출 실패"); }}}}}그러나 위 구현에는 두 가지 문제가 있습니다.
1) 동시성
run ()에서 closeFile ()을 호출 한 후 동시 문제가 발생할 수있는 장소는 subAppend () 메소드가 로그를 작성하는 것만 발생합니다. 이 순간 파일이 닫히고 다음 오류 가보고됩니다.
java.io.ioexception : sun.nio.cs.streamencoder.write에서 sun.nio.cs.streamencoder.write에서 sun.nio.cs.streamencoder.write에서 sun.nio.cs.streamencoder.write (unknown source)에서 java.write (unkendreststreamwrite)에서 sun.nio.cs.streamencoder.write에서 닫힌 스트림. ..................솔루션은 비교적 간단합니다. 전체 run () 메서드를 동기화하고 동기화 된 키워드를 추가하십시오. 그러나 저자는 로그가 실제로 쓰기를 원하고 글쓰기 속도가 충분히 빠르면 로그가 손실 될 수있는 상황을 해결하지 못했습니다.
타이머를 사용하여 구현하는 것이 더 간단하지만 타이머의 작업이 너무 오래 실행되면 타이머 개체를 독점적으로 점유하여 후속 작업을 언제든지 실행할 수 없습니다. 솔루션도 더 간단합니다. 스레드 풀 버전 타이머 클래스 ScheduleDexecutorService는 다음을 구현하는 데 사용됩니다.
/ ** * */ package net.csdn.blog; import java.io.file; import java.io.ioexception; import java.text.simpledateformat; import java.util.date; java.util.concurrent.executors import; java.util.concurrent.timeUnit import; import org.apache.log4j.fileappender; import org.apache.log4j.layout; import org.apache.log4j.helpers.loglog; /** * @Author Coder_Xia * <p> * ScheduleDexecutorService를 사용하여 시간이 지정된 구성 인쇄 로그를 구현하십시오. 기본적으로 패턴은 매일 롤오버를 의미하는 " '.'yyyy-mm-dd" *로 설정됩니다. */ 개인 정적 최종 문자열 datePattern = " '.'yyyy-mm-dd-hh-mm'.log'"; / *** 간격 시간, 단위 : ming*/ private int intervaltime = 10; simpledateformat sdf = new simpledateformat (datepattern); /*** 기본 생성자는 아무것도하지 않습니다. */public scheduledexecutorserviceappender () {}/** * a <code> scheduledexecutorserviceappender </code>를 인스턴스화하고 <code> filename </code>로 설계된 * 파일을 엽니 다. 열린 Filename 은이 애플 렌더의 OUPUT 목적지가됩니다. */ public scheduledexecutorserviceappender (레이아웃 레이아웃, 문자열 filename)는 ioexception {super (레이아웃, 파일 이름, true); activateOptions (); } / ** * @return intervaltime * / public int getInterValtime () {return IntervalTime; } / ** * @param intervaltime * set * / public void setInterValtime (int intervaltime) {this.intervaltime = intervaltime; } @Override public void activeAteOptions () {super.ActivateOptions (); executors.newsingLethreadScheduledExecutor (). ScheduLeatFixEdrate (new logtimertask (), 1, IntervalTime * 60000, TimeUnit.milliseconds); } class logtimertask는 runnable {@override public void run () {String datedfilename = filename + sdf.format (new date ()); CloseFile (); 파일 target = 새 파일 (DatedFilename); if (target.exists ()) target.delete (); 파일 = 새 파일 (filename); 부울 결과 = file.renameto (대상); if (result) loglog.debug (filename + " ->" + datedfilename); else loglog.error ( "" " + filename +"] 이름을 [ " + datedfilename +"]로 바꾸지 못했습니다. "); try {setfile (filename, true, bufferedio, buffersize); } catch (ioException e) {ErrorHandler.error ( "setFile (" + filename + ", true) 호출 실패"); }}}}}타이밍 구현과 관련하여 이것은 거의 끝입니다. 기본값은 10 분 안에 새 로그 파일을 생성하는 것입니다. 구성 할 때 직접 설정할 수 있습니다. 그러나 숨겨진 위험이 있습니다. 구성 담당자가 시간 간격이 몇 분인 것을 알지 못하는 경우, 초가 몇 초라고 생각하면 600이 된 다음 G로 로그 파일을 생성하기 위해 디버그를 열면 재앙이됩니다. 다음 변환은 RollingFileAppender의 최대 크기와 일치하는 최대 백업 파일 수를 결합한 다음 다시 개선하는 것입니다. 다음에 우리는 변환 과정을 계속 설명 할 것입니다.
모듈 이름 구성을 추가하십시오
Log4J 시간 인쇄의 사용자 정의 클래스 구현에 대해 언급 했으므로 지정된 크기와 백업 파일 수에 대해서는 이야기하지 않습니다. RollingFileAppender 클래스 복사 코드에서 이전 사용자 정의 클래스에 추가 할 수 있습니다. 해결해야 할 유일한 것은 동시성 문제, 즉 로그 이벤트가 발생하면 출력 스트림의 오류가보고됩니다.
이제 그러한 응용 프로그램 시나리오가 있으며 종종 다음과 같습니다.
1. 프로젝트에는 여러 가지 다른 프로젝트가 포함되어 있습니다.
2. 같은 프로젝트에는 다른 모듈이 포함되어 있습니다.
첫 번째 경우 Log4J <CatoGery = "Test">를 구성한 다음 Logger를 생성 할 때 다음 메소드를 사용할 수 있습니다.
로거 로거 = logger.getLogger ( "테스트");
두 번째 경우, 다른 모듈을 동일한 로그 파일에 인쇄하기를 희망하지만 로그에서 모듈 이름을 인쇄하여 문제가있을 때 문제를 찾을 수 있기를 바랍니다. 따라서이 기사에서는 구성 모듈레 이름을 Appender 클래스에 추가해야합니다. 아래의 변환을 시작합시다. 시간이 정한 인쇄와 달리 RollingFileAppender 클래스를 변환의 기본 클래스로 사용합니다.
먼저 구성 항목 모듈 라메 이름을 추가하고 Get and Set 메소드를 추가하십시오.
RollingFileAppender에서 상속되므로 subAppend ()에서 loggingEvent의 데이터를 형식화하면 FormatInfo 메소드를 추가하여 데이터를 형식화하면 코드가 생략됩니다.
최종 제품 범주는 다음과 같습니다.
패키지 net.csdn.blog; import org.apache.log4j.category; import org.apache.log4j.rollingfileAppender; import org.apache.log4j.spi.loggingevent; / ** * @author coder_xia * */ public class moduleAppender 확장 RollingFileAppender {private String modulename; / ** * @return the modulename */ public string getModulEname () {return modulename; } / ** * @param modulename * 설정 * / public void setModulename (String modulename) {this.modulename = modulename; } / ** * 형식 인쇄 콘텐츠 * * @param 이벤트 * 이벤트 * @return msg * / private String formatinfo (loggingevent 이벤트) {StringBuilder sb = new StringBuilder (); if (modulename! = null) {sb.append (modulename) .append ( "|"); sb.append (event.getMessage ()); } return sb.toString (); } @override public void subAppend (loggingevent event) {string msg = formatinfo (이벤트); super.subAppend (new loggingevent (category.class.getName (), event .getLogger (), event.getLevel (), msg, null)); }}