I want to set the minimum log level programatically at runtime on a set of loggers. I am using Microsoft.Extensions.Logging and as well as Serilog (with Serilog.Extensions.Logging).
It seems that Serilog does not respect the log level set on the ILoggingBuilder:
var level = LogLevel.Debug;
Log.Logger = new LoggerConfiguration().WriteTo
.Console(outputTemplate: "[Serilog {SourceContext} {Level:w4}] {Message:lj}{Exception}{NewLine}")
.CreateLogger();
var loggerFactory = LoggerFactory.Create(builder =>
builder.SetMinimumLevel(level)
.AddSerilog()
.AddSimpleConsole(options => { options.SingleLine = true;})
);
var logger = loggerFactory.CreateLogger("LoggingTest");
logger.LogInformation("Info Message");
logger.LogDebug("Debug Message");
Produces:
[Serilog LoggingTest info] Info Message
info: LoggingTest[0] Info Message
dbug: LoggingTest[0] Debug Message
I could sync these by using .MinimumLevel.ControlledBy(levelSwitch) on Log.Logger a long with a mapping from Mircosoft to Serilog Log Levels, but I would then need to change both where ever one is set which isn't ideal.
Is there a way to get Serilog to respect the minimum log level set on the builder?
Related
I have two projects A and B, both of them use a NLog libiary. Now I have an issue:
if A writes loginfo into the log file first, then the B never logs. And if B writes loginfo into the log file first, then A never logs.
As A and B use a same NLog libiary, so they use the same Nlog Config, but they will be built in two processors, here is the config info.
Does somebody have any good idea on this issue?
//Set NLog Config by:
//https://github.com/nlog/NLog/wiki/Configuration-API
private static Logger GenerateLogInstance()
{
// Step 1. Create configuration object
var config = new LoggingConfiguration();
// Step 2. Create targets
var fileTarget = new FileTarget()
{
FileName = #"C:\Logs\${shortdate}.log",
Layout = #"${longdate} ${uppercase:${level}} ${message}${onexception:${newline}EXCEPTION\: ${exception:format=ToString}}"
};
//var wrapper = new AsyncTargetWrapper(fileTarget, 5000, AsyncTargetWrapperOverflowAction.Discard);
// Step 3. Define rules
config.AddTarget("myprojectLog", fileTarget);
config.LoggingRules.Add(new NLog.Config.LoggingRule("*", NLog.LogLevel.Trace, fileTarget));
// Step 4. Activate the configuration
var factory = new LogFactory(config);
return factory.GetLogger("myprojectLog");
}
I don't use nlog but take a look at the following. You may need to set concurrentWrites="true"
File target
concurrentWrites - Enables support for optimized concurrent writes to
same log file from multiple processes on the same machine-host, when
using keepFileOpen = true. By using a special technique that lets it
keep the files open from multiple processes. If only single process
(and single AppDomain) application is logging, then it is faster to
set to concurrentWrites = False. Boolean Default: True. Note: in UWP
this setting should be false
Could you try this instead:
private static LogFactory GenerateLogFactory()
{
// Step 0. Create isolated LogFactory
var logFactory = new LogFactory();
// Step 1. Create configuration object
var config = new LoggingConfiguration(logFactory);
// Step 2. Create targets
var fileTarget = new FileTarget()
{
FileName = #"C:\Logs\${shortdate}.log",
Layout = #"${longdate} ${uppercase:${level}} ${message}${onexception:${newline}EXCEPTION\: ${exception:format=ToString}}"
};
// Step 3. Define rules
config.AddTarget("myprojectLog", fileTarget);
config.LoggingRules.Add(new NLog.Config.LoggingRule("*", NLog.LogLevel.Trace, fileTarget));
// Step 4. Activate the configuration
logFactory.Configuration = config;
return logFactory;
}
private static Logger GenerateLogInstance()
{
return GenerateLogFactory().GetLogger("myprojectLog");
}
Btw. if two projects in the same solution is using this same method, then you can consider doing this:
Lazy<LogFactory> LazyLogFactory = new Lazy<LogFactory>(() => GenerateLogFactory());
private static Logger GenerateLogInstance(string loggerName = "myprojectLog")
{
return LazyLogFactory.Value.GetLogger(loggerName);
}
I'm writing a WEB API call for NLog, so remote apps can log to my logging table.
In my controller I have (hard-coded for now as a sanity check):
NLogger.LogError("Some Error Message", "An exception", 5, "A computer name");
Then my static LogError method looks like this (I tried LogEventInfo() too):
public static void LogError(string msg, string ex, int appid, string machineName)
{
LogEventInfo logEvent = new LogEventInfo(LogLevel.Error, "Api Logger", "Another test msg");
logEvent.Properties["myMsg"] = msg;
logEvent.Properties["myEx"] = ex;
logEvent.Properties["myAppId"] = appid;
logEvent.Properties["myMachineName"] = machineName;
NLogManager.Instance.Log(logEvent);
}
Lastly, this is my code first config for that rule (there's 2 others with different db targets):
private static void ConfigureApiLog()
{
var dbApiErrorTarget = new DatabaseTarget
{
ConnectionString = ConnectionFactory.GetSqlConnection().ConnectionString,
CommandText = "usp_LogError",
CommandType = CommandType.StoredProcedure
};
dbApiErrorTarget.Parameters.Add(new DatabaseParameterInfo("#level",
new global::NLog.Layouts.SimpleLayout("${level}")));
dbApiErrorTarget.Parameters.Add(new DatabaseParameterInfo("#logger",
new global::NLog.Layouts.SimpleLayout("${logger}")));
dbApiErrorTarget.Parameters.Add(new DatabaseParameterInfo("#message",
new global::NLog.Layouts.SimpleLayout("${event-properties:item=myMsg}")));
dbApiErrorTarget.Parameters.Add(new DatabaseParameterInfo("#exception",
new global::NLog.Layouts.SimpleLayout("${event-properties:item=myEx}")));
dbApiErrorTarget.Parameters.Add(new DatabaseParameterInfo("#AppId",
new global::NLog.Layouts.SimpleLayout("${event-properties:item=myAppId}")));
dbApiErrorTarget.Parameters.Add(new DatabaseParameterInfo("#MachineName",
new global::NLog.Layouts.SimpleLayout("${event-properties:item=myMachineName}")));
Config.AddTarget("database", dbApiErrorTarget);
Config.LoggingRules.Add(new LoggingRule("*", LogLevel.Error, LogLevel.Fatal, dbApiErrorTarget));
}
I expect 1 log per log call to the logger instance, but I'm getting two and I'm not exactly sure why:
Id Date Level Logger Message Exception AppId MachineName
1 2017-03-03 22:43:20.557 Error Api Logger Another test msg 0 mylocalmachine
2 2017-03-03 22:43:20.603 Error Api Logger Some Error Message An exception 5 A computer name
AppId 0 is my Api, 5 is some remote app, hard coded at this point as POC.
Might be that it's Friday, but I can't seem to figure out what's wrong with the code. Any help would be appreciated!
I had this problem and found the problem, for my specific case. Problem was double entries everywhere on every target.
I had a .NET Core web application which used the UseNLog() inside the Program.cs IWebHostBuilder. This performs AddNLog() internally.
Then in the startup I manually did loggerInstance.AddNLog() which caused the dual insertion.
The latter must be removed since UseNLog is a far better and "sooner" time to enable NLog
Hope it helps!
Rules in NLog can be target specific, or it can write to all targets. The default rule(s) won't have any targets specified, so they will write to any you create. You're also adding your own rule, which writes specifically to your target and no others.
Thus, the double-logging.
You can remove the default rules to resolve the issue.
Config.LoggingRules.Clear();
before you add your rule.
In the latest Microsoft.Azure.WebJobs.ServiceBus package, it gives you the ability to receive batches of messages from eventhubs. I would like to set how many messages I want to receive in a batch.
The core ServiceBus library allows you to overload the Receive() function and provide the batch size.
How does one do this in the initial config of an EventHubs receiver, or is something else required?
You can do this in Functions via the eventHub configuration block in host.json as described here. E.g.:
{
"eventHub": {
"maxBatchSize": 500,
"prefetchCount": 100
}
}
We apply those configuration settings to the EventProcessorOptions when we create the EventProcessorHost (see here).
Steph,
The MaxBatchSize can be configured through EventProcessorOptions, you can pass it as a parameter when creating a new EventHubConfiguration.
var options = EventProcessorOptions.DefaultOptions;
options.MaxBatchSize = 50;
var eventHubConfig = new EventHubConfiguration(options);
string eventHubName = "MyHubName";
eventHubConfig.AddSender(eventHubName, "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=SendRule;SharedAccessKey=xxxxxxxx");
eventHubConfig.AddReceiver(eventHubName, "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=ReceiveRule;SharedAccessKey=yyyyyyy");
config.UseEventHub(eventHubConfig);
JobHost host = new JobHost(config);
As you can notice in the source code of EventHubConfiguration.cs if no EventProcessorOptions is specified, the MaxBatchSize is set to 1000 instead of 10 by default.
public EventHubConfiguration(
EventProcessorOptions options,
PartitionManagerOptions partitionOptions = null)
{
if (options == null)
{
options = EventProcessorOptions.DefaultOptions;
options.MaxBatchSize = 1000;
}
_partitionOptions = partitionOptions;
_options = options;
}
Based on the answer here: How Thread-Safe is NLog? I have created a Logger and added two MappedDiagnosticsContext to NLog:
NLog.MappedDiagnosticsContext.Set("Module", string.Format("{0}.{1}", module.ComputerName, module.ModuleType));
NLog.MappedDiagnosticsContext.Set("ModuleCoreLogLevel", string.Format("LogLevel.{0}", module.CoreLogLevel));
I can successfully use the "Module" Context in the NLog config (programmatically) to generate the file name the logger should log to:
${{when:when=length('${{mdc:Module}}') == 0:inner=UNSPECIFIED}}${{when:when=length('${{mdc:Module}}') > 0:inner=${{mdc:Module}}}}.txt
This logs e.g. all messages with a "Module" context of "Test" to a filename called Module.txt
I now want to be able to set the LogLevel for the different Modules using this way, and only Log messages which correspond to this LogLevel (or higher)
I am trying to do this through filters, this means, on the LoggingRule I am trying to add a filter:
rule92.Filters.Add(new ConditionBasedFilter { Condition = "(level < '${mdc:ModuleCoreLogLevel}')", Action = FilterResult.IgnoreFinal });
This however does not seem to filter messages.
If I have for example a message which is emitted using Logger.Trace(), and the LogLevel on "ModuleCoreLogLevel" is set to LogLevel.Debug, I can still the Message in the resulting LogFile.
I solved this by doing this for each level:
rule92.Filters.Add(new ConditionBasedFilter { Condition = "(equals('${mdc:ModuleCoreLogLevel}', 'LogLevel.Trace') and level < LogLevel.Trace)", Action = FilterResult.IgnoreFinal });
I need to change the layout of my log file when writing log entries from an aspect. I will likely need to change other settings as well in time.
I have created two FileTargets that create the layout as I require.
LoggingConfiguration config = new LoggingConfiguration();
LoggingConfiguration aspectConfig = new LoggingConfiguration();
FileTarget fileTarget = new FileTarget();
fileTarget.Name = "fileTarget";
fileTarget.Layout = "${longdate} ${machineName} ${callsite} ${message} ${exception:format=tostring}";
fileTarget.FileName = String.Format("{0}Admin.log", Config.DatabasePath);
fileTarget.KeepFileOpen = false;
FileTarget aspectTarget = new FileTarget();
aspectTarget.Name = "aspectTarget";
aspectTarget.Layout = "${longdate} ${machineName} ${message} ${exception:format=tostring}";
aspectTarget.FileName = String.Format("{0}Admin.log", Config.DatabasePath);
aspectTarget.KeepFileOpen = false;
LoggingRule rule2 = new LoggingRule("*", LogLevel.Trace, fileTarget);
config.LoggingRules.Add(rule2);
LoggingRule aspectRule = new LoggingRule("aspects", LogLevel.Trace, aspectTarget);
aspectConfig.LoggingRules.Add(aspectRule);
LogManager.Configuration = config;
There may be other changes/differences to come but that is besides the point at this stage.
Using the following works fine for the 'default or '*' config ie by using :
var log = LogManager.GetCurrentClassLogger();
log.Fatal("##########################################");
What I want to do is be able to use the 'aspectRule' rather than the default '*' rule by calling logger and specifying the 'aspectRule'.
I had thought this would work:
var log = LogManager.GetLogger("aspects");
log.Fatal("########################################");
which is all well and good but I can't see how I define a new Logger called 'aspects' particularly as in code I don't even create a Logger called '*' :-)
Note that I am not using an XML config file and need a solution to use programatically.
Any ideas?
EDIT:
I need to write 1 log entry as in this example:
if (someCondition)
logger.Fatal("#############"); // use layout in fileTarget
else
logger.Fatal("##############"); // use layout in aspectTarget
EDIT: To answer your edited question, LoggingRule rule2 = new LoggingRule("*", LogLevel.Trace, fileTarget) will log any output to the log file.
If you want a specific output then you'll have to be specific with NLog.
For example, log anything ranging from Trace up to Info with a fileTarget ("*"), and have two other specific targets aspects and rule2 with log levels from Error to Fatal.
Then you could log like this:
LogManager.GetCurrentClassLogger().Debug( "Non-specific message" );
LogManager.GetLogger( "rule2" ).Fatal( "From rule2" );
LogManager.GetLogger( "aspects" ).Fatal( "From Aspects" );
--
You're creating two configurations config and aspectConfig, setting up both and eventually setting LogManager.Configuration = config.
Eventually aspectConfig is not being used. To resolve, remove all aspectConfig related code and fix the acpects line to this:
config.LoggingRules.Add(aspectRule);