Search code examples
c#system.diagnosticsopen-telemetry

System.Diagnostics.Activity- Parent/Child relation doesnt work when using multiple Threads


I have a problem that when parent activity is started by another thread, then the main thread wont see this activity, and next Activities that supposed to be children activities that are being created in main thread, wont be added to the parent Activity that i created in another thread.

What i have:

  1. Static definition of Sytem.Diagnostics.ActivitySource:

     public static readonly ActivitySource MyActivitySource = new ActivitySource("myAppName");
    
  2. Static definition of tracer provider OpenTelemetry.Trace.TraceProvider:

     public static TracerProvider traceProvider;
    
  3. Static definition of parentActivity:

     public static Activity parentActivity;
    

Now how it works:

  1. First Activity is created in another thread (with use of System.Timers.Timer), and then function is being invoked on main thread. Below code is executed only once- when parentActivity is not initialized:

     parentActivity = MyActivitySource.StartActivity("CycleActivity", ActivityKind.Internal);
     parentActivity.AddTag("hostname", Environment.MachineName)
     parentActivity.Start();
     mainForm.Invoke(new Action(() => initiateProcess()));
    
  2. Then in the initiateProcess function we start child activity- and everything is working fine, i can see in logs that children activity has parent 'CycleActivity' which was created in the 4th step from another thread.

     public void initiateProcess()
     {
         using (var subActivity= global.MyActivitySource.StartActivity("sendingTriggerToDevice"))
         {
             subActivity.Start();
             deviceTcp.Send("Trigger");
             subActivity.Stop();
         }
     }
    
  3. The problem occurs when in the next step, which consist of waiting for the data to come from deviceTcp. I have defined callback function, that is being run when deviceTcp send data to my application. And im creating activity inside that function and the body looks like:

    public void deviceTcpMessageReceived(string data)
    {
      using (var subActivity = global.MyActivitySource.StartActivity("deviceTcpMessageReceived"))
      {
         subActivity.Start();
         //.... additional stuff
      }
      parentActivity.Stop();
    }
    

Parent activity which has been started in the 4th step is not being stopped in the meantime. I know that deviceTcpMessageReceived was run, and everything worked fine, except that activity created in callback named deviceTcpMessageReceived was not wired to the parent activity.

  1. Summary

Any ideas, why the parent Activity is only visible by MyActivitySource when i synchronously Invoke a call (from subthread) of initiateProcesson the main thread, and it is not visible, when message from device is comming, and callback function deviceTcpMessageReceived is being ran on the Main thread?

I have read in the documentation System.Diagnostics.Activity that I can set an ActivityKind when i start new Activity that:

By default, all new Activities are set to Internal, which is appropriate for Activities that are an internal operation within an application with no remote parent or children.

But i have no idea what Activity Kind i should use. IMO they shouldnt impact relationship behaviour between parent/children activities. (And they clearly dont do it, when you look at 4 and 5th step that i described).

Also the parent activity from the subthread is not closed (there is no using, or Stop being called in the meantime), until we successfully receive data from deviceTcp, so i dont understand why Activity created in the 6th has no parent.

Any help or suggestion is welcome.


Solution

  • If Activities are not in the same scope, they might not wire up to each other. To solve your problem you have to set up Parent Activity in your callback function. If you want to wire up callback event to the Cycle Activity you have to call SetParentId method to wire up your callback Activity, your code could look like this:

    public void deviceTcpMessageReceived(string data)
    {
      using (var subActivity = global.MyActivitySource.StartActivity("deviceTcpMessageReceived"))
      {
         subActivity.SetParentId(parentActivity.TraceId, parentActivity.SpanId);
         subActivity.Start();
         //.... additional stuff
      }
      parentActivity.Stop();
    }