This scenario is an evolution from Scenario 1, containing a REST API in front of the existing Time REST API. In this case trace will have to be transport between two processes over HTTP.
The additional requirement is:
- End-to-end transaction details containing both REST APIs and the SQL dependency
.NET Core 3.0 simplifies distributed tracing by having built-in HTTP tracing correlation. The only required code change is to set the desired activity id format to W3C.
public static void Main(string[] args)
{
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
Activity.ForceDefaultIdFormat = true;
CreateHostBuilder(args).Build().Run();
}
The class System.Diagnostics.Activity
is used by libraries to generate traces that can be observed by Diagnostic Listeners in order to generate spans/operations according to the tracing backend of choice. Examples are Http Client and Azure Service Bus.
As mentioned before, correlation happens by having spans sharing the same TraceID. To propagate the traceId used in first span, http request headers are used. The forwarded value will then be used to argument new traces, correlating with the value used by the caller.
Name | SpanId | ParentId | TraceId |
---|---|---|---|
main api http get | 41d990c15431a48b | cd4262a7f7adf040bdd892959cf8c4fc | |
call time api | 4a28d39ff0e725f2 | 41d990c15431a48b | cd4262a7f7adf040bdd892959cf8c4fc |
time api http get | 4dcd0354450f7920 | 4a28d39ff0e725f2 | cd4262a7f7adf040bdd892959cf8c4fc |
sql call | 48475897d66c3b03 | 4dcd0354450f7920 | cd4262a7f7adf040bdd892959cf8c4fc |
The header propagating the correlation is named traceparent
, as defined by W3C, on it's simplest form contains a value with the following pattern:
00-cd4262a7f7adf040bdd892959cf8c4fc-4a28d39ff0e725f2-01
There are 4 fields in the header value, separated by a '-':
- Version: currently 00
- TraceId: The trace identifier started by caller
- ParentId: The span identifier that originated the call
- Trace flags: current version only supported the flag
sampled
, which indicates if the caller is sampling the request
In Jaeger UI, we can observe two services as part of the transaction:
Moreover, we can look at the dependency graph between services:
The same applies to Application Insights:
The dependency graph in Application Insights is available under Application Map, displaying calls counter, avg. duration and error percentage:
In order to run this scenario locally ensure the following requirements are met:
- SQL Server is available at
server=localhost;user id=sa;password=Pass@Word1;
A way to accomplish it is to run as a linux docker container:
docker run --name sqlserver -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Pass@Word1" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-GA-ubuntu-16.04
- When using OpenTelemetry, ensure Jaeger is running locally
docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
-p 5775:5775/udp \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14268:14268 \
-p 9411:9411 \
jaegertracing/all-in-one
- When using Application Insights, ensure the instrumentation key is set (a simpler way to provide settings to all applications is to create file appsettings.Development.json in folder ./shared):
{
"SampleApp": {
"UseApplicationInsights": "true",
"UseOpenTelemetry": "false",
"ApplicationInsightsInstrumentationKey": "<Instrumentation key>"
}
}
To run you can either:
- debug from Visual Studio (start project -> docker-compose)
- start from the terminal, using docker-compose (docker-compose up --build).
To generate load use the following script:
watch -n 1 curl --request GET http://localhost:5001/api/dbtime
while (1) {Invoke-WebRequest -Uri http://localhost:5001/api/dbtime; sleep 1}