André Alves de Lima

Talking about Software Development and more…

Application Insights no Windows Forms

Você conhece o Application Insights? Não? Então você não sabe o que está perdendo. O Application Insights é um dos serviços da Microsoft disponíveis no Azure que tem como objetivo o monitoramento de aplicações nas mais diversas plataformas de desenvolvimento (.NET, Java, PHP, etc).

Como de costume, a Microsoft facilitou a vida dos programadores web criando um pacote do NuGet que implementa o monitoramento automático de sites ASP.NET MVC. Ao adicionar esse pacote, sua aplicação web estará sendo automaticamente monitorada pelo Application Insights, registrando número de requisições, falhas em requisições, tempo de resposta, etc. Meu amigo Renato Groffe já escreveu um artigo muito bom mostrando como monitorar aplicações web com o Application Insights.

Porém, como é que podemos utilizar o Application Insights em aplicações desktop (por “aplicação desktop” eu quero dizer aplicação que roda 100% no desktop, e não os apps universais para Windows 10)? Nesse caso, até que não é tão difícil, mas, como sempre, existe muito pouco conteúdo demonstrando esse cenário. E foi justamente devido a essa deficiência que eu resolvi mostrar neste artigo como utilizar o Application Insights no Windows Forms.

Criando o recurso do Application Insights no Azure

Antes de começar a monitorar o nosso aplicativo, temos que criar o recurso do Application Insights no Azure. Para isso, você precisa, obviamente, de uma conta no Azure. Se você ainda não tem uma, crie uma conta “trial, que te dá uma quantidade específica de crédito para você utilizar gratuitamente por um mês. Após o período “trial“, a conta é convertida em “pay as you go“, ou seja, você paga somente o que você utilizar. Para o Application Insights isso não é um problema, pois a versão mais simples dele é totalmente gratuita.

Uma vez tendo a sua conta no Azure, vá até o portal de administração (portal.azure.com) e crie um novo recurso do Application Insights:

A única informação que precisamos para ligar a nossa aplicação com o recurso do Application Insights é a chave de instrumentação. Essa informação está disponível dentro das configurações do recurso do Application Insights, na categoria “Properties“. Copie essa informação, pois precisaremos dessa chave de instrumentação quando formos configurar o Application Insights no nosso projeto Windows Forms:

Criando o aplicativo de exemplo

Com a chave de instrumentação em mãos, chegou a hora de criarmos o nosso aplicativo de exemplo. Além de possibilitar o monitoramento automático de exceções não tratadas, com o Application Insights nós podemos monitorar eventos, como a abertura de formulários, cliques em botões, etc.

Pensando nisso, para demonstrar as opções de monitoramento do Application Insights, vamos criar um novo projeto do tipo “Windows Forms Application“. Feito isso, ajuste o layout do formulário que foi criado automaticamente pelo Visual Studio, de forma que ele fique parecido com a imagem abaixo:

A ideia desse aplicativo de exemplo é muito simples. Uma vez clicado em um dos botões “Ação“, um MessageBox será exibido com o texto do botão clicado. Por outro lado, quando clicamos no botão “Exception“, lançaremos uma nova exceção não tratada (throw Exception) para conferirmos como funciona o monitoramento automático de exceções não tratadas do Application Insights.

No code-behind do formulário, vamos criar o handler para o evento “Click” dos botões “Ação 1-4“:

        // C#
        private void acaoButton_Click(object sender, EventArgs e)
        {
            var botao = sender as Button;
            if (botao != null)
            {
                MessageBox.Show(botao.Text);
            }
        }
    ' VB.NET
    Private Sub AcaoButton_Click(sender As Object, e As EventArgs)
        Dim botao = TryCast(sender, Button)
        If (botao IsNot Nothing) Then
            MessageBox.Show(botao.Text)
        End If
    End Sub

Depois disso, vá até a janela de propriedades de cada um dos botões de ação e escolha o método que acabamos de criar para o evento “Click” dos botões:

Feito isso, vamos implementar a funcionalidade do botão “Exception“, que disparará uma exceção quando for clicado:

        // C#
        private void exceptionButton_Click(object sender, EventArgs e)
        {
            throw new Exception("Descrição da exception não tratada");
        }
    ' VB.NET
    Private Sub exceptionButton_Click(sender As Object, e As EventArgs) Handles exceptionButton.Click
        Throw New Exception("Descrição da exception não tratada")
    End Sub

Execute a aplicação e veja o resultado ao clicar nos botões “Ação“:

E ao clicar no botão “Exception“:

Logando informações no Application Insights

Agora que já temos a nossa aplicação de exemplo pronta, vamos entender como podemos adicionar o log de informações no Application Insights. Nesse caso, nós iremos logar três tipos de informações: abertura da janela, clique nos botões de ação e exceções não tratadas (logado automaticamente pela biblioteca do Application Insights).

