mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 07:48:16 +08:00
Feature/fix tracing (#297)
* hacked together tracing fix by wrapping middleware delegate in another delegate * #227 have re-implemented tracing, cleaned up trace names, probably still need some refactoring and tests as this was a bit of a hack job * #227 bit of checking for when we dont want to use tracing, also removed a unit test for websockets that wasnt a unit test, i stuck it there because i wanted the code coverage and now im paying the price, will have to work out a better way to do it * #227 a bit of refactoring to make this work better, still a bit hacky...would like to revisit the whole thing one day * #227 dont need this * #227 or this * #227 small refactor
This commit is contained in:
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
22
src/Ocelot/Logging/IOcelotLogger.cs
Normal file
22
src/Ocelot/Logging/IOcelotLogger.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Ocelot.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Thin wrapper around the DotNet core logging framework, used to allow the scopedDataRepository to be injected giving access to the Ocelot RequestId
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// The name of the type the logger has been built for.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
@ -1,27 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Ocelot.Logging
|
||||
namespace Ocelot.Logging
|
||||
{
|
||||
public interface IOcelotLoggerFactory
|
||||
{
|
||||
IOcelotLogger CreateLogger<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thin wrapper around the DotNet core logging framework, used to allow the scopedDataRepository to be injected giving access to the Ocelot RequestId
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// The name of the type the logger has been built for.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
|
@ -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<OcelotDiagnosticListener>();
|
||||
}
|
||||
|
||||
[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<string, string>(x.Key, x.Value.GetValue())).GetEnumerator()))
|
||||
{
|
||||
spanBuilder.AsChildOf(spanContext);
|
||||
};
|
||||
span = _tracer.Start(spanBuilder);
|
||||
httpContext.SetSpan(span);
|
||||
}
|
||||
span?.Log(LogField.CreateNew().Event(@event));
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,8 @@
|
||||
rest of asp.net..
|
||||
*/
|
||||
|
||||
builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";
|
||||
|
||||
builder.Use(async (context, task) =>
|
||||
{
|
||||
var downstreamContext = new DownstreamContext(context);
|
||||
|
@ -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<object>(methodinfo, parameters);
|
||||
@ -93,6 +115,14 @@ namespace Ocelot.Middleware.Pipeline
|
||||
});
|
||||
}
|
||||
|
||||
private static void Write(DiagnosticListener diagnosticListener, string message, string middlewareName, DownstreamContext context)
|
||||
{
|
||||
if(diagnosticListener != null)
|
||||
{
|
||||
diagnosticListener.Write(message, new { name = middlewareName, context = context });
|
||||
}
|
||||
}
|
||||
|
||||
public static IOcelotPipelineBuilder MapWhen(this IOcelotPipelineBuilder app, Predicate predicate, Action<IOcelotPipelineBuilder> configuration)
|
||||
{
|
||||
if (app == null)
|
||||
|
@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Butterfly.Client.Tracing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
|
17
src/Ocelot/Requester/FakeServiceTracer.cs
Normal file
17
src/Ocelot/Requester/FakeServiceTracer.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using Butterfly.Client.Tracing;
|
||||
using Butterfly.OpenTracing;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class FakeServiceTracer : IServiceTracer
|
||||
{
|
||||
public ITracer Tracer { get; }
|
||||
public string ServiceName { get; }
|
||||
public string Environment { get; }
|
||||
public string Identity { get; }
|
||||
public ISpan Start(ISpanBuilder spanBuilder)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using Butterfly.Client.Tracing;
|
||||
using Butterfly.OpenTracing;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
@ -22,16 +21,4 @@ namespace Ocelot.Requester
|
||||
return new OcelotHttpTracingHandler(_tracer, _repo);
|
||||
}
|
||||
}
|
||||
|
||||
public class FakeServiceTracer : IServiceTracer
|
||||
{
|
||||
public ITracer Tracer { get; }
|
||||
public string ServiceName { get; }
|
||||
public string Environment { get; }
|
||||
public string Identity { get; }
|
||||
public ISpan Start(ISpanBuilder spanBuilder)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user