13

Okay don't laugh. In 2005 I read about tracing using System.Diagnostics namespace, it was complicated and I have used log4net and NLog ever since (and so has everyone else).

Today, my app will be hosted on Windows Azure Websites and that uses our old friend, Trace.

http://azure.microsoft.com/en-gb/documentation/articles/web-sites-enable-diagnostic-log/

Smugly, I always used abstractions, IoC, so I'm just writing a new little shim to write using Trace but it only has TraceInformation, TraceWarning and TraceError.

There's some Write* methods but I've not clue where they'll end up and under what circumstances. Horrible API. [gags]

Which method is for verbose/debug level?

Edit: removed "Easy one" from the title. Clearly it is not.

Luke Puplett
  • 42,091
  • 47
  • 181
  • 266

2 Answers2

15

What you are describing is the System.Diagnostics.Trace class, which does have some simple methods to write to diagnostics trace output.

That's far from how powerful the tracing diagnostics are in .NET

The nicest way to do tracing is to create a TraceSource. In a TraceSource class there's a Switch property which in turns has a Level property where you define which levels of verbosity you want for that specific TraceSource. You can make that tracesource listen to all levels:

var ts = new TraceSource("My Verbose Debugger") {Switch = {Level = SourceLevels.All}};

Then to trace something, you trace to that source, where you specify the level, like this:

ts.TraceData(TraceEventType.Verbose, 0, dataToBeTraced);

The 0 is the id of the trace and dataToBeTraced is an object with the specific data you want to trace (it's a params [] object parameter, so you can pass in many objects if you wish).

Now how to use that data? Using a TraceListener which you add to your TraceSource's Listeners collection. You make your own class deriving from TraceListener and override the TraceData method, like this:

class MyTraceListener : TraceListener
{
  public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, params object[] data)
  {
    base.TraceData(eventCache, source, eventType, id, data);
    // do what you want with the objects in the "data" parameter
  }
}

The listener can be shared among many tracesources, and it will only receive the data level that it's TraceSwitch level allows.

System.Diagnostics.Trace uses a listener like this (the DefaultTraceListener) which is added by default to both Debug.Listeners and Trace.Listeners, but internally, it works as I described.

This all might look a bit confusing at first, but it's really powerful, and once you have a set of helper classes... at least I, have stopped using third-party logging libraries and use this to great extent.

As for Azure, this is pure speculation since I've never done any Azure, but I guess you'd configure your tracesource like this in your app.config (maybe web.config? not sure about web), adding a default azure listener for the log:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sharedListeners>
       <add name="AzureListener" type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
           <filter type="" />
       </add>
    </sharedListeners>        
    <sources>
      <source name="MyTraceSource" switchValue="Verbose" >
         <listeners>
            <add name="AzureListener" />
         </listeners>
      </source>
   </sources>
 </system.diagnostics>
</configuration>

"MyTraceSource" is the string name you gave yo your TraceSource in the constructor when doing that via code.

Or you can just create a TraceSource in code like above, and add a Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener to its Listeners collection

Jcl
  • 27,696
  • 5
  • 61
  • 92
  • This is a great answer. I have just learnt the same stuff this morning, regarding `TraceSource.TraceData|Event`. But its the configuration that began to be the next mystery and your discovery of `Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener` might hold the key. Thanks, I'll update later. – Luke Puplett Mar 04 '15 at 11:20
  • As stated, I haven't done any Azure so I'm speculating. However I've been interested in starting so I have tidbits of googled information here and there – Jcl Mar 04 '15 at 11:22
  • I've got it working, without the config, so WAWS must add it itself. If I enable editing via Monaco maybe I'll see it in the web.config. Anyway, I'll now switch to Error level in the management console and see if my `WriteLine` statements are ignored. If not, I'll replace the static `Trace` with a `TraceSource` and see if I can break it and maybe repair it again with config. – Luke Puplett Mar 04 '15 at 11:33
  • Actually, its working such that `Trace.WriteLine` is verbose. But I'm not happy with reusing `TraceError` for fatals, so I'll try using a `TraceSource` now and see what happens. – Luke Puplett Mar 04 '15 at 12:55
3

I wanted to add my own story.

The simple answer to my question is that Trace.WriteLine is effectively verbose. However, .NET tracing is powerful but complicated but I managed to get it working into Table Storage.

  • In the Azure portal, turn on Application Logging (Table Storage) under the Configure tab and setup your table. If you make a new one, it won't appear until you save changes.

  • In your application, you only need to use methods on System.Diagnostics.Trace and WAWS will setup the requisite listener automatically.

My stumbling block has been tracing fatal messages appropriately. Since there is no static method for critical level messages, I am forced to use the methods on TraceSource and pass in my desired level enum.

For example, in my logging abstraction, the level comes in as LoggingLevel.Fatal and I need to call TraceSource.TraceEvent(TraceEventType.Critical, ...

However, just newing-up a TraceSource does nothing since, as Jcl explains, it needs a listener. That's where I'm now stuck.

var listener = (TraceListener)new Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener();

_traceSource = new TraceSource(name, SourceLevels.All);
_traceSource.Listeners.Add(listener);

This needed a reference to Microsoft.WindowsAzure.Diagnostics in the SDK locally but it strangely doesn't compile.

That cast there won't work. I shouldn't even need it anyway.

Error 3 Cannot convert type 'Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener' to 'System.Diagnostics.TraceListener' Evoq.AppName.CoreLib C:\DATA\Code\AppName\Evoq.AppName\Evoq.AppName.CoreLib\Instrumentation\AzureApplicationDiagnosticsLogger.cs 30 28

Strange part is that the RedGate Reflector shows an inheritance chain like this:

TraceListener, v4.0_4.0.0.0__b77a5c561934e089\System.dll
RDEventMonitoringAgentListener, MonAgentListener.dll
DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics.dll

If I reflect my TraceSource class there, its (also) from:

v4.0_4.0.0.0__b77a5c561934e089\System.dll

Odd. I'm giving up now. I have a product to ship. I'll revert to using the static Trace class and come back to it if it starts to cost.

Luke

Luke Puplett
  • 42,091
  • 47
  • 181
  • 266
  • 1
    I know you said you gave up, but check this: http://cryclops.com/2013/09/stupid-simple-azure-logging-for-cloud-services/ They do it by means of adding the `DiagnosticMonitorTraceListener` to a named `TraceSource` by means of adding it to the config file, and it seems to work – Jcl Mar 04 '15 at 15:36
  • Thanks for finding this. You've been a great help. – Luke Puplett Mar 04 '15 at 16:11
  • I'm glad I've been. If you happen to make it work in the end, please share it when you have time... as I said, I'm planning to try azure for some stuff in the future and it might save some headaches :-) – Jcl Mar 04 '15 at 16:49
  • 1
    I'll try and remember (I'm usually pretty good). If anyone else reading this wants to chime-in, please do. Over and out. – Luke Puplett Mar 05 '15 at 15:43
  • I also faced same issue. However I resolved it by adding reference to MonAgentListener assembly, its part of azure SDK. – Dilip Nannaware Jan 06 '16 at 06:16