Serilog, Change the loglevel at runtime for a specific namespace (> MinimumLevel) - serilog

This is my default Serilog configuration
SeriLogLevelSwitch.MinimumLevel = LogEventLevel.Information;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(SeriLogLevelSwitch)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
.Enrich.FromLogContext()
....
How can i change the loglevel to Debug for a specific namespace at runtime when the default is Information?

Each of your MinimumLevel.Override can have its own LoggingLevelSwitch, which allows you to control the log level for each particular override at run-time.
Create individual LoggingLevelSwitch for each override that you intend to modify whilst the app is running, and store these instances in a place that you can access from other parts of your application, which will allow you to change the MinimumLevel of these LoggingLevelSwitch(es).
e.g.
public class LoggingLevelSwitches
{
// Logging level switch that will be used for the "Microsoft" namespace
public static readonly LoggingLevelSwitch MicrosoftLevelSwitch
= new LoggingLevelSwitch(LogEventLevel.Warning);
// Logging level switch that will be used for the "Microsoft.Hosting.Lifetime" namespace
public static readonly LoggingLevelSwitch MicrosoftHostingLifetimeLevelSwitch
= new LoggingLevelSwitch(LogEventLevel.Information);
}
Configure your Serilog logging pipeline to use these LoggingLevelSwitch instances:
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LoggingLevelSwitches.MicrosoftLevelSwitch)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime",
LoggingLevelSwitches.MicrosoftHostingLifetimeLevelSwitch)
.Enrich.FromLogContext()
.CreateLogger();
// ...
}
Then somewhere in your application, for example, in the code that handles your application configuration that can be changed at run-time, update the LoggingLevelSwitch instance(s) to the new LogEventLevel that you want:
public class AppSettings
{
void ChangeLoggingEventLevel()
{
LoggingLevelSwitches.MicrosoftHostingLifetimeLevelSwitch
.MinimumLevel = LogEventLevel.Error;
LoggingLevelSwitches.MicrosoftHostingLifetimeLevelSwitch
.MinimumLevel = LogEventLevel.Warning;
// ...
}
}
As you can see, the LogEventLevel is controlled by the LoggingLevelSwitch instances, so it's up to you to decide where in your application (and how) these instances will be modified, to affect the logging pipeline.
The example above I'm assuming you have a screen (or API) in your application that a user would be able to configure the logging levels.
If you don't have that, then another approach is to have a background thread that periodically checks a configuration file, an environment variable, or query a database, etc. to determine what these logging levels should be.
If you're using a .NET Core Host, you can use the Options pattern which can handle the refresh of the configuration for you, and allow you to execute code when the configuration changes (where you'd change the MinimumLevel of your LoggingLevelSwitch(es) you have.

You can use an environment variable paired with another MinimumLevel.Override to change the loglevel to Debug for a specific namespace at runtime like so:
using System;
...
SeriLogLevelSwitch.MinimumLevel = LogEventLevel.Information;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(SeriLogLevelSwitch)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
.MinimumLevel.Override(Environment.GetEnvironmentVariable("SPECIFIC_NAMESPACE"), LogEventLevel.Debug)
.Enrich.FromLogContext()
....
Then, ensure the environment variable SPECIFIC_NAMESPACE is accessible by your application at runtime. Note that "namespace" is synonymous with "source context prefix"
Environment.GetEnvironmentVariable

Related

How can I create the logger with an enricher in my baseclass that has to use something in the class?

