Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Add test logging helpers to Hosting #1005

Merged
merged 16 commits into from Apr 6, 2017
4 changes: 3 additions & 1 deletion build/dependencies.props
Expand Up @@ -11,5 +11,7 @@
<WebAdministrationVersion>7.0.0</WebAdministrationVersion>
<WindowsApiSetsVersion>1.0.1</WindowsApiSetsVersion>
<XunitVersion>2.2.0</XunitVersion>
<SerilogExtensionsLoggingVersion>1.4.0</SerilogExtensionsLoggingVersion>
<SerilogFileSinkVersion>3.2.0</SerilogFileSinkVersion>
</PropertyGroup>
</Project>
</Project>
@@ -1,7 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Net.Http;
using System.Threading;
using Microsoft.Extensions.Logging;

namespace Microsoft.AspNetCore.Server.IntegrationTesting
{
Expand All @@ -10,25 +13,57 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
/// </summary>
public class DeploymentResult
{
private readonly ILoggerFactory _loggerFactory;

/// <summary>
/// Base Uri of the deployment application.
/// </summary>
public string ApplicationBaseUri { get; set; }
public string ApplicationBaseUri { get; }

/// <summary>
/// The folder where the application is hosted. This path can be different from the
/// original application source location if published before deployment.
/// </summary>
public string ContentRoot { get; set; }
public string ContentRoot { get; }

/// <summary>
/// Original deployment parameters used for this deployment.
/// </summary>
public DeploymentParameters DeploymentParameters { get; set; }
public DeploymentParameters DeploymentParameters { get; }

/// <summary>
/// Triggered when the host process dies or pulled down.
/// </summary>
public CancellationToken HostShutdownToken { get; set; }
public CancellationToken HostShutdownToken { get; }

/// <summary>
/// An <see cref="HttpClient"/> with <see cref="LoggingHandler"/> configured and the <see cref="HttpClient.BaseAddress"/> set to the <see cref="ApplicationBaseUri"/>
/// </summary>
public HttpClient HttpClient { get; }

public DeploymentResult(ILoggerFactory loggerFactory, DeploymentParameters deploymentParameters, string applicationBaseUri)
: this(loggerFactory, deploymentParameters: deploymentParameters, applicationBaseUri: applicationBaseUri, contentRoot: string.Empty, hostShutdownToken: CancellationToken.None)
{ }

public DeploymentResult(ILoggerFactory loggerFactory, DeploymentParameters deploymentParameters, string applicationBaseUri, string contentRoot, CancellationToken hostShutdownToken)
{
_loggerFactory = loggerFactory;

ApplicationBaseUri = applicationBaseUri;
ContentRoot = contentRoot;
DeploymentParameters = deploymentParameters;
HostShutdownToken = hostShutdownToken;

HttpClient = CreateHttpClient(new HttpClientHandler());
}

/// <summary>
/// Create an <see cref="HttpClient"/> with <see cref="LoggingHandler"/> configured and the <see cref="HttpClient.BaseAddress"/> set to the <see cref="ApplicationBaseUri"/>,
/// but using the provided <see cref="HttpMessageHandler"/> and the underlying handler.
/// </summary>
/// <param name="baseHandler"></param>
/// <returns></returns>
public HttpClient CreateHttpClient(HttpMessageHandler baseHandler) =>
new HttpClient(new LoggingHandler(_loggerFactory, baseHandler)) { BaseAddress = new Uri(ApplicationBaseUri) };
}
}
@@ -0,0 +1,34 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace Microsoft.AspNetCore.Server.IntegrationTesting
{
internal class LoggingHandler : DelegatingHandler
{
private ILogger _logger;

public LoggingHandler(ILoggerFactory loggerFactory, HttpMessageHandler innerHandler) : base(innerHandler)
{
_logger = loggerFactory.CreateLogger<HttpClient>();
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
_logger.LogDebug("Sending {method} {url}", request.Method, request.RequestUri);
try
{
var response = await base.SendAsync(request, cancellationToken);
_logger.LogDebug("Received {statusCode} {reasonPhrase} {url}", response.StatusCode, response.ReasonPhrase, request.RequestUri);
return response;
}
catch (Exception ex)
{
_logger.LogError(0, ex, "Exception while sending '{method} {url}'", request.Method, request.RequestUri, request.RequestUri);
throw;
}
}
}
}
Expand Up @@ -14,7 +14,7 @@ public static void StartAndCaptureOutAndErrToLogger(this Process process, string
{
if (!string.IsNullOrEmpty(dataArgs.Data))
{
logger.LogWarning($"{prefix} stdout: {{line}}", dataArgs.Data);
logger.LogInformation($"{prefix} stdout: {{line}}", dataArgs.Data);
}
};

Expand Down
@@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
Expand Down Expand Up @@ -59,7 +59,7 @@ public class RetryHelper
if (exception is HttpRequestException
#if NET46
|| exception is System.Net.WebException
#elif NETSTANDARD1_3
#elif NETSTANDARD1_5
#else
#error Target frameworks need to be updated.
#endif
Expand Down
Expand Up @@ -26,14 +26,16 @@ public abstract class ApplicationDeployer : IApplicationDeployer

private readonly Stopwatch _stopwatch = new Stopwatch();

public ApplicationDeployer(DeploymentParameters deploymentParameters, ILogger logger)
public ApplicationDeployer(DeploymentParameters deploymentParameters, ILoggerFactory loggerFactory)
{
DeploymentParameters = deploymentParameters;
Logger = logger;
LoggerFactory = loggerFactory;
Logger = LoggerFactory.CreateLogger(GetType().FullName);
}

protected DeploymentParameters DeploymentParameters { get; }

protected ILoggerFactory LoggerFactory { get; }
protected ILogger Logger { get; }

public abstract Task<DeploymentResult> DeployAsync();
Expand Down
Expand Up @@ -32,14 +32,14 @@ public static IApplicationDeployer Create(DeploymentParameters deploymentParamet
switch (deploymentParameters.ServerType)
{
case ServerType.IISExpress:
return new IISExpressDeployer(deploymentParameters, loggerFactory.CreateLogger<IISExpressDeployer>());
return new IISExpressDeployer(deploymentParameters, loggerFactory);
case ServerType.IIS:
throw new NotSupportedException("The IIS deployer is no longer supported");
case ServerType.WebListener:
case ServerType.Kestrel:
return new SelfHostDeployer(deploymentParameters, loggerFactory.CreateLogger<SelfHostDeployer>());
return new SelfHostDeployer(deploymentParameters, loggerFactory);
case ServerType.Nginx:
return new NginxDeployer(deploymentParameters, loggerFactory.CreateLogger<NginxDeployer>());
return new NginxDeployer(deploymentParameters, loggerFactory);
default:
throw new NotSupportedException(
string.Format("Found no deployers suitable for server type '{0}' with the current runtime.",
Expand Down
Expand Up @@ -27,8 +27,8 @@ public class IISExpressDeployer : ApplicationDeployer

private Process _hostProcess;

public IISExpressDeployer(DeploymentParameters deploymentParameters, ILogger logger)
: base(deploymentParameters, logger)
public IISExpressDeployer(DeploymentParameters deploymentParameters, ILoggerFactory loggerFactory)
: base(deploymentParameters, loggerFactory)
{
}

Expand Down Expand Up @@ -74,14 +74,13 @@ public override async Task<DeploymentResult> DeployAsync()

Logger.LogInformation("Application ready at URL: {appUrl}", actualUri);

return new DeploymentResult
{
ContentRoot = contentRoot,
DeploymentParameters = DeploymentParameters,
// Right now this works only for urls like http://localhost:5001/. Does not work for http://localhost:5001/subpath.
ApplicationBaseUri = actualUri.ToString(),
HostShutdownToken = hostExitToken
};
// Right now this works only for urls like http://localhost:5001/. Does not work for http://localhost:5001/subpath.
return new DeploymentResult(
LoggerFactory,
DeploymentParameters,
applicationBaseUri: actualUri.ToString(),
contentRoot: contentRoot,
hostShutdownToken: hostExitToken);
}
}

Expand Down Expand Up @@ -294,7 +293,7 @@ public override void Dispose()

// If by this point, the host process is still running (somehow), throw an error.
// A test failure is better than a silent hang and unknown failure later on
if(!_hostProcess.HasExited)
if (!_hostProcess.HasExited)
{
throw new Exception($"iisexpress Process {_hostProcess.Id} failed to shutdown");
}
Expand Down
Expand Up @@ -19,8 +19,8 @@ public class NginxDeployer : SelfHostDeployer
private string _configFile;
private readonly int _waitTime = (int)TimeSpan.FromSeconds(30).TotalMilliseconds;

public NginxDeployer(DeploymentParameters deploymentParameters, ILogger logger)
: base(deploymentParameters, logger)
public NginxDeployer(DeploymentParameters deploymentParameters, ILoggerFactory loggerFactory)
: base(deploymentParameters, loggerFactory)
{
}

Expand Down Expand Up @@ -61,13 +61,12 @@ public override async Task<DeploymentResult> DeployAsync()
}
}

return new DeploymentResult
{
ContentRoot = DeploymentParameters.ApplicationPath,
DeploymentParameters = DeploymentParameters,
ApplicationBaseUri = uri.ToString(),
HostShutdownToken = exitToken
};
return new DeploymentResult(
LoggerFactory,
DeploymentParameters,
applicationBaseUri: uri.ToString(),
contentRoot: DeploymentParameters.ApplicationPath,
hostShutdownToken: exitToken);
}
}

