New test.
[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
42 namespace System.Diagnostics 
43 {
44         [DefaultEvent ("EntryWritten")]
45         [InstallerType (typeof (EventLogInstaller))]
46         [Designer ("Microsoft.VisualStudio.Install.EventLogInstallableComponentDesigner, " + Consts.AssemblyMicrosoft_VisualStudio)]
47         public class EventLog : Component, ISupportInitialize 
48         {
49                 private string source;
50                 private string logName;
51                 private string machineName;
52                 private bool doRaiseEvents = false;
53                 private ISynchronizeInvoke synchronizingObject = null;
54
55                 // IMPORTANT: also update constants in EventLogTest
56                 internal const string LOCAL_FILE_IMPL = "local";
57                 private const string WIN32_IMPL = "win32";
58                 private const string NULL_IMPL = "null";
59
60                 internal const string EVENTLOG_TYPE_VAR = "MONO_EVENTLOG_TYPE";
61
62                 private EventLogImpl Impl;
63
64                 public EventLog() : this (string.Empty)
65                 {
66                 }
67
68                 public EventLog(string logName) : this (logName, ".")
69                 {
70                 }
71
72                 public EventLog(string logName, string machineName)
73                         : this (logName, machineName, string.Empty)
74                 {
75                 }
76
77                 public EventLog(string logName, string machineName, string source)
78                 {
79                         if (logName == null) {
80                                 throw new ArgumentNullException ("logName");
81                         }
82                         if (machineName == null || machineName.Length == 0)
83 #if NET_2_0
84                                 throw new ArgumentException (string.Format (
85                                         CultureInfo.InvariantCulture, "Invalid value '{0}' for"
86                                         + " parameter 'machineName'.", machineName));
87 #else
88                                 throw new ArgumentException (string.Format (
89                                         CultureInfo.InvariantCulture, "Invalid value {0} for"
90                                         + " parameter MachineName.", machineName));
91 #endif
92
93                         this.source = source;
94                         this.machineName = machineName;
95                         this.logName = logName;
96
97                         Impl = CreateEventLogImpl (this);
98                         Impl.EntryWritten += new EntryWrittenEventHandler (EntryWrittenHandler);
99                 }
100
101                 private void EntryWrittenHandler (object sender, EntryWrittenEventArgs e)
102                 {
103                         if (doRaiseEvents)
104                                 OnEntryWritten (e.Entry);
105                 }
106
107                 [Browsable (false), DefaultValue (false)]
108                 [MonitoringDescription ("If enabled raises event when a log is written.")]
109                 public bool EnableRaisingEvents {
110                         get {return doRaiseEvents;}
111                         set {doRaiseEvents = value;}
112                 }
113
114                 [Browsable (false), DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
115                 [MonitoringDescription ("The entries in the log.")]
116                 public EventLogEntryCollection Entries {
117                         get {return new EventLogEntryCollection(Impl);}
118                 }
119
120                 [ReadOnly (true), DefaultValue (""), RecommendedAsConfigurable (true)]
121                 [TypeConverter ("System.Diagnostics.Design.LogConverter, " + Consts.AssemblySystem_Design)]
122                 [MonitoringDescription ("Name of the log that is read and written.")]
123                 public string Log {
124                         get {
125                                 if (source != null && source.Length > 0)
126                                         return GetLogName ();
127                                 return logName;
128                         }
129                         set {
130                                 if (value == null)
131                                         throw new ArgumentNullException ("value");
132                                 logName = value;
133                         }
134                 }
135
136                 [Browsable (false)]
137                 public string LogDisplayName {
138                         get {return Impl.LogDisplayName;}
139                 }
140
141                 [ReadOnly (true), DefaultValue ("."), RecommendedAsConfigurable (true)]
142                 [MonitoringDescription ("Name of the machine that this log get written to.")]
143                 public string MachineName {
144                         get {return machineName;}
145                         set {machineName = value;}
146                 }
147
148                 [ReadOnly (true), DefaultValue (""), RecommendedAsConfigurable (true)]
149                 [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
150                 [MonitoringDescription ("The application name that writes the log.")]
151                 public string Source {
152                         get { return source; }
153                         set { source = (value == null) ? string.Empty : value; }
154                 }
155
156                 [Browsable (false), DefaultValue (null)]
157                 [MonitoringDescription ("An object that synchronizes event handler calls.")]
158                 public ISynchronizeInvoke SynchronizingObject {
159                         get {return synchronizingObject;}
160                         set {synchronizingObject = value;}
161                 }
162
163                 public void BeginInit ()
164                 {
165                         Impl.BeginInit();
166                 }
167
168                 public void Clear ()
169                 {
170                         string logName = Log;
171                         if (logName == null || logName.Length == 0)
172                                 throw new ArgumentException ("Log property value has not been specified.");
173
174                         if (!EventLog.Exists (logName, MachineName))
175                                 throw new InvalidOperationException (string.Format (
176                                         CultureInfo.InvariantCulture, "Event Log '{0}'"
177                                         + " does not exist on computer '{1}'.", logName,
178                                         machineName));
179
180                         Impl.Clear ();
181                 }
182
183                 public void Close ()
184                 {
185                         Impl.Close();
186                 }
187
188                 public static void CreateEventSource (string source, string logName)
189                 {
190                         CreateEventSource (source, logName, ".");
191                 }
192
193                 public static void CreateEventSource (string source, 
194                         string logName, 
195                         string machineName)
196                 {
197                         CreateEventSource (new EventSourceCreationData (source, logName,
198                                 machineName));
199                 }
200
201 #if NET_2_0
202                 [MonoTODO ("Support remote machine")]
203                 public
204 #else
205                 private
206 #endif
207                 static void CreateEventSource (EventSourceCreationData sourceData)
208                 {
209                         if (sourceData.Source == null || sourceData.Source.Length == 0)
210                                 throw new ArgumentException ("Source property value has not been specified.");
211
212                         if (sourceData.LogName == null || sourceData.LogName.Length == 0)
213                                 throw new ArgumentException ("Log property value has not been specified.");
214
215                         if (SourceExists (sourceData.Source, sourceData.MachineName))
216                                 throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
217                                         "Source '{0}' already exists on '{1}'.", sourceData.Source,
218                                         sourceData.MachineName));
219
220                         EventLogImpl impl = CreateEventLogImpl (sourceData.LogName,
221                                 sourceData.MachineName, sourceData.Source);
222                         impl.CreateEventSource (sourceData);
223                 }
224
225                 public static void Delete (string logName)
226                 {
227                         Delete (logName, ".");
228                 }
229
230                 [MonoTODO ("Support remote machine")]
231                 public static void Delete (string logName, string machineName)
232                 {
233                         if (machineName == null || machineName.Length == 0)
234                                 throw new ArgumentException ("Invalid format for argument"
235                                         + " machineName.");
236
237                         if (logName == null || logName.Length == 0)
238                                 throw new ArgumentException ("Log to delete was not specified.");
239
240                         EventLogImpl impl = CreateEventLogImpl (logName, machineName, 
241                                 string.Empty);
242                         impl.Delete (logName, machineName);
243                 }
244
245                 public static void DeleteEventSource (string source)
246                 {
247                         DeleteEventSource (source, ".");
248                 }
249
250                 [MonoTODO ("Support remote machine")]
251                 public static void DeleteEventSource (string source, string machineName)
252                 {
253                         if (machineName == null || machineName.Length == 0)
254 #if NET_2_0
255                                 throw new ArgumentException (string.Format (
256                                         CultureInfo.InvariantCulture, "Invalid value '{0}' for"
257                                         + " parameter 'machineName'.", machineName));
258 #else
259                                 throw new ArgumentException (string.Format (
260                                         CultureInfo.InvariantCulture, "Invalid value {0} for"
261                                         + " parameter machineName.", machineName));
262 #endif
263
264                         EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
265                                 source);
266                         impl.DeleteEventSource (source, machineName);
267                 }
268
269                 protected override void Dispose (bool disposing)
270                 {
271                         if (Impl != null)
272                                 Impl.Dispose (disposing);
273                 }
274
275                 public void EndInit()
276                 {
277                         Impl.EndInit();
278                 }
279
280                 public static bool Exists (string logName)
281                 {
282                         return Exists (logName, ".");
283                 }
284
285                 [MonoTODO ("Support remote machine")]
286                 public static bool Exists (string logName, string machineName)
287                 {
288                         if (machineName == null || machineName.Length == 0)
289                                 throw new ArgumentException ("Invalid format for argument machineName.");
290
291                         if (logName == null || logName.Length == 0)
292                                 return false; 
293
294                         EventLogImpl impl = CreateEventLogImpl (logName, machineName,
295                                 string.Empty);
296                         return impl.Exists (logName, machineName);
297                 }
298
299                 public static EventLog[] GetEventLogs ()
300                 {
301                         return GetEventLogs (".");
302                 }
303
304                 [MonoTODO ("Support remote machine")]
305                 public static EventLog[] GetEventLogs (string machineName)
306                 {
307                         EventLogImpl impl = CreateEventLogImpl (new EventLog ());
308                         return impl.GetEventLogs (machineName);
309                 }
310
311                 [MonoTODO ("Support remote machine")]
312                 public static string LogNameFromSourceName (string source, string machineName)
313                 {
314                         if (machineName == null || machineName.Length == 0)
315 #if NET_2_0
316                                 throw new ArgumentException (string.Format (
317                                         CultureInfo.InvariantCulture, "Invalid value '{0}' for"
318                                         + " parameter 'MachineName'.", machineName));
319 #else
320                                 throw new ArgumentException (string.Format (
321                                         CultureInfo.InvariantCulture, "Invalid value {0} for"
322                                         + " parameter MachineName.", machineName));
323 #endif
324
325                         EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
326                                 source);
327                         return impl.LogNameFromSourceName (source, machineName);
328                 }
329
330                 public static bool SourceExists (string source)
331                 {
332                         return SourceExists (source, ".");
333                 }
334
335                 [MonoTODO ("Support remote machines")]
336                 public static bool SourceExists (string source, string machineName)
337                 {
338                         if (machineName == null || machineName.Length == 0)
339                                 throw new ArgumentException (string.Format (
340                                         CultureInfo.InvariantCulture, "Invalid value '{0}' for"
341                                         + " parameter 'machineName'.", machineName));
342
343                         EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
344                                 source);
345                         return impl.SourceExists (source, machineName);
346                 }
347
348                 public void WriteEntry (string message)
349                 {
350                         WriteEntry (message, EventLogEntryType.Information);
351                 }
352
353                 public void WriteEntry (string message, EventLogEntryType type)
354                 {
355                         WriteEntry (message, type, 0);
356                 }
357
358                 public void WriteEntry (string message, EventLogEntryType type, 
359                         int eventID)
360                 {
361                         WriteEntry (message, type, eventID, 0);
362                 }
363
364                 public void WriteEntry (string message, EventLogEntryType type, 
365                         int eventID,
366                         short category)
367                 {
368                         WriteEntry (message, type, eventID, category, null);
369                 }
370
371                 public void WriteEntry (string message, EventLogEntryType type, 
372                         int eventID,
373                         short category, byte[] rawData)
374                 {
375                         WriteEntry (new string [] { message }, type, eventID,
376                                 category, rawData);
377                 }
378
379                 public static void WriteEntry (string source, string message)
380                 {
381                         WriteEntry (source, message, EventLogEntryType.Information);
382                 }
383
384                 public static void WriteEntry (string source, string message, 
385                         EventLogEntryType type)
386                 {
387                         WriteEntry (source, message, type, 0);
388                 }
389
390                 public static void WriteEntry (string source, string message, 
391                         EventLogEntryType type, int eventID)
392                 {
393                         WriteEntry (source, message, type, eventID, 0);
394                 }
395
396                 public static void WriteEntry (string source, string message, 
397                         EventLogEntryType type, int eventID, short category)
398                 {
399                         WriteEntry (source, message, type, eventID, category, null);
400                 }
401
402                 public static void WriteEntry (string source, string message, 
403                         EventLogEntryType type, int eventID, short category, 
404                         byte[] rawData)
405                 {
406                         using (EventLog eventLog = new EventLog ()) {
407                                 eventLog.Source = source;
408                                 eventLog.WriteEntry (message, type, eventID, category, rawData);
409                         }
410                 }
411
412 #if NET_2_0
413                 public void WriteEvent (EventInstance instance, params object [] values)
414                 {
415                         WriteEvent (instance, null, values);
416                 }
417
418                 public void WriteEvent (EventInstance instance, byte [] data, params object [] values)
419                 {
420                         if (instance == null)
421                                 throw new ArgumentNullException ("instance");
422
423                         string [] replacementStrings = null;
424                         if (values != null) {
425                                 replacementStrings = new string [values.Length];
426                                 for (int i = 0; i < values.Length; i++) {
427                                         object value = values [i];
428                                         if (value == null)
429                                                 replacementStrings [i] = string.Empty;
430                                         else
431                                                 replacementStrings [i] = values [i].ToString ();
432                                 }
433                         } else {
434                                 replacementStrings = new string [0];
435                         }
436
437                         WriteEntry (replacementStrings, instance.EntryType, instance
438                                 .InstanceId, (short) instance.CategoryId, data);
439                 }
440
441                 public static void WriteEvent (string source, EventInstance instance, params object [] values)
442                 {
443                         WriteEvent (source, instance, null, values);
444                 }
445
446                 public static void WriteEvent (string source, EventInstance instance, byte [] data, params object [] values)
447                 {
448                         using (EventLog eventLog = new EventLog ()) {
449                                 eventLog.Source = source;
450                                 eventLog.WriteEvent (instance, data, values);
451                         }
452                 }
453 #endif
454
455                 internal void OnEntryWritten (EventLogEntry newEntry)
456                 {
457                         if (EntryWritten != null)
458                                 EntryWritten (this, new EntryWrittenEventArgs (newEntry));
459                 }
460
461                 [MonitoringDescription ("Raised for each EventLog entry written.")]
462                 [MonoTODO ("Use FSM for local file implementation, and NotifyChangeEventLog for win32")]
463                 public event EntryWrittenEventHandler EntryWritten;
464
465                 internal string GetLogName ()
466                 {
467                         if (logName != null && logName.Length > 0)
468                                 return logName;
469
470                         // if no log name has been set, then use source to determine name of log
471                         logName = LogNameFromSourceName (source, machineName);
472                         return logName;
473                 }
474
475                 private static EventLogImpl CreateEventLogImpl (string logName, string machineName, string source)
476                 {
477                         EventLog eventLog = new EventLog (logName, machineName, source);
478                         return CreateEventLogImpl (eventLog);
479                 }
480
481                 private static EventLogImpl CreateEventLogImpl (EventLog eventLog)
482                 {
483                         switch (EventLogImplType) {
484                         case LOCAL_FILE_IMPL:
485                                 return new LocalFileEventLog (eventLog);
486                         case WIN32_IMPL:
487                                 return new Win32EventLog (eventLog);
488                         case NULL_IMPL:
489                                 return new NullEventLog (eventLog);
490                         default:
491                                 // we should never get here
492                                 throw new NotSupportedException (string.Format (
493                                         CultureInfo.InvariantCulture, "Eventlog implementation"
494                                         + " '{0}' is not supported.", EventLogImplType));
495                         }
496                 }
497
498                 private static bool Win32EventLogEnabled {
499                         get {
500                                 return (Environment.OSVersion.Platform == PlatformID.Win32NT);
501                         }
502                 }
503
504                 // IMPORTANT: also modify corresponding property in EventLogTest
505                 private static string EventLogImplType
506                 {
507                         get {
508                                 string implType = Environment.GetEnvironmentVariable (EVENTLOG_TYPE_VAR);
509                                 if (implType == null) {
510                                         if (Win32EventLogEnabled)
511                                                 return WIN32_IMPL;
512                                         implType = NULL_IMPL;
513                                 } else {
514                                         if (Win32EventLogEnabled && string.Compare (implType, WIN32_IMPL, true) == 0)
515                                                 implType = WIN32_IMPL;
516                                         else if (string.Compare (implType, NULL_IMPL, true) == 0)
517                                                 implType = NULL_IMPL;
518                                         else if (string.Compare (implType, 0, LOCAL_FILE_IMPL, 0, LOCAL_FILE_IMPL.Length, true) == 0)
519                                                 implType = LOCAL_FILE_IMPL;
520                                         else
521                                                 throw new NotSupportedException (string.Format (
522                                                         CultureInfo.InvariantCulture, "Eventlog implementation"
523                                                         + " '{0}' is not supported.", implType));
524                                 }
525                                 return implType;
526                         }
527                 }
528
529                 private void WriteEntry (string [] replacementStrings, EventLogEntryType type, long instanceID, short category, byte [] rawData)
530                 {
531                         if (Source.Length == 0)
532                                 throw new ArgumentException ("Source property was not set"
533                                         + "before writing to the event log.");
534
535                         if (!Enum.IsDefined (typeof (EventLogEntryType), type))
536                                 throw new InvalidEnumArgumentException ("type", (int) type,
537                                         typeof (EventLogEntryType));
538
539 #if NET_2_0
540                         ValidateEventID (instanceID);
541 #endif
542
543                         if (!SourceExists (Source, MachineName)) {
544                                 if (Log == null || Log.Length == 0) {
545                                         Log = "Application";
546                                 }
547                                 CreateEventSource (Source, Log, MachineName);
548
549 #if ONLY_1_1
550                                 ValidateEventID (instanceID);
551 #endif
552                         } else if (logName != null && logName.Length != 0) {
553 #if ONLY_1_1
554                                 ValidateEventID (instanceID);
555 #endif
556                                 string actualLog = LogNameFromSourceName (Source, MachineName);
557                                 if (string.Compare (logName, actualLog, true, CultureInfo.InvariantCulture) != 0)
558                                         throw new ArgumentException (string.Format (
559                                                 CultureInfo.InvariantCulture, "The source '{0}' is not"
560                                                 + " registered in log '{1}' (it is registered in log"
561                                                 + " '{2}'). The Source and Log properties must be"
562                                                 + " matched, or you may set Log to the empty string,"
563                                                 + " and it will automatically be matched to the Source"
564                                                 + " property.", Source, logName, actualLog));
565                         }
566
567 #if ONLY_1_1
568                         ValidateEventID (instanceID);
569 #endif
570
571                         if (rawData == null)
572                                 rawData = new byte [0];
573
574                         Impl.WriteEntry (replacementStrings, type, (uint) instanceID, category, rawData);
575                 }
576
577                 private void ValidateEventID (long instanceID)
578                 {
579                         int eventID = GetEventID (instanceID);
580                         if (eventID < ushort.MinValue || eventID > ushort.MaxValue)
581                                 throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
582                                         "Invalid eventID value '{0}'. It must be in the range between"
583                                         + " '{1}' and '{2}'.", instanceID, ushort.MinValue, ushort.MaxValue));
584                 }
585
586                 internal static int GetEventID (long instanceID)
587                 {
588                         long inst = (instanceID < 0) ? -instanceID : instanceID;
589
590                         // MSDN: eventID equals the InstanceId with the top two bits masked
591                         int eventID = (int) (inst & 0x3fffffff);
592                         return (instanceID < 0) ? -eventID : eventID;
593                 }
594         }
595 }