mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-17 22:50:05 +02:00
Add WireMockAspNetCoreLogger to log Kestrel warnings/errors (#1432)
* Add WireMockAspNetCoreLogger * fix tests * x * .
This commit is contained in:
@@ -8,6 +8,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using SharpYaml.Model;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
@@ -288,7 +289,24 @@ namespace WireMock.Net.ConsoleApplication
|
|||||||
|
|
||||||
var todos = new Dictionary<int, Todo>();
|
var todos = new Dictionary<int, Todo>();
|
||||||
|
|
||||||
var server = WireMockServer.Start();
|
var server = WireMockServer.Start(new WireMockServerSettings
|
||||||
|
{
|
||||||
|
Logger = new WireMockConsoleLogger(),
|
||||||
|
|
||||||
|
Port = 9091
|
||||||
|
});
|
||||||
|
|
||||||
|
server
|
||||||
|
.WhenRequest(r => r
|
||||||
|
.WithPath("/Content-Length")
|
||||||
|
.UsingAnyMethod()
|
||||||
|
)
|
||||||
|
.ThenRespondWith(r => r
|
||||||
|
.WithStatusCode(HttpStatusCode.OK)
|
||||||
|
.WithHeader("Content-Length", "42")
|
||||||
|
);
|
||||||
|
|
||||||
|
System.Console.ReadLine();
|
||||||
|
|
||||||
//server
|
//server
|
||||||
// .Given(Request.Create()
|
// .Given(Request.Create()
|
||||||
|
|||||||
38
src/WireMock.Net.Minimal/Logging/WireMockAspNetCoreLogger.cs
Normal file
38
src/WireMock.Net.Minimal/Logging/WireMockAspNetCoreLogger.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace WireMock.Logging;
|
||||||
|
|
||||||
|
internal sealed class WireMockAspNetCoreLogger(IWireMockLogger logger, string categoryName) : ILogger
|
||||||
|
{
|
||||||
|
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
|
||||||
|
|
||||||
|
public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Warning;
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||||
|
{
|
||||||
|
if (!IsEnabled(logLevel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = formatter(state, exception);
|
||||||
|
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
message = $"{message} | Exception: {exception}";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (logLevel)
|
||||||
|
{
|
||||||
|
case LogLevel.Warning:
|
||||||
|
logger.Warn("[{0}] {1}", categoryName, message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
logger.Error("[{0}] {1}", categoryName, message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace WireMock.Logging;
|
||||||
|
|
||||||
|
internal sealed class WireMockAspNetCoreLoggerProvider : ILoggerProvider
|
||||||
|
{
|
||||||
|
private readonly IWireMockLogger _logger;
|
||||||
|
|
||||||
|
public WireMockAspNetCoreLoggerProvider(IWireMockLogger logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger CreateLogger(string categoryName) => new WireMockAspNetCoreLogger(_logger, categoryName);
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Owin.Mappers;
|
using WireMock.Owin.Mappers;
|
||||||
@@ -57,6 +57,12 @@ internal partial class AspNetCoreSelfHost
|
|||||||
_host = builder
|
_host = builder
|
||||||
.UseSetting("suppressStatusMessages", "True") // https://andrewlock.net/suppressing-the-startup-and-shutdown-messages-in-asp-net-core/
|
.UseSetting("suppressStatusMessages", "True") // https://andrewlock.net/suppressing-the-startup-and-shutdown-messages-in-asp-net-core/
|
||||||
.ConfigureAppConfigurationUsingEnvironmentVariables()
|
.ConfigureAppConfigurationUsingEnvironmentVariables()
|
||||||
|
.ConfigureLogging(logging =>
|
||||||
|
{
|
||||||
|
logging.ClearProviders();
|
||||||
|
logging.AddProvider(new WireMockAspNetCoreLoggerProvider(_logger));
|
||||||
|
logging.SetMinimumLevel(LogLevel.Warning);
|
||||||
|
})
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
services.AddSingleton(_wireMockMiddlewareOptions);
|
services.AddSingleton(_wireMockMiddlewareOptions);
|
||||||
@@ -169,10 +175,10 @@ internal partial class AspNetCoreSelfHost
|
|||||||
|
|
||||||
return _host.RunAsync(token);
|
return _host.RunAsync(token);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
RunningException = e;
|
RunningException = ex;
|
||||||
_logger.Error(e.ToString());
|
_logger.Error("Error while RunAsync", ex);
|
||||||
|
|
||||||
IsStarted = false;
|
IsStarted = false;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -88,19 +86,15 @@ namespace WireMock.Owin.Mappers
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var statusCodeType = responseMessage.StatusCode?.GetType();
|
if (responseMessage.StatusCode is HttpStatusCode or int)
|
||||||
if (statusCodeType != null)
|
|
||||||
{
|
{
|
||||||
if (statusCodeType == typeof(int) || statusCodeType == typeof(int?) || statusCodeType.GetTypeInfo().IsEnum)
|
response.StatusCode = MapStatusCode((int) responseMessage.StatusCode);
|
||||||
{
|
}
|
||||||
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!);
|
else if (responseMessage.StatusCode is string statusCodeAsString)
|
||||||
}
|
{
|
||||||
else if (statusCodeType == typeof(string))
|
// Note: this case will also match on null
|
||||||
{
|
_ = int.TryParse(statusCodeAsString, out var statusCodeTypeAsInt);
|
||||||
// Note: this case will also match on null
|
response.StatusCode = MapStatusCode(statusCodeTypeAsInt);
|
||||||
int.TryParse(responseMessage.StatusCode as string, out var statusCodeTypeAsInt);
|
|
||||||
response.StatusCode = MapStatusCode(statusCodeTypeAsInt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SetResponseHeaders(responseMessage, bytes != null, response);
|
SetResponseHeaders(responseMessage, bytes != null, response);
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
|
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
|
||||||
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using JsonConverter.Newtonsoft.Json;
|
using JsonConverter.Newtonsoft.Json;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
message.Headers.GetValues("x").Should().Equal(new[] { "value-1" });
|
message.Headers.GetValues("x").Should().Equal(["value-1"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -101,7 +101,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
(await message.Content!.ReadAsStringAsync(_ct)).Should().Be("{\"x\":42}");
|
(await message.Content!.ReadAsStringAsync(_ct)).Should().Be("{\"x\":42}");
|
||||||
message.Content.Headers.GetValues("Content-Type").Should().Equal(new[] { "application/json" });
|
message.Content.Headers.GetValues("Content-Type").Should().Equal(["application/json"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -121,7 +121,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
(await message.Content!.ReadAsStringAsync(_ct)).Should().Be("{\"x\":42}");
|
(await message.Content!.ReadAsStringAsync(_ct)).Should().Be("{\"x\":42}");
|
||||||
message.Content.Headers.GetValues("Content-Type").Should().Equal(new[] { "application/json; charset=utf-8" });
|
message.Content.Headers.GetValues("Content-Type").Should().Equal(["application/json; charset=utf-8"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -142,7 +142,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
(await message.Content!.ReadAsStringAsync(_ct)).Should().Be("{\"x\":42}");
|
(await message.Content!.ReadAsStringAsync(_ct)).Should().Be("{\"x\":42}");
|
||||||
message.Content.Headers.GetValues("Content-Type").Should().Equal(new[] { "multipart/form-data" });
|
message.Content.Headers.GetValues("Content-Type").Should().Equal(["multipart/form-data"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
message.Content!.Headers.GetValues("Content-Type").Should().Equal(new[] { "application/xml" });
|
message.Content!.Headers.GetValues("Content-Type").Should().Equal(["application/xml"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -181,7 +181,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
message.Content!.Headers.GetValues("Content-Type").Should().Equal(new[] { "application/xml; charset=UTF-8" });
|
message.Content!.Headers.GetValues("Content-Type").Should().Equal(["application/xml; charset=UTF-8"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -200,7 +200,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
message.Content!.Headers.GetValues("Content-Type").Should().Equal(new[] { "application/xml; charset=Ascii" });
|
message.Content!.Headers.GetValues("Content-Type").Should().Equal(["application/xml; charset=Ascii"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -242,7 +242,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
(await message.Content!.ReadAsStringAsync(_ct)).Should().Be(body);
|
(await message.Content!.ReadAsStringAsync(_ct)).Should().Be(body);
|
||||||
message.Content.Headers.GetValues("Content-Type").Should().Equal(new[] { "multipart/form-data" });
|
message.Content.Headers.GetValues("Content-Type").Should().Equal(["multipart/form-data"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@@ -269,7 +269,4 @@ public class HttpRequestMessageHelperTests
|
|||||||
// Assert
|
// Assert
|
||||||
message.Content?.Headers.ContentLength.Should().Be(resultShouldBe ? value : null);
|
message.Content?.Headers.ContentLength.Should().Be(resultShouldBe ? value : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -706,7 +706,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output, ITestContextAcc
|
|||||||
public async Task WithWebSocketProxy_Should_Proxy_Multiple_TextMessages()
|
public async Task WithWebSocketProxy_Should_Proxy_Multiple_TextMessages()
|
||||||
{
|
{
|
||||||
// Arrange - Start target echo server
|
// Arrange - Start target echo server
|
||||||
using var exampleEchoServer = WireMockServer.Start(new WireMockServerSettings
|
var exampleEchoServer = WireMockServer.Start(new WireMockServerSettings
|
||||||
{
|
{
|
||||||
Logger = new TestOutputHelperWireMockLogger(output),
|
Logger = new TestOutputHelperWireMockLogger(output),
|
||||||
Urls = ["ws://localhost:0"]
|
Urls = ["ws://localhost:0"]
|
||||||
@@ -722,7 +722,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output, ITestContextAcc
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Arrange - Start proxy server
|
// Arrange - Start proxy server
|
||||||
using var sut = WireMockServer.Start(new WireMockServerSettings
|
var sut = WireMockServer.Start(new WireMockServerSettings
|
||||||
{
|
{
|
||||||
Logger = new TestOutputHelperWireMockLogger(output),
|
Logger = new TestOutputHelperWireMockLogger(output),
|
||||||
Urls = ["ws://localhost:0"]
|
Urls = ["ws://localhost:0"]
|
||||||
@@ -755,6 +755,14 @@ public class WebSocketIntegrationTests(ITestOutputHelper output, ITestContextAcc
|
|||||||
}
|
}
|
||||||
|
|
||||||
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
|
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
|
||||||
|
|
||||||
|
await Task.Delay(250, _ct);
|
||||||
|
|
||||||
|
sut.Stop();
|
||||||
|
sut.Dispose();
|
||||||
|
|
||||||
|
exampleEchoServer.Stop();
|
||||||
|
exampleEchoServer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -431,8 +431,13 @@ public partial class WireMockServerTests(ITestOutputHelper testOutputHelper)
|
|||||||
using var server = WireMockServer.Start();
|
using var server = WireMockServer.Start();
|
||||||
|
|
||||||
server
|
server
|
||||||
.Given(Request.Create().WithPath(path).UsingHead())
|
.WhenRequest(r => r
|
||||||
.RespondWith(Response.Create().WithHeader(HttpKnownHeaderNames.ContentLength, length));
|
.WithPath(path)
|
||||||
|
.UsingHead()
|
||||||
|
)
|
||||||
|
.ThenRespondWith(r => r
|
||||||
|
.WithHeader(HttpKnownHeaderNames.ContentLength, length)
|
||||||
|
);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, path);
|
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, path);
|
||||||
@@ -442,6 +447,45 @@ public partial class WireMockServerTests(ITestOutputHelper testOutputHelper)
|
|||||||
response.Content.Headers.GetValues(HttpKnownHeaderNames.ContentLength).Should().Contain(length);
|
response.Content.Headers.GetValues(HttpKnownHeaderNames.ContentLength).Should().Contain(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
[Theory]
|
||||||
|
[InlineData("DELETE")]
|
||||||
|
[InlineData("GET")]
|
||||||
|
[InlineData("OPTIONS")]
|
||||||
|
[InlineData("PATCH")]
|
||||||
|
[InlineData("POST")]
|
||||||
|
[InlineData("PUT")]
|
||||||
|
[InlineData("TRACE")]
|
||||||
|
public async Task WireMockServer_Should_LogAndThrowExceptionWhenInvalidContentLength(string method)
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
const string length = "42";
|
||||||
|
var path = $"/InvalidContentLength_{Guid.NewGuid()}";
|
||||||
|
using var server = WireMockServer.Start(new WireMockServerSettings
|
||||||
|
{
|
||||||
|
Logger = new TestOutputHelperWireMockLogger(testOutputHelper)
|
||||||
|
});
|
||||||
|
|
||||||
|
server
|
||||||
|
.WhenRequest(r => r
|
||||||
|
.WithPath(path)
|
||||||
|
.UsingAnyMethod()
|
||||||
|
)
|
||||||
|
.ThenRespondWith(r => r
|
||||||
|
.WithStatusCode(HttpStatusCode.OK)
|
||||||
|
.WithHeader(HttpKnownHeaderNames.ContentLength, length)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Parse(method), path);
|
||||||
|
var response = await server.CreateClient().SendAsync(httpRequestMessage, _ct);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
|
||||||
|
testOutputHelper.Output.Should().Contain($"Response Content-Length mismatch: too few bytes written (0 of {length}).");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("TRACE")]
|
[InlineData("TRACE")]
|
||||||
[InlineData("GET")]
|
[InlineData("GET")]
|
||||||
|
|||||||
Reference in New Issue
Block a user