Utilizamos el siguiente filtro que agregamos a HttpConfiguration.Filters
. Algunos código:
internal class LoggingFilter : IExceptionFilter, IActionFilter
{
private readonly ILog log;
public LoggingFilter(ILog log)
{
if (log == null)
{
throw new ArgumentNullException("log");
}
this.log = log;
}
public bool AllowMultiple
{
get { return false; }
}
Task IExceptionFilter.ExecuteExceptionFilterAsync(HttpActionExecutedContext actionContext, CancellationToken cancellationToken)
{
if (actionContext == null)
{
throw new ArgumentNullException("actionContext");
}
this.log.Error(string.Format("Unexpected error while executing {0}", this.BuildLogEntry(actionContext.ActionContext)), actionContext.Exception);
return TaskHelpers.Completed();
}
Task<HttpResponseMessage> IActionFilter.ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
if (actionContext == null)
{
throw new ArgumentNullException("actionContext");
}
if (continuation == null)
{
throw new ArgumentNullException("continuation");
}
if (!this.log.IsDebugEnabled)
{
// no point running at all if logging isn't currently enabled
return continuation();
}
string logEntry = this.BuildLogEntry(actionContext);
IDisposable logContext = this.log.DebugTiming("Executing {0}", logEntry);
Task<string> requestContent;
if (actionContext.Request.Content != null)
{
requestContent = actionContext.Request.Content.ReadAsStringAsync().ContinueWith(requestResult => string.IsNullOrEmpty(requestResult.Result) ? "N/A" : requestResult.Result);
}
else
{
requestContent = TaskHelpers.FromResult("N/A");
}
return requestContent.ContinueWith(
requestResult =>
{
this.log.DebugFormat("{0}, Request = {1}", logEntry, requestResult.Result);
return continuation()
.ContinueWith(t =>
{
Task<string> responseContent;
if (t.IsCompleted && t.Result.Content != null)
{
responseContent = t.Result.Content.ReadAsStringAsync().ContinueWith(responseResult => string.IsNullOrEmpty(responseResult.Result) ? "N/A" : responseResult.Result);
}
else
{
responseContent = TaskHelpers.FromResult("N/A");
}
return responseContent.ContinueWith(
responseResult =>
{
using (logContext)
{
this.log.DebugFormat("{0}, Status Code: {1}, Response = {2}", logEntry, t.Result.StatusCode, responseResult.Result);
}
return t.Result;
});
}).Unwrap();
}).Unwrap();
}
/// <summary>
/// Builds log data about the request.
/// </summary>
/// <param name="actionContext">Data associated with the call</param>
private string BuildLogEntry(HttpActionContext actionContext)
{
string route = actionContext.Request.GetRouteData().Route.RouteTemplate;
string method = actionContext.Request.Method.Method;
string url = actionContext.Request.RequestUri.AbsoluteUri;
string controllerName = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName;
string actionName = actionContext.ActionDescriptor.ActionName;
return string.Format("{0} {1}, route: {2}, controller:{3}, action:{4}", method, url, route, controllerName, actionName);
}
}
Utilizamos log4net, que puede sustituir a la aplicación ILog
con lo que mejor le parezca. ILog.DebugTiming
es solo un método de extensión que usa un cronómetro para obtener el tiempo transcurrido para cada llamada.
Editar: En esta publicación Get the IP address of the remote host tiene detalles sobre cómo obtener la dirección IP de la persona que llama de forma remota.
Cheers, Dean
El enlace que envió obras: GetClientIp cadena privada (HttpRequestMessage petición) \t \t { \t \t \t si (request.Properties.ContainsKey ("MS_HttpContext")) \t \t \t { \t \t \t \t return ((HttpContextWrapper) request.Properties ["MS_HttpContext"]). Request.UserHostAddress; \t \t \t} \t \t \t si (request.Properties.ContainsKey (RemoteEndpointMessageProperty.Name)) \t \t \t { \t \t \t \t RemoteEndpointMessageProperty prop; \t \t \t \t prop = (RemoteEndpointMessageProperty) request.Properties [RemoteEndpointMessageProperty.Name]; \t \t \t \t return prop.Address; \t \t \t} \t \t \t return null; \t } – maxfridbe
Parece que la respuesta de Mike es la mejor opción: donde puede inyectar la implementación de registro real o incluso el marco de trabajo de API incorporado si así lo desea (y así evitar duplicar el esfuerzo). – Lex
Investigamos los bits de 'ITraceWriter', pero parecía muy difícil correlacionar los tiempos de inicio y fin y parece haber una clara falta de implementaciones de fuerza de producción en la comunidad para guiar la escritura propia. Decidimos que el enfoque anterior era más simple. –