Primeiramente, temos que adicionar uma referência à biblioteca do Application Insights via NuGet. Para isso, vá até a tela de gerenciamento de pacotes do NuGet, procure por “Application Insights“, encontre o item “Application Insights Windows Server” e escolha a opção para instalá-lo:

Ao adicionarmos esse pacote, um novo arquivo será adicionado no projeto, o ApplicationInsights.config:

Abra esse arquivo e adicione no final dele a tag “InstrumentationKey” contendo a chave de instrumentação:

<InstrumentationKey>SUA_CHAVE_DE_INSTRUMENTACAO</InstrumentationKey>

Com isso, temos o Application Insights configurado e pronto para logar informações da nossa aplicação. Nesse ponto, qualquer exceção do nosso sistema que for lançada e não tratada já será automaticamente logada no nosso recurso do Application Insights. Além disso, se estivéssemos trabalhando com uma aplicação web, o Application Insights logaria automaticamente diversas outras informações, como seções iniciadas, tempo de resposta, views não encontradas, etc.

Porém, apesar do log de exceções não tratadas ser muito útil para encontrarmos potenciais problemas nos nossos aplicativos, evidentemente esse tipo de informação não é o suficiente para conseguirmos entender a utilização do nosso sistema. Seria interessante sabermos quando e quantas vezes cada um dos formulários do nosso aplicativo foram abertos e botões foram clicados, não é mesmo?

Não se preocupe. Nós conseguimos logar esse tipo de informação no Application Insights também. Para isso, a primeira coisa que temos que fazer é criar uma instância de TelemetryClient no code-behind do nosso formulário:

    // C#
    public partial class Form1 : System.Windows.Forms.Form
    {
        private Microsoft.ApplicationInsights.TelemetryClient _telemetryClient = new Microsoft.ApplicationInsights.TelemetryClient();

        public Form1()
        {
            InitializeComponent();
        }
    }
' VB.NET
Public Class Form1
    Private TelemetryClient As New Microsoft.ApplicationInsights.TelemetryClient
End Class

Depois disso, vamos fazer um override do método Load do formulário para configurarmos o TelemetryClient com o nome do usuário e o tipo de sistema operacional que está sendo utilizado. Dessa forma, essas informações serão logadas automaticamente com todos os eventos daqui para a frente. Com isso, nós conseguiremos fazer estatísticas por usuário e sistema operacional (por exemplo, quantas vezes o usuário X clicou no botão Y):

        // C#
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            _telemetryClient.Context.User.Id = Environment.UserName;
            _telemetryClient.Context.Device.OperatingSystem = Environment.OSVersion.ToString();
        }
    ' VB.NET
    Protected Overrides Sub OnLoad(e As EventArgs)
        MyBase.OnLoad(e)

        TelemetryClient.Context.User.Id = Environment.UserName
        TelemetryClient.Context.Device.OperatingSystem = Environment.OSVersion.ToString()
    End Sub

Agora chegou a hora de logar a informação de que o formulário foi aberto. Esse tipo de informação deve ser logada com o método “TrackPageView” do TelemetryClient, passando o nome do formulário que foi aberto. Faremos isso no override do método OnShown:

        // C#
        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);

            _telemetryClient.TrackPageView(this.Name);
        }
    ' VB.NET
    Protected Overrides Sub OnShown(e As EventArgs)
        MyBase.OnShown(e)

        TelemetryClient.TrackPageView(Me.Name)
    End Sub

E como é que podemos fazer o log do clique dos botões? Simples! Basta utilizarmos o método “TrackEvent” do TelemetryClient, passando o texto do botão que foi clicado. Veja a chamada de “TrackEvent” logo após a exibição da MessageBox:

        // C#
        private void acaoButton_Click(object sender, EventArgs e)
        {
            var botao = sender as Button;
            if (botao != null)
            {
                MessageBox.Show(botao.Text);
                _telemetryClient.TrackEvent(botao.Text);
            }
        }
    ' VB.NET
    Private Sub AcaoButton_Click(sender As Object, e As EventArgs) Handles acao4Button.Click, acao3Button.Click, acao2Button.Click, acao1Button.Click
        Dim botao = TryCast(sender, Button)
        If (botao IsNot Nothing) Then
            MessageBox.Show(botao.Text)
            TelemetryClient.TrackEvent(botao.Text)
        End If
    End Sub

Porém, tudo isso que fizemos até agora será em vão se não chamarmos o método “Flush” do TelemetryClient. Esse é o método que faz com que as informações logadas em memória sejam mandadas para o nosso recurso do Application Insights no Azure. Chamaremos esse método no override de OnClosing:

        // C#
        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            _telemetryClient.Flush();
        }
    ' VB.NET
    Protected Overrides Sub OnClosing(e As System.ComponentModel.CancelEventArgs)
        MyBase.OnClosing(e)

        TelemetryClient.Flush()
    End Sub

