[System] Removal of the NET_2_0 in the source code
[mono.git] / mcs / class / System / System.Diagnostics / EventLog.cs
1 //
2 // System.Diagnostics.EventLog.cs
3 //
4 // Authors:
5 //      Jonathan Pryor (jonpryor@vt.edu)
6 //      Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 //      Gert Driesen (drieseng@users.sourceforge.net)
8 //
9 // Copyright (C) 2002
10 // Copyright (C) 2003 Andreas Nahr
11 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
12 //
13 //
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:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
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.
32 //
33
34 using System;
35 using System.Diagnostics;
36 using System.Collections;
37 using System.ComponentModel;
38 using System.ComponentModel.Design;
39 using System.Globalization;
40 using System.IO;
41 using System.Runtime.InteropServices;
42
43 namespace System.Diagnostics 
44 {
45         [DefaultEvent ("EntryWritten")]
46         [InstallerType (typeof (EventLogInstaller))]
47         [MonitoringDescription ("Represents an event log")]
48         public class EventLog : Component, ISupportInitialize 
49         {
50                 private string source;
51                 private string logName;
52                 private string machineName;
53                 private bool doRaiseEvents = false;
54                 private ISynchronizeInvoke synchronizingObject = null;
55
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";
60
61                 internal const string EVENTLOG_TYPE_VAR = "MONO_EVENTLOG_TYPE";
62
63                 private EventLogImpl Impl;
64
65                 public EventLog() : this (string.Empty)
66                 {
67                 }
68
69                 public EventLog(string logName) : this (logName, ".")
70                 {
71                 }
72
73                 public EventLog(string logName, string machineName)
74                         : this (logName, machineName, string.Empty)
75                 {
76                 }
77
78                 public EventLog(string logName, string machineName, string source)
79                 {
80                         if (logName == null) {
81                                 throw new ArgumentNullException ("logName");
82                         }
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));
87
88                         this.source = source;
89                         this.machineName = machineName;
90                         this.logName = logName;
91
92                         Impl = CreateEventLogImpl (this);
93                 }
94
95                 [Browsable (false), DefaultValue (false)]
96                 [MonitoringDescription ("If enabled raises event when a log is written.")]
97                 public bool EnableRaisingEvents {
98                         get {return doRaiseEvents;}
99                         set {
100                                 if (value == doRaiseEvents)
101                                         return;
102
103                                 if (value)
104                                         Impl.EnableNotification ();
105                                 else
106                                         Impl.DisableNotification ();
107                                 doRaiseEvents = value;
108                         }
109                 }
110
111                 [Browsable (false), DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
112                 [MonitoringDescription ("The entries in the log.")]
113                 public EventLogEntryCollection Entries {
114                         get {return new EventLogEntryCollection(Impl);}
115                 }
116
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.")]
120                 public string Log {
121                         get {
122                                 if (source != null && source.Length > 0)
123                                         return GetLogName ();
124                                 return logName;
125                         }
126                         set {
127                                 if (value == null)
128                                         throw new ArgumentNullException ("value");
129
130                                 // log name is treated case-insensitively on all platforms
131                                 if (string.Compare (logName, value, true) != 0) {
132                                         logName = value;
133                                         Reset ();
134                                 }
135                         }
136                 }
137
138                 [Browsable (false)]
139                 public string LogDisplayName {
140                         get { return Impl.LogDisplayName; }
141                 }
142
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; }
147                         set {
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));
152
153                                 if (string.Compare (machineName, value, true) != 0) {
154                                         Close ();
155                                         machineName = value;
156                                 }
157                         }
158                 }
159
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; }
165                         set {
166                                 if (value == null)
167                                         value = string.Empty;
168
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)) {
172                                         source = value;
173                                 } else if (string.Compare (source, value, true) != 0) {
174                                         source = value;
175                                         Reset ();
176                                 }
177                         }
178                 }
179
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;}
185                 }
186
187                 [MonoTODO]
188                 [ComVisibleAttribute (false)]
189                 [Browsable (false)]
190                 public OverflowAction OverflowAction {
191                         get { return Impl.OverflowAction; }
192                 }
193
194                 [MonoTODO]
195                 [ComVisibleAttribute (false)]
196                 [Browsable (false)]
197                 public int MinimumRetentionDays {
198                         get { return Impl.MinimumRetentionDays; }
199                 }
200
201                 [MonoTODO]
202                 [ComVisibleAttribute (false)]
203                 [Browsable (false)]
204                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
205                 public long MaximumKilobytes {
206                         get { return Impl.MaximumKilobytes; }
207                         set { Impl.MaximumKilobytes = value; }
208                 }
209
210                 [MonoTODO]
211                 [ComVisible (false)]
212                 public void ModifyOverflowPolicy (OverflowAction action, int retentionDays)
213                 {
214                         Impl.ModifyOverflowPolicy (action, retentionDays);
215                 }
216
217                 [MonoTODO]
218                 [ComVisibleAttribute (false)]
219                 public void RegisterDisplayName (string resourceFile, long resourceId)
220                 {
221                         Impl.RegisterDisplayName (resourceFile, resourceId);
222                 }
223
224                 public void BeginInit ()
225                 {
226                         Impl.BeginInit();
227                 }
228
229                 public void Clear ()
230                 {
231                         string logName = Log;
232                         if (logName == null || logName.Length == 0)
233                                 throw new ArgumentException ("Log property value has not been specified.");
234
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,
239                                         machineName));
240
241                         Impl.Clear ();
242                         Reset ();
243                 }
244
245                 public void Close ()
246                 {
247                         Impl.Close();
248                         EnableRaisingEvents = false;
249                 }
250
251                 internal void Reset ()
252                 {
253                         bool enableRaisingEvents = EnableRaisingEvents;
254                         Close ();
255                         EnableRaisingEvents = enableRaisingEvents;
256                 }
257
258                 public static void CreateEventSource (string source, string logName)
259                 {
260                         CreateEventSource (source, logName, ".");
261                 }
262
263                 [Obsolete ("use CreateEventSource(EventSourceCreationData) instead")]
264                 public static void CreateEventSource (string source, 
265                         string logName, 
266                         string machineName)
267                 {
268                         CreateEventSource (new EventSourceCreationData (source, logName,
269                                 machineName));
270                 }
271
272                 [MonoNotSupported ("remote machine is not supported")]
273                 public static void CreateEventSource (EventSourceCreationData sourceData)
274                 {
275                         if (sourceData.Source == null || sourceData.Source.Length == 0)
276                                 throw new ArgumentException ("Source property value has not been specified.");
277
278                         if (sourceData.LogName == null || sourceData.LogName.Length == 0)
279                                 throw new ArgumentException ("Log property value has not been specified.");
280
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));
285
286                         EventLogImpl impl = CreateEventLogImpl (sourceData.LogName,
287                                 sourceData.MachineName, sourceData.Source);
288                         impl.CreateEventSource (sourceData);
289                 }
290
291                 public static void Delete (string logName)
292                 {
293                         Delete (logName, ".");
294                 }
295
296                 [MonoNotSupported ("remote machine is not supported")]
297                 public static void Delete (string logName, string machineName)
298                 {
299                         if (machineName == null || machineName.Trim ().Length == 0)
300                                 throw new ArgumentException ("Invalid format for argument"
301                                         + " machineName.");
302
303                         if (logName == null || logName.Length == 0)
304                                 throw new ArgumentException ("Log to delete was not specified.");
305
306                         EventLogImpl impl = CreateEventLogImpl (logName, machineName, 
307                                 string.Empty);
308                         impl.Delete (logName, machineName);
309                 }
310
311                 public static void DeleteEventSource (string source)
312                 {
313                         DeleteEventSource (source, ".");
314                 }
315
316                 [MonoNotSupported ("remote machine is not supported")]
317                 public static void DeleteEventSource (string source, string machineName)
318                 {
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));
323
324                         EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
325                                 source);
326                         impl.DeleteEventSource (source, machineName);
327                 }
328
329                 protected override void Dispose (bool disposing)
330                 {
331                         if (Impl != null)
332                                 Impl.Dispose (disposing);
333                 }
334
335                 public void EndInit()
336                 {
337                         Impl.EndInit();
338                 }
339
340                 public static bool Exists (string logName)
341                 {
342                         return Exists (logName, ".");
343                 }
344
345                 [MonoNotSupported ("remote machine is not supported")]
346                 public static bool Exists (string logName, string machineName)
347                 {
348                         if (machineName == null || machineName.Trim ().Length == 0)
349                                 throw new ArgumentException ("Invalid format for argument machineName.");
350
351                         if (logName == null || logName.Length == 0)
352                                 return false; 
353
354                         EventLogImpl impl = CreateEventLogImpl (logName, machineName,
355                                 string.Empty);
356                         return impl.Exists (logName, machineName);
357                 }
358
359                 public static EventLog[] GetEventLogs ()
360                 {
361                         return GetEventLogs (".");
362                 }
363
364                 [MonoNotSupported ("remote machine is not supported")]
365                 public static EventLog[] GetEventLogs (string machineName)
366                 {
367                         EventLogImpl impl = CreateEventLogImpl (new EventLog ());
368                         return impl.GetEventLogs (machineName);
369                 }
370
371                 [MonoNotSupported ("remote machine is not supported")]
372                 public static string LogNameFromSourceName (string source, string machineName)
373                 {
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));
378
379                         EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
380                                 source);
381                         return impl.LogNameFromSourceName (source, machineName);
382                 }
383
384                 public static bool SourceExists (string source)
385                 {
386                         return SourceExists (source, ".");
387                 }
388
389                 [MonoNotSupported ("remote machine is not supported")]
390                 public static bool SourceExists (string source, string machineName)
391                 {
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));
396
397                         EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
398                                 source);
399                         return impl.SourceExists (source, machineName);
400                 }
401
402                 public void WriteEntry (string message)
403                 {
404                         WriteEntry (message, EventLogEntryType.Information);
405                 }
406
407                 public void WriteEntry (string message, EventLogEntryType type)
408                 {
409                         WriteEntry (message, type, 0);
410                 }
411
412                 public void WriteEntry (string message, EventLogEntryType type, 
413                         int eventID)
414                 {
415                         WriteEntry (message, type, eventID, 0);
416                 }
417
418                 public void WriteEntry (string message, EventLogEntryType type, 
419                         int eventID,
420                         short category)
421                 {
422                         WriteEntry (message, type, eventID, category, null);
423                 }
424
425                 public void WriteEntry (string message, EventLogEntryType type, 
426                         int eventID,
427                         short category, byte[] rawData)
428                 {
429                         WriteEntry (new string [] { message }, type, eventID,
430                                 category, rawData);
431                 }
432
433                 public static void WriteEntry (string source, string message)
434                 {
435                         WriteEntry (source, message, EventLogEntryType.Information);
436                 }
437
438                 public static void WriteEntry (string source, string message, 
439                         EventLogEntryType type)
440                 {
441                         WriteEntry (source, message, type, 0);
442                 }
443
444                 public static void WriteEntry (string source, string message, 
445                         EventLogEntryType type, int eventID)
446                 {
447                         WriteEntry (source, message, type, eventID, 0);
448                 }
449
450                 public static void WriteEntry (string source, string message, 
451                         EventLogEntryType type, int eventID, short category)
452                 {
453                         WriteEntry (source, message, type, eventID, category, null);
454                 }
455
456                 public static void WriteEntry (string source, string message, 
457                         EventLogEntryType type, int eventID, short category, 
458                         byte[] rawData)
459                 {
460                         using (EventLog eventLog = new EventLog ()) {
461                                 eventLog.Source = source;
462                                 eventLog.WriteEntry (message, type, eventID, category, rawData);
463                         }
464                 }
465
466                 [ComVisible (false)]
467                 public void WriteEvent (EventInstance instance, params object [] values)
468                 {
469                         WriteEvent (instance, null, values);
470                 }
471
472                 [ComVisible (false)]
473                 public void WriteEvent (EventInstance instance, byte [] data, params object [] values)
474                 {
475                         if (instance == null)
476                                 throw new ArgumentNullException ("instance");
477
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];
483                                         if (value == null)
484                                                 replacementStrings [i] = string.Empty;
485                                         else
486                                                 replacementStrings [i] = values [i].ToString ();
487                                 }
488                         } else {
489                                 replacementStrings = new string [0];
490                         }
491
492                         WriteEntry (replacementStrings, instance.EntryType, instance
493                                 .InstanceId, (short) instance.CategoryId, data);
494                 }
495
496                 public static void WriteEvent (string source, EventInstance instance, params object [] values)
497                 {
498                         WriteEvent (source, instance, null, values);
499                 }
500
501                 public static void WriteEvent (string source, EventInstance instance, byte [] data, params object [] values)
502                 {
503                         using (EventLog eventLog = new EventLog ()) {
504                                 eventLog.Source = source;
505                                 eventLog.WriteEvent (instance, data, values);
506                         }
507                 }
508
509                 internal void OnEntryWritten (EventLogEntry newEntry)
510                 {
511                         if (doRaiseEvents && EntryWritten != null)
512                                 EntryWritten (this, new EntryWrittenEventArgs (newEntry));
513                 }
514
515                 [MonitoringDescription ("Raised for each EventLog entry written.")]
516                 public event EntryWrittenEventHandler EntryWritten;
517
518                 internal string GetLogName ()
519                 {
520                         if (logName != null && logName.Length > 0)
521                                 return logName;
522
523                         // if no log name has been set, then use source to determine name of log
524                         logName = LogNameFromSourceName (source, machineName);
525                         return logName;
526                 }
527
528                 private static EventLogImpl CreateEventLogImpl (string logName, string machineName, string source)
529                 {
530                         EventLog eventLog = new EventLog (logName, machineName, source);
531                         return CreateEventLogImpl (eventLog);
532                 }
533
534                 private static EventLogImpl CreateEventLogImpl (EventLog eventLog)
535                 {
536                         switch (EventLogImplType) {
537                         case LOCAL_FILE_IMPL:
538                                 return new LocalFileEventLog (eventLog);
539                         case WIN32_IMPL:
540                                 return new Win32EventLog (eventLog);
541                         case NULL_IMPL:
542                                 return new NullEventLog (eventLog);
543                         default:
544                                 // we should never get here
545                                 throw new NotSupportedException (string.Format (
546                                         CultureInfo.InvariantCulture, "Eventlog implementation"
547                                         + " '{0}' is not supported.", EventLogImplType));
548                         }
549                 }
550
551                 private static bool Win32EventLogEnabled {
552                         get {
553                                 return (Environment.OSVersion.Platform == PlatformID.Win32NT);
554                         }
555                 }
556
557                 // IMPORTANT: also modify corresponding property in EventLogTest
558                 private static string EventLogImplType {
559                         get {
560                                 string implType = Environment.GetEnvironmentVariable (EVENTLOG_TYPE_VAR);
561                                 if (implType == null) {
562                                         if (Win32EventLogEnabled)
563                                                 return WIN32_IMPL;
564                                         implType = NULL_IMPL;
565                                 } else {
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;
572                                         else
573                                                 throw new NotSupportedException (string.Format (
574                                                         CultureInfo.InvariantCulture, "Eventlog implementation"
575                                                         + " '{0}' is not supported.", implType));
576                                 }
577                                 return implType;
578                         }
579                 }
580
581                 private void WriteEntry (string [] replacementStrings, EventLogEntryType type, long instanceID, short category, byte [] rawData)
582                 {
583                         if (Source.Length == 0)
584                                 throw new ArgumentException ("Source property was not set"
585                                         + "before writing to the event log.");
586
587                         if (!Enum.IsDefined (typeof (EventLogEntryType), type))
588                                 throw new InvalidEnumArgumentException ("type", (int) type,
589                                         typeof (EventLogEntryType));
590
591                         ValidateEventID (instanceID);
592
593                         if (!SourceExists (Source, MachineName)) {
594                                 if (Log == null || Log.Length == 0) {
595                                         Log = "Application";
596                                 }
597                                 CreateEventSource (Source, Log, MachineName);
598
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));
609                         }
610
611
612                         if (rawData == null)
613                                 rawData = new byte [0];
614
615                         Impl.WriteEntry (replacementStrings, type, (uint) instanceID, category, rawData);
616                 }
617
618                 private void ValidateEventID (long instanceID)
619                 {
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));
625                 }
626
627                 internal static int GetEventID (long instanceID)
628                 {
629                         long inst = (instanceID < 0) ? -instanceID : instanceID;
630
631                         // MSDN: eventID equals the InstanceId with the top two bits masked
632                         int eventID = (int) (inst & 0x3fffffff);
633                         return (instanceID < 0) ? -eventID : eventID;
634                 }
635         }
636 }