consuming WMI events from Logging Application Block

Topics: Logging Application Block
Oct 10, 2008 at 11:18 AM
Hello,

we like to use the WMI tracer of the Logging Application Block. The sending of log messages works quite good and I can see that an event got fired. But I don't know how to get the message information via WMI. I can't find any information in which WMI tables the information is saved. Can anybody help me, please?

Here is the code to see that an event got fired, but it doesn't show me the message:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\EnterpriseLibrary")
Set objEvents = objWMIService.ExecNotificationQuery _
("SELECT * FROM LogEntryV20")

Wscript.Echo "Waiting for events ..."
Do While(True)
    Set objReceivedEvent = objEvents.NextEvent

    'report an event
    Wscript.Echo "LogEntryV20 event has occurred."

Loop

Thanks
Daniel

Oct 10, 2008 at 11:50 AM

Hi Daniel,

You can take a look at the test fixture for the wmi trace listener (available at Blocks\Logging\Tests\Logging\TraceListeners\WmiListenerFixture.cs) for one example of how the events can be consumed.

Regards,

Fernando

Oct 13, 2008 at 4:40 PM
Hi Fernando,

thanks for your answer! I have now already a much better understanding of how it all works together.

I have just one question/problem regarding the test

public void TestLoggingACustomLogEntry()

in the file you mentioned (Blocks\Logging\Tests\Logging\TraceListeners\WmiListenerFixture.cs).

I need to use a CustomLogEntry, but I'm not able to extract that test to my own program. Even when I create a complete new solution and copy the test code into a project, I cannot see the arrival of the event via WMI. I tried it now already for more than a day, so it's getting a little bit frustrating...

So, why is it running in this test environment and why not in a new project?? I even debugged through the code and the Instrumentation.Fire is called but it does not arrive...

If somebody can help me, that would be really great!

Thanks
Daniel




I have just one question/problem regarding the test

 

Oct 13, 2008 at 5:05 PM
Hi Daniel,

You need to install WMI classes. If you don't, calls to Instrumentation.Fire will fail silently. You need to add the [Instrumented] attribute to the assembly where your custom log entry resides, and you also need a project installer class on your assembly to work with installutil.exe; having a class which just derives from System.Management.Instrumentation.DefaultManagementProjectInstaller does the trick.

Regards,
Fernando
Oct 14, 2008 at 9:16 AM
Hi Fernando,

I tried now to register all used Enterprise Library dls with installutil and added the following things to my dll:

[

RunInstaller(true)]

 

 

public class OldInstaller :

 

 

DefaultManagementProjectInstaller { }

 

and in AssemblyInfo.cs:

[

assembly: Instrumented(@"root\EnterpriseLibrary")]

 

and registered the DLL with installutil but I still can't see the event. If I change the Instrumented root\EnterpriseLibrary to a different name I get an error "invalid namespace" at ManagementInstrumentation.Fire in the Logging Block.

And I don't understand why in the WmiListenerFixture.cs file I can't see any of the extensions you mentioned.
If I use the LogEntry class insted of my custom logEntry class the event is directly fired and I can catch it.

So, why is my custom LogEntry not working???

Any suggestions are welcome...

Regards
Daniel

Oct 14, 2008 at 1:06 PM
Hi Daniel,

This worked for me:

using System;

using System.ComponentModel;

using System.Diagnostics;

using System.Management;

using System.Management.Instrumentation;

using System.Text;

using System.Threading;

using Microsoft.Practices.EnterpriseLibrary.Logging;

using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;

 

[assembly: Instrumented(@"root\EnterpriseLibrary")]

 

namespace WmiTraceListenerTest

{

    class Program

    {

        static void Main(string[] args)

        {

            string wmiPath = @"\root\EnterpriseLibrary";

            ManagementScope scope = new ManagementScope(@"\\." + wmiPath);

            scope.Options.EnablePrivileges = true;

 

            StringBuilder sb = new StringBuilder("SELECT * FROM ");

            sb.Append("LogEntryV20");

            string query = sb.ToString();

            EventQuery eq = new EventQuery(query);

 

            using (ManagementEventWatcher watcher = new ManagementEventWatcher(scope, eq))

            {

                watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);

                watcher.Start();

 

                WmiTraceListener listener = new WmiTraceListener();

                listener.TraceData(

                    new TraceEventCache(),

                    "source",

                    TraceEventType.Error,

                    0,

                    new MyLogEntry { Foo = "myfoo", Message = "message", Categories = new string[] { "category" } });

 

                Thread.Sleep(5000);

 

                watcher.Stop();

            }

        }

 

        private static void watcher_EventArrived(object sender, EventArrivedEventArgs e)

        {

            Console.WriteLine("Event arrived! {0}.", e.NewEvent.Properties["Foo"].Value);

        }

    }

 

    [InstrumentationClass(InstrumentationType.Event)]

    public class MyLogEntry : LogEntry

    {

        public string Foo { get; set; }

    }

 

    [RunInstaller(true)]

    public class MyInstaller : DefaultManagementProjectInstaller

    {

    }

}

<!--EndFragment-->
Hope this helps,
Fernando
Oct 14, 2008 at 1:47 PM
Hi Fernando,

thanks a lot for that code! It's finally running! I had two remaining issues in my code:
- I used a complex datatype in my derived CustomLogEntry class, that seems to prevent the event from firing
- I had to reboot my machine to allow CustomLogEntry to work (at least it worked after doing that, though normal LogEntry worked also before...)

