In this blog, I will demonstrate new diagnostics features of the new SDK (April 2012, v2.0) in a sample application. All the diagnostics instrumentation will be done on built-in features by modifying configuration settings.  This is well aligned with “Configure before Customize” approach.

 

Requirements:

The Solution

  • Started VS
  • File –> New Projects
  • Selected “Cloud” template
clip_image001
  • Added 2 projects from left selection targeting .NET 4.5 and Windows Azure SDK v2.0
clip_image002
  • Renamed the projects by clicking on top of the selected project names
clip_image003
  • Selected a template for web application
clip_image004
  • Added a task controller to the web app
   1: using System.Linq;
   2: using System.Web.Mvc;
   3: using ToDoCommon;
   4: using ToDoData.Models;
   5:  
   6: namespace ToDoListWeb2.Controllers
   7: {
   8:     //[Authorize]
   9:     //[InitializeSimpleMembership]
  10:     public class TaskController : Controller
  11:     {
  12:         //
  13:         // GET: /Home/
  14:  
  15:         public ActionResult Index()
  16:         {
  17:             Util.TestTrace2();
  18:             using (var context = new ToDoContext())
  19:                 return View(context.ToDoItems.ToList());
  20:         }
  21:  
  22:         //
  23:         // GET: /Home/Details/5
  24:  
  25:         public ActionResult Details(int id = 0)
  26:         {
  27:             using (var context = new ToDoContext())
  28:                 return View(context.ToDoItems.Find(id));
  29:         }
  30:     }
  31: }
   
  • Added respective Views
 
   1: @model IEnumerable<ToDoData.Models.ToDoItem>
   2:  
   3: @{
   4:     ViewBag.Title = "Index";
   5:     Layout = "~/Views/Shared/_Layout.cshtml";
   6: }
   7:  
   8: <h2>Index</h2>
   9:  
  10: <p>
  11:     @Html.ActionLink("Create New", "Create")
  12: </p>
  13: <table>
  14:     <tr>
  15:         <th>
  16:             @Html.DisplayNameFor(model => model.Name)
  17:         </th>
  18:         <th>
  19:             @Html.DisplayNameFor(model => model.Description)
  20:         </th>
  21:         <th>
  22:             @Html.DisplayNameFor(model => model.Rank)
  23:         </th>
  24:         <th>
  25:             @Html.DisplayNameFor(model => model.IsComplete)
  26:         </th>
  27:         <th></th>
  28:     </tr>
  29:  
  30: @foreach (var item in Model) {
  31:     <tr>
  32:         <td>
  33:             @Html.DisplayFor(modelItem => item.Name)
  34:         </td>
  35:         <td>
  36:             @Html.DisplayFor(modelItem => item.Description)
  37:         </td>
  38:         <td>
  39:             @Html.DisplayFor(modelItem => item.Rank)
  40:         </td>
  41:         <td>
  42:             @Html.DisplayFor(modelItem => item.IsComplete)
  43:         </td>
  44:         <td>
  45:             @Html.ActionLink("Edit", "Edit", new { id=item.ItemId }) |
  46:             @Html.ActionLink("Details", "Details", new { id=item.ItemId }) |
  47:             @Html.ActionLink("Delete", "Delete", new { id=item.ItemId })
  48:         </td>
  49:     </tr>
  50: }
  51:  
  52: </table>
       
  • Created a dummy test method in a utility class
   1: using System;
   2: using System.Diagnostics;
   3: using System.Text;
   4: using Microsoft.WindowsAzure.ServiceRuntime;
   5:  
   6: namespace ToDoCommon
   7: {
   8:     public class Util
   9:     {
  10:         public static void TestTrace2(string info = "")
  11:         {
  12:             Trace.TraceInformation("Dummy entry. Trace.TraceInformation from {0} {1}",
  13:                                    RoleEnvironment.CurrentRoleInstance.Role.Name, info);
  14:             Trace.TraceWarning("Dummay entry. Trace.TraceWarning from {0} {1}",
  15:                                RoleEnvironment.CurrentRoleInstance.Role.Name, info);
  16:             try
  17:             {
  18:                 throw new ArgumentException("Dummy exception", new ArgumentException("Inner dummy exception..."));
  19:             }
  20:             catch (Exception ex)
  21:             {
  22:                 var sb =
  23:                     new StringBuilder(string.Format("Dummy entry. Trace.TraceError from {0} {1}",
  24:                                                     RoleEnvironment.CurrentRoleInstance.Role.Name, info));
  25:                 sb.AppendLine();
  26:                 var innerEx = ex.InnerException;
  27:                 while (innerEx != null)
  28:                 {
  29:                     sb.AppendFormat("\t Inner Exc Message: {0}\r\n", innerEx.Message);
  30:                     innerEx = innerEx.InnerException;
  31:                 }
  32:  
  33:                 sb.AppendLine("StackTrace: " + ex.StackTrace);
  34:                 Trace.TraceError(sb.ToString());
  35:             }
  36:         }
  37:     }
  38: }

 

