定期的に間隔時間と印刷ログを構成します
リクエストを受け取った後、ログはlog4jを介して定期的に印刷されます。要件の説明は次のとおりです。ログを定期的に印刷できる必要があり、時間間隔を一致させることができます。タイミングといえば、まず第一に、私はDailyrollingfileappenderクラスについて考えます。さまざまなタイミング。 DatePatternによると、SimpleDateFormatクラスを参照できます。いくつかの一般的なタイミング設定は次のとおりです。
観察を通して、私はn分と同様の日付形式がないことがわかったので、dailylollingfileappenderクラスに基づいてカスタムクラスを書きました。プロセスは次のとおりです。
1)dailyrollingfileappenderクラスのソースコードをコピーし、minuterollingappenderの名前を変更します。 log4j.xmlで構成するために、構成アイテム間隔時間を追加してセットを追加してメソッドを取得します。
private int intervaltime = 10;
2)DailyrollingFileAppenderクラスはRollingCalendarクラスを使用して次の間隔時間を計算し、パラメーター間隔時間を渡す必要があるため、RollingCalendarクラスは内部クラスとして変更されます。その方法は、DatePatternに基づいて次のロールオーバーアクションの時間を計算するため、現時点では他の時間モードは必要ありません。変更方法は次のとおりです。
public date getNextCheckDate(日付今){this.settime(now); this.set(calendar.second、0); this.set(calendar.millisecond、0); this.add(calendar.minute、intervaltime); return getTime(); }3)時間に応じてタイムモードが一致する場合、タイムモードを無効にする必要があります。静的ファイナルに変更し、get、setメソッド、および応答でminuterollingappenderコンストラクターのdatepatternパラメーターを削除します。
private static string datepattern = "'.'yyyy-mm-dd-hhmm'.log'";
同様に、複数のDatePatternsを提供するComputeCheckPerioD()も削除できます。変換が完了し、完成品カテゴリは次のとおりです。
パッケージnet.csdn.blog; java.io.fileをインポートします。 java.io.ioexceptionをインポートします。 java.io.interruptedioexceptionをインポートします。 java.text.simpledateformatをインポートします。 java.util.calendarをインポートします。 Import Java.util.date; java.util.gregoriancalendarをインポートします。 org.apache.log4j.fileappenderをインポートします。 Import org.apache.log4j.layout; org.apache.log4j.helpers.loglogをインポートします。 org.apache.log4j.spi.loggingeventをインポートします。 /** * Minute Appenderによるタイマー可能 * * @author coder_xia * * /public class minuterollingappender extends fileappender { /** *日付パターン。デフォルトでは、パターンは「 '.'yyyy-mm-dd」 *に設定されています。 */ private static string datepattern = "'.'yyyy-mm-dd-hhmm'.log'"; / ***間隔時間、ユニット:分*/ private int intervaltime = 10; /** *ログファイルは、次の間隔が入力されたときにScheduleDFileName *変数の値に変更されます。たとえば、ロールオーバー *期間が1時間の場合、ログファイルは次の1時間の開始時に * "ScheduledFileName"の値に変更されます。 * *ロギングアクティビティに応じてロールオーバーが発生する正確な時間。 */プライベート文字列ScheduleDfileName; /***次回ロールオーバーを推定するときに発生するはずです。 */ private long nextcheck = system.currenttimemillis()-1;日付= new date(); SimpleDateFormat SDF; RollingCalendar rc = new RollingCalendar(); /***デフォルトのコンストラクターは何もしません。 */public minuterollingAppender(){}/** * <code> minuterollingAppender </code>をインスタンス化し、<code> filename </code>によって設計されたファイルを開きます。オープンしたファイル名は、このAppenderの * Ouput宛先になります。 */ public minuterollingAppender(レイアウトレイアウト、文字列ファイル名)IoException {super(layout、filename、true); ActivateOptions(); } / ** * @return the intervaltime * / public int getIntervalTime(){return intervaltime; } / ** * @param intervaltime * set * / public void setintervaltime(int intervaltime){this.intervaltime = intervaltime; } @Override public void ActivateOptions(){super.activateoptions(); if(filename!= null){now.settime(system.currenttimemillis()); sdf = new SimpledateFormat(datePattern); file file = new file(filename); ScheduleDfileName = filename + sdf.format(new Date(file.lastModified())); } else {loglog .error( "ファイルまたはdatepatternオプションのいずれかがAppender [" + name + "]に設定されていません。"); }} /***現在のファイルを新しいファイルにロールオーバーします。 */ void rollover()throws ioexception {string datedfileName = filename + sdf.format(now); //現在の間隔の//境界内にいるため、ロールオーバーするには時期尚早です。 //次の間隔に到達すると、ロールオーバーが発生します。 if(scheduledfilename.equals(datedfilename)){return; } //現在のファイルを閉じて、datedfileName this.closefile()に名前を変更します。ファイルターゲット= newファイル(ScheduleDfileName); if(target.exists()){target.delete(); } file file = new file(filename); boolean result = file.renameto(ターゲット); if(result){loglog.debug(filename + " - >" + scheduleDfileName); } else {loglog.error( "[" + filename + "]に[" + scheduledfilename + "]に変更に失敗しました。"); } try {//これもファイルを閉じます。複数の//クローズ操作が安全であるため、これは問題ありません。 this.setfile(filename、true、this.bufferedio、this.bufferize); } catch(ioException e){errorhandler.error( "setfile(" + filename + "、true)call failed。"); } ScheduleDfileName = datedFileName; } /***この方法は、MinuterollingAppenderをスーパークラスと区別します。 * * <p> *実際にロギングする前に、この方法では、ロールオーバーを行う時が来たかどうかを確認します。そうであれば、次のロールオーバー時間をスケジュールしてから *ロールオーバーします。 * */ @Override Protected void subappend(loggingEventイベント){long n = system.currenttimemillis(); if(n> = nextcheck){now.settime(n); nextcheck = rc.getNextCheckMillis(現在); try {rollover(); } catch(ioException ioe){if(ioe instanceof interrumentedioexception){thread.currentthread()。 } loglog.error( "rollover()failed。"、ioe); }} super.subappend(event); } /*** RollingCalendarは、MinuterollingAppenderのヘルパークラスです。 *周期性のタイプと現在の時間が与えられた場合、次の *間隔の開始を計算します。 * */ class RollingCalendarはGregorianCalendarを拡張します{private static final long serialversionuid = -3560331770601814177l; RollingCalendar(){super(); } public long getnextcheckmillis(date now){return getnextcheckdate(now).getTime(); } public date getNextCheckdate(date now){this.settime(now); this.set(calendar.second、0); this.set(calendar.millisecond、0); this.add(calendar.minute、intervaltime); return getTime(); }}}テスト構成ファイルは次のとおりです。
<?xml version = "1.0" encoding = "utf-8"?> <!doctype log4j:構成システム "log4j.dtd"> <log4j:log4j = "http://jakarta.apache.org/log4j/" /> <param name = "append" balue = "true"/> <param name = "intervaltime" value = "2"/> <layout> <param name = "conversionPattern" value = "%p%d(%c:%l) - %m%n"/> </layout> </layout> </appender> <root> <root> <debug "/> < </log4j:構成>
タイミングの実装については、Javaが提供するタイマーの実装を使用することもできます。これにより、ログを記録するたびに時間の計算と比較が排除されます。違いは、実際にスレッドをセットアップしてロールオーバーメソッドを呼び出すことです。実装は次のとおりです。
パッケージnet.csdn.blog; java.io.fileをインポートします。 java.io.ioexceptionをインポートします。 java.text.simpledateformatをインポートします。 Import Java.util.date; java.util.timerをインポートします。 java.util.timertaskをインポートします。 org.apache.log4j.fileappenderをインポートします。 Import org.apache.log4j.layout; org.apache.log4j.helpers.loglogをインポートします。 Public Class TimerTaskRollingAppenderは、FileAppender { /***日付パターンを拡張します。デフォルトでは、パターンは「 '.'yyyy-mm-dd」 *に設定されています。 */ private static final string datepattern = "'.'yyyy-mm-dd-hhmm'.log'"; / ***間隔時間、ユニット:分*/ private int intervaltime = 10; SimpleDateFormat sdf = new SimpledateFormat(datePattern); /***デフォルトのコンストラクターは何もしません。 */public TimertaskRollingAppender(){}/** * a <code> timertaskrollingAppender </code>をインスタンス化し、<code> filename </code>によって設計されたファイルを開きます。オープンしたファイル名は、このAppenderの * Ouput宛先になります。 */ public TimertaskRollingAppender(レイアウトレイアウト、文字列ファイル名)throws ioException {super(layout、filename、true); ActivateOptions(); } / ** * @return the intervaltime * / public int getIntervalTime(){return intervaltime; } / ** * @param intervaltime * set * / public void setintervaltime(int intervaltime){this.intervaltime = intervaltime; } @Override public void ActivateOptions(){super.activateoptions();タイマータイマー= new Timer(); Timer.schedule(new logtimertask()、1000、intervaltime * 60000); } class logtimertask extends timertask {@override public void run(){string datedfileName = filename + sdf.format(new date()); closefile();ファイルターゲット= newファイル(datedFileName); if(target.exists())target.delete(); file file = new file(filename); boolean result = 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)call failed。"); }}}}}ただし、上記の実装には2つの問題があります。
1)並行性
run()でclosefile()を呼び出した後に並行性の問題が発生する可能性のある場所、subappend()メソッドは、たまたまログを書きます。現時点では、ファイルは閉じられており、次のエラーが報告されます。
java.io.ioexception:sun.nio.cs.streamencoder.ensureopen(未知のソース)でsun.nio.cs.streamencoder.write(未知のソース)でsun.nio.cs.streamencoder.write(未知のソース)でsun.nio.cs.streamencoder.ensureopenで閉鎖されたストリーム。ソリューションは比較的簡単です。 run()メソッド全体を同期させ、同期キーワードを追加するだけです。しかし、著者は、実際に書きたいと思っていて、書き込み速度が十分に速い場合、ログが失われる可能性のある状況を解決していません。
タイマーを使用して実装することはより簡単ですが、タイマーのタスクが長すぎると実行される場合、それらはタイマーオブジェクトのみを占有し、後続のタスクをいつでも実行できなくなります。ソリューションもシンプルです。スレッドプールバージョンタイマークラスScheduleDexecutorServiceは、以下を実装するために使用されます。
/ ** * */ package net.csdn.blog; java.io.fileをインポートします。 java.io.ioexceptionをインポートします。 java.text.simpledateformatをインポートします。 Import Java.util.date; java.util.concurrent.executorsをインポートします。 java.util.concurrent.timeunitをインポートします。 org.apache.log4j.fileappenderをインポートします。 Import org.apache.log4j.layout; org.apache.log4j.helpers.loglogをインポートします。 /** * @author coder_xia * <p> *スケジュールされたexecutorserviceを使用して、時限構成印刷ログを実装 * <p> * * * /public class scheduledexexecutorserviceappender extends fileappender { /** *日付パターンを拡張します。デフォルトでは、パターンは「 '.'yyyy-mm-dd」 *に設定されています。 */ private static final string datepattern = "'.'yyyy-mm-dd-hhmm'.log'"; / ***間隔時間、ユニット:分*/ private int intervaltime = 10; SimpleDateFormat sdf = new SimpledateFormat(datePattern); /***デフォルトのコンストラクターは何もしません。 */public ScheduleDexecutorServiceAppender(){}/** * scheduledexecutorserviceappender </code>をインスタンス化し、<code> filename </code>によって設計された *ファイルを開きます。オープンしたファイル名は、このAppenderのOuput宛先になります。 */ public ScheduleDexecutorServiceAppender(レイアウトレイアウト、文字列ファイル名)IoException {super(layout、filename、true); ActivateOptions(); } / ** * @return the intervaltime * / public int getIntervalTime(){return intervaltime; } / ** * @param intervaltime * set * / public void setintervaltime(int intervaltime){this.intervaltime = intervaltime; } @Override public void ActivateOptions(){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();ファイルターゲット= newファイル(datedFileName); if(target.exists())target.delete(); file file = new file(filename); boolean result = 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)call failed。"); }}}}}タイミングの実装に関して、これはほぼ終わりです。デフォルトは、10分で新しいログファイルを生成することです。構成するときに自分で設定できます。しかし、隠された危険があります。構成担当者が時間間隔が数分であることを知らない場合、秒であると思われる場合、600があり、Gでログファイルを生成するためにデバッグを開いて、間違いなく災害になります。次の変換は、RollingFileAppenderの最大サイズと一致するバックアップファイルの最大数を組み合わせて、再度改善することです。次回は、変換プロセスについて引き続き説明します。
モジュール名構成を追加します
LOG4Jタイム付き印刷のカスタムクラス実装について言及したため、指定されたサイズとバックアップファイルの数については説明しません。 RollingFileAppenderクラスのコピーコードから以前のカスタムクラスに追加できます。解決する必要がある唯一のことは、並行性の問題です。つまり、名前変更ファイルが閉じられている場合、ログイベントが発生したときに出力ストリームが閉じられたエラーが報告されます。
現在、このようなアプリケーションシナリオがあり、しばしばあります。
1.プロジェクトには複数の異なるプロジェクトが含まれています。
2。同じプロジェクトには、異なるモジュールが含まれています。
最初のケースでは、log4j <catogery = "test">を構成し、ロガーを生成するときに次の方法を使用できます。
logger logger = logger.getLogger( "test");
2番目のケースでは、異なるモジュールを同じログファイルに印刷することを望んでいますが、問題がある場合に問題を見つけるためにログにモジュール名を印刷することを望んでいます。したがって、この記事では、Appenderクラスに構成モジュレーションを追加する必要があります。以下の変換を始めましょう。時限印刷とは異なり、変換の基本クラスとしてRollingFileAppenderクラスを使用します。
まず、構成項目モジュレナームを追加し、取得メソッドとセットメソッドを追加します。
RollingFileAppenderから継承されているため、Subappend()のLoggingEventのデータをフォーマットするだけで、Formatinfoメソッドを追加してデータをフォーマットする必要があり、コードは省略されています。
最終製品カテゴリは次のとおりです。
パッケージnet.csdn.blog; Import org.apache.log4j.category; org.apache.log4j.rollingfileappenderをインポートします。 org.apache.log4j.spi.loggingeventをインポートします。 / ** * @author coder_xia * */ public class moduleappender extends RollingFileAppender {private string modulename; / ** * @return the moduleName */ public string getModulename(){return moduleName; } / ** * @param modulename * set * / public void setModulename(string modulename){this.modulename = modulename; } / ** *フォーマット印刷コンテンツ * * @param event * event * @return msg * / private string formatinfo(loggingevent event){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(event); super.subappend(new LoggingEvent(category.class.getName()、event .getLogger()、event.getLevel()、MSG、null)); }}