Why do NLog configuration changes not take effect? - nlog

I tried to change NLog's configuration via:
LogManager.Configuration.AddTarget (name, foo);
LogManager.Configuration.LoggingRules.Add (new LoggingRule ("*", LogLevel.Trace, foo));
but it silently ignored my changes. However, when I tried
SimpleConfigurator.ConfigureForTargetLogging (this, level);
it worked right away - but discarded all previous configuration. Why is that, and how do I solve this?

SimpleConfigurator replaces the current configuration, so you cannot use it for aggregated cases.
NLog does not detect and follow changes made to its current configuration unless you notify it via LogManager.Configuration = new_config. So use this instead:
var conf = LogManager.Configuration;
conf.AddTarget (name, foo);
conf.LoggingRules.Add (new LoggingRule ("*", LogLevel.Trace, foo));
LogManager.Configuration = conf;

Related

Serilog MinimumLevel Override with AspNetCore

Serilog with ASP NET 5 Razor Pages.
Reducing log verbosity is very useful for Informational logs.
However for debug logs, how to get a MinimumLevel.Override("Microsoft.AspNetCore") to be specific to a debug file sink?
Creating 2 configurations could be a solution, but feels like something more elegant may be possible?
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.FromLogContext()
// for debug file sink I want the override to be Debug
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Debug)
.WriteTo.File("debug.txt", restrictedToMinimumLevel: LogEventLevel.Debug)
// for info and warning file sinks I want the override to be Warning
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.WriteTo.File("info.txt", restrictedToMinimumLevel: LogEventLevel.Information)
.WriteTo.File("warning.txt", restrictedToMinimumLevel: LogEventLevel.Warning)
.CreateLogger();
Everything works as expected using just one override. But not together.
In the example above the Warning override takes precedence and no AspNetCore Debug event logs are written to debug.txt
Edit
In summary, I'd like my debug log to include Information event level from Microsoft.AspNetCore and my info log file to include Warning event level from Microsoft.AspNetCore
I got the 2 logs files how I wanted by commenting out and in 1. and 2. below
// 1. for debug file sink I want AspNetCore.Information or Debug level override
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Information)
.WriteTo.File($#"{logFilePath}debugx.txt", restrictedToMinimumLevel: LogEventLevel.Debug, rollingInterval: RollingInterval.Day)
// 2. for info and warning file sinks below I want only AspNetCore warnings
//.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
It's an interesting one
You want to filter log data and want to populate into different file sinks.
For Example /Logs/Error/Errlog.txt and /Logs/Info/InfoLog.txt
You can achieve this by using Serilog.Expressions nuget package. If time permits, I will paste a working example here.
Serilog.Expressions sample from Serilog
https://github.com/serilog/serilog-expressions/blob/dev/example/Sample/Program.cs
In below example it will exclude Name=User line and only print second line on console
using var log = new LoggerConfiguration()
.Filter.ByExcluding("#m like 'Welcome!%' and Name = 'User'")
.WriteTo.Console()
.CreateLogger();
// Logged normally
log.Information("Welcome!, {Name}", "User");
// Excluded by the filter
log.Information("Welcome!, {Name}", "Domain\\UserName");
Here is the filtering example for \Logs\Info\Info-20210720.txt which filters Error, Fatal or Warning levels. More information here
var exprInfo = "#l='Error' or #l='Fatal' or #l='Warning'";
var loggerInfo = new LoggerConfiguration()
.WriteTo.File(
#"C:\Temp\Logs\Info\Info-.txt",
fileSizeLimitBytes: 1_000_000,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level}] [{SourceContext}] [{EventId}] {Message}{NewLine}{Exception}",
rollingInterval: RollingInterval.Day,
rollOnFileSizeLimit: true,
shared: true,
flushToDiskInterval: TimeSpan.FromSeconds(1))
.MinimumLevel.Override("Microsoft", LogEventLevel.Debug)
.Filter.ByExcluding(exprInfo)
.CreateLogger();
try
{
loggerInfo.Debug("TEST");
SelfLog.Enable(Console.Out);
var sw = System.Diagnostics.Stopwatch.StartNew();
for (var i = 0; i < 100; ++i)
{
loggerInfo.Information("Hello, file logger!>>>>>>{Count}", i);
loggerInfo.Information("Writing to log file with INFORMATION severity level.");
loggerInfo.Debug("Writing to log file with DEBUG severity level.");
loggerInfo.Warning("Writing to log file with WARNING severity level.");
loggerInfo.Error("Writing to log file with ERROR severity level.");
loggerInfo.Fatal("Writing to log file with CRITICAL severity level.");
}
sw.Stop();
Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms");
Console.WriteLine($"Size: {new FileInfo("log.txt").Length}");
Console.WriteLine("Press any key to delete the temporary log file...");
Console.ReadKey(true);
}
catch (Exception ex)
{
loggerInfo.Fatal(ex, "Application Start-up for Serilog failed");
throw;
}
finally
{
Log.CloseAndFlush();
}
I solved it by using sub loggers and filters as described in here: How can I override Serilog levels differently for different sinks?
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.FromLogContext()
// Includes Debug from Microsoft.AspNetCore (noisy)
// useful for deep debugging
.WriteTo.File($#"logs/debug.txt", rollingInterval: RollingInterval.Day)
// Info-with-framework (useful for debugging)
.WriteTo.Logger(lc => lc
.MinimumLevel.Information()
.Filter.ByExcluding("RequestPath in ['/health-check', '/health-check-db']")
.WriteTo.File("logs/info-with-framework.txt", rollingInterval: RollingInterval.Day)
.WriteTo.Console()
)
// Info
// framework minimum level is Warning (normal everyday looking at logs)
.WriteTo.Logger(lc => lc
.MinimumLevel.Information()
.Filter.ByExcluding("RequestPath in ['/health-check', '/health-check-db']")
.Filter.ByExcluding("SourceContext = 'Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware'")
.Filter.ByExcluding(logEvent =>
logEvent.Level < LogEventLevel.Warning &&
Matching.FromSource("Microsoft.AspNetCore").Invoke(logEvent))
.WriteTo.File("logs/info.txt", rollingInterval: RollingInterval.Day))
// Warning (bad things - Warnings, Error and Fatal)
.WriteTo.Logger(lc => lc
.MinimumLevel.Warning()
// stopping duplicate stacktraces, see blog 2021/03/10/a11-serilog-logging-in-razor-pages
.Filter.ByExcluding("SourceContext = 'Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware'")
.WriteTo.File("logs/warning.txt", rollingInterval: RollingInterval.Day))
// SignalR - tweak levels by filtering on these namespaces
// Microsoft.AspNetCore.SignalR
// Microsoft.AspNetCore.Http.Connections
.CreateLogger();
Although this works, there may be a better way https://nblumhardt.com/2016/07/serilog-2-write-to-logger/
I feel like you don't need those minium level override calls. The restricted to minimum level parameter in the sinks will take are of filtering.
You do need to set the minimum level to info so the info sink can work.

How do I set default options for traceur.compile and traceur.require?

Using the official traceur module, is it possible to set the default options for compile and require?
For example, this code works:
var traceur = require('traceur');
console.log(
traceur.compile('{ let x = 1; }', { experimental:true }).js
);
Now if I remove traceur.compile's 2nd argument (the options object):
console.log(
traceur.compile('{ let x = 1; }').js
);
Traceur will throw an error as the blockBinding option is not enabled. Is there any way to change the default options, in order to compile files without always passing an options object?
My main concern, apart from applying the DRY principle, is getting the traceur.require function to compile files with customized options -- as far as I can see, traceur.require and traceur.require.makeDefault() do not even take an options argument.
For instance, considering this code sample:
require('traceur').require('./index');
And this piece of code:
require('traceur').require.makeDefault();
require('./index');
Is there any way to compile the required file with the experimental option enabled?
Preferably by altering the default options, as I cannot see any other viable way.
Using Node 0.10.29 and Traceur 0.0.49.
Here's a full example of what I'd like to achieve.
bootstrap.js (entry point):
var traceur = require('traceur');
traceur.options.experimental = true;
traceur.require.makeDefault();
require('./index');
index.js:
import {x} from './lib';
// using a block binding in order to check
// whether this file was compiled with experimental features enabled
{
let y = x;
console.log(y);
}
lib.js:
export var x = (() => {
if (true) {
// should be compiled with experimental features enabled too
let x = 1;
return x;
}
})();
Expected console output: 1
traceur.options.experimental=true serves as a setter which enables the experimental features in the traceur.options object, but unfortunately traceur.options does not seem to affect traceur.compile nor traceur.require as far as I can see.
The Using Traceur with Node.js Wiki page does not mention anything about compiling options. The Options for Compiling page does not mention the Traceur API in Node.js, in fact, I cannot find any documentation about the Traceur API in Node.js.
Fabrício Matté ;-) added support for giving the default options to makeDefault(), see
https://github.com/google/traceur-compiler/blob/master/src/node/require.js#L58
A separate bug with the option experimental was fixed today, 16JUL14.

