Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"sdk": {
"version": "2.2.105"
}
}
}
4 changes: 3 additions & 1 deletion samples/InlineInitializationSample/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using Microsoft.AspNetCore.Builder;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Events;

namespace InlineInitializationSample
{
Expand Down
19 changes: 10 additions & 9 deletions src/Serilog.AspNetCore/AspNetCore/RequestLoggingMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Serilog.Events;
using Serilog.Extensions.Hosting;
using Serilog.Parsing;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace Serilog.AspNetCore
{
class RequestLoggingMiddleware
internal class RequestLoggingMiddleware
{
readonly RequestDelegate _next;
readonly DiagnosticContext _diagnosticContext;
readonly MessageTemplate _messageTemplate;

readonly Func<HttpContext, LogEventLevel> _getLogLevel;
static readonly LogEventProperty[] NoProperties = new LogEventProperty[0];

public RequestLoggingMiddleware(RequestDelegate next, DiagnosticContext diagnosticContext, RequestLoggingOptions options)
Expand All @@ -38,6 +38,7 @@ public RequestLoggingMiddleware(RequestDelegate next, DiagnosticContext diagnost
_next = next ?? throw new ArgumentNullException(nameof(next));
_diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext));

_getLogLevel = options.GetLogLevel;
_messageTemplate = new MessageTemplateParser().Parse(options.MessageTemplate);
}

Expand Down Expand Up @@ -68,13 +69,13 @@ public async Task Invoke(HttpContext httpContext)
}
}

bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector, int statusCode, double elapsedMs, Exception ex)
private bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector collector, int statusCode, double elapsedMs, Exception ex)
{
var logger = Log.ForContext<RequestLoggingMiddleware>();
var level = statusCode > 499 ? LogEventLevel.Error : LogEventLevel.Information;
var level = _getLogLevel(httpContext);

if (!logger.IsEnabled(level)) return false;

if (!collector.TryComplete(out var collectedProperties))
collectedProperties = NoProperties;

Expand Down
48 changes: 48 additions & 0 deletions src/Serilog.AspNetCore/AspNetCore/RequestLoggingOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2019 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Microsoft.AspNetCore.Http;
using Serilog.Events;
using System;

namespace Serilog.AspNetCore
{
/// <summary>
/// Contains options for the <see cref="Serilog.AspNetCore.RequestLoggingMiddleware"/>.
/// </summary>
public class RequestLoggingOptions
{
/// <summary>
/// Gets or sets the message template. The default value is
/// <c>"HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms"</c>. The
/// template can contain any of the placeholders from the default template, names of properties
/// added by ASP.NET Core, and names of properties added to the <see cref="IDiagnosticContext"/>.
/// </summary>
/// <value>
/// The message template.
/// </value>
public string MessageTemplate { get; set; }

/// <summary>
/// Gets or sets the function returning the <see cref="LogEventLevel"/> based on the current <see cref="HttpContext"/>.
/// The default behavior returns LogEventLevel.Error when HttpStatusCode is greater than 499
/// </summary>
/// <value>
/// The function returning the <see cref="LogEventLevel"/>.
/// </value>
public Func<HttpContext, LogEventLevel> GetLogLevel { get; set; }

internal RequestLoggingOptions() { }
}
}
28 changes: 0 additions & 28 deletions src/Serilog.AspNetCore/RequestLoggingOptions.cs

This file was deleted.

42 changes: 38 additions & 4 deletions src/Serilog.AspNetCore/SerilogApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
// limitations under the License.

using System;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Serilog.AspNetCore;
using Serilog.Events;

namespace Serilog
{
Expand All @@ -26,6 +30,9 @@ public static class SerilogApplicationBuilderExtensions
const string DefaultRequestCompletionMessageTemplate =
"HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms";

static Func<HttpContext, LogEventLevel> DefaultGetLogLevel =
ctx => ctx.Response.StatusCode > 499 ? LogEventLevel.Error : LogEventLevel.Information;

/// <summary>
/// Adds middleware for streamlined request logging. Instead of writing HTTP request information
/// like method, path, timing, status code and exception details
Expand All @@ -43,11 +50,38 @@ public static class SerilogApplicationBuilderExtensions
/// <returns>The application builder.</returns>
public static IApplicationBuilder UseSerilogRequestLogging(
this IApplicationBuilder app,
string messageTemplate = DefaultRequestCompletionMessageTemplate)
string messageTemplate)
=> app.UseSerilogRequestLogging(opts => opts.MessageTemplate = messageTemplate);

/// <summary>
/// Adds middleware for streamlined request logging. Instead of writing HTTP request information
/// like method, path, timing, status code and exception details
/// in several events, this middleware collects information during the request (including from
/// <see cref="IDiagnosticContext"/>), and writes a single event at request completion. Add this
/// in <c>Startup.cs</c> before any handlers whose activities should be logged.
/// </summary>
/// <param name="app">The application builder.</param>
/// <param name="configureOptions">A <see cref="System.Action{T}" /> to configure the provided <see cref="RequestLoggingOptions" />.</param>
/// <returns>The application builder.</returns>
public static IApplicationBuilder UseSerilogRequestLogging(
this IApplicationBuilder app,
Action<RequestLoggingOptions> configureOptions = null)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate));
return app.UseMiddleware<RequestLoggingMiddleware>(new RequestLoggingOptions(messageTemplate));

var opts = new RequestLoggingOptions
{
GetLogLevel = DefaultGetLogLevel,
MessageTemplate = DefaultRequestCompletionMessageTemplate
};
configureOptions?.Invoke(opts);

if (opts.MessageTemplate == null)
throw new ArgumentException($"{nameof(opts.MessageTemplate)} cannot be null.");
if (opts.GetLogLevel == null)
throw new ArgumentException($"{nameof(opts.GetLogLevel)} cannot be null.");

return app.UseMiddleware<RequestLoggingMiddleware>(opts);
}
}
}
}