Customers who sign-up prior to 30/06/2024 get unlimited access to free features, newer features (with some restrictions), but for free for at least 1 year.Sign up now! https://webveta.alightservices.com/
Categories
.Net C# Logging NLog

Custom Layout Renderer for NLog using C# New

In a previous blog post – Custom Layout Renderer for NLog using C#, I have mentioned about how to create a class inheriting LayoutRenderer and writing custom Layout Renderer.

This blog post talks about inheriting from WrapperLayoutRendererBase class.

The WrapperLayoutRendererBase class has a method with the signature:

protected override string Transform(string text)

This method needs to be overridden. The nice thing about using this class is other layout renderers can be used in combination.

Here is a GitHub repo implementing hash functions that can be used as LayoutRenderers.

https://github.com/ALightTechnologyAndServicesLimited/ALight.NLog.LayoutRenderer.Hash

The source code has 4 projects:

  1. The actual implementation
  2. Unit Tests
  3. Benchmark Tests
  4. Example

The usage is straightforward, use hash or securehash layout renderer.

  <extensions>
    <add assembly="ALight.NLog.LayoutRenderer.Hash" />
  </extensions>




<target xsi:type="File" name="logfile" fileName="c:\temp\console-example.log"
        layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring} ${hash:hello} 
        ${securehash:value=${level}} ${event-properties:item=secret} ${hash:${event-properties:item=secret}} ${hash:${level}}" />

The hash implementation is about 10 – 11 times faster but uses Murmur3 non cryptographic hash.

I might consider a nuget package at a later point, but not now, because the code is very minimal and like a small helper class.

Mr. Kanti Kalyan Arumilli

Arumilli Kanti Kalyan, Founder & CEO
Arumilli Kanti Kalyan, Founder & CEO

B.Tech, M.B.A

Facebook

LinkedIn

Threads

Instagram

Youtube

Founder & CEO, Lead Full-Stack .Net developer

ALight Technology And Services Limited

ALight Technologies USA Inc

Youtube

Facebook

LinkedIn

Phone / SMS / WhatsApp on the following 3 numbers:

+91-789-362-6688, +1-480-347-6849, +44-07718-273-964

+44-33-3303-1284 (Preferred number if calling from U.K, No WhatsApp)

kantikalyan@gmail.com, kantikalyan@outlook.com, admin@alightservices.com, kantikalyan.arumilli@alightservices.com, KArumilli2020@student.hult.edu, KantiKArumilli@outlook.com and 3 more rarely used email addresses – hardly once or twice a year.

Categories
.Net C# Logging NLog

Custom Layout Renderer for NLog using C#

*** use WrapperLayoutRendererBase instead of LayoutRenderer, this blog post is about LayoutRenderer ***

NLog has lot of built-in layout renderers. Here is a list of official layout renderers:

https://nlog-project.org/config/?tab=layout-renderers

Creating custom layout renderer is very simple and straight-forward. Here is the official documentation:

https://github.com/NLog/readthedocs/blob/master/docs/How-to-write-a-custom-layout-renderer.md

There are 2 ways:

Lambda:

LayoutRenderer.Register("name", (logEvent, config) => "Message");

In the above scenarion, we can use ${name} when defining a layout, of course instead of returning static string we can do something useful based on the use-case.

Class:

Define a new class inherited from NLog.LayoutRenderers.LayoutRenderer. Use class attribute LayoutRenderer(“name”). LayoutRenderer is an abstract class, implement the class by overriding Append(StringBuilder builder, LogEventInfo logEvent) method i.e Append the value that needs to be added to the log by appending to the StringBuilder parameter.

[LayoutRenderer("Example")]
public class ExampleLayoutRenderer : NLog.LayoutRenderers.LayoutRenderer
{
    public string Value { get; set; }

    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
        builder.Append("Value");
    }
}

Extra parameters can be passed and assigned as properties of the custom class.

[LayoutRenderer("example")]
public class ExampleLayoutRenderer : NLog.LayoutRenderers.LayoutRenderer
{
    public string Value { get; set; }

    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
        // Your custom code
        builder.Append(Value);
    }
}

In the config file the custom layout renderer with params can be used like follows:

{example}

{example:value=abc}

Sometimes, in logs we write some data that might be needed for identifying but not necessarily the real value and for various reasons we might just need a hash. For example: SessionId / Some Cookie Value / IP address etc… Having a custom renderer for hashing such a value could be used.

