Improving logging and request id (#189)

* hacking around to work out why logging and request id isnt working

* pass request id into logger so it can be structured, removed a bunch of debug logging we dont need because diagnostic trace gets it

* changed config dependency

* always have tracing available

* made it so we dont need to pass config into services.AddOcelot anymore with .net core 2.0

* add test

* lots of changes relating to logging and request ids, also updated documentation

* fixed failing test i missed
This commit is contained in:
Tom Pallister
2018-01-06 16:39:05 +00:00
committed by GitHub
parent 464f266148
commit 6a20baeb97
43 changed files with 562 additions and 197 deletions

View File

@ -1,5 +1,4 @@
using System;
using CacheManager.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
@ -8,7 +7,6 @@ using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
using Ocelot.AcceptanceTests.Caching;
namespace Ocelot.AcceptanceTests
{
@ -26,7 +24,7 @@ namespace Ocelot.AcceptanceTests
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public IConfiguration Configuration { get; }
public virtual void ConfigureServices(IServiceCollection services)
{
@ -35,48 +33,7 @@ namespace Ocelot.AcceptanceTests
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}
public class Startup_WithCustomCacheHandle : AcceptanceTestsStartup
{
public Startup_WithCustomCacheHandle(IHostingEnvironment env) : base(env) { }
public override void ConfigureServices(IServiceCollection services)
{
services.AddOcelot(Configuration)
.AddCacheManager((x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithJsonSerializer()
.WithHandle(typeof(InMemoryJsonHandle<>));
});
}
}
public class Startup_WithConsul_And_CustomCacheHandle : AcceptanceTestsStartup
{
public Startup_WithConsul_And_CustomCacheHandle(IHostingEnvironment env) : base(env) { }
public override void ConfigureServices(IServiceCollection services)
{
services.AddOcelot(Configuration)
.AddCacheManager((x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithJsonSerializer()
.WithHandle(typeof(InMemoryJsonHandle<>));
})
.AddStoreOcelotConfigurationInConsul();
}
}
}

View File

@ -22,25 +22,18 @@ namespace Ocelot.AcceptanceTests
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
Config = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public static IConfiguration Config { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithDictionaryHandle();
};
services.AddOcelot(Configuration).AddStoreOcelotConfigurationInConsul();
services.AddOcelot(Config).AddStoreOcelotConfigurationInConsul();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
public void Configure(IApplicationBuilder app)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}

View File

@ -0,0 +1,29 @@
using CacheManager.Core;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.AcceptanceTests.Caching;
namespace Ocelot.AcceptanceTests
{
public class StartupWithConsulAndCustomCacheHandle : AcceptanceTestsStartup
{
public StartupWithConsulAndCustomCacheHandle(IHostingEnvironment env) : base(env) { }
public override void ConfigureServices(IServiceCollection services)
{
services.AddOcelot(Configuration)
.AddCacheManager((x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithJsonSerializer()
.WithHandle(typeof(InMemoryJsonHandle<>));
})
.AddStoreOcelotConfigurationInConsul();
}
}
}

View File

@ -0,0 +1,28 @@
using CacheManager.Core;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.AcceptanceTests.Caching;
namespace Ocelot.AcceptanceTests
{
public class StartupWithCustomCacheHandle : AcceptanceTestsStartup
{
public StartupWithCustomCacheHandle(IHostingEnvironment env) : base(env) { }
public override void ConfigureServices(IServiceCollection services)
{
services.AddOcelot(Configuration)
.AddCacheManager((x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithJsonSerializer()
.WithHandle(typeof(InMemoryJsonHandle<>));
});
}
}
}

View File

@ -118,7 +118,7 @@ namespace Ocelot.AcceptanceTests
});
_ocelotServer = new TestServer(_webHostBuilder
.UseStartup<Startup_WithCustomCacheHandle>());
.UseStartup<StartupWithCustomCacheHandle>());
_ocelotClient = _ocelotServer.CreateClient();
}
@ -148,7 +148,7 @@ namespace Ocelot.AcceptanceTests
});
_ocelotServer = new TestServer(_webHostBuilder
.UseStartup<Startup_WithConsul_And_CustomCacheHandle>());
.UseStartup<StartupWithConsulAndCustomCacheHandle>());
_ocelotClient = _ocelotServer.CreateClient();
}

