kevinhakanson.com

.NET Zipkin Investigation (Medidata.ZipkinTracerModule)

July 29, 2016 #dotnet #api #monitoring

Medidata.ZipkinTracerModule is a “A .NET implementation of the Zipkin Tracer client.”  I built a simple Web Api 2 service, similar to the code from the Zipkin JS Investigation.  The service recursively calls itself base on the count parameter:  (e.g., http://localhost:52399/api/hello?count=3).

Hello World! (x3)
Hello World! (x2)
Hello World!

ZipkinTracerModule is implemented as a OWIN Middleware and required an OwinStartup class to configure Zipkin.

public class OwinStartup  
{  
    public static ZipkinConfig _zipkinConfig;  
    public void Configuration(IAppBuilder app)  
    {  
        var zipkinConfig = new ZipkinConfig  
        {  
            Domain = request => new Uri("http://localhost"),  
            ZipkinBaseUri = new Uri("http://10.194.52.207:9411"),  
            SpanProcessorBatchSize = 1,  
            SampleRate = 1  
        };  
        // TODO: how can I register this with Unity and inject into Controller?  
        _zipkinConfig = zipkinConfig;  
        app.UseZipkin(zipkinConfig);  
    }  
}

The HelloController uses the ZipkinConfig and the OwinContext to configure a ZipkinClient which plugs into the .NET provided HttpClient.  This is what propagates the trace, parent, and span ID values.

public class HelloController : ApiController  
{  
    private IZipkinConfig _zipkinConfig = OwinStartup._zipkinConfig;  
    // TODO: how can I inject _zipkinConfig using Unity?  

    public async System.Threading.Tasks.Task<HttpResponseMessage> Get(int count = 1)  
    {  
        string msg = "Hello World!\n";  
        if (count > 1)  
        {  
            var context = System.Web.HttpContext.Current.GetOwinContext();  
            var client = new ZipkinClient(_zipkinConfig, context);  

            using (var httpClient = new HttpClient(new ZipkinMessageHandler(client)))  
            {  
                httpClient.BaseAddress = new Uri(Request.RequestUri, RequestContext.VirtualPathRoot);  
                HttpResponseMessage httpClientResponse = await httpClient.GetAsync($"api/hello?count={count - 1}");  
                if (httpClientResponse.IsSuccessStatusCode)  
                {  
                    var text = await httpClientResponse.Content.ReadAsStringAsync();  
                    msg = $"Hello World! (x{count})\n" + text;  
                }  
            }  
        }  

        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");  
        response.Content = new StringContent(msg, Encoding.UTF8, "text/plain");  

        return response;  
    }  
}  

There was a bug with the NuGet version, so needed to compile from source and make a fix (see Pull Request #91).  Also, C# code may look a bit ugly, and wasn’t able to get Unity working to dependency inject the ZipkinConfig data.

The final result from http://10.194.52.207:9411/traces/4e3c247d68677a1e?serviceName=localhost.

[  
  {  
    "traceId": "4e3c247d68677a1e",  
    "name": "get",  
    "id": "4e3c247d68677a1e",  
    "timestamp": 1469831966269543,  
    "duration": 210907,  
    "annotations": [  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966269543,  
        "value": "sr"  
      },  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966480450,  
        "value": "ss"  
      }  
    ],  
    "binaryAnnotations": [  
      {  
        "key": "http.path",  
        "value": "/api/hello",  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        }  
      }  
    ]  
  },  
  {  
    "traceId": "4e3c247d68677a1e",  
    "name": "get",  
    "id": "4c3c03caf1337298",  
    "parentId": "4e3c247d68677a1e",  
    "timestamp": 1469831966361425,  
    "duration": 100997,  
    "annotations": [  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966361425,  
        "value": "cs"  
      },  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966383550,  
        "value": "sr"  
      },  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966449422,  
        "value": "cr"  
      },  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966462422,  
        "value": "ss"  
      }  
    ],  
    "binaryAnnotations": [  
      {  
        "key": "http.path",  
        "value": "/api/hello",  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        }  
      },  
      {  
        "key": "http.status",  
        "value": "200",  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        }  
      },  
      {  
        "key": "sa",  
        "value": "1",  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "127.0.0.1",  
          "port": 52399  
        }  
      }  
    ]  
  },  
  {  
    "traceId": "4e3c247d68677a1e",  
    "name": "get",  
    "id": "4f18dc0c1c6745e0",  
    "parentId": "4c3c03caf1337298",  
    "timestamp": 1469831966398553,  
    "duration": 63869,  
    "annotations": [  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966398553,  
        "value": "cs"  
      },  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966417424,  
        "value": "sr"  
      },  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966445425,  
        "value": "cr"  
      },  
      {  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        },  
        "timestamp": 1469831966462422,  
        "value": "ss"  
      }  
    ],  
    "binaryAnnotations": [  
      {  
        "key": "http.path",  
        "value": "/api/hello",  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        }  
      },  
      {  
        "key": "http.status",  
        "value": "200",  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "10.211.55.8",  
          "port": 52399  
        }  
      },  
      {  
        "key": "sa",  
        "value": "1",  
        "endpoint": {  
          "serviceName": "localhost",  
          "ipv4": "127.0.0.1",  
          "port": 52399  
        }  
      }  
    ]  
  }  
]