jonwagner / EventSourceProxy

EventSourceProxy (ESP) is the easiest way to add scalable Event Tracing for Windows (ETW) logging to your .NET program
Other
97 stars 20 forks source link

Can dynamic be used as event argument? #3

Closed pbolduc closed 11 years ago

pbolduc commented 11 years ago

I am trying use dynamic arguments and expect them to be json serialized. It seems to work when I log locally using the Semantic Logging Service (SemanticLogging-svc.exe -c), but when I log to Azure table storage, all of my data is being stripped out and an empty string is being logged.

[EventSource(Guid = EventSourceGuid, Name = EventSourceName)]
public abstract class ApiEventSource : EventSource
{
    /// <summary>
    /// Gets the global instance of the ApiEventSource log.
    /// </summary>
    public readonly static ApiEventSource Log = EventSourceImplementer.GetEventSourceAs<ApiEventSource>();

    [Event(1, Level = Verbose, Keywords = Keywords.Default, Message = "Email address changed.")]
    public abstract void EmailAddressChanged(dynamic data);
}

Later in my code, I do:

string email = "...";
string newEmailAddress = "...";

var data = new { OldAddress = email, NewAddress = newEmailAddress };
Log.EmailAddressChanged(data);

But in my SLABLogsTable, I only get empty strings.

slablogstable

I am wondering if this is a valid use case? I want to avoid having to create logging POCO for every event. Do I have to register a provider or something?

jonwagner commented 11 years ago

The best way to do it would be to put parameters on the method:

Public void OnAddressChanged(string old, string new)

It would eliminate your anonymous class on the method calls.

There are probably some good use cases for accepting dynamics. I'm also surprised that you are getting different behavior depending on the log target.

I haven't gone too deep in this code lately, so give me a few days so set up some test cases. I should be able to give you a little more info tomorrow.

On Sep 24, 2013, at 7:08 PM, "Phil Bolduc" notifications@github.com wrote:

I am trying use dynamic arguments and expect them to be json serialized. It seems to work when I log locally using the Semantic Logging Service (SemanticLogging-svc.exe -c), but when I log to Azure table storage, all of my data is being stripped out and an empty string is being logged.

[EventSource(Guid = EventSourceGuid, Name = EventSourceName)] public abstract class ApiEventSource : EventSource { ///

/// Gets the global instance of the ApiEventSource log.
/// </summary>

public readonly static ApiEventSource Log = EventSourceImplementer.GetEventSourceAs<ApiEventSource>();
[Event(1, Level = Verbose, Keywords = Keywords.Default, Message = "Email address changed.")]
public abstract void EmailAddressChanged(dynamic data);

} Later in my code, I do:

string email = "..."; string newEmailAddress = "...";

var data = new { OldAddress = email, NewAddress = newEmailAddress }; Log.EmailAddressChanged(data); But in my SLABLogsTable, I only get empty strings.

I am wondering if this is a valid use case? I want to avoid having to create logging POCO for every event. Do I have to register a provider or something?

— Reply to this email directly or view it on GitHub.

pbolduc commented 11 years ago

One reason I want to avoid multiple parameters is that the Azure will create a column in the table for each parameter name. I will get a column 'old' and column 'new'. Our event source has a large number of events, each event would have different parameter names. It makes it difficult

I am currently testing with an alternative approach for creating a dynamic type,

dynamic data = new ExpandoObject();
data.OldAddress = "...";
data.NewAddress = "...";
Log.EmailAddressChanged(data);

It is definitely not as pretty as my original. I just not sure if I have to do:

EventSourceImplementer.RegisterProvider<ExpandoObject>(new JsonObjectSerializer());

To make that work. If I have any break through, I will let you know.

pbolduc commented 11 years ago

More digging, came across this blog, where the author calls out that anonymous types are 'internal'.

http://joseoncode.com/2011/04/10/unit-tests-anonymous-types-and-dynamic/

I searched on MSDN and found:

http://msdn.microsoft.com/en-us/vstudio/ee957397.aspx

"The compiler generates an internal sealed class that models the anonymous type."
jonwagner commented 11 years ago

