Part 2 – Observability for your azd-compatible app

Posted by

This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Community Hub.

In Part 1, I walked you through how to azdev-ify a simple Python app. In this post, we will:

  • add the Azure resources to enable the observability features in azd
  • add manual instrumentation code in the app 
  • create a launch.json file to run the app locally and make sure we can send data to Application Insights
  • deploy the app to Azure

 

Previously…

We azdev-ified a simple Python app: TheCatSaidNo and deployed the app to Azure. Don’t worry if you have already deleted everything. I have updated the code for part 1 because of the Bicep modules improvements we shipped in the azure-dev-cli_0.4.0-beta.1 release. You don't need to update your codes, just start from my GitHub repository (branch: part1):

  1. Make sure have the pre-requisites installed:
  2. In a new empty directory, run azd up -t https://github.com/puicchan/theCatSaidNo -b part1​

    If you run `azd monitor --overview` at this point, you will get an error - “Error: application does not contain an Application Insights dashboard.” That’s because we didn’t create any Azure Monitor resources in part 1,

 

Step 1 - add Application Insights

The Azure Developer CLI (azd) provides a monitor command to help you get insight into how your applications are performing so that you can proactively identify issues. We need to first add the Azure resources to the resource group created in part 1.

  1. Refer to a sample, e.g., ToDo Python Mongo. Copy the directory /infra/core/monitor to your /infra folder.
  2. In main.bicep: add the following parameters. If you want to override the default azd naming convention, provide your own values here. This is new since version 0.4.0-beta.1. param applicationInsightsDashboardName string = '' param applicationInsightsName string = '' param logAnalyticsName string = ''​
  3. Add the call to monitoring.bicep in /core/monitor// Monitor application with Azure Monitor module monitoring './core/monitor/monitoring.bicep' = { name: 'monitoring' scope: rg params: { location: location tags: tags logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' } }
  4. Pass the application insight name as a param to appservice.bicep in the web module: applicationInsightsName: monitoring.outputs.applicationInsightsName
  5. Add output for the App Insight connection string to make sure it’s stored in the .env file:output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString​
  6. Here's the complete main.biceptargetScope = 'subscription' @minLength(1) @maxLength(64) @description('Name of the the environment which is used to generate a short unique hash used in all resources.') param environmentName string @minLength(1) @description('Primary location for all resources') param location string // Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,: // "resourceGroupName": { // "value": "myGroupName" // } param appServicePlanName string = '' param resourceGroupName string = '' param webServiceName string = '' param applicationInsightsDashboardName string = '' param applicationInsightsName string = '' param logAnalyticsName string = '' // serviceName is used as value for the tag (azd-service-name) azd uses to identify param serviceName string = 'web' @description('Id of the user or app to assign application roles') param principalId string = '' var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName } // Organize resources in a resource group resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: !empty(resourceGroupName) ? resourceGroupName : '${abbrs.resourcesResourceGroups}${environmentName}' location: location tags: tags } // The application frontend module web './core/host/appservice.bicep' = { name: serviceName scope: rg params: { name: !empty(webServiceName) ? webServiceName : '${abbrs.webSitesAppService}web-${resourceToken}' location: location tags: union(tags, { 'azd-service-name': serviceName }) applicationInsightsName: monitoring.outputs.applicationInsightsName appServicePlanId: appServicePlan.outputs.id runtimeName: 'python' runtimeVersion: '3.8' scmDoBuildDuringDeployment: true } } // Create an App Service Plan to group applications under the same payment plan and SKU module appServicePlan './core/host/appserviceplan.bicep' = { name: 'appserviceplan' scope: rg params: { name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}' location: location tags: tags sku: { name: 'B1' } } } // Monitor application with Azure Monitor module monitoring './core/monitor/monitoring.bicep' = { name: 'monitoring' scope: rg params: { location: location tags: tags logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' applicationInsightsName: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' applicationInsightsDashboardName: !empty(applicationInsightsDashboardName) ? applicationInsightsDashboardName : '${abbrs.portalDashboards}${resourceToken}' } } // App outputs output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId output REACT_APP_WEB_BASE_URL string = web.outputs.uri output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString
  7. Run `azd provision` to provision the additional Azure resources
  8. Once provisioning is complete, run `azd monitor --overview` to open the Application Insight dashboard in the browser.

    The dashboard is not that exciting yet. Auto-instrumentation application monitoring is not yet available for Python appHowever, if you examine your code, you will see that:

    • APPLICATIONINSIGHTS_CONNECTION_STRING is added to the .env file for your current azd environment.
    • The same connection string is added to the application settings in the configuration of your web app in Azure Portal:web.png

 

