1 #region Copyright & License
2 //
3 // Copyright 2001-2005 The Apache Software Foundation
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 #endregion
18 
19 using System;
20 using System.IO;
21 using System.Collections;
22 
23 using log4net.Filter;
24 using log4net.Util;
25 using log4net.Layout;
26 using log4net.Core;
27 
28 namespace log4net.Appender
29 {
30 	/// <summary>
31 	/// Abstract base class implementation of <see cref="IAppender"/>.
32 	/// </summary>
33 	/// <remarks>
34 	/// <para>
35 	/// This class provides the code for common functionality, such
36 	/// as support for threshold filtering and support for general filters.
37 	/// </para>
38 	/// <para>
39 	/// Appenders can also implement the <see cref="IOptionHandler"/> interface. Therefore
40 	/// they would require that the <see cref="IOptionHandler.ActivateOptions()"/> method
41 	/// be called after the appenders properties have been configured.
42 	/// </para>
43 	/// </remarks>
44 	/// <author>Nicko Cadell</author>
45 	/// <author>Gert Driesen</author>
46 	public abstract class AppenderSkeleton : IAppender, IBulkAppender, IOptionHandler
47 	{
48 		#region Protected Instance Constructors
49 
50 		/// <summary>
51 		/// Default constructor
52 		/// </summary>
53 		/// <remarks>
54 		/// <para>Empty default constructor</para>
55 		/// </remarks>
AppenderSkeleton()56 		protected AppenderSkeleton()
57 		{
58 			m_errorHandler = new OnlyOnceErrorHandler(this.GetType().Name);
59 		}
60 
61 		#endregion Protected Instance Constructors
62 
63 		#region Finalizer
64 
65 		/// <summary>
66 		/// Finalizes this appender by calling the implementation's
67 		/// <see cref="Close"/> method.
68 		/// </summary>
69 		/// <remarks>
70 		/// <para>
71 		/// If this appender has not been closed then the <c>Finalize</c> method
72 		/// will call <see cref="Close"/>.
73 		/// </para>
74 		/// </remarks>
~AppenderSkeleton()75 		~AppenderSkeleton()
76 		{
77 			// An appender might be closed then garbage collected.
78 			// There is no point in closing twice.
79 			if (!m_closed)
80 			{
81 				LogLog.Debug("AppenderSkeleton: Finalizing appender named ["+m_name+"].");
82 				Close();
83 			}
84 		}
85 
86 		#endregion Finalizer
87 
88 		#region Public Instance Properties
89 
90 		/// <summary>
91 		/// Gets or sets the threshold <see cref="Level"/> of this appender.
92 		/// </summary>
93 		/// <value>
94 		/// The threshold <see cref="Level"/> of the appender.
95 		/// </value>
96 		/// <remarks>
97 		/// <para>
98 		/// All log events with lower level than the threshold level are ignored
99 		/// by the appender.
100 		/// </para>
101 		/// <para>
102 		/// In configuration files this option is specified by setting the
103 		/// value of the <see cref="Threshold"/> option to a level
104 		/// string, such as "DEBUG", "INFO" and so on.
105 		/// </para>
106 		/// </remarks>
107 		public Level Threshold
108 		{
109 			get { return m_threshold; }
110 			set { m_threshold = value; }
111 		}
112 
113 		/// <summary>
114 		/// Gets or sets the <see cref="IErrorHandler"/> for this appender.
115 		/// </summary>
116 		/// <value>The <see cref="IErrorHandler"/> of the appender</value>
117 		/// <remarks>
118 		/// <para>
119 		/// The <see cref="AppenderSkeleton"/> provides a default
120 		/// implementation for the <see cref="ErrorHandler"/> property.
121 		/// </para>
122 		/// </remarks>
123 		virtual public IErrorHandler ErrorHandler
124 		{
125 			get { return this.m_errorHandler; }
126 			set
127 			{
128 				lock(this)
129 				{
130 					if (value == null)
131 					{
132 						// We do not throw exception here since the cause is probably a
133 						// bad config file.
134 						LogLog.Warn("AppenderSkeleton: You have tried to set a null error-handler.");
135 					}
136 					else
137 					{
138 						m_errorHandler = value;
139 					}
140 				}
141 			}
142 		}
143 
144 		/// <summary>
145 		/// The filter chain.
146 		/// </summary>
147 		/// <value>The head of the filter chain filter chain.</value>
148 		/// <remarks>
149 		/// <para>
150 		/// Returns the head Filter. The Filters are organized in a linked list
151 		/// and so all Filters on this Appender are available through the result.
152 		/// </para>
153 		/// </remarks>
154 		virtual public IFilter FilterHead
155 		{
156 			get { return m_headFilter; }
157 		}
158 
159 		/// <summary>
160 		/// Gets or sets the <see cref="ILayout"/> for this appender.
161 		/// </summary>
162 		/// <value>The layout of the appender.</value>
163 		/// <remarks>
164 		/// <para>
165 		/// See <see cref="RequiresLayout"/> for more information.
166 		/// </para>
167 		/// </remarks>
168 		/// <seealso cref="RequiresLayout"/>
169 		virtual public ILayout Layout
170 		{
171 			get { return m_layout; }
172 			set { m_layout = value; }
173 		}
174 
175 		#endregion
176 
177 		#region Implementation of IOptionHandler
178 
179 		/// <summary>
180 		/// Initialize the appender based on the options set
181 		/// </summary>
182 		/// <remarks>
183 		/// <para>
184 		/// This is part of the <see cref="IOptionHandler"/> delayed object
185 		/// activation scheme. The <see cref="ActivateOptions"/> method must
186 		/// be called on this object after the configuration properties have
187 		/// been set. Until <see cref="ActivateOptions"/> is called this
188 		/// object is in an undefined state and must not be used.
189 		/// </para>
190 		/// <para>
191 		/// If any of the configuration properties are modified then
192 		/// <see cref="ActivateOptions"/> must be called again.
193 		/// </para>
194 		/// </remarks>
ActivateOptions()195 		virtual public void ActivateOptions()
196 		{
197 		}
198 
199 		#endregion Implementation of IOptionHandler
200 
201 		#region Implementation of IAppender
202 
203 		/// <summary>
204 		/// Gets or sets the name of this appender.
205 		/// </summary>
206 		/// <value>The name of the appender.</value>
207 		/// <remarks>
208 		/// <para>
209 		/// The name uniquely identifies the appender.
210 		/// </para>
211 		/// </remarks>
212 		public string Name
213 		{
214 			get { return m_name; }
215 			set { m_name = value; }
216 		}
217 
218 		/// <summary>
219 		/// Closes the appender and release resources.
220 		/// </summary>
221 		/// <remarks>
222 		/// <para>
223 		/// Release any resources allocated within the appender such as file handles,
224 		/// network connections, etc.
225 		/// </para>
226 		/// <para>
227 		/// It is a programming error to append to a closed appender.
228 		/// </para>
229 		/// <para>
230 		/// This method cannot be overridden by subclasses. This method
231 		/// delegates the closing of the appender to the <see cref="OnClose"/>
232 		/// method which must be overridden in the subclass.
233 		/// </para>
234 		/// </remarks>
Close()235 		public void Close()
236 		{
237 			// This lock prevents the appender being closed while it is still appending
238 			lock(this)
239 			{
240 				if (!m_closed)
241 				{
242 					OnClose();
243 					m_closed = true;
244 				}
245 			}
246 		}
247 
248 		/// <summary>
249 		/// Performs threshold checks and invokes filters before
250 		/// delegating actual logging to the subclasses specific
251 		/// <see cref="Append(LoggingEvent)"/> method.
252 		/// </summary>
253 		/// <param name="loggingEvent">The event to log.</param>
254 		/// <remarks>
255 		/// <para>
256 		/// This method cannot be overridden by derived classes. A
257 		/// derived class should override the <see cref="Append(LoggingEvent)"/> method
258 		/// which is called by this method.
259 		/// </para>
260 		/// <para>
261 		/// The implementation of this method is as follows:
262 		/// </para>
263 		/// <para>
264 		/// <list type="bullet">
265 		///		<item>
266 		///			<description>
267 		///			Checks that the severity of the <paramref name="loggingEvent"/>
268 		///			is greater than or equal to the <see cref="Threshold"/> of this
269 		///			appender.</description>
270 		///		</item>
271 		///		<item>
272 		///			<description>
273 		///			Checks that the <see cref="IFilter"/> chain accepts the
274 		///			<paramref name="loggingEvent"/>.
275 		///			</description>
276 		///		</item>
277 		///		<item>
278 		///			<description>
279 		///			Calls <see cref="PreAppendCheck()"/> and checks that
280 		///			it returns <c>true</c>.</description>
281 		///		</item>
282 		/// </list>
283 		/// </para>
284 		/// <para>
285 		/// If all of the above steps succeed then the <paramref name="loggingEvent"/>
286 		/// will be passed to the abstract <see cref="Append(LoggingEvent)"/> method.
287 		/// </para>
288 		/// </remarks>
DoAppend(LoggingEvent loggingEvent)289 		public void DoAppend(LoggingEvent loggingEvent)
290 		{
291 			// This lock is absolutely critical for correct formatting
292 			// of the message in a multi-threaded environment.  Without
293 			// this, the message may be broken up into elements from
294 			// multiple thread contexts (like get the wrong thread ID).
295 
296 			lock(this)
297 			{
298 				if (m_closed)
299 				{
300 					ErrorHandler.Error("Attempted to append to closed appender named ["+m_name+"].");
301 					return;
302 				}
303 
304 				// prevent re-entry
305 				if (m_recursiveGuard)
306 				{
307 					return;
308 				}
309 
310 				try
311 				{
312 					m_recursiveGuard = true;
313 
314 					if (FilterEvent(loggingEvent) && PreAppendCheck())
315 					{
316 						this.Append(loggingEvent);
317 					}
318 				}
319 				catch(Exception ex)
320 				{
321 					ErrorHandler.Error("Failed in DoAppend", ex);
322 				}
323 #if !MONO && !NET_2_0
324 				// on .NET 2.0 (and higher) and Mono (all profiles),
325 				// exceptions that do not derive from System.Exception will be
326 				// wrapped in a RuntimeWrappedException by the runtime, and as
327 				// such will be catched by the catch clause above
328 				catch
329 				{
330 					// Catch handler for non System.Exception types
331 					ErrorHandler.Error("Failed in DoAppend (unknown exception)");
332 				}
333 #endif
334 				finally
335 				{
336 					m_recursiveGuard = false;
337 				}
338 			}
339 		}
340 
341 		#endregion Implementation of IAppender
342 
343 		#region Implementation of IBulkAppender
344 
345 		/// <summary>
346 		/// Performs threshold checks and invokes filters before
347 		/// delegating actual logging to the subclasses specific
348 		/// <see cref="Append(LoggingEvent[])"/> method.
349 		/// </summary>
350 		/// <param name="loggingEvents">The array of events to log.</param>
351 		/// <remarks>
352 		/// <para>
353 		/// This method cannot be overridden by derived classes. A
354 		/// derived class should override the <see cref="Append(LoggingEvent[])"/> method
355 		/// which is called by this method.
356 		/// </para>
357 		/// <para>
358 		/// The implementation of this method is as follows:
359 		/// </para>
360 		/// <para>
361 		/// <list type="bullet">
362 		///		<item>
363 		///			<description>
364 		///			Checks that the severity of the <paramref name="loggingEvent"/>
365 		///			is greater than or equal to the <see cref="Threshold"/> of this
366 		///			appender.</description>
367 		///		</item>
368 		///		<item>
369 		///			<description>
370 		///			Checks that the <see cref="IFilter"/> chain accepts the
371 		///			<paramref name="loggingEvent"/>.
372 		///			</description>
373 		///		</item>
374 		///		<item>
375 		///			<description>
376 		///			Calls <see cref="PreAppendCheck()"/> and checks that
377 		///			it returns <c>true</c>.</description>
378 		///		</item>
379 		/// </list>
380 		/// </para>
381 		/// <para>
382 		/// If all of the above steps succeed then the <paramref name="loggingEvents"/>
383 		/// will be passed to the <see cref="Append(LoggingEvent[])"/> method.
384 		/// </para>
385 		/// </remarks>
DoAppend(LoggingEvent[] loggingEvents)386 		public void DoAppend(LoggingEvent[] loggingEvents)
387 		{
388 			// This lock is absolutely critical for correct formatting
389 			// of the message in a multi-threaded environment.  Without
390 			// this, the message may be broken up into elements from
391 			// multiple thread contexts (like get the wrong thread ID).
392 
393 			lock(this)
394 			{
395 				if (m_closed)
396 				{
397 					ErrorHandler.Error("Attempted to append to closed appender named ["+m_name+"].");
398 					return;
399 				}
400 
401 				// prevent re-entry
402 				if (m_recursiveGuard)
403 				{
404 					return;
405 				}
406 
407 				try
408 				{
409 					m_recursiveGuard = true;
410 
411 					ArrayList filteredEvents = new ArrayList(loggingEvents.Length);
412 
413 					foreach(LoggingEvent loggingEvent in loggingEvents)
414 					{
415 						if (FilterEvent(loggingEvent))
416 						{
417 							filteredEvents.Add(loggingEvent);
418 						}
419 					}
420 
421 					if (filteredEvents.Count > 0 && PreAppendCheck())
422 					{
423 						this.Append((LoggingEvent[])filteredEvents.ToArray(typeof(LoggingEvent)));
424 					}
425 				}
426 				catch(Exception ex)
427 				{
428 					ErrorHandler.Error("Failed in Bulk DoAppend", ex);
429 				}
430 #if !MONO && !NET_2_0
431 				// on .NET 2.0 (and higher) and Mono (all profiles),
432 				// exceptions that do not derive from System.Exception will be
433 				// wrapped in a RuntimeWrappedException by the runtime, and as
434 				// such will be catched by the catch clause above
435 				catch
436 				{
437 					// Catch handler for non System.Exception types
438 					ErrorHandler.Error("Failed in Bulk DoAppend (unknown exception)");
439 				}
440 #endif
441 				finally
442 				{
443 					m_recursiveGuard = false;
444 				}
445 			}
446 		}
447 
448 		#endregion Implementation of IBulkAppender
449 
450 		/// <summary>
451 		/// Test if the logging event should we output by this appender
452 		/// </summary>
453 		/// <param name="loggingEvent">the event to test</param>
454 		/// <returns><c>true</c> if the event should be output, <c>false</c> if the event should be ignored</returns>
455 		/// <remarks>
456 		/// <para>
457 		/// This method checks the logging event against the threshold level set
458 		/// on this appender and also against the filters specified on this
459 		/// appender.
460 		/// </para>
461 		/// <para>
462 		/// The implementation of this method is as follows:
463 		/// </para>
464 		/// <para>
465 		/// <list type="bullet">
466 		///		<item>
467 		///			<description>
468 		///			Checks that the severity of the <paramref name="loggingEvent"/>
469 		///			is greater than or equal to the <see cref="Threshold"/> of this
470 		///			appender.</description>
471 		///		</item>
472 		///		<item>
473 		///			<description>
474 		///			Checks that the <see cref="IFilter"/> chain accepts the
475 		///			<paramref name="loggingEvent"/>.
476 		///			</description>
477 		///		</item>
478 		/// </list>
479 		/// </para>
480 		/// </remarks>
FilterEvent(LoggingEvent loggingEvent)481 		virtual protected bool FilterEvent(LoggingEvent loggingEvent)
482 		{
483 			if (!IsAsSevereAsThreshold(loggingEvent.Level))
484 			{
485 				return false;
486 			}
487 
488 			IFilter f = this.FilterHead;
489 
490 			while(f != null)
491 			{
492 				switch(f.Decide(loggingEvent))
493 				{
494 					case FilterDecision.Deny:
495 						return false;	// Return without appending
496 
497 					case FilterDecision.Accept:
498 						f = null;		// Break out of the loop
499 						break;
500 
501 					case FilterDecision.Neutral:
502 						f = f.Next;		// Move to next filter
503 						break;
504 				}
505 			}
506 
507 			return true;
508 		}
509 
510 		#region Public Instance Methods
511 
512 		/// <summary>
513 		/// Adds a filter to the end of the filter chain.
514 		/// </summary>
515 		/// <param name="filter">the filter to add to this appender</param>
516 		/// <remarks>
517 		/// <para>
518 		/// The Filters are organized in a linked list.
519 		/// </para>
520 		/// <para>
521 		/// Setting this property causes the new filter to be pushed onto the
522 		/// back of the filter chain.
523 		/// </para>
524 		/// </remarks>
AddFilter(IFilter filter)525 		virtual public void AddFilter(IFilter filter)
526 		{
527 			if (filter == null)
528 			{
529 				throw new ArgumentNullException("filter param must not be null");
530 			}
531 
532 			if (m_headFilter == null)
533 			{
534 				m_headFilter = m_tailFilter = filter;
535 			}
536 			else
537 			{
538 				m_tailFilter.Next = filter;
539 				m_tailFilter = filter;
540 			}
541 		}
542 
543 		/// <summary>
544 		/// Clears the filter list for this appender.
545 		/// </summary>
546 		/// <remarks>
547 		/// <para>
548 		/// Clears the filter list for this appender.
549 		/// </para>
550 		/// </remarks>
ClearFilters()551 		virtual public void ClearFilters()
552 		{
553 			m_headFilter = m_tailFilter = null;
554 		}
555 
556 		#endregion Public Instance Methods
557 
558 		#region Protected Instance Methods
559 
560 		/// <summary>
561 		/// Checks if the message level is below this appender's threshold.
562 		/// </summary>
563 		/// <param name="level"><see cref="Level"/> to test against.</param>
564 		/// <remarks>
565 		/// <para>
566 		/// If there is no threshold set, then the return value is always <c>true</c>.
567 		/// </para>
568 		/// </remarks>
569 		/// <returns>
570 		/// <c>true</c> if the <paramref name="level"/> meets the <see cref="Threshold"/>
571 		/// requirements of this appender.
572 		/// </returns>
IsAsSevereAsThreshold(Level level)573 		virtual protected bool IsAsSevereAsThreshold(Level level)
574 		{
575 			return ((m_threshold == null) || level >= m_threshold);
576 		}
577 
578 		/// <summary>
579 		/// Is called when the appender is closed. Derived classes should override
580 		/// this method if resources need to be released.
581 		/// </summary>
582 		/// <remarks>
583 		/// <para>
584 		/// Releases any resources allocated within the appender such as file handles,
585 		/// network connections, etc.
586 		/// </para>
587 		/// <para>
588 		/// It is a programming error to append to a closed appender.
589 		/// </para>
590 		/// </remarks>
OnClose()591 		virtual protected void OnClose()
592 		{
593 			// Do nothing by default
594 		}
595 
596 		/// <summary>
597 		/// Subclasses of <see cref="AppenderSkeleton"/> should implement this method
598 		/// to perform actual logging.
599 		/// </summary>
600 		/// <param name="loggingEvent">The event to append.</param>
601 		/// <remarks>
602 		/// <para>
603 		/// A subclass must implement this method to perform
604 		/// logging of the <paramref name="loggingEvent"/>.
605 		/// </para>
606 		/// <para>This method will be called by <see cref="DoAppend(LoggingEvent)"/>
607 		/// if all the conditions listed for that method are met.
608 		/// </para>
609 		/// <para>
610 		/// To restrict the logging of events in the appender
611 		/// override the <see cref="PreAppendCheck()"/> method.
612 		/// </para>
613 		/// </remarks>
Append(LoggingEvent loggingEvent)614 		abstract protected void Append(LoggingEvent loggingEvent);
615 
616 		/// <summary>
617 		/// Append a bulk array of logging events.
618 		/// </summary>
619 		/// <param name="loggingEvents">the array of logging events</param>
620 		/// <remarks>
621 		/// <para>
622 		/// This base class implementation calls the <see cref="Append(LoggingEvent)"/>
623 		/// method for each element in the bulk array.
624 		/// </para>
625 		/// <para>
626 		/// A sub class that can better process a bulk array of events should
627 		/// override this method in addition to <see cref="Append(LoggingEvent)"/>.
628 		/// </para>
629 		/// </remarks>
Append(LoggingEvent[] loggingEvents)630 		virtual protected void Append(LoggingEvent[] loggingEvents)
631 		{
632 			foreach(LoggingEvent loggingEvent in loggingEvents)
633 			{
634 				Append(loggingEvent);
635 			}
636 		}
637 
638 		/// <summary>
639 		/// Called before <see cref="Append(LoggingEvent)"/> as a precondition.
640 		/// </summary>
641 		/// <remarks>
642 		/// <para>
643 		/// This method is called by <see cref="DoAppend(LoggingEvent)"/>
644 		/// before the call to the abstract <see cref="Append(LoggingEvent)"/> method.
645 		/// </para>
646 		/// <para>
647 		/// This method can be overridden in a subclass to extend the checks
648 		/// made before the event is passed to the <see cref="Append(LoggingEvent)"/> method.
649 		/// </para>
650 		/// <para>
651 		/// A subclass should ensure that they delegate this call to
652 		/// this base class if it is overridden.
653 		/// </para>
654 		/// </remarks>
655 		/// <returns><c>true</c> if the call to <see cref="Append(LoggingEvent)"/> should proceed.</returns>
PreAppendCheck()656 		virtual protected bool PreAppendCheck()
657 		{
658 			if ((m_layout == null) && RequiresLayout)
659 			{
660 				ErrorHandler.Error("AppenderSkeleton: No layout set for the appender named ["+m_name+"].");
661 				return false;
662 			}
663 
664 			return true;
665 		}
666 
667 		/// <summary>
668 		/// Renders the <see cref="LoggingEvent"/> to a string.
669 		/// </summary>
670 		/// <param name="loggingEvent">The event to render.</param>
671 		/// <returns>The event rendered as a string.</returns>
672 		/// <remarks>
673 		/// <para>
674 		/// Helper method to render a <see cref="LoggingEvent"/> to
675 		/// a string. This appender must have a <see cref="Layout"/>
676 		/// set to render the <paramref name="loggingEvent"/> to
677 		/// a string.
678 		/// </para>
679 		/// <para>If there is exception data in the logging event and
680 		/// the layout does not process the exception, this method
681 		/// will append the exception text to the rendered string.
682 		/// </para>
683 		/// <para>
684 		/// Where possible use the alternative version of this method
685 		/// <see cref="RenderLoggingEvent(TextWriter,LoggingEvent)"/>.
686 		/// That method streams the rendering onto an existing Writer
687 		/// which can give better performance if the caller already has
688 		/// a <see cref="TextWriter"/> open and ready for writing.
689 		/// </para>
690 		/// </remarks>
RenderLoggingEvent(LoggingEvent loggingEvent)691 		protected string RenderLoggingEvent(LoggingEvent loggingEvent)
692 		{
693 			// Create the render writer on first use
694 			if (m_renderWriter == null)
695 			{
696 				m_renderWriter = new ReusableStringWriter(System.Globalization.CultureInfo.InvariantCulture);
697 			}
698 
699 			// Reset the writer so we can reuse it
700 			m_renderWriter.Reset(c_renderBufferMaxCapacity, c_renderBufferSize);
701 
702 			RenderLoggingEvent(m_renderWriter, loggingEvent);
703 			return m_renderWriter.ToString();
704 		}
705 
706 		/// <summary>
707 		/// Renders the <see cref="LoggingEvent"/> to a string.
708 		/// </summary>
709 		/// <param name="loggingEvent">The event to render.</param>
710 		/// <param name="writer">The TextWriter to write the formatted event to</param>
711 		/// <remarks>
712 		/// <para>
713 		/// Helper method to render a <see cref="LoggingEvent"/> to
714 		/// a string. This appender must have a <see cref="Layout"/>
715 		/// set to render the <paramref name="loggingEvent"/> to
716 		/// a string.
717 		/// </para>
718 		/// <para>If there is exception data in the logging event and
719 		/// the layout does not process the exception, this method
720 		/// will append the exception text to the rendered string.
721 		/// </para>
722 		/// <para>
723 		/// Use this method in preference to <see cref="RenderLoggingEvent(LoggingEvent)"/>
724 		/// where possible. If, however, the caller needs to render the event
725 		/// to a string then <see cref="RenderLoggingEvent(LoggingEvent)"/> does
726 		/// provide an efficient mechanism for doing so.
727 		/// </para>
728 		/// </remarks>
RenderLoggingEvent(TextWriter writer, LoggingEvent loggingEvent)729 		protected void RenderLoggingEvent(TextWriter writer, LoggingEvent loggingEvent)
730 		{
731 			if (m_layout == null)
732 			{
733 				throw new InvalidOperationException("A layout must be set");
734 			}
735 
736 			if (m_layout.IgnoresException)
737 			{
738 				string exceptionStr = loggingEvent.GetExceptionString();
739 				if (exceptionStr != null && exceptionStr.Length > 0)
740 				{
741 					// render the event and the exception
742 					m_layout.Format(writer, loggingEvent);
743 					writer.WriteLine(exceptionStr);
744 				}
745 				else
746 				{
747 					// there is no exception to render
748 					m_layout.Format(writer, loggingEvent);
749 				}
750 			}
751 			else
752 			{
753 				// The layout will render the exception
754 				m_layout.Format(writer, loggingEvent);
755 			}
756 		}
757 
758 		/// <summary>
759 		/// Tests if this appender requires a <see cref="Layout"/> to be set.
760 		/// </summary>
761 		/// <remarks>
762 		/// <para>
763 		/// In the rather exceptional case, where the appender
764 		/// implementation admits a layout but can also work without it,
765 		/// then the appender should return <c>true</c>.
766 		/// </para>
767 		/// <para>
768 		/// This default implementation always returns <c>true</c>.
769 		/// </para>
770 		/// </remarks>
771 		/// <returns>
772 		/// <c>true</c> if the appender requires a layout object, otherwise <c>false</c>.
773 		/// </returns>
774 		virtual protected bool RequiresLayout
775 		{
776 			get { return false; }
777 		}
778 
779 		#endregion
780 
781 		#region Private Instance Fields
782 
783 		/// <summary>
784 		/// The layout of this appender.
785 		/// </summary>
786 		/// <remarks>
787 		/// See <see cref="Layout"/> for more information.
788 		/// </remarks>
789 		private ILayout m_layout;
790 
791 		/// <summary>
792 		/// The name of this appender.
793 		/// </summary>
794 		/// <remarks>
795 		/// See <see cref="Name"/> for more information.
796 		/// </remarks>
797 		private string m_name;
798 
799 		/// <summary>
800 		/// The level threshold of this appender.
801 		/// </summary>
802 		/// <remarks>
803 		/// <para>
804 		/// There is no level threshold filtering by default.
805 		/// </para>
806 		/// <para>
807 		/// See <see cref="Threshold"/> for more information.
808 		/// </para>
809 		/// </remarks>
810 		private Level m_threshold;
811 
812 		/// <summary>
813 		/// It is assumed and enforced that errorHandler is never null.
814 		/// </summary>
815 		/// <remarks>
816 		/// <para>
817 		/// It is assumed and enforced that errorHandler is never null.
818 		/// </para>
819 		/// <para>
820 		/// See <see cref="ErrorHandler"/> for more information.
821 		/// </para>
822 		/// </remarks>
823 		private IErrorHandler m_errorHandler;
824 
825 		/// <summary>
826 		/// The first filter in the filter chain.
827 		/// </summary>
828 		/// <remarks>
829 		/// <para>
830 		/// Set to <c>null</c> initially.
831 		/// </para>
832 		/// <para>
833 		/// See <see cref="IFilter"/> for more information.
834 		/// </para>
835 		/// </remarks>
836 		private IFilter m_headFilter;
837 
838 		/// <summary>
839 		/// The last filter in the filter chain.
840 		/// </summary>
841 		/// <remarks>
842 		/// See <see cref="IFilter"/> for more information.
843 		/// </remarks>
844 		private IFilter m_tailFilter;
845 
846 		/// <summary>
847 		/// Flag indicating if this appender is closed.
848 		/// </summary>
849 		/// <remarks>
850 		/// See <see cref="Close"/> for more information.
851 		/// </remarks>
852 		private bool m_closed = false;
853 
854 		/// <summary>
855 		/// The guard prevents an appender from repeatedly calling its own DoAppend method
856 		/// </summary>
857 		private bool m_recursiveGuard = false;
858 
859 		/// <summary>
860 		/// StringWriter used to render events
861 		/// </summary>
862 		private ReusableStringWriter m_renderWriter = null;
863 
864 		#endregion Private Instance Fields
865 
866 		#region Constants
867 
868 		/// <summary>
869 		/// Initial buffer size
870 		/// </summary>
871 		private const int c_renderBufferSize = 256;
872 
873 		/// <summary>
874 		/// Maximum buffer size before it is recycled
875 		/// </summary>
876 		private const int c_renderBufferMaxCapacity = 1024;
877 
878 		#endregion
879 	}
880 }
881