Very few lines of code, i.e hashing and appending to a stringbuilder would solve the need i.e 2 – 3 lines of code. But, I think the 2 – 3 lines of code could be helpful for other people. I am considering creating an opensource version. I might or might not release nuget package. If I release nuget package, I would definitely make an anouncement. Anyway, not a big open-source project, but just few lines of code.

Mr. Kanti Kalyan Arumilli

Arumilli Kanti Kalyan, Founder & CEO
Arumilli Kanti Kalyan, Founder & CEO

B.Tech, M.B.A

Facebook

LinkedIn

Threads

Instagram

Youtube

Founder & CEO, Lead Full-Stack .Net developer

ALight Technology And Services Limited

ALight Technologies USA Inc

Youtube

Facebook

LinkedIn

Phone / SMS / WhatsApp on the following 3 numbers:

+91-789-362-6688, +1-480-347-6849, +44-07718-273-964

+44-33-3303-1284 (Preferred number if calling from U.K, No WhatsApp)

kantikalyan@gmail.com, kantikalyan@outlook.com, admin@alightservices.com, kantikalyan.arumilli@alightservices.com, KArumilli2020@student.hult.edu, KantiKArumilli@outlook.com and 3 more rarely used email addresses – hardly once or twice a year.

Categories
.Net C# NLog UnitTests

Code Coverage in .Net 2023

In the past, I have written a blog post about code coverage using NUnit, OpenCover and ReportGenerator https://www.alightservices.com/2022/06/23/code-coverage-in-net/.

OpenCover development has stopped for over 2 years.

This blog post talks about another tool known as Coverlet.

This blog posts has a accompanying Github repo – https://github.com/ALightTechnologyAndServicesLimited/CodeCoverage/

The code has 4 methods regarding DateTime class. And 3 helper methods:

  • double GetUnixEpoch(DateTime)
  • DateTime GetDateTimeFromUnixEpoch(double)
  • long GetYYYMMDD(DateTime)
  • bool IsLeapYear(int)

There are 9 unit tests for the above mentioned 4 methods.

The batch file – RunCodeCoverage.bat has the commands.

The usage is very simple, add the Coverlet nuget package, install the ReportGenerator tool and run some commands. Navigate to the unittest project.

dotnet add package coverlet.collector

dotnet tool install -g dotnet-reportgenerator-globaltool

The nuget package coverlet.collector needs to be added for every unit test project. The reportgenerator can be installed once.

Then run this command for invoking coverlet:

dotnet test --collect:"XPlat Code Coverage"

This generates a xml file under TestResults/[some-random-guid].

This xml file would be used by reportgenerator for generating HTML report:

reportgenerator.exe "-reports:TestResults**.*.xml" "-targetdir:report"