Expand Down
Expand Up @@ -26,8 +26,8 @@ public class RemoteWindowsDeployer : ApplicationDeployer
private bool _isDisposed;
private static readonly Lazy<Scripts> _scripts = new Lazy<Scripts>(() => CopyEmbeddedScriptFilesToDisk());

public RemoteWindowsDeployer(RemoteWindowsDeploymentParameters deploymentParameters, ILogger logger)
: base(deploymentParameters, logger)
public RemoteWindowsDeployer(RemoteWindowsDeploymentParameters deploymentParameters, ILoggerFactory loggerFactory)
: base(deploymentParameters, loggerFactory)
{
_deploymentParameters = deploymentParameters;

Expand Down Expand Up @@ -103,11 +103,10 @@ public override async Task<DeploymentResult> DeployAsync()

await RunScriptAsync("StartServer");

return new DeploymentResult
{
ApplicationBaseUri = DeploymentParameters.ApplicationBaseUriHint,
DeploymentParameters = DeploymentParameters
};
return new DeploymentResult(
LoggerFactory,
DeploymentParameters,
DeploymentParameters.ApplicationBaseUriHint);
}
}

Expand Down
Expand Up @@ -23,8 +23,8 @@ public class SelfHostDeployer : ApplicationDeployer

public Process HostProcess { get; private set; }

