Hierarchy.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  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.Appender;
  22. using log4net.Core;
  23. using log4net.Repository;
  24. using log4net.Util;
  25. namespace log4net.Repository.Hierarchy
  26. {
  27. #region LoggerCreationEvent
  28. /// <summary>
  29. /// Delegate used to handle logger creation event notifications.
  30. /// </summary>
  31. /// <param name="sender">The <see cref="Hierarchy"/> in which the <see cref="Logger"/> has been created.</param>
  32. /// <param name="e">The <see cref="LoggerCreationEventArgs"/> event args that hold the <see cref="Logger"/> instance that has been created.</param>
  33. /// <remarks>
  34. /// <para>
  35. /// Delegate used to handle logger creation event notifications.
  36. /// </para>
  37. /// </remarks>
  38. public delegate void LoggerCreationEventHandler(object sender, LoggerCreationEventArgs e);
  39. /// <summary>
  40. /// Provides data for the <see cref="Hierarchy.LoggerCreatedEvent"/> event.
  41. /// </summary>
  42. /// <remarks>
  43. /// <para>
  44. /// A <see cref="Hierarchy.LoggerCreatedEvent"/> event is raised every time a
  45. /// <see cref="Logger"/> is created.
  46. /// </para>
  47. /// </remarks>
  48. public class LoggerCreationEventArgs : EventArgs
  49. {
  50. /// <summary>
  51. /// The <see cref="Logger"/> created
  52. /// </summary>
  53. private Logger m_log;
  54. /// <summary>
  55. /// Constructor
  56. /// </summary>
  57. /// <param name="log">The <see cref="Logger"/> that has been created.</param>
  58. /// <remarks>
  59. /// <para>
  60. /// Initializes a new instance of the <see cref="LoggerCreationEventArgs" /> event argument
  61. /// class,with the specified <see cref="Logger"/>.
  62. /// </para>
  63. /// </remarks>
  64. public LoggerCreationEventArgs(Logger log)
  65. {
  66. m_log = log;
  67. }
  68. /// <summary>
  69. /// Gets the <see cref="Logger"/> that has been created.
  70. /// </summary>
  71. /// <value>
  72. /// The <see cref="Logger"/> that has been created.
  73. /// </value>
  74. /// <remarks>
  75. /// <para>
  76. /// The <see cref="Logger"/> that has been created.
  77. /// </para>
  78. /// </remarks>
  79. public Logger Logger
  80. {
  81. get { return m_log; }
  82. }
  83. }
  84. #endregion LoggerCreationEvent
  85. /// <summary>
  86. /// Hierarchical organization of loggers
  87. /// </summary>
  88. /// <remarks>
  89. /// <para>
  90. /// <i>The casual user should not have to deal with this class
  91. /// directly.</i>
  92. /// </para>
  93. /// <para>
  94. /// This class is specialized in retrieving loggers by name and
  95. /// also maintaining the logger hierarchy. Implements the
  96. /// <see cref="ILoggerRepository"/> interface.
  97. /// </para>
  98. /// <para>
  99. /// The structure of the logger hierarchy is maintained by the
  100. /// <see cref="M:GetLogger(string)"/> method. The hierarchy is such that children
  101. /// link to their parent but parents do not have any references to their
  102. /// children. Moreover, loggers can be instantiated in any order, in
  103. /// particular descendant before ancestor.
  104. /// </para>
  105. /// <para>
  106. /// In case a descendant is created before a particular ancestor,
  107. /// then it creates a provision node for the ancestor and adds itself
  108. /// to the provision node. Other descendants of the same ancestor add
  109. /// themselves to the previously created provision node.
  110. /// </para>
  111. /// </remarks>
  112. /// <author>Nicko Cadell</author>
  113. /// <author>Gert Driesen</author>
  114. public class Hierarchy : LoggerRepositorySkeleton, IBasicRepositoryConfigurator, IXmlRepositoryConfigurator
  115. {
  116. #region Public Events
  117. /// <summary>
  118. /// Event used to notify that a logger has been created.
  119. /// </summary>
  120. /// <remarks>
  121. /// <para>
  122. /// Event raised when a logger is created.
  123. /// </para>
  124. /// </remarks>
  125. public event LoggerCreationEventHandler LoggerCreatedEvent
  126. {
  127. add { m_loggerCreatedEvent += value; }
  128. remove { m_loggerCreatedEvent -= value; }
  129. }
  130. #endregion Public Events
  131. #region Public Instance Constructors
  132. /// <summary>
  133. /// Default constructor
  134. /// </summary>
  135. /// <remarks>
  136. /// <para>
  137. /// Initializes a new instance of the <see cref="Hierarchy" /> class.
  138. /// </para>
  139. /// </remarks>
  140. public Hierarchy() : this(new DefaultLoggerFactory())
  141. {
  142. }
  143. /// <summary>
  144. /// Construct with properties
  145. /// </summary>
  146. /// <param name="properties">The properties to pass to this repository.</param>
  147. /// <remarks>
  148. /// <para>
  149. /// Initializes a new instance of the <see cref="Hierarchy" /> class.
  150. /// </para>
  151. /// </remarks>
  152. public Hierarchy(PropertiesDictionary properties) : this(properties, new DefaultLoggerFactory())
  153. {
  154. }
  155. /// <summary>
  156. /// Construct with a logger factory
  157. /// </summary>
  158. /// <param name="loggerFactory">The factory to use to create new logger instances.</param>
  159. /// <remarks>
  160. /// <para>
  161. /// Initializes a new instance of the <see cref="Hierarchy" /> class with
  162. /// the specified <see cref="ILoggerFactory" />.
  163. /// </para>
  164. /// </remarks>
  165. public Hierarchy(ILoggerFactory loggerFactory) : this(new PropertiesDictionary(), loggerFactory)
  166. {
  167. }
  168. /// <summary>
  169. /// Construct with properties and a logger factory
  170. /// </summary>
  171. /// <param name="properties">The properties to pass to this repository.</param>
  172. /// <param name="loggerFactory">The factory to use to create new logger instances.</param>
  173. /// <remarks>
  174. /// <para>
  175. /// Initializes a new instance of the <see cref="Hierarchy" /> class with
  176. /// the specified <see cref="ILoggerFactory" />.
  177. /// </para>
  178. /// </remarks>
  179. public Hierarchy(PropertiesDictionary properties, ILoggerFactory loggerFactory) : base(properties)
  180. {
  181. if (loggerFactory == null)
  182. {
  183. throw new ArgumentNullException("loggerFactory");
  184. }
  185. m_defaultFactory = loggerFactory;
  186. m_ht = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable());
  187. }
  188. #endregion Public Instance Constructors
  189. #region Public Instance Properties
  190. /// <summary>
  191. /// Has no appender warning been emitted
  192. /// </summary>
  193. /// <remarks>
  194. /// <para>
  195. /// Flag to indicate if we have already issued a warning
  196. /// about not having an appender warning.
  197. /// </para>
  198. /// </remarks>
  199. public bool EmittedNoAppenderWarning
  200. {
  201. get { return m_emittedNoAppenderWarning; }
  202. set { m_emittedNoAppenderWarning = value; }
  203. }
  204. /// <summary>
  205. /// Get the root of this hierarchy
  206. /// </summary>
  207. /// <remarks>
  208. /// <para>
  209. /// Get the root of this hierarchy.
  210. /// </para>
  211. /// </remarks>
  212. public Logger Root
  213. {
  214. get
  215. {
  216. if (m_root == null)
  217. {
  218. lock(this)
  219. {
  220. if (m_root == null)
  221. {
  222. // Create the root logger
  223. Logger root = m_defaultFactory.CreateLogger(this, null);
  224. root.Hierarchy = this;
  225. // Store root
  226. m_root = root;
  227. }
  228. }
  229. }
  230. return m_root;
  231. }
  232. }
  233. /// <summary>
  234. /// Gets or sets the default <see cref="ILoggerFactory" /> instance.
  235. /// </summary>
  236. /// <value>The default <see cref="ILoggerFactory" /></value>
  237. /// <remarks>
  238. /// <para>
  239. /// The logger factory is used to create logger instances.
  240. /// </para>
  241. /// </remarks>
  242. public ILoggerFactory LoggerFactory
  243. {
  244. get { return m_defaultFactory; }
  245. set
  246. {
  247. if (value == null)
  248. {
  249. throw new ArgumentNullException("value");
  250. }
  251. m_defaultFactory = value;
  252. }
  253. }
  254. #endregion Public Instance Properties
  255. #region Override Implementation of LoggerRepositorySkeleton
  256. /// <summary>
  257. /// Test if a logger exists
  258. /// </summary>
  259. /// <param name="name">The name of the logger to lookup</param>
  260. /// <returns>The Logger object with the name specified</returns>
  261. /// <remarks>
  262. /// <para>
  263. /// Check if the named logger exists in the hierarchy. If so return
  264. /// its reference, otherwise returns <c>null</c>.
  265. /// </para>
  266. /// </remarks>
  267. override public ILogger Exists(string name)
  268. {
  269. if (name == null)
  270. {
  271. throw new ArgumentNullException("name");
  272. }
  273. lock(m_ht)
  274. {
  275. return m_ht[new LoggerKey(name)] as Logger;
  276. }
  277. }
  278. /// <summary>
  279. /// Returns all the currently defined loggers in the hierarchy as an Array
  280. /// </summary>
  281. /// <returns>All the defined loggers</returns>
  282. /// <remarks>
  283. /// <para>
  284. /// Returns all the currently defined loggers in the hierarchy as an Array.
  285. /// The root logger is <b>not</b> included in the returned
  286. /// enumeration.
  287. /// </para>
  288. /// </remarks>
  289. override public ILogger[] GetCurrentLoggers()
  290. {
  291. // The accumulation in loggers is necessary because not all elements in
  292. // ht are Logger objects as there might be some ProvisionNodes
  293. // as well.
  294. lock(m_ht)
  295. {
  296. System.Collections.ArrayList loggers = new System.Collections.ArrayList(m_ht.Count);
  297. // Iterate through m_ht values
  298. foreach(object node in m_ht.Values)
  299. {
  300. if (node is Logger)
  301. {
  302. loggers.Add(node);
  303. }
  304. }
  305. return (Logger[])loggers.ToArray(typeof(Logger));
  306. }
  307. }
  308. /// <summary>
  309. /// Return a new logger instance named as the first parameter using
  310. /// the default factory.
  311. /// </summary>
  312. /// <remarks>
  313. /// <para>
  314. /// Return a new logger instance named as the first parameter using
  315. /// the default factory.
  316. /// </para>
  317. /// <para>
  318. /// If a logger of that name already exists, then it will be
  319. /// returned. Otherwise, a new logger will be instantiated and
  320. /// then linked with its existing ancestors as well as children.
  321. /// </para>
  322. /// </remarks>
  323. /// <param name="name">The name of the logger to retrieve</param>
  324. /// <returns>The logger object with the name specified</returns>
  325. override public ILogger GetLogger(string name)
  326. {
  327. if (name == null)
  328. {
  329. throw new ArgumentNullException("name");
  330. }
  331. return GetLogger(name, m_defaultFactory);
  332. }
  333. /// <summary>
  334. /// Shutting down a hierarchy will <i>safely</i> close and remove
  335. /// all appenders in all loggers including the root logger.
  336. /// </summary>
  337. /// <remarks>
  338. /// <para>
  339. /// Shutting down a hierarchy will <i>safely</i> close and remove
  340. /// all appenders in all loggers including the root logger.
  341. /// </para>
  342. /// <para>
  343. /// Some appenders need to be closed before the
  344. /// application exists. Otherwise, pending logging events might be
  345. /// lost.
  346. /// </para>
  347. /// <para>
  348. /// The <c>Shutdown</c> method is careful to close nested
  349. /// appenders before closing regular appenders. This is allows
  350. /// configurations where a regular appender is attached to a logger
  351. /// and again to a nested appender.
  352. /// </para>
  353. /// </remarks>
  354. override public void Shutdown()
  355. {
  356. LogLog.Debug(declaringType, "Shutdown called on Hierarchy ["+this.Name+"]");
  357. // begin by closing nested appenders
  358. Root.CloseNestedAppenders();
  359. lock(m_ht)
  360. {
  361. ILogger[] currentLoggers = this.GetCurrentLoggers();
  362. foreach(Logger logger in currentLoggers)
  363. {
  364. logger.CloseNestedAppenders();
  365. }
  366. // then, remove all appenders
  367. Root.RemoveAllAppenders();
  368. foreach(Logger logger in currentLoggers)
  369. {
  370. logger.RemoveAllAppenders();
  371. }
  372. }
  373. base.Shutdown();
  374. }
  375. /// <summary>
  376. /// Reset all values contained in this hierarchy instance to their default.
  377. /// </summary>
  378. /// <remarks>
  379. /// <para>
  380. /// Reset all values contained in this hierarchy instance to their
  381. /// default. This removes all appenders from all loggers, sets
  382. /// the level of all non-root loggers to <c>null</c>,
  383. /// sets their additivity flag to <c>true</c> and sets the level
  384. /// of the root logger to <see cref="Level.Debug"/>. Moreover,
  385. /// message disabling is set its default "off" value.
  386. /// </para>
  387. /// <para>
  388. /// Existing loggers are not removed. They are just reset.
  389. /// </para>
  390. /// <para>
  391. /// This method should be used sparingly and with care as it will
  392. /// block all logging until it is completed.
  393. /// </para>
  394. /// </remarks>
  395. override public void ResetConfiguration()
  396. {
  397. Root.Level = LevelMap.LookupWithDefault(Level.Debug);
  398. Threshold = LevelMap.LookupWithDefault(Level.All);
  399. // the synchronization is needed to prevent hashtable surprises
  400. lock(m_ht)
  401. {
  402. Shutdown(); // nested locks are OK
  403. foreach(Logger l in this.GetCurrentLoggers())
  404. {
  405. l.Level = null;
  406. l.Additivity = true;
  407. }
  408. }
  409. base.ResetConfiguration();
  410. // Notify listeners
  411. OnConfigurationChanged(null);
  412. }
  413. /// <summary>
  414. /// Log the logEvent through this hierarchy.
  415. /// </summary>
  416. /// <param name="logEvent">the event to log</param>
  417. /// <remarks>
  418. /// <para>
  419. /// This method should not normally be used to log.
  420. /// The <see cref="ILog"/> interface should be used
  421. /// for routine logging. This interface can be obtained
  422. /// using the <see cref="M:log4net.LogManager.GetLogger(string)"/> method.
  423. /// </para>
  424. /// <para>
  425. /// The <c>logEvent</c> is delivered to the appropriate logger and
  426. /// that logger is then responsible for logging the event.
  427. /// </para>
  428. /// </remarks>
  429. override public void Log(LoggingEvent logEvent)
  430. {
  431. if (logEvent == null)
  432. {
  433. throw new ArgumentNullException("logEvent");
  434. }
  435. this.GetLogger(logEvent.LoggerName, m_defaultFactory).Log(logEvent);
  436. }
  437. /// <summary>
  438. /// Returns all the Appenders that are currently configured
  439. /// </summary>
  440. /// <returns>An array containing all the currently configured appenders</returns>
  441. /// <remarks>
  442. /// <para>
  443. /// Returns all the <see cref="log4net.Appender.IAppender"/> instances that are currently configured.
  444. /// All the loggers are searched for appenders. The appenders may also be containers
  445. /// for appenders and these are also searched for additional loggers.
  446. /// </para>
  447. /// <para>
  448. /// The list returned is unordered but does not contain duplicates.
  449. /// </para>
  450. /// </remarks>
  451. override public Appender.IAppender[] GetAppenders()
  452. {
  453. System.Collections.ArrayList appenderList = new System.Collections.ArrayList();
  454. CollectAppenders(appenderList, Root);
  455. foreach(Logger logger in GetCurrentLoggers())
  456. {
  457. CollectAppenders(appenderList, logger);
  458. }
  459. return (Appender.IAppender[])appenderList.ToArray(typeof(Appender.IAppender));
  460. }
  461. #endregion Override Implementation of LoggerRepositorySkeleton
  462. #region Private Static Methods
  463. /// <summary>
  464. /// Collect the appenders from an <see cref="IAppenderAttachable"/>.
  465. /// The appender may also be a container.
  466. /// </summary>
  467. /// <param name="appenderList"></param>
  468. /// <param name="appender"></param>
  469. private static void CollectAppender(System.Collections.ArrayList appenderList, Appender.IAppender appender)
  470. {
  471. if (!appenderList.Contains(appender))
  472. {
  473. appenderList.Add(appender);
  474. IAppenderAttachable container = appender as IAppenderAttachable;
  475. if (container != null)
  476. {
  477. CollectAppenders(appenderList, container);
  478. }
  479. }
  480. }
  481. /// <summary>
  482. /// Collect the appenders from an <see cref="IAppenderAttachable"/> container
  483. /// </summary>
  484. /// <param name="appenderList"></param>
  485. /// <param name="container"></param>
  486. private static void CollectAppenders(System.Collections.ArrayList appenderList, IAppenderAttachable container)
  487. {
  488. foreach(Appender.IAppender appender in container.Appenders)
  489. {
  490. CollectAppender(appenderList, appender);
  491. }
  492. }
  493. #endregion
  494. #region Implementation of IBasicRepositoryConfigurator
  495. /// <summary>
  496. /// Initialize the log4net system using the specified appender
  497. /// </summary>
  498. /// <param name="appender">the appender to use to log all logging events</param>
  499. void IBasicRepositoryConfigurator.Configure(IAppender appender)
  500. {
  501. BasicRepositoryConfigure(appender);
  502. }
  503. /// <summary>
  504. /// Initialize the log4net system using the specified appenders
  505. /// </summary>
  506. /// <param name="appenders">the appenders to use to log all logging events</param>
  507. void IBasicRepositoryConfigurator.Configure(params IAppender[] appenders)
  508. {
  509. BasicRepositoryConfigure(appenders);
  510. }
  511. /// <summary>
  512. /// Initialize the log4net system using the specified appenders
  513. /// </summary>
  514. /// <param name="appenders">the appenders to use to log all logging events</param>
  515. /// <remarks>
  516. /// <para>
  517. /// This method provides the same functionality as the
  518. /// <see cref="M:IBasicRepositoryConfigurator.Configure(IAppender)"/> method implemented
  519. /// on this object, but it is protected and therefore can be called by subclasses.
  520. /// </para>
  521. /// </remarks>
  522. protected void BasicRepositoryConfigure(params IAppender[] appenders)
  523. {
  524. ArrayList configurationMessages = new ArrayList();
  525. using (new LogLog.LogReceivedAdapter(configurationMessages))
  526. {
  527. foreach (IAppender appender in appenders)
  528. {
  529. Root.AddAppender(appender);
  530. }
  531. }
  532. Configured = true;
  533. ConfigurationMessages = configurationMessages;
  534. // Notify listeners
  535. OnConfigurationChanged(new ConfigurationChangedEventArgs(configurationMessages));
  536. }
  537. #endregion Implementation of IBasicRepositoryConfigurator
  538. #region Implementation of IXmlRepositoryConfigurator
  539. /// <summary>
  540. /// Initialize the log4net system using the specified config
  541. /// </summary>
  542. /// <param name="element">the element containing the root of the config</param>
  543. void IXmlRepositoryConfigurator.Configure(System.Xml.XmlElement element)
  544. {
  545. XmlRepositoryConfigure(element);
  546. }
  547. /// <summary>
  548. /// Initialize the log4net system using the specified config
  549. /// </summary>
  550. /// <param name="element">the element containing the root of the config</param>
  551. /// <remarks>
  552. /// <para>
  553. /// This method provides the same functionality as the
  554. /// <see cref="M:IBasicRepositoryConfigurator.Configure(IAppender)"/> method implemented
  555. /// on this object, but it is protected and therefore can be called by subclasses.
  556. /// </para>
  557. /// </remarks>
  558. protected void XmlRepositoryConfigure(System.Xml.XmlElement element)
  559. {
  560. ArrayList configurationMessages = new ArrayList();
  561. using (new LogLog.LogReceivedAdapter(configurationMessages))
  562. {
  563. XmlHierarchyConfigurator config = new XmlHierarchyConfigurator(this);
  564. config.Configure(element);
  565. }
  566. Configured = true;
  567. ConfigurationMessages = configurationMessages;
  568. // Notify listeners
  569. OnConfigurationChanged(new ConfigurationChangedEventArgs(configurationMessages));
  570. }
  571. #endregion Implementation of IXmlRepositoryConfigurator
  572. #region Public Instance Methods
  573. /// <summary>
  574. /// Test if this hierarchy is disabled for the specified <see cref="Level"/>.
  575. /// </summary>
  576. /// <param name="level">The level to check against.</param>
  577. /// <returns>
  578. /// <c>true</c> if the repository is disabled for the level argument, <c>false</c> otherwise.
  579. /// </returns>
  580. /// <remarks>
  581. /// <para>
  582. /// If this hierarchy has not been configured then this method will
  583. /// always return <c>true</c>.
  584. /// </para>
  585. /// <para>
  586. /// This method will return <c>true</c> if this repository is
  587. /// disabled for <c>level</c> object passed as parameter and
  588. /// <c>false</c> otherwise.
  589. /// </para>
  590. /// <para>
  591. /// See also the <see cref="ILoggerRepository.Threshold"/> property.
  592. /// </para>
  593. /// </remarks>
  594. public bool IsDisabled(Level level)
  595. {
  596. // Cast level to object for performance
  597. if ((object)level == null)
  598. {
  599. throw new ArgumentNullException("level");
  600. }
  601. if (Configured)
  602. {
  603. return Threshold > level;
  604. }
  605. else
  606. {
  607. // If not configured the hierarchy is effectively disabled
  608. return true;
  609. }
  610. }
  611. /// <summary>
  612. /// Clear all logger definitions from the internal hashtable
  613. /// </summary>
  614. /// <remarks>
  615. /// <para>
  616. /// This call will clear all logger definitions from the internal
  617. /// hashtable. Invoking this method will irrevocably mess up the
  618. /// logger hierarchy.
  619. /// </para>
  620. /// <para>
  621. /// You should <b>really</b> know what you are doing before
  622. /// invoking this method.
  623. /// </para>
  624. /// </remarks>
  625. public void Clear()
  626. {
  627. lock(m_ht)
  628. {
  629. m_ht.Clear();
  630. }
  631. }
  632. /// <summary>
  633. /// Return a new logger instance named as the first parameter using
  634. /// <paramref name="factory"/>.
  635. /// </summary>
  636. /// <param name="name">The name of the logger to retrieve</param>
  637. /// <param name="factory">The factory that will make the new logger instance</param>
  638. /// <returns>The logger object with the name specified</returns>
  639. /// <remarks>
  640. /// <para>
  641. /// If a logger of that name already exists, then it will be
  642. /// returned. Otherwise, a new logger will be instantiated by the
  643. /// <paramref name="factory"/> parameter and linked with its existing
  644. /// ancestors as well as children.
  645. /// </para>
  646. /// </remarks>
  647. public Logger GetLogger(string name, ILoggerFactory factory)
  648. {
  649. if (name == null)
  650. {
  651. throw new ArgumentNullException("name");
  652. }
  653. if (factory == null)
  654. {
  655. throw new ArgumentNullException("factory");
  656. }
  657. LoggerKey key = new LoggerKey(name);
  658. // Synchronize to prevent write conflicts. Read conflicts (in
  659. // GetEffectiveLevel() method) are possible only if variable
  660. // assignments are non-atomic.
  661. lock(m_ht)
  662. {
  663. Logger logger = null;
  664. Object node = m_ht[key];
  665. if (node == null)
  666. {
  667. logger = factory.CreateLogger(this, name);
  668. logger.Hierarchy = this;
  669. m_ht[key] = logger;
  670. UpdateParents(logger);
  671. OnLoggerCreationEvent(logger);
  672. return logger;
  673. }
  674. Logger nodeLogger = node as Logger;
  675. if (nodeLogger != null)
  676. {
  677. return nodeLogger;
  678. }
  679. ProvisionNode nodeProvisionNode = node as ProvisionNode;
  680. if (nodeProvisionNode != null)
  681. {
  682. logger = factory.CreateLogger(this, name);
  683. logger.Hierarchy = this;
  684. m_ht[key] = logger;
  685. UpdateChildren(nodeProvisionNode, logger);
  686. UpdateParents(logger);
  687. OnLoggerCreationEvent(logger);
  688. return logger;
  689. }
  690. // It should be impossible to arrive here but let's keep the compiler happy.
  691. return null;
  692. }
  693. }
  694. #endregion Public Instance Methods
  695. #region Protected Instance Methods
  696. /// <summary>
  697. /// Sends a logger creation event to all registered listeners
  698. /// </summary>
  699. /// <param name="logger">The newly created logger</param>
  700. /// <remarks>
  701. /// Raises the logger creation event.
  702. /// </remarks>
  703. protected virtual void OnLoggerCreationEvent(Logger logger)
  704. {
  705. LoggerCreationEventHandler handler = m_loggerCreatedEvent;
  706. if (handler != null)
  707. {
  708. handler(this, new LoggerCreationEventArgs(logger));
  709. }
  710. }
  711. #endregion Protected Instance Methods
  712. #region Private Instance Methods
  713. /// <summary>
  714. /// Updates all the parents of the specified logger
  715. /// </summary>
  716. /// <param name="log">The logger to update the parents for</param>
  717. /// <remarks>
  718. /// <para>
  719. /// This method loops through all the <i>potential</i> parents of
  720. /// <paramref name="log"/>. There 3 possible cases:
  721. /// </para>
  722. /// <list type="number">
  723. /// <item>
  724. /// <term>No entry for the potential parent of <paramref name="log"/> exists</term>
  725. /// <description>
  726. /// We create a ProvisionNode for this potential
  727. /// parent and insert <paramref name="log"/> in that provision node.
  728. /// </description>
  729. /// </item>
  730. /// <item>
  731. /// <term>The entry is of type Logger for the potential parent.</term>
  732. /// <description>
  733. /// The entry is <paramref name="log"/>'s nearest existing parent. We
  734. /// update <paramref name="log"/>'s parent field with this entry. We also break from
  735. /// he loop because updating our parent's parent is our parent's
  736. /// responsibility.
  737. /// </description>
  738. /// </item>
  739. /// <item>
  740. /// <term>The entry is of type ProvisionNode for this potential parent.</term>
  741. /// <description>
  742. /// We add <paramref name="log"/> to the list of children for this
  743. /// potential parent.
  744. /// </description>
  745. /// </item>
  746. /// </list>
  747. /// </remarks>
  748. private void UpdateParents(Logger log)
  749. {
  750. string name = log.Name;
  751. int length = name.Length;
  752. bool parentFound = false;
  753. // if name = "w.x.y.z", loop through "w.x.y", "w.x" and "w", but not "w.x.y.z"
  754. for(int i = name.LastIndexOf('.', length-1); i >= 0; i = name.LastIndexOf('.', i-1))
  755. {
  756. string substr = name.Substring(0, i);
  757. LoggerKey key = new LoggerKey(substr); // simple constructor
  758. Object node = m_ht[key];
  759. // Create a provision node for a future parent.
  760. if (node == null)
  761. {
  762. ProvisionNode pn = new ProvisionNode(log);
  763. m_ht[key] = pn;
  764. }
  765. else
  766. {
  767. Logger nodeLogger = node as Logger;
  768. if (nodeLogger != null)
  769. {
  770. parentFound = true;
  771. log.Parent = nodeLogger;
  772. break; // no need to update the ancestors of the closest ancestor
  773. }
  774. else
  775. {
  776. ProvisionNode nodeProvisionNode = node as ProvisionNode;
  777. if (nodeProvisionNode != null)
  778. {
  779. nodeProvisionNode.Add(log);
  780. }
  781. else
  782. {
  783. LogLog.Error(declaringType, "Unexpected object type ["+node.GetType()+"] in ht.", new LogException());
  784. }
  785. }
  786. }
  787. if (i == 0) {
  788. // logger name starts with a dot
  789. // and we've hit the start
  790. break;
  791. }
  792. }
  793. // If we could not find any existing parents, then link with root.
  794. if (!parentFound)
  795. {
  796. log.Parent = this.Root;
  797. }
  798. }
  799. /// <summary>
  800. /// Replace a <see cref="ProvisionNode"/> with a <see cref="Logger"/> in the hierarchy.
  801. /// </summary>
  802. /// <param name="pn"></param>
  803. /// <param name="log"></param>
  804. /// <remarks>
  805. /// <para>
  806. /// We update the links for all the children that placed themselves
  807. /// in the provision node 'pn'. The second argument 'log' is a
  808. /// reference for the newly created Logger, parent of all the
  809. /// children in 'pn'.
  810. /// </para>
  811. /// <para>
  812. /// We loop on all the children 'c' in 'pn'.
  813. /// </para>
  814. /// <para>
  815. /// If the child 'c' has been already linked to a child of
  816. /// 'log' then there is no need to update 'c'.
  817. /// </para>
  818. /// <para>
  819. /// Otherwise, we set log's parent field to c's parent and set
  820. /// c's parent field to log.
  821. /// </para>
  822. /// </remarks>
  823. private static void UpdateChildren(ProvisionNode pn, Logger log)
  824. {
  825. for(int i = 0; i < pn.Count; i++)
  826. {
  827. Logger childLogger = (Logger)pn[i];
  828. // Unless this child already points to a correct (lower) parent,
  829. // make log.Parent point to childLogger.Parent and childLogger.Parent to log.
  830. if (!childLogger.Parent.Name.StartsWith(log.Name))
  831. {
  832. log.Parent = childLogger.Parent;
  833. childLogger.Parent = log;
  834. }
  835. }
  836. }
  837. /// <summary>
  838. /// Define or redefine a Level using the values in the <see cref="LevelEntry"/> argument
  839. /// </summary>
  840. /// <param name="levelEntry">the level values</param>
  841. /// <remarks>
  842. /// <para>
  843. /// Define or redefine a Level using the values in the <see cref="LevelEntry"/> argument
  844. /// </para>
  845. /// <para>
  846. /// Supports setting levels via the configuration file.
  847. /// </para>
  848. /// </remarks>
  849. internal void AddLevel(LevelEntry levelEntry)
  850. {
  851. if (levelEntry == null) throw new ArgumentNullException("levelEntry");
  852. if (levelEntry.Name == null) throw new ArgumentNullException("levelEntry.Name");
  853. // Lookup replacement value
  854. if (levelEntry.Value == -1)
  855. {
  856. Level previousLevel = LevelMap[levelEntry.Name];
  857. if (previousLevel == null)
  858. {
  859. throw new InvalidOperationException("Cannot redefine level ["+levelEntry.Name+"] because it is not defined in the LevelMap. To define the level supply the level value.");
  860. }
  861. levelEntry.Value = previousLevel.Value;
  862. }
  863. LevelMap.Add(levelEntry.Name, levelEntry.Value, levelEntry.DisplayName);
  864. }
  865. /// <summary>
  866. /// A class to hold the value, name and display name for a level
  867. /// </summary>
  868. /// <remarks>
  869. /// <para>
  870. /// A class to hold the value, name and display name for a level
  871. /// </para>
  872. /// </remarks>
  873. internal class LevelEntry
  874. {
  875. private int m_levelValue = -1;
  876. private string m_levelName = null;
  877. private string m_levelDisplayName = null;
  878. /// <summary>
  879. /// Value of the level
  880. /// </summary>
  881. /// <remarks>
  882. /// <para>
  883. /// If the value is not set (defaults to -1) the value will be looked
  884. /// up for the current level with the same name.
  885. /// </para>
  886. /// </remarks>
  887. public int Value
  888. {
  889. get { return m_levelValue; }
  890. set { m_levelValue = value; }
  891. }
  892. /// <summary>
  893. /// Name of the level
  894. /// </summary>
  895. /// <value>
  896. /// The name of the level
  897. /// </value>
  898. /// <remarks>
  899. /// <para>
  900. /// The name of the level.
  901. /// </para>
  902. /// </remarks>
  903. public string Name
  904. {
  905. get { return m_levelName; }
  906. set { m_levelName = value; }
  907. }
  908. /// <summary>
  909. /// Display name for the level
  910. /// </summary>
  911. /// <value>
  912. /// The display name of the level
  913. /// </value>
  914. /// <remarks>
  915. /// <para>
  916. /// The display name of the level.
  917. /// </para>
  918. /// </remarks>
  919. public string DisplayName
  920. {
  921. get { return m_levelDisplayName; }
  922. set { m_levelDisplayName = value; }
  923. }
  924. /// <summary>
  925. /// Override <c>Object.ToString</c> to return sensible debug info
  926. /// </summary>
  927. /// <returns>string info about this object</returns>
  928. public override string ToString()
  929. {
  930. return "LevelEntry(Value="+m_levelValue+", Name="+m_levelName+", DisplayName="+m_levelDisplayName+")";
  931. }
  932. }
  933. /// <summary>
  934. /// Set a Property using the values in the <see cref="LevelEntry"/> argument
  935. /// </summary>
  936. /// <param name="propertyEntry">the property value</param>
  937. /// <remarks>
  938. /// <para>
  939. /// Set a Property using the values in the <see cref="LevelEntry"/> argument.
  940. /// </para>
  941. /// <para>
  942. /// Supports setting property values via the configuration file.
  943. /// </para>
  944. /// </remarks>
  945. internal void AddProperty(PropertyEntry propertyEntry)
  946. {
  947. if (propertyEntry == null) throw new ArgumentNullException("propertyEntry");
  948. if (propertyEntry.Key == null) throw new ArgumentNullException("propertyEntry.Key");
  949. Properties[propertyEntry.Key] = propertyEntry.Value;
  950. }
  951. #endregion Private Instance Methods
  952. #region Private Instance Fields
  953. private ILoggerFactory m_defaultFactory;
  954. private System.Collections.Hashtable m_ht;
  955. private Logger m_root;
  956. private bool m_emittedNoAppenderWarning = false;
  957. private event LoggerCreationEventHandler m_loggerCreatedEvent;
  958. #endregion Private Instance Fields
  959. #region Private Static Fields
  960. /// <summary>
  961. /// The fully qualified type of the Hierarchy class.
  962. /// </summary>
  963. /// <remarks>
  964. /// Used by the internal logger to record the Type of the
  965. /// log message.
  966. /// </remarks>
  967. private readonly static Type declaringType = typeof(Hierarchy);
  968. #endregion Private Static Fields
  969. }
  970. }