I was using the following in my static constructor of the base class
static ApplicationBase()
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithThreadId()
.Enrich.FromLogContext()
.MinimumLevel.Debug()
.WriteTo.Async(a => a.RollingFile(
new RenderedCompactJsonFormatter(),
#"c:\logs\log-{Date}.txt", fileSizeLimitBytes: 4194304))
.CreateLogger();
}
Now I need to attach one of our own custom enricher. The enricher is expecting a function that must use a modifier of the actual Application class.
For example, I need to do
.Enrich.WithStoreData(()=>GetStoreData)
Well, it doesn't have to be a function, the bottom line is, the call of GetStoreData is using an object instantiated in the actual child application class (and I cannot change the lifecycle of that object), so I can't access the object from the static constructor.
That means I have to move the logger creation to the normal base constructor. Because it has many children, how can I ensure the logger creation is executed only once? That means I have to apply a lock and check if the logger has been created already. That's really ugly.
And I am not using any container like autofac, so I will not want to create a wrapper of the logger.
At this point, I can only think of the idea creating the logger in the base constructor, and protect it with a lock.
Any other suggestion?
You can only use enrichment while configuring Serilog, and if you are configuring your static logger in your base class, then you cannot change the enrichment later at run time.
But you can use Contextual loggers at run time to add additional properties to your logger: Serilog Context and Correlation
Adding Log Context
// Log.Logger is initialized in your static base
var StudentLogger = Log.Logger.ForContext<Student>();
StudentLogger.Error(/* log message */);
Adding correlation:
// Log.Logger is initialized in your static base
var orderId = "some value";
var corrLog = Log.Logger.ForContext("orderId", orderId)
corrLog.Error(/* log message */);

load properties file depends on spring profiles

I want to use PropertyPlaceholderConfiguration to load different property file depends on spring.profiles.active passed when web application launched. I have different stages divided by two groups. read application.properties when spring profile is 'prod', otherwise read application-dev.properties file.
When I launched non-prod stage, the developmentPropertyPlaceholderConfigurer() called, "Development properties read" print out, I guess application-dev should be loaded. But When I use #Value("${aws.key}") to read the value, it's application.properties' value.
I don't know what's wrong
Forgot mention I use spring-boot.
I did little test, let's say I have two same properties name in both file. aws.key=dev in dev file aws.key=prod in default file. Even if I active dev stage, the aws.key=prod in application.properties was always read in. But, If I remove 'aws.key' in application.properties, then aws.key=dev was read in. I think the appliaction-dev.properties file was read in, then spring boot read application.properties again override the same properties even if I do not want spring boot to read application.property in non prod stage. how to solve it?
#Configuration
public class PropertyPlaceholderConfiguration {
#Bean
#Profile({"test","qa","demo","dev","AWS","localhost"})
public static PropertySourcesPlaceholderConfigurer developmentPropertyPlaceholderConfigurer() {
System.out.println("Development properties read");
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
configurer.setLocation(new ClassPathResource("application-dev.properties"));
return configurer;
}
#Bean
#Profile("prod") // The default
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
System.out.println("Production properties read");
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
configurer.setLocation(new ClassPathResource("application.properties"));
return configurer;
} }

Serilog separate rolling files for each SourceContext