Ok, time to run the cloud service. Then browse the Task page where some dummy traces are passed to listener as sampled just above this.

clip_image005

Where are the Logs?

All the diagnostics logs are written to files in "C:\Users\[your_user_account]\AppData\Local\” path. Please note, you need change your folder settings, since the AppData is hidden. Here, DevelopmentStorage and dftmp folder are the locations where all the application related logs are written.

From the folder above, the logs matching selected log level (Error by default) are persisted into the storage table (WADLogsTable, etc). To see the entries, you can use different tools (storage explorer, etc.), I am going to use VS for this purpose as well. To do that, click on View on VS’ top menu, then Server Explorer (or Ctrl + W). On the Server Explorer panel, click on Windows Azure Storage then Development (local run), then Tables and "WADLogsTable" (storage table name).

clip_image006

 

How it works?

As you can see, diagnostics are already enabled for both web and worker roles. If you look at the web.config in the web application, you will see that a trace listener called 'AzureDiagnostics' also added (web role and the associated web application runs on different processes).

clip_image007

You can see that from GUI as well by right click on a role then Properties:

clip_image008

Now, we know the reason why error-type logs are written only, because, by default, it is set to 'Errors only'. Diagnostics settings here are reflected in the diagnostics configuration file called 'diagnostics.wadcfg'.

How-to Change Settings

Now, if you want to change the settings, please follow the steps picture below:

clip_image009

(1): Role diagnostics configuration window

For example, when I change log level to Information and enable both Event and Performance counters logs, I am able to see all the logs (except Verbose) for application, performance and events, as seen below:

clip_image010

clip_image011

And you can change the setting while your application running (alive!)

And Deployment

Please note that before going to deploy the solution, in regards to diagnostics feature context and the image (1) - "Role diagnostics configuration window" above:

  • You need to update storage account accordingly. You should use a separate diagnostic storage table for PROD environment
  • Set the log level for errors initially, when troubleshooting, you can set the level accordingly depending on implemented diagnostics features on your application (Information, Verbose, etc.).
  • Set the buffer size and transfer intervals (less buffer/interval means, less amount of data and more often data transfer from the local folder to storage table. Please refer this link fore more details: http://msdn.microsoft.com/en-us/library/windowsazure/dn186185.aspx.

I have deployed the solution here. As said, before and sampled in picture below, I am able to see the diagnostic data from VS and to change the configuration settings –alive- if needed.

image 

 

Conclusion

Software applications in general should have diagnostics enabled. This is exclusively critical for those running in Azure, because they are most likely a composite of applications running in different platforms and networks/locations. Health of the system (the overall application) is dependent on health of each its subsystems and to identify the cause of bugs (and, no mistake they occurs) when alive, you need to instrument your solution with meaningful diagnostics features, at least when passing the message one domain to another.

I have written several blogs about diagnostics features:

Helpful links: