Serilog Level switcher via api endpoint - c#

I am using Serilog for logging in my .net 6 web api and would like to expose an endpoint to enable me to change the global logging level. This will enable me to switch the level via a front end toggle switch. This is what I have implemented so far but it does not seem to have any effect at all. Even just setting the logging level in the startup is not moving the default level off of "Information". I am using the .net DI to configure everything.
This is what I have so far:
Program.cs
var levelSwitcher = new LoggingLevelSwitch(LogEventLevel.Verbose);
builder.Services.AddSingleton<LoggingLevelSwitch>(levelSwitcher);
var seriLogger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(levelSwitcher)
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.CreateLogger();
Then in my endpoint controller
private readonly LoggingLevelSwitch _levelSwitch;
private readonly ILogger<DiagnosticsController> _logger;
public DiagnosticsController(ILogger<DiagnosticsController> logger, LoggingLevelSwitch levelSwitch)
{
_logger = logger;
_levelSwitch = levelSwitch;
}
[HttpGet(Name = "SwitchLoggingLevel")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult SwitchLoggingLevel(string level)
{
if (Enum.TryParse(level, out Serilog.Events.LogEventLevel newLoggingLevel))
{
_levelSwitch.MinimumLevel = newLoggingLevel;
return Ok();
}
return BadRequest();
}
From what I can see in the debugger is that the logger is still on "Information" minimum level and even when call the endpoint and passing in a more or less verbose level nothing is having an effect.
My initial thoughts are that the way I am initializing the switcher in the DI environment is incorrect, but I am not sure on how to do it correctly (ie creating a service and then immediately using it in the subsequent config of the logger). But even if that is incorrect, the actual minimum level is not even being set to "Error" as configured.

Check that you have configured Serilog correctly because the code works as expected.
Assign your variable seriLogger to Log.Logger or
Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(levelSwitcher)
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
builder.Host.UseSerilog();
Note that you need to add WriteTo.Console() or any other sink.

(Posted on behalf of the question author to move it to the answer space).
Whilst #Roman Marusyk didn't exactly solve the issue, he gave enough information to get me across the line. This is what I ended up with in my DI configuration:
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var levelSwitcher = new LoggingLevelSwitch(LogEventLevel.Warning);
builder.Host.UseSerilog((ctx, lc) => lc
.MinimumLevel.ControlledBy(levelSwitcher)
.ReadFrom.Configuration(ctx.Configuration));
// Add services to the container.
builder.Services.TryAddSingleton(levelSwitcher);
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseSerilogRequestLogging();
app.MapControllers();
app.Run();
}
This is still not production ready, but it works as expected now. I have an API endpoint that I can pass through a LogEventLevel as a string and it then gets applied to the Logger System. Here is my appSettings.json just for completeness:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"Serilog": {
"using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"Override": {
"Microsoft.AspNetCore": "Warning"
},
"WriteTo": [
{
"name": "Console"
},
{
"Name": "File",
"Args": {
"buffered": false,
"path": "./logs/data-logs.txt",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [
"WithMachineName",
"FromLogContext",
"WithThreadId",
"WithProcessId",
"WithClientAgent",
"WithClientIp"
]
}
}
And finally my controller that does the switch
public class DiagnosticsController : ControllerBase
{
private readonly LoggingLevelSwitch _levelSwitch;
private readonly ILogger<DiagnosticsController> _logger;
public DiagnosticsController(ILogger<DiagnosticsController> logger, LoggingLevelSwitch levelSwitch)
{
_logger = logger;
_levelSwitch = levelSwitch;
}
[HttpGet(Name = "SwitchLoggingLevel")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult SwitchLoggingLevel(string level)
{
if (Enum.TryParse(level, out Serilog.Events.LogEventLevel newLoggingLevel))
{
_levelSwitch.MinimumLevel = newLoggingLevel;
return Ok();
}
return BadRequest();
}
[HttpGet(Name = "GetCurrentLoggingLevel")]
[ProducesResponseType(StatusCodes.Status200OK)]
public IActionResult CurrentLoggingLevel()
{
return Ok(_levelSwitch.MinimumLevel.ToString());
}
}

Related

Serilog + Azure - No custom logs appearing in Log Analytics workspace

I'm trying to use Serilog to log to the Log Analytics workspace from an app service, but nothing is being logged there from any of my controllers (which are taking ILogger using DI).
Is there something obvious I'm missing? The workspace shows no custom logs, and the calls to the API return everything they are expected to return.
public class Program
{
public static void Main(string[] args)
{
// The initial "bootstrap" logger is able to log errors during start-up. It's completely replaced by the
// logger configured in `UseSerilog()` below, once configuration and dependency-injection have both been
// set up successfully.
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();
Log.Information("Starting up!");
try
{
CreateHostBuilder(args).Build().Run();
Log.Information("Stopped cleanly");
return;
}
catch (Exception ex)
{
Log.Fatal(ex, "An unhandled exception occured during bootstrapping");
return;
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Controllers
services.AddControllers().AddNewtonsoftJson();
// Web
services.AddWebRegistry(Configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
// Write streamlined request completion events, instead of the more verbose ones from the framework.
// To use the default framework request logging instead, remove this line and set the "Microsoft"
// level in appsettings.json to "Information".
app.UseSerilogRequestLogging();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
{
"Serilog": {
"Using": [ "Serilog.Sinks.AzureAnalytics" ],
"MinimumLevel": "Debug",
"Override": {
"System": "Information",
"Microsoft": "Information",
"Microsoft.AspNetCore.Authentication": "Information",
"Microsoft.AspNetCore.SignalR": "Debug",
"Microsoft.AspNetCore.Http.Connections": "Debug"
},
"WriteTo": [
{
"Name": "AzureAnalytics",
"Args": {
"logName": "devlog",
"authenticationId": "secret",
"workspaceId": "secret"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId", "WithThreadName", "WithEventType" ]
},
I think you're missing the package to sink logs from Serilog to Log Analytics:
Add the following packages:
Serilog.AspNetCore
Serilog.Sinks.AzureAnalytics
then:
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.AzureAnalytics(workspaceId: "WORKSPACE ID",
authenticationId: "PRIMARY KEY")
.CreateLogger();
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog();
var logger = new LoggerConfiguration().ReadFrom.Configuration(builder.Configuration).Enrich.FromLogContext()
.CreateLogger();
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(logger);
This is my config file
"Serilog": {
"Using": [ "Serilog.Sinks.File", "Serilog.Sinks.AzureAnalytics" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"SerilogTest": "Debug",
"Microsoft.AspNetCore": "Warning",
"Microsoft": "Warning"
}
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "../logs/webapi-.log",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {CorrelationId} {Level:u3} {Username} {Message:lj}{Exception}{NewLine}"
}
},
{
"Name": "AzureAnalytics",
"Args": {
"logName": "mycustomtablename",
"authenticationId": "xxxxxxx",
"workspaceId": "xxxxxx",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {CorrelationId} {Level:u3} {Username} {Message:lj}{Exception}{NewLine}"
}
}
]
}

How to set telemetryConfiguration for Application Insights for Serilog via AppSettings

For my ASP.NET 5 Web Project I want to log to Application Insights via Serilog. Now I finally got everything working, but I cannot belive this is they way to go. I hope you can help me to improve it.
The main problem I see, is that the Instrumentation Key of Application Insights is both configured in the Serilog and ApplicationInsights section of appsettings.json. I would expect Serilog to use the default/active Client (and configuration), but instead it seems to create a second Client based on its own configuration.
Documentation at https://github.com/serilog/serilog-sinks-applicationinsights says:
As mentioned above you can also pass an instrumentation key but it's actively discouraged
If I remove the instrumentationKey setting from the Serilog section in appsettings.json, it doesn't log any Serilog statements to Application Insights anymore. To solve this issue, I saw you can use .WriteTo.ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Traces), but I don't want hard-coded configuration and prefer to do it via appsettings.json. Also, I am affraid having two Clients/configurations, does not give the best performance.
Now, is there any way to do the configuration via appsettings.json and not have to duplicate the instrumentation key?
Program.cs
public static void Main(string[] args)
{
LoggingExtensions.SetupLoggerConfiguration(AppName, AppVersion);
try
{
Log.Information("Starting web host for {0}", AppName);
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((hostBuilderContext, loggerConfiguration) =>
{
loggerConfiguration.ConfigureBaseLogging(
hostBuilderContext.Configuration, AppName, AppVersion);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
public static string AppName => typeof(Program).Assembly.GetName().Name;
public static Version AppVersion => typeof(Program).Assembly.GetName().Version;
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddApplicationInsightsTelemetry(Configuration["APPINSIGHTS_CONNECTIONSTRING"]);
...
}
LoggingExtensions.cs
internal static void SetupLoggerConfiguration(string appName, Version appVersion)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
.Build();
Log.Logger = new LoggerConfiguration()
.ConfigureBaseLogging(configuration, appName, appVersion)
.CreateLogger();
}
internal static LoggerConfiguration ConfigureBaseLogging(
this LoggerConfiguration loggerConfiguration,
IConfiguration configuration,
string appName,
Version appVersion
)
{
loggerConfiguration
.ReadFrom.Configuration(configuration)
.Enrich.WithProperty("ApplicationVersion", appVersion.ToString())
.Enrich.WithProperty("ApplicationName", appName);
return loggerConfiguration;
}
HomeController.cs
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public ActionResult Index()
{
_logger.LogInformation("Home page visit");
return View();
}
appsettings.json
{
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.ApplicationInsights"
],
"MinimumLevel": {
"Default": "Information"
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console"
}
},
{
"Name": "ApplicationInsights",
"Args": {
"restrictedToMinimumLevel": "Information",
"instrumentationKey": "xxx",
"telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
}
}
],
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithThreadId"
]
},
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=xxx;IngestionEndpoint=https://xxx.in.applicationinsights.azure.com/"
}
}
Update 1
Seems that the ApplicationInsights sink relies on TelemetryConfiguration.Active which is deprecated and probably not set in .NET 5.
https://github.com/serilog/serilog-sinks-applicationinsights/issues/141
For now, I used the solution at https://github.com/serilog/serilog-sinks-applicationinsights/issues/121#issuecomment-798849183, but please if someone has a better solution... tell me!
Update 2
Found a better solution for now at https://github.com/serilog/serilog-sinks-applicationinsights/issues/156
The deprecated TelemetryConfiguration.Active can be used by setting this option in startup.cs:
services.AddApplicationInsightsTelemetry(opt => opt.EnableActiveTelemetryConfigurationSetup = true);
// No need to set InstrumentationKey here as you can do this by setting
// { "ApplicationInsights": { "InstrumentationKey": "xxx" } }
// in appsettings.json
Also add the following on top of the Program.cs as described on https://nblumhardt.com/2020/10/bootstrap-logger/
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();

Binding section from appsettings.json to options

So I was trying to follow the Microsoft Docs, Options pattern in ASP.NET Core however for the life of me I can't seem to correctly get the "AppSettings" section to my AppSettings object as an option. Configuration.GetSection("AppSettings"); is returning null for me yet
appsettings.Development.json
{
"AppSettings": {
"Secret": "DEVELOPMENTTOKENTEST"
},
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Enrich": [ "FromLogContext", "WithMachineName" ],
"Properties": {
"Application": "Oasis.API"
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs/api_.log",
"outputTemplate": "{Timestamp:o} [{Level:u3}] ({SourceContext}) {Message}{NewLine}{Exception}",
"rollingInterval": "Day",
"retainedFileCountLimit": 1
}
}
]
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Data Source=JS-PC;Initial Catalog=Oasis;Integrated Security=True"
}
}
Class for my AppSettings section.
public class AppSettings
{
public string Secret { get; set; }
}
How I am trying to read in the section and configure it for dependency injection for use in a service.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddControllers();
// Configure API secret
var test = Configuration.GetSection("AppSettings"); // null... oh?
services.Configure<AppSettings>(con => Configuration.GetSection("AppSettings").Bind(con));
// Swagger generation
services.AddSwaggerGen();
// Database and Repositories
ConfigureRepositories(services);
// Register Services
ConfigureServiceClasses(services);
}
Here is how I have it setup to take the IOptions in my service.
WebUserService.cs
public class WebUserService : IWebUserService
{
private readonly ILogger<WebUserService> _logger;
private readonly IWebUserRepository _webUserRepository;
private readonly AppSettings _appSettings;
public WebUserService(ILogger<WebUserService> logger, IWebUserRepository webUserRepository, IOptions<AppSettings> appSettings)
{
_logger = logger;
_webUserRepository = webUserRepository;
_appSettings = appSettings.Value;
}
}
You don't need to setup.Its implementation will be injected by the DI system.You can use following to get the Secret:
using Microsoft.Extensions.Configuration;
//....
private readonly IConfiguration _mySettings;
public HomeController(IConfiguration mySettings)
{
_mySettings = mySettings;
}
public IActionResult Index()
{
var result = _mySettings.GetValue<string>("AppSettings:Secret");
//...
}
Test Result:
By the way, if you want to inject it,you can use following code.
//...
var settingsSection =Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(settingsSection);
In controller:
private readonly IConfiguration _mySettings;
private readonly AppSettings _settings;
public HomeController(IOptions<AppSettings> settingsAccessor, IConfiguration mySettings)
{
_mySettings = mySettings;
_settings = settingsAccessor.Value;
}
public IActionResult Index()
{
var vm = new AppSettings
{
Secret= _settings.Secret
};
var result = _mySettings.GetValue<string>("AppSettings:Secret");
}
Test result:
Refer to this thread.

