LocalSyslogAppender.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  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. // .NET Compact Framework 1.0 has no support for Marshal.StringToHGlobalAnsi
  20. // SSCLI 1.0 has no support for Marshal.StringToHGlobalAnsi
  21. #if !NETCF && !SSCLI
  22. using System;
  23. using System.Runtime.InteropServices;
  24. using log4net.Core;
  25. using log4net.Appender;
  26. using log4net.Util;
  27. using log4net.Layout;
  28. namespace log4net.Appender
  29. {
  30. /// <summary>
  31. /// Logs events to a local syslog service.
  32. /// </summary>
  33. /// <remarks>
  34. /// <note>
  35. /// This appender uses the POSIX libc library functions <c>openlog</c>, <c>syslog</c>, and <c>closelog</c>.
  36. /// If these functions are not available on the local system then this appender will not work!
  37. /// </note>
  38. /// <para>
  39. /// The functions <c>openlog</c>, <c>syslog</c>, and <c>closelog</c> are specified in SUSv2 and
  40. /// POSIX 1003.1-2001 standards. These are used to log messages to the local syslog service.
  41. /// </para>
  42. /// <para>
  43. /// This appender talks to a local syslog service. If you need to log to a remote syslog
  44. /// daemon and you cannot configure your local syslog service to do this you may be
  45. /// able to use the <see cref="RemoteSyslogAppender"/> to log via UDP.
  46. /// </para>
  47. /// <para>
  48. /// Syslog messages must have a facility and and a severity. The severity
  49. /// is derived from the Level of the logging event.
  50. /// The facility must be chosen from the set of defined syslog
  51. /// <see cref="SyslogFacility"/> values. The facilities list is predefined
  52. /// and cannot be extended.
  53. /// </para>
  54. /// <para>
  55. /// An identifier is specified with each log message. This can be specified
  56. /// by setting the <see cref="Identity"/> property. The identity (also know
  57. /// as the tag) must not contain white space. The default value for the
  58. /// identity is the application name (from <see cref="SystemInfo.ApplicationFriendlyName"/>).
  59. /// </para>
  60. /// </remarks>
  61. /// <author>Rob Lyon</author>
  62. /// <author>Nicko Cadell</author>
  63. public class LocalSyslogAppender : AppenderSkeleton
  64. {
  65. #region Enumerations
  66. /// <summary>
  67. /// syslog severities
  68. /// </summary>
  69. /// <remarks>
  70. /// <para>
  71. /// The log4net Level maps to a syslog severity using the
  72. /// <see cref="LocalSyslogAppender.AddMapping"/> method and the <see cref="LevelSeverity"/>
  73. /// class. The severity is set on <see cref="LevelSeverity.Severity"/>.
  74. /// </para>
  75. /// </remarks>
  76. public enum SyslogSeverity
  77. {
  78. /// <summary>
  79. /// system is unusable
  80. /// </summary>
  81. Emergency = 0,
  82. /// <summary>
  83. /// action must be taken immediately
  84. /// </summary>
  85. Alert = 1,
  86. /// <summary>
  87. /// critical conditions
  88. /// </summary>
  89. Critical = 2,
  90. /// <summary>
  91. /// error conditions
  92. /// </summary>
  93. Error = 3,
  94. /// <summary>
  95. /// warning conditions
  96. /// </summary>
  97. Warning = 4,
  98. /// <summary>
  99. /// normal but significant condition
  100. /// </summary>
  101. Notice = 5,
  102. /// <summary>
  103. /// informational
  104. /// </summary>
  105. Informational = 6,
  106. /// <summary>
  107. /// debug-level messages
  108. /// </summary>
  109. Debug = 7
  110. };
  111. /// <summary>
  112. /// syslog facilities
  113. /// </summary>
  114. /// <remarks>
  115. /// <para>
  116. /// The syslog facility defines which subsystem the logging comes from.
  117. /// This is set on the <see cref="Facility"/> property.
  118. /// </para>
  119. /// </remarks>
  120. public enum SyslogFacility
  121. {
  122. /// <summary>
  123. /// kernel messages
  124. /// </summary>
  125. Kernel = 0,
  126. /// <summary>
  127. /// random user-level messages
  128. /// </summary>
  129. User = 1,
  130. /// <summary>
  131. /// mail system
  132. /// </summary>
  133. Mail = 2,
  134. /// <summary>
  135. /// system daemons
  136. /// </summary>
  137. Daemons = 3,
  138. /// <summary>
  139. /// security/authorization messages
  140. /// </summary>
  141. Authorization = 4,
  142. /// <summary>
  143. /// messages generated internally by syslogd
  144. /// </summary>
  145. Syslog = 5,
  146. /// <summary>
  147. /// line printer subsystem
  148. /// </summary>
  149. Printer = 6,
  150. /// <summary>
  151. /// network news subsystem
  152. /// </summary>
  153. News = 7,
  154. /// <summary>
  155. /// UUCP subsystem
  156. /// </summary>
  157. Uucp = 8,
  158. /// <summary>
  159. /// clock (cron/at) daemon
  160. /// </summary>
  161. Clock = 9,
  162. /// <summary>
  163. /// security/authorization messages (private)
  164. /// </summary>
  165. Authorization2 = 10,
  166. /// <summary>
  167. /// ftp daemon
  168. /// </summary>
  169. Ftp = 11,
  170. /// <summary>
  171. /// NTP subsystem
  172. /// </summary>
  173. Ntp = 12,
  174. /// <summary>
  175. /// log audit
  176. /// </summary>
  177. Audit = 13,
  178. /// <summary>
  179. /// log alert
  180. /// </summary>
  181. Alert = 14,
  182. /// <summary>
  183. /// clock daemon
  184. /// </summary>
  185. Clock2 = 15,
  186. /// <summary>
  187. /// reserved for local use
  188. /// </summary>
  189. Local0 = 16,
  190. /// <summary>
  191. /// reserved for local use
  192. /// </summary>
  193. Local1 = 17,
  194. /// <summary>
  195. /// reserved for local use
  196. /// </summary>
  197. Local2 = 18,
  198. /// <summary>
  199. /// reserved for local use
  200. /// </summary>
  201. Local3 = 19,
  202. /// <summary>
  203. /// reserved for local use
  204. /// </summary>
  205. Local4 = 20,
  206. /// <summary>
  207. /// reserved for local use
  208. /// </summary>
  209. Local5 = 21,
  210. /// <summary>
  211. /// reserved for local use
  212. /// </summary>
  213. Local6 = 22,
  214. /// <summary>
  215. /// reserved for local use
  216. /// </summary>
  217. Local7 = 23
  218. }
  219. #endregion // Enumerations
  220. #region Public Instance Constructors
  221. /// <summary>
  222. /// Initializes a new instance of the <see cref="LocalSyslogAppender" /> class.
  223. /// </summary>
  224. /// <remarks>
  225. /// This instance of the <see cref="LocalSyslogAppender" /> class is set up to write
  226. /// to a local syslog service.
  227. /// </remarks>
  228. public LocalSyslogAppender()
  229. {
  230. }
  231. #endregion // Public Instance Constructors
  232. #region Public Instance Properties
  233. /// <summary>
  234. /// Message identity
  235. /// </summary>
  236. /// <remarks>
  237. /// <para>
  238. /// An identifier is specified with each log message. This can be specified
  239. /// by setting the <see cref="Identity"/> property. The identity (also know
  240. /// as the tag) must not contain white space. The default value for the
  241. /// identity is the application name (from <see cref="SystemInfo.ApplicationFriendlyName"/>).
  242. /// </para>
  243. /// </remarks>
  244. public string Identity
  245. {
  246. get { return m_identity; }
  247. set { m_identity = value; }
  248. }
  249. /// <summary>
  250. /// Syslog facility
  251. /// </summary>
  252. /// <remarks>
  253. /// Set to one of the <see cref="SyslogFacility"/> values. The list of
  254. /// facilities is predefined and cannot be extended. The default value
  255. /// is <see cref="SyslogFacility.User"/>.
  256. /// </remarks>
  257. public SyslogFacility Facility
  258. {
  259. get { return m_facility; }
  260. set { m_facility = value; }
  261. }
  262. #endregion // Public Instance Properties
  263. /// <summary>
  264. /// Add a mapping of level to severity
  265. /// </summary>
  266. /// <param name="mapping">The mapping to add</param>
  267. /// <remarks>
  268. /// <para>
  269. /// Adds a <see cref="LevelSeverity"/> to this appender.
  270. /// </para>
  271. /// </remarks>
  272. public void AddMapping(LevelSeverity mapping)
  273. {
  274. m_levelMapping.Add(mapping);
  275. }
  276. #region IOptionHandler Implementation
  277. /// <summary>
  278. /// Initialize the appender based on the options set.
  279. /// </summary>
  280. /// <remarks>
  281. /// <para>
  282. /// This is part of the <see cref="IOptionHandler"/> delayed object
  283. /// activation scheme. The <see cref="ActivateOptions"/> method must
  284. /// be called on this object after the configuration properties have
  285. /// been set. Until <see cref="ActivateOptions"/> is called this
  286. /// object is in an undefined state and must not be used.
  287. /// </para>
  288. /// <para>
  289. /// If any of the configuration properties are modified then
  290. /// <see cref="ActivateOptions"/> must be called again.
  291. /// </para>
  292. /// </remarks>
  293. #if NET_4_0 || MONO_4_0 || NETSTANDARD1_3
  294. [System.Security.SecuritySafeCritical]
  295. #endif
  296. public override void ActivateOptions()
  297. {
  298. base.ActivateOptions();
  299. m_levelMapping.ActivateOptions();
  300. string identString = m_identity;
  301. if (identString == null)
  302. {
  303. // Set to app name by default
  304. identString = SystemInfo.ApplicationFriendlyName;
  305. }
  306. // create the native heap ansi string. Note this is a copy of our string
  307. // so we do not need to hold on to the string itself, holding on to the
  308. // handle will keep the heap ansi string alive.
  309. m_handleToIdentity = Marshal.StringToHGlobalAnsi(identString);
  310. // open syslog
  311. openlog(m_handleToIdentity, 1, m_facility);
  312. }
  313. #endregion // IOptionHandler Implementation
  314. #region AppenderSkeleton Implementation
  315. /// <summary>
  316. /// This method is called by the <see cref="M:AppenderSkeleton.DoAppend(LoggingEvent)"/> method.
  317. /// </summary>
  318. /// <param name="loggingEvent">The event to log.</param>
  319. /// <remarks>
  320. /// <para>
  321. /// Writes the event to a remote syslog daemon.
  322. /// </para>
  323. /// <para>
  324. /// The format of the output will depend on the appender's layout.
  325. /// </para>
  326. /// </remarks>
  327. #if NET_4_0 || MONO_4_0 || NETSTANDARD1_3
  328. [System.Security.SecuritySafeCritical]
  329. #endif
  330. #if !NETSTANDARD1_3
  331. [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, UnmanagedCode = true)]
  332. #endif
  333. protected override void Append(LoggingEvent loggingEvent)
  334. {
  335. int priority = GeneratePriority(m_facility, GetSeverity(loggingEvent.Level));
  336. string message = RenderLoggingEvent(loggingEvent);
  337. // Call the local libc syslog method
  338. // The second argument is a printf style format string
  339. syslog(priority, "%s", message);
  340. }
  341. /// <summary>
  342. /// Close the syslog when the appender is closed
  343. /// </summary>
  344. /// <remarks>
  345. /// <para>
  346. /// Close the syslog when the appender is closed
  347. /// </para>
  348. /// </remarks>
  349. #if NET_4_0 || MONO_4_0 || NETSTANDARD1_3
  350. [System.Security.SecuritySafeCritical]
  351. #endif
  352. protected override void OnClose()
  353. {
  354. base.OnClose();
  355. try
  356. {
  357. // close syslog
  358. closelog();
  359. }
  360. catch(DllNotFoundException)
  361. {
  362. // Ignore dll not found at this point
  363. }
  364. if (m_handleToIdentity != IntPtr.Zero)
  365. {
  366. // free global ident
  367. Marshal.FreeHGlobal(m_handleToIdentity);
  368. }
  369. }
  370. /// <summary>
  371. /// This appender requires a <see cref="AppenderSkeleton.Layout"/> to be set.
  372. /// </summary>
  373. /// <value><c>true</c></value>
  374. /// <remarks>
  375. /// <para>
  376. /// This appender requires a <see cref="AppenderSkeleton.Layout"/> to be set.
  377. /// </para>
  378. /// </remarks>
  379. override protected bool RequiresLayout
  380. {
  381. get { return true; }
  382. }
  383. #endregion // AppenderSkeleton Implementation
  384. #region Protected Members
  385. /// <summary>
  386. /// Translates a log4net level to a syslog severity.
  387. /// </summary>
  388. /// <param name="level">A log4net level.</param>
  389. /// <returns>A syslog severity.</returns>
  390. /// <remarks>
  391. /// <para>
  392. /// Translates a log4net level to a syslog severity.
  393. /// </para>
  394. /// </remarks>
  395. virtual protected SyslogSeverity GetSeverity(Level level)
  396. {
  397. LevelSeverity levelSeverity = m_levelMapping.Lookup(level) as LevelSeverity;
  398. if (levelSeverity != null)
  399. {
  400. return levelSeverity.Severity;
  401. }
  402. //
  403. // Fallback to sensible default values
  404. //
  405. if (level >= Level.Alert)
  406. {
  407. return SyslogSeverity.Alert;
  408. }
  409. else if (level >= Level.Critical)
  410. {
  411. return SyslogSeverity.Critical;
  412. }
  413. else if (level >= Level.Error)
  414. {
  415. return SyslogSeverity.Error;
  416. }
  417. else if (level >= Level.Warn)
  418. {
  419. return SyslogSeverity.Warning;
  420. }
  421. else if (level >= Level.Notice)
  422. {
  423. return SyslogSeverity.Notice;
  424. }
  425. else if (level >= Level.Info)
  426. {
  427. return SyslogSeverity.Informational;
  428. }
  429. // Default setting
  430. return SyslogSeverity.Debug;
  431. }
  432. #endregion // Protected Members
  433. #region Public Static Members
  434. /// <summary>
  435. /// Generate a syslog priority.
  436. /// </summary>
  437. /// <param name="facility">The syslog facility.</param>
  438. /// <param name="severity">The syslog severity.</param>
  439. /// <returns>A syslog priority.</returns>
  440. private static int GeneratePriority(SyslogFacility facility, SyslogSeverity severity)
  441. {
  442. return ((int)facility * 8) + (int)severity;
  443. }
  444. #endregion // Public Static Members
  445. #region Private Instances Fields
  446. /// <summary>
  447. /// The facility. The default facility is <see cref="SyslogFacility.User"/>.
  448. /// </summary>
  449. private SyslogFacility m_facility = SyslogFacility.User;
  450. /// <summary>
  451. /// The message identity
  452. /// </summary>
  453. private string m_identity;
  454. /// <summary>
  455. /// Marshaled handle to the identity string. We have to hold on to the
  456. /// string as the <c>openlog</c> and <c>syslog</c> APIs just hold the
  457. /// pointer to the ident and dereference it for each log message.
  458. /// </summary>
  459. private IntPtr m_handleToIdentity = IntPtr.Zero;
  460. /// <summary>
  461. /// Mapping from level object to syslog severity
  462. /// </summary>
  463. private LevelMapping m_levelMapping = new LevelMapping();
  464. #endregion // Private Instances Fields
  465. #region External Members
  466. /// <summary>
  467. /// Open connection to system logger.
  468. /// </summary>
  469. [DllImport("libc")]
  470. private static extern void openlog(IntPtr ident, int option, SyslogFacility facility);
  471. /// <summary>
  472. /// Generate a log message.
  473. /// </summary>
  474. /// <remarks>
  475. /// <para>
  476. /// The libc syslog method takes a format string and a variable argument list similar
  477. /// to the classic printf function. As this type of vararg list is not supported
  478. /// by C# we need to specify the arguments explicitly. Here we have specified the
  479. /// format string with a single message argument. The caller must set the format
  480. /// string to <c>"%s"</c>.
  481. /// </para>
  482. /// </remarks>
  483. [DllImport("libc", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
  484. private static extern void syslog(int priority, string format, string message);
  485. /// <summary>
  486. /// Close descriptor used to write to system logger.
  487. /// </summary>
  488. [DllImport("libc")]
  489. private static extern void closelog();
  490. #endregion // External Members
  491. #region LevelSeverity LevelMapping Entry
  492. /// <summary>
  493. /// A class to act as a mapping between the level that a logging call is made at and
  494. /// the syslog severity that is should be logged at.
  495. /// </summary>
  496. /// <remarks>
  497. /// <para>
  498. /// A class to act as a mapping between the level that a logging call is made at and
  499. /// the syslog severity that is should be logged at.
  500. /// </para>
  501. /// </remarks>
  502. public class LevelSeverity : LevelMappingEntry
  503. {
  504. private SyslogSeverity m_severity;
  505. /// <summary>
  506. /// The mapped syslog severity for the specified level
  507. /// </summary>
  508. /// <remarks>
  509. /// <para>
  510. /// Required property.
  511. /// The mapped syslog severity for the specified level
  512. /// </para>
  513. /// </remarks>
  514. public SyslogSeverity Severity
  515. {
  516. get { return m_severity; }
  517. set { m_severity = value; }
  518. }
  519. }
  520. #endregion // LevelSeverity LevelMapping Entry
  521. }
  522. }
  523. #endif