Tuesday 22 April 2014

Creating Performance Counters in Azure Worker Roles



Creating Performance Counters in Azure Worker Roles

I have written a number of work roles and wanted to monitor these roles. Using the ms-windows framework the best approach is to create a custom performance counters. That’s fine for a windows hosted service but how do I do this in an Azure worker role. Plus how do I provide the performance counters metrics in the azure monitor page.

This blog will explain how.

Now the first bit of code is the performance counter creation. Seeing as though this is a custom performance counter we will need to create it on the OnStart method. I created a method called InstallPerformanceCounters and here is the code below.

        public bool InstallPerformanceCounters()
        {
            if (PerformanceCounterCategory.Exists("ALAzurePerformanceCounter"))
            {
                PerformanceCounterCategory.Delete("ALAzurePerformanceCounter");
            }

            if (!PerformanceCounterCategory.Exists("ALAzurePerformanceCounter"))
            {
                logger.Info("ALAzurePerformanceCounter performance counter category does not exist.");

                CounterCreationDataCollection counters = new CounterCreationDataCollection();

                // 1. counter for counting totals: PerformanceCounterType.NumberOfItems64
                CounterCreationData totalMessageCounts = new CounterCreationData();
                totalMessageCounts.CounterName = "MessageCount";
                totalMessageCounts.CounterHelp = "Total number of messages processed";
                totalMessageCounts.CounterType = PerformanceCounterType.NumberOfItems64;
                counters.Add(totalMessageCounts);

                // create new category with the counters above
                logger.Info("Creating ALAzurePerformanceCounter performance counters");

                PerformanceCounterCategory.Create("ALAzurePerformanceCounter", "ALAzurePerformanceCounter Worker Role", PerformanceCounterCategoryType.MultiInstance, counters);
                logger.Info("Created ALAzurePerformanceCounter performance counters");
            }
            else
            {
            }
            return true;
        }
                                                          
­­The second bit of code updates the diagnostic configuration so we can view the performance counter in the azure portal.

        private static void ConfigureDiagnostics()
        {
            try
            {
                CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"));

                var roleInstanceDiagnosticManager = CloudAccountDiagnosticMonitorExtensions.CreateRoleInstanceDiagnosticManager(
                    RoleEnvironment.GetConfigurationSettingValue("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"),
                           RoleEnvironment.DeploymentId,
                           RoleEnvironment.CurrentRoleInstance.Role.Name,
                           RoleEnvironment.CurrentRoleInstance.Id);

                var roleDiagnosticMonitorConfiguration = roleInstanceDiagnosticManager.GetCurrentConfiguration();

                // Copy settings from configuration.
                double workerRolePeriod;
                if (!double.TryParse(RoleEnvironment.GetConfigurationSettingValue(WorkerRolePeriodName), out workerRolePeriod))
                {
                    Trace.WriteLine("WebRole environment diagnostics error: " + WorkerRolePeriodName + " parse failed.");

                    // Set the default to one minute.
                    workerRolePeriod = 1d;
                }

                // Transfer diagnostic information once every webRolePeriod minutes.
                TimeSpan transferPeriod = TimeSpan.FromMinutes(workerRolePeriod);
                roleDiagnosticMonitorConfiguration.PerformanceCounters.ScheduledTransferPeriod = transferPeriod;

                double workerRoleSampleRate;
                if (!double.TryParse(RoleEnvironment.GetConfigurationSettingValue(WorkerRoleSampleRateName), out workerRoleSampleRate))
                {
                    Trace.WriteLine("WorkerRole environment diagnostics error: " + WorkerRoleSampleRateName + " parse failed.");

                    // Set the default to ten seconds.
                    workerRoleSampleRate = 10d;
                }
                roleDiagnosticMonitorConfiguration.PerformanceCounters.DataSources.Clear();                                roleDiagnosticMonitorConfiguration.PerformanceCounters.DataSources.Add(
                    new PerformanceCounterConfiguration()
                    {
                        CounterSpecifier = @"\ALAzurePerformanceCounter(azure)\MessageCount",
                        SampleRate = TimeSpan.FromSeconds(workerRoleSampleRate)
                    });
                               roleInstanceDiagnosticManager.SetCurrentConfiguration(roleDiagnosticMonitorConfiguration);

           
            }
            catch (RoleEnvironmentException rex)
            {
                // The connection string was missing.
                Trace.WriteLine("WorkerRole environment diagnostics error: " + rex.Message);
                logger.ErrorException("WorkerRole:Exception", rex);

            }
            catch (InvalidOperationException iox)
            {
                // Parse of the connection string failed.
                Trace.WriteLine("WorkerRole environment diagnostics error: " + iox.Message);
                logger.ErrorException("WorkerRole:Exception", iox);
            }
        }