Is there an equivalent of log.IsDebugEnabled in Winston?

Is there an equivalent of log.IsDebugEnabled in Winston?
I want to use this to skip expensive logging code in a production environment but have it execute in development.
For example:
if(winston.isDebugEnabled){
// Call to expensive dump routine here
dump();
}
Checking winston.debug just checks whether the method is defined, not whether it is enabled.
Many thanks!
Edit: Added code example.
I've added a method to my logger to achieve just that:
logger.isLevelEnabled = function(level) {
return _.any(this.transports, function(transport) {
return (transport.level && this.levels[transport.level] <= this.levels[level])
|| (!transport.level && this.levels[this.level] <= this.levels[level]);
}, this);
};
This goes through each of your logger's transports and checks whether it 'wants' to log the specified level.
Note _.any is lodash, you can replace with for loop.
I'm sure you'd be able to get that directly from winston, but if you want to have different logging levels for different environments, you should pass these in when you're creating the winston.logger.
For example:
// default log file level is info (which is the lowest by default)
var logFileLevel = 'info';
if (process.env.NODE_ENV == 'production') {
// only write logs with a level of 'error' or above when in production
logFileLevel = 'error';
}
var logger = new (winston.Logger)({
transports: [
new (winston.transports.File)({
filename: '/var/log/node-logger.log',
level: logFileLevel
})
]
});
It's also useful to switch out the transports. You might want to use the Console transport whilst in development, and the File transport when in production for example.
More documentation on all this on the winston readme.
Try
if ( logger.levels[logger.level] >= logger.levels['debug'] ) {
// expensive calculation here
logger.debug(...)
}
Since Winston 3.1.0 (PR), you can use the Logger functions isLevelEnabled(string) & isXXXEnabled() for this.

