This commit is contained in:
TomPallister 2020-05-25 18:39:00 +01:00
parent 56fe7b5ba9
commit 21b9d73c7f
9 changed files with 226 additions and 133 deletions

View File

@ -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
@ -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
@ -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}

View File

@ -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
^^^^^^^^^

View File

@ -1,23 +1,22 @@
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;
namespace OcelotOpenTracing
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 =>
{
@ -36,10 +35,6 @@ namespace OcelotOpenTracing
})
.ConfigureServices((context, services) =>
{
services
.AddOcelot()
.AddOpenTracing();
services.AddSingleton<ITracer>(sp =>
{
var loggerFactory = sp.GetService<ILoggerFactory>();
@ -50,6 +45,9 @@ namespace OcelotOpenTracing
return tracer;
});
services
.AddOcelot()
.AddOpenTracing();
})
.ConfigureLogging(logging =>
{

View File

@ -1,16 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Logging;
using System;
namespace Ocelot.Tracing.OpenTracing
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.AddSingleton<ITracer, OpenTracingTracer>();
builder.Services.TryAddSingleton<ITracer, OpenTracingTracer>();
return builder;
}
}

View File

@ -1,32 +1,36 @@
using Microsoft.AspNetCore.Http;
using OpenTracing;
using OpenTracing.Propagation;
using OpenTracing.Tag;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Ocelot.Tracing.OpenTracing
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;
private readonly ITracer _tracer;
public OpenTracingTracer(ITracer tracer)
{
this.tracer = tracer ?? throw new ArgumentNullException(nameof(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)
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken,
Action<string> addTraceIdToRepo,
Func<HttpRequestMessage,
CancellationToken,
Task<HttpResponseMessage>> baseSendAsync)
{
using (IScope scope = this.tracer.BuildSpan(request.RequestUri.AbsoluteUri).StartActive(finishSpanOnDispose: true))
using (IScope scope = _tracer.BuildSpan(request.RequestUri.AbsoluteUri).StartActive(finishSpanOnDispose: true))
{
var span = scope.Span;
@ -38,7 +42,7 @@ namespace Ocelot.Tracing.OpenTracing
var headers = new Dictionary<string, string>();
this.tracer.Inject(span.Context, BuiltinFormats.HttpHeaders, new TextMapInjectAdapter(headers));
_tracer.Inject(span.Context, BuiltinFormats.HttpHeaders, new TextMapInjectAdapter(headers));
foreach (var item in headers)
{

View File

@ -26,6 +26,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Tracing.OpenTracing\Ocelot.Tracing.OpenTracing.csproj" />
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
<ProjectReference Include="..\..\src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj" />
@ -41,6 +42,7 @@
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Moq" Version="4.13.1" />
<PackageReference Include="OpenTracing" Version="0.12.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164">
<PrivateAssets>all</PrivateAssets>
</PackageReference>

View File

@ -41,9 +41,9 @@ namespace Ocelot.AcceptanceTests
int port2 = RandomPortFinder.GetRandomPort();
var configuration = new FileConfiguration()
{
ReRoutes = new List<FileReRoute>()
Routes = new List<FileRoute>()
{
new FileReRoute()
new FileRoute()
{
DownstreamPathTemplate = "/api/values",
DownstreamScheme = "http",
@ -62,7 +62,7 @@ namespace Ocelot.AcceptanceTests
UseTracing = true
}
},
new FileReRoute()
new FileRoute()
{
DownstreamPathTemplate = "/api/values",
DownstreamScheme = "http",
@ -100,13 +100,8 @@ namespace Ocelot.AcceptanceTests
.When(_ => _steps.WhenIGetUrlOnTheApiGateway("/api002/values"))
.Then(_ => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(_ => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
.And(_ => ThenTheTracerIsCalled(fakeTracer))
.BDDfy();
var commandOnAllStateMachines = Wait.WaitFor(10000).Until(() => fakeTracer.BuildSpanCalled >= 2);
_output.WriteLine($"fakeTracer.BuildSpanCalled is {fakeTracer.BuildSpanCalled}");
commandOnAllStateMachines.ShouldBeTrue();
}
[Fact]
@ -115,9 +110,9 @@ namespace Ocelot.AcceptanceTests
int port = RandomPortFinder.GetRandomPort();
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
Routes = new List<FileRoute>
{
new FileReRoute
new FileRoute
{
DownstreamPathTemplate = "/api/values",
DownstreamScheme = "http",
@ -145,6 +140,7 @@ namespace Ocelot.AcceptanceTests
};
var butterflyPort = RandomPortFinder.GetRandomPort();
var butterflyUrl = $"http://localhost:{butterflyPort}";
var fakeTracer = new FakeTracer();
@ -161,6 +157,15 @@ namespace Ocelot.AcceptanceTests
.BDDfy();
}
private void ThenTheTracerIsCalled(FakeTracer fakeTracer)
{
var commandOnAllStateMachines = Wait.WaitFor(10000).Until(() => fakeTracer.BuildSpanCalled >= 2);
_output.WriteLine($"fakeTracer.BuildSpanCalled is {fakeTracer.BuildSpanCalled}");
commandOnAllStateMachines.ShouldBeTrue();
}
private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl)
{
_serviceOneBuilder = new WebHostBuilder()

View File

@ -46,6 +46,7 @@ namespace Ocelot.AcceptanceTests
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
using CookieHeaderValue = Microsoft.Net.Http.Headers.CookieHeaderValue;
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;
using Ocelot.Tracing.OpenTracing;
public class Steps : IDisposable
{
@ -1214,6 +1215,41 @@ namespace Ocelot.AcceptanceTests
_ocelotClient = _ocelotServer.CreateClient();
}
internal void GivenOcelotIsRunningUsingOpenTracing(OpenTracing.ITracer fakeTracer)
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddOcelot()
.AddOpenTracing();
s.AddSingleton<OpenTracing.ITracer>(fakeTracer);
})
.Configure(app =>
{
app.Use(async (_, next) =>
{
await next.Invoke();
});
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void ThenWarningShouldBeLogged()
{
MockLoggerFactory loggerFactory = (MockLoggerFactory)_ocelotServer.Host.Services.GetService<IOcelotLoggerFactory>();