dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.96k stars 4.65k forks source link

[Question] Any plan to use roslyn source generator to make `EventSource` and `ActivitySource` easily? #107069

Open Cricle opened 2 weeks ago

Cricle commented 2 weeks ago

EventSource

According to eventsource-getting-started

Eventone must call like WriteEvent or WriteEventCore to write event.

Sample for ArrayPoolEventSource.cs, must new the EventData array, write value and call WriteEventCore. But in reality, these codes are fixed, got value ptr->set value size->set is reserved.

https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Buffers/ArrayPoolEventSource.cs#L54

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern",
                   Justification = EventSourceSuppressMessage)]
[Event(1, Level = EventLevel.Verbose)]
internal unsafe void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId)
{
    EventData* payload = stackalloc EventData[4];
    payload[0].Size = sizeof(int);
    payload[0].DataPointer = ((IntPtr)(&bufferId));
    payload[0].Reserved = 0;
    payload[1].Size = sizeof(int);
    payload[1].DataPointer = ((IntPtr)(&bufferSize));
    payload[1].Reserved = 0;
    payload[2].Size = sizeof(int);
    payload[2].DataPointer = ((IntPtr)(&poolId));
    payload[2].Reserved = 0;
    payload[3].Size = sizeof(int);
    payload[3].DataPointer = ((IntPtr)(&bucketId));
    payload[3].Reserved = 0;
    WriteEventCore(1, 4, payload);
}

Any plan make the EventSource code generate? Like that.

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern",
                   Justification = EventSourceSuppressMessage)]
[Event(1, Level = EventLevel.Verbose)]
internal partial unsafe void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId);

//The code generated
internal partial unsafe void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId)
{
    global::System.Diagnostics.Tracing.EventSource.EventData* payload= stackalloc 
    global::System.Diagnostics.Tracing.EventSource.EventData[4];
    datas[0] = new global::System.Diagnostics.Tracing.EventSource.EventData
    {
        DataPointer = (nint)(&bufferId),
        Size = sizeof(int)
    };
   //...
    WriteEventCore(1, 4, payload);
}

ActivitySource

According to distributed-tracing-instrumentation-walkthroughs

Everyone want to add distributed-tracing-instrumentation-walkthroughs, must to do that create an activitysource(->add tags)->add events

But the events possible like eventsource event define, so sometimes you have to write two similar codes.

For me

I have a project that attempted to generate these from the source, but there were many unexpected issues with the details or implementation. That's why I want to ask if there are any plans to implement these in the runtime.

For my project, I make the Logger <-> ActivitySource <-> EventSource interoperable


internal class Program
{
    private static void Main(string[] args)
    {
        var logger = new NullLoggerFactory().CreateLogger<Program>();
        using (var a = TestActivity.source.StartActivity("test"))
        {
            TestActivity.Raise(logger,123);
        }
    }
}
[EventSource(Name = "test"), EventSourceGenerate(GenerateSingleton = true)]
internal partial class TestEventSource : EventSource
{
}

internal static partial class TestActivity
{
    public static readonly ActivitySource source = new ActivitySource("test");
}

[MapToEventSource(typeof(TestEventSource))]
[MapToActivity(typeof(TestActivity), WithEventSourceCall = true, GenerateWithLog = true)]
internal static partial class Logger
{
    [Event(1)]
    [LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "{i} raised!")]
    public static partial void Raise(ILogger logger, int i);
}

Will generate codes

