// 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.Collections.Generic; using Microsoft.AspNet.Http; using Microsoft.Extensions.Logging; namespace Microsoft.AspNet.Hosting.Internal { internal static class HostingLoggerExtensions { private const long TicksPerMillisecond = 10000; public static IDisposable RequestScope(this ILogger logger, HttpContext httpContext) { return logger.BeginScopeImpl(new HostingLogScope(httpContext)); } public static void RequestStarting(this ILogger logger, HttpContext httpContext) { if (logger.IsEnabled(LogLevel.Information)) { logger.Log( logLevel: LogLevel.Information, eventId: LoggerEventIds.RequestStarting, state: new HostingRequestStarting(httpContext), exception: null, formatter: HostingRequestStarting.Callback); } } public static void RequestFinished(this ILogger logger, HttpContext httpContext, int startTimeInTicks, int currentTick) { if (logger.IsEnabled(LogLevel.Information)) { var elapsed = new TimeSpan(TicksPerMillisecond * (currentTick < startTimeInTicks ? (int.MaxValue - startTimeInTicks) + (currentTick - int.MinValue) : currentTick - startTimeInTicks)); logger.Log( logLevel: LogLevel.Information, eventId: LoggerEventIds.RequestFinished, state: new HostingRequestFinished(httpContext, elapsed), exception: null, formatter: HostingRequestFinished.Callback); } } public static void ApplicationError(this ILogger logger, Exception exception) { logger.LogError( eventId: LoggerEventIds.ApplicationStartupException, message: "Application startup exception", error: exception); } public static void Starting(this ILogger logger) { if (logger.IsEnabled(LogLevel.Verbose)) { logger.LogVerbose( eventId: LoggerEventIds.Starting, data: "Hosting starting"); } } public static void Started(this ILogger logger) { if (logger.IsEnabled(LogLevel.Verbose)) { logger.LogVerbose( eventId: LoggerEventIds.Started, data: "Hosting started"); } } public static void Shutdown(this ILogger logger) { if (logger.IsEnabled(LogLevel.Verbose)) { logger.LogVerbose( eventId: LoggerEventIds.Shutdown, data: "Hosting shutdown"); } } private class HostingLogScope : ILogValues { private readonly HttpContext _httpContext; private string _cachedToString; private IEnumerable> _cachedGetValues; public HostingLogScope(HttpContext httpContext) { _httpContext = httpContext; } public override string ToString() { if (_cachedToString == null) { _cachedToString = $"RequestId:{_httpContext.TraceIdentifier} RequestPath:{_httpContext.Request.Path}"; } return _cachedToString; } public IEnumerable> GetValues() { if (_cachedGetValues == null) { _cachedGetValues = new[] { new KeyValuePair("RequestId", _httpContext.TraceIdentifier), new KeyValuePair("RequestPath", _httpContext.Request.Path.ToString()), }; } return _cachedGetValues; } } private class HostingRequestStarting : ILogValues { internal static readonly Func Callback = (state, exception) => ((HostingRequestStarting)state).ToString(); private readonly HttpRequest _request; private string _cachedToString; private IEnumerable> _cachedGetValues; public HostingRequestStarting(HttpContext httpContext) { _request = httpContext.Request; } public override string ToString() { if (_cachedToString == null) { _cachedToString = $"Request starting {_request.Protocol} {_request.Method} {_request.Scheme}://{_request.Host}{_request.PathBase}{_request.Path}{_request.QueryString} {_request.ContentType} {_request.ContentLength}"; } return _cachedToString; } public IEnumerable> GetValues() { if (_cachedGetValues == null) { _cachedGetValues = new[] { new KeyValuePair("Protocol", _request.Protocol), new KeyValuePair("Method", _request.Method), new KeyValuePair("ContentType", _request.ContentType), new KeyValuePair("ContentLength", _request.ContentLength), new KeyValuePair("Scheme", _request.Scheme.ToString()), new KeyValuePair("Host", _request.Host.ToString()), new KeyValuePair("PathBase", _request.PathBase.ToString()), new KeyValuePair("Path", _request.Path.ToString()), new KeyValuePair("QueryString", _request.QueryString.ToString()), }; } return _cachedGetValues; } } private class HostingRequestFinished { internal static readonly Func Callback = (state, exception) => ((HostingRequestFinished)state).ToString(); private readonly HttpContext _httpContext; private readonly TimeSpan _elapsed; private IEnumerable> _cachedGetValues; private string _cachedToString; public HostingRequestFinished(HttpContext httpContext, TimeSpan elapsed) { _httpContext = httpContext; _elapsed = elapsed; } public override string ToString() { if (_cachedToString == null) { _cachedToString = $"Request finished in {_elapsed.TotalMilliseconds}ms {_httpContext.Response.StatusCode} {_httpContext.Response.ContentType}"; } return _cachedToString; } public IEnumerable> GetValues() { if (_cachedGetValues == null) { _cachedGetValues = new[] { new KeyValuePair("ElapsedMilliseconds", _elapsed.TotalMilliseconds), new KeyValuePair("StatusCode", _httpContext.Response.StatusCode), new KeyValuePair("ContentType", _httpContext.Response.ContentType), }; } return _cachedGetValues; } } } }