问题
I am trying to add logging to my app using Web Api 2 and Owin, so I started using Microsoft Owin Logging, which requires an ILogger
and ILoggerFactory
, that has been implemented and it works great when I need to log anything inside the STARTUP method or any of the Owin Middleware components.
For example, when I am in the Startup
method I can create the logger using:
public void Configuration(IAppBuilder app)
{
// Creates configuration
var configuration = new HttpConfiguration();
// Configure WebApi Settings
WebApiConfig.Register(configuration);
app.SetLoggerFactory(new OwinLog4NetLoggerFactory("Default"));
var logger = app.CreateLogger<Startup>();
logger.WriteInformation("test log");
// Enabled WebApi in OWIN
app.UseWebApi(configuration);
}
Where "OwinLog4NetLoggerFactory" is my custom ILoggerFactory implementation.
So far, so good... but... How can I create the logger when I am in the actual web api action method?... I tried accessing the Request.GetOwinEnvironment()
and the logger factory is not in the dictionary.
For example:
public class AccountController : ApiController
{
public int Get(int id)
{
// Create logger here
return id + 1;
}
}
I know I can create a static class with a reference to the Logger Factory or even Injection to add the logger to the api controller, but that seems too complicated for something that should be already there.
Any ideas would be appreciated.
回答1:
I'd recommend writing your middleware so that you can handle the logging outside of the controller:
public class LoggingMiddleware : OwinMiddleware
{
public LoggingMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
//handle request logging
await Next.Invoke(context);
//handle response logging
}
}
Then in Startup class:
public class Startup
{
// ReSharper disable once UnusedMember.Global
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
appBuilder.Use<LoggingMiddleware>();
appBuilder.UseWebApi(config);
}
}
The request would then come in, hit the request logging code in the LoggingMiddleware, hit the controller code and then response would be logged on the LoggingMiddleware on the way back.
However, if all you are wanting to do is send an object through from middleware to the controller you can use context.Set("loggingObject", loggingObject);
in the middleware and then
var loggingObject = Request.GetOwinContext().Get<LoggerClass>("loggingObject");
in the controller.
回答2:
instead of adding logging code in every method, I create a MessageLoggingHandler that can be registered in Global.asax.cs once, and it then logs every Request and Response.
Here is the code that I use, you can modify as per your requirements:
First Create a MessageHandler class that inherits from DelegationHandler:
public abstract class MessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var corrId = Guid.NewGuid();
var requestMethod = request.Method.Method.ToString();
var requestUri = request.RequestUri.ToString();
var ipAddress = request.GetOwinContext().Request.RemoteIpAddress;
var requestMessage = await request.Content.ReadAsByteArrayAsync();
await LogMessageAsync(corrId, requestUri, ipAddress, "Request", requestMethod, request.Headers.ToString(), requestMessage, string.Empty);
var response = await base.SendAsync(request, cancellationToken);
var responseMessage = await response.Content.ReadAsByteArrayAsync();
await LogMessageAsync(corrId, requestUri, ipAddress, "Response", requestMethod, response.Headers.ToString(), responseMessage, ((int)response.StatusCode).ToString() + "-" + response.ReasonPhrase);
return response;
}
protected abstract Task LogMessageAsync(Guid CorrelationId, string APIUrl, string ClientIPAddress, string RequestResponse, string HttpMethod, string HttpHeaders, byte[] HttpMessage, string HttpStatusCode);
}
public class MessageLoggingHandler : MessageHandler
{
protected override async Task LogMessageAsync(Guid CorrelationId, string APIUrl, string ClientIPAddress, string RequestResponse, string HttpMethod, string HttpHeaders, byte[] HttpMessage, string HttpStatusCode)
{
// Create logger here
//Do your logging here
}
}
Then in your Global.asax.cs, you need to register the above created MessageLoggingHandler:
GlobalConfiguration.Configuration.MessageHandlers.Add(new MessageLoggingHandler());
Just be aware, this will log every request and response, will full message body. This can take a lot of space very quickly (depending on your API's usage). So you may need to tweak this (for example - keep records for a month or so, or ignore 200-OK responses etc)
回答3:
I would recommend using the Common.Logging library in your applications, available on NuGet. Common.Logging gives you a common interface for using your preferred logging solution. It solves a lot of issues like yours. Here is an example using Common.Logging with NLog:
In your controller, you would access it like so:
public class MyController : ApiController
{
private static readonly ILog Log = LogManager.GetLogger<MyController>();
public async Task<IHttpActionResult> Get([FromUri] int id)
{
Log.Debug("Called Get with id " + id.ToString());
return Ok();
}
}
Pick up the latest Common.Logging.NLog package on NuGet (as of this writing, it should be Common.Logging.NLog41). Then in your web.config, you would configure Common.Logging to use your NLog configuration:
<common>
<logging>
<factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog41">
<arg key="configType" value="FILE" />
<arg key="configFile" value="~/NLog.config" />
</factoryAdapter>
</logging>
</common>
Here are some additional links:
https://github.com/net-commons/common-logging
https://cmatskas.com/an-introduction-to-common-logging-api-2/
来源:https://stackoverflow.com/questions/32429462/microsoft-owin-logging-web-api-2-how-do-i-create-the-logger