From 0f48e2c96c597947ec5fb184e69b55208d0354c0 Mon Sep 17 00:00:00 2001 From: Carolyn Van Slyck Date: Tue, 28 Jul 2015 18:00:15 -0500 Subject: [PATCH 1/2] Trace HTTP requests/errors under Flurl.Http source The default level is "Error". To enable tracing add the following to the app.config ```xml ``` or programmatically enable tracing via: ```csharp OpenStackNet.Tracing.Http.Listeners.Add(myListener); OpenStackNet.Tracing.Http.Switch.Level = SourceLevels.All; ``` Fixes #533 --- .../{Configuration.cs => OpenStackNet.cs} | 19 +++++++++++++++++++ src/corelib/corelib.v4.0.csproj | 2 +- src/testing/unit/HttpTest.cs | 5 +++++ 3 files changed, 25 insertions(+), 1 deletion(-) rename src/corelib/{Configuration.cs => OpenStackNet.cs} (77%) diff --git a/src/corelib/Configuration.cs b/src/corelib/OpenStackNet.cs similarity index 77% rename from src/corelib/Configuration.cs rename to src/corelib/OpenStackNet.cs index 559d70973..90e2b159e 100644 --- a/src/corelib/Configuration.cs +++ b/src/corelib/OpenStackNet.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using Flurl.Http; using Flurl.Http.Configuration; using Newtonsoft.Json; @@ -54,6 +55,8 @@ public static void Configure(Action configureFlur { // Apply our default settings c.HttpClientFactory = new AuthenticatedHttpClientFactory(); + c.AfterCall = Tracing.TraceHttpCall; + c.OnError = Tracing.TraceFailedHttpCall; // Apply application's default settings if (configureFlurl != null) @@ -63,5 +66,21 @@ public static void Configure(Action configureFlur _isConfigured = true; } } + + public static class Tracing + { + public static readonly TraceSource Http = new TraceSource("Flurl.Http"); + + public static void TraceFailedHttpCall(HttpCall httpCall) + { + Http.TraceData(TraceEventType.Error, 0, JsonConvert.SerializeObject(httpCall, Formatting.Indented)); + Http.Flush(); + } + + public static void TraceHttpCall(HttpCall httpCall) + { + Http.TraceData(TraceEventType.Information, 0, JsonConvert.SerializeObject(httpCall, Formatting.Indented)); + } + } } } diff --git a/src/corelib/corelib.v4.0.csproj b/src/corelib/corelib.v4.0.csproj index 544f7aca4..58b3a16d6 100644 --- a/src/corelib/corelib.v4.0.csproj +++ b/src/corelib/corelib.v4.0.csproj @@ -80,7 +80,7 @@ - + diff --git a/src/testing/unit/HttpTest.cs b/src/testing/unit/HttpTest.cs index b940779ba..a63c18959 100644 --- a/src/testing/unit/HttpTest.cs +++ b/src/testing/unit/HttpTest.cs @@ -23,6 +23,11 @@ public HttpTest() FlurlHttp.Configure(opts => { opts.HttpClientFactory = new TestHttpClientFactory(this); + opts.AfterCall = call => // Restore handler which was nuked by the base HttpTest + { + CallLog.Add(call); + OpenStackNet.Tracing.TraceHttpCall(call); + }; }); } From 94ff49bf0aa7a5c51423bb31fdac7a90745a0be4 Mon Sep 17 00:00:00 2001 From: Carolyn Van Slyck Date: Thu, 30 Jul 2015 10:56:44 -0500 Subject: [PATCH 2/2] Turn on tracing in integration tests --- src/corelib/OpenStackNet.cs | 19 ++++++- .../v1/ContentDeliveryNetworkServiceTests.cs | 5 +- .../v1/ServiceTests.cs | 52 ++++++++++--------- src/testing/integration/XunitTraceListener.cs | 28 ++++++++++ src/testing/integration/integration.csproj | 3 +- 5 files changed, 79 insertions(+), 28 deletions(-) create mode 100644 src/testing/integration/XunitTraceListener.cs diff --git a/src/corelib/OpenStackNet.cs b/src/corelib/OpenStackNet.cs index 90e2b159e..d4f530376 100644 --- a/src/corelib/OpenStackNet.cs +++ b/src/corelib/OpenStackNet.cs @@ -67,16 +67,33 @@ public static void Configure(Action configureFlur } } + /// + /// Provides global point for programmatically configuraing tracing + /// public static class Tracing { - public static readonly TraceSource Http = new TraceSource("Flurl.Http"); + /// + /// Trace source for all HTTP requests. Default level is Error. + /// + /// In your app or web.config the trace soruce name is "Flurl.Http". + /// + /// + public static readonly TraceSource Http = new TraceSource("Flurl.Http", SourceLevels.Error); + /// + /// Traces a failed HTTP request + /// + /// The Flurl HTTP call instance, containing information about the request and response. public static void TraceFailedHttpCall(HttpCall httpCall) { Http.TraceData(TraceEventType.Error, 0, JsonConvert.SerializeObject(httpCall, Formatting.Indented)); Http.Flush(); } + /// + /// Traces an HTTP request + /// + /// The Flurl HTTP call instance, containing information about the request and response. public static void TraceHttpCall(HttpCall httpCall) { Http.TraceData(TraceEventType.Information, 0, JsonConvert.SerializeObject(httpCall, Formatting.Indented)); diff --git a/src/testing/integration/ContentDeliveryNetworks/v1/ContentDeliveryNetworkServiceTests.cs b/src/testing/integration/ContentDeliveryNetworks/v1/ContentDeliveryNetworkServiceTests.cs index d1259f0cd..62763d2b0 100644 --- a/src/testing/integration/ContentDeliveryNetworks/v1/ContentDeliveryNetworkServiceTests.cs +++ b/src/testing/integration/ContentDeliveryNetworks/v1/ContentDeliveryNetworkServiceTests.cs @@ -1,6 +1,7 @@ using net.openstack.Providers.Rackspace; using OpenStack.Synchronous; using Xunit; +using Xunit.Abstractions; namespace OpenStack.ContentDeliveryNetworks.v1 { @@ -8,8 +9,10 @@ public class ContentDeliveryNetworkServiceTests { private readonly ContentDeliveryNetworkService _cdnService; - public ContentDeliveryNetworkServiceTests() + public ContentDeliveryNetworkServiceTests(ITestOutputHelper testLog) { + OpenStackNet.Tracing.Http.Listeners.Add(new XunitTraceListener(testLog)); + var identity = TestIdentityProvider.GetIdentityFromEnvironment(); var authenticationProvider = new CloudIdentityProvider(identity) { diff --git a/src/testing/integration/ContentDeliveryNetworks/v1/ServiceTests.cs b/src/testing/integration/ContentDeliveryNetworks/v1/ServiceTests.cs index a986947fb..a5a878512 100644 --- a/src/testing/integration/ContentDeliveryNetworks/v1/ServiceTests.cs +++ b/src/testing/integration/ContentDeliveryNetworks/v1/ServiceTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Flurl.Http; @@ -14,12 +15,13 @@ namespace OpenStack.ContentDeliveryNetworks.v1 { public class ServiceTests { - private readonly ITestOutputHelper _log; private readonly ContentDeliveryNetworkService _cdnService; - public ServiceTests(ITestOutputHelper log) + public ServiceTests(ITestOutputHelper testLog) { - _log = log; + var testOutput = new XunitTraceListener(testLog); + OpenStackNet.Tracing.Http.Listeners.Add(testOutput); + Trace.Listeners.Add(testOutput); var identity = TestIdentityProvider.GetIdentityFromEnvironment(); var authenticationProvider = new CloudIdentityProvider(identity) @@ -34,26 +36,26 @@ public async void CreateAServiceOnAkamai_UsingDefaults() { try { - _log.WriteLine("Looking for a CDN flavor provided by Akamai..."); + Trace.WriteLine("Looking for a CDN flavor provided by Akamai..."); var flavors = await _cdnService.ListFlavorsAsync(); var flavor = flavors.FirstOrDefault(x => x.Providers.Any(p => string.Equals(p.Name, "Akamai", StringComparison.OrdinalIgnoreCase))); Assert.NotNull(flavor); var akamaiFlavor = flavor.Id; - _log.WriteLine("Found the {0} flavor", akamaiFlavor); + Trace.WriteLine(string.Format("Found the {0} flavor", akamaiFlavor)); - _log.WriteLine("Creating a CDN service using defaults for anything I can omit..."); + Trace.WriteLine("Creating a CDN service using defaults for anything I can omit..."); var domains = new[] {new ServiceDomain("mirror.example.com")}; var origins = new[] {new ServiceOrigin("example.com")}; var serviceDefinition = new ServiceDefinition("ci-test", akamaiFlavor, domains, origins); var serviceId = await _cdnService.CreateServiceAsync(serviceDefinition); - _log.WriteLine("Service was created: {0}", serviceId); + Trace.WriteLine(string.Format("Service was created: {0}", serviceId)); try { - _log.WriteLine("Waiting for the service to be deployed..."); - var service = await _cdnService.WaitForServiceDeployedAsync(serviceId, progress: new Progress(x => _log.WriteLine("..."))); + Trace.WriteLine("Waiting for the service to be deployed..."); + var service = await _cdnService.WaitForServiceDeployedAsync(serviceId, progress: new Progress(x => Trace.WriteLine("..."))); - _log.WriteLine("Verifying service matches the requested definition..."); + Trace.WriteLine("Verifying service matches the requested definition..."); Assert.Equal("ci-test", service.Name); Assert.Equal(serviceDefinition.FlavorId, service.FlavorId); @@ -63,31 +65,31 @@ public async void CreateAServiceOnAkamai_UsingDefaults() Assert.Equal(serviceDefinition.Domains.Count, service.Domains.Count()); Assert.Equal(serviceDefinition.Domains.First().Domain, service.Domains.First().Domain); - _log.WriteLine("Updating the service..."); + Trace.WriteLine("Updating the service..."); var patch = new JsonPatchDocument(); patch.Replace(x => x.Name, "ci-test2"); var intranetOnly = new ServiceRestriction("intranet", new[] {new ServiceRestrictionRule("intranet", "intranet.example.com")}); patch.Add(x => x.Restrictions, intranetOnly, 0); await _cdnService.UpdateServiceAsync(serviceId, patch); - _log.WriteLine("Waiting for the service changes to be deployed..."); - service = await _cdnService.WaitForServiceDeployedAsync(serviceId, progress: new Progress(x => _log.WriteLine("..."))); + Trace.WriteLine("Waiting for the service changes to be deployed..."); + service = await _cdnService.WaitForServiceDeployedAsync(serviceId, progress: new Progress(x => Trace.WriteLine("..."))); - _log.WriteLine("Verifying service matches updated definition..."); + Trace.WriteLine("Verifying service matches updated definition..."); Assert.Equal("ci-test2", service.Name); Assert.Equal(JsonConvert.SerializeObject(intranetOnly), JsonConvert.SerializeObject(service.Restrictions.First())); - _log.WriteLine("Purging all assets on service"); + Trace.WriteLine("Purging all assets on service"); await _cdnService.PurgeCachedAssetsAsync(serviceId); } finally { - _log.WriteLine("Cleaning up any test data..."); + Trace.WriteLine("Cleaning up any test data..."); - _log.WriteLine("Removing the service..."); + Trace.WriteLine("Removing the service..."); _cdnService.DeleteService(serviceId); _cdnService.WaitForServiceDeleted(serviceId); - _log.WriteLine("The service was cleaned up sucessfully."); + Trace.WriteLine("The service was cleaned up sucessfully."); } } catch (FlurlHttpException ex) @@ -113,7 +115,7 @@ public async void FindServiceOnAPage() { if (currentPage.Any(x => x.Name == "ci-test3")) { - _log.WriteLine("Found the desired service"); + Trace.WriteLine("Found the desired service"); break; } @@ -126,17 +128,17 @@ public async void FindServiceOnAPage() } finally { - _log.WriteLine("Cleaning up any test data..."); + Trace.WriteLine("Cleaning up any test data..."); - _log.WriteLine("Removing the services..."); + Trace.WriteLine("Removing the services..."); var deletes = serviceIds.Select(serviceId => _cdnService .DeleteServiceAsync(serviceId) .ContinueWith(t => _cdnService.WaitForServiceDeletedAsync(serviceId)) - .ContinueWith(t => _log.WriteLine("Service was deleted: {0}", serviceId))) + .ContinueWith(t => Trace.WriteLine(string.Format("Service was deleted: {0}", serviceId)))) .ToArray(); Task.WaitAll(deletes); - _log.WriteLine("The services were cleaned up sucessfully."); + Trace.WriteLine("The services were cleaned up sucessfully."); } } @@ -146,12 +148,12 @@ private async Task CreateService(string name, string domain, string orig var flavors = await _cdnService.ListFlavorsAsync(); var flavor = flavors.First(); - _log.WriteLine("Creating CDN Service: {0} for {1} originating from {2}", name, domain, origin); + Trace.WriteLine(string.Format("Creating CDN Service: {0} for {1} originating from {2}", name, domain, origin)); var domains = new[] { new ServiceDomain(domain) }; var origins = new[] { new ServiceOrigin(origin) }; var serviceDefinition = new ServiceDefinition(name, flavor.Id, domains, origins); var serviceId = await _cdnService.CreateServiceAsync(serviceDefinition); - _log.WriteLine("Service was created: {0}", serviceId); + Trace.WriteLine("Service was created: {0}", serviceId); return serviceId; } } diff --git a/src/testing/integration/XunitTraceListener.cs b/src/testing/integration/XunitTraceListener.cs new file mode 100644 index 000000000..3d319e581 --- /dev/null +++ b/src/testing/integration/XunitTraceListener.cs @@ -0,0 +1,28 @@ +using System.Diagnostics; +using Xunit.Abstractions; + +namespace OpenStack +{ + public class XunitTraceListener : TraceListener + { + private readonly ITestOutputHelper _testLog; + + public XunitTraceListener(ITestOutputHelper testLog) + { + _testLog = testLog; + } + + public override void Write(string message) + { + if (message.StartsWith(OpenStackNet.Tracing.Http.Name)) + return; + + _testLog.WriteLine(message); + } + + public override void WriteLine(string message) + { + _testLog.WriteLine(message); + } + } +} diff --git a/src/testing/integration/integration.csproj b/src/testing/integration/integration.csproj index 56c162182..0e136e5ce 100644 --- a/src/testing/integration/integration.csproj +++ b/src/testing/integration/integration.csproj @@ -130,6 +130,7 @@ + @@ -185,4 +186,4 @@ --> - + \ No newline at end of file