NLog default wrapper usage in JSON-config - c#

Documentation says:
Sometimes we require ALL targets to be wrapped in the same way, for example to add buffering and/or retrying. NLog provides <default-wrapper /> syntax for that. You simply put this element in the <targets /> section and all your targets will be automatically wrapped with the specified wrapper.
It also provides the following example:
<nlog>
<targets>
<default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>
<target name="f1" xsi:type="File" fileName="f1.txt"/>
<target name="f2" xsi:type="File" fileName="f2.txt"/>
</targets>
<targets>
<default-wrapper xsi:type="AsyncWrapper">
<wrapper-target xsi:type="RetryingWrapper"/>
</default-wrapper>
<target name="n1" xsi:type="Network" address="tcp://localhost:4001"/>
<target name="n2" xsi:type="Network" address="tcp://localhost:4002"/>
<target name="n3" xsi:type="Network" address="tcp://localhost:4003"/>
</targets>
</nlog>
So, <default-wrapper /> element should be a child of <targets> to be applied to each <target> within the same parent.
On the other hand, Extended Json NLog Config Example uses default-wrapper on the same level as targets element:
"default-wrapper": {
"type": "AsyncWrapper",
"overflowAction": "Block"
},
"targets": {
So, I have the following questions:
Is the Extended Json NLog Config Example correct?
Will all targets from targets element be wrapped with the default-wrapper, placed out of the targets?
What if there are default-wrapper within targets and default-wrapper out of targets in the same configuration file?

Yes there was some discussion about whether XML-config and JSON-config should be completely 1-to-1. See also: https://github.com/NLog/NLog.Extensions.Logging/pull/283
Because JSON-config represented targets-section as a dictionary of known target-names, then it didn't feel natural to have default-wrapper and default-target-parameters as reserved magic-strings.
Instead they were moved out of targets-section, as you have discovered from the documentation.
Yes all items in the targets-section will be wrapped, when using default-wrapper.
Have not tested what happens if placing default-wrapper and default-target-parameters inside the targets-section-dictionary. Maybe check the NLog InternalLogger whether it complains about unknown configuration-items.
Notice more work is being done to give default-wrapper and default-target-parameters a better name, since it might not be easy to guess that they both apply to the targets-section. See also: https://github.com/NLog/NLog.Extensions.Logging/pull/500

Related

Specflow - How to get Target Name from srprofile programatically in C#

Is there any way to get the Target name defined in specflow's srs profile ?
Below is what is defined in srs profile,
<Targets>
<Target name="Login">
<Filter>#login</Filter>
</Target>
</Targets>
I want to get the name of the target "Login" and save it in a variable in C#.
We don't have an API to get the target name, but you can get to it with a little bit more stuff in the srProfile.
This is how you do it:
<?xml version="1.0" encoding="utf-8"?>
<TestProfile xmlns="http://www.specflow.org/schemas/plus/TestProfile/1.5">
<Targets>
<Target name="Target1">
<DeploymentTransformationSteps>
<EnvironmentVariable variable="RUNNER_TARGET" value="Target1" />
</DeploymentTransformationSteps>
</Target>
<Target name="Target2">
<DeploymentTransformationSteps>
<EnvironmentVariable variable="RUNNER_TARGET" value="Target2" />
</DeploymentTransformationSteps>
</Target>
</Targets>
</TestProfile>
You specify a deployment transformation step which sets an environment variable to the name of the target.
In your bindings, you can get the value of the environment variable by normal .NET APIs.
var targetName = Environment.GetEnvironmentVariable("RUNNER_TARGET");
You can find a complete example here: https://github.com/SpecFlowOSS/SpecFlow.Plus.Examples/tree/master/AccessTargetName
Full disclosure: I am one of the developers of SpecFlow and SpecFlow+

NLOG: how to use variable to specify target

Is it possible to use a variable to specify a specific target?
I want to switch between database and filelogging depending on the environment i'm running my application on.
This however does not seem to work:
<logger name="MyLogger" minlevel="Warn" writeTo="${var:myTargetName}" />
In my application startup I have (error occurs on first line):
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
var config = LoadConfiguration();
NLog.LogManager.Configuration.Variables["myTargetName"] = config.GetSection("LogTargets:TargetName").Value;
NLog.LogManager.KeepVariablesOnReload = true;
NLog.LogManager.Configuration.Reload();
When startin the application the following exception is thrown:
"Target ${var:myTargetName} not found."
I guess the variables are not available when parsing the config file.
How can I set the variables so NLOG uses them while parsing the config-file?
Is it possible to use a variable to set an attribute value?
Or is this not supported by NLOG?
Note: I do have another variable in the NLOG config which does work
<target xsi:type="Database" name="databaseTarget">
<connectionString>${var:connectionString}</connectionString>
...........
</target>
I guess the loggers are checked once on startup and the actual target is evaluated when a logevent occurs.
Update: got it working without variable in config file.
I had to remove the logger from the config and create it through code
var myCustomTarget = NLog.LogManager.Configuration.FindTargetByName(config.GetSection("LogTargets:TargetName").Value);
NLog.LogManager.Configuration.AddRuleForAllLevels(myCustomTarget , "MyLogger", true);
One way to do it is to have a different Web.config file based on the environment and there you will change the connection string. That is the method I am using.
Also NLOG is initialized once the application starts thus you cannot change what's written in the NLOG.config
Setting variables via NLog.LogManager.Configuration.Variables does not work for all attributes in nlog.config file. I don't know why, but that's a known problem and that is unfortunate how it works. But there is a simple workaround, here is an example how I solved this problem for the attribute connectionString of a target.
<target xsi:type="Database" name="tDatabase"
dbProvider="Microsoft.Data.Sqlite.SqliteConnection, Microsoft.Data.Sqlite"
connectionString="Data Source =.\${environment:DATABASE_FILE_NAME};"
commandText="INSERT INTO ...">
<parameter name="#MachineName" layout="${machinename}" />
...
</target>
You can than simple set the environment variable in your code like this
Environment.SetEnvironmentVariable("DATABASE_FILE_NAME", "foo.db");
So you just use ${environment:DATABASE_FILE_NAME} and
Environment.SetEnvironmentVariable("DATABASE_FILE_NAME", "foo.db");
instead of ${var:DATABASE_FILE_NAME} and
NLog.LogManager.Configuration.Variables["DATABASE_FILE_NAME"] = "foo.db";
NLog 4.6.7 makes it easier to update the logging-rules at runtime like this:
<nlog>
<variable name="fileMinLevel" value="Off" />
<variable name="databaseMinLevel" value="Off" />
<rules>
<logger minLevel="${var:fileMinLevel}" writeTo="fileTarget" />
<logger minLevel="${var:databaseMinLevel}" writeTo="databaseTarget" />
</rules>
</nlog>
Then you can do this:
if (IsDevelopment())
LogManager.Configuration.Variables["fileMinLevel"] = "Debug";
else
LogManager.Configuration.Variables["databaseMinLevel"] = "Debug";
LogManager.ReconfigExistingLoggers();
See also: https://github.com/NLog/NLog/wiki/Filtering-log-messages#semi-dynamic-routing-rules
See also: https://github.com/NLog/NLog/wiki/Environment-specific-NLog-Logging-Configuration

NLog - Archive Start / Stop Time

Just started working with NLog and have it running with the following configuration:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
throwConfigExceptions="true">
<targets async="true">
<target name="logfile"
xsi:type="File"
layout="${longdate} [${level:uppercase=true}] (${threadid}) ${logger}: ${message} ${onexception:${newline}Exception\: ${exception:format=type,message,method,stacktrace:maxInnerExceptionLevel=5:innerFormat=shortType,message,method}}"
fileName="logs/current.log"
archiveFileName="logs/Archive/${ticks}.log"
archiveEvery="Minute"
archiveOldFileOnStartup="true"
keepFileOpen="false"
/>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile" />
</rules>
</nlog>
Everything is working as expected. However, I need to have the rotated file be in the format of ${archive_start_ticks}_${arhive_end_ticks}.log rather than the current format which is ${archive_end_ticks}.log.
I was initially hoping I could name the active log file as ${ticks} and then, on archive, use the active log file's name as a parameter into the archive file to compose some like:
fileName="logs/${ticks}"
archiveFileName="logs/Archive/${fileName}_${ticks}.log"
Of course, there's two issues here:
Using ${ticks} for the active file creates a new file for each log line.
I can't seem to reference the original fileName as an input variable into archiveFileName.
That said, what is the best way to achieve this goal? Is this something NLog can handle natively or with minor extensions?
Updating in case anyone ever cares:
I bailed on using the FileTarget with configurations and wrote my own Target wrapped in a BufferedWrapper. On each flush, I use the first and last LogEvents to determine the timespan which gives me what I need to for the required file format with little custom code to support.

How to specify template, or tokens in filename of Enterprise Library Logging file listener

From what I read it appears, that enterprise library does not allow tokens in filename, which is very strange, because all other logging solutions do.
It's very confusing, I am definetly missing something. Cause I am not even able to specify Process name in flat or lolling file listener.
NLog
<target name="txtFile"
xsi:type="File"
fileName="${cached:cached=true:inner=${date:format=yyyy-MM-dd HH-mm-ss}}.txt"
layout="${longdate} ${level} ${message}"/>
Log4Net
<file type="log4net.Util.PatternString" value="~/App_Data/%property{LogName}" />
The only solution I know (excluding programmatic setup) now is using Environment Variables on Process Level
<add name="loggingErrorsLog"
fileName="%LOGS_DIR%\%PROCESSNAME%\loggingErrors.log" ...
And to set those variables in code:
var logsDir = Environment.GetEnvironmentVariable(EnvVarLogsRoot, EnvironmentVariableTarget.Machine) ?? "Logs";
Environment.SetEnvironmentVariable(EnvVarLogsRoot, logsDir);

Building sub projects using NANT

I'm trying to build a project(A) using NANT. The project(A) relies upon another project(B) which is also built with NANT. I want to be able to invoke the build of the dependent project(B) from within the build of project(A). I've tried including the build file of project B in the build file of project A. This creates an error because the two build files contain targets that share the same name.
Is there a way to alias the included build file?
You can do it like this by creating a "parent" buildfile, that uses the "nant" action to call other buildfiles.
<target name="rebuild" depends="" >
<nant target="${target::get-current-target()}">
<buildfiles>
<include name="projectB.build" />
<include name="projectC.build" />
</buildfiles>
</nant>
</target>
I was trying to do this using an include task but have found that the nant task is actually what I required.
You can have several of such targets in your 'master' file. I often use the following construction to share a set of build files between targets to make script maintenance easier.
<fileset id="buildfiles.all">
<include name="projectB.build"/>
<include name="projectB.build"/>
</fileset>
<target name="build">
<nant target="${target::get-current-target()}">
<buildfiles refid="buildfiles.all" />
</nant>
</target>
<target name="clean">
<nant target="${target::get-current-target()}">
<buildfiles refid="buildfiles.all" />
</nant>
</target>
<target name="publish">
<nant target="${target::get-current-target()}">
<buildfiles refid="buildfiles.all" />
</nant>
</target>

Categories

Resources