View File

@ -25,7 +25,7 @@ namespace Ocelot.IntegrationTests
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
@ -45,8 +45,6 @@ namespace Ocelot.IntegrationTests
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}

View File

@ -34,7 +34,7 @@ namespace Ocelot.IntegrationTests
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public IConfiguration Configuration { get; }
public virtual void ConfigureServices(IServiceCollection services)
{
@ -46,9 +46,6 @@ namespace Ocelot.IntegrationTests
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//this is from Ocelot...so we need to move stuff below into it...
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}

View File

@ -13,29 +13,11 @@ namespace Ocelot.ManualTest
{
public class ManualTestStartup
{
public ManualTestStartup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithDictionaryHandle();
x.WithDictionaryHandle();
};
services.AddAuthentication()
@ -45,14 +27,13 @@ namespace Ocelot.ManualTest
x.Audience = "test";
});
services.AddOcelot(Configuration)
services.AddOcelot()
.AddCacheManager(settings)
.AddAdministration("/administration", "secret");
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
public void Configure(IApplicationBuilder app)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}

View File

@ -1,6 +1,8 @@
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
namespace Ocelot.ManualTest
{
@ -14,6 +16,20 @@ namespace Ocelot.ManualTest
});
builder.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("configuration.json");
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.UseIISIntegration()
.UseStartup<ManualTestStartup>();
var host = builder.Build();

View File

@ -1,10 +1,10 @@
{
"Logging": {
"IncludeScopes": true,
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace",
"System": "Information",
"Microsoft": "Information"
"Default": "Debug",
"System": "Error",
"Microsoft": "Error"
}
}
}

View File

@ -62,6 +62,7 @@
"DownstreamPort": 80,
"UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Get" ],
"RequestIdKey": "ReRouteRequestId",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,

View File

@ -38,7 +38,7 @@ namespace Ocelot.UnitTests.Configuration
{
var fileConfig = new FileConfiguration();
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
var config = new OcelotConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig);
var config = new OcelotConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig, "asdf");
this.Given(x => GivenTheFollowingConfiguration(fileConfig))
.And(x => GivenTheRepoReturns(new OkResponse()))

View File

@ -94,6 +94,8 @@ namespace Ocelot.UnitTests.Configuration
public string AdministrationPath {get;}
public ServiceProviderConfiguration ServiceProviderConfiguration => throw new NotImplementedException();
public string RequestId {get;}
}
}
}

View File

@ -30,9 +30,9 @@ namespace Ocelot.UnitTests.Configuration
{
var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build();
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig))))
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig, ""))))
.When(x => x.WhenIGetTheConfig())
.Then(x => x.TheFollowingIsReturned(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig))))
.Then(x => x.TheFollowingIsReturned(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty, serviceProviderConfig, ""))))
.BDDfy();
}

View File

@ -51,14 +51,15 @@ namespace Ocelot.UnitTests.Configuration
}
[Fact]
public void should_use_global_cofiguration_over_re_route_specific()
public void should_use_re_route_over_global_specific()
{
var reRoute = new FileReRoute
{
RequestIdKey = "cheese"
}; var globalConfig = new FileGlobalConfiguration
};
var globalConfig = new FileGlobalConfiguration
{
RequestIdKey = "cheese"
RequestIdKey = "test"
};
this.Given(x => x.GivenTheFollowingReRoute(reRoute))

View File

