AppenderSkeleton.cs 25 KB

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