Windows Azure Diagnostics (SDK 1.4)

En este post vamos a pararnos un poco más en profundidad en cada una de las fuentes de diagnóstico disponible a través de Microsoft.WindowsAzure.Diagnostics.dll.
Un punto importante a la hora de utilizar el sistema de diagnósticos de Windows Azure es que este trabaja conjuntamente con Windows Azure Storage, donde vamos a almacenar toda la información que cada una de las fuentes disponibles nos facilite.
Antes de centrarnos en la configuración, veamos qué podemos recuperar de nuestras instancias y dónde se almacena:
 
Fuente Descripción Destino
Windows Azure Logs En estos logs se guardan todas aquellas trazas registradas a través de la clase System.Diagnostics.Trace. Tabla
Performance Counters Se almacenan los contadores de rendimiento configurados previamente. Tabla
Windows Azure Diagnostic Infrastructure Logs Son todos aquellos registros relacionados con el servicio de Windows Azure. Tabla
Windows Event Logs Podemos añadir tanto fuentes predeterminadas de Windows, como System o Application, como aquellas dadas de alta programáticamente. Tabla
Crash Dumps Volcados de memoria de nuestra aplicación. Blob
Custom Error Logs Esta opción nos permite dar de alta nuestros propios logs dentro del proceso de transferencia de Windows Azure Diagnostics para poder recuperar los mismos a través del storage. Blob
IIS 7.0 Logs Podemos recuperar toda la información generada de forma automática relacionada con IIS Blob
Failed Request Logs Almacena información sobre aquellas peticiones fallidas de IIS Blob
Si bien cada una de las fuentes tiene su propia configuración, lo primero que debemos recuperar es la configuración inicial por defecto de los diagnósticos de la siguiente manera:
//We need to get default initial configuration
var config = DiagnosticMonitor.GetDefaultInitialConfiguration();
Generalmente esta configuración se recupera y se modifica en el archivo WebRole.cs o WorkerRole.cs en el método OnStart ya que es el punto de entrada de nuestra aplicación y se ejecuta cuando nuestras instancias se están iniciando.
En los siguientes apartados utilizaremos el objeto config para configurar cada una de las fuentes.

Windows Azure Logs

Esta primera fuente lo que nos va a permitir es almacenar en una tabla, generada de forma automática por el sistema de diagnósticos llamada WADLogsTable, todas aquellas líneas de registro que realicemos en nuestro código a través de System.Diagnostics.Trace, como por ejemplo:
Trace.WriteLine("WorkerRole_Diag entry point called", "Information");
Para poder recuperar estas trazas, debemos añadir el siguiente código:
//Windows Azure Logs. Table: WADLogsTable
config.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
config.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Performance counters

Una de las fuentes más importantes para poder conocer el rendimiento de nuestros sistemas trata de los contadores de rendimiento. Para poder conocer cuál sería el nombre del contador que queremos añadir, podemos localizarlos en nuestro propio sistema:
 
 
Una vez elegidos los que queramos auditar, los agregamos al sistema de diagnóstico de la siguiente manera:
//Performance counters. Table: WADPerformanceCountersTable
 config.PerformanceCounters.DataSources.Add(
    new PerformanceCounterConfiguration
    {
        CounterSpecifier = @"Processor(*)*",
        SampleRate = TimeSpan.FromSeconds(1)
    }
 );

 config.PerformanceCounters.DataSources.Add(
    new PerformanceCounterConfiguration
    {
        CounterSpecifier = @"Memory*",
        SampleRate = TimeSpan.FromSeconds(1)
    }
 );

config.PerformanceCounters.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Windows Azure Diagnostic Infrastructure Log

Cuando trabajamos en local con development fabric podemos visualizar una consola por cada instancia que tengamos de nuestros roles donde nos facilita información de su estado.
 
 
Para poder recuperar esta información utilizaremos DiagnosticInfrastructureLogs para recuperar esta información en forma de tabla, la cual recibirá el nombre WADDiagnosticInfrastructureLogsTable:
//Windows Azure Diagnostic Infrastructure Logs. Table: WADDiagnosticInfrastructureLogsTable
config.DiagnosticInfrastructureLogs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
config.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Windows Event Logs