@ -24,7 +24,7 @@ namespace Ocelot.UnitTests.DependencyInjection
{
private IServiceCollection _services;
private IServiceProvider _serviceProvider;
private IConfigurationRoot _configRoot;
private IConfiguration _configRoot;
private IOcelotBuilder _ocelotBuilder;
private int _maxRetries;
@ -35,6 +35,7 @@ namespace Ocelot.UnitTests.DependencyInjection
_services = new ServiceCollection();
_services.AddSingleton(builder);
_services.AddSingleton<IHostingEnvironment, HostingEnvironment>();
_services.AddSingleton<IConfiguration>(_configRoot);
_maxRetries = 100;
}
private Exception _ex;
@ -95,6 +96,14 @@ namespace Ocelot.UnitTests.DependencyInjection
.BDDfy();
}
[Fact]
public void should_set_up_without_passing_in_config()
{
this.When(x => WhenISetUpOcelotServicesWithoutConfig())
.Then(x => ThenAnExceptionIsntThrown())
.BDDfy();
}
private void ThenTheCorrectAdminPathIsRegitered()
{
_serviceProvider = _services.BuildServiceProvider();
@ -156,6 +165,19 @@ namespace Ocelot.UnitTests.DependencyInjection
_ex = e;
}
}
private void WhenISetUpOcelotServicesWithoutConfig()
{
try
{
_ocelotBuilder = _services.AddOcelot();
}
catch (Exception e)
{
_ex = e;
}
}
private void WhenISetUpCacheManager()
{
try

View File

@ -34,7 +34,7 @@
[Fact]
public void should_call_scoped_data_repository_correctly()
{
var config = new OcelotConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build());
var config = new OcelotConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build(), "");
this.Given(x => x.GivenTheDownStreamRouteFinderReturns(
new DownstreamRoute(

View File

@ -422,7 +422,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig)
{
_reRoutesConfig = reRoutesConfig;
_config = new OcelotConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig);
_config = new OcelotConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig, "");
}
private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath)

View File

