Skip to content

Commit

Permalink
Feat/opentracing (#1243)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomPallister committed May 25, 2020
1 parent 3439be8 commit 865520f
Show file tree
Hide file tree
Showing 13 changed files with 929 additions and 76 deletions.
17 changes: 17 additions & 0 deletions Ocelot.sln
Expand Up @@ -82,6 +82,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "basic", "basic", "{ED066001
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "graphql", "graphql", "{C15CD120-5F8D-41DE-9B21-00E3EA77D6C1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.OpenTracing", "src\Ocelot.Tracing.OpenTracing\Ocelot.Tracing.OpenTracing.csproj", "{11C622AD-8C0A-4CF4-811B-3DBB76550797}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "open-tracing", "open-tracing", "{731C6A8A-69ED-445C-A132-C638AA93F9C7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotOpenTracing", "samples\OcelotOpenTracing\OcelotOpenTracing.csproj", "{C9427E78-4281-4F59-A66E-17C0B66550E5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -180,6 +186,14 @@ Global
{33BE6D88-F188-4E60-83AC-3C4B94D24675}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33BE6D88-F188-4E60-83AC-3C4B94D24675}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33BE6D88-F188-4E60-83AC-3C4B94D24675}.Release|Any CPU.Build.0 = Release|Any CPU
{11C622AD-8C0A-4CF4-811B-3DBB76550797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11C622AD-8C0A-4CF4-811B-3DBB76550797}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11C622AD-8C0A-4CF4-811B-3DBB76550797}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11C622AD-8C0A-4CF4-811B-3DBB76550797}.Release|Any CPU.Build.0 = Release|Any CPU
{C9427E78-4281-4F59-A66E-17C0B66550E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9427E78-4281-4F59-A66E-17C0B66550E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9427E78-4281-4F59-A66E-17C0B66550E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9427E78-4281-4F59-A66E-17C0B66550E5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -214,6 +228,9 @@ Global
{1F1F324D-6EA4-4E63-A6A7-C6053F412F1A} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
{ED066001-BAF7-4117-9884-DF591A56347D} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
{C15CD120-5F8D-41DE-9B21-00E3EA77D6C1} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
{11C622AD-8C0A-4CF4-811B-3DBB76550797} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{731C6A8A-69ED-445C-A132-C638AA93F9C7} = {8FA0CBA0-0338-48EB-B37F-83CA5022237C}
{C9427E78-4281-4F59-A66E-17C0B66550E5} = {731C6A8A-69ED-445C-A132-C638AA93F9C7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
Expand Down
37 changes: 35 additions & 2 deletions docs/features/tracing.rst
@@ -1,8 +1,41 @@
Tracing
=======

This page details how to perform distributed tracing with Ocelot. At the moment we only support Butterfly but other tracers might just work without
anything Ocelot specific.
This page details how to perform distributed tracing with Ocelot.

OpenTracing
^^^^^^^^^^^

Ocelot providers tracing functionality from the excellent `OpenTracing C# <https://github.com/opentracing/opentracing-csharp>`_ project. The code for the Ocelot integration
can be found `here <https://github.com/ThreeMammals/Ocelot.Tracing.OpenTracing>`_.

The example below uses `Jaeger C# <https://github.com/jaegertracing/jaeger-client-csharp>`_ client to provide the tracer used in Ocelot.

.. code-block:: csharp
services.AddSingleton<ITracer>(sp =>
{
var loggerFactory = sp.GetService<ILoggerFactory>();
Configuration config = new Configuration(context.HostingEnvironment.ApplicationName, loggerFactory);
var tracer = config.GetTracer();
GlobalTracer.Register(tracer);
return tracer;
});
services
.AddOcelot()
.AddOpenTracing();
Then in your ocelot.json add the following to the Route you want to trace..

.. code-block:: json
"HttpHandlerOptions": {
"UseTracing": true
},
Ocelot will now send tracing information to Jaeger when this Route is called.

Butterfly
^^^^^^^^^
Expand Down
30 changes: 30 additions & 0 deletions samples/OcelotOpenTracing/OcelotOpenTracing.csproj
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Jaeger" Version="0.3.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Ocelot.Tracing.OpenTracing\Ocelot.Tracing.OpenTracing.csproj" />
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
</ItemGroup>

<ItemGroup>
<Content Update="appsettings.Development.json">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="appsettings.json">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="ocelot.json">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>

</Project>
65 changes: 65 additions & 0 deletions samples/OcelotOpenTracing/Program.cs
@@ -0,0 +1,65 @@
namespace OcelotOpenTracing
{
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System.IO;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Microsoft.Extensions.Logging;
using Ocelot.Tracing.OpenTracing;
using Jaeger;
using Microsoft.Extensions.DependencyInjection;
using OpenTracing;
using OpenTracing.Util;

internal static class Program
{
private static void Main(string[] args)
{
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json",
optional: true, reloadOnChange: false)
.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
})
.ConfigureServices((context, services) =>
{
services.AddSingleton<ITracer>(sp =>
{
var loggerFactory = sp.GetService<ILoggerFactory>();
Configuration config = new Configuration(context.HostingEnvironment.ApplicationName, loggerFactory);
var tracer = config.GetTracer();
GlobalTracer.Register(tracer);
return tracer;
});
services
.AddOcelot()
.AddOpenTracing();
})
.ConfigureLogging(logging =>
{
logging.AddConsole();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
})
.Build()
.Run();
}
}
}
9 changes: 9 additions & 0 deletions samples/OcelotOpenTracing/appsettings.Development.json
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
10 changes: 10 additions & 0 deletions samples/OcelotOpenTracing/appsettings.json
@@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
24 changes: 24 additions & 0 deletions samples/OcelotOpenTracing/ocelot.json
@@ -0,0 +1,24 @@
{
"ReRoutes": [
{
"HttpHandlerOptions": {
"UseTracing": true
},
"DownstreamPathTemplate": "/todos/{id}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "jsonplaceholder.typicode.com",
"Port": 443
}
],
"UpstreamPathTemplate": "/posts/{id}",
"UpstreamHttpMethod": [
"Get"
]
}
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:5000"
}
}
21 changes: 21 additions & 0 deletions src/Ocelot.Tracing.OpenTracing/Ocelot.Tracing.OpenTracing.csproj
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Version>0.0.0-dev</Version>
<Authors>Kjell-Åke Gafvelin</Authors>
<Description>This package provides OpenTracing support to Ocelot.</Description>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot</PackageProjectUrl>
<PackageTags>API Gateway;.NET core; OpenTracing</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="OpenTracing" Version="0.12.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions src/Ocelot.Tracing.OpenTracing/OcelotBuilderExtensions.cs
@@ -0,0 +1,15 @@
namespace Ocelot.Tracing.OpenTracing
{
using Microsoft.Extensions.DependencyInjection.Extensions;
using Ocelot.DependencyInjection;
using Ocelot.Logging;

public static class OcelotBuilderExtensions
{
public static IOcelotBuilder AddOpenTracing(this IOcelotBuilder builder)
{
builder.Services.TryAddSingleton<ITracer, OpenTracingTracer>();
return builder;
}
}
}
75 changes: 75 additions & 0 deletions src/Ocelot.Tracing.OpenTracing/OpenTracingTracer.cs
@@ -0,0 +1,75 @@
namespace Ocelot.Tracing.OpenTracing
{
using global::OpenTracing;
using global::OpenTracing.Propagation;
using global::OpenTracing.Tag;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class OpenTracingTracer : Logging.ITracer
{
private readonly ITracer _tracer;

public OpenTracingTracer(ITracer tracer)
{
_tracer = tracer ?? throw new ArgumentNullException(nameof(tracer));
}

public void Event(HttpContext httpContext, string @event)
{
}

public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken,
Action<string> addTraceIdToRepo,
Func<HttpRequestMessage,
CancellationToken,
Task<HttpResponseMessage>> baseSendAsync)
{
using (IScope scope = _tracer.BuildSpan(request.RequestUri.AbsoluteUri).StartActive(finishSpanOnDispose: true))
{
var span = scope.Span;

span.SetTag(Tags.SpanKind, Tags.SpanKindClient)
.SetTag(Tags.HttpMethod, request.Method.Method)
.SetTag(Tags.HttpUrl, request.RequestUri.OriginalString);

addTraceIdToRepo(span.Context.SpanId);

var headers = new Dictionary<string, string>();

_tracer.Inject(span.Context, BuiltinFormats.HttpHeaders, new TextMapInjectAdapter(headers));

foreach (var item in headers)
{
request.Headers.Add(item.Key, item.Value);
}

try
{
var response = await baseSendAsync(request, cancellationToken);

span.SetTag(Tags.HttpStatus, (int)response.StatusCode);

return response;
}
catch (HttpRequestException ex)
{
Tags.Error.Set(scope.Span, true);

span.Log(new Dictionary<string, object>(3)
{
{ LogFields.Event, Tags.Error.Key },
{ LogFields.ErrorKind, ex.GetType().Name },
{ LogFields.ErrorObject, ex }
});
throw;
}
}
}
}
}

0 comments on commit 865520f

Please sign in to comment.