El visor de eventos de Windows creo que es algo que no necesita presentación :D . También podemos recuperar esta información a excepción de la fuente Security. Estos datos se nos presentarán a través de la tabla WADWindowsEventLogsTable.
//Windows Event Logs. Table: WADWindowsEventLogsTable
config.WindowsEventLog.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
config.WindowsEventLog.DataSources.Add("Application!*");
config.WindowsEventLog.DataSources.Add("System!*");
config.WindowsEventLog.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Crash Dumps

Uno de los clásicos no podía faltar :) Todo lo relacionado con los volcados de memoria se puede conseguir utilizando estas líneas:
//Crash Dumps. Blob. Container: wad-crash-dumps
CrashDumps.EnableCollection(true);
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
En este caso debemos tener en cuenta dos pasos: CrashDumps.EnableCollection nos permite habilitar la recolección tanto de mini dumps (parámetro a false) o full dumps (parámetro a true). Si sólo indicamos esta línea lo que vamos a conseguir es el almacenamiento local de estos volcados de memoria, pero no serán transferidos a nuestro storage. Para conseguir este segundo paso debemos especificar cada cuánto tiempo vamos a realizar la transferencia de los directorios de diagnóstico a través de config.Directories.ScheduledTransferPeriod como se indica en la segunda línea del código anterior.

Custom Error Logs

Imaginemos que ya tenemos un sistema de diagnóstico de terceros que no deseamos modificar, pero aun así queremos recuperar los ficheros generados de todas las instancias para poder procesarlos. En este caso podemos crear nuevos directorios de diagnóstico, lo cual nos va a permitir que los mismos sean transferidos cada cierto tiempo a los blobs de nuestra cuenta de storage:
//Custom Error Logs
var localResource = RoleEnvironment.GetLocalResource("CustomErrorStorage");
var directoryConfiguration = new DirectoryConfiguration
{
    Container = "wad-mycustom-log-container",
    DirectoryQuotaInMB = localResource.MaximumSizeInMegabytes,
    Path = localResource.RootPath
};

config.Directories.DataSources.Add(directoryConfiguration);
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);

IIS Logs

En el caso de los logs de IIS basta con configurar el tiempo de transferencia del apartado Directories, ya que se genera una carpeta donde almacena de forma automática la información:
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Failed Request Logs

Esta fuente está relacionada también con Internet Information Services tras la cual podemos recuperar todas aquellas peticiones fallidas. A excepción del resto, para poder configurar la misma debemos modificar el archivo web.config de nuestro web role para añadir la sección tracing dentro de system.webServer:
<system.webServer>
   <!-- Blob. Container: wad-iis-failedreqlogfiles -->
     <tracing>
       <traceFailedRequests>
          <add path="*">
             <traceAreas>
                <add provider="ASP" verbosity="Verbose" />
                <add provider="ASPNET"
                     areas="Infrastructure,Module,Page,AppServices"
                     verbosity="Verbose" />
                <add provider="ISAPI Extension" verbosity="Verbose" />
                <add provider="WWW Server"
                areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module"
                verbosity="Verbose" />
            </traceAreas>
            <failureDefinitions timeTaken="00:00:15" statusCodes="400-599" />
          </add>
        </traceFailedRequests>
      </tracing>
 <modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
Al generar esta información dentro de un archivo, necesitamos indicar el tiempo de transferencia de los directorios de diagnóstico como en los casos anteriores:
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);
Nota: Todas aquellas fuentes que se almacenan en blobs generan una carpeta dentro del directorio DiagnosticStore para almacenar los archivos con la información. Para poder ser transferidos necesitamos indicar cada cuánto tiempo queremos que se envíe la información a Windows Azure Storage. Este tiempo de transferencia basta con definirlo una vez para todas las fuentes que usen este sistema.
Para que todos estos cambios tengan efecto, debemos iniciar la nueva configuración tomando como parámetros el nombre de la cadena de conexión que utilizará Windows Azure Diagnostics (por defecto Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString) y el objeto config que hemos personalizado:
//Start with new configuration
DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", config);
Por último, como recomendación, existe una aplicación de Cerebrata llamada Azure Diagnostics Manager la cual nos va a facilitar mucho la interpretación de toda esta información de una forma muy visual y organizada.
Adjunto un proyecto de ejemplo donde podemos probar cada uno de los casos ;)
 
Espero que sea de utilidad :D
 