The final code is the OnStart and OnRun whereby the OnStart is the first method called on initialisation and calls the above methods whilst the OnRun executes in a loop until shutdown. The OnRun updates the performance counter in this case it’s a counter.

        public override bool OnStart()
        {
            // Set the maximum number of concurrent connections
            ServicePointManager.DefaultConnectionLimit = 12;
            try
            {
                // Setup to handle service configuration changes at runtime.
                // For information on handling configuration changes
                // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
                // Not Implemented in this case.
                //-RoleEnvironment.Changing += this.RoleEnvironmentChanging;
                //-RoleEnvironment.Changed += this.RoleEnvironmentChanged;
               
                InstallPerformanceCounters();

                // This is a sample worker implementation. Replace with your logic.
                DiagnosticMonitorConfiguration diagnosticConfig = DiagnosticMonitor.GetDefaultInitialConfiguration();

                // Transfer diagnostic information once every minute.
                TimeSpan transferPeriod = TimeSpan.FromMinutes(1d);
                diagnosticConfig.PerformanceCounters.ScheduledTransferPeriod = transferPeriod;

                // Set the diagnostic monitor configuration.
                DiagnosticMonitor.Start(WADConnectionString, diagnosticConfig);

                ConfigureDiagnostics();
            }
            catch (Exception exc)
            {
                logger.ErrorException("WorkerRole:Exception", exc);
            }

            return base.OnStart();
        }

        public override void Run()
        {
            // This is a sample worker implementation. Replace with your logic.
            logger.Info("PerformanceCounter entry point called");

            string Instance = "azure";

            try
            {
                System.Diagnostics.PerformanceCounter _perfMessageCount = new System.Diagnostics.PerformanceCounter();//"ALAzurePerformanceCounter", "MessageCount", Instance, false);

                if (!PerformanceCounterCategory.InstanceExists(Instance, "ALAzurePerformanceCounter"))
                {
                    _perfMessageCount.CategoryName = "ALAzurePerformanceCounter";
                    _perfMessageCount.InstanceName = Instance;
                    _perfMessageCount.CounterName = "MessageCount";
                    _perfMessageCount.MachineName = ".";
                    _perfMessageCount.InstanceLifetime = PerformanceCounterInstanceLifetime.Global;
                    _perfMessageCount.ReadOnly = false;
                }

                while (true)
                {
                    _perfMessageCount.Increment();
                    Thread.Sleep(10000);
                    logger.Info("Working");
                }
            }
            catch (Exception exc)
            {
                logger.ErrorException("WorkerRole:Exception", exc);
            }
        }

For this demonstration the C# code above is sufficient to illustrate the usage of the performance counter for the worker role.
Now we need to modify the azure worker role configuration.

1: We need to set the azure worker role to at a higher privileges.
2: We need to configure the azure diagnostic storage to an azure storage account. The azure monitor page diagnostics will look at the storage to retrieve the data.
3: We need to configure the diagnostic configuration to include the performance counter that we want to monitor
4: Set Azure Diagnostic to verbose in the Azure Portal.
Azure Worker Role Configuration

Step 1:
Modify ServiceDefinition.csdef
<ServiceDefinition name="ALPerformanceCounter" xmlns="" schemaVersion="2014-01.2.3">
  <WorkerRole name="PerformanceCounter" vmsize="Small">
    <Runtime executionContext="elevated" />
Step 2:
Modify ServiceConfiguration.Cloud.cscfg to include the diagnostic azure storage. Make sure you use https!!!
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=https;AccountName=alperformance;AccountKey=XXXXX” />
Step 3:
Modify diagnostics.wadcfg to include the performance counter
<?xml version="1.0" encoding="utf-8"?>
<DiagnosticMonitorConfiguration configurationChangePollInterval="PT1M" overallQuotaInMB="4096" xmlns="http://schemas.microsoft.com/ServiceHosting/2010/10/DiagnosticsConfiguration">
  <DiagnosticInfrastructureLogs />
  <Directories>
    <IISLogs container="wad-iis-logfiles" />
    <CrashDumps container="wad-crash-dumps" />
  </Directories>
  <Logs bufferQuotaInMB="1024" scheduledTransferPeriod="PT1M" scheduledTransferLogLevelFilter="Error" />
  <PerformanceCounters bufferQuotaInMB="512" scheduledTransferPeriod="PT1M">
    <PerformanceCounterConfiguration counterSpecifier="\ALAzurePerformanceCounter(azure)\MessageCount" sampleRate="PT1M" />
  </PerformanceCounters>
  <WindowsEventLog bufferQuotaInMB="1024" scheduledTransferPeriod="PT1M"

scheduledTransferLogLevelFilter="Error">
    <DataSource name="Application!*" />
  </WindowsEventLog>
</DiagnosticMonitorConfiguration>

This can also be done in visual studio.

 
Edit the project worker role.

 
Step 4:
Inside the Azure portal. Ensure the monitoring level is verbose.


Step 5:
Add the metric in the dashboard.


Step 6:
View the performance counter in the monitor page



Step 7:
Select the performance counter to create a rule.
This allows you to alert the support desk if there is an issue.

Enjoy.