2 // System.Diagnostics.EventLog.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 // Gert Driesen (drieseng@users.sourceforge.net)
10 // Copyright (C) 2003 Andreas Nahr
11 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Diagnostics;
36 using System.Collections;
37 using System.ComponentModel;
38 using System.ComponentModel.Design;
39 using System.Globalization;
41 using System.Runtime.InteropServices;
43 namespace System.Diagnostics
45 [DefaultEvent ("EntryWritten")]
46 [InstallerType (typeof (EventLogInstaller))]
47 [MonitoringDescription ("Represents an event log")]
48 public class EventLog : Component, ISupportInitialize
50 private string source;
51 private string logName;
52 private string machineName;
53 private bool doRaiseEvents = false;
54 private ISynchronizeInvoke synchronizingObject = null;
56 // IMPORTANT: also update constants in EventLogTest
57 internal const string LOCAL_FILE_IMPL = "local";
58 private const string WIN32_IMPL = "win32";
59 private const string NULL_IMPL = "null";
61 internal const string EVENTLOG_TYPE_VAR = "MONO_EVENTLOG_TYPE";
63 private EventLogImpl Impl;
65 public EventLog() : this (string.Empty)
69 public EventLog(string logName) : this (logName, ".")
73 public EventLog(string logName, string machineName)
74 : this (logName, machineName, string.Empty)
78 public EventLog(string logName, string machineName, string source)
80 if (logName == null) {
81 throw new ArgumentNullException ("logName");
83 if (machineName == null || machineName.Trim ().Length == 0)
84 throw new ArgumentException (string.Format (
85 CultureInfo.InvariantCulture, "Invalid value '{0}' for"
86 + " parameter 'machineName'.", machineName));
89 this.machineName = machineName;
90 this.logName = logName;
92 Impl = CreateEventLogImpl (this);
95 [Browsable (false), DefaultValue (false)]
96 [MonitoringDescription ("If enabled raises event when a log is written.")]
97 public bool EnableRaisingEvents {
98 get {return doRaiseEvents;}
100 if (value == doRaiseEvents)
104 Impl.EnableNotification ();
106 Impl.DisableNotification ();
107 doRaiseEvents = value;
111 [Browsable (false), DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
112 [MonitoringDescription ("The entries in the log.")]
113 public EventLogEntryCollection Entries {
114 get {return new EventLogEntryCollection(Impl);}
117 [ReadOnly (true), DefaultValue (""), RecommendedAsConfigurable (true)]
118 [TypeConverter ("System.Diagnostics.Design.LogConverter, " + Consts.AssemblySystem_Design)]
119 [MonitoringDescription ("Name of the log that is read and written.")]
122 if (source != null && source.Length > 0)
123 return GetLogName ();
128 throw new ArgumentNullException ("value");
130 // log name is treated case-insensitively on all platforms
131 if (string.Compare (logName, value, true) != 0) {
139 public string LogDisplayName {
140 get { return Impl.LogDisplayName; }
143 [ReadOnly (true), DefaultValue ("."), RecommendedAsConfigurable (true)]
144 [MonitoringDescription ("Name of the machine that this log get written to.")]
145 public string MachineName {
146 get { return machineName; }
148 if (value == null || value.Trim ().Length == 0)
149 throw new ArgumentException (string.Format (
150 CultureInfo.InvariantCulture, "Invalid value {0} for"
151 + " property MachineName.", value));
153 if (string.Compare (machineName, value, true) != 0) {
160 [ReadOnly (true), DefaultValue (""), RecommendedAsConfigurable (true)]
161 [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
162 [MonitoringDescription ("The application name that writes the log.")]
163 public string Source {
164 get { return source; }
167 value = string.Empty;
169 // Source only affects eventlog implementation if Source was set
170 // and no Log was set
171 if (source == null || source.Length == 0 && (logName == null ||logName.Length == 0)) {
173 } else if (string.Compare (source, value, true) != 0) {
180 [Browsable (false), DefaultValue (null)]
181 [MonitoringDescription ("An object that synchronizes event handler calls.")]
182 public ISynchronizeInvoke SynchronizingObject {
183 get {return synchronizingObject;}
184 set {synchronizingObject = value;}
188 [ComVisibleAttribute (false)]
190 public OverflowAction OverflowAction {
191 get { return Impl.OverflowAction; }
195 [ComVisibleAttribute (false)]
197 public int MinimumRetentionDays {
198 get { return Impl.MinimumRetentionDays; }
202 [ComVisibleAttribute (false)]
204 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
205 public long MaximumKilobytes {
206 get { return Impl.MaximumKilobytes; }
207 set { Impl.MaximumKilobytes = value; }
212 public void ModifyOverflowPolicy (OverflowAction action, int retentionDays)
214 Impl.ModifyOverflowPolicy (action, retentionDays);
218 [ComVisibleAttribute (false)]
219 public void RegisterDisplayName (string resourceFile, long resourceId)
221 Impl.RegisterDisplayName (resourceFile, resourceId);
224 public void BeginInit ()
231 string logName = Log;
232 if (logName == null || logName.Length == 0)
233 throw new ArgumentException ("Log property value has not been specified.");
235 if (!EventLog.Exists (logName, MachineName))
236 throw new InvalidOperationException (string.Format (
237 CultureInfo.InvariantCulture, "Event Log '{0}'"
238 + " does not exist on computer '{1}'.", logName,
248 EnableRaisingEvents = false;
251 internal void Reset ()
253 bool enableRaisingEvents = EnableRaisingEvents;
255 EnableRaisingEvents = enableRaisingEvents;
258 public static void CreateEventSource (string source, string logName)
260 CreateEventSource (source, logName, ".");
263 [Obsolete ("use CreateEventSource(EventSourceCreationData) instead")]
264 public static void CreateEventSource (string source,
268 CreateEventSource (new EventSourceCreationData (source, logName,
272 [MonoNotSupported ("remote machine is not supported")]
273 public static void CreateEventSource (EventSourceCreationData sourceData)
275 if (sourceData.Source == null || sourceData.Source.Length == 0)
276 throw new ArgumentException ("Source property value has not been specified.");
278 if (sourceData.LogName == null || sourceData.LogName.Length == 0)
279 throw new ArgumentException ("Log property value has not been specified.");
281 if (SourceExists (sourceData.Source, sourceData.MachineName))
282 throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
283 "Source '{0}' already exists on '{1}'.", sourceData.Source,
284 sourceData.MachineName));
286 EventLogImpl impl = CreateEventLogImpl (sourceData.LogName,
287 sourceData.MachineName, sourceData.Source);
288 impl.CreateEventSource (sourceData);
291 public static void Delete (string logName)
293 Delete (logName, ".");
296 [MonoNotSupported ("remote machine is not supported")]
297 public static void Delete (string logName, string machineName)
299 if (machineName == null || machineName.Trim ().Length == 0)
300 throw new ArgumentException ("Invalid format for argument"
303 if (logName == null || logName.Length == 0)
304 throw new ArgumentException ("Log to delete was not specified.");
306 EventLogImpl impl = CreateEventLogImpl (logName, machineName,
308 impl.Delete (logName, machineName);
311 public static void DeleteEventSource (string source)
313 DeleteEventSource (source, ".");
316 [MonoNotSupported ("remote machine is not supported")]
317 public static void DeleteEventSource (string source, string machineName)
319 if (machineName == null || machineName.Trim ().Length == 0)
320 throw new ArgumentException (string.Format (
321 CultureInfo.InvariantCulture, "Invalid value '{0}' for"
322 + " parameter 'machineName'.", machineName));
324 EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
326 impl.DeleteEventSource (source, machineName);
329 protected override void Dispose (bool disposing)
332 Impl.Dispose (disposing);
335 public void EndInit()
340 public static bool Exists (string logName)
342 return Exists (logName, ".");
345 [MonoNotSupported ("remote machine is not supported")]
346 public static bool Exists (string logName, string machineName)
348 if (machineName == null || machineName.Trim ().Length == 0)
349 throw new ArgumentException ("Invalid format for argument machineName.");
351 if (logName == null || logName.Length == 0)
354 EventLogImpl impl = CreateEventLogImpl (logName, machineName,
356 return impl.Exists (logName, machineName);
359 public static EventLog[] GetEventLogs ()
361 return GetEventLogs (".");
364 [MonoNotSupported ("remote machine is not supported")]
365 public static EventLog[] GetEventLogs (string machineName)
367 EventLogImpl impl = CreateEventLogImpl (new EventLog ());
368 return impl.GetEventLogs (machineName);
371 [MonoNotSupported ("remote machine is not supported")]
372 public static string LogNameFromSourceName (string source, string machineName)
374 if (machineName == null || machineName.Trim ().Length == 0)
375 throw new ArgumentException (string.Format (
376 CultureInfo.InvariantCulture, "Invalid value '{0}' for"
377 + " parameter 'MachineName'.", machineName));
379 EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
381 return impl.LogNameFromSourceName (source, machineName);
384 public static bool SourceExists (string source)
386 return SourceExists (source, ".");
389 [MonoNotSupported ("remote machine is not supported")]
390 public static bool SourceExists (string source, string machineName)
392 if (machineName == null || machineName.Trim ().Length == 0)
393 throw new ArgumentException (string.Format (
394 CultureInfo.InvariantCulture, "Invalid value '{0}' for"
395 + " parameter 'machineName'.", machineName));
397 EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
399 return impl.SourceExists (source, machineName);
402 public void WriteEntry (string message)
404 WriteEntry (message, EventLogEntryType.Information);
407 public void WriteEntry (string message, EventLogEntryType type)
409 WriteEntry (message, type, 0);
412 public void WriteEntry (string message, EventLogEntryType type,
415 WriteEntry (message, type, eventID, 0);
418 public void WriteEntry (string message, EventLogEntryType type,
422 WriteEntry (message, type, eventID, category, null);
425 public void WriteEntry (string message, EventLogEntryType type,
427 short category, byte[] rawData)
429 WriteEntry (new string [] { message }, type, eventID,
433 public static void WriteEntry (string source, string message)
435 WriteEntry (source, message, EventLogEntryType.Information);
438 public static void WriteEntry (string source, string message,
439 EventLogEntryType type)
441 WriteEntry (source, message, type, 0);
444 public static void WriteEntry (string source, string message,
445 EventLogEntryType type, int eventID)
447 WriteEntry (source, message, type, eventID, 0);
450 public static void WriteEntry (string source, string message,
451 EventLogEntryType type, int eventID, short category)
453 WriteEntry (source, message, type, eventID, category, null);
456 public static void WriteEntry (string source, string message,
457 EventLogEntryType type, int eventID, short category,
460 using (EventLog eventLog = new EventLog ()) {
461 eventLog.Source = source;
462 eventLog.WriteEntry (message, type, eventID, category, rawData);
467 public void WriteEvent (EventInstance instance, params object [] values)
469 WriteEvent (instance, null, values);
473 public void WriteEvent (EventInstance instance, byte [] data, params object [] values)
475 if (instance == null)
476 throw new ArgumentNullException ("instance");
478 string [] replacementStrings = null;
479 if (values != null) {
480 replacementStrings = new string [values.Length];
481 for (int i = 0; i < values.Length; i++) {
482 object value = values [i];
484 replacementStrings [i] = string.Empty;
486 replacementStrings [i] = values [i].ToString ();
489 replacementStrings = new string [0];
492 WriteEntry (replacementStrings, instance.EntryType, instance
493 .InstanceId, (short) instance.CategoryId, data);
496 public static void WriteEvent (string source, EventInstance instance, params object [] values)
498 WriteEvent (source, instance, null, values);
501 public static void WriteEvent (string source, EventInstance instance, byte [] data, params object [] values)
503 using (EventLog eventLog = new EventLog ()) {
504 eventLog.Source = source;
505 eventLog.WriteEvent (instance, data, values);
509 internal void OnEntryWritten (EventLogEntry newEntry)
511 if (doRaiseEvents && EntryWritten != null)
512 EntryWritten (this, new EntryWrittenEventArgs (newEntry));
515 [MonitoringDescription ("Raised for each EventLog entry written.")]
516 public event EntryWrittenEventHandler EntryWritten;
518 internal string GetLogName ()
520 if (logName != null && logName.Length > 0)
523 // if no log name has been set, then use source to determine name of log
524 logName = LogNameFromSourceName (source, machineName);
528 private static EventLogImpl CreateEventLogImpl (string logName, string machineName, string source)
530 EventLog eventLog = new EventLog (logName, machineName, source);
531 return CreateEventLogImpl (eventLog);
534 private static EventLogImpl CreateEventLogImpl (EventLog eventLog)
536 switch (EventLogImplType) {
537 case LOCAL_FILE_IMPL:
538 return new LocalFileEventLog (eventLog);
540 return new Win32EventLog (eventLog);
542 return new NullEventLog (eventLog);
544 // we should never get here
545 throw new NotSupportedException (string.Format (
546 CultureInfo.InvariantCulture, "Eventlog implementation"
547 + " '{0}' is not supported.", EventLogImplType));
551 private static bool Win32EventLogEnabled {
553 return (Environment.OSVersion.Platform == PlatformID.Win32NT);
557 // IMPORTANT: also modify corresponding property in EventLogTest
558 private static string EventLogImplType {
560 string implType = Environment.GetEnvironmentVariable (EVENTLOG_TYPE_VAR);
561 if (implType == null) {
562 if (Win32EventLogEnabled)
564 implType = NULL_IMPL;
566 if (Win32EventLogEnabled && string.Compare (implType, WIN32_IMPL, true) == 0)
567 implType = WIN32_IMPL;
568 else if (string.Compare (implType, NULL_IMPL, true) == 0)
569 implType = NULL_IMPL;
570 else if (string.Compare (implType, 0, LOCAL_FILE_IMPL, 0, LOCAL_FILE_IMPL.Length, true) == 0)
571 implType = LOCAL_FILE_IMPL;
573 throw new NotSupportedException (string.Format (
574 CultureInfo.InvariantCulture, "Eventlog implementation"
575 + " '{0}' is not supported.", implType));
581 private void WriteEntry (string [] replacementStrings, EventLogEntryType type, long instanceID, short category, byte [] rawData)
583 if (Source.Length == 0)
584 throw new ArgumentException ("Source property was not set"
585 + "before writing to the event log.");
587 if (!Enum.IsDefined (typeof (EventLogEntryType), type))
588 throw new InvalidEnumArgumentException ("type", (int) type,
589 typeof (EventLogEntryType));
591 ValidateEventID (instanceID);
593 if (!SourceExists (Source, MachineName)) {
594 if (Log == null || Log.Length == 0) {
597 CreateEventSource (Source, Log, MachineName);
599 } else if (logName != null && logName.Length != 0) {
600 string actualLog = LogNameFromSourceName (Source, MachineName);
601 if (string.Compare (logName, actualLog, true, CultureInfo.InvariantCulture) != 0)
602 throw new ArgumentException (string.Format (
603 CultureInfo.InvariantCulture, "The source '{0}' is not"
604 + " registered in log '{1}' (it is registered in log"
605 + " '{2}'). The Source and Log properties must be"
606 + " matched, or you may set Log to the empty string,"
607 + " and it will automatically be matched to the Source"
608 + " property.", Source, logName, actualLog));
613 rawData = new byte [0];
615 Impl.WriteEntry (replacementStrings, type, (uint) instanceID, category, rawData);
618 private void ValidateEventID (long instanceID)
620 int eventID = GetEventID (instanceID);
621 if (eventID < ushort.MinValue || eventID > ushort.MaxValue)
622 throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
623 "Invalid eventID value '{0}'. It must be in the range between"
624 + " '{1}' and '{2}'.", instanceID, ushort.MinValue, ushort.MaxValue));
627 internal static int GetEventID (long instanceID)
629 long inst = (instanceID < 0) ? -instanceID : instanceID;
631 // MSDN: eventID equals the InstanceId with the top two bits masked
632 int eventID = (int) (inst & 0x3fffffff);
633 return (instanceID < 0) ? -eventID : eventID;