@ -11,39 +11,96 @@ namespace Ocelot.UnitTests.Errors
using TestStack.BDDfy;
using Xunit;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.Provider;
using Moq;
using Ocelot.Configuration;
using Rafty.Concensus;
public class ExceptionHandlerMiddlewareTests : ServerHostedMiddlewareTest
{
bool _shouldThrowAnException = false;
private Mock<IOcelotConfigurationProvider> _provider;
public ExceptionHandlerMiddlewareTests()
{
_provider = new Mock<IOcelotConfigurationProvider>();
GivenTheTestServerIsConfigured();
}
[Fact]
public void NoDownstreamException()
{
var config = new OcelotConfiguration(null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddleware())
.Then(_ => ThenTheResponseIsOk())
.And(_ => TheRequestIdIsNotSet())
.BDDfy();
}
private void TheRequestIdIsNotSet()
{
ScopedRepository.Verify(x => x.Add<string>(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}
[Fact]
public void DownstreamException()
{
var config = new OcelotConfiguration(null, null, null, null);
this.Given(_ => GivenAnExceptionWillBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddleware())
.Then(_ => ThenTheResponseIsError())
.BDDfy();
}
[Fact]
public void ShouldSetRequestId()
{
var config = new OcelotConfiguration(null, null, null, "requestidkey");
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenTheResponseIsOk())
.And(_ => TheRequestIdIsSet("RequestId", "1234"))
.BDDfy();
}
[Fact]
public void ShouldNotSetRequestId()
{
var config = new OcelotConfiguration(null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenTheResponseIsOk())
.And(_ => TheRequestIdIsNotSet())
.BDDfy();
}
private void TheRequestIdIsSet(string key, string value)
{
ScopedRepository.Verify(x => x.Add<string>(key, value), Times.Once);
}
private void GivenTheConfigurationIs(IOcelotConfiguration config)
{
var response = new Ocelot.Responses.OkResponse<IOcelotConfiguration>(config);
_provider
.Setup(x => x.Get()).ReturnsAsync(response);
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(ScopedRepository.Object);
services.AddSingleton<IOcelotConfigurationProvider>(_provider.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)

View File

@ -25,7 +25,7 @@ namespace Ocelot.UnitTests.Infrastructure
//TODO - Additional tests -> HttpContent null. This should never happen
[Fact]
public void Get_returns_correct_key_from_http_context()
public void get_returns_correct_key_from_http_context()
{
this.Given(x => x.GivenAHttpContextContaining("key", "string"))
@ -35,7 +35,7 @@ namespace Ocelot.UnitTests.Infrastructure
}
[Fact]
public void Get_returns_error_response_if_the_key_is_not_found() //Therefore does not return null
public void get_returns_error_response_if_the_key_is_not_found() //Therefore does not return null
{
this.Given(x => x.GivenAHttpContextContaining("key", "string"))
.When(x => x.GetIsCalledWithKey<string>("keyDoesNotExist"))
@ -43,6 +43,21 @@ namespace Ocelot.UnitTests.Infrastructure
.BDDfy();
}
[Fact]
public void should_update()
{
this.Given(x => x.GivenAHttpContextContaining("key", "string"))
.And(x => x.UpdateIsCalledWith<string>("key", "new string"))
.When(x => x.GetIsCalledWithKey<string>("key"))
.Then(x => x.ThenTheResultIsAnOkResponse<string>("new string"))
.BDDfy();
}
private void UpdateIsCalledWith<T>(string key, string value)
{
_httpDataRepository.Update(key, value);
}
private void GivenAHttpContextContaining(string key, object o)
{
_httpContext.Items.Add(key, o);

View File

@ -19,14 +19,14 @@
using TestStack.BDDfy;
using Xunit;
public class RequestIdMiddlewareTests : ServerHostedMiddlewareTest
public class ReRouteRequestIdMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly HttpRequestMessage _downstreamRequest;
private Response<DownstreamRoute> _downstreamRoute;
private string _value;
private string _key;
public RequestIdMiddlewareTests()
public ReRouteRequestIdMiddlewareTests()
{
_downstreamRequest = new HttpRequestMessage();
@ -50,6 +50,7 @@
var requestId = Guid.NewGuid().ToString();
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => GivenThereIsNoGlobalRequestId())
.And(x => x.GivenTheRequestIdIsAddedToTheRequest("LSRequestId", requestId))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheTraceIdIs(requestId))
@ -67,11 +68,74 @@
.Build());
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => GivenThereIsNoGlobalRequestId())
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheTraceIdIsAnything())
.BDDfy();
}
[Fact]
public void should_add_request_id_scoped_repo_for_logging_later()
{
var downstreamRoute = new DownstreamRoute(new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamPathTemplate("any old string")
.WithRequestIdKey("LSRequestId")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build());
var requestId = Guid.NewGuid().ToString();
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => GivenThereIsNoGlobalRequestId())
.And(x => x.GivenTheRequestIdIsAddedToTheRequest("LSRequestId", requestId))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheTraceIdIs(requestId))
.And(x => ThenTheRequestIdIsSaved())
.BDDfy();
}
[Fact]
public void should_update_request_id_scoped_repo_for_logging_later()
{
var downstreamRoute = new DownstreamRoute(new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamPathTemplate("any old string")
.WithRequestIdKey("LSRequestId")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build());
var requestId = Guid.NewGuid().ToString();
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => GivenTheRequestIdWasSetGlobally())
.And(x => x.GivenTheRequestIdIsAddedToTheRequest("LSRequestId", requestId))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheTraceIdIs(requestId))
.And(x => ThenTheRequestIdIsUpdated())
.BDDfy();
}
private void GivenThereIsNoGlobalRequestId()
{
ScopedRepository.Setup(x => x.Get<string>("RequestId")).Returns(new OkResponse<string>(null));
}
private void GivenTheRequestIdWasSetGlobally()
{
ScopedRepository.Setup(x => x.Get<string>("RequestId")).Returns(new OkResponse<string>("alreadyset"));
}
private void ThenTheRequestIdIsSaved()
{
ScopedRepository.Verify(x => x.Add<string>("RequestId", _value), Times.Once);
}
private void ThenTheRequestIdIsUpdated()
{
ScopedRepository.Verify(x => x.Update<string>("RequestId", _value), Times.Once);
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();

View File

@ -53,6 +53,12 @@
ResponseMessage = Client.GetAsync(Url).Result;
}
protected void WhenICallTheMiddlewareWithTheRequestIdKey(string requestIdKey, string value)
{
Client.DefaultRequestHeaders.Add(requestIdKey, value);
ResponseMessage = Client.GetAsync(Url).Result;
}
public void Dispose()
{
Client.Dispose();