public async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var runInfo = new RunInfo {
                LastRunTime = (DateTimeOffset.UtcNow - TimeSpan.FromHours(1)).UtcTicks
            };

            if (File.Exists("runInfo.json"))
            {
                using var r = new StreamReader("runInfo.json");
                var json = await r.ReadToEndAsync();

                runInfo = JsonConvert.DeserializeObject <RunInfo>(json);
            }

            var jaegerUrl = $"http://{_configuration["JaegerUrl"]}";

            var channel = GrpcChannel.ForAddress(jaegerUrl);

            var client = new QueryService.QueryServiceClient(channel);

            var findTracesRequest = new FindTracesRequest
            {
                Query = new TraceQueryParameters
                {
                    OperationName = "ProcessDataEvent",
                    ServiceName   = "Orchestrator",
                    StartTimeMin  = Timestamp.FromDateTimeOffset(new DateTimeOffset(runInfo.LastRunTime, TimeSpan.Zero)),
                    StartTimeMax  = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
                    SearchDepth   = 1000
                }
            };
            // I could store the time of the last run and look for traces since then...
            // I could also keep track of all the traces ever looked up, and ignore those.
            var streamResult = client.FindTraces(findTracesRequest, cancellationToken: stoppingToken).ResponseStream;

            List <ByteString> traceIds       = new List <ByteString>();
            var          currentTraceId      = "";
            TraceDetails currentTraceDetails = null;

            using var fileName   = File.OpenWrite(_configuration["outputPath"]);
            using var textWriter = new StreamWriter(fileName);

            var headers = string.Join(',', "tId"
                                      , "dataPullType"
                                      , "FromZone"
                                      , "ToZone"
                                      , "DataSize"
                                      , "TotalDuration"
                                      , "TriggerStepClientDuration"
                                      , "TriggerStepServerDuration"
                                      , "DataPullDuration"
                                      , "DataPushDuration"
                                      , "ComputeDuration"
                                      , "DataMasterPullCall"
                                      , "DataPeerPullCall"
                                      , "DataMasterPushCall");

            await textWriter.WriteLineAsync(headers);

            while (await streamResult.MoveNext(CancellationToken.None))
            {
                var currentChunk = streamResult.Current;

                // I need to identify the numbers on my figure.
                // And then, for each trace, calculate the aggregates
                foreach (var span in currentChunk.Spans)
                {
                    if (runInfo.UsedTraceIds.Contains(span.TraceId.ToBase64()))
                    {
                        Console.WriteLine("Found an old trace, ignoring");
                        continue;
                    }

                    if (span.TraceId.ToBase64() != currentTraceId)
                    {
                        await textWriter.FlushAsync();

                        // new trace is starting.
                        if (currentTraceDetails != null)
                        {
                            await textWriter.WriteLineAsync(currentTraceDetails.ToString());
                        }
                        currentTraceDetails         = new TraceDetails();
                        currentTraceId              = span.TraceId.ToBase64();
                        currentTraceDetails.TraceId = currentTraceId;
                        traceIds.Add(span.TraceId);
                    }

                    AddInfoToCurrentTraceDetails(span, currentTraceDetails);
                }
            }

            if (currentTraceDetails != null)
            {
                await textWriter.WriteLineAsync(currentTraceDetails.ToString());
            }


            foreach (var traceId in traceIds)
            {
                runInfo.UsedTraceIds.Add(traceId.ToBase64());
            }

            runInfo.LastRunTime = DateTimeOffset.UtcNow.Ticks;

            await File.WriteAllTextAsync("runInfo.json", JsonConvert.SerializeObject(runInfo));
        }
        private static void AddInfoToCurrentTraceDetails(Span span, TraceDetails currentTraceDetails)
        {
            if (span.Process.ServiceName == "Orchestrator")
            {
                if (span.OperationName == "ProcessDataEvent")
                {
                    // set the total duration
                    currentTraceDetails.TotalDuration = (int)span.Duration.ToTimeSpan().TotalMilliseconds;
                }

                if (span.OperationName == "SidecarService/TriggerStep")
                {
                    // Trigger step duration (excludes the call to the Kubernetes API.
                    currentTraceDetails.TriggerStepClientDuration = (int)span.Duration.ToTimeSpan().TotalMilliseconds;

                    if (currentTraceDetails.DataPullType == "none")
                    {
                        currentTraceDetails.DataPullType = "local";
                    }
                }
            }

            if (span.Process.ServiceName == "Sidecar")
            {
                if (span.OperationName == "SidecarService/TriggerStep")
                {
                    currentTraceDetails.TriggerStepServerDuration = (int)span.Duration.ToTimeSpan().TotalMilliseconds;
                }

                if (span.OperationName == "StorageAdapter/PullData")
                {
                    // 4
                    currentTraceDetails.DataPullDuration = (int)span.Duration.ToTimeSpan().TotalMilliseconds;
                }

                if (span.OperationName == "StorageAdapter/PushData")
                {
                    // 11
                    currentTraceDetails.DataPushDuration = (int)span.Duration.ToTimeSpan().TotalMilliseconds;
                }

                if (span.OperationName == "ComputeStepService/TriggerCompute-Single")
                {
                    // 9
                    currentTraceDetails.ComputeDuration = (int)span.Duration.ToTimeSpan().TotalMilliseconds;
                }
            }

            if (span.Process.ServiceName == "DataAdapter")
            {
                if (span.OperationName == "DataMasterService/GetAddrForDataChunk")
                {
                    currentTraceDetails.DataPullType       = "remote";
                    currentTraceDetails.DataMasterPullCall = (int)span.Duration.ToTimeSpan().TotalMilliseconds;
                }

                if (span.OperationName == "StorageAdapter/PullData")
                {
                    // Here I need to extract a few things
                    var from = span.Tags.SingleOrDefault(kv => kv.Key == "wf-from");
                    var to   = span.Tags.SingleOrDefault(kv => kv.Key == "wf-to");
                    var ds   = span.Tags.SingleOrDefault(kv => kv.Key == "wf-ds");
                    currentTraceDetails.FromZone = from == null ? "null" : from.VStr.Split("-")[1];
                    currentTraceDetails.ToZone   = to == null ? "null" : to.VStr.Split("-")[1];
                    currentTraceDetails.DataSize = ds?.VInt64 ?? 0;

                    currentTraceDetails.DataPeerPullCall = (int)span.Duration.ToTimeSpan().TotalMilliseconds;
                }

                if (span.OperationName == "DataMasterService/SignalDataChunkAvailable")
                {
                    currentTraceDetails.DataMasterPushCall = (int)span.Duration.ToTimeSpan().TotalMilliseconds;
                }
            }
        }