The above command looks for xml files under TestResults/* folder and generates HTML reports under report folder.

The HTML report looks like this. If there is any code that was not covered under unit test, those can be seen easily. The following screenshots have 100% code coverage, but if there is any code not covered, the code would be shown in red.

Mr. Kanti Kalyan Arumilli

Arumilli Kanti Kalyan, Founder & CEO
Arumilli Kanti Kalyan, Founder & CEO

B.Tech, M.B.A

Facebook

LinkedIn

Threads

Instagram

Youtube

Founder & CEO, Lead Full-Stack .Net developer

ALight Technology And Services Limited

ALight Technologies USA Inc

Youtube

Facebook

LinkedIn

Phone / SMS / WhatsApp on the following 3 numbers:

+91-789-362-6688, +1-480-347-6849, +44-07718-273-964

+44-33-3303-1284 (Preferred number if calling from U.K, No WhatsApp)

kantikalyan@gmail.com, kantikalyan@outlook.com, admin@alightservices.com, kantikalyan.arumilli@alightservices.com, KArumilli2020@student.hult.edu, KantiKArumilli@outlook.com and 3 more rarely used email addresses – hardly once or twice a year.

Categories
.Net C# Logging NLog

NLog FallbackGroup Target

In the past I have written few blog posts on NLog and discussed several techniques:

Programatically configuring NLog in C#

NLog in .Net Applications

Some NLog configuration examples for writing into different log management systems

How to log debug level logs only when an exception occurs in ASP.Net using NLog

And I have discussed about a possibility of capturing more information in logs only when needed such as in the case of errors or exceptions in the following blog post:

An abnormal way of logging – open for discussion

My use-case explanation:

I am planning to use Gelf logging for easier compatibility reasons. Gelf logs can be ingested into pretty much every major centralized logging platforms such as: Kibana, GrayLog, Seq, Grafana. Some would require some intermediary software to accept Gelf formatted logs and some can directly ingest Gelf formatted logs. However, for various reasons, sometimes the logging server might not be available, specifically when the log ingestors are not in a cluster. Log files can be easily ingested into the above mentioned centralized logging agents using different sofware.

Based on the above use-case I wanted to use Gelf for directly logging into the centralized logging server and as a failover, I want to write the logs to a file that would get ingested at a later point by some other software.

Now, by combing the previous post example, we can achieve AspNetBuffering and ingest different levels of logs only when errors occur. The code samples should be very easy to understand.

Read the How to log debug level logs only when an exception occurs in ASP.Net using NLog prior to continuing.

<extensions>
    <add assembly="NLog.Web.AspNetCore"/>
    <add assembly="NLog.Targets.Gelf"/>
</extensions>

<targets>
    <target xsi:type="AspNetBufferingWrapper" name="aspnetbuffer" bufferGrowLimit="100000" growBufferAsNeeded="true">
        <target xsi:type="PostFilteringWrapper" defaultFilter="level &gt;= LogLevel.Info">
            <target xsi:type="FallbackGroup" returnToFirstOnSuccess="true">
                <target xsi:type="gelf" endpoint="tcp://logs.local:12201" facility="console-runner" sendLastFormatParameter="true">
                    <parameter name="param1" layout="${longdate}"/>
                    <parameter name="param2" layout="${callsite}"/>
                 </target>
		<target xsi:type="File" fileName="c:\temp\nlog-AspNetCore-all-${shortdate}.log" layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />
              </target>
              <when exists="level &gt;= LogLevel.Warn" filter="level &gt;= LogLevel.Debug"/>
       </target>
    </target>
</targets>

<rules>
    <logger name="*" minlevel="Debug" writeTo="aspnetbuffer" />
</rules>

In the above code we have wrapped Gelf logger, File logger inside a FallBackGroup logger. The FallBackGroup logger is wrapped inside a PostFilteringWrapper. The PostFilteringWrapper is wrapped inside a AspNetBufferingWrapper.

In the above code in the <rules> section we are sending all Debug and above logs to the AspNetBufferingWrapper.

Now AspNetBufferingWrapper buffers the log messages for an entire request, response cycle and sends the log messages to the PostFilteringWrapper.

The PostFilteringWrapper sees if there are any Warnings or above loglevel, if yes sends all the messages that have Debug and above loglevels. Else sends Info and above messages. The target of PostFilteringWrapper is the FallbackGroup logger which receives these messages.

The FallBackGroup logger attempts to use the Gelf logger, if the Gelf logger is unable to process the messages, the logs are sent to the File logger.

Mr. Kanti Kalyan Arumilli

Arumilli Kanti Kalyan, Founder & CEO
Arumilli Kanti Kalyan, Founder & CEO

B.Tech, M.B.A

Facebook

LinkedIn

Threads

Instagram

Youtube

Founder & CEO, Lead Full-Stack .Net developer

ALight Technology And Services Limited

ALight Technologies USA Inc

Youtube

Facebook

LinkedIn

Phone / SMS / WhatsApp on the following 3 numbers:

+91-789-362-6688, +1-480-347-6849, +44-07718-273-964

+44-33-3303-1284 (Preferred number if calling from U.K, No WhatsApp)

kantikalyan@gmail.com, kantikalyan@outlook.com, admin@alightservices.com, kantikalyan.arumilli@alightservices.com, KArumilli2020@student.hult.edu, KantiKArumilli@outlook.com and 3 more rarely used email addresses – hardly once or twice a year.

Categories
.Net C# Logging NLog

How to log debug level logs only when an exception occurs in ASP.Net using NLog

In the past I have written few blog posts on NLog and discussed several techniques:

Programatically configuring NLog in C#

NLog in .Net Applications

Some NLog configuration examples for writing into different log management systems

And I have discussed about a possibility of capturing more information in logs only when needed such as in the case of errors or exceptions in the following blog post:

An abnormal way of logging – open for discussion

This blog post is for Asp.Net Core only.

This should be possible by combining AspNetBufferingWrapper and PostFilteringWrapper.

Sample configuration provided below:

AspNetBufferingWrapper:

AspNetBufferingWrapper buffers all the messages in a ASP.Net request and sends all the messages to the wrapped target.

Remember to set this logger properly. This involves:

  1. Adding the NLog.Web.AspNetCore nuget package
  2. Properly configuring nlog.config file
  3. Registering the middleware
dotnet add package NLog.Web.AspNetCore --version 5.2.1
<extensions>
    <add assembly="NLog.Web.AspNetCore"/>
</extensions>
using NLog.Web;

app.UseMiddleware<NLogBufferingTargetWrapperMiddleware>();

PostFilteringWrapper:

This wrapper evaluates a specified condition and filters logs, then sends the logs to the wrapped target:

<target xsi:type="PostFilteringWrapper" defaultFilter="level &gt;= LogLevel.Info">
    <target .... />
    <when exists="level &gt;= LogLevel.Warn" filter="level &gt;= LogLevel.Debug"/>
</target>

The above configuration by default logs Info and above logs, but if there is a Warn or higher, logs debug or higher. For this to work properly obviously this logger has to receive Debug messages otherwise there is no point in using this logger.

Now combing these two loggers, here is an example:

<extensions>
    <add assembly="NLog.Web.AspNetCore"/>
</extensions>

<targets>
    <target xsi:type="AspNetBufferingWrapper" name="aspnetbuffer" bufferGrowLimit="1000" growBufferAsNeeded="true">
        <target xsi:type="PostFilteringWrapper" defaultFilter="level &gt;= LogLevel.Info">
            <target xsi:type="File" fileName="c:\temp\nlog-AspNetCore-all-${shortdate}.log"
 layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />
        <when exists="level &gt;= LogLevel.Warn" filter="level &gt;= LogLevel.Debug"/>
        </target>
    </target>
</targets>

<rules>
    <logger name="*" minlevel="Debug" writeTo="aspnetbuffer" />
</rules>

Hoping this post helps someone!

Happy development.

Mr. Kanti Kalyan Arumilli

Arumilli Kanti Kalyan, Founder & CEO
Arumilli Kanti Kalyan, Founder & CEO

B.Tech, M.B.A

Facebook

LinkedIn

Threads

Instagram

Youtube

Founder & CEO, Lead Full-Stack .Net developer

ALight Technology And Services Limited

ALight Technologies USA Inc

Youtube

Facebook

LinkedIn

Phone / SMS / WhatsApp on the following 3 numbers:

+91-789-362-6688, +1-480-347-6849, +44-07718-273-964

+44-33-3303-1284 (Preferred number if calling from U.K, No WhatsApp)

kantikalyan@gmail.com, kantikalyan@outlook.com, admin@alightservices.com, kantikalyan.arumilli@alightservices.com, KArumilli2020@student.hult.edu, KantiKArumilli@outlook.com and 3 more rarely used email addresses – hardly once or twice a year.

Categories
.Net ASP.Net NLog

Some NLog configuration examples for writing into different log management systems

This blog post is going to discuss few different useful configurations for writing logs using NLog.

  1. Writing directly into AWS CloudWatch

This can be accomplished by adding the NLog.AWS.Logger nuget package. Nuget link. Link for documentation / github.

Although, NLog.AWS.Logger supports other logging frameworks such as Log4Net, Serilog, those are out of context for the current blog post.

In nlog.config enable the extension:

<extensions>
    <add assembly="NLog.AWS.Logger" />
  </extensions>

Define the logger as a target:

<targets>
    <target name="aws" type="AWSTarget" logGroup="NLog.ConfigExample" region="us-east-1"/>
</targets>

2. Writing logs into ElasticSearch

Use NLog.Targets.ElasticSearch nuget package. Nuget URL. Github / documentation link.

Enable the extension:

<extensions>
    <add assembly="NLog.Targets.ElasticSearch"/>
  </extensions>

Define the target, documentation says preferred to wrap in a buffering wrapper:

<targets>
    <target name="elastic" xsi:type="BufferingWrapper" flushTimeout="5000">
      <target xsi:type="ElasticSearch" uri="http://localhost:9200/" />
    </target>
  </targets>

*Instead of hardcoing IP address or “localhost”, I would say use some name such as “elasticsearch” or “kibana” and then use the HOSTS file for mapping to the actual server. Then even if you have several applications on the same server and if the elasticsearch server gets changed, you don’t have to edit all the config files, you can edit just the hosts file. hosts file is located at /etc/hosts on Linux and C:\Windows\System32\drivers\etc\hosts on Windows.

Now we will discuss about 4 different interesting wrappers:

  1. Buffering Wrapper
  2. Async Wrapper
  3. AspNetBuffering Wrapper
  4. FallbackGroup Wrapper

These 4 loggers are wrappers i.e these loggers don’t write logs directly. Instead they are used to wrap other loggers by providing some interesting functionality that can be used to take advantage based upon necessity and use-case.

  1. Buffering Wrapper

Buffers log events and sends in batches.

As mentioned above in the ElasticSearch example, the wrapper would buffer messages and sends in batches.

Documentation

There is a very interesting use-case by using AutoFlushWrapper with BufferingWrapper and the actual target that writes the logs, such as writing the logs only when error happen.

2. Async Wrapper

When you don’t need buffering but at the same time if you don’t want your application to wait until logging is done, this could be useful.

Documentation

3. AspNetBuffering Wrapper

Waits until the completion of ASP.Net request and then sends the logs to the wrapped target.

Documentation

4. FallbackGroup Wrapper

This wrapper can be used for wrapping around multiple targets. For example ElasticSearch followed by Cloudwatch followed by File. i.e if the logger is unable to write to ElasticSearch, it would write to Cloudwatch, if that too failed it would write the logs into file.

Documentation

Mr. Kanti Kalyan Arumilli

Arumilli Kanti Kalyan, Founder & CEO
Arumilli Kanti Kalyan, Founder & CEO

B.Tech, M.B.A

Facebook

LinkedIn

Threads

Instagram

Youtube

Founder & CEO, Lead Full-Stack .Net developer

ALight Technology And Services Limited

ALight Technologies USA Inc

Youtube

Facebook

LinkedIn

Phone / SMS / WhatsApp on the following 3 numbers:

+91-789-362-6688, +1-480-347-6849, +44-07718-273-964

+44-33-3303-1284 (Preferred number if calling from U.K, No WhatsApp)

kantikalyan@gmail.com, kantikalyan@outlook.com, admin@alightservices.com, kantikalyan.arumilli@alightservices.com, KArumilli2020@student.hult.edu, KantiKArumilli@outlook.com and 3 more rarely used email addresses – hardly once or twice a year.

Categories
.Net ASP.Net C# NLog

An abnormal way of logging – open for discussion

While I have been brainstorming about something, some small idea came to my mind. People who would read this blog post would either call me stooooopid or might say nice idea.

Anyway the point is, we use logging for various purposes – mostly for troubleshooting. Very verbose logs are a nightmare in terms of performance, storage, retrieval and digging through for the right information. Sometimes, issues troubleshooting becomes a pain because of inadequate information in logs.

What if we log Info and above under normal circumstances, trace and / or debug in certain conditions such as unexpected expectations or errors?

Here is a brief overview of how this might be implemented – in this case, there is a slight memory pressure.

  1. Collect trace and / or debug into Memory log i.e for example if using NLog, use Memory target.
  2. Have some static method that writes the logs from Memory target into a different log target such as File / Database etc…
  3. In the specific conditions such as exception call the static method and in ASP.Net even implement a exception filter to perform the same.

This might be a win-win scenario i.e collecting detailed information in case of unexpected exceptions and error, for any other normal scenario normal logging. Because memory target is being used, very small performance hit, slightly higher memory usage are the drawbacks.

I would love to know how other developers are implementing or handling such use cases.

Mr. Kanti Kalyan Arumilli

Arumilli Kanti Kalyan, Founder & CEO
Arumilli Kanti Kalyan, Founder & CEO

B.Tech, M.B.A

Facebook

LinkedIn

Threads

Instagram

Youtube

Founder & CEO, Lead Full-Stack .Net developer

ALight Technology And Services Limited

ALight Technologies USA Inc

Youtube

Facebook

LinkedIn

Phone / SMS / WhatsApp on the following 3 numbers:

+91-789-362-6688, +1-480-347-6849, +44-07718-273-964

+44-33-3303-1284 (Preferred number if calling from U.K, No WhatsApp)

kantikalyan@gmail.com, kantikalyan@outlook.com, admin@alightservices.com, kantikalyan.arumilli@alightservices.com, KArumilli2020@student.hult.edu, KantiKArumilli@outlook.com and 3 more rarely used email addresses – hardly once or twice a year.