Movendo o código para um formulário “base”

Tudo isso parece muito bonito até agora, mas, da forma que demonstrei aqui acima, estamos logando somente as informações de um formulário. Além disso, se incluirmos mais botões nesse formulário, teremos que adicionar a chamada de telemetria no evento “Click” de cada um desses botões. Será que não dá para fazer isso de uma forma mais automatizada? Dá sim! E é isso que eu vou mostrar agora para você.

Vamos criar um formulário “base” que deverá ser utilizado como “pai” de todos os formulários do nosso sistema. Nesse formulário adicionaremos todo o código para lidar com a telemetria automaticamente nos formulários herdados. Chamemos esse formulário de “BaseForm“.

No formulário base, vamos adicionar todo o código relacionado a telemetria que vimos na seção anterior para configurarmos o usuário, sistema operacional, log quando abrirmos o formulário e chamada do “Flush” quando estivermos fechando o formulário:

    // C#
    public partial class BaseForm : Form
    {
        private Microsoft.ApplicationInsights.TelemetryClient _telemetryClient = new Microsoft.ApplicationInsights.TelemetryClient();

        public BaseForm()
        {
            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            _telemetryClient.Context.User.Id = Environment.UserName;
            _telemetryClient.Context.Device.OperatingSystem = Environment.OSVersion.ToString();
        }

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            _telemetryClient.TrackPageView(this.Name);
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);
            _telemetryClient.Flush();
        }
    }
' VB.NET
Public Class BaseForm
    Private TelemetryClient As New Microsoft.ApplicationInsights.TelemetryClient

    Protected Overrides Sub OnLoad(e As EventArgs)
        MyBase.OnLoad(e)

        TelemetryClient.Context.User.Id = Environment.UserName
        TelemetryClient.Context.Device.OperatingSystem = Environment.OSVersion.ToString()
    End Sub

    Protected Overrides Sub OnShown(e As EventArgs)
        MyBase.OnShown(e)

        TelemetryClient.TrackPageView(Me.Name)
    End Sub

    Protected Overrides Sub OnClosing(e As System.ComponentModel.CancelEventArgs)
        MyBase.OnClosing(e)

        TelemetryClient.Flush()
    End Sub
End Class

Até aqui tudo certo, não é mesmo? É basicamente o mesmo código que utilizamos anteriormente. Porém, como podemos fazer para que esse formulário base lide também com o log dos botões? Para isso, podemos percorrer os controles do formulário no método OnLoad e, ao encontrarmos um botão, adicionamos um handler para o evento “Click” e logamos a ação com o TelemetryClient:

        // C#
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            _telemetryClient.Context.User.Id = Environment.UserName;
            _telemetryClient.Context.Device.OperatingSystem = Environment.OSVersion.ToString();

            foreach (Control control in this.Controls)
            {
                RegistrarAcaoDeTelemetria(control);
            }
        }

        private void RegistrarAcaoDeTelemetria(Control controle)
        {
            if (controle is Button)
                RegistrarAcaoDeTelemetria((Button)controle);
        }

        private void RegistrarAcaoDeTelemetria(Button botao)
        {
            botao.Click += (s, e) => _telemetryClient.TrackEvent(botao.Text);
        }
    ' VB.NET
    Protected Overrides Sub OnLoad(e As EventArgs)
        MyBase.OnLoad(e)

        TelemetryClient.Context.User.Id = Environment.UserName
        TelemetryClient.Context.Device.OperatingSystem = Environment.OSVersion.ToString()

        For Each control As Control In Me.Controls
            RegistrarAcaoDeTelemetria(control)
        Next
    End Sub

    Private Sub RegistrarAcaoDeTelemetria(control As Control)
        If (TypeOf (control) Is Button) Then
            RegistrarAcaoDeTelemetria(TryCast(control, Button))
        End If
    End Sub

    Private Sub RegistrarAcaoDeTelemetria(botao As Button)
        AddHandler botao.Click, Sub(s As Object, e As EventArgs)
                                    TelemetryClient.TrackEvent(botao.Text)
                                End Sub
    End Sub

Pronto! Agora você pode herdar o formulário Form1 de BaseForm e remover dele toda a parafernália do Application Insights.

Analisando as informações no Application Insights

OK, até aqui vimos como fazer para logar as informações no Application Insights, mas, como é que podemos analisar essas informações? Para isso, temos que ir até o recurso do Application Insights que criamos no portal do Microsoft Azure e clicarmos em “Metrics Explorer“:

Dentro do Metrics Explorer nós podemos montar gráficos e tabelas com as informações que foram logadas, como, por exemplo, a quantidade de cliques em cada um dos botões e a quantidade de vezes que cada formulário foi aberto:

