Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EventSource EventWrittenEventArgs.ActivityId isn't always set #42128

Open
MihaZupan opened this issue Sep 11, 2020 · 7 comments
Open

EventSource EventWrittenEventArgs.ActivityId isn't always set #42128

MihaZupan opened this issue Sep 11, 2020 · 7 comments
Labels
area-System.Diagnostics.Tracing enhancement Product code improvement that does NOT require public API changes/additions
Milestone

Comments

@MihaZupan
Copy link
Member

MihaZupan commented Sep 11, 2020

I ran into this while working on tests for #41670.

public Guid ActivityId
{
get
{
Guid activityId = m_activityId;
if (activityId == Guid.Empty)
{
activityId = EventSource.CurrentThreadActivityId;
}
return activityId;
}
internal set => m_activityId = value;
}

The ActivityId property will return the m_activityId field if set (Start/Stop events).
If it wasn't set, it will fetch the current thread's activity id - but it won't save it.

This means that the behavior is correct when the EventArgs are analyzed in the event callback itself.
If the EventArgs are stored and looked at later, ActivityId info is lost (it'll always return Zero).

Considering that the property is described as Activity ID for the thread on which the event was written, this seems like a bug and not intentional behavior (which is also incredibly confusing to debug).

Is there a significant perf drawback to the approach of simply always fetching EventSource.CurrentThreadActivityId when creating the EventArgs and removing the getter logic?

@ghost
Copy link

ghost commented Sep 11, 2020

Tagging subscribers to this area: @tarekgh, @tommcdon, @pjanotti
See info in area-owners.md if you want to be subscribed.

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Sep 11, 2020
@tarekgh
Copy link
Member

tarekgh commented Sep 11, 2020

CC @noahfalk who can address this issue.

@tommcdon tommcdon added this to the 6.0.0 milestone Sep 11, 2020
@tommcdon tommcdon removed the untriaged New issue has not been triaged by the area owner label Sep 11, 2020
@tommcdon
Copy link
Member

adding @sywhang @josalem. It sounds like we should document this behavior for 5.0 and try to address it in 6.0.

@josalem
Copy link
Contributor

josalem commented Sep 11, 2020

This means that the behavior is correct when the EventArgs are analyzed in the event callback itself.
If the EventArgs are stored and looked at later, ActivityId info is lost (it'll always return Zero).

How are you consuming these events? Are you listening to these events live via an EventListener or are you using EventPipe to collect these events and save them to a file?

@MihaZupan
Copy link
Member Author

MihaZupan commented Sep 11, 2020

The use case here is using TestEventListener during tests, and saving events to a ConcurrentQueue.
After the tested code runs, EventArgs are analyzed (including comparing ActivityIds).

I modified the TestEventListener to allow subscribing to multiple event sources (including TplEventSource to track activities in tests) - I will ping you on the PR.

using var listener = new TestEventListener("System.Net.Sockets", EventLevel.Verbose, 0.1);
listener.AddActivityTracking(); // Doesn't exist on current TestEventListener

var events = new ConcurrentQueue<EventWrittenEventArgs>();
await listener.RunWithCallbackAsync(events.Enqueue, async () =>
{
    // Stuff that triggers events
}

EventWrittenEventArgs start = Assert.Single(events, e => e.EventName == "FooStart");
EventWrittenEventArgs something = Assert.Single(events, e => e.EventName == "FooSomething");
EventWrittenEventArgs stop = Assert.Single(events, e => e.EventName == "FooStop");

Assert.NotEqual(Guid.Empty, start.ActivityId);
Assert.Equal(start.ActivityId, something.ActivityId); // Fail
Assert.Equal(start.ActivityId, stop.ActivityId);

Here, ActivityId would be set correctly for FooStart/FooStop, but Guid.Empty for FooSomething.

If instead you look at the EventArgs inside the RunWithCallbackAsync callback, it will be correct (as you're still on the Thread that logged the event).

listener.RunWithCallbackAsync(e => { Console.WriteLine(e.ActivityId); events.Enqueue(e); }, ...

@josalem
Copy link
Contributor

josalem commented Sep 11, 2020

I'm not sure if there is an explicit reason for this behavior (the code hasn't changed in ~1 year), but I agree with Tom on how we should handle this (document now; address/change in 6.0). For now, would it be sufficient for the test to store a tuple (Guid, EventWrittenEventArgs) in the queue and just fetch the Activity ID in the callback directly?

@MihaZupan
Copy link
Member Author

Yes a temporary fix would be changing the test / adding 2 lines of reflection into TestEventListener to set the private field.

MihaZupan added a commit to MihaZupan/runtime that referenced this issue Sep 14, 2020
MihaZupan added a commit to MihaZupan/runtime that referenced this issue Sep 24, 2020
MihaZupan added a commit that referenced this issue Sep 25, 2020
* Correct NameResolutionTelemetry logic

Taken from c4c9a2f99b7e339388199086d3014041abccc21e

* Enable listening to multiple sources with TestEventListener

* Workaround EventWrittenEventArgs bug when the EventArgs are stored

Workaround #42128

* Correct System.Net.Sockets Telemetry

* Avoid using value tuple in TestEventListener

* Remove unnecessary argument to OnCompletedInternal

* Remove redundant Telemetry.IsEnabled check

* Log Connect/Accept start before the initial context capture

* Use SocketHelperBase in Accept tests

* Avoid duplicate events for BeginConnect without ConnextEx support

* Enable Sync Socket tests

* Revert unrelated SocketPal change

* Log the correct ErrorCode in case of socket disposal

* Add more info on TelemetryTest timeout

* Add PlatformSpecific attribute to ConnectFailure test

* Add missing BeginConnect AfterConnect call on sync failure

* Add comment around GetHelperBase

* Correct WaitForEventCountersAsync helper

* Assert against SocketError.TimedOut in ConnectFailed test

* Add EndConnect comment

* Log Failure around sync SocketPal exceptions

* Don't assert that the exception message is empty

* Dispose socket in a different Thread

* Disable Telemetry failure tests for Sync on RedHat7

* Use more descriptive names in MemberData generation

* Avoid using reflection for #42128 workaround

* Remove ConnectCanceled event
@josalem josalem added the enhancement Product code improvement that does NOT require public API changes/additions label Nov 7, 2020
@tommcdon tommcdon modified the milestones: 6.0.0, 7.0.0 Jun 16, 2021
@tommcdon tommcdon modified the milestones: 7.0.0, Future Jun 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Diagnostics.Tracing enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

No branches or pull requests

5 participants