diff --git a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
index 6c66f3c0..332c25b7 100644
--- a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
+++ b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
@@ -1,13 +1,24 @@
-using Ocelot.Configuration.File;
+using Butterfly.Client.Tracing;
+using Ocelot.Configuration.File;
+using Ocelot.Requester;
namespace Ocelot.Configuration.Creator
{
public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator
{
+ private IServiceTracer _tracer;
+
+ public HttpHandlerOptionsCreator(IServiceTracer tracer)
+ {
+ _tracer = tracer;
+ }
+
public HttpHandlerOptions Create(FileReRoute fileReRoute)
{
+ var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) ? fileReRoute.HttpHandlerOptions.UseTracing : false;
+
return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
- fileReRoute.HttpHandlerOptions.UseCookieContainer, fileReRoute.HttpHandlerOptions.UseTracing);
+ fileReRoute.HttpHandlerOptions.UseCookieContainer, useTracing);
}
}
}
diff --git a/src/Ocelot/Infrastructure/Extensions/StringValuesExtensions.cs b/src/Ocelot/Infrastructure/Extensions/StringValuesExtensions.cs
new file mode 100644
index 00000000..1b6d1682
--- /dev/null
+++ b/src/Ocelot/Infrastructure/Extensions/StringValuesExtensions.cs
@@ -0,0 +1,17 @@
+using Microsoft.Extensions.Primitives;
+using System.Linq;
+
+namespace Ocelot.Infrastructure.Extensions
+{
+ internal static class StringValueExtensions
+ {
+ public static string GetValue(this StringValues stringValues)
+ {
+ if (stringValues.Count == 1)
+ {
+ return stringValues;
+ }
+ return stringValues.ToArray().LastOrDefault();
+ }
+ }
+}
diff --git a/src/Ocelot/Logging/IOcelotLogger.cs b/src/Ocelot/Logging/IOcelotLogger.cs
new file mode 100644
index 00000000..67bac731
--- /dev/null
+++ b/src/Ocelot/Logging/IOcelotLogger.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Ocelot.Logging
+{
+ ///
+ /// Thin wrapper around the DotNet core logging framework, used to allow the scopedDataRepository to be injected giving access to the Ocelot RequestId
+ ///
+ public interface IOcelotLogger
+ {
+ void LogTrace(string message, params object[] args);
+ void LogDebug(string message, params object[] args);
+ void LogInformation(string message, params object[] args);
+ void LogError(string message, Exception exception);
+ void LogError(string message, params object[] args);
+ void LogCritical(string message, Exception exception);
+
+ ///
+ /// The name of the type the logger has been built for.
+ ///
+ string Name { get; }
+ }
+}
diff --git a/src/Ocelot/Logging/IOcelotLoggerFactory.cs b/src/Ocelot/Logging/IOcelotLoggerFactory.cs
index 3a145595..2407afcd 100644
--- a/src/Ocelot/Logging/IOcelotLoggerFactory.cs
+++ b/src/Ocelot/Logging/IOcelotLoggerFactory.cs
@@ -1,27 +1,7 @@
-using System;
-
-namespace Ocelot.Logging
+namespace Ocelot.Logging
{
public interface IOcelotLoggerFactory
{
IOcelotLogger CreateLogger();
}
-
- ///
- /// Thin wrapper around the DotNet core logging framework, used to allow the scopedDataRepository to be injected giving access to the Ocelot RequestId
- ///
- public interface IOcelotLogger
- {
- void LogTrace(string message, params object[] args);
- void LogDebug(string message, params object[] args);
- void LogInformation(string message, params object[] args);
- void LogError(string message, Exception exception);
- void LogError(string message, params object[] args);
- void LogCritical(string message, Exception exception);
-
- ///
- /// The name of the type the logger has been built for.
- ///
- string Name { get; }
- }
}
diff --git a/src/Ocelot/Logging/OcelotDiagnosticListener.cs b/src/Ocelot/Logging/OcelotDiagnosticListener.cs
index ee0b5b8a..d75cfd2c 100644
--- a/src/Ocelot/Logging/OcelotDiagnosticListener.cs
+++ b/src/Ocelot/Logging/OcelotDiagnosticListener.cs
@@ -3,18 +3,48 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DiagnosticAdapter;
using Butterfly.Client.AspNetCore;
using Butterfly.OpenTracing;
+using Ocelot.Middleware;
+using Butterfly.Client.Tracing;
+using System.Linq;
+using System.Collections.Generic;
+using Ocelot.Infrastructure.Extensions;
+using Microsoft.Extensions.Logging;
+using Ocelot.Requester;
namespace Ocelot.Logging
{
public class OcelotDiagnosticListener
{
+ private IServiceTracer _tracer;
private IOcelotLogger _logger;
- public OcelotDiagnosticListener(IOcelotLoggerFactory factory)
+ public OcelotDiagnosticListener(IOcelotLoggerFactory factory, IServiceTracer tracer)
{
+ _tracer = tracer;
_logger = factory.CreateLogger();
}
+ [DiagnosticName("Ocelot.MiddlewareException")]
+ public virtual void OcelotMiddlewareException(Exception exception, DownstreamContext context, string name)
+ {
+ _logger.LogTrace($"Ocelot.MiddlewareException: {name}; {exception.Message}");
+ Event(context.HttpContext, $"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
+ }
+
+ [DiagnosticName("Ocelot.MiddlewareStarted")]
+ public virtual void OcelotMiddlewareStarted(DownstreamContext context, string name)
+ {
+ _logger.LogTrace($"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
+ Event(context.HttpContext, $"Ocelot.MiddlewareStarted: {name}; {context.HttpContext.Request.Path}");
+ }
+
+ [DiagnosticName("Ocelot.MiddlewareFinished")]
+ public virtual void OcelotMiddlewareFinished(DownstreamContext context, string name)
+ {
+ _logger.LogTrace($"OcelotMiddlewareFinished: {name}; {context.HttpContext.Request.Path}");
+ Event(context.HttpContext, $"OcelotMiddlewareFinished: {name}; {context.HttpContext.Request.Path}");
+ }
+
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting")]
public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
{
@@ -36,8 +66,28 @@ namespace Ocelot.Logging
}
private void Event(HttpContext httpContext, string @event)
- {
+ {
+ // Hack - if the user isnt using tracing the code gets here and will blow up on
+ // _tracer.Tracer.TryExtract. We already use the fake tracer for another scenario
+ // so sticking it here as well..I guess we need a factory for this but no idea
+ // how to hook that into the diagnostic framework at the moment.
+ if(_tracer.GetType() == typeof(FakeServiceTracer))
+ {
+ return;
+ }
+
var span = httpContext.GetSpan();
+ if(span == null)
+ {
+ var spanBuilder = new SpanBuilder($"server {httpContext.Request.Method} {httpContext.Request.Path}");
+ if (_tracer.Tracer.TryExtract(out var spanContext, httpContext.Request.Headers, (c, k) => c[k].GetValue(),
+ c => c.Select(x => new KeyValuePair(x.Key, x.Value.GetValue())).GetEnumerator()))
+ {
+ spanBuilder.AsChildOf(spanContext);
+ };
+ span = _tracer.Start(spanBuilder);
+ httpContext.SetSpan(span);
+ }
span?.Log(LogField.CreateNew().Event(@event));
}
}
diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs
index cda82bef..61d13f52 100644
--- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs
+++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs
@@ -65,6 +65,8 @@
rest of asp.net..
*/
+ builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
+
builder.Use(async (context, task) =>
{
var downstreamContext = new DownstreamContext(context);
diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs
index 5989b1dd..539fa6a1 100644
--- a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs
+++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs
@@ -3,6 +3,7 @@
// Removed code and changed RequestDelete to OcelotRequestDelete, HttpContext to DownstreamContext, removed some exception handling messages
using System;
+using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@@ -75,7 +76,28 @@ namespace Ocelot.Middleware.Pipeline
var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);
if (parameters.Length == 1)
{
- return (OcelotRequestDelegate)methodinfo.CreateDelegate(typeof(OcelotRequestDelegate), instance);
+ var ocelotDelegate = (OcelotRequestDelegate)methodinfo.CreateDelegate(typeof(OcelotRequestDelegate), instance);
+ var diagnosticListener = (DiagnosticListener)app.ApplicationServices.GetService(typeof(DiagnosticListener));
+ var middlewareName = ocelotDelegate.Target.GetType().Name;
+
+ OcelotRequestDelegate wrapped = context => {
+ try
+ {
+ Write(diagnosticListener, "Ocelot.MiddlewareStarted", middlewareName, context);
+ return ocelotDelegate(context);
+ }
+ catch(Exception ex)
+ {
+ Write(diagnosticListener, "Ocelot.MiddlewareException", middlewareName, context);
+ throw ex;
+ }
+ finally
+ {
+ Write(diagnosticListener, "Ocelot.MiddlewareFinished", middlewareName, context);
+ }
+ };
+
+ return wrapped;
}
var factory = Compile