Confused about how JSHint options work

Problem
I have made a sample JavaScript file to illustrate my confusion.
(function() { console.log(true == true); })()
This should trip two errors; first the use of == instead of ===, and second, a missing semicolon. However, when I run
var jshint = require('jshint').JSHINT;
jshint('(function() { console.log(true == true); })()');
on in the Node REPL I get errors when I don't expect to. The reason I expect no errors is because in the JSHint documents, it says:
The second parameter [to jshint] is an optional object of options which control
the operation of JSHINT. Most of the options are booleans: They are
all optional and have a default value of false.
I would expect this to produce no errors as I've defined no options so all should be false. The final part of unexpected behavior is that setting options seems to do nothing. The following two calls to jshint also produce the same errors:
var myFile = '(function() { console.log(true == true); })()';
jshint(myFile, {eqeqeq: false, asi: false});
jshint('/*jshint eqeqeq:false, asi:false */\n' + myFile);
Question
I'm not sure how the JSHint options work and I very well may be misinterpreting the documentation. I'd appreciate knowing what in either my invocation of jshint above or in my assumptions is incorrect or if there actually is a problem with JSHint.
Not all options are off by default. In our docs (http://www.jshint.com/docs/) you can see that some options are 'enforcing' and some 'relaxing'[1]. This means that some warnings will be displayed by default and you need to enable 'relaxing' option to turn them off.
Now, with this information, let's look at your second question:
/*jshint eqeqeq:false, asi:false */
(function () { console.log(true == true); })()
This code will trigger an unsafe comparison warning for == true and a missing semicolon. You tried to fix that by turning off two options eqeqeq and asi. The former, when set to true, requires strict comparison everywhere and is turned off by default so you can omit it. You get a warning because JSHint considers == true/false/0/null comparisons unsafe and currently there is no way to turn that off (there won't be warning for a == b for example)[2]. And the latter option (asi) is a relaxing option so you actually need to turn it on to tell JSHint that it's okay to tolerate missing semicolons. So to make your example pass you will need to change it this way:
/*jshint asi:true */
(function () { console.log(true === true); }()
[1] — I understand that this is confusing. Because of backwards compatibility I can't just change how options work but I'm working towards making it less confusing. I will add default values for each option to the docs soon-ish.
[2] — The upcoming 1.0.0 release will allow you to ignore any warnings by their code, even if there's no corresponding named option.

Configuring log4net appenders via XML file *and* code

I started to play with log4net today and so far, I really like it. In order to preserve our current logging functionality, the app needs to create a new log file whenever the application is started. The log file name has the date and time stamp encoded in it. Currently, I've got log4net configured via an XmlConfigurator, which works great, except that the filename for my RollingFileAppender is hardcoded in the configuration XML file.
I'd like to continue to use the XmlConfigurator, but after calling Configure(), I want to get at the RollingFileAppender and, in code, change its file value to be a dynamically-generated string. The sample documentation online seems to be down right now, but I've poked through the SDK reference, and it looks like I could use the Heirarchy and GetAppenders() to do what I need to do. Am I on the right track?
Ok, I took a stab at this and tried the following code, which didn't work:
private static readonly ILog _log = LogManager.GetLogger(typeof(GUI));
// in the config file, I've set the filename to example.log, and it works
XmlConfigurator.Configure(new FileInfo("log_config.xml"));
Hierarchy hierarchy = LogManager.GetRepository() as Hierarchy;
if(hierarchy != null) {
// get the appenders
IAppender[] appenders = hierarchy.GetAppenders();
// change the filename for the RollingFileAppender
foreach( IAppender a in appenders) {
RollingFileAppender rfa = a as RollingFileAppender;
if(rfa == null)
continue;
rfa.File = "newfile.log"; // no runtime error, but doesn't work.
}
}
_log.Info("Application started");
Try this snippet:
XmlConfigurator.Configure();
log4net.Repository.ILoggerRepository repo = LogManager.GetRepository();
foreach (log4net.Appender.IAppender appender in repo.GetAppenders())
{
if (appender.Name.CompareTo("RollingFileAppender") == 0 && appender is log4net.Appender.RollingFileAppender)
{
var appndr = appender as log4net.Appender.RollingFileAppender;
string logPath = "MyApplication.log";
appndr.File = logPath;
appndr.ActivateOptions();
}
I had posted similar article here
Do you in this case need the rolling file appender? If not I would expect that your code would create the desired result if you used the normal file appender.
Edit: Maybe it works with the RollingFile Appender if you call ActivateOptions() on the appender.

Resources