How to get Serilog to use custom enricher from json config file

I wish to use formatted UTC timestamps in my Serilog output.
I have written a custom enricher that works fine when called from C# code.
public class UtcTimestampEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory pf)
{
logEvent.AddPropertyIfAbsent(pf.CreateProperty("UtcTimestamp", logEvent.Timestamp.UtcDateTime));
}
}
....
var loggerConfig = new LoggerConfiguration().MinimumLevel.Debug()
.Enrich.FromLogContext()
.Enrich.With(new UtcTimestampEnricher())
.Filter
.ByIncludingOnly( expr) // need this .Filter to ensure that
// Serilog.Filters.Expressions.dll gets loaded, else filters in config file get ignored
.WriteTo.Console(
outputTemplate: "[{UtcTimestamp:HH:mm:ss.fff} {Level:u3} {Subsystem}] {Message:lj}{NewLine}{Exception}",
restrictedToMinimumLevel: LogEventLevel.Information);
Log.Logger = loggerConfig.CreateLogger();
Now I wish to use the utcTimestamp enricher while configuring the logger from my custom json config file.
var jsonLogconfiguration = new ConfigurationBuilder()
.AddJsonFile(logconfigFname)
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(jsonLogconfiguration)
.CreateLogger();
My json config file
{
"Serilog": {
"Using": [
"Serilog.Sinks.Console"
],
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "{UtcTimestamp:yyyy,MM,dd,HH,mm,ss,fff },{Level:u3},{Subsystem},{Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext" , "UtcTimestampEnricher"],
"Filter": [
{
"Name": "ByIncludingOnly",
"Args": {
"expression": "Subsystem = 'Config' or Subsystem = 'Admin' "
}
}
]
}
}
The message I get on the console: ( I have previously called serilog selflog to get serilog debug messages about itself)
Serilog.Debugging.SelfLog.Enable(msg => Console.WriteLine(msg));
Serilog debug output.
2020-06-05T09:34:01.3898891Z Unable to find a method called UtcTimestampEnricher. Candidate methods are:
Serilog.LoggerConfiguration When(Serilog.Configuration.LoggerEnrichmentConfiguration, System.String, System.Action`1[Serilog.Configuration.LoggerEnrichmentConfiguration])
Serilog.LoggerConfiguration With(Serilog.Configuration.LoggerEnrichmentConfiguration, Serilog.Core.ILogEventEnricher)
Serilog.LoggerConfiguration FromLogContext(Serilog.Configuration.LoggerEnrichmentConfiguration)
I got similar output when I tried to use
"Enrich": [ "FromLogContext" , "UtcTimestamp"],
Even though the problem has been already solved I want to share my 2 cents regarding this problem.
Whenever you create a custom Enricher you have two options how you can register that:
Either via code
or via configuration
Via Code
Enricher
public class UtcTimestampEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory pf)
{
...
}
}
Registration
Log.Logger = new LoggerConfiguration()
.Enrich.With<UtcTimestampEnricher>()
.CreateLogger()
Via Configuration
Enricher
public class UtcTimestampEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory pf)
{
...
}
}
Registration helper
public static class LoggingExtensions
{
public static LoggerConfiguration WithUtcTimestamp(
this LoggerEnrichmentConfiguration enrich)
{
if (enrich == null)
throw new ArgumentNullException(nameof(enrich));
return enrich.With<UtcTimestampEnricher>();
}
}
Registration
{
"Serilog": {
...,
"Using": [ "Your.Assembly.Name" ],
"Enrich": [ "FromLogContext", "WithUtcTimestamp" ]
},
So, as you can see what you need to register is the registration helper method (not the enricher itself) in case of configuration based setup.
I finally figured this out, and could get the custom enricher to work by specifying it in C# code
var jsonLogconfiguration = new ConfigurationBuilder()
.AddJsonFile(logconfigFname)
.Build();
Log.Logger = new LoggerConfiguration()
.Enrich.With(new UtcTimestampEnricher()) // is necessary
.ReadFrom.Configuration(jsonLogconfiguration)
.CreateLogger()
and removing the enrich with from the config file
"Enrich": [ "FromLogContext" ],

Avoid logging twice using netcore2.0 on AWS Lambda with Serilog

After upgrading my netcore project to 2.0, i see double logs when my application is running on AWS Lambda, which utilizes the Serilog framework. Please see my setup below:
public void ConfigureServices(IServiceCollection services)
{
...
// Setup logging
Serilog.Debugging.SelfLog.Enable(Console.Error);
var logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.FromLogContext();
if (GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "").Equals("local"))
logger.WriteTo.Console();
else
logger.WriteTo.Console(new JsonFormatter());
Log.Logger = logger.CreateLogger();
...
}
and
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddSerilog();
...
}
Any ideas as to why this suddenly might be the case? Lambda grabs any console log statements and inserts into cloudwatch, but now twice and in two different formats. Eg.
[Information] PodioDataCollector.Controllers.CustomerController:
Validating API Key
and
{
"Timestamp": "2018-02-14T08:12:54.7921006+00:00",
"Level": "Information",
"MessageTemplate": "Validating API Key",
"Properties": {
"SourceContext": "...",
"ActionId": "...",
"ActionName": "...",
"RequestId": "...",
"RequestPath": "...",
"CorrelationId": "..."
} }
I only expected the log message formatted in json. This started when i upgraded to netcore2.0
Thanks in advance
If you are using Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction as the base class for your lambda entrypoint, it includes a default logger in the logging providers before calling your override of the Init method. I believe you could use an Init method such as the following to avoid the dual-logging.
public class LambdaEntryPoint : Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction
{
public LambdaEntryPoint()
{
var configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", true)
.AddEnvironmentVariables()
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
}
protected override void Init(IWebHostBuilder builder)
{
builder.ConfigureLogging(loggingBuilder =>
{
loggingBuilder.ClearProviders();
loggingBuilder.AddSerilog();
})
.UseStartup<Startup>();
}
}
The logging implementation, and Serilog provider, have both changed a little in Core 2.0.
You will need: https://github.com/serilog/serilog-aspnetcore instead of Serilog.Extensions.Logging (setup instructions are on the README).

Categories

Resources