That would work. You might even be able to just do EventSourceImplementer.RegisterProvider(new JsonObjectSerializer()); I see your point on the columns. Maybe the solution would be to add a provider that wraps all of the parameters in another layer? Then: Log.AddressChanged(old, new); would log: { data: { old: "foo", new: "bar" } } Best of both worlds?

Date: Tue, 24 Sep 2013 17:05:23 -0700 From: notifications@github.com To: EventSourceProxy@noreply.github.com CC: jonwagner@hotmail.com Subject: Re: [EventSourceProxy] Can dynamic be used as event argument? (#3)

One reason I want to avoid multiple parameters is that the Azure will create a column in the table for each parameter name. I will get a column 'old' and column 'new'. Our event source has a large number of events, each event would have different parameter names. It makes it difficult

I am currently testing with an alternative approach for creating a dynamic type,

dynamic data = new ExpandoObject(); data.OldAddress = "..."; data.NewAddress = "..."; Log.EmailAddressChanged(data);

It is definitely not as pretty as my original. I just not sure if I have to do:

EventSourceImplementer.RegisterProvider(new JsonObjectSerializer());

To make that work. If I have any break through, I will let you know.

— Reply to this email directly or view it on GitHub.

pbolduc commented 11 years ago

Another update. Appears the serialization IS working in one of our Azure environments. I will have to do more research to identify what the difference could be between the environments.

pbolduc commented 11 years ago

The only different between my two environments is that in the working environment, the level at which events are being logged at is Verbose. In the non-working environment, the logging level is restricted to Informational. When I change the logging level to Verbose, I get the details.

Can you think of anything in EventSourceProxy that would contribute to that?

pbolduc commented 11 years ago

I think I found the issue:

public class JsonObjectSerializer : TraceSerializationProvider
{
    #region Constructors
    /// <summary>
    /// Initializes a new instance of the JsonObjectSerializer class.
    /// The default is to serialize objects only in Verbose tracing.
    /// </summary>
    public JsonObjectSerializer() : base(EventLevel.Verbose)
    {
    }
pbolduc commented 11 years ago

I tried unsuccessfully to use:

    static ApiEventSource()
    {
        EventSourceImplementer.RegisterProvider(typeof(ApiEventSource), typeof(JsonObjectSerializer), new JsonObjectSerializer(EventLevel.LogAlways));
        Log = EventSourceImplementer.GetEventSourceAs<ApiEventSource>();
    }
jonwagner commented 11 years ago

Aha. I'll dive into that!

On Sep 25, 2013, at 12:57 AM, "Phil Bolduc" notifications@github.com wrote:

I think I found the issue:

public class JsonObjectSerializer : TraceSerializationProvider {

region Constructors

/// <summary>

/// Initializes a new instance of the JsonObjectSerializer class.
/// The default is to serialize objects only in Verbose tracing.
/// </summary>

public JsonObjectSerializer() : base(EventLevel.Verbose)
{
}

— Reply to this email directly or view it on GitHub.

jonwagner commented 11 years ago

That overload of registerprovider is supposed to be internal. You should just be able to use:

EventSourceImplementer.RegisterProvider(typeof(ApiEventSource), new JsonObjectSerializer(EventLevel.LogAlways));

Or:

EventSourceImplementer.RegisterProvider<ApiEventSource>(new JsonObjectSerializer(EventLevel.LogAlways));

If you use the 3 parameter version, the 2nd parameter should be typeof(TraceSerializationProvider), but since it's going to be private in the next build, you should just use the two parameter version.

jonwagner commented 11 years ago

This should take care of you for now. I'm going to open up a feature request to see if we can do better blob logging.

You can use the anonymous class technique, but you're allocating a new object on the stack each time, so you will have a performance hit. Let's see if we can design a better way to take care of that.

pbolduc commented 11 years ago

That worked!

    static ApiEventSource()
    {
        EventSourceImplementer.RegisterProvider<ApiEventSource>(new JsonObjectSerializer(EventLevel.LogAlways));
        Log = EventSourceImplementer.GetEventSourceAs<ApiEventSource>();
    }