NLog - Can a FallbackGroup wrapper contain another FallbackGroup wrapper? - c#

In NLog ... can a FallbackGroup wrapper contain another FallbackGroup wrapper? That is I want a cascade effect to handle if my 1st target (writing to a Database) fails, then my 2nd target will be to write to both a file and send an email.
And what config settings are needed to implement this structure (so that NLog handles encountered exceptions [ex. writing to the database failure] properly)?
Thanks for your time and any suggestions you offer.

If you want fallback to write to two targets then you can combine it with SplitGroup:
<target xsi:type="FallbackGroup"
name="db_fallback"
returnToFirstOnSuccess="true">
<target xsi:type="Database" name="db" />
<target xsi:type="SplitGroup" name="split_db_fallback">
<target xsi:type="file" name="file_db_fallback" />
<target xsi:type="mail" name="mail_db_fallback" />
</target>
</target>
See also https://github.com/nlog/nlog/wiki/SplitGroup-target

Related

having an issue saving NLog file to basefolder

I'm having a hard time understanding why ${basedir} does nothing to the current directory of the project and when I change it to the explicit path it does saves the log.
I tried to change the name of ${basedir} to ${currentdir} but it did nothing.
This one works
<target xsi:type="File" name="myAppLoggerTarget" fileName="C:\Users\Omer\Desktop\DBBalancers\Logs\${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
This one works
<target xsi:type="File" name="myAppLoggerTarget" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
Link to nlog docs suggests: "The current application domain's base directory."
I would try to print out the: "AppDomain.CurrentDomain.BaseDirectory" and see whats the value of that variable.
On the same nlog docs page you can also find about process dir:
processDir - Introduced in NLog 4.4.2. Render the base directory of the current process? Default false.

Set ApplicationData folder on app.config and get it on Nlog.config

I'd like to set the path of NLog log file on app.config.
I know how to set the applicationData folder on NLog.config. It's like that:
<target xsi:type="File"
name="logFileCsv"
fileName="${specialfolder:folder=ApplicationData}/something/something.log">
And if I use the NLog.Extended I know how to get key values from app.config and use them on NLog.config, like that:
<target xsi:type="File" name="logFileCsv" fileName="${appsetting:name=LogPath">
And on app.config should be like this
<add key="LogPath" value="${APPDATA}/something/something.log" />
But it's not working the way I want. NLog is creating the log file on c:\..\${APPDATA}/something/something.log"
So, I'd like to know how to set the ApplicationData folder on app.config to be understandable by NLog?
Unfortunately the ${appsetting} won't evaluate the value.
As work around you can do:
<target fileName="${APPDATA}/${appsetting:name=LogPath}" />
note: wrapping in a ${replace} also won't work
I tried the answer of Julian, but it wasn't utterly successful:
Error FileTarget(Name=logFileCsv): Failed write to file '\\something\something.log
However, with that help and workaround, I figure out how to do it:
So in App.config just add the final path:
<add key="LogPath" value="\something\something.log"/>
and in NLog.Config I did this:
<target xsi:type="File" name="logFileCsv" fileName="${specialfolder:folder=ApplicationData}/${appsetting:name=LogPath}">
And now the log is written on the right path: C:\Users\user\AppData\Roaming\something\something.log

XAML access user directory [duplicate]

My NLog targets is like this:
<targets>
<target xsi:type="Console" name="console"
layout="${longdate}|${level}|${message}" />
<target xsi:type="File" name="ErrorLog" fileName="${basedir}/error.txt"
layout="${longdate}
Trace: ${stacktrace}
${message}" />
<target xsi:type="File" name="AccessLog" fileName="${basedir}/access.txt"
layout="${shortdate} | ${message}" />
</targets>
But this causes problems if the user isn't an admin on their machine, because they will not have write access to "Program Files". How can I get something like %AppData% to NLog instead of BaseDir?
You're looking for the NLog special folders.
Example:
...fileName="${specialfolder:folder=ApplicationData}/Program/file.txt"...
Oren's answer should be the right answer. However, for the life of me I couldn't get it to work with my .NET 4.0 website using nLog 2.0.0.0. I ended up using simply
fileName="${basedir}app_data\logs\${shortdate}.log"
${specialfolder:ApplicationData} also works
The previous answers helped solve the problem I was having, but a couple of years later and the solution is now somewhat different under v4.3. The directory and filename are combined with the path.
#theGecko's link is still current for the syntax, but the page is deficient of an example:
https://github.com/nlog/NLog/wiki/Special-Folder-Layout-Renderer
The following example would write the file myLog.log to the current users application data roaming directory C:\USers\current.user\AppData\Roaming\My\Path\Somewhere:
fileName="${specialfolder:dir=My/Path/Somewhere/:file=myFile.log:folder=ApplicationData}"
For logging to the project directory:
While the previous answers work for the original question, searching for how to log to the project APP_DATA directory leads to this question. And while bkaid's answer works for ASP.NET and for using the APP_DATA folder specifically, for .NET Core and .NET 5 the solution is a bit different, because that motif has been abandoned in favor of defining a wwwroot folder for only those things which should be served, and the remainder being private. The answer for .NET Core/5, then, is to write to the solution root directory:
First, ensure the NLog.Web.AspNetCore assembly is added to nlog.config:
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
Then use one of the layout renderers provided by that extension, in this case ${aspnet-appbasepath} which references the solution root directory:
<targets>
<target name="file"
type="File"
xsi:type="File"
fileName="${aspnet-appbasepath}/log/${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}"/>
</targets>
This will write the file to <solution folder>/log/2021-07-01.log, which will never be served by the public-facing website. Other layout renderers provided by this assembly are listed on the NLog website.