¡Saludos!

 

Cambiar la edición y el tamaño de nuestras bases de datos en SQL Azure

 

 
Cuando decidimos contratar SQL Azure como servidor de nuestras bases de datos relacionales lo primero que debemos estimar es el tamaño de nuestra base de datos. En la actualidad existen diferentes tamaños disponibles, agrupadas en dos ediciones:
  • Web edition
    • 1 GB
    • 5 GB
  • Business edition
    • 10 GB
    • 20 GB
    • 30 GB
    • 40 GB
    • 50 GB
Cuando queremos dar de alta una base de datos de cualquiera de estos tamaños podemos hacerlo bien a través del portal de Windows Azure:

 

a través de SQL Server 2008 R2 Management Studio:

 
 o bien utilizando directamente la siguiente sentencia SQL:

 

Create DATABASE mybbdd (EDITION='web',MAXSIZE=1GB)

 

Lo más lógico,  cuando utilizamos un modelo Pay as you go, es que vayamos adaptando el tamaño de nuestras base de datos cuando nuestro negocio lo requiera. A día de hoy el portal no nos ofrece una forma visual para modificar las características de las bases de datos existentes, por lo que es necesario modificar las mismas utilizando sentencias SQL. Cuando realizamos una modificación esta puede ser a nivel de edición y/o de tamaño:
ALTER DATABASE mybbdd MODIFY (EDITION='business', MAXSIZE=50GB)
 
ALTER DATABASE mybbdd MODIFY (EDITION='web', MAXSIZE=5GB)
Nota: En realidad, indicando sólo el tamaño es suficiente, ya que la edición será modificada automáticamente de web a business o viceversa si la situación lo requiere.
 
ALTER DATABASE mybbdd MODIFY (MAXSIZE=20GB)

 

 

Espero que sea de utilidad :D
¡Saludos!

Instalar Windows Azure Service Management CmdLets en Windows 7 SP 1 con Windows Azure SDK 1.4

Después de la nueva versión del SDK de Windows Azure y el Service Pack 1 de Windows 7, podemos encontrarnos ciertas trabas si intentamos instalar Windows Azure Service Management CmdLets:

 

 
Esto puede ocurrir principalmente por varios motivos:
  • Tenemos instalado el service pack 1 para Windows 7.
  • Hemos actualizado a Windows Azure SDK 1.4
  • Ambos :D
 Al no tener las nuevas versiones contempladas tanto en uno de los archivos de configuración como en uno de los script de powershell no puede reconocer las nuevas versiones tanto de compilación del sistema operativo como la del SDK. Para solucionarlo, podemos realizar los siguientes pasos:

Añadir el builder number de Windows 7 con sp 1

 
Dentro del directorio de WASM nos ubicamos en WASMCmdletssetup. Dentro de la misma tenemos un archivo llamado Dependencies.dep el cual lo abrimos con el Notepad para añadir el nuevo build number.
 
 
En este archivo podemos ver y modificar todas las dependencias necesarias para la instalación e incluso modificar la descripción y la url donde de donde podemos descargar la dependencia requerida. Centrándonos en el sistema operativo, como vemos en la imagen anterior, vemos que aparece un atributo llamado buildNumber el cual contiene todas las numeraciones de las compilaciones válidas. 7600 se corresponde con Windows 7 sin service pack. ¿Cómo sabemos cuál es la versión de compilación del sistema operativo? La forma más sencilla de llegar a este valor es abriendo una aplicación propietaria de Microsoft integrada en el sistema operativo que tenemos instalado. Por ejemplo, abrimos la aplicación Paint y pulsamos sobre About Paint/Sobre Paint.
 
 
Voilà! En este caso podemos confirmar que 7601 se corresponde con Windows 7 SP 1:D
El siguiente paso se trata de modificar uno de los powershell donde se verifica la versión del SDK de Windows Azure dentro del registro. En la versión anterior, el número anterior se correspondía con la 1.3.11122.0038.
 
 
Para que la comprobación se realice con éxito, necesitamos cambiar el valor de SearchVersion a 1.4.20227.1419 como se muestra en la imagen.
 
 
¡Listo! ya tenemos adaptadas todas las comprobaciones para lanzar de nuevo la instalación.
 
 
Espero que sea de utilidad :D
¡Saludos!

Refrescar un WebGrid cada X tiempo de forma asíncrona

Con la nueva versión ASP.NET MVC 3, disponemos de una nueva extensión para la creación de grids. Para conocer más detalles sobre este nuevo helper podéis consultar el artículo de Jose M. Aguilar :)
En este post me gustaría mostraros cómo es posible actualizar de forma asíncrona un grid cada un número determinado de segundos.

Del lado del servidor he adjuntado una base de datos con una única tabla para el ejemplo que os quiero mostrar:
 

Un repositorio para recuperar los valores:

using System.Linq;

namespace WebGridAsync.Models
{
    public class StuffRepository
    {
        private readonly StuffEntities _model;
        public StuffRepository()
        {
            _model = new StuffEntities();
        }

        public FileViewModel GetFiles(int page = 1)
        {
            var fileViewModel = new FileViewModel();
            var files = _model.Files.ToList();
            fileViewModel.FilesPerPages = 5;
            fileViewModel.NumberOfFiles = _model.Files.Count();
            fileViewModel.Files = files.Skip((page - 1) * fileViewModel.FilesPerPages)
                                       .Take(fileViewModel.FilesPerPages);
            return fileViewModel;
        }

        public void SaveChanges()
        {
            _model.SaveChanges();
        }
    }
}
Y por último un controlador donde lo que vamos a actualizar es el estado de los archivos en cada llamada:
using System.Web.Mvc;
using WebGridAsync.Models;

namespace WebGridAsync.Controllers
{
    public class HomeController : Controller
    {
        private readonly StuffRepository _stuffRepository;
        public HomeController()
        {
            _stuffRepository = new StuffRepository();
        }

        public ActionResult Index(int page = 1)
        {
            return View(GetFilesWithUpdatedStatus());
        }

        public FileViewModel GetFilesWithUpdatedStatus()
        {
            var fileViewModel = _stuffRepository.GetFiles();

            foreach (var myFile in fileViewModel.Files)
            {
                switch (myFile.Status)
                {
                    case "Pending":
                        myFile.Status = "Processing";
                        break;
                    case "Processing":
                        myFile.Status = "Waiting";
                        break;
                    case "Waiting":
                        myFile.Status = "More waiting...";
                        break;
                    case "More waiting...":
                        myFile.Status = "Completed";
                        break;
                    default:
                        myFile.Status = "Pending";
                        break;
                }
            }

            _stuffRepository.SaveChanges();
            return fileViewModel;
        }
    }
}
Por último, necesitamos crear una vista donde hagamos uso del helper WebGrid. Es importante que el mismo esté incluido en un div con un id asociado.
@model WebGridAsync.Models.FileViewModel
@{
    ViewBag.Title = "Async WebGrid";

    WebGrid grid = new WebGrid(rowsPerPage: Model.FilesPerPages, canSort: false);
    grid.Bind(Model.Files, rowCount: Model.NumberOfFiles);
}
<h2>@ViewBag.Message</h2>
<div id="myWebGrid">
    @grid.GetHtml(columns: new[]{
    grid.Column("Name"),
    grid.Column("Date"),
    grid.Column("Status")
})
</div>
Para hacer la llamada asíncrona, necesitamos utilizar algo de código javascript, con la ayuda de JQuery. En la misma página que el grid anterior incluimos lo siguiente:
<script language="javascript" type="text/javascript">

    $(document).ready(function () {
        $.ajaxSetup({
            cache: false
        });

        setTimeout(reloadAsync, 5000);
    });

    function reloadAsync() {
        $("#myWebGrid").load("/Home/Index #myWebGrid", function (response, status, xhr) {
            if (status == "error") {
                var msg = "Sorry but there was an error: ";
                $("#error").html(msg + xhr.status + " " + xhr.statusText);
            }
            else {
                setTimeout(reloadAsync, 5000);
            }

        });

    }
</script>
Lo que estamos haciendo en realidad es inicializar un timer para que cada 5 segundos solicite los datos al servidor. El método load de JQuery va a realizar una llamada a la acción Index del controlador Home para que este le devuelva la página renderizada. Una vez obtiene el resultado va a localizar el elemento div myWebGrid para reemplazar el que se está visualizando actualmente por el nuevo resultado.
Adjunto el proyecto por si fuera de utilidad :D
 
¡Saludos!