Using Google Stackdriver Tracing with dotnet

Feb 17, 2019 | 2 minutes read

Recently wanted to implement tracing in a non asp.net application and found zero good examples on how to implement. I looked at the source for the V2 implementation and got it working. Just for any future reference here is the code. The code pushes the spans every second. Using this nuget package Google.Cloud.Logging.V2

 public class StackTracer 
    {
        static readonly Random Random = new Random();

        private  Google.Api.Gax.ResourceNames.ProjectName projectName;
        private readonly TraceServiceClient traceClient;
        private readonly List<Span> spans = new List<Span>();

        public StackTracer()
        {
            var projectId = "";
            this.traceClient = TraceServiceClient.Create();
            this.projectName = new Google.Api.Gax.ResourceNames.ProjectName(projectId);

            var uploadTimer = new Timer(1_000);
            uploadTimer.Elapsed += this.OnTimedEvent;
            uploadTimer.AutoReset = true;
            uploadTimer.Enabled = true;
        }

        public void AddTrace(string traceId, string spanName, DateTimeOffset start, DateTimeOffset end)
        {
            var hexTraceId = Guid.Parse(traceId).ToString("N");
            var spanId = GetRandomHexNumber(16);
            var legitSpanName = new SpanName(this.projectName.ProjectId, hexTraceId, spanId);
            var truncString = new TruncatableString
            {
                Value = spanName,
                TruncatedByteCount = 0
            };

            var span = new Span
            {
                SpanName = legitSpanName,
                DisplayName = truncString,
                SpanId = spanId,
                StartTime = Timestamp.FromDateTimeOffset(start),
                EndTime = Timestamp.FromDateTimeOffset(end)
            };
            lock (this.spans)
            {
                this.spans.Add(span);
            }
        }

        private void OnTimedEvent(Object source, ElapsedEventArgs e)
        {
            var timeSpans = new List<Span>();
            // Keep the lock has tight has possible, grab the values we need then clear out the existing ones.
            lock (this.spans)
            {
                timeSpans.AddRange(this.spans);
                this.spans.Clear();
            }

            // Just in case things back up, ensure we write each one without clobbering via threads
            lock (this.traceClient)
            {
                if (!timeSpans.Any())
                {
                    return;
                }

                try
                {
                    this.traceClient.BatchWriteSpans(this.projectName, timeSpans);
                }
                catch (Exception ex)
                {
                }
            }
        }

        /// Taken from a random stackoverflow. Should see if there is way to make it cleaner. Used to get
        ///  the spanid. Spanids have to be unique within a traceid
        private static string GetRandomHexNumber(int digits)
        {
            var buffer = new byte[digits / 2];
            Random.NextBytes(buffer);
            string result = String.Concat(buffer.Select(x => x.ToString("X2")).ToArray());
            if (digits % 2 == 0)
            {
                return result;
            }
            return result + Random.Next(16).ToString("X");
        }
    }