LoggerRepositorySkeleton.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  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.Collections;
  21. using log4net.ObjectRenderer;
  22. using log4net.Core;
  23. using log4net.Util;
  24. using log4net.Plugin;
  25. using System.Threading;
  26. namespace log4net.Repository
  27. {
  28. /// <summary>
  29. /// Base implementation of <see cref="ILoggerRepository"/>
  30. /// </summary>
  31. /// <remarks>
  32. /// <para>
  33. /// Default abstract implementation of the <see cref="ILoggerRepository"/> interface.
  34. /// </para>
  35. /// <para>
  36. /// Skeleton implementation of the <see cref="ILoggerRepository"/> interface.
  37. /// All <see cref="ILoggerRepository"/> types can extend this type.
  38. /// </para>
  39. /// </remarks>
  40. /// <author>Nicko Cadell</author>
  41. /// <author>Gert Driesen</author>
  42. public abstract class LoggerRepositorySkeleton : ILoggerRepository, Appender.IFlushable
  43. {
  44. #region Member Variables
  45. private string m_name;
  46. private RendererMap m_rendererMap;
  47. private PluginMap m_pluginMap;
  48. private LevelMap m_levelMap;
  49. private Level m_threshold;
  50. private bool m_configured;
  51. private ICollection m_configurationMessages;
  52. private event LoggerRepositoryShutdownEventHandler m_shutdownEvent;
  53. private event LoggerRepositoryConfigurationResetEventHandler m_configurationResetEvent;
  54. private event LoggerRepositoryConfigurationChangedEventHandler m_configurationChangedEvent;
  55. private PropertiesDictionary m_properties;
  56. #endregion
  57. #region Constructors
  58. /// <summary>
  59. /// Default Constructor
  60. /// </summary>
  61. /// <remarks>
  62. /// <para>
  63. /// Initializes the repository with default (empty) properties.
  64. /// </para>
  65. /// </remarks>
  66. protected LoggerRepositorySkeleton() : this(new PropertiesDictionary())
  67. {
  68. }
  69. /// <summary>
  70. /// Construct the repository using specific properties
  71. /// </summary>
  72. /// <param name="properties">the properties to set for this repository</param>
  73. /// <remarks>
  74. /// <para>
  75. /// Initializes the repository with specified properties.
  76. /// </para>
  77. /// </remarks>
  78. protected LoggerRepositorySkeleton(PropertiesDictionary properties)
  79. {
  80. m_properties = properties;
  81. m_rendererMap = new RendererMap();
  82. m_pluginMap = new PluginMap(this);
  83. m_levelMap = new LevelMap();
  84. m_configurationMessages = EmptyCollection.Instance;
  85. m_configured = false;
  86. AddBuiltinLevels();
  87. // Don't disable any levels by default.
  88. m_threshold = Level.All;
  89. }
  90. #endregion
  91. #region Implementation of ILoggerRepository
  92. /// <summary>
  93. /// The name of the repository
  94. /// </summary>
  95. /// <value>
  96. /// The string name of the repository
  97. /// </value>
  98. /// <remarks>
  99. /// <para>
  100. /// The name of this repository. The name is
  101. /// used to store and lookup the repositories
  102. /// stored by the <see cref="IRepositorySelector"/>.
  103. /// </para>
  104. /// </remarks>
  105. virtual public string Name
  106. {
  107. get { return m_name; }
  108. set { m_name = value; }
  109. }
  110. /// <summary>
  111. /// The threshold for all events in this repository
  112. /// </summary>
  113. /// <value>
  114. /// The threshold for all events in this repository
  115. /// </value>
  116. /// <remarks>
  117. /// <para>
  118. /// The threshold for all events in this repository
  119. /// </para>
  120. /// </remarks>
  121. virtual public Level Threshold
  122. {
  123. get { return m_threshold; }
  124. set
  125. {
  126. if (value != null)
  127. {
  128. m_threshold = value;
  129. }
  130. else
  131. {
  132. // Must not set threshold to null
  133. LogLog.Warn(declaringType, "LoggerRepositorySkeleton: Threshold cannot be set to null. Setting to ALL");
  134. m_threshold = Level.All;
  135. }
  136. }
  137. }
  138. /// <summary>
  139. /// RendererMap accesses the object renderer map for this repository.
  140. /// </summary>
  141. /// <value>
  142. /// RendererMap accesses the object renderer map for this repository.
  143. /// </value>
  144. /// <remarks>
  145. /// <para>
  146. /// RendererMap accesses the object renderer map for this repository.
  147. /// </para>
  148. /// <para>
  149. /// The RendererMap holds a mapping between types and
  150. /// <see cref="IObjectRenderer"/> objects.
  151. /// </para>
  152. /// </remarks>
  153. virtual public RendererMap RendererMap
  154. {
  155. get { return m_rendererMap; }
  156. }
  157. /// <summary>
  158. /// The plugin map for this repository.
  159. /// </summary>
  160. /// <value>
  161. /// The plugin map for this repository.
  162. /// </value>
  163. /// <remarks>
  164. /// <para>
  165. /// The plugin map holds the <see cref="IPlugin"/> instances
  166. /// that have been attached to this repository.
  167. /// </para>
  168. /// </remarks>
  169. virtual public PluginMap PluginMap
  170. {
  171. get { return m_pluginMap; }
  172. }
  173. /// <summary>
  174. /// Get the level map for the Repository.
  175. /// </summary>
  176. /// <remarks>
  177. /// <para>
  178. /// Get the level map for the Repository.
  179. /// </para>
  180. /// <para>
  181. /// The level map defines the mappings between
  182. /// level names and <see cref="Level"/> objects in
  183. /// this repository.
  184. /// </para>
  185. /// </remarks>
  186. virtual public LevelMap LevelMap
  187. {
  188. get { return m_levelMap; }
  189. }
  190. /// <summary>
  191. /// Test if logger exists
  192. /// </summary>
  193. /// <param name="name">The name of the logger to lookup</param>
  194. /// <returns>The Logger object with the name specified</returns>
  195. /// <remarks>
  196. /// <para>
  197. /// Check if the named logger exists in the repository. If so return
  198. /// its reference, otherwise returns <c>null</c>.
  199. /// </para>
  200. /// </remarks>
  201. abstract public ILogger Exists(string name);
  202. /// <summary>
  203. /// Returns all the currently defined loggers in the repository
  204. /// </summary>
  205. /// <returns>All the defined loggers</returns>
  206. /// <remarks>
  207. /// <para>
  208. /// Returns all the currently defined loggers in the repository as an Array.
  209. /// </para>
  210. /// </remarks>
  211. abstract public ILogger[] GetCurrentLoggers();
  212. /// <summary>
  213. /// Return a new logger instance
  214. /// </summary>
  215. /// <param name="name">The name of the logger to retrieve</param>
  216. /// <returns>The logger object with the name specified</returns>
  217. /// <remarks>
  218. /// <para>
  219. /// Return a new logger instance.
  220. /// </para>
  221. /// <para>
  222. /// If a logger of that name already exists, then it will be
  223. /// returned. Otherwise, a new logger will be instantiated and
  224. /// then linked with its existing ancestors as well as children.
  225. /// </para>
  226. /// </remarks>
  227. abstract public ILogger GetLogger(string name);
  228. /// <summary>
  229. /// Shutdown the repository
  230. /// </summary>
  231. /// <remarks>
  232. /// <para>
  233. /// Shutdown the repository. Can be overridden in a subclass.
  234. /// This base class implementation notifies the <see cref="ShutdownEvent"/>
  235. /// listeners and all attached plugins of the shutdown event.
  236. /// </para>
  237. /// </remarks>
  238. virtual public void Shutdown()
  239. {
  240. // Shutdown attached plugins
  241. foreach(IPlugin plugin in PluginMap.AllPlugins)
  242. {
  243. plugin.Shutdown();
  244. }
  245. // Notify listeners
  246. OnShutdown(null);
  247. }
  248. /// <summary>
  249. /// Reset the repositories configuration to a default state
  250. /// </summary>
  251. /// <remarks>
  252. /// <para>
  253. /// Reset all values contained in this instance to their
  254. /// default state.
  255. /// </para>
  256. /// <para>
  257. /// Existing loggers are not removed. They are just reset.
  258. /// </para>
  259. /// <para>
  260. /// This method should be used sparingly and with care as it will
  261. /// block all logging until it is completed.
  262. /// </para>
  263. /// </remarks>
  264. virtual public void ResetConfiguration()
  265. {
  266. // Clear internal data structures
  267. m_rendererMap.Clear();
  268. m_levelMap.Clear();
  269. m_configurationMessages = EmptyCollection.Instance;
  270. // Add the predefined levels to the map
  271. AddBuiltinLevels();
  272. Configured = false;
  273. // Notify listeners
  274. OnConfigurationReset(null);
  275. }
  276. /// <summary>
  277. /// Log the logEvent through this repository.
  278. /// </summary>
  279. /// <param name="logEvent">the event to log</param>
  280. /// <remarks>
  281. /// <para>
  282. /// This method should not normally be used to log.
  283. /// The <see cref="ILog"/> interface should be used
  284. /// for routine logging. This interface can be obtained
  285. /// using the <see cref="M:log4net.LogManager.GetLogger(string)"/> method.
  286. /// </para>
  287. /// <para>
  288. /// The <c>logEvent</c> is delivered to the appropriate logger and
  289. /// that logger is then responsible for logging the event.
  290. /// </para>
  291. /// </remarks>
  292. abstract public void Log(LoggingEvent logEvent);
  293. /// <summary>
  294. /// Flag indicates if this repository has been configured.
  295. /// </summary>
  296. /// <value>
  297. /// Flag indicates if this repository has been configured.
  298. /// </value>
  299. /// <remarks>
  300. /// <para>
  301. /// Flag indicates if this repository has been configured.
  302. /// </para>
  303. /// </remarks>
  304. virtual public bool Configured
  305. {
  306. get { return m_configured; }
  307. set { m_configured = value; }
  308. }
  309. /// <summary>
  310. /// Contains a list of internal messages captures during the
  311. /// last configuration.
  312. /// </summary>
  313. virtual public ICollection ConfigurationMessages
  314. {
  315. get { return m_configurationMessages; }
  316. set { m_configurationMessages = value; }
  317. }
  318. /// <summary>
  319. /// Event to notify that the repository has been shutdown.
  320. /// </summary>
  321. /// <value>
  322. /// Event to notify that the repository has been shutdown.
  323. /// </value>
  324. /// <remarks>
  325. /// <para>
  326. /// Event raised when the repository has been shutdown.
  327. /// </para>
  328. /// </remarks>
  329. public event LoggerRepositoryShutdownEventHandler ShutdownEvent
  330. {
  331. add { m_shutdownEvent += value; }
  332. remove { m_shutdownEvent -= value; }
  333. }
  334. /// <summary>
  335. /// Event to notify that the repository has had its configuration reset.
  336. /// </summary>
  337. /// <value>
  338. /// Event to notify that the repository has had its configuration reset.
  339. /// </value>
  340. /// <remarks>
  341. /// <para>
  342. /// Event raised when the repository's configuration has been
  343. /// reset to default.
  344. /// </para>
  345. /// </remarks>
  346. public event LoggerRepositoryConfigurationResetEventHandler ConfigurationReset
  347. {
  348. add { m_configurationResetEvent += value; }
  349. remove { m_configurationResetEvent -= value; }
  350. }
  351. /// <summary>
  352. /// Event to notify that the repository has had its configuration changed.
  353. /// </summary>
  354. /// <value>
  355. /// Event to notify that the repository has had its configuration changed.
  356. /// </value>
  357. /// <remarks>
  358. /// <para>
  359. /// Event raised when the repository's configuration has been changed.
  360. /// </para>
  361. /// </remarks>
  362. public event LoggerRepositoryConfigurationChangedEventHandler ConfigurationChanged
  363. {
  364. add { m_configurationChangedEvent += value; }
  365. remove { m_configurationChangedEvent -= value; }
  366. }
  367. /// <summary>
  368. /// Repository specific properties
  369. /// </summary>
  370. /// <value>
  371. /// Repository specific properties
  372. /// </value>
  373. /// <remarks>
  374. /// These properties can be specified on a repository specific basis
  375. /// </remarks>
  376. public PropertiesDictionary Properties
  377. {
  378. get { return m_properties; }
  379. }
  380. /// <summary>
  381. /// Returns all the Appenders that are configured as an Array.
  382. /// </summary>
  383. /// <returns>All the Appenders</returns>
  384. /// <remarks>
  385. /// <para>
  386. /// Returns all the Appenders that are configured as an Array.
  387. /// </para>
  388. /// </remarks>
  389. abstract public log4net.Appender.IAppender[] GetAppenders();
  390. #endregion
  391. #region Private Static Fields
  392. /// <summary>
  393. /// The fully qualified type of the LoggerRepositorySkeleton class.
  394. /// </summary>
  395. /// <remarks>
  396. /// Used by the internal logger to record the Type of the
  397. /// log message.
  398. /// </remarks>
  399. private readonly static Type declaringType = typeof(LoggerRepositorySkeleton);
  400. #endregion Private Static Fields
  401. private void AddBuiltinLevels()
  402. {
  403. // Add the predefined levels to the map
  404. m_levelMap.Add(Level.Off);
  405. // Unrecoverable errors
  406. m_levelMap.Add(Level.Emergency);
  407. m_levelMap.Add(Level.Fatal);
  408. m_levelMap.Add(Level.Alert);
  409. // Recoverable errors
  410. m_levelMap.Add(Level.Critical);
  411. m_levelMap.Add(Level.Severe);
  412. m_levelMap.Add(Level.Error);
  413. m_levelMap.Add(Level.Warn);
  414. // Information
  415. m_levelMap.Add(Level.Notice);
  416. m_levelMap.Add(Level.Info);
  417. // Debug
  418. m_levelMap.Add(Level.Debug);
  419. m_levelMap.Add(Level.Fine);
  420. m_levelMap.Add(Level.Trace);
  421. m_levelMap.Add(Level.Finer);
  422. m_levelMap.Add(Level.Verbose);
  423. m_levelMap.Add(Level.Finest);
  424. m_levelMap.Add(Level.All);
  425. }
  426. /// <summary>
  427. /// Adds an object renderer for a specific class.
  428. /// </summary>
  429. /// <param name="typeToRender">The type that will be rendered by the renderer supplied.</param>
  430. /// <param name="rendererInstance">The object renderer used to render the object.</param>
  431. /// <remarks>
  432. /// <para>
  433. /// Adds an object renderer for a specific class.
  434. /// </para>
  435. /// </remarks>
  436. virtual public void AddRenderer(Type typeToRender, IObjectRenderer rendererInstance)
  437. {
  438. if (typeToRender == null)
  439. {
  440. throw new ArgumentNullException("typeToRender");
  441. }
  442. if (rendererInstance == null)
  443. {
  444. throw new ArgumentNullException("rendererInstance");
  445. }
  446. m_rendererMap.Put(typeToRender, rendererInstance);
  447. }
  448. /// <summary>
  449. /// Notify the registered listeners that the repository is shutting down
  450. /// </summary>
  451. /// <param name="e">Empty EventArgs</param>
  452. /// <remarks>
  453. /// <para>
  454. /// Notify any listeners that this repository is shutting down.
  455. /// </para>
  456. /// </remarks>
  457. protected virtual void OnShutdown(EventArgs e)
  458. {
  459. if (e == null)
  460. {
  461. e = EventArgs.Empty;
  462. }
  463. LoggerRepositoryShutdownEventHandler handler = m_shutdownEvent;
  464. if (handler != null)
  465. {
  466. handler(this, e);
  467. }
  468. }
  469. /// <summary>
  470. /// Notify the registered listeners that the repository has had its configuration reset
  471. /// </summary>
  472. /// <param name="e">Empty EventArgs</param>
  473. /// <remarks>
  474. /// <para>
  475. /// Notify any listeners that this repository's configuration has been reset.
  476. /// </para>
  477. /// </remarks>
  478. protected virtual void OnConfigurationReset(EventArgs e)
  479. {
  480. if (e == null)
  481. {
  482. e = EventArgs.Empty;
  483. }
  484. LoggerRepositoryConfigurationResetEventHandler handler = m_configurationResetEvent;
  485. if (handler != null)
  486. {
  487. handler(this, e);
  488. }
  489. }
  490. /// <summary>
  491. /// Notify the registered listeners that the repository has had its configuration changed
  492. /// </summary>
  493. /// <param name="e">Empty EventArgs</param>
  494. /// <remarks>
  495. /// <para>
  496. /// Notify any listeners that this repository's configuration has changed.
  497. /// </para>
  498. /// </remarks>
  499. protected virtual void OnConfigurationChanged(EventArgs e)
  500. {
  501. if (e == null)
  502. {
  503. e = EventArgs.Empty;
  504. }
  505. LoggerRepositoryConfigurationChangedEventHandler handler = m_configurationChangedEvent;
  506. if (handler != null)
  507. {
  508. handler(this, e);
  509. }
  510. }
  511. /// <summary>
  512. /// Raise a configuration changed event on this repository
  513. /// </summary>
  514. /// <param name="e">EventArgs.Empty</param>
  515. /// <remarks>
  516. /// <para>
  517. /// Applications that programmatically change the configuration of the repository should
  518. /// raise this event notification to notify listeners.
  519. /// </para>
  520. /// </remarks>
  521. public void RaiseConfigurationChanged(EventArgs e)
  522. {
  523. OnConfigurationChanged(e);
  524. }
  525. private static int GetWaitTime(DateTime startTimeUtc, int millisecondsTimeout)
  526. {
  527. if (millisecondsTimeout == Timeout.Infinite) return Timeout.Infinite;
  528. if (millisecondsTimeout == 0) return 0;
  529. int elapsedMilliseconds = (int)(DateTime.UtcNow - startTimeUtc).TotalMilliseconds;
  530. int timeout = millisecondsTimeout - elapsedMilliseconds;
  531. if (timeout < 0) timeout = 0;
  532. return timeout;
  533. }
  534. /// <summary>
  535. /// Flushes all configured Appenders that implement <see cref="log4net.Appender.IFlushable"/>.
  536. /// </summary>
  537. /// <param name="millisecondsTimeout">The maximum time in milliseconds to wait for logging events from asycnhronous appenders to be flushed,
  538. /// or <see cref="Timeout.Infinite"/> to wait indefinitely.</param>
  539. /// <returns><c>True</c> if all logging events were flushed successfully, else <c>false</c>.</returns>
  540. public bool Flush(int millisecondsTimeout)
  541. {
  542. if (millisecondsTimeout < -1) throw new ArgumentOutOfRangeException("millisecondsTimeout", "Timeout must be -1 (Timeout.Infinite) or non-negative");
  543. // Assume success until one of the appenders fails
  544. bool result = true;
  545. // Use DateTime.UtcNow rather than a System.Diagnostics.Stopwatch for compatibility with .NET 1.x
  546. DateTime startTimeUtc = DateTime.UtcNow;
  547. // Do buffering appenders first. These may be forwarding to other appenders
  548. foreach(log4net.Appender.IAppender appender in GetAppenders())
  549. {
  550. log4net.Appender.IFlushable flushable = appender as log4net.Appender.IFlushable;
  551. if (flushable == null) continue;
  552. if (appender is Appender.BufferingAppenderSkeleton)
  553. {
  554. int timeout = GetWaitTime(startTimeUtc, millisecondsTimeout);
  555. if (!flushable.Flush(timeout)) result = false;
  556. }
  557. }
  558. // Do non-buffering appenders.
  559. foreach (log4net.Appender.IAppender appender in GetAppenders())
  560. {
  561. log4net.Appender.IFlushable flushable = appender as log4net.Appender.IFlushable;
  562. if (flushable == null) continue;
  563. if (!(appender is Appender.BufferingAppenderSkeleton))
  564. {
  565. int timeout = GetWaitTime(startTimeUtc, millisecondsTimeout);
  566. if (!flushable.Flush(timeout)) result = false;
  567. }
  568. }
  569. return result;
  570. }
  571. }
  572. }