OSDN Git Service

Log のインデントをわかりやすくしてみた。
[strokestylet/CsWin10Desktop3.git] / FDK / Log.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Linq;
6 using System.Text;
7
8 namespace FDK
9 {
10         public class Log
11         {
12                 private static readonly string tagINFO = "[INFORMATION]";
13                 private static readonly string tagWARNING = "[  WARNING  ]";
14                 private static readonly string tagERROR = "[   ERROR   ]";
15                 private static readonly string tagINTERVAL = "[ INTERVAL  ]";
16
17                 /// <summary>
18                 ///             これを設定しておくと、スレッドID の横に (名前) と出力されるようになる。
19                 /// </summary>
20                 public static void 現在のスレッドに名前をつける( string 名前 )
21                 {
22                         lock( Log._スレッド間同期 )
23                         {
24                                 // (1) Log用に名前を設定
25                                 var ID = Log.GetCurrentThreadId();
26
27                                 if( Log._IDto名前.ContainsKey( ID ) )
28                                         Log._IDto名前.Remove( ID );
29
30                                 Log._IDto名前.Add( ID, 名前 );
31
32                                 // (2) デバッグ用に名前を設定
33                                 System.Threading.Thread.CurrentThread.Name = 名前;
34                         }
35                 }
36
37                 public static void Info( string 出力 )
38                 {
39                         lock( Log._スレッド間同期 )
40                         {
41                                 Log._一定時間が経過していたら区切り線を表示する();
42                                 Trace.WriteLine( $"{tagINFO} {Log._日時とスレッドID}{Log._インデックスを返す( Log._深さ )}{出力}" );
43                         }
44                 }
45
46                 public static void WARNING( string 出力 )
47                 {
48                         lock( Log._スレッド間同期 )
49                         {
50                                 Log._一定時間が経過していたら区切り線を表示する();
51                                 Trace.WriteLine( $"{tagWARNING} {Log._日時とスレッドID} {出力}" );
52                         }
53                 }
54
55                 public static void ERROR( string 出力 )
56                 {
57                         lock( Log._スレッド間同期 )
58                         {
59                                 Log._一定時間が経過していたら区切り線を表示する();
60                                 Trace.WriteLine( $"{tagERROR} {Log._日時とスレッドID} {出力}" );
61                         }
62                 }
63
64                 public static void ERRORandTHROW( string 出力, Exception inner = null )
65                 {
66                         Log.ERROR( 出力 );
67                         throw new FDKException( 出力, inner );
68                 }
69
70                 public static void Header( string ヘッダ出力 )
71                 {
72                         lock( Log._スレッド間同期 )
73                         {
74                                 Log._一定時間が経過していたら区切り線を表示する();
75                                 Log.Info( "" );
76                                 Log.Info( $"======== {ヘッダ出力} ========" );
77                         }
78                 }
79
80                 public static LogBlock Block( string ブロック名 )
81                 {
82                         return new LogBlock( ブロック名 );
83                 }
84
85                 /// <summary>
86                 ///             連続して呼び出しても、前回の同一識別キーでの表示から一定時間が経たないと表示しないInfoメソッド。
87                 /// </summary>
88                 /// <remarks>
89                 ///             毎秒60回の進行描画の進捗など、連続して呼び出すと膨大な数のログが出力されてしまう場合に使用する。
90                 /// </remarks>
91                 /// <param name="識別キー"></param>
92                 /// <param name="出力"></param>
93                 public static void 定間隔Info( string 識別キー, string 出力, double 間隔sec = 0.25 )
94                 {
95                         lock( Log._スレッド間同期 )
96                         {
97                                 if( Log._識別キー別最終表示時刻.ContainsKey( 識別キー ) )
98                                 {
99                                         if( ( DateTime.Now - Log._識別キー別最終表示時刻[ 識別キー ] ).TotalSeconds >= 間隔sec )
100                                         {
101                                                 Log._識別キー別最終表示時刻[ 識別キー ] = DateTime.Now;
102                                                 Trace.WriteLine( $"{tagINFO} {Log._日時とスレッドID} {出力}" );
103                                         }
104                                 }
105                                 else
106                                 {
107                                         Log._識別キー別最終表示時刻.Add( 識別キー, DateTime.Now );
108                                         Trace.WriteLine( $"{tagINFO} {Log._日時とスレッドID} {出力}" );
109                                 }
110                         }
111                 }
112
113                 /// <summary>
114                 ///             指定されたフォルダ内に配置可能な、新しいログファイル名を生成して返す。
115                 /// </summary>
116                 /// <param name="ログフォルダパス">ログファイルを配置するフォルダのパス。</param>
117                 /// <param name="ログファイルの接頭辞">ログファイル名に付与する接頭辞。</param>
118                 /// <param name="最大保存期間">フォルダ内に保存しておく最大の期間。</param>
119                 /// <returns>生成されたログファイル名。パス付き。</returns>
120                 /// <remarks>
121                 ///             ログファイル名は、現在時刻をベースに名付けられる。
122                 ///             同時に、フォルダ内に存在するすべてのファイルの更新時刻をチェックし、最大保存期間を超える古いファイルは、自動的に削除する。
123                 /// </remarks>
124                 public static string ログファイル名を生成する( string ログフォルダパス, string ログファイルの接頭辞, TimeSpan 最大保存期間 )
125                 {
126                         var 現在の日時 = DateTime.Now;
127
128                         if( Directory.Exists( ログフォルダパス ) )
129                         {
130                                 // (A) フォルダがある場合 → 最大保存期間を超える古いファイルを削除する。
131                                 var 削除対象ファイルs = Directory.GetFiles( ログフォルダパス ).Where(
132                                         ( file ) => ( File.GetLastWriteTime( file ) < ( 現在の日時 - 最大保存期間 ) ) );
133
134                                 foreach( var path in 削除対象ファイルs )
135                                         File.Delete( path );
136                         }
137                         else
138                         {
139                                 // (B) フォルダがない場合 → 作成する。
140                                 Directory.CreateDirectory( ログフォルダパス );
141                         }
142
143                         // 現在の時刻をもとに、新しいログファイル名を生成して返す。
144                         return Path.Combine( ログフォルダパス, $"{ログファイルの接頭辞}{現在の日時.ToString( "yyyyMMdd-HHmmssffff" )}.txt" );
145                 }
146
147
148                 internal static void BeginInfo( string 開始ブロック名 )
149                 {
150                         lock( Log._スレッド間同期 )
151                         {
152                                 Log._一定時間が経過していたら区切り線を表示する();
153                                 Trace.WriteLine( $"{tagINFO} {Log._日時とスレッドID}{Log._インデックスを返す( Log._深さ )}{開始ブロック名} --> 開始" );
154
155                                 Log._深さ++;
156                         }
157                 }
158
159                 internal static void EndInfo( string 終了ブロック名 )
160                 {
161                         lock( Log._スレッド間同期 )
162                         {
163                                 Log._深さ = Math.Max( ( Log._深さ - 1 ), 0 );
164
165                                 Log._一定時間が経過していたら区切り線を表示する();
166                                 Trace.WriteLine( $"{tagINFO} {Log._日時とスレッドID}{Log._インデックスを返す( Log._深さ )}{終了ブロック名} <-- 終了" );
167                         }
168                 }
169
170
171                 private const double _最小区切り時間 = 2.0; // 区切り線を入れる最小の間隔[秒]。
172
173                 private static string _日時とスレッドID
174                 {
175                         get
176                         {
177                                 var NETスレッドID = System.Threading.Thread.CurrentThread.ManagedThreadId;
178                                 var Win32スレッドID = Log.GetCurrentThreadId();
179                                 var スレッド識別文字列 = ( Log._IDto名前.ContainsKey( Win32スレッドID ) ) ? $"({Log._IDto名前[ Win32スレッドID ]})" : "";
180                                 return $"{DateTime.Now.ToLongTimeString()} [{NETスレッドID:00},0x{Win32スレッドID:x}{スレッド識別文字列}]";
181                         }
182                 }
183
184                 private static readonly Dictionary<uint, string> _IDto名前 = new Dictionary<uint, string>();
185
186                 private static Dictionary<string, DateTime> _識別キー別最終表示時刻 = new Dictionary<string, DateTime>();
187
188                 private static TimeSpan _経過時間
189                 {
190                         get
191                         {
192                                 var 現在時刻 = DateTime.Now;
193                                 var 経過時間 = 現在時刻 - Log._最終表示時刻;
194                                 Log._最終表示時刻 = 現在時刻;  // 更新
195                                 return 経過時間;
196                         }
197                 }
198
199                 private static DateTime _最終表示時刻 = DateTime.Now;
200
201                 private static int _深さ = 0;
202
203                 private static readonly object _スレッド間同期 = new object();
204
205
206                 private static void _一定時間が経過していたら区切り線を表示する()
207                 {
208                         var span = Log._経過時間.TotalSeconds;
209
210                         if( Log._最小区切り時間 < span )
211                         {
212                                 #region " faces "
213                                 //----------------
214                                 var faces = new[] {
215                                         @" ^^) _旦~~",
216                                         @"( ..)φ",
217                                         @"( `ー´)ノ",
218                                         @"(#^^#)",
219                                         @"('ω')",
220                                         @"(´・ω・`)",
221                                         @"(*´ω`*)",
222                                         @"( ・_・)ノΞ●~*",
223                                         @"∠( ˙-˙ )/",
224                                         @"(*/▽\*)",
225                                         @"(ɔ ˘⌣˘)˘⌣˘ c)",
226                                         @"((*◕ω◕)ノ",
227                                         @"ㆆ﹏ㆆ",
228                                         @"(゚Д゚;≡;゚д゚)",
229                                         @"(゚дノ[壁]",
230                                         @"ʅ(´-ω-`)ʃ",
231                                         @"(*´﹃`*)",
232                                         @"(>ω<)",
233                                         @"٩(๑❛ᴗ❛๑)۶ ",
234                                         @"(。・ω・。)",
235                                         @"_(┐「ε:)_",
236                                         @"(ノ-_-)ノ~┻━┻",
237                                         @"_(ˇωˇ」∠)_ ",
238                                 };
239                                 int faceNow = ( (int) ( span * 1000.0 ) ) % faces.Length;
240                                 //----------------
241                                 #endregion
242
243                                 Trace.WriteLine( $"{tagINTERVAL} ...... {faces[ faceNow ]} ......" );
244                         }
245                 }
246
247                 private static string _インデックスを返す( int 長さ )
248                 {
249                         var sb = new StringBuilder();
250
251                         for( int i = 0; i < 長さ; i++ )
252                                 sb.Append( ( 0 < i ) ? "| " : "  " );
253
254                         return sb.ToString();
255                 }
256
257
258                 #region " Win32 "
259                 //-----------------
260                 [System.Runtime.InteropServices.DllImport( "kernel32.dll" )]
261                 private static extern uint GetCurrentThreadId();
262                 //-----------------
263                 #endregion
264         }
265 }