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