public SelfHostDeployer(DeploymentParameters deploymentParameters, ILogger logger)
: base(deploymentParameters, logger)
public SelfHostDeployer(DeploymentParameters deploymentParameters, ILoggerFactory loggerFactory)
: base(deploymentParameters, loggerFactory)
{
}

Expand All @@ -47,13 +47,12 @@ public override async Task<DeploymentResult> DeployAsync()

Logger.LogInformation("Application ready at URL: {appUrl}", actualUrl);

return new DeploymentResult
{
ContentRoot = DeploymentParameters.PublishApplicationBeforeDeployment ? DeploymentParameters.PublishedApplicationRootPath : DeploymentParameters.ApplicationPath,
DeploymentParameters = DeploymentParameters,
ApplicationBaseUri = actualUrl.ToString(),
HostShutdownToken = hostExitToken
};
return new DeploymentResult(
LoggerFactory,
DeploymentParameters,
applicationBaseUri: actualUrl.ToString(),
contentRoot: DeploymentParameters.PublishApplicationBeforeDeployment ? DeploymentParameters.PublishedApplicationRootPath : DeploymentParameters.ApplicationPath,
hostShutdownToken: hostExitToken);
}
}

Expand Down
Expand Up @@ -5,7 +5,7 @@
<PropertyGroup>
<Description>ASP.NET Core helpers to deploy applications to IIS Express, IIS, WebListener and Kestrel for testing.</Description>
<VersionPrefix>0.4.0</VersionPrefix>
<TargetFrameworks>net46;netstandard1.3</TargetFrameworks>
<TargetFrameworks>net46;netstandard1.5</TargetFrameworks>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What did you need from 1.5?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assembly.GetEntryAssembly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And since these are only used in tests which target netcoreapp1.1 and we don't ship the package, this is a fully compatible change (though I will be confirming that before merging)

<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;testing</PackageTags>
Expand All @@ -20,11 +20,15 @@
<PackageReference Include="System.ValueTuple" Version="$(CoreFxVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.RuntimeEnvironment.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
<PackageReference Include="Microsoft.NETCore.Windows.ApiSets" Version="$(WindowsApiSetsVersion)" />
<PackageReference Include="Serilog.Extensions.Logging" Version="$(SerilogExtensionsLoggingVersion)" />
<PackageReference Include="Serilog.Sinks.File" Version="$(SerilogFileSinkVersion)" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
Expand All @@ -33,7 +37,7 @@
<Reference Include="System.Xml.Linq" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
<PackageReference Include="System.Diagnostics.Process" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Threading.Thread" Version="$(CoreFxVersion)" />
</ItemGroup>
Expand Down