Do you know if it's possible to use complex datatypes as return values for the public properties in the Custom LogEntry? WMI supports that in general, but the Application Block does seem to stop firing events completely if you use them...

So if you change your code in the following way you should see what I mean:

 

public class complexClass

 

{

 

private int myName;

 

 

public complexClass()

 

{

myName = 1;

}

 

public int MyName1

 

{

 

get { return myName; }

 

 

set { myName = value; }

 

}

}

[

InstrumentationClass(InstrumentationType.Event)]

 

 

public class MyLogEntry : LogEntry

 

{

 

public complexClass Foo { get { return new complexClass(); } }

 

}


Do you have any idea why this does not work?

Thanks
Daniel
Oct 14, 2008 at 2:02 PM

Hi Daniel,

CLI to WMI mapping is quite limited. According to http://msdn.microsoft.com/en-us/library/aa719697(VS.71).aspx embedded objects should work, but I haven't tried it on events. Try making the ComplexClass a WMI class of type "Instance" and see if it works.

Fernando

Oct 14, 2008 at 2:52 PM
Hi Fernando,

sorry, I don't know what you mean by making a class a WMI class of type "Instance". Can you please give me one example for that?

Thanks
Daniel
Oct 14, 2008 at 5:18 PM

[InstrumentationClass(InstrumentationType.Instance)] on the class.

 

Fernando
Oct 15, 2008 at 8:45 AM
Hi Fernando,

I tried that, but it doesn't work in that way...

Anyway, if you have another idea, please let me know, otherwise I will convert the object to simple datatypes. That's not so nice but it should work.

Thanks so much again for all your help, without that it would have been really difficult and cumbersome to find a solution!

Bye
Daniel
Oct 16, 2008 at 11:33 AM
Beats me :)

I've used nested objects with WMI.NET in the past, but not for event classes; it's entirely possible that it isn't supported. It's good you have a backup plan.

Cheers,
Fernando
Dec 16, 2008 at 2:39 PM
Hi

This thread is probably long over but I'll try anyway!

I've done something similar here where I've created my own custom log entry class. I've configured my application to log at various points (for timing stats) and I've configured the app so tha WMI events are fired when Logger.Write() is called.

I've written another small program that listens and consumes the WMI events. What I've found is that when I query the propertydatacollection of the ManagementBaseObject I find that its actually just the regular LogEntry (LogEntryV20) property collection rather than the CustomLogEntry property collection.

To me it just looks like its been recast or something along the way and lost the original context by which this event was logged. No matter what I try I can't get my custom logentry class to be the context of the ManagementBaseObject.

If anyone is still listening to this thread I can supply source code etc.

thanks for any help.

jk
Dec 17, 2008 at 7:20 AM
I think that's the normal behavior.  I've just based my assumption here through the WmiListenerFixture test class included in the entlib source since I'm not that familiar with this scenario. But you could send me your solution.


Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@avanade.com
Dec 17, 2008 at 8:56 AM
Edited Dec 17, 2008 at 9:00 AM
Hi

Thanks for getting back to me. I've since sorted it out but thought I'd post what I found anyway.

It seems that it depends on which Logger.Write() overload you call will determine whether or not your subclassed LogEntry gets preserved.

If you call Logger.Write(LogEntry logEntry) and have a consumer waiting for the WMI event to be caught you will get your subclassed LogEntry no problem.

If you call any other overload (e.g. Logger.Write(object message, ICollection<string> Categories) it will fail.

It's probably a misunderstanding on my part as I assumed all the Logger.Write() methods had the first argument being a LogEntry.. they are not. The first is of type object hence those overloads construct a new base LogEntry. My bad but it was pretty confusing.

I've attached code here just to demonstrate (never attached code before. sorry for any formatting issues):

using System.Text;
using System.ComponentModel;
using System.Management.Instrumentation;
using System.Management;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
using System.Diagnostics;
using System.Threading;

[assembly: Instrumented(@"root/EnterpriseLibrary")]
namespace customlogentry
{
    class Program
    {
        static void Main(string[] args)
        {
            ManagementScope scope = new ManagementScope(@"root\EnterpriseLibrary");
            scope.Connect();
            WqlEventQuery query = new WqlEventQuery("LogEntryV20");
            ManagementEventWatcher watcher = new ManagementEventWatcher(scope, query);
           
            watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
            watcher.Start();
           
            //Comment out these three lines and enable the other commented stuff to see it cast back to LogEntry
            MyLogEntry logEntry = new MyLogEntry("myfoo");
            logEntry.Categories = new string[] { "TestCategory" };
            Logger.Write(logEntry);
            //Logger.Write(new MyLogEntry("myfoo"), "TestCategory");
            Thread.Sleep(5000);
            watcher.Stop();

        }
        private static void watcher_EventArrived(object sender, EventArrivedEventArgs e)
        {
            Console.WriteLine("Event arrived! {0}.", e.NewEvent.Properties["Foo"].Value);
        }

    }

    [InstrumentationClass(InstrumentationType.Event)]
    public class MyLogEntry : LogEntry
    {
        private string _foo;
        public string Foo
        {
            get {return _foo;}
            set { _foo = value;}
        }

        public MyLogEntry(string foo)
        {
            _foo = foo;
        }
    }

    [RunInstaller(true)]
    public class MyInstaller : DefaultManagementProjectInstaller
    {
    }

}