Building Custom Trace Listener For Azure

Last week, while working with Azure and PowerShell a curious thought came up to build a TraceListener which will write all the trace information to Azure blob storage in a file which can be analyzed later for detailed reports of application diagnostics.

This article focuses on the described approach and let’s see how we can build trace listener.

.NET Tracing Essentials

I am sure that most people going through this document are developers like me and we always leverage Visual Studio’s debugging capabilities to do the step by step analysis of the code we write. But as we know that debugger and all its associated tools / statements like Debug.WriteLine are available only during the debug mode build of the application and we do not have any control over it once we build the application in release mode and deploy it to the production servers.

So to collect all the diagnostic information and to do the analyses of code step by step, .NET introduced a way which is called Tracing. Basically it is nothing but a store which keeps all the diagnostic information about the application. Let me take you all back to older days of ASP.NET web forms development where we used to have a tag at top of each file something like,

  1. <%@ Page Language="C#" AutoEventWireup="false" Trace="true" Codebehind="Test.aspx.cs" Inherits="Demo.Test"%>  
And then we used to see all the tracing information of this particular page by access URL something like, 

http://localhost/Demo/Test.aspx?trace=true

Which used to show a very detailed diagnostic information including Request details and state variables.

Trace Listener Demystified

Now the core concept still remains the same, i.e. trace store contains all the diagnostic information however what has changed is the way you manage this. Before going ahead, let me give you a brief description about how and what can be traced.

It’s quite simple and a one liner, all you need to do is something like this,
  1. Trace.WriteLine("This message would be traced!");  
Or you can classify information based on what you want to trace e.g. Warning or Error,
  1. Trace.TraceWarning("Hey, this is warning!");  
  2. Trace.TraceError("Duh! Error!");  
Now once the information is traced, it’s up to us how we want to process it.

I.e. we can simply pick up all the traced information and dump it to the place wherever we want which is known as listening to the trace and the component who does this job is called as Trace Listener.

Considering common requirements to dump the traced information to some file on file system, Microsoft has already created some out of the box trace listeners which we can consume directly and they do all the work to write that trace information to a file, we don’t have to invest in any of effort to work with System.IO APIs or open close file stream objects.

Now the next question is – how can I use it in my application?

Well, there are two ways to include the trace listener in your application.
  1. The XML way (Configuration settings)

    Simply paste the snippet below in your web / app configuration file and you are done.

    Whenever you will write Trace.WriteLine statement in your application, all the messages will be logged to the application.log file on your D drive.
    1. <configuration>  
    2.   <system.diagnostics>  
    3.     <trace autoflush="true">  
    4.       <listeners>  
    5.         <add name="ooblistener"  
    6.              type="System.Diagnostics.TextWriterTraceListener"  
    7.              initializeData="D:\\application.log" />  
    8.         <remove name="Default" />  
    9.       </listeners>  
    10.     </trace>  
    11.   </system.diagnostics>  
    12.        </configuration>  
  2. The programmatic way

    There is always a way to do everything programmatically (almost) and stands true for adding listener too. All you need to do is include System.Diagnostics namespace in your code and do something like this,
    1. // Creating object of trace listener to add  
    2.         TraceListener ooblistener = new TextWriterTraceListener();  
    3.   
    4.         // Initialization of attributes  
    5.         ooblistener.Name = "ooblistener";  
    6.         ooblistener.Attributes.Add("type""System.Diagnostics.TextWriterTraceListener");  
    7.         ooblistener.Attributes.Add("initializeData""D:\\application.log");  
    8.   
    9.         // Adding trace listeners to existing collection  
    10.  Trace.Listeners.Add(ooblistener);  

Similar to TextWriterTraceListener, there are other out of the box trace listeners available for you to use directly. You can read more about them here.

Tracing in Azure

With this basic information of Tracing, let’s dive deep into the Azure world.

The basic still remains the same in Azure too i.e. tracing can be done and trace listeners can be used to listen it, however the change is in the way you monitor the traced information.

Consider a scenario wherein you are using default out of the box TextWriterTraceListener as shown in example above which will be logging all the traced information to some file on the file system. Now how do you get access to this file unless you are running your application on infrastructure which is in your control?

Most of the time we do host web applications either as PaaS or SaaS in Azure wherein you don’t really manage the infrastructure and have limited accessibility to it so what needs to do in that case? And this is where Azure also offers you some out of the box trace listeners which dump all the trace information to Azure storage.

Let’s see what we get as an out of the box trace listener in Azure.

Trace Listener in Azure

