Yeah, this really is a challenge. Sooner or later something needs to be aware of WMI. With respect to some of the questions you raise, it is interesting to note that Microsoft themselves don't have a great solution either. They attempted to produce a somewhat standardized event schema with their EIF offering, but EIF is not really recommended any longer. In PAG's latest effort Enterprise Library, they take the approach of a single WMI Event class which essentially mirrors log4net's LoggingEvent class in principle. Either way, both the WMI Event producer and consumer must be aware of the WMI event schema. This probably means that you can't dynamically define WMI Events that are useful to anyone else. I think there is value in allowing different event classes to be fired. From a WMI Event consumer perspective, you'd probably want to receive as few events as possible (filtering can be done via WMI infrastructure based on event type) rather than receiving all events and then filtering based on event data.
We are seeing a similar problem with the Event Id for the Windows Event log. The solutions that I have seen so far require you to place an additional layer on top of log4net in order to pass data through it. Here again, I thought that passing a specialized object through log4net targeted at a particular appender might solve the problem more elegantly. In either case, the client is aware that it must pass an Event Id. The basic issue seems to be that certain appenders provide functionality that is not represented in the basic logging interface. The more I think about this, the more it seems reasonable for the client code to know about the extra information that it is attempting to log. It is, in fact, the originator of the logging request. If a particular appender can not support a certain piece of data, so be it. What would happen to any standard object if it were passed to log4net. I assume that it would either call ToString() or an appropriate layout would be used. Either of these methods will work with WMI Event objects. In fact, I have used the ConsoleAppender and XmlLayout in testing the code I sent earlier. They log the WMI Event classes just fine. -----Original Message----- From: Nicko Cadell [mailto:[EMAIL PROTECTED] Sent: Tuesday, January 18, 2005 1:42 PM To: Log4NET Dev Subject: RE: WMI Appender Tom, Thanks for sharing the code and ideas. I think WMI is an area where we could do a lot more integration work. I think that your approach is valid, but does still run into the same problems. Basically using your appender the caller has to construct their WMIable object and pass it to log4net. If the event is delivered to the appender then Instrumentation.Fire is called with the message object. Log4net is being used only as a configurable switch. The message object is not necessarily compatible with other appenders - this could be overcome with custom ObjectRenderers. The biggest drawback is that the appender does not work with any other type of message data, therefore it cannot be use in conjunction with exiting applications that have logging. There are a number of possible approaches that need to be considered: * There is the method that you have proposed where the caller has to generate an WMI aware object, pack it with the right data, and then log it. * Another approach is to have a single WMI aware type within the appender. For each event the appender creates a new (or maybe reuses) one of these object, populates it with all the data from the logging event, then fires it. This means that the layout of the WMI event will be the same for all users and event types. Depending on the way WMI is used this may not be appropriate. * Yet another approach is to have the appender accept any message, then pass it to a 3rd party factory object that will convert the logging event into a WMI compatible object. The appender would need to be configured with the type of the 3rd party converter object. This would give the flexibility required, but it does require custom code to get the base case working. * An ideal solution would be to specify the format of the WMI message in the config for the appender. The appender would perform the mapping from the logging event into the WMI message similarly to the way in which the AsoNetAppender uses the PatternLayout to setup the stored procedure parameters. The difficulty with this is that it can't use the Instrumentation.Fire method to raise the event, we would need to get in at a lower level where the WMI message exists as data rather than as an object. Are there any WMI experts out there who would know if an app should fire different types of object for different events? Are there standard WMI schemas that the application needs to conform to? In the long term I would prefer a solution that used a 3rd party factory to convert the event into a WMI object, rather than one that required the user to change all their logging calls to pass WMI objects. What are your thoughts on this? Nicko > -----Original Message----- > From: Whitner, Tom [mailto:[EMAIL PROTECTED] > Sent: 18 January 2005 17:17 > To: [email protected] > Subject: WMI Appender > > > I have been experimenting with WMI and log4net. I > noticed that work on the WMI Appender is postponed. "Because > of the way that a managed WMI schema must be registered with > the system (using assembly attributes) there seems little > scope for a generic appender for WMI. The immediate plan is > to look at generating a sample appender that does WMI." > > I have created a generic appender class that "Fires" > WMI Events and does not require registering as mentioned > previously. The WMI Events themselves must be published, but > they exist in client/consumer code not within log4net. > Essentially, this class looks at the original object that was > logged (loggingEvent.MessageObject) and using reflection, > determines if it is a WMI Instrumentation class. If it is, > it fires it. I have included the code below for review. Is > this a viable approach to providing WMI functionality to > log4net? I am concerned that my approach may violate > principles that I am not aware of. BTW, I am aware that > there are improvements that must be made such as caching and > exception handling. This was intended to demonstrate the concept. > > Thanks, > Tom Whitner > > /// <summary> > /// A log4net appender that fires WMI Events /// </summary> > public class WMIAppender : AppenderSkeleton { > /// <summary> > /// Default Constructor > /// </summary> > public WMIAppender() > { > } > > /// <summary> > /// "Appends" WMI events by firing them. > /// </summary> > /// <param name="loggingEvent">The logging event to > append.</param> > /// <remarks>Non-Wmi events will not be fired.</remarks> > protected override void Append(LoggingEvent loggingEvent) > { > if (IsWmiEvent(loggingEvent.MessageObject)) > { > > // TODO: Add exception handling in > case schema > // was not published. > > Instrumentation.Fire(loggingEvent.MessageObject); > } > } > > /// <summary> > /// Verifies that <see cref="Append(LoggingEvent)"/> > should occur. > /// </summary> > /// <returns></returns> > protected override bool PreAppendCheck() > { > // PreAppendCheck() should return false for > non-WMI event objects. > // However, if does not have access to the > loggingEvent. > // Therefore, the check must currently be > done in Append(). > return base.PreAppendCheck (); > } > > /// <summary> > /// Determine if the specified object is a WMI Event. > /// </summary> > /// <param name="o">The object to test.</param> > /// <returns></returns> > protected bool IsWmiEvent(object o) > { > // TODO: Lookup class in cache; return and > exit if found. > > // This attribute may only occur once. > > object[] attributes = > o.GetType().GetCustomAttributes(typeof(InstrumentationClassAtt > ribute), true); > > switch (attributes.Length) > { > case 0: > { > // no attribute present = non > wmi class > return false; > } > case 1: > { > bool isWmiEvent = false; > > // we have one attribute. > Now test for instumentation type. > // only events can be fired. > InstrumentationClassAttribute > ica = (InstrumentationClassAttribute)attributes[0]; > if (ica.InstrumentationType > == InstrumentationType.Event) > { > isWmiEvent = true; > } > > // TODO: Cache results for next time. > > return isWmiEvent; > } > default: > { > // by definition, this > attrbiute can only appear once. > throw new > ApplicationException("Multiple InstrumentationClassAttributes > attached."); > } > } > } > } > >
