กำหนดเวลาช่วงเวลาและบันทึกการพิมพ์เป็นประจำ
หลังจากได้รับคำขอบันทึกจะถูกพิมพ์เป็นประจำผ่าน log4j คำอธิบายข้อกำหนดมีดังนี้บันทึกจะต้องสามารถพิมพ์เป็นประจำและช่วงเวลาสามารถจับคู่ได้ การพูดถึงเวลาก่อนอื่นฉันคิดถึงชั้นเรียน DailyrollingFilePender การกำหนดเวลาต่างๆ ตาม DatePattern คุณสามารถอ้างถึงคลาส SimpledateFormat การตั้งค่าเวลาทั่วไปบางอย่างมีดังนี้:
จากการสังเกตฉันพบว่าไม่มีรูปแบบวันที่คล้ายกับ N นาทีดังนั้นฉันจึงเขียนคลาสที่กำหนดเองตามคลาส DailyrollingFileAppender กระบวนการมีดังนี้:
1) คัดลอกซอร์สโค้ดของคลาส DailyrollingFileAppender และเปลี่ยนชื่อ IT 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, Intervalime); return getTime (); -3) เมื่อโหมดเวลาถูกจับคู่ตามนาทีโหมดเวลาจะต้องปิดใช้งาน เปลี่ยนเป็นแบบคงที่สุดท้ายและลบพารามิเตอร์ DatePattern ในวิธีการรับ, Set Method และตัวสร้าง minuterollingappender ในการตอบสนอง
สตริงคงที่ส่วนตัว datepattern = "'.'yyyy-mm-dd-hh-mm'.log'";
ในทำนองเดียวกัน computecheckperiod () ซึ่งให้บริการหลายวันที่สามารถลบได้; การเปลี่ยนแปลงเสร็จสมบูรณ์และหมวดหมู่ผลิตภัณฑ์สำเร็จรูปมีดังนี้:
Package net.csdn.blog; นำเข้า Java.io.File; นำเข้า java.io.ioException; นำเข้า java.io.InterruptedioException; นำเข้า java.text.simpledateFormat; นำเข้า Java.util.Calendar; นำเข้า java.util.date; นำเข้า Java.util.Gregoriancalendar; นำเข้า org.apache.log4j.fileAppender; นำเข้า org.apache.log4j.layout; นำเข้า org.apache.log4j.helpers.loglog; นำเข้า org.apache.log4j.spi.loggingEvent; /** * จับเวลาโดย Minute Appender * * @author coder_xia * * /คลาสสาธารณะ minuterollingappender ขยาย filePender { /** * รูปแบบวันที่ โดยค่าเริ่มต้นรูปแบบจะถูกตั้งค่าเป็น "'.'yyyy-mm-dd" * หมายถึงโรลโอเวอร์รายวัน */ สตริงคงที่ส่วนตัว datepattern = "'.'yyyy-mm-dd-hh-mm'.log'"; / *** เวลาช่วงเวลาหน่วย: นาที*/ ช่วงเวลา int ส่วนตัว = 10; /** * ไฟล์บันทึกจะถูกเปลี่ยนชื่อเป็นค่าของตัวแปร scheduledfilename * เมื่อป้อนช่วงเวลาถัดไป ตัวอย่างเช่นหากระยะเวลาโรลโอเวอร์ * เป็นเวลาหนึ่งชั่วโมงไฟล์บันทึกจะถูกเปลี่ยนชื่อเป็นค่า * "scheduledfilename" ในช่วงต้นชั่วโมงถัดไป * * เวลาที่แม่นยำเมื่อมีการโรลโอเวอร์เกิดขึ้นขึ้นอยู่กับกิจกรรมการบันทึก */ สตริงส่วนตัว scheduledfilename; /*** ในครั้งต่อไปที่เราประเมินการโรลโอเวอร์จะเกิดขึ้น */ Long Private Long NextCheck = System.currentTimeMillis () - 1; วันที่ตอนนี้ = วันที่ใหม่ (); SimpledateFormat SDF; RollingCalendar RC = ใหม่ RollingCalendar (); /*** ตัวสร้างเริ่มต้นไม่ทำอะไรเลย */Public Minuterollingappender () {}/** * อินสแตนซ์ <code> minuterollingappender </code> และเปิดไฟล์ * ออกแบบโดย <code> ชื่อไฟล์ </code> ชื่อไฟล์ที่เปิดจะกลายเป็นปลายทาง * ouput สำหรับ appender นี้ */ public minuterollingappender (เค้าโครงเค้าโครงชื่อสตริง) พ่น IOException {super (เลย์เอาต์, ชื่อไฟล์, จริง); ActivateOptions (); } / ** * @return ช่วงเวลา * / public int getIntervaltime () {return IntervalTime; } / ** * @param Intervaltime * ช่วงเวลาที่จะตั้งค่า * / โมฆะสาธารณะ setIntervalTime (intenttime int) {this.intervaltime = IntervalTime; } @Override โมฆะสาธารณะ activateOptions () {super.activateOptions (); if (filename! = null) {now.settime (System.currentTimeMillis ()); SDF = ใหม่ simpledateFormat (datepattern); ไฟล์ไฟล์ = ไฟล์ใหม่ (ชื่อไฟล์); scheduledFileName = filename + sdf.format (วันที่ใหม่ (file.lastmodified ())); } else {loglog. error ("ตัวเลือกไฟล์หรือ datepattern ไม่ได้ตั้งไว้สำหรับ appender [" + name + "]."); }} /*** โรลโอเวอร์ไฟล์ปัจจุบันไปยังไฟล์ใหม่ */ void rollover () พ่น IOException {String datedFilename = filename + sdf.format (ตอนนี้); // มันเร็วเกินไปที่จะม้วนเพราะเรายังคงอยู่ในขอบเขต // ของช่วงเวลาปัจจุบัน โรลโอเวอร์จะเกิดขึ้นเมื่อถึงช่วงเวลาถัดไปถึง // if (scheduledfilename.equals (datedfilename)) {return; } // ปิดไฟล์ปัจจุบันและเปลี่ยนชื่อเป็น datedfilename this.closefile (); file target = ไฟล์ใหม่ (scheduledfilename); if (target.exists ()) {target.delete (); } ไฟล์ไฟล์ = ไฟล์ใหม่ (ชื่อไฟล์); ผลลัพธ์บูลีน = file.renameto (เป้าหมาย); if (result) {loglog.debug (ชื่อไฟล์ + " ->" + scheduledfilename); } else {loglog.error ("ล้มเหลวในการเปลี่ยนชื่อ [" + filename + "] ถึง [" + scheduledfilename + "]"); } ลอง {// สิ่งนี้จะปิดไฟล์ด้วย นี่ก็โอเคเนื่องจากการดำเนินงานหลายครั้ง // ปิดนั้นปลอดภัย this.setFile (ชื่อไฟล์, true, this.bufferedio, this.buffersize); } catch (ioexception e) {errorhandler.error ("setfile (" + filename + ", true) การโทรล้มเหลว"); } scheduledFileName = datedFileName; } /*** วิธีนี้สร้างความแตกต่าง minuterollingappender จากคลาส Super * * <p> * ก่อนที่จะเข้าสู่ระบบวิธีนี้จะตรวจสอบว่าถึงเวลาต้องทำ * โรลโอเวอร์หรือไม่ ถ้าเป็นเช่นนั้นมันจะกำหนดเวลาโรลโอเวอร์ครั้งต่อไปแล้ว * โรลโอเวอร์ * */ @Override Void Void Subappend (Event LoggingEvent) {long n = system.currentTimeMillis (); if (n> = nextCheck) {now.Settime (n); nextCheck = rc.getNextCheckMillis (ตอนนี้); ลอง {Rollover (); } catch (ioexception ioe) {if (ioe instanceof interruptedioException) {thread.currentthread (). interrupt (); } loglog.error ("โรลโอเวอร์ () ล้มเหลว", ioe); }} super.subappend (เหตุการณ์); } /*** RollingCalendar เป็นคลาส Helper to Minuterollingappender ด้วยประเภทของระยะเวลา * และเวลาปัจจุบันมันจะคำนวณจุดเริ่มต้นของช่วงเวลา * ถัดไป * */ คลาส RollingCalendar ขยาย GregorianCalendar {ส่วนตัวคงที่สุดท้าย Long SerialVersionuid = -3560331770601814177L; RollingCalendar () {super (); } สาธารณะยาว getNextCheckMillis (วันที่ตอนนี้) {ส่งคืน getNextCheckDate (ตอนนี้) .getTime (); } วันที่สาธารณะ getNextCheckDate (วันที่ตอนนี้) {this.settime (ตอนนี้); this.set (Calendar.second, 0); this.set (calendar.millisecond, 0); this.add (Calendar.minute, Intervalime); return getTime (); -ไฟล์การกำหนดค่าการทดสอบมีดังนี้:
<? xml version = "1.0" การเข้ารหัส = "utf-8"?> <! doctype log4j: ระบบการกำหนดค่า "log4j.dtd"> <log4j: การกำหนดค่า xmlns: log4j = "http://jakarta.apache.org/log4j/" value = "log4jtest.log"/> <param name = "ผนวก" value = "true"/> <param name = "IntervalTime" value = "2"/> <yleout> <param name = "conversionPattern" value = "%p%d (%c:%l) ref = "myfile"/> </root> </og4j: การกำหนดค่า>
เกี่ยวกับการใช้งานเวลาคุณยังสามารถใช้การใช้งานตัวจับเวลาที่จัดทำโดย Java ซึ่งจะช่วยลดการคำนวณและการเปรียบเทียบเวลาทุกครั้งที่คุณบันทึกบันทึก ความแตกต่างคือการตั้งค่าเธรดและเรียกวิธีการโรลโอเวอร์ การดำเนินการมีดังนี้:
Package net.csdn.blog; นำเข้า Java.io.File; นำเข้า java.io.ioException; นำเข้า java.text.simpledateFormat; นำเข้า java.util.date; นำเข้า java.util.timer; นำเข้า java.util.timertask; นำเข้า org.apache.log4j.fileAppender; นำเข้า org.apache.log4j.layout; นำเข้า org.apache.log4j.helpers.loglog; TimertaskRollingappender ระดับสาธารณะขยาย FilePender { /*** รูปแบบวันที่ โดยค่าเริ่มต้นรูปแบบจะถูกตั้งค่าเป็น "'.'yyyy-mm-dd" * หมายถึงโรลโอเวอร์รายวัน */ สตริงสุดท้ายคงที่ส่วนตัว datepattern = "'.'yyyy-mm-dd-hh-mm'.log'"; / *** เวลาช่วงเวลาหน่วย: นาที*/ ช่วงเวลา int ส่วนตัว = 10; SimpledateFormat SDF = ใหม่ SimpleDateFormat (DatePattern); /*** ตัวสร้างเริ่มต้นไม่ทำอะไรเลย */Public TimerTaskRollingAppender () {}/** * อินสแตนซ์ <code> TimerTaskRollingAppender </code> และเปิดไฟล์ * ออกแบบโดย <code> ชื่อไฟล์ </code> ชื่อไฟล์ที่เปิดจะกลายเป็นปลายทาง * ouput สำหรับ appender นี้ */ สาธารณะ timertaskrollingappender (เลย์เอาต์เลย์เอาต์, ชื่อไฟล์สตริง) พ่น IOException {super (เลย์เอาต์, ชื่อไฟล์, จริง); ActivateOptions (); } / ** * @return ช่วงเวลา * / public int getIntervaltime () {return IntervalTime; } / ** * @param Intervaltime * ช่วงเวลาที่จะตั้งค่า * / โมฆะสาธารณะ setIntervalTime (intenttime int) {this.intervaltime = IntervalTime; } @Override โมฆะสาธารณะ activateOptions () {super.activateOptions (); ตัวจับเวลาตัวจับเวลา = ตัวจับเวลาใหม่ (); timer.schedule (ใหม่ logtimertask (), 1,000, ช่วงเวลา * 60000); } คลาส logtimertask ขยาย timertask {@Override โมฆะสาธารณะเรียกใช้ () {สตริง datedFileName = filename + sdf.format (วันที่ใหม่ ()); ปิดไฟล์ (); file target = ไฟล์ใหม่ (datedfilename); if (target.exists ()) target.delete (); ไฟล์ไฟล์ = ไฟล์ใหม่ (ชื่อไฟล์); ผลลัพธ์บูลีน = file.renameto (เป้าหมาย); if (ผลลัพธ์) loglog.debug (ชื่อไฟล์ + " ->" + datedfilename); else loglog.error ("ไม่สามารถเปลี่ยนชื่อ [" + filename + "] ถึง [" + datedfilename + "]"); ลอง {setFile (ชื่อไฟล์, จริง, บัฟเฟอร์, บัฟเฟอร์); } catch (ioexception e) {errorhandler.error ("setfile (" + filename + ", true) การโทรล้มเหลว"); -อย่างไรก็ตามมีสองปัญหาเกี่ยวกับการใช้งานข้างต้น:
1) พร้อมกัน
สถานที่ที่ปัญหาพร้อมกันอาจเกิดขึ้นหลังจากเรียกปิดไฟล์ () ในการเรียกใช้ () วิธีการย่อย () เพิ่งเกิดขึ้นเพื่อเขียนบันทึก ในขณะนี้ไฟล์ถูกปิดและจะมีการรายงานข้อผิดพลาดต่อไปนี้:
java.io.ioexception: สตรีมปิดที่ sun.nio.cs.streamencoder.ensureopen (แหล่งที่ไม่รู้จัก) ที่ sun.nio.cs.streamencoder.write (แหล่งที่ไม่รู้จัก) ที่ sun.nio.cs.streamencoder.write (ไม่รู้จักแหล่งที่มา -การแก้ปัญหาค่อนข้างง่าย เพียงทำวิธีการเรียกใช้ทั้งหมด () ซิงโครไนซ์และเพิ่มคำหลักที่ซิงโครไนซ์ อย่างไรก็ตามผู้เขียนยังไม่ได้แก้ไขสถานการณ์ที่บันทึกอาจหายไปหากเขาต้องการเขียนและความเร็วในการเขียนนั้นเร็วพอ
การใช้ตัวจับเวลาเพื่อใช้งานนั้นง่ายกว่า แต่ถ้างานในตัวจับเวลาถูกดำเนินการนานเกินไปพวกเขาจะครอบครองวัตถุจับเวลาโดยเฉพาะทำให้งานที่ตามมาไม่สามารถดำเนินการได้ตลอดเวลา การแก้ปัญหานั้นง่ายกว่า THREAL POOL VERSION TIMER CLASS SCHENULEDEXECUTORSERVICE ใช้ในการใช้งานต่อไปนี้:
/ ** * */ package net.csdn.blog; นำเข้า Java.io.File; นำเข้า java.io.ioException; นำเข้า java.text.simpledateFormat; นำเข้า java.util.date; นำเข้า java.util.concurrent.executors; นำเข้า java.util.concurrent.timeUnit; นำเข้า org.apache.log4j.fileAppender; นำเข้า org.apache.log4j.layout; นำเข้า org.apache.log4j.helpers.loglog; /** * @author coder_xia * <p> * ใช้ ScheduleDexecutorService เพื่อใช้บันทึกการพิมพ์กำหนดค่าที่กำหนดเวลา * <p> * * /คลาสสาธารณะ ScheduleDexecutorServiceAppender ขยาย FilePender { /** * รูปแบบวันที่ โดยค่าเริ่มต้นรูปแบบจะถูกตั้งค่าเป็น "'.'yyyy-mm-dd" * หมายถึงโรลโอเวอร์รายวัน */ สตริงสุดท้ายคงที่ส่วนตัว datepattern = "'.'yyyy-mm-dd-hh-mm'.log'"; / *** เวลาช่วงเวลาหน่วย: นาที*/ ช่วงเวลา int ส่วนตัว = 10; SimpledateFormat SDF = ใหม่ SimpleDateFormat (DatePattern); /*** ตัวสร้างเริ่มต้นไม่ทำอะไรเลย */Public ScheduleDexecutorSerViceAppender () {}/** * อินสแตนซ์ <code> ScheduleDexecutorSerViceAppender </code> และเปิดไฟล์ * ที่ออกแบบโดย <code> ชื่อไฟล์ </code> ชื่อไฟล์ที่เปิดจะกลายเป็น * ปลายทาง ouput สำหรับ appender นี้ */ สาธารณะ ScheduleDexecutorServiceAppender (เค้าโครงเค้าโครงชื่อสตริง) พ่น IOException {super (เลย์เอาต์ชื่อไฟล์จริง); ActivateOptions (); } / ** * @return ช่วงเวลา * / public int getIntervaltime () {return IntervalTime; } / ** * @param Intervaltime * ช่วงเวลาที่จะตั้งค่า * / โมฆะสาธารณะ setIntervalTime (intenttime int) {this.intervaltime = IntervalTime; } @Override โมฆะสาธารณะ activateOptions () {super.activateOptions (); Executors.newsingLethreadscheduledExecutor (). ScheduleatFixedrate (ใหม่ LogTimerTask (), 1, IntervalTime * 60000, TimeUnit.Milliseconds); } คลาส LogTimerTask ใช้งาน runnable {@Override public void run () {String datedFileName = filename + sdf.format (วันที่ใหม่ ()); ปิดไฟล์ (); file target = ไฟล์ใหม่ (datedfilename); if (target.exists ()) target.delete (); ไฟล์ไฟล์ = ไฟล์ใหม่ (ชื่อไฟล์); ผลลัพธ์บูลีน = file.renameto (เป้าหมาย); if (ผลลัพธ์) loglog.debug (ชื่อไฟล์ + " ->" + datedfilename); else loglog.error ("ไม่สามารถเปลี่ยนชื่อ [" + filename + "] ถึง [" + datedfilename + "]"); ลอง {setFile (ชื่อไฟล์, จริง, บัฟเฟอร์, บัฟเฟอร์); } catch (ioexception e) {errorhandler.error ("setfile (" + filename + ", true) การโทรล้มเหลว"); -เกี่ยวกับการดำเนินการตามกำหนดเวลานี่เกือบจะสิ้นสุดแล้ว ค่าเริ่มต้นคือการสร้างไฟล์บันทึกใหม่ใน 10 นาที คุณสามารถตั้งค่าด้วยตัวเองเมื่อกำหนดค่า อย่างไรก็ตามมีอันตรายที่ซ่อนอยู่ หากบุคคลที่กำหนดค่าไม่ทราบว่าช่วงเวลาเป็นนาทีหากคุณคิดว่าเป็นวินาทีคุณมี 600 แล้วเปิดการดีบักเพื่อสร้างไฟล์บันทึกด้วย G มันจะเป็นหายนะแน่นอน การแปลงต่อไปนี้คือการรวมขนาดสูงสุดของ RollingFileAppender และจำนวนไฟล์สำรองสูงสุดให้เข้ากันแล้วปรับปรุงอีกครั้ง ครั้งต่อไปเราจะอธิบายกระบวนการเปลี่ยนแปลงต่อไป
เพิ่มการกำหนดค่าชื่อโมดูล
ฉันพูดถึงการใช้งานคลาสที่กำหนดเองของการพิมพ์ตามกำหนดเวลา log4j ดังนั้นฉันจะไม่พูดถึงขนาดที่ระบุและจำนวนไฟล์สำรอง ฉันสามารถเพิ่มได้จากรหัสคัดลอกคลาส RollingFilePender ไปยังคลาสที่กำหนดเองก่อนหน้า สิ่งเดียวที่ต้องแก้ไขคือปัญหาพร้อมกันนั่นคือเมื่อไฟล์ Rename ถูกปิดจะมีการรายงานข้อผิดพลาดของการปิดเอาต์พุตสตรีมจะถูกรายงานเมื่อมีเหตุการณ์บันทึกเกิดขึ้น
ขณะนี้มีสถานการณ์แอปพลิเคชันและมีบ่อยครั้ง:
1. โครงการมีหลายโครงการที่แตกต่างกัน
2. โครงการเดียวกันมีโมดูลที่แตกต่างกัน
สำหรับกรณีแรกคุณสามารถกำหนดค่า log4j <catogery = "test"> จากนั้นใช้วิธีการต่อไปนี้เมื่อสร้าง logger:
logger logger = logger.getLogger ("ทดสอบ");ในกรณีที่สองเราหวังว่าจะพิมพ์โมดูลที่แตกต่างกันลงในไฟล์บันทึกเดียวกัน แต่เราหวังว่าจะพิมพ์ชื่อโมดูลในบันทึกเพื่อค้นหาปัญหาเมื่อมีปัญหา ดังนั้นบทความนี้จำเป็นต้องเพิ่ม modulename การกำหนดค่าลงในคลาส Appender มาเริ่มการเปลี่ยนแปลงด้านล่าง ซึ่งแตกต่างจากการพิมพ์ที่หมดเวลาเราใช้คลาส RollingFilePender เป็นคลาสพื้นฐานสำหรับการแปลง
ก่อนอื่นให้เพิ่ม modulename รายการการกำหนดค่าและเพิ่มวิธีการรับและตั้งค่า;
เนื่องจากได้รับการสืบทอดจาก RollingFileAppender คุณจะต้องจัดรูปแบบข้อมูลใน LoggingEvent ใน Subappend () เพิ่มวิธีการจัดรูปแบบเพื่อจัดรูปแบบข้อมูลและรหัสถูกละเว้น
หมวดหมู่ผลิตภัณฑ์สุดท้ายมีดังนี้:
Package net.csdn.blog; นำเข้า org.apache.log4j.category; นำเข้า org.apache.log4j.rollingFilePender; นำเข้า org.apache.log4j.spi.loggingEvent; / ** * @author coder_xia * */ คลาสสาธารณะ ModuleAppender ขยาย RollingFileAppender {modulename สตริงส่วนตัว; / ** * @return modulename */ สตริงสาธารณะ getModulename () {return modulename; } / ** * @param modulename * modulename to set * / public void setModulename (String modulename) {this.modulename = modulename; } / ** * รูปแบบการพิมพ์เนื้อหา * * @param เหตุการณ์ * เหตุการณ์ * @return msg * / สตริงส่วนตัว formatInfo (เหตุการณ์ loggingEvent) {StringBuilder sb = new StringBuilder (); if (modulename! = null) {sb.append (modulename) .append ("|"); sb.append (event.getMessage ()); } return sb.toString (); } @Override โมฆะสาธารณะ subappend (เหตุการณ์ loggingEvent) {String msg = formatInfo (เหตุการณ์); super.subappend (ใหม่ LoggingEvent (category.class.getName (), เหตุการณ์. getLogger (), event.getLevel (), ผงชูรส, null)); -