Similar to System.Diagnostics Azure has Microsoft.WindowsAzure.Diagnostics namespace which contains the trace listener known as DiagnosticMonitorTraceListener. As explained, this guys does nothing special except logging all the traced information to Azure storage, specifically inside table storage and this information can be accessed either programmatically or using various tools such as
Cloud Storage Explorer.

This article doesn’t focus on detailed documentation and usage of Azure out of the box diagnostic trace listener and so we will not be going through details of it, however if you are curious to read more about this then you can start with this link here.

Building Custom Trace Listener

Now that we have seen almost all the required basic so let’s get started with building our own custom trace listener. As mentioned earlier, we will be writing this trace listener for specific need i.e. write all the trace information into a file and store it as Azure blob inside Azure storage. To being with – we will need to create a class inheriting from System.Diagnostics.TraceListener namespace. It is an abstract class and you will need to override few of its methods to write your own implementation.

Talk of implementation, we will be doing all the work with Azure storage using managed APIs provided through Nuget package WindowsAzure.Storage.

Let’s see how it look after the implementation,

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Diagnostics;  
  4. using System.Linq;  
  5. using System.Text;  
  6. using Microsoft.Azure;   
  7. using Microsoft.WindowsAzure.Storage;   
  8. using Microsoft.WindowsAzure.Storage.Blob;   
  9.   
  10. namespace Demo.StorageListener  
  11. {  
  12.     public class BlobWriterStorageListener : System.Diagnostics.TraceListener  
  13.     {  
  14.         protected override string[] GetSupportedAttributes()  
  15.         {  
  16.           return new[] { "StorageConnectionString""LogsContainerName""LogFileName" };  
  17.         }  
  18.   
  19.         public override void Write(string message, string category)  
  20.         {  
  21.             string stroageConnectionString = Attributes["StorageConnectionString"];  
  22.             string logsContainerName = Attributes["LogsContainerName"];  
  23.             string logFileName = Attributes["LogFileName"];  
  24.   
  25.             CloudStorageAccount storageAccount = CloudStorageAccount.Parse(stroageConnectionString);  
  26.   
  27.             CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();  
  28.   
  29.             CloudBlobContainer container = blobClient.GetContainerReference(logsContainerName);  
  30.             container.CreateIfNotExists();  
  31.   
  32.             CloudAppendBlob appendBlob = container.GetAppendBlobReference(logFileName);  
  33.             appendBlob.CreateOrReplace();  
  34.   
  35.             appendBlob.AppendText(String.Format("Message:{0} Category:{1} Timestamp: {2:u}", message, category, DateTime.UtcNow));  
  36.   
  37.         }  
  38.   
  39.         public override void WriteLine(string message, string category)  
  40.         {  
  41.             Write(message + "\n", category);  
  42.         }  
  43.   
  44.         public override void Write(string message)  
  45.         {  
  46.             Write(message, null);  
  47.         }  
  48.   
  49.         public override void WriteLine(string message)  
  50.         {  
  51.             Write(message + "\n");  
  52.         }  
  53.     }  
  54. }  
Observe the attributes which will be initialized at declaration time. (Remember how we initialized name and type properties of TextWriterTraceListener), Code is quite simple, it reads the storage connection string and creates the blob file with trace contents. For basic actions to work with Azure storage services, you can refer this great documentation here.

To test the trace listener, let’s create a console application and add declare the trace listener in its configuration file.
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.   <startup>  
  4.     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />  
  5.   </startup>  
  6.   <system.diagnostics>  
  7.     <trace autoflush="true">  
  8.       <listeners>  
  9.         <remove name="default"/>  
  10.         <add name="AzureBlobStorageListener"   
  11. type="Demo.BlobWriterStorageListener, Demo"          StorageConnectionString="storage_connection_string"   
  12.               LogsContainerName="logs"   
  13.               LogFileName="application.log" />  
  14.       </listeners>  
  15.     </trace>  
  16.   </system.diagnostics>  
  17. </configuration>  
Once this is done, let’s write a simple trace message using line below and observe that the message gets logged to the storage account in a blob file application.log.
  1. namespace TestListener  
  2. {  
  3.     class Program  
  4.     {  
  5.         static void Main(string[] args)  
  6.         {  
  7.             Trace.WriteLine("Hey There!!", EventLogEntryType.Information.ToString());  
  8.             Console.ReadLine();  
  9.         }  
  10.     }  
  11. }  
This is how the application.log file looks, (you can use Visual Studio’s Cloud Explorer to explore your storage artifacts).

url

notepad

Note that the solution above is created for a specific need to write all trace information in a blob and may not be the best approach to do it as you might have to take manual efforts to search a particular event in huge log file (or unless you have some pre-built tools to do it for you) and that’s why storing this information in systematic way makes sense which can either be achieved using SQL tables or table storage offerings.


Similar Articles