Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/opentracing #1243

Merged
merged 2 commits into from
May 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Ocelot.sln
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
10 changes: 10 additions & 0 deletions samples/OcelotOpenTracing/appsettings.json
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
}
Loading