Reference to Exercise 1: Debugging an Application in the Cloud in Azure Toolkit
- Global.asax
- WebRole.cs
- Custome Trace Listener - AzureDiagnosticsTableStorageListener implementation
Global.asax
>>Add System.trace Table Storage Listener At Application_Start()
The ConfigureTraceListener method retrieves the EnableTableStorageTraceListener configuration setting and, if its value is true, it creates a new instance of the TableStorageTraceListener class, defined in the project that you added to the solution earlier, and then adds it to the collection of available trace listeners. Note that the method also enables the AutoFlush property of the Trace object to ensure that trace messages are written immediately to table storage, allowing you to retrieve them as they occur.
>>Register "RoleEnvironmentChanged" method to reconfig System.trace Table Storage Listener
>>Add "System.Diagnostics.Trace.TraceError(lastError.Message);" at Application_Error()
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
namespace WebApplication1
{
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
{
configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
});
ConfigureTraceListener();
RoleEnvironment.Changed += RoleEnvironmentChanged;
}
private void RoleEnvironmentChanged(object sender, RoleEnvironmentChangedEventArgs e)
{
// configure trace listener for any changes to EnableTableStorageTraceListener
if (e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>().Any(change => change.ConfigurationSettingName == "EnableTableStorageTraceListener"))
{
ConfigureTraceListener();
}
}
private static void ConfigureTraceListener()
{
bool enableTraceListener = false;
string enableTraceListenerSetting = RoleEnvironment.GetConfigurationSettingValue("EnableTableStorageTraceListener");
if (bool.TryParse(enableTraceListenerSetting, out enableTraceListener))
{
if (enableTraceListener)
{
AzureDiagnostics.TableStorageTraceListener listener =
new AzureDiagnostics.TableStorageTraceListener("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString")
{
Name = "TableStorageTraceListener"
};
System.Diagnostics.Trace.Listeners.Add(listener);
System.Diagnostics.Trace.AutoFlush = true;
}
else
{
System.Diagnostics.Trace.Listeners.Remove("TableStorageTraceListener");
}
}
}
void Application_End(object sender, EventArgs e)
{
// Code that runs on application shutdown
}
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
var lastError = Server.GetLastError();
System.Diagnostics.Trace.TraceError(lastError.Message);
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
}
}
}
WebRole.cs
>>Register "RoleEnvironmentChanging" method to avoid tracing listener configure changing cause web role restart
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
namespace WebRole1
{
public class WebRole : RoleEntryPoint
{
public override bool OnStart()
{
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += RoleEnvironmentChanging;
return base.OnStart();
}
private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
{
// for any configuration setting change except EnableTableStorageTraceListener
if (e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>().Any(change => change.ConfigurationSettingName != "EnableTableStorageTraceListener"))
{
// Set e.Cancel to true to restart this role instance
e.Cancel = true;
}
}
}
}
TableStorageTraceListener && LogEntry Class
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using System.Data.Services.Client;
using System.Threading;
namespace AzureDiagnostics
{
public class TableStorageTraceListener : TraceListener
{
private const string DEFAULT_DIAGNOSTICS_CONNECTION_STRING = "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString";
public static readonly string DIAGNOSTICS_TABLE = "DevLogsTable";
[ThreadStatic]
private static StringBuilder messageBuffer;
private object initializationSection = new object();
private bool isInitialized = false;
private object traceLogAccess = new object();
private List<LogEntry> traceLog = new List<LogEntry>();
private CloudTableClient tableStorage;
private string connectionString;
public TableStorageTraceListener()
: this(DEFAULT_DIAGNOSTICS_CONNECTION_STRING)
{
}
public TableStorageTraceListener(string connectionString)
{
this.connectionString = connectionString;
}
public override bool IsThreadSafe
{
get
{
return true;
}
}
public override void Write(string message)
{
if (TableStorageTraceListener.messageBuffer == null)
{
TableStorageTraceListener.messageBuffer = new StringBuilder();
}
TableStorageTraceListener.messageBuffer.Append(message);
}
public override void WriteLine(string message)
{
if (TableStorageTraceListener.messageBuffer == null)
{
TableStorageTraceListener.messageBuffer = new StringBuilder();
}
TableStorageTraceListener.messageBuffer.AppendLine(message);
}
public override void Flush()
{
if (!this.isInitialized)
{
lock (this.initializationSection)
{
if (!this.isInitialized)
{
Initialize();
}
}
}
TableServiceContext context = tableStorage.GetDataServiceContext();
context.MergeOption = MergeOption.AppendOnly;
lock (this.traceLogAccess)
{
this.traceLog.ForEach(entry => context.AddObject(DIAGNOSTICS_TABLE, entry));
this.traceLog.Clear();
}
if (context.Entities.Count > 0)
{
context.BeginSaveChangesWithRetries(SaveChangesOptions.None, (ar) =>
{
context.EndSaveChangesWithRetries(ar);
}, null);
}
}
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
{
base.TraceData(eventCache, source, eventType, id, data);
AppendEntry(id, eventType, eventCache);
}
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, params object[] data)
{
base.TraceData(eventCache, source, eventType, id, data);
AppendEntry(id, eventType, eventCache);
}
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id)
{
base.TraceEvent(eventCache, source, eventType, id);
AppendEntry(id, eventType, eventCache);
}
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
{
base.TraceEvent(eventCache, source, eventType, id, format, args);
AppendEntry(id, eventType, eventCache);
}
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
{
base.TraceEvent(eventCache, source, eventType, id, message);
AppendEntry(id, eventType, eventCache);
}
public override void TraceTransfer(TraceEventCache eventCache, string source, int id, string message, Guid relatedActivityId)
{
base.TraceTransfer(eventCache, source, id, message, relatedActivityId);
AppendEntry(id, TraceEventType.Transfer, eventCache);
}
private void Initialize()
{
CloudStorageAccount account = CloudStorageAccount.FromConfigurationSetting(this.connectionString);
this.tableStorage = account.CreateCloudTableClient();
this.tableStorage.CreateTableIfNotExist(DIAGNOSTICS_TABLE);
this.isInitialized = true;
}
private void AppendEntry(int id, TraceEventType eventType, TraceEventCache eventCache)
{
if (TableStorageTraceListener.messageBuffer == null)
{
TableStorageTraceListener.messageBuffer = new StringBuilder();
}
string message = TableStorageTraceListener.messageBuffer.ToString();
TableStorageTraceListener.messageBuffer.Length = 0;
if (message.EndsWith(Environment.NewLine))
{
message = message.Substring(0, message.Length - Environment.NewLine.Length);
}
if (message.Length == 0)
{
return;
}
LogEntry entry = new LogEntry()
{
PartitionKey = string.Format("{0:D10}", eventCache.Timestamp >> 30),
RowKey = string.Format("{0:D19}", eventCache.Timestamp),
EventTickCount = eventCache.Timestamp,
Level = (int) eventType,
EventId = id,
Pid = eventCache.ProcessId,
Tid = eventCache.ThreadId,
Message = message
};
lock (this.traceLogAccess)
{
this.traceLog.Add(entry);
}
}
}
}
// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------
namespace AzureDiagnostics
{
using Microsoft.WindowsAzure.StorageClient;
public class LogEntry : TableServiceEntity
{
public long EventTickCount { get; set; }
public int Level { get; set; }
public int EventId { get; set; }
public int Pid { get; set; }
public string Tid { get; set; }
public string Message { get; set; }
}
}
Test project
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Error(object sender, EventArgs e)
{
int i = 0;
int j = 100 / i;
Response.Write(j);
}
}
}
Note: TraceListeners can be added by configuring them in the system.diagnostics section of the configuration file.
<configuration>
<system.diagnostics>
<trace autoflush="true" indentsize="2">
<listeners>
<add name="customAzureTableLog" type="AzureDiagnostics.TableStorageTraceListener,AzureDiagnostics">
</add>
</listeners>
</trace>
</system.diagnostics>
</configuration>