Customizations on MSBuild (like version) for a C# solution

I'm thinking that the final result is going to be "it can't be that easily done", but just seems like it should be. I have a personal project I am working on. I'd hate to have to manually (or even in script) change versions, company, copyright, and all that on ALL the assembly.cs files and would like all that to be either in a script or in a file I can change (so the script stays the same mostly) when I want to update the version. But it seems like MSBuild is mostly a "build as is specified in Visual Studio". I'd just hate to have all that history of these files where I change just the version and possibly even make a mistake as this project will continue to get bigger and bigger. I'd like to just be able to add a new project to Visual studio and have whatever command line in my powershell script just say "compile this, but give it this company name and this file version instead of whatever is listed in the code file".
Google has NOT proven fruitful in this. I've even found it difficult to build my files to a specific folder. I've had to so far make sure all my projects are 2 folders deep and was able to say to build them at ....\, but I would like to be able to change that randomly if I like and have them built elsewhere if I so desire.
Is MSBuild perhaps not the way to go? Is there someway else to build visual studio that would be better from command line? Eventually I also want to auto build the install with wix and be able to match its version with the binary versions.
thank you
Since csproj is xml, you can use XmlUpdate "helpers" to modify the values inside the csproj file before you do your build.
For other files, you can use some other Tasks to do the job.
Here is one helpful target:
http://msbuildtasks.tigris.org/ and/or https://github.com/loresoft/msbuildtasks has the ( FileUpdate (and SvnVersion task if that is your Source-Control) ) tasks.
<Target Name="BeforeBuild_VersionTagIt_Target">
<ItemGroup>
<AssemblyInfoFiles Include="$(ProjectDir)\**\*AssemblyInfo.cs" />
</ItemGroup>
<!--
<SvnVersion LocalPath="$(MSBuildProjectDirectory)" ToolPath="$(SVNToolPath)">
<Output TaskParameter="Revision" PropertyName="MyRevision" />
</SvnVersion>
-->
<PropertyGroup>
<MyRevision>9999</MyRevision>
</PropertyGroup>
<FileUpdate Files="#(AssemblyInfoFiles)"
Regex="AssemblyFileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"
ReplacementText="AssemblyFileVersion("$1.$2.$3.$(MyRevision)" />
</Target>
Below is an example of manipulating the csproj(xml).
How to add a linked file to a csproj file with MSBuild. (3.5 Framework)
But basically, when you build, you can put all the repetative stuff in a msbuild definition file (usually with the extension .proj or .msbuild)...and call msbuild.exe MyFile.proj.
Inside the .proj file, you will reference your .sln file.
For example:
$(WorkingCheckout) would be a variable (not defined here)...that has the directory where you got a copy of hte code from your source-control.
<Target Name="BuildIt" >
<MSBuild Projects="$(WorkingCheckout)\MySolution.sln" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="TargetOutputsItemName"></Output>
</MSBuild>
<Message Text="BuildItUp completed" />
</Target>
So below is the more complete example.
You would save this as "MyBuild.proj" and then call
"msbuild.exe" "MyBuild.proj".
Start .proj code. (Note, I did not import the libraries for the FileUpdate Task)
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapped">
<PropertyGroup>
<!-- Always declare some kind of "base directory" and then work off of that in the majority of cases -->
<WorkingCheckout>.</WorkingCheckout>
</PropertyGroup>
<Target Name="AllTargetsWrapped">
<CallTarget Targets="BeforeBuild_VersionTagIt_Target" />
<CallTarget Targets="BuildItUp" />
</Target>
<Target Name="BuildItUp" >
<MSBuild Projects="$(WorkingCheckout)\MySolution.sln" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="TargetOutputsItemName"></Output>
</MSBuild>
<Message Text="BuildItUp completed" />
</Target>
<Target Name="BeforeBuild_VersionTagIt_Target">
<ItemGroup>
<AssemblyInfoFiles Include="$(ProjectDir)\**\*AssemblyInfo.cs" />
</ItemGroup>
<!--
<SvnVersion LocalPath="$(MSBuildProjectDirectory)" ToolPath="$(SVNToolPath)">
<Output TaskParameter="Revision" PropertyName="MyRevision" />
</SvnVersion>
-->
<PropertyGroup>
<MyRevision>9999</MyRevision>
</PropertyGroup>
<FileUpdate Files="#(AssemblyInfoFiles)"
Regex="AssemblyFileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"
ReplacementText="AssemblyFileVersion("$1.$2.$3.$(MyRevision)" />
</Target>
</Project>
To enhance the above, you would create a new target that would run before "BeforeBuild_VersionTagIt_Target", that would pull your code from source-control and put it in the $(WorkingCheckout) folder.
The basic steps would then be: 1. Checkout code from Source-Control. 2. Run the targets that alter the AssemblyVersion (and whatever else you want to manipulate) and 3. Build the .sln file.
That's the basics of a .proj file. You can do much more. Usually by using helper libraries that already exists.

