Nancy plugin for application-wide logging using the Serilog logging framework.
Package | Nancy | Version |
---|---|---|
Nancy.Serilog | 2.0+ (dotnet core stable) |
Install it from Nuget:
# Dotnet core
dotnet add package Nancy.Serilog
Enable logging from your Bootstrapper at ApplicationStartup
, this block is a good place to configure the actual logger.
using Nancy.Serilog;
// ...
class CustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
pipelines.EnableSerilog();
// Configure logger to output json-formatted logs to the console
// or use the sink of your choice
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console(new JsonFormatter())
.CreateLogger()
}
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
// Register request based dependency
container.Register((tinyIoc, namedParams) => context.GetContextualLogger());
// other dependencies using ILogger should be registered here as well
container.Register<IThirdPartyService, ThirdPartyService>();
}
}
Now data from your requests will be logged when receiving requests and when returing responses:
public class Index : NancyModule
{
public Index()
{
Get("/", args => "Hello From Home");
}
}
Without doing any extra configuration, navigating to /
(root) will be logged: In the following screenshot I am using a self-hosted Nancy app (the sample of this repo) with some ignored fields.
Notice how the two logs are correlated with the same RequestId
property. This property is attached to requests and responses so that you can find logs coming from a single roundtrip. When you write custom log messages, you want to include this RequestId
property to your custom logs so that these too will be correlated to the same requests and responses. To do that, you want to use a logger that is bound to the request context: Nancy.Serilog provides an extension method called GetContexualLogger()
you can register as the request container level from your Bootstrapper
as in the following snippet.
using Nancy;
using Serilog;
public class Users : NancyModule
{
// have ILogger as a dependency
public Users(ILogger logger)
{
Post("/hello/{user}", args =>
{
var user = (string)args.user;
logger.Information("{User} Logged In", user);
return $"Hello {user}";
};
}
}
public class Bootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
pipelines.EnableSerilog();
StaticConfiguration.DisableErrorTraces = false;
}
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
// Register request based dependency
container.Register((tinyIoc, namedParams) => context.GetContextualLogger());
// other dependencies using ILogger should be registered here as well
container.Register<IThirdPartyService, ThirdPartyService>();
}
}
Then POST
ing some data from Postman will give us the following (using Seq to browse log data), see how the second log message also has RequestId
:
Nancy.Serilog will try to retrieve all the information it can get from requests, responses and errors. However, you can still tell the library what fields to ignore fluently from the logs, it goes like this:
class CustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
pipelines.EnableSerilog(new NancySerilogOptions
{
IgnoredResponseLogFields =
Ignore.FromResponse()
.Field(res => res.RawResponseCookies)
.Field(res => res.ReponseHeaders)
.Field(res => res.ResponseCookies),
IgnoredRequestLogFields =
Ignore.FromRequest()
.Field(req => req.Method)
.Field(req => req.RequestHeaders),
IgnoredErrorLogFields =
Ignore.FromError()
.Field(error => error.ResolvedRouteParameters)
.Field(error => error.StatusCode)
});
}
}