this is my serilog configuration :
Log.Logger = new LoggerConfiguration()
.WriteTo.RollingFile(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
#"Logs\log-{Date}.txt"))
.MinimumLevel.ControlledBy(LogLevelSwitch)
.CreateLogger();
i'm logging like this :
var log = Serilog.Log.ForContext<SomeClassName>();
log.Information("some log text");
My question :
I need a modification in my configuration to create separate logs for each SourceContext.
I need log files somthing like Logs\log-{Date}-{SourceContext}.txt
thanks pros...
One way to implement this is to create your own ILogEventSink implementation (the interface is very simple) that internally dispatches to one of several RollingFileSinks based on the SourceContext property of the LogEvent.
To configure it (let's say you call your sink SourceRollingFileSink) you can use WriteTo.Sink():
Log.Logger = new LoggerConfiguration()
.WriteTo.Sink(new SourceRollingFileSink())
.CreateLogger();
There's no out-of-the-box implementation so some work is required, but it should not be complicated. Be aware that implementations of ILogEventSink need to be thread-safe.

How do I stop TopShelf creating multiple log files using log4net?

One of my TopShelf-hosted Windows services is creating duplicate log files that look like this:
myapp.20140729.log
myapp.20140729.log.20140729.log
A similar problem has been described on StackOverflow before - the solutions in 10639682 didn't work for me, but 579688 suggests it could be caused by initialising the logging system twice.
I'm using the TopShelf Log4Net plugin, but I'm also doing my own log4net logging and need access to the ILog instance before the service has actually started, so my code looks like this:
public static void Main(string[] args) {
XmlConfigurator.Configure();
var log = LogManager.GetLogger(typeof(MyService));
var container = new Container();
RegisterComponents(container, log);
log.InfoFormat("Starting MyService");
RunService(container);
}
public static void RunService(Container container) {
HostFactory.Run(x => {
x.Service<PortalAdaptor>(s => {
s.ConstructUsing(name => container.Resolve<MyService>());
s.WhenStarted(f => f.Start());
s.WhenStopped(f => f.Stop());
});
x.RunAsLocalService();
x.SetDescription("My Service");
x.SetDisplayName("My Service");
x.SetServiceName("MyService");
x.UseLog4Net();
});
}
I've done a little digging, and it appears that calling x.UseLog4Net() in my RunService method is actually running XmlConfigurator.Configure() again - see Log4NetLogWriterFactory, line 62 in the TopShelf code.
So...
Am I correct in thinking that calling XmlConfigurator.Configure() twice could be the cause of my duplicate log file issues?
If so - how can I either inject an existing config into TopShelf, OR get the TopShelf initialization to run before I start my service so I can start logging before the service starts?
Call Hostfactory.New instead of HostFactory.Run to get a reference to the host, do your logging, then call the Run() method on the host:
var host = HostFactory.New(configureService);
var log = LogManager.GetLogger(typeof(MyService));
log.InfoFormat("Starting MyService");
host.Run();
An alternative approache would be that once you have done your initial logging, call LogManager.ResetConfiguration to clear the configuration and allow TopShelf to reload it:
Resets all values contained in the repository instance to their
defaults. This removes all appenders from all loggers, sets the level
of all non-root loggers to null, sets their additivity flag to true
and sets the level of the root logger to Debug. Moreover, message
disabling is set to its default "off" value.

My logging framework is tied to my application forever!

Ok, so I'm looking at NLog. Based on the usage, my application would be tied to the logging framework. How do I overcome this?
Also, when using NLog, I have to write too much monkey-code for every class I'm using this framework on. Is it a good practice to make one static class and access it from anywhere in my application?
example:
//the monkey code
private static Logger logger = LogManager.GetCurrentClassLogger();
//the coupling.
logger.Log(/*...*/);
Create your own logging interface:
public interface IMyOwnLogger {
void Log(string message);
}
Create implementation:
public class NLogLogger : IMyOwnLogger {
void Log(string message) {
StackFrame frame = new StackFrame(1, false);
Logger logger = LogManager.GetLogger(frame.GetMethod().DeclaringType.FullName);
logger.Log(/*...*/);
}
}
Bind IMyOwnLogger to NLogLogger in your IOC container.
Inject where needed (or use IOC.Get<IMyOwnLogger>()).
EDIT:
Idsa made a comment about loosing calling class. Remember you can always use stack trace:
var method = (new StackTrace()).GetFrame(1).GetMethod()
and extract calling class from there.
EDIT:
This is how GetCurrentClassLogger in NLog looks like, so using StackTrace in our class doesn't create additional overhead:
[MethodImpl(MethodImplOptions.NoInlining)]
public static Logger GetCurrentClassLogger()
{
#if SILVERLIGHT
StackFrame frame = new StackTrace().GetFrame(1);
#else
StackFrame frame = new StackFrame(1, false);
#endif
return globalFactory.GetLogger(frame.GetMethod().DeclaringType.FullName);
}
Personally, I avoid tying any logging framework to my code by using
TraceSource to instrument my code. I then use a logging framework (typically Enterprise Library's Logging Application Block) to "listen" to trace output at runtime and do whatever is necessary with that information. (i.e. write to a database, send emails, etc)

Resources