Skip to content

Commit fbb1c04

Browse files
Add Producer and Consumer Trace Class (#238)
* create ConsumerTrace and ProducerTrace * simplify change to only addong ConsumerTrace * undo changes in client trace * add ConsumerTraceExtensions * finalize implementation * examples added for async message * update travis to latest dotnet version * update dotnet version to 3.1.100 * revert travis to dotnet: 2.0.0 and lower dotnet versions for message center example * downgrade dotnet version in example projects * use Newtonsoft.Json as it is not supported in dotnet version 2.2 * use readkey in examples * documentation * fix images * fix the header * fix images * alignments * typos * add unit test * AddAnnotation example in guide
1 parent 5ee6e43 commit fbb1c04

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1268
-251
lines changed

Diff for: .travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ dist: trusty
44
mono:
55
- latest
66
dotnet: 2.0.0
7-
os:
7+
os:
88
- linux
99
script:
1010
- ./build.sh

Diff for: Examples/async.spans/README.md

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Basic example showing how to record async spans between PRODUCER and CONSUMER applications
2+
3+
This document will show how to implement basic PRODUCER and CONSUMER spans using zipkin4net library.
4+
5+
## Implementation Overview
6+
7+
We have 3 applications to produce example PRODUCER and CONSUMER spans.
8+
9+
- `example.message.center` - Stores and pops messages. The messages contain trace information.
10+
- `example.message.producer` - Creates a message with trace information and stores it to `example.message.center`. Logs PRODUCER span to zipkin server.
11+
- `example.message.consumer` - Fetches the message from `example.message.center`. Logs CONSUMER span to zipkin server.
12+
13+
## Pre-requisites
14+
15+
- To build the example, you need to install at least [dotnet 2.2](https://dotnet.microsoft.com/download/dotnet-core/2.2)
16+
- To run the examples, you need a live zipkin server.
17+
18+
## Running the example
19+
20+
1. Run `example.message.center` app
21+
- On a command line, navigate to `Examples\async.spans\example.message.center`
22+
- Run `dotnet run`
23+
![example.message.center](images/run-example.message.center.PNG)
24+
25+
2. Run `example.message.producer` app
26+
- On a command line, navigate to `Examples\async.spans\example.message.producer`
27+
- Run `dotnet run <base url of live zipkin server>`
28+
![example.message.producer](images/run-example.message.producer.PNG)
29+
30+
3. Run `example.message.consumer` app
31+
- On a command line, navigate to `Examples\async.spans\example.message.consumer`
32+
- Run `dotnet run <base url of live zipkin server>`
33+
![example.message.consumer](images/run-example.message.consumer.PNG)
34+
35+
4. Check the output
36+
- Go to zipkin UI
37+
- Search for `message.producer` or `message.consumer` as serviceName
38+
- Click one of the search result, it should show the PRODUCER and CONSUMER spans
39+
![example-output](images/run-example-output.PNG)
40+
41+
## What to take note on how to create/use PRODUCER and CONSUMER spans
42+
43+
### PRODUCER spans
44+
45+
- To make a PRODUCER span, you need to use `ProducerTrace` class
46+
- Example code from [example.message.producer](example.message.producer/Program.cs)
47+
```csharp
48+
using (var messageProducerTrace = new ProducerTrace("<Application name>", "<RPC here>"))
49+
{
50+
// TracedActionAsync extension method logs error annotation if exception occurs
51+
await messageProducerTrace.TracedActionAsync(ProduceMessage(messageProducerTrace.Trace.CurrentSpan, text));
52+
messageProducerTrace.AddAnnotation(Annotations.Tag("sampleProducerTag", "success!"));
53+
}
54+
```
55+
- `TracedActionAsync` is used to run the process that is measured to log error annotation in your zipkin trace if exception is thrown.
56+
- Make a way that trace information is passed to the consumer. So in the example, the trace information is part of the message which will be parsed by the consumer application to create CONSUMER spans.
57+
- Also, custom annotations can be added using the ProducerTrace object method `AddAnnotation`.
58+
59+
### CONSUMER spans
60+
61+
- To make a CONSUMER span, you need to use `ConsumerTrace` class
62+
- Example code from [example.message.consumer](example.message.consumer/Program.cs)
63+
```csharp
64+
static async Task ProcessMessage(Message message)
65+
{
66+
// need to supply trace information from producer
67+
using (var messageConsumerTrace = new ConsumerTrace(
68+
serviceName: "<Application name>",
69+
rpc: "<RPC here>",
70+
encodedTraceId: message.TraceId,
71+
encodedSpanId: message.SpanId,
72+
encodedParentSpanId: message.ParentId,
73+
sampledStr: message.Sampled,
74+
flagsStr: message.Flags.ToString(CultureInfo.InvariantCulture)))
75+
{
76+
await messageConsumerTrace.TracedActionAsync(Task.Delay(600)); // Test delay for mock processing
77+
messageConsumerTrace.AddAnnotation(Annotations.Tag("sampleConsumerTag", "success!"));
78+
}
79+
}
80+
```
81+
- In the example PRODUCER application passed the trace information through the `message` object. Using the trace information, CONSUMER span is created.
82+
- `TracedActionAsync` is used to run the process that is measured to log error annotation in your zipkin trace if exception is thrown.
83+
- Also, custom annotations can be added using the ConsumerTrace object method `AddAnnotation`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using example.message.common;
4+
using Microsoft.AspNetCore.Mvc;
5+
6+
namespace example.message.center.Controllers
7+
{
8+
[ApiController]
9+
[Route("[controller]")]
10+
public class MessagesController : ControllerBase
11+
{
12+
private static readonly Stack<Message> Messages = new Stack<Message>();
13+
14+
[HttpPost]
15+
[Route("pop")]
16+
public Message GetOneMessage()
17+
{
18+
if (!Messages.Any())
19+
return null;
20+
21+
return Messages.Pop();
22+
}
23+
24+
[HttpPost]
25+
[Route("push")]
26+
public string SaveMessage([FromBody]Message message)
27+
{
28+
Messages.Push(message);
29+
30+
return "Ok";
31+
}
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
3+
namespace example.message.center.Controllers
4+
{
5+
[ApiController]
6+
[Route("[controller]")]
7+
public class WelcomeController : ControllerBase
8+
{
9+
[HttpGet]
10+
public string Welcome()
11+
{
12+
return "Welcome to Message center!";
13+
}
14+
}
15+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.AspNetCore;
2+
using Microsoft.AspNetCore.Hosting;
3+
4+
namespace example.message.center
5+
{
6+
public class Program
7+
{
8+
public static void Main(string[] args)
9+
{
10+
CreateWebHostBuilder(args).Build().Run();
11+
}
12+
13+
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
14+
WebHost.CreateDefaultBuilder(args)
15+
.UseStartup<Startup>();
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"iisSettings": {
4+
"windowsAuthentication": false,
5+
"anonymousAuthentication": true,
6+
"iisExpress": {
7+
"applicationUrl": "http://localhost:51589",
8+
"sslPort": 0
9+
}
10+
},
11+
"profiles": {
12+
"IIS Express": {
13+
"commandName": "IISExpress",
14+
"launchBrowser": true,
15+
"launchUrl": "welcome",
16+
"environmentVariables": {
17+
"ASPNETCORE_ENVIRONMENT": "Development"
18+
}
19+
},
20+
"example.message.center": {
21+
"commandName": "Project",
22+
"launchBrowser": true,
23+
"launchUrl": "welcome",
24+
"applicationUrl": "http://localhost:51589",
25+
"environmentVariables": {
26+
"ASPNETCORE_ENVIRONMENT": "Development"
27+
}
28+
}
29+
}
30+
}
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Hosting;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Newtonsoft.Json.Serialization;
7+
8+
namespace example.message.center
9+
{
10+
public class Startup
11+
{
12+
public Startup(IConfiguration configuration)
13+
{
14+
Configuration = configuration;
15+
}
16+
17+
public IConfiguration Configuration { get; }
18+
19+
// This method gets called by the runtime. Use this method to add services to the container.
20+
public void ConfigureServices(IServiceCollection services)
21+
{
22+
services
23+
.AddMvc()
24+
.AddJsonOptions(options => {
25+
// send back a ISO date
26+
var settings = options.SerializerSettings;
27+
settings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat;
28+
// dont mess with case of properties
29+
var resolver = options.SerializerSettings.ContractResolver as DefaultContractResolver;
30+
resolver.NamingStrategy = null;
31+
})
32+
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
33+
}
34+
35+
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
36+
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
37+
{
38+
if (env.IsDevelopment())
39+
{
40+
app.UseDeveloperExceptionPage();
41+
}
42+
43+
app.UseMvc();
44+
}
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Debug",
5+
"System": "Information",
6+
"Microsoft": "Information"
7+
}
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Warning"
5+
}
6+
},
7+
"AllowedHosts": "*"
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp2.2</TargetFramework>
5+
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.AspNetCore.App" />
10+
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
11+
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.4" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<ProjectReference Include="..\example.message.common\example.message.common.csproj" />
16+
</ItemGroup>
17+
18+
</Project>
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Runtime.Serialization;
2+
3+
namespace example.message.common
4+
{
5+
[DataContract(Name = "message")]
6+
public class Message
7+
{
8+
[DataMember(Name = "text")]
9+
public string Text { get; set; }
10+
11+
[DataMember(Name = "trace_id")]
12+
public string TraceId { get; set; }
13+
14+
[DataMember(Name = "parent_id")]
15+
public string ParentId { get; set; }
16+
17+
[DataMember(Name = "span_id")]
18+
public string SpanId { get; set; }
19+
20+
[DataMember(Name = "sampled")]
21+
public string Sampled { get; set; }
22+
23+
[DataMember(Name = "flags")]
24+
public long Flags { get; set; }
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using zipkin4net;
3+
4+
namespace example.message.common
5+
{
6+
public class ZipkinConsoleLogger : ILogger
7+
{
8+
public void LogError(string message)
9+
{
10+
Console.WriteLine(message);
11+
}
12+
13+
public void LogInformation(string message)
14+
{
15+
Console.WriteLine(message);
16+
}
17+
18+
public void LogWarning(string message)
19+
{
20+
Console.WriteLine(message);
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using zipkin4net;
2+
using zipkin4net.Tracers.Zipkin;
3+
using zipkin4net.Transport.Http;
4+
5+
namespace example.message.common
6+
{
7+
public static class ZipkinHelper
8+
{
9+
public static void StartZipkin(string zipkinServer)
10+
{
11+
TraceManager.SamplingRate = 1.0f;
12+
var httpSender = new HttpZipkinSender(zipkinServer, "application/json");
13+
var tracer = new ZipkinTracer(httpSender, new JSONSpanSerializer());
14+
TraceManager.RegisterTracer(tracer);
15+
TraceManager.Start(new ZipkinConsoleLogger());
16+
}
17+
18+
public static void StopZipkin()
19+
{
20+
TraceManager.Stop();
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<ProjectReference Include="..\..\..\Src\zipkin4net\Src\zipkin4net.csproj" />
9+
</ItemGroup>
10+
11+
</Project>

0 commit comments

Comments
 (0)