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