internal partial class TestEventSource
{
    [Diagnostics.Generator.Core.Annotations.EventSourceAccesstorInstanceAttribute]
    public static readonly global::TestEventSource Instance = new global::TestEventSource();
}
[global::Diagnostics.Generator.Core.Annotations.ActivityMapToEventSourceAttribute(typeof(global::TestEventSource), 1)]
internal partial class TestActivity
{
#region Raise
    [global::Diagnostics.Generator.Core.Annotations.ActivityMapToEventAttribute(1, "Raise", new global::System.Type[] { typeof(int) })]
    public static void Raise(int i, global::System.DateTimeOffset timestamp = default, global::System.Collections.Generic.IEnumerable<global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>>? additionTags = null, [global::System.Runtime.CompilerServices.CallerFilePathAttribute] string? filePath = null, [global::System.Runtime.CompilerServices.CallerMemberNameAttribute] string? memberName = null, [global::System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0)
    {
        Raise(global::System.Diagnostics.Activity.Current, i, timestamp, additionTags);
    }

    [global::Diagnostics.Generator.Core.Annotations.ActivityMapToEventAttribute(1, "Raise", new global::System.Type[] { typeof(int) })]
    public static void Raise(global::System.Diagnostics.Activity? activity, int i, global::System.DateTimeOffset timestamp = default, global::System.Collections.Generic.IEnumerable<global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>>? additionTags = null, [global::System.Runtime.CompilerServices.CallerFilePathAttribute] string? filePath = null, [global::System.Runtime.CompilerServices.CallerMemberNameAttribute] string? memberName = null, [global::System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0)
    {
        if (global::TestEventSource.Instance.IsEnabled())
        {
            global::TestEventSource.Instance.Raise(i);
        }

        if (activity != null)
        {
            global::System.Diagnostics.ActivityTagsCollection? tags = null;
            tags = new global::System.Diagnostics.ActivityTagsCollection();
            tags["i"] = i;
            if (additionTags != null)
            {
                foreach (global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?> tag in additionTags)
                {
                    if (tag.Key != null)
                    {
                        tags[tag.Key] = tag.Value;
                    }
                }
            }

            activity.AddEvent(new global::System.Diagnostics.ActivityEvent("Raise", timestamp, tags));
            OnRaise(activity, i, timestamp, additionTags);
        }
    }

    static partial void OnRaise(global::System.Diagnostics.Activity? activity, int i, global::System.DateTimeOffset timestamp = default, global::System.Collections.Generic.IEnumerable<global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>>? additionTags = null, [global::System.Runtime.CompilerServices.CallerFilePathAttribute] string? filePath = null, [global::System.Runtime.CompilerServices.CallerMemberNameAttribute] string? memberName = null, [global::System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0);
    [global::Diagnostics.Generator.Core.Annotations.ActivityMapToEventAttribute(1, "Raise", new global::System.Type[] { typeof(int) })]
    public static void Raise(global::Microsoft.Extensions.Logging.ILogger logger, int i, global::System.DateTimeOffset timestamp = default, global::System.Collections.Generic.IEnumerable<global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>>? additionTags = null, [global::System.Runtime.CompilerServices.CallerFilePathAttribute] string? filePath = null, [global::System.Runtime.CompilerServices.CallerMemberNameAttribute] string? memberName = null, [global::System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0)
    {
        global::Logger.Raise(logger, i);
        Raise(global::System.Diagnostics.Activity.Current, i, timestamp, additionTags);
    }

    [global::Diagnostics.Generator.Core.Annotations.ActivityMapToEventAttribute(1, "Raise", new global::System.Type[] { typeof(int) })]
    public static void Raise(global::Microsoft.Extensions.Logging.ILogger logger, global::System.Diagnostics.Activity? activity, int i, global::System.DateTimeOffset timestamp = default, global::System.Collections.Generic.IEnumerable<global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>>? additionTags = null, [global::System.Runtime.CompilerServices.CallerFilePathAttribute] string? filePath = null, [global::System.Runtime.CompilerServices.CallerMemberNameAttribute] string? memberName = null, [global::System.Runtime.CompilerServices.CallerLineNumberAttribute] int lineNumber = 0)
    {
        global::Logger.Raise(logger, i);
        Raise(activity, i, timestamp, additionTags);
    }
#endregion
}
internal partial class TestEventSource
{
    [System.Diagnostics.Tracing.EventAttribute(1, Level = global::System.Diagnostics.Tracing.EventLevel.Informational, Message = "{i} raised!")]
    public void Raise(int i)
    {
        global::System.Diagnostics.Tracing.EventSource.EventData* datas = stackalloc global::System.Diagnostics.Tracing.EventSource.EventData[1];
        datas[0] = new global::System.Diagnostics.Tracing.EventSource.EventData
        {
            DataPointer = (nint)(&i),
            Size = sizeof(int)
        };
        WriteEventWithRelatedActivityIdCore(1, null, 1, datas);
        OnRaise(i);
    }

    [global::System.Diagnostics.Tracing.NonEventAttribute]
    partial void OnRaise(int i);
}

I try to make use OTEL easily, but perhaps this is not a perfect solution.

julealgon commented 2 weeks ago

ActivitySource is mentioned in the title but not in the post. Can you elaborate what the benefits would be for that?

Cricle commented 2 weeks ago

ActivitySource is mentioned in the title but not in the post. Can you elaborate what the benefits would be for that?

Oh!I forgot write that 😱

tommcdon commented 2 weeks ago

Hi @Cricle! Some source generator work has been done on EventSource, for example https://github.com/dotnet/runtime/pull/45699. Remaining work is tracked on https://github.com/dotnet/runtime/issues/56154. Our team does not have immediate plans to work on this - please do feel free to submit a PR if you plan on working on it as we welcome community contributions!

Cricle commented 2 weeks ago

I think this will be a huge challenge for me.

But I will try to do it.

I am not familiar with the code structure of runtime, so I may not be able to complete it in 10.0.0.

Cricle commented 1 week ago

But in reality, the form of ActivitySource event generation is still unknown