I'm using Serilog to log Errors in my WebApi with the following configuration.
private static readonly string LogFileName = System.Web.Hosting.HostingEnvironment.MapPath(#"~/App_Data/Logs/Service.log");
private static void ConfigureLogging(ContainerBuilder builder)
{
var loggerConfig = new LoggerConfiguration()
.ReadFrom.AppSettings()
.WriteTo.Console()
.WriteTo.File(LogFileName != null ? LogFileName : "C:/Temp/Logs/Service.log",
shared: true,
rollingInterval: RollingInterval.Day);
// configure global logger for logging in owin middleware
Log.Logger = loggerConfig.CreateLogger();
builder.Register(_ => Log.Logger).As<ILogger>(); }`
With Appsettings
<appSettings>
<add key="serilog:minimum-level" value="Warning" />
</appSettings>
The problem is, it only logs once. The Service is still alive, I can send more Successful requests.
And I also see the requests/errors logged in console. But it doesnt add up in logfile.
BUT if I edit the WebConfig - change from Warning to Verbose then back to Warning, so no real changes - it logs again.
For Debugging and diagnostic use this selflog which might show you the extact error or problem your application is facing. Check this here https://github.com/serilog/serilog/wiki/Debugging-and-Diagnostics
Related
I'm using Serilog with Azure Functions v4 and .NET 7. I am trying to write logs to both the local console and Application Insights. I already spent hours trying different solutions and the situation at the time of writing seems to be that this technology stack (Azure Functions + Application Insights) is not mature or too recent, and not yet very well documented.
Setting up appsettings.{env}.json files by environment and trying to add a different Serilog config by environment does not work with Azure Functions. Depending on the solution I tried, either Microsoft logger is used or there are no logs.
The code below is working for the console but I get duplicate logs on Application Insights. And if I remove Console sink, it works well in Application Insights but I get no more local logs:
.UseSerilog((hostBuilderContext, serviceProvider, loggerConfiguration) =>
loggerConfiguration
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.ApplicationInsights(
serviceProvider.GetRequiredService<TelemetryConfiguration>(),
TelemetryConverter.Traces)
)
I also tried other ways of setting up the logger, for example in .ConfigureServices(..., injecting it differently in the function code, but no luck. Many samples talk about a Startup method that I don't have. If it helps I can post the whole Program.cs and the function code here.
So I now added .Enrich.WithEnvironmentName() to get the environment name from an environment variable, and the question is: how can I filter the logs using ByIncludingOnly in the code above (not config file) to include logs depending on the environment name (Local or Development)? Same question for ByExcluding - I guess answer will be similar.
This is the way to do it. I ended up not using the Environment enricher because it is at the time of writing not supporting the Azure environment variable for Azure Functions (AZURE_FUNCTIONS_ENVIRONMENT). See this issue:
https://github.com/serilog/serilog-enrichers-environment/issues/49
This is how I got this working:
First option enriching the log event:
// Careful: Serilog is not able to enrich with the value of this variable and will default to 'Production'
var value = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") ?? "Development";
var predicate = $"EnvironmentName = 'Local'";
Log.Logger = new LoggerConfiguration()
.Enrich.WithProperty("EnvironmentName", value)
.MinimumLevel.Debug()
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(predicate)
.WriteTo.Console(outputTemplate: "{Properties} {Message}"))
.WriteTo.Logger(lc => lc
.WriteTo.File(path: Directory.GetCurrentDirectory() + #"\test.txt"))
.CreateLogger();
Second option without enriching the log event:
var value = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") ?? "Development";
var predicate = $"{value} = 'Local'";
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(predicate)
.WriteTo.Console(outputTemplate: "{Properties} {Message}"))
.WriteTo.Logger(lc => lc
.WriteTo.File(path: Directory.GetCurrentDirectory() + #"\test.txt"))
.CreateLogger();
NLog is configured to write logs to this location:
<variable name="logDirectory" value="${aspnet-appbasepath}\Data\Logs" />
I have the follow code setting up my ASP app:
public static void Main(string[] args)
{
var logger1 = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger1.Info("Hello World");
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
...
// logging
builder.Logging.ClearProviders();
builder.Host.UseNLog();
...
WebApplication app = builder.Build();
}
logger1 is writing files to C:\Foo\Bar\bin\Debug\net6.0\Data\Logs
If I inject an ILogger<T> somewhere else in my app the logs are instead written to C:\Foo\Bar\Data\Logs.
How can I make Logger1 write to C:\Foo\Bar\Data\Logs instead of C:\Foo\Bar\bin\Debug\net6.0\Data\Logs.
I'm aware can create a logger that writes where I wish via app.Services.GetRequiredService<ILoggerFactory>() however I need a logger before app exists.
logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger(); also writes to \bin.
NLog.Web.AspNetCore v5.2 has been released, that now prioritize current-directory for ${aspnet-appbasepath}.
https://www.nuget.org/packages/NLog.Web.AspNetCore
I have a web api (.NET Core 3.1) which is using Serilog for logging. Serilog is added to the IWebHostBuilder quite early:
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return WebHost
.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog((context, configuration) =>
{
if (context.HostingEnvironment.IsDevelopment())
{
configuration.WriteTo.Console(LogEventLevel.Debug);
return;
}
configuration.WriteTo.ApplicationInsights(TelemetryConverter.Traces, LogEventLevel.Error);
});
}
This means (afaik) that I need to have already configured the logger at this point. So this is the very first thing I do in the main:
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.CreateLogger();
var host = CreateWebHostBuilder(args).Build();
host.Run();
}
But the line .ReadFrom.Configuration(Configuration) requires the configuration to be set up. This is usually done in the StartUp (again, afaik) which has not yet been run at this time. Obviously I could move my LoggerConfiguration to later, but the .UseSerilog would be called before it was configured.
So how do I configure Serilog with IConfugration, when I haven't set it up yet?
#RubenBartelink pointed to a very good ressource in comment.
This is also described in the Serilog for ASP.NET Core documentation.
In particular the two-stage initialization part, which states:
Two-stage initialization
The example at the top of this page shows how to configure Serilog immediately when the application starts.
This has the benefit of catching and reporting exceptions thrown during set-up of the ASP.NET Core host.
The downside of initializing Serilog first is that services from the ASP.NET Core host, including the appsettings.json configuration and dependency injection, aren't available yet.
To address this, Serilog supports two-stage initialization. An initial "bootstrap" logger is configured immediately when the program starts, and this is replaced by the fully-configured logger once the host has loaded.
To use this technique, first replace the initial CreateLogger() call with CreateBootstrapLogger():
using Serilog;
using Serilog.Events;
public class Program
{
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateBootstrapLogger(); // <-- Change this line!
Then, pass a callback to UseSerilog() that creates the final logger:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
It's important to note that the final logger completely replaces the bootstrap logger: if you want both to log to the console, for instance, you'll need to specify WriteTo.Console() in both places, as the example shows.
Consuming appsettings.json configuration
Using two-stage initialization, insert the ReadFrom.Configuration(context.Configuration) call shown in the example above. The JSON configuration syntax is documented in the Serilog.Settings.Configuration README.
I have a .net 5 web application that uses Application Insights. I try to log into AI trace by using ILogger<>. However: When analyzing the "traces" - Content in AI on Azure the logs are not shown.
Part of StartUp:
services.AddLogging(loggingbuilder =>
{
loggingbuilder.AddFilter<ApplicationInsightsLoggerProvider>("", LogLevel.Trace);
loggingbuilder.AddApplicationInsights();
});
services.AddApplicationInsightsTelemetry();
The constructor of the class that should do the logging injects ILogger and AppInsights via dependency injection:
public ImportService(ILogger<ImportService> log, TelemetryClient telemetryClient)
{
_log = log;
_telemetryClient = telemetryClient;
}
Inside the method I have the following two logging attempts:
public async Task<Customer> UpdateCustomerByEmail(string email)
{
_telemetryClient.TrackTrace("From Telemetry");
_log.LogWarning("From Log");
[...]
}
While the first one ("from Telemetry") ends up correctly in AI-traces, the second one ("From Log") never shows up there.
The instrumentationkey is stored in the appsettings (and obviously correct because the telemetryClient-Track is working)
Might this documentation be relevant for you? Adding the following code to program.cs worked for me:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddApplicationInsights("<instrumentationKeyHere>");
logging.AddFilter<Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider>("", LogLevel.Information);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
The nuget package Microsoft.Extensions.Logging.ApplicationInsights must also be installed.
In your case it might be sufficient to simply provide the Instrumentation Key as a paramter to the AddApplicationInsights function.
As stated in the documentation "This code is required only when you use a standalone logging provider. For regular Application Insights monitoring, the instrumentation key is loaded automatically from the configuration path ApplicationInsights: Instrumentationkey."
This might explain why regular monitoring works, but not for logging.
I'm trying to set up a simple logging configuration for my Windows service using Topshelf and Serilog (the Serilog.Extras.Topshelf package respectively).
HostLogger.UseLogger(new SerilogHostLoggerConfigurator(new LoggerConfiguration()
.WriteTo.RollingFile(AppDomain.CurrentDomain.BaseDirectory + "\\logs\\app-{Date}.log")
.WriteTo.ColoredConsole()
.MinimumLevel.Debug()
.CreateLogger()));
HostFactory.Run(x => {
x.UseSerilog();
...
The service runs fine, however no output is made, neither to the console nor the specified log file (I can see that one is being created but it remains empty). Has anyone experience using both frameworks?
The second call "x.UseSerilog()" resets TopShelf's HostLogger to use Serilog's global instance (Log.Logger), which you have not configured.
Remove the second call and the logging should start working.
Another option is to configure the global logger:
Log.Logger = new LoggerConfiguration()
.WriteTo.RollingFile(AppDomain.CurrentDomain.BaseDirectory + "\\logs\\app-{Date}.log")
.WriteTo.ColoredConsole()
.MinimumLevel.Debug()
.CreateLogger();
HostFactory.Run(x => {
// configure TopShelf to use Serilog's global instance.
x.UseSerilog();
}