Source

log4net-patches / RFA-NG

Full commit
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
# HG changeset patch
# Parent 5a5078efc63aebd4e047af51bbbe4286376db5c2
A RFA-NG implementation that cleans up a few things

diff -r 5a5078efc63a src/Appender/RollingFileAppenderNG.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Appender/RollingFileAppenderNG.cs	Wed Sep 21 22:50:10 2011 +0200
@@ -0,0 +1,143 @@
+#region Apache License
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more 
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership. 
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with 
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+
+using log4net.Util;
+using log4net.Core;
+using log4net.Appender.RollingFileAppenderNGAPI;
+
+namespace log4net.Appender
+{
+	/// <summary>
+	/// Appender that rolls log files based on size or date or both.
+	/// </summary>
+	/// <author>Dominik Psenner</author>
+	public class RollingFileAppenderNG : FileAppender
+	{
+		#region Public Instance Constructors
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="RollingFileAppenderNG" /> class.
+		/// </summary>
+		/// <remarks>
+		/// <para>
+		/// Default constructor.
+		/// </para>
+		/// </remarks>
+		public RollingFileAppenderNG() 
+		{
+			// for now set up the cron rolling condition and the index rolling strategy by default
+			RollingCondition = new CronRollingCondition("*", "*", "*", "*", "*");
+			RollingStrategy = new IndexRollingStrategy();
+		}
+
+		#endregion Public Instance Constructors
+
+		#region Public Instance Properties
+		public IRollingCondition RollingCondition
+		{
+			get;
+			private set;
+		}
+		public IRollingStrategy RollingStrategy
+		{
+			get;
+			private set;
+		}
+		#endregion Public Instance Properties
+
+		#region Override implementation of FileAppender 
+  
+		/// <summary>
+		/// Sets the quiet writer being used.
+		/// </summary>
+		/// <remarks>
+		/// This method can be overridden by sub classes.
+		/// </remarks>
+		/// <param name="writer">the writer to set</param>
+		override protected void SetQWForFiles(TextWriter writer) 
+		{
+			QuietWriter = new CountingQuietTextWriter(writer, ErrorHandler);
+		}
+
+		/// <summary>
+		/// Write out a logging event.
+		/// </summary>
+		/// <param name="loggingEvent">the event to write to file.</param>
+		/// <remarks>
+		/// <para>
+		/// Handles append time behavior for RollingFileAppenderNG.
+		/// </para>
+		/// </remarks>
+		override protected void Append(LoggingEvent loggingEvent) 
+		{
+			RollFileTrigger();
+			base.Append(loggingEvent);
+		}
+  
+		/// <summary>
+		/// Write out an array of logging events.
+		/// </summary>
+		/// <param name="loggingEvents">the events to write to file.</param>
+		/// <remarks>
+		/// <para>
+		/// Handles append time behavior for RollingFileAppenderNG.
+		/// </para>
+		/// </remarks>
+		override protected void Append(LoggingEvent[] loggingEvents) 
+		{
+			RollFileTrigger();
+			base.Append(loggingEvents);
+		}
+
+		/// <summary>
+		/// Performs any required rolling before outputting the next event
+		/// </summary>
+		/// <remarks>
+		/// <para>
+		/// Handles append time behavior for RollingFileAppenderNG. It checks first
+		/// whether the conditions to roll the file are met and if so asks the roll
+		/// file strategy to do the roll operation.
+		/// </para>
+		/// </remarks>
+		protected void RollFileTrigger()
+		{
+			if (RollingCondition == null)
+			{
+				//@@@ TODO throw exception
+			}
+			if (RollingStrategy == null)
+			{
+				//@@@ TODO throw exception
+			}
+
+			// check if the rolling conditions are met
+			if (RollingCondition.IsMet(File))
+			{
+				// let the strategy do all the required rolling
+				RollingStrategy.Roll(File);
+			}
+		}
+		#endregion
+	}
+}
diff -r 5a5078efc63a src/Appender/RollingFileAppenderNGAPI/CronRollingCondition.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Appender/RollingFileAppenderNGAPI/CronRollingCondition.cs	Wed Sep 21 22:50:10 2011 +0200
@@ -0,0 +1,198 @@
+#region Apache License
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more 
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership. 
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with 
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+using System;
+using System.IO;
+
+namespace log4net.Appender.RollingFileAppenderNGAPI
+{
+	/// <summary>
+	/// A implementation of the <see cref="IRollingCondition"/> interface that rolls
+	/// the file cronologically.
+	/// </summary>
+	/// <author>Dominik Psenner</author>
+	public class CronRollingCondition : IRollingCondition
+	{
+		#region Public Instance Constructors
+
+		public CronRollingCondition()
+			: this("*", "*", "*", "*", "*")
+		{
+		}
+
+		public CronRollingCondition(string dow, string month, string day, string hour, string minute)
+		{
+			Dow = TryParse(dow);
+			Month = TryParse(month);
+			Day = TryParse(day);
+			Hour = TryParse(hour);
+			Minute = TryParse(minute);
+		}
+
+		#endregion
+
+		#region Protected Instance Properties
+
+		protected Tuple<int?, MatchType> Dow { get; private set; }
+		protected Tuple<int?, MatchType> Month { get; private set; }
+		protected Tuple<int?, MatchType> Day { get; private set; }
+		protected Tuple<int?, MatchType> Hour { get; private set; }
+		protected Tuple<int?, MatchType> Minute { get; private set; }
+
+		#endregion
+
+		#region Private Instance Properties
+
+		private ulong last_rolled = 0;
+
+		#endregion
+
+		#region Protected Inner Classes
+
+		protected enum MatchType
+		{
+			Nothing,
+			Exact,
+			Remainder,
+		}
+
+		#endregion
+
+		#region Protected Static Methods
+
+		/// <summary>
+		/// This method parses a string to match any of these:
+		/// i
+		/// *
+		/// */i
+		/// </summary>
+		/// <param name="input"></param>
+		/// <returns></returns>
+		static protected Tuple<int?, MatchType> TryParse(string input)
+		{
+			// trim input and strip empty spaces
+			string inputParsed = input.Trim().Replace(" ", "").Replace("\t", "");
+
+			// match anything: *
+			if (inputParsed == "*")
+			{
+				return Tuple.Create<int?, MatchType>(null, MatchType.Nothing);
+			}
+			// match a remainder: */c
+			if (inputParsed.StartsWith("*/") || inputParsed.StartsWith(@"*\"))
+			{
+				// strip first two chars
+				inputParsed = inputParsed.Substring(2);
+				// parse the remainder
+				int i = -1;
+				if (int.TryParse(inputParsed, out i))
+				{
+					return Tuple.Create<int?, MatchType>(i, MatchType.Remainder);
+				}
+			}
+			else // match one specific numer: i
+			{
+				int i = -1;
+				if (int.TryParse(inputParsed, out i))
+				{
+					return Tuple.Create<int?, MatchType>(i, MatchType.Exact);
+				}
+			}
+
+			// throw exception by default
+			throw new FormatException(string.Format("The input string '{0}' could not be parsed to a valid format.", input));
+		}
+
+		#endregion
+
+		#region Implementation of IRollingCondition
+
+		public bool IsMet(string file)
+		{
+			DateTime now = DateTime.Now;
+
+			// check only every minute
+			// we can skip the check as we checked this minute already 
+			// and if we don't we may run into the situation to roll a file twice
+			if (GetUniqueIndex(now) == last_rolled)
+			{
+				return false;
+			}
+			if (!IsMet(Dow, (int)now.DayOfWeek))
+			{
+				return false;
+			}
+			if (!IsMet(Month, now.Month))
+			{
+				return false;
+			}
+			if (!IsMet(Day, now.Day))
+			{
+				return false;
+			}
+			if (!IsMet(Hour, now.Hour))
+			{
+				return false;
+			}
+			if (!IsMet(Minute, now.Minute))
+			{
+				return false;
+			}
+
+			last_rolled = GetUniqueIndex(now);
+			return true;
+		}
+
+		private static uint GetUniqueIndex(DateTime now)
+		{
+			return (uint)now.DayOfWeek * (uint)now.Month * (uint)now.Day * (uint)now.Hour * (uint)now.Minute;
+		}
+
+		#endregion
+
+		#region Private Methods
+
+		private bool IsMet(Tuple<int?, MatchType> match, int item)
+		{
+			switch (match.Item2)
+			{
+				case MatchType.Exact:
+					if (match.Item1.Value != item)
+					{
+						return false;
+					}
+					break;
+				case MatchType.Remainder:
+					// special case: */0, the division through 0 is undefined; this match should pass
+					if (match.Item1.Value == 0)
+					{
+						return true;
+					}
+					else if (item % match.Item1.Value != 0)
+					{
+						return false;
+					}
+					break;
+			}
+			return true;
+		}
+
+		#endregion
+	}
+}
diff -r 5a5078efc63a src/Appender/RollingFileAppenderNGAPI/IRollingCondition.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Appender/RollingFileAppenderNGAPI/IRollingCondition.cs	Wed Sep 21 22:50:10 2011 +0200
@@ -0,0 +1,38 @@
+#region Apache License
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more 
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership. 
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with 
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+using System;
+
+namespace log4net.Appender.RollingFileAppenderNGAPI
+{
+	/// <summary>
+	/// The interface definition of a rolling condition.
+	/// </summary>
+	/// <author>Dominik Psenner</author>
+	public interface IRollingCondition
+	{
+		/// <summary>
+		/// This method should implement all checks needed to determine if a file
+		/// can be rolled based on the conditions it implies to the file.
+		/// </summary>
+		/// <param name="file">the file to be rolled</param>
+		/// <returns>true when the file has met all conditions to be rolled</returns>
+		bool IsMet(string file);
+	}
+}
diff -r 5a5078efc63a src/Appender/RollingFileAppenderNGAPI/IRollingStrategy.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Appender/RollingFileAppenderNGAPI/IRollingStrategy.cs	Wed Sep 21 22:50:10 2011 +0200
@@ -0,0 +1,32 @@
+#region Apache License
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more 
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership. 
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with 
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+using System;
+
+namespace log4net.Appender.RollingFileAppenderNGAPI
+{
+	public interface IRollingStrategy
+	{
+		/// <summary>
+		/// This method should implement all the rolling operations.
+		/// </summary>
+		/// <param name="file">the file to be rolled</param>
+		void Roll(string file);
+	}
+}
diff -r 5a5078efc63a src/Appender/RollingFileAppenderNGAPI/IndexRollingStrategy.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Appender/RollingFileAppenderNGAPI/IndexRollingStrategy.cs	Wed Sep 21 22:50:10 2011 +0200
@@ -0,0 +1,77 @@
+#region Apache License
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more 
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership. 
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with 
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+using System;
+using System.IO;
+
+namespace log4net.Appender.RollingFileAppenderNGAPI
+{
+	/// <summary>
+	/// This is a simple rolling strategy implementation that rolls a file by
+	/// appending the index 0. If a file exists that is named exactly as that
+	/// that file will be renamed to index 1 until reaching the maximum index.
+	/// The last file will be removed.
+	/// </summary>
+	/// <author>Dominik Psenner</author>
+	public class IndexRollingStrategy : IRollingStrategy
+	{
+		#region Implementation of IRollingStrategy
+
+		/// <summary>
+		/// This method rolls a file with backup indexes between
+		/// [0..10].
+		/// </summary>
+		/// <param name="file"></param>
+		public void Roll(string file)
+		{
+			DoRoll(file, file, 0, 10);
+		}
+
+		#endregion
+
+		#region Private Methods
+
+		private void DoRoll(string baseFilename, string currentFilename, int currentIndex, int maxIndex)
+		{
+			if (currentIndex > maxIndex)
+			{
+				if (File.Exists(currentFilename))
+				{
+					File.Delete(currentFilename);
+				}
+				return;
+			}
+			if (!File.Exists(currentFilename))
+			{
+				return;
+			}
+
+			// determine next filename
+			string nextFilename = string.Format("{0}.{1}", baseFilename, currentIndex);
+
+			// iterate the process until we meet the end
+			DoRoll(baseFilename, nextFilename, currentIndex + 1, maxIndex);
+
+			// rename this file now that there's free room after us
+			File.Move(currentFilename, nextFilename);
+		}
+
+		#endregion
+	}
+}
diff -r 5a5078efc63a src/log4net.vs2010.csproj
--- a/src/log4net.vs2010.csproj	Tue Sep 20 17:52:26 2011 +0000
+++ b/src/log4net.vs2010.csproj	Wed Sep 21 22:50:10 2011 +0200
@@ -187,6 +187,13 @@
     <Compile Include="Appender\RollingFileAppender.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="Appender\RollingFileAppenderNG.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Appender\RollingFileAppenderNGAPI\CronRollingCondition.cs" />
+    <Compile Include="Appender\RollingFileAppenderNGAPI\IndexRollingStrategy.cs" />
+    <Compile Include="Appender\RollingFileAppenderNGAPI\IRollingCondition.cs" />
+    <Compile Include="Appender\RollingFileAppenderNGAPI\IRollingStrategy.cs" />    
     <Compile Include="Appender\SmtpAppender.cs">
       <SubType>Code</SubType>
     </Compile>
@@ -780,4 +787,4 @@
     <PostBuildEvent>
     </PostBuildEvent>
   </PropertyGroup>
-</Project>
\ No newline at end of file
+</Project>