How to log custom variables - c#

I want to log the request duration. For that, I have a middleware and on the OnActionExecuted I assign the time elapsed to a variable and try to register it using the Custom Layout Rendere through a lambda function
requestDuration = _stopWatch.ElapsedMilliseconds;
LayoutRenderer.Register("requestDuration", logEvent => requestDuration);
On my nlog.config I have the following
<column name ="RequestDuration" layout="${requestDuration}" quoting="Nothing"/>
NLog complains saying that this will be ignored:
Error Error parsing layout requestDuration will be ignored. Exception: System.ArgumentException: LayoutRenderer cannot be found: 'requestDuration'
at NLog.Config.Factory`2.CreateInstance(String itemName)
at NLog.Layouts.LayoutParser.GetLayoutRenderer(ConfigurationItemFactory configurationItemFactory, String name)
Am I registering the layoutRenderer at the wrong place?
Please note that I'm trying to have a column that is the request duration, and not simply writing that time on the log message

If you get "LayoutRenderer cannot be found" while you have registered your layout renderer, then NLog parsed the config before the layout register.
You could do a reinitialize after the register:
LogManager.Configuration = LogManager.Configuration.Reload();
Although registering earlier is better.
Please note that it looks like a custom layout renderer for this case isn't really needed. There are a lot of context options for this, see https://github.com/NLog/NLog/wiki/Context

Related

Is it possible to add dynamic data to an MassTransit courier/routing slip custom event?

I have a MassTransit routing slip configured and working. For reference, the routing slip takes in an ID of an item in a MongoDB database and then creates a "version" of that document in a SQL database using EF Core. The activities (as commands) are:
Migrate document to SQL
Update audit info in MongoDB document
Update MongoDB document status (i.e. to published)
All of the above are write commands.
I have added a new 1st step which runs a query to make sure the MongoDB document is valid (e.g. name and description fields are completed) before running the migration. If this step fails it throws a custom exception, which in turns fires a failed event which is then picked up and managed by my saga. Below is a snippet of my activity code followed by the routing slip builder code:
Activity code
var result = await _queryDispatcher.ExecuteAsync<SelectModuleValidationResultById, ModuleValidationResult>(query).ConfigureAwait(false);
if (!result.ModuleValidationMessages.Any())
{
return context.Completed();
}
return context.Faulted(new ModuleNotValidException
{
ModuleId = messageCommand.ModuleId,
ModuleValidationMessages = result.ModuleValidationMessages
});
Routing slip builder code
builder.AddActivity(
nameof(Step1ValidateModule),
context.GetDestinationAddress(ActivityHelper.BuildQueueName<Step1ValidateModule>(ActivityQueueType.Execute)),
new SelectModuleValidationResultById(
context.Message.ModuleId,
context.Message.UserId,
context.Message.LanguageId)
);
builder.AddSubscription(
context.SourceAddress,
RoutingSlipEvents.ActivityFaulted,
RoutingSlipEventContents.All,
nameof(Step1ValidateModule),
x => x.Send<IModuleValidationFailed>(new
{
context.Message.ModuleId,
context.Message.LanguageId,
context.Message.UserId,
context.Message.DeploymentId,
}));
Whilst all of this works and the event gets picked up by my saga I would ideally like to add the ModuleValidationMessages (i.e. any failed validation messages) to the event being returned but I can't figure out how or even if that's possible (or more fundamentally if it's right thing to do).
It's worth noting that this is a last resort check and that the validation is checked by the client before even trying the migration so worse case scenario I can just leave it has "Has validation issues" but ideally I would like to include the derail in the failed response.
Good use case, and yes, it's possible to add the details you need to the built-in routing slip events. Instead of throwing an exception, you can Terminate the routing slip, and include variables - such as an array of messages, which are added to the RoutingSlipTerminated event that will be published.
This way, it isn't a fault but more of a business decision to terminate the routing slip prematurely. It's a contextual difference, which is why it allows variables to be specified (versus Faulted, which is a full-tilt exception).
You can then pull the array from the variables and use those in your saga or consumer.

NLog: How to determine if a named logger was not found in config

Let's say I have this partial configuration, with NLog:
<rules>
<logger name="ExistsInConfig" writeTo="Console"/>
</rules>
..and then I write this code:
var configuredLogger = LogManager.GetLogger("ExistsInConfig");
configuredLogger.Log(LogLevel.Info, "hello, cruel world!");
var missingLogger = LogManager.GetLogger("NotInConfig");
missingLogger.Log(LogLevel.Info, "goodbye, cruel world!");
In the console output I will see only the first logging statement, because the second named logger was not found in the config file.
How can I programatically detect that the second logger was not found, and therefore will produce no output?
If you have the instance of Logger, you could ask it to it:
bool hasConfigRuleForInfo = missingLogger.IsEnabled(LogLevel.Info)
If not, then you need some tricks, some possibilities:
or create your own LogManager class remember which remembers which loggers are used
or read with reflection the private propertiy LogManager.factory.loggerCache (not supported of course ;))
add a wildcard( *) rule to your config (API or XML) and write to MemoryTarget or a Custom Target. This could effect your performance. PS. with ${logger} you get the logger name. You will also need the final option on other rules.
I think this is the best way:
if (!NLog.LogManager.Configuration.ConfiguredNamedTargets.Any(t => t.Name.Equals("NameToValidate")))
{
//config not found
}
With #Julian 's answer, you could have the Config you are looking for but not the level you are comparing within. You could even have the Config without any level activated in your NLog config.
In those cases you would get an incorrect check response.

Is it possible to only display SourceContext in Serilog if ForContext<class> has been defined

I am new to Serilog and I am struggling with displaying the class name only when required without having to add {SourceContext:l}to each log message. I am aware I can just write the code as:
Log.ForContext<Class1>().Verbose("message {SourceContext:l}");
I have the logger being setup as below:
var log = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo
.ColoredConsole(outputTemplate: "{Timestamp:G} [{Level}] {SourceContext} {Message}{NewLine:l}{Exception:l}")
.CreateLogger();
I am hoping to only have the class name displayed when the class has been specified as seen here.
Log.ForContext<Class1>().Verbose("message");
And not displayed if the log is created like.
Log.Verbose("message");
However on the above example it will print a log out in the format of
'{SourceContext} message' instead of just 'message' which is not what I want.
This may be a bug in ColoredConsoleSink - generally when rendering output tokens a missing value will be left blank.
Raised: https://github.com/serilog/serilog/issues/649
You might try switching to the Literate Console sink (https://github.com/serilog/serilog-sinks-literate) which I don't believe has this bug.

Log some information no matter what using log4net

Situation is that level of log4net is configured as 'Error' but there is some information i need to write under a 'no matter what' condition
for example
"loggin has started"
if only 'Error' or 'Fatal' is enabled i cant log this in Error or Fatal since its just information
so is there any way i can do that other than change the level of the logger to info, write the log and then change back the level because that will act just like a workaround not a solution
and without using Headers since they only come at the beginning
EDIT: In an Appender
StringMatchFilter stringFilter = new StringMatchFilter();
stringFilter.AcceptOnMatch = true;
stringFilter.StringToMatch = "successfully";
stringFilter.ActivateOptions();
appender.AddFilter(stringFilter);
DenyAllFilter deny = new DenyAllFilter();
deny.ActivateOptions();
appender.AddFilter(deny);
adding to an appender and setting level 'All' to the root and managing levels in appenders but still i am unable to write any message containing 'successfully'
but please note when i set appender level to info the filter begins to work
I assume this is all because you don't like the idea of using .FatalFormat when it is not really an error.
I'm not sure how you would do this programatically, but if you were using a config file you add a section like
<logger name="ALWAYS">
<level value="DEBUG" />
appender-ref ref="RollingFileAppender" />
</logger>
which means you can log your messages like
log4net.LogManager.GetLogger("Always").InfoFormat( ... );
or you could create a static
static readonly log4net.ILog alwaysLogger = log4net.LogManager.GetLogger("Always");
and log via.
alwaysLogger.InfoFormat(....);
My suggestion would be to create a filter that looks first at a string match and then at the level. That way you could have a key string that you pass in the message (say "AppInfo" or something that would be unique and not found in an error). Then, your filter would pick that up and log it even when you logged it at the INFO level but the filter would ignore all other messages that weren't ERROR or FATAL. I wrote an article on CodeProject that will show you how to do complex filtering like this:
http://www.codeproject.com/KB/dotnet/Log4net_Tutorial.aspx
The one key here is that you would probably need to not specify the filtering in the root for this particular appender, since root supersedes the appender if I remember correctly.

How flexible is NLog? I want custom layout properties

I'm using NLog to send emails when an exception occurs in my app. Here's a portion of my target :
<target xsi:type="Mail"
name="email"
subject="${level}:" .. >
I receive emails with subjects like "Error:" or "Fatal:". This works fine but I want to add the Exception.Message to the subject of the email
Is it possible to setup custom properties in NLog. I can't find out how to do this, so just to make it clear what I want here is an example of the kind of thing I'm trying to do :
m_oLogger.Fatal( oException.BuildMessage(), new {MyMessage=oException.Message});
*Note that BuildMessage() is just an extension method to convert the full exception details (including inner exceptions) to a readable string
And in my target :
<target xsi:type="Mail"
name="email"
subject="${level}: ${Custom.MyMessage}" .. >
Then I would get emails with the subjects like :
Fatal: Syntax error in parameters or
arguments. The server response was:
Account does not exist
Is this kind of flexibility possible with NLog? If not, do you know of another .NET logging platforms that offers this kind of functionality?
In general instead of creating custom layoutRenderer suggested by wageoghe it's possible to use EventContext-Layout-Renderer that allows to pass any number of custom  properties 
LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "Pass my custom value");
theEvent.Properties["MyValue"] = "My custom string";`
log.Log(theEvent);
and in your NLog.config file:
${event-context:item=MyValue} -- renders "My custom string
For the particular https://github.com/nlog/NLog/wiki/Exception-Layout-Renderer the format should be
${exception:format=message}
It is very easy to add a custom LayoutRenderer to NLog. See my first answer to this question for an example of a LayoutRenderer that allows you to add the System.Diagnostics.Trace.CorrelationManager.ActivityId value to your logging output.
To do what you want, you probably don't need a custom LayoutRenderer. If you want to send an email whose subject is the log leve PLUS the Message property of the exception, you should be able to configure something like this:
<target xsi:type="Mail"
name="email"
subject="${level}: ${exception.Message}" ..>
That should create the subject of the email by concatenating the level and the value of the Exception.Message property. You will have to call the logging overload that takes an Exception as a parameter.
Does this help?
This is likely impractical: I'm fairly sure the NLog framework doesn't have reflection as a part of its logging format [to do so would require NLog to have some sort of concept of where your referenced assemblies are and what their type is.]
Can you simply do all your message parsing/formatting in the C# code, and and pass it as a part of the already existing variables? They list a lot of these in the Mail-target NLog documenation.

Categories