Tracing Azure applications from your desktop

One of the big challenges faced with a deployed Azure hosted role is how to get access to tracing information. Well, you can use the RoleManager logging capabilities but this is far from ideal as you have to download the logs and that doesn’t give you real time information.  There is a better way! The .NET Framework already provides the TraceListener that most of you will be familiar with. By creating your own custom TraceListener, you can push trace messages anywhere you like.  Then, by using the magic provided by the service bus for traversing firewalls (see previous post), you can pick up these trace messages in an application running on your desktop.

We need a client to send the messages and a server to receive them. Let’s start with the Azure client. The first thing to do is implement the custom TraceListener:

public class AzureTraceListener : TraceListener
    {
    ITrace traceChannel;

    public AzureTraceListener(string solutionName, string solutionPassword, string servicePath)
    {
        // Create the endpoint address for the service bus
        Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", solutionName, servicePath);
        EndpointAddress endPoint = new EndpointAddress(serviceUri);

        // Setup the authentication
        TransportClientEndpointBehavior credentials = new TransportClientEndpointBehavior();
        credentials.CredentialType = TransportClientCredentialType.UserNamePassword;
        credentials.Credentials.UserName.UserName = solutionName;
        credentials.Credentials.UserName.Password = solutionPassword;

        // Create the channel and open it
        ChannelFactory<ITrace> channelFactory = new ChannelFactory<ITrace>(new NetEventRelayBinding(), endPoint);
        channelFactory.Endpoint.Behaviors.Add(credentials);
        traceChannel = channelFactory.CreateChannel();
    }

    public override void WriteLine(string message)
    {
        traceChannel.WriteLine(message);
    }

    public override void Write(string message)
    {
        traceChannel.Write(message);
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

As you can see, there is some setup stuff for WCF and the service bus, but basically all you have to do is override the Write and WriteLineMethods. The ITrace interface is simple as well:

[ServiceContract]
public interface ITrace
{
     [OperationContract(IsOneWay=true)]
     void WriteLine(string text);

     [OperationContract(IsOneWay = true)]
     void Write(string text);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }Now we need an app to send the messages.  For the purposes of this article, I have created a simple console app, but this could be any Azure role.

static void Main(string[] args)
{
     string solutionName = "yoursolutionname";
     string password = "yourpassword";
     string servicePath = "tracer";

     TraceListener traceListener = new AzureTraceListener(solutionName, password, servicePath);
     Trace.Listeners.Add(traceListener);
     Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));

     while (true)
     {
          Trace.WriteLine("Hello world at " + DateTime.Now.ToString());
          Thread.Sleep(1000);
     }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }This simple app simply creates a new custom TraceListener and adds it to the TraceListener’s collection. I’ve also added Console.Out as another listener so you can see what’s being sent.

So that’s the Azure end done, what about the desktop end? The first thing you have to do is implement the TraceService that the custom listener will call:

    public class TraceService : ITrace
    {
        public static event ReceivedMessageEventHandler RecievedMessageEvent;

        void ITrace.WriteLine(string text)
        {
            RecievedMessageEvent(this, text);
        }

        void ITrace.Write(string text)
        {
            RecievedMessageEvent(this, text);
        }
    }

    public delegate void ReceivedMessageEventHandler(object sender, string message);

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }The event delegate is there to push out the messages to the app hosting this class. Next, we have to create the class that will host the service:

   

public class AzureTraceReceiver
    {
        ServiceHost serviceHost;

        public AzureTraceReceiver(string solutionName, string solutionPassword, string servicePath)
        {
            Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", solutionName, servicePath);

            TransportClientEndpointBehavior credentials = new TransportClientEndpointBehavior();
            credentials.CredentialType = TransportClientCredentialType.UserNamePassword;
            credentials.Credentials.UserName.UserName = solutionName;
            credentials.Credentials.UserName.Password = solutionPassword;

            serviceHost = new ServiceHost(typeof(TraceService));

            ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(typeof(ITrace), new NetEventRelayBinding(), serviceUri);
            endpoint.Behaviors.Add(credentials);
        }

        public void Start()
        {
            serviceHost.Open();
        }

        public void Stop()
        {
            serviceHost.Close();
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }This is basic WCF code, nothing special here. All we do is create some credentials for authenticating with the service bus, create an endpoint, add the credentials and start up the service host.

Now all we have to do is implement the desktop app. Again, for simplicity, I am creating a simple console app:

     

   static void Main(string[] args)
        {
            Console.Write("AZURE Trace Listener Sample started.\nRegistering with Service Bus...");

            string solutionName = "yoursolutionname";
            string solutionPassword = "yourpassword";
            string servicePath = "tracer";

            // Start up the receiver
            AzureTraceReceiver receiver = new AzureTraceReceiver(solutionName, solutionPassword, servicePath);
            receiver.Start();

            // Hook up the event handler for incoming messages
            TraceService.RecievedMessageEvent += new ReceivedMessageEventHandler(TraceService_myEvent);

            // Now, just hang around and wait!
            Console.WriteLine("DONE\nWaiting for trace messages...");
            string input = Console.ReadLine();
            receiver.Stop();
        }

        static void TraceService_myEvent(object sender, string message)
        {
            Console.WriteLine(message);
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }This app simply instantiates the receiver class and starts the service host. An event handler is registered and then just waits for messages. When the client sends a trace message the event handler fires and the message is written to the console.

You may have noticed that I have used the NetEventRelayBinding for the service bus. This was deliberate as it allows you to hook up multiple server ends to receive the messages in a classic pub/sub pattern. This means you can run multiple instances of this server on multiple machines and they all receive the same messages. You can use other bindings if you required. Another advantage of this binding is that you don’t have to have any apps listening, but bear in mind, after commercial release, the messages you send to the service bus will be chargable whether you are listening or not, although you wont have to pay for the outbound bandwidth. I put all the WCF and service bus setup into the code, but this could easily be place into a configuation file. I prefer it this way as I have a blind spot when it comes to reading WCF config in xml and I always get it wrong.

So that’s it. You now have the ability to monitor your Azure roles from anywhere.

If you want the full solution, then drop me an email.

Postscript: Having built this code, I subsequently discovered that Christian Weyer has done something similar. It’s nice to know that others are suffering the same problems.

Advertisements