MVCBuildViews not working correctly

So I edited my csproj file on an MVC 3 RTM application to set the following property:
<MvcBuildViews>true</MvcBuildViews>
This should cause my views to be complied during build and force a build error if my view is broken. This is the only change I made, however, when I try to build the application, I get the following error:
It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS.
The project compiles and runs successfully if I change back to false,
The following are the build tasks configured in the csproj file (these were never manually edited, they were added by Visual Studio 2010)
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target> -->
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
Am I missing something here? How do I get MVC 3 / Visual Studio 2010 configured correctly to validate my views at build time?
I had this problem a few days ago and I fixed it by deleting obj/Debug folder. Cleaning the project also works. I have no idea about the cause of the issue, though.
See Joe Cartano's answer for a more permanent solution.
This problem occurs when there is web project output (templated web.config or temporary publish files) in the obj folder. The ASP.NET compiler used isn't smart enough to ignore stuff in the obj folder, so it throws errors instead.
Another fix is to nuke the publish output right before calling <AspNetCompiler>. Open your .csproj and change this:
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
to this:
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<ItemGroup>
<ExtraWebConfigs Include="$(BaseIntermediateOutputPath)\**\web.config" />
<ExtraPackageTmp Include="$([System.IO.Directory]::GetDirectories("$(BaseIntermediateOutputPath)", "PackageTmp", System.IO.SearchOption.AllDirectories))" />
</ItemGroup>
<Delete Files="#(ExtraWebConfigs)" />
<RemoveDir Directories="#(ExtraPackageTmp)" />
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
That will delete all web.configs under \obj, as well as all PackageTmp folders under \obj.
UPDATE:
Even better, based off https://stackoverflow.com/a/48582282/8037 you can exclude the obj folder entirely. Apparently the <AspNetCompiler /> task doesn't have an exclude parameter, but if you switch to calling the aspnet_compiler .exe directly, you can exclude obj like this:
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<Exec Command="$(MSBuildFrameworkToolsPath)aspnet_compiler.exe -v temp -p $(WebProjectOutputDir) -x $(BaseIntermediateOutputPath)"/>
</Target>
When you get this error do you have another web.config file in your obj folder? If you are using MSDeploy this might help: MSDN Blog: The Aspnet Compiler Build Task in Visual Studio 2010 ASP.Net MVC 2 Projects, if not, maybe another web.config is being generated by some tool you are running.
This is what worked for me. Optionally, you may specify a condition with the configuration.
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
<Target Name="AfterBuild" Condition="'$(Configuration)'!='Debug'">
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
</Target>
This issue of Compile-time View Checking even though MvcBuildViews is set to 'true' is well-explained in the following MSDN blog:
https://blogs.msdn.microsoft.com/jimlamb/2010/04/20/turn-on-compile-time-view-checking-for-asp-net-mvc-projects-in-tfs-build-2010/
You could do the fix by editing .csproj file directly:
<PropertyGroup>
<MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
<Target Name="BuildViews" Condition="'$(MvcBuildViews)'=='true'" AfterTargets="Build">
<Message Importance="normal" Text="Precompiling views" />
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
A simple solution kinda compiled from the other answers here
You can simply remove the whole /obj folder like this:
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<RemoveDir Directories="$(ProjectDir)$(BaseIntermediateOutputPath)" /> <!--add this line-->
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>

Categories