Step 2 - manually instrumenting your app

Let’s track incoming requests with OpenCensus Python and instrument your application with the flask middleware so that incoming requests sent to your app is tracked. (To learn more about what Azure Monitor supports, refer to setting up Azure Monitor for your Python app.)

 

For this step, I recommend using Visual Studio Code and the following extensions:

Get Started Tutorial for Python in Visual Studio Code is a good reference if you are not familiar with Visual Studio Code.

 

  1. Add to requirements.txtpython-dotenv opencensus-ext-azure >= 1.0.2 opencensus-ext-flask >= 0.7.3 opencensus-ext-requests >= 0.7.3​
  2. Modify app.py to: import os from dotenv import load_dotenv from flask import Flask, render_template, send_from_directory from opencensus.ext.azure.trace_exporter import AzureExporter from opencensus.ext.flask.flask_middleware import FlaskMiddleware from opencensus.trace.samplers import ProbabilitySampler INSTRUMENTATION_KEY = os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING") app = Flask(__name__) middleware = FlaskMiddleware( app, exporter=AzureExporter(connection_string=INSTRUMENTATION_KEY), sampler=ProbabilitySampler(rate=1.0), ) @app.route("/favicon.ico") def favicon(): return send_from_directory( os.path.join(app.root_path, "static"), "favicon.ico", mimetype="image/vnd.microsoft.icon", ) @app.route("/") def home(): return render_template("home.html") if __name__ == "__main__": app.run(debug=True)​
  3. To run locally, we need to read from the .env file to get the current azd environment context. The easiest is to customize run and debug in Visual Studio Code by creating a launch.json file:
    • Ctrl-Shift+D or click “Run and Debug” in the sidebar
    • Click “create a launch.json file” to customize a launch.json file
    • Select “Flask Launch and debug a Flask web application
    • Modify the generated file to: { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Python: Flask", "type": "python", "request": "launch", "module": "flask", "env": { "FLASK_APP": "app.py", "FLASK_DEBUG": "1" }, "args": [ "run", "--no-debugger", "--no-reload" ], "jinja": true, "justMyCode": true, "envFile": "${input:dotEnvFilePath}" } ], "inputs": [ { "id": "dotEnvFilePath", "type": "command", "command": "azure-dev.commands.getDotEnvFilePath" } ] }​
  4. Create and activate a new virtual environment . I am using Windows. So: py -m venv .venv .venv\scripts\activate pip3 install -r ./requirements.txt​
  5. Click the Run view in the sidebar and hit the play button for Python: Flask
    • Browse to http://localhost:5000 to launch the app.
    • Click the button a few times and/or reload the page to generate some traffic.

    Take a break; perhaps play with your cat or dog for real. The data will take a short while to show up in Application Insights.

  6. Run `azd monitor --overview` to open the dashboard and notice the change dashboard.png
  7. Run `azd deploy` to deploy your app to Azure and start monitoring your app!

 

Get the code for this blog post here. Next, we will explore how you can use `azd pipeline config` to set up GitHub action to deploy update upon code check in.

 

Feel free to run `azd down` to clean up all the Azure resources. As you saw, it’s easy to get things up and running again. Just `azd up`!

 

We love your feedback! If you have any comments or ideas, feel free to add a comment or submit an issue to the Azure Developer CLI Repo.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.