Para ver a listagem de todas as informações que foram logadas, podemos clicar também em “Search“:

Note que, como mencionei anteriormente, as exceções são logadas automaticamente. Dá até para clicar na exceção e ver a call stack!

Muito ninja, né?

Tópicos futuros

Olhando tudo isso que eu apresentei neste artigo, você pode ter ficado com algumas dúvidas. Eu convido você a deixa-las registradas na área de comentários, dessa forma eu posso tentar, na medida do possível, te ajudar melhor. Entretanto, tem algumas perguntas que eu já vou listar aqui e deixar em aberto para abordá-las em artigos futuros.

Primeiramente, neste artigo eu só mostrei como logar cliques nos botões do formulário. E se eu quiser logar outros tipos de evento? Seria possível criarmos outras versões do método “RegistrarAcaoDeTelemetria” recebendo o tipo de controle que você quer tratar. Porém, uma opção melhor ainda seria criar uma estrutura de providers ou plug-ins utilizando o MEF (Managed Extensibility Framework). Vou deixar esse tópico para um outro artigo.

Outra dúvida muito comum é: e se eu estiver rodando o meu aplicativo em um computador que não tiver conexão constante com a Internet? Não tem problema! Nesse caso podemos utilizar um canal de persistência que acumulará as informações em disco e fará o envio somente quando uma conexão estiver disponível. Também vou deixar para mostrar isso em um próximo artigo.

E, por fim, outra dúvida que possa surgir é sobre os outros métodos do TelemetryClient:

Neste artigo eu só mostrei a utilização do TrackEvent e TrackPageView. Em um próximo artigo da série Application Insights eu mostrarei para você a funcionalidade dos outros métodos.

Concluindo

Logar informações e métricas é uma atividade fundamental para entendermos o que está acontecendo com os nossos aplicativos. Com esse tipo de informação conseguimos saber quais funcionalidades estão sendo mais utilizadas e, quem sabe, quais funcionalidades o usuário não está nem utilizando. Sabe aquele método sinistro que você demorou três semanas para implementar? Será que o usuário está realmente utilizando? Se você não logar as informações de utilização do sistema você nunca vai saber!

Além disso, temos também o log de exceções, que é indispensável para conseguirmos reproduzir os bugs de forma mais fácil, o que nos faz economizar tempo (e consequentemente dinheiro $$$) no suporte.

Se você ainda não tem uma camada de log de ações e métricas na sua aplicação, o Application Insights cai como uma luva. Ele implementa o log automático de exceções, além de possibilitar o log de eventos customizados, tornando possível saber exatamente quais usuários estão utilizando quais funcionalidades.

O único problema dele é que 99% dos artigos escritos sobre o Application Insights abordam a sua utilização em sites web. Este artigo veio justamente para tentar corrigir essa deficiência, mostrando como utilizar o Application Insights no Windows Forms. Portanto, agora não tem mais desculpa. Não perca tempo e comece agora mesmo a coletar métricas nos seus aplicativos desktop.

Você gostou das funcionalidades do Application Insights? Pegou o exemplo deste artigo e aplicou no seu projeto? Então conte para gente nos comentários como é que foi a sua experiência com ele e quais foram os ganhos que você obteve.

Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado e ficará sabendo também em primeira mão sobre o artigo da próxima semana, além de receber dicas “bônus” que eu só compartilho por e-mail. Inscreva-se utilizando o formulário logo abaixo.

Até a próxima!

André Lima

Newsletter do André Lima

* indicates required



Powered by MailChimp

5 thoughts on “Application Insights no Windows Forms

  • Renato Groffe disse:

    Excelente post André! Claro e bem objetivo.

    E muito obrigado por divulgar meu trabalho aqui!

    Abs

    • andrealveslima disse:

      Fala Renato! Muito obrigado pelo comentário! E parabéns mais uma vez pelo seu artigo sobre Application Insights também..

      Abraço!
      André Lima

  • […] que esse processo pode ser muito facilitado ao utilizarmos o Application Insights, como eu já demonstrei no passado. Mas, em alguns casos acabamos preferindo fazer a implementação desse log de forma […]

  • Marcus Couto disse:

    Excelente artigo. Ajudou bastante.
    Teria mais algum artigo ou referência sobre como utilizar o Application Insights usando MEF para criação de um plugin desacoplado como mencionou?

    obrigado

    • andrealveslima disse:

      Olá Marcus, obrigado pelo comentário!

      Infelizmente ainda não consegui tirar um tempo para pesquisar e escrever sobre esses assuntos mais avançados do Application Insights.. Na verdade, esse artigo teve pouquíssimos acessos, aí acabei dando prioridade para outros assuntos mais procurados..

      Mas, o que você está querendo construir na sua aplicação? Você tem alguma familiaridade com o MEF ou nunca utilizou?

      Abraço!
      André Lima

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *