TextWriterAppender.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  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 log4net.Util;
  22. using log4net.Layout;
  23. using log4net.Core;
  24. namespace log4net.Appender
  25. {
  26. /// <summary>
  27. /// Sends logging events to a <see cref="TextWriter"/>.
  28. /// </summary>
  29. /// <remarks>
  30. /// <para>
  31. /// An Appender that writes to a <see cref="TextWriter"/>.
  32. /// </para>
  33. /// <para>
  34. /// This appender may be used stand alone if initialized with an appropriate
  35. /// writer, however it is typically used as a base class for an appender that
  36. /// can open a <see cref="TextWriter"/> to write to.
  37. /// </para>
  38. /// </remarks>
  39. /// <author>Nicko Cadell</author>
  40. /// <author>Gert Driesen</author>
  41. /// <author>Douglas de la Torre</author>
  42. public class TextWriterAppender : AppenderSkeleton
  43. {
  44. #region Public Instance Constructors
  45. /// <summary>
  46. /// Initializes a new instance of the <see cref="TextWriterAppender" /> class.
  47. /// </summary>
  48. /// <remarks>
  49. /// <para>
  50. /// Default constructor.
  51. /// </para>
  52. /// </remarks>
  53. public TextWriterAppender()
  54. {
  55. }
  56. /// <summary>
  57. /// Initializes a new instance of the <see cref="TextWriterAppender" /> class and
  58. /// sets the output destination to a new <see cref="StreamWriter"/> initialized
  59. /// with the specified <see cref="Stream"/>.
  60. /// </summary>
  61. /// <param name="layout">The layout to use with this appender.</param>
  62. /// <param name="os">The <see cref="Stream"/> to output to.</param>
  63. /// <remarks>
  64. /// <para>
  65. /// Obsolete constructor.
  66. /// </para>
  67. /// </remarks>
  68. [Obsolete("Instead use the default constructor and set the Layout & Writer properties")]
  69. public TextWriterAppender(ILayout layout, Stream os) : this(layout, new StreamWriter(os))
  70. {
  71. }
  72. /// <summary>
  73. /// Initializes a new instance of the <see cref="TextWriterAppender" /> class and sets
  74. /// the output destination to the specified <see cref="StreamWriter" />.
  75. /// </summary>
  76. /// <param name="layout">The layout to use with this appender</param>
  77. /// <param name="writer">The <see cref="TextWriter" /> to output to</param>
  78. /// <remarks>
  79. /// The <see cref="TextWriter" /> must have been previously opened.
  80. /// </remarks>
  81. /// <remarks>
  82. /// <para>
  83. /// Obsolete constructor.
  84. /// </para>
  85. /// </remarks>
  86. [Obsolete("Instead use the default constructor and set the Layout & Writer properties")]
  87. public TextWriterAppender(ILayout layout, TextWriter writer)
  88. {
  89. Layout = layout;
  90. Writer = writer;
  91. }
  92. #endregion
  93. #region Public Instance Properties
  94. /// <summary>
  95. /// Gets or set whether the appender will flush at the end
  96. /// of each append operation.
  97. /// </summary>
  98. /// <value>
  99. /// <para>
  100. /// The default behavior is to flush at the end of each
  101. /// append operation.
  102. /// </para>
  103. /// <para>
  104. /// If this option is set to <c>false</c>, then the underlying
  105. /// stream can defer persisting the logging event to a later
  106. /// time.
  107. /// </para>
  108. /// </value>
  109. /// <remarks>
  110. /// Avoiding the flush operation at the end of each append results in
  111. /// a performance gain of 10 to 20 percent. However, there is safety
  112. /// trade-off involved in skipping flushing. Indeed, when flushing is
  113. /// skipped, then it is likely that the last few log events will not
  114. /// be recorded on disk when the application exits. This is a high
  115. /// price to pay even for a 20% performance gain.
  116. /// </remarks>
  117. public bool ImmediateFlush
  118. {
  119. get { return m_immediateFlush; }
  120. set { m_immediateFlush = value; }
  121. }
  122. /// <summary>
  123. /// Sets the <see cref="TextWriter"/> where the log output will go.
  124. /// </summary>
  125. /// <remarks>
  126. /// <para>
  127. /// The specified <see cref="TextWriter"/> must be open and writable.
  128. /// </para>
  129. /// <para>
  130. /// The <see cref="TextWriter"/> will be closed when the appender
  131. /// instance is closed.
  132. /// </para>
  133. /// <para>
  134. /// <b>Note:</b> Logging to an unopened <see cref="TextWriter"/> will fail.
  135. /// </para>
  136. /// </remarks>
  137. virtual public TextWriter Writer
  138. {
  139. get { return m_qtw; }
  140. set
  141. {
  142. lock(this)
  143. {
  144. Reset();
  145. if (value != null)
  146. {
  147. m_qtw = new QuietTextWriter(value, ErrorHandler);
  148. WriteHeader();
  149. }
  150. }
  151. }
  152. }
  153. #endregion Public Instance Properties
  154. #region Override implementation of AppenderSkeleton
  155. /// <summary>
  156. /// This method determines if there is a sense in attempting to append.
  157. /// </summary>
  158. /// <remarks>
  159. /// <para>
  160. /// This method checks if an output target has been set and if a
  161. /// layout has been set.
  162. /// </para>
  163. /// </remarks>
  164. /// <returns><c>false</c> if any of the preconditions fail.</returns>
  165. override protected bool PreAppendCheck()
  166. {
  167. if (!base.PreAppendCheck())
  168. {
  169. return false;
  170. }
  171. if (m_qtw == null)
  172. {
  173. // Allow subclass to lazily create the writer
  174. PrepareWriter();
  175. if (m_qtw == null)
  176. {
  177. ErrorHandler.Error("No output stream or file set for the appender named ["+ Name +"].");
  178. return false;
  179. }
  180. }
  181. if (m_qtw.Closed)
  182. {
  183. ErrorHandler.Error("Output stream for appender named ["+ Name +"] has been closed.");
  184. return false;
  185. }
  186. return true;
  187. }
  188. /// <summary>
  189. /// This method is called by the <see cref="M:AppenderSkeleton.DoAppend(LoggingEvent)"/>
  190. /// method.
  191. /// </summary>
  192. /// <param name="loggingEvent">The event to log.</param>
  193. /// <remarks>
  194. /// <para>
  195. /// Writes a log statement to the output stream if the output stream exists
  196. /// and is writable.
  197. /// </para>
  198. /// <para>
  199. /// The format of the output will depend on the appender's layout.
  200. /// </para>
  201. /// </remarks>
  202. override protected void Append(LoggingEvent loggingEvent)
  203. {
  204. RenderLoggingEvent(m_qtw, loggingEvent);
  205. if (m_immediateFlush)
  206. {
  207. m_qtw.Flush();
  208. }
  209. }
  210. /// <summary>
  211. /// This method is called by the <see cref="M:AppenderSkeleton.DoAppend(LoggingEvent[])"/>
  212. /// method.
  213. /// </summary>
  214. /// <param name="loggingEvents">The array of events to log.</param>
  215. /// <remarks>
  216. /// <para>
  217. /// This method writes all the bulk logged events to the output writer
  218. /// before flushing the stream.
  219. /// </para>
  220. /// </remarks>
  221. override protected void Append(LoggingEvent[] loggingEvents)
  222. {
  223. foreach(LoggingEvent loggingEvent in loggingEvents)
  224. {
  225. RenderLoggingEvent(m_qtw, loggingEvent);
  226. }
  227. if (m_immediateFlush)
  228. {
  229. m_qtw.Flush();
  230. }
  231. }
  232. /// <summary>
  233. /// Close this appender instance. The underlying stream or writer is also closed.
  234. /// </summary>
  235. /// <remarks>
  236. /// Closed appenders cannot be reused.
  237. /// </remarks>
  238. override protected void OnClose()
  239. {
  240. lock(this)
  241. {
  242. Reset();
  243. }
  244. }
  245. /// <summary>
  246. /// Gets or set the <see cref="IErrorHandler"/> and the underlying
  247. /// <see cref="QuietTextWriter"/>, if any, for this appender.
  248. /// </summary>
  249. /// <value>
  250. /// The <see cref="IErrorHandler"/> for this appender.
  251. /// </value>
  252. override public IErrorHandler ErrorHandler
  253. {
  254. get { return base.ErrorHandler; }
  255. set
  256. {
  257. lock(this)
  258. {
  259. if (value == null)
  260. {
  261. LogLog.Warn(declaringType, "TextWriterAppender: You have tried to set a null error-handler.");
  262. }
  263. else
  264. {
  265. base.ErrorHandler = value;
  266. if (m_qtw != null)
  267. {
  268. m_qtw.ErrorHandler = value;
  269. }
  270. }
  271. }
  272. }
  273. }
  274. /// <summary>
  275. /// This appender requires a <see cref="Layout"/> to be set.
  276. /// </summary>
  277. /// <value><c>true</c></value>
  278. /// <remarks>
  279. /// <para>
  280. /// This appender requires a <see cref="Layout"/> to be set.
  281. /// </para>
  282. /// </remarks>
  283. override protected bool RequiresLayout
  284. {
  285. get { return true; }
  286. }
  287. #endregion Override implementation of AppenderSkeleton
  288. #region Protected Instance Methods
  289. /// <summary>
  290. /// Writes the footer and closes the underlying <see cref="TextWriter"/>.
  291. /// </summary>
  292. /// <remarks>
  293. /// <para>
  294. /// Writes the footer and closes the underlying <see cref="TextWriter"/>.
  295. /// </para>
  296. /// </remarks>
  297. virtual protected void WriteFooterAndCloseWriter()
  298. {
  299. WriteFooter();
  300. CloseWriter();
  301. }
  302. /// <summary>
  303. /// Closes the underlying <see cref="TextWriter"/>.
  304. /// </summary>
  305. /// <remarks>
  306. /// <para>
  307. /// Closes the underlying <see cref="TextWriter"/>.
  308. /// </para>
  309. /// </remarks>
  310. virtual protected void CloseWriter()
  311. {
  312. if (m_qtw != null)
  313. {
  314. try
  315. {
  316. m_qtw.Close();
  317. }
  318. catch(Exception e)
  319. {
  320. ErrorHandler.Error("Could not close writer ["+m_qtw+"]", e);
  321. // do need to invoke an error handler
  322. // at this late stage
  323. }
  324. }
  325. }
  326. /// <summary>
  327. /// Clears internal references to the underlying <see cref="TextWriter" />
  328. /// and other variables.
  329. /// </summary>
  330. /// <remarks>
  331. /// <para>
  332. /// Subclasses can override this method for an alternate closing behavior.
  333. /// </para>
  334. /// </remarks>
  335. virtual protected void Reset()
  336. {
  337. WriteFooterAndCloseWriter();
  338. m_qtw = null;
  339. }
  340. /// <summary>
  341. /// Writes a footer as produced by the embedded layout's <see cref="ILayout.Footer"/> property.
  342. /// </summary>
  343. /// <remarks>
  344. /// <para>
  345. /// Writes a footer as produced by the embedded layout's <see cref="ILayout.Footer"/> property.
  346. /// </para>
  347. /// </remarks>
  348. virtual protected void WriteFooter()
  349. {
  350. if (Layout != null && m_qtw != null && !m_qtw.Closed)
  351. {
  352. string f = Layout.Footer;
  353. if (f != null)
  354. {
  355. m_qtw.Write(f);
  356. }
  357. }
  358. }
  359. /// <summary>
  360. /// Writes a header produced by the embedded layout's <see cref="ILayout.Header"/> property.
  361. /// </summary>
  362. /// <remarks>
  363. /// <para>
  364. /// Writes a header produced by the embedded layout's <see cref="ILayout.Header"/> property.
  365. /// </para>
  366. /// </remarks>
  367. virtual protected void WriteHeader()
  368. {
  369. if (Layout != null && m_qtw != null && !m_qtw.Closed)
  370. {
  371. string h = Layout.Header;
  372. if (h != null)
  373. {
  374. m_qtw.Write(h);
  375. }
  376. }
  377. }
  378. /// <summary>
  379. /// Called to allow a subclass to lazily initialize the writer
  380. /// </summary>
  381. /// <remarks>
  382. /// <para>
  383. /// This method is called when an event is logged and the <see cref="Writer"/> or
  384. /// <see cref="QuietWriter"/> have not been set. This allows a subclass to
  385. /// attempt to initialize the writer multiple times.
  386. /// </para>
  387. /// </remarks>
  388. virtual protected void PrepareWriter()
  389. {
  390. }
  391. /// <summary>
  392. /// Gets or sets the <see cref="log4net.Util.QuietTextWriter"/> where logging events
  393. /// will be written to.
  394. /// </summary>
  395. /// <value>
  396. /// The <see cref="log4net.Util.QuietTextWriter"/> where logging events are written.
  397. /// </value>
  398. /// <remarks>
  399. /// <para>
  400. /// This is the <see cref="log4net.Util.QuietTextWriter"/> where logging events
  401. /// will be written to.
  402. /// </para>
  403. /// </remarks>
  404. protected QuietTextWriter QuietWriter
  405. {
  406. get { return m_qtw; }
  407. set { m_qtw = value; }
  408. }
  409. #endregion Protected Instance Methods
  410. #region Private Instance Fields
  411. /// <summary>
  412. /// This is the <see cref="log4net.Util.QuietTextWriter"/> where logging events
  413. /// will be written to.
  414. /// </summary>
  415. private QuietTextWriter m_qtw;
  416. /// <summary>
  417. /// Immediate flush means that the underlying <see cref="TextWriter" />
  418. /// or output stream will be flushed at the end of each append operation.
  419. /// </summary>
  420. /// <remarks>
  421. /// <para>
  422. /// Immediate flush is slower but ensures that each append request is
  423. /// actually written. If <see cref="ImmediateFlush"/> is set to
  424. /// <c>false</c>, then there is a good chance that the last few
  425. /// logging events are not actually persisted if and when the application
  426. /// crashes.
  427. /// </para>
  428. /// <para>
  429. /// The default value is <c>true</c>.
  430. /// </para>
  431. /// </remarks>
  432. private bool m_immediateFlush = true;
  433. #endregion Private Instance Fields
  434. #region Private Static Fields
  435. /// <summary>
  436. /// The fully qualified type of the TextWriterAppender class.
  437. /// </summary>
  438. /// <remarks>
  439. /// Used by the internal logger to record the Type of the
  440. /// log message.
  441. /// </remarks>
  442. private readonly static Type declaringType = typeof(TextWriterAppender);
  443. #endregion Private Static Fields
  444. /// <summary>
  445. /// Flushes any buffered log data.
  446. /// </summary>
  447. /// <param name="millisecondsTimeout">The maximum time to wait for logging events to be flushed.</param>
  448. /// <returns><c>True</c> if all logging events were flushed successfully, else <c>false</c>.</returns>
  449. public override bool Flush(int millisecondsTimeout)
  450. {
  451. // Nothing to do if ImmediateFlush is true
  452. if (m_immediateFlush) return true;
  453. // lock(this) will block any Appends while the buffer is flushed.
  454. lock (this)
  455. {
  456. m_qtw.Flush();
  457. }
  458. return true;
  459. }
  460. }
  461. }