Azure App Service (Web Apps) の Python アプリのログ - Application Insights と診断ログ

:::note warn

Application Insights の Python SDK は 本記事でご紹介している OpenCensus 版は2024年９月のリタイアが発表されており、[**OpenTelemetry 版**](https://learn.microsoft.com/ja-jp/azure/azure-monitor/app/opentelemetry-enable?tabs=python)への移行が推奨されています。

OpenTelemetry 版の使い方については、[**こちらの記事**](https://qiita.com/YoshiakiOi/items/c4de9b1ffc01ecf292f9)をご参照ください。
:::


# 本記事について

Azure で Python のウェブアプリケーションを展開する時、コンテナー化して Container Apps や AKS (Azure Kubernetes Service) にデプロイすることもできますが、より容易に実行できる環境として Azure App Service (Web Apps) が挙げられます。

本記事では、その App Service (Web Apps) に Python のアプリケーションを展開したとき、どのようにログを保管・活用できるか、Application Insights と診断ログという2つの方式をそれぞれ見ていきます。

# 利用するサンプルについて

App Service クイックスタートにある Python アプリ (Flask アプリ) を利用します。

https://learn.microsoft.com/ja-jp/azure/app-service/quickstart-python?tabs=flask%2Cwindows%2Cazure-cli%2Cvscode-deploy%2Cdeploy-instructions-azportal%2Cterminal-bash%2Cdeploy-instructions-zip-azcli

# App Service におけるログ収集について

## ログ収集サービスの概要とイメージ

App Service 上のアプリログの収集において、利用できるのは Application Insights と診断ログ機能になります。

App Service と両者の構成イメージは下記になります。（あくまでイメージであり実際のアーキテクチャがこうなっているというわけではありません。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/30b4bc0f-db93-a87b-4653-dde31b6d7e7b.png)


Application Insights は Azure の APM のサービスです。Python の場合は App Service プラットフォームでの有効化には対応していないため、OpenCensus のライブラリを利用し、Application Insights にログ出力を行うようアプリケーションコード内で設定します。

https://learn.microsoft.com/ja-jp/azure/azure-monitor/app/opencensus-python

一方、診断ログはプラットフォーム側でログを取得する仕組みです。アプリケーションがコンソール出力するログやゲートウェイが記録する HTTP リクエストのログ、プラットフォーム側の監査ログなどを Azure Monitor の Log Analytics ワークスペースに出力できます。

https://learn.microsoft.com/ja-jp/azure/app-service/monitor-app-service

https://learn.microsoft.com/ja-jp/azure/app-service/monitor-app-service-reference

# 実際のログ収集設定

診断ログ、Application Insights のそれぞれについてログ出力の設定を見ていきます。

## 診断ログ

App Service の診断ログは画面上の診断設定で簡単に行えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/d6c4bebc-2b9d-77b5-aa35-4eaf5c16ba6f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/93e301e4-d1b3-02e4-f0f9-277d8f2c9c2e.png)

## Application Insights

Application Insights にログを出力するには、Python アプリの場合は、コードの修正が必要です。Application Insights を先に (Log Analytics ワークスペースベースで) 作っておきます。そして接続文字列を控えておきます。そして、上記のサンプルの app.py ファイルに下記を追加します。（本番環境では、接続文字列は Key Vault に保存するなどしてください。）

```python:app.py
from opencensus.ext.azure.log_exporter import AzureLogHandler
logger = logging.getLogger(__name__)
logger.addHandler(AzureLogHandler(connection_string="<Application Insights の接続文字列>"))
```

また、requirements.txt に opencensus-ext-azure を追加しておきます。

```text:requirement.txt
opencensus-ext-azure
```

そして、logger を使ってログ出力をします。

今回は診断ログと Application Insights のログ出力を比較する目的もあり、下記のようにしてみます。print メソッドでコンソールに出力した文字列がログとして取りこまれるか、logger のハンドラーで streamHandler と AzureLogHandler を追加したときにそれぞれどう扱われるか、を確認したいと思います。

```python:app.py
import os
import logging
from opencensus.ext.azure.log_exporter import AzureLogHandler

from flask import (Flask, redirect, render_template, request,
                   send_from_directory, url_for)

app = Flask(__name__)

fmt = "%(asctime)s %(levelname)s %(name)s %(message)s"
logging.basicConfig(format=fmt)

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())
logger.addHandler(AzureLogHandler(connection_string="<Application Insights の接続文字列>"))

@app.route('/')
def index():
   print('<print メソッド> Request for index page received')
   logger.info("<logger> Request for index page received")
   return render_template('index.html')

@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('/hello', methods=['POST'])
def hello():
   name = request.form.get('name')

   if name:
       print('<print メソッド> Request for hello page received with name=%s' % name)
       logger.info("<logger> Request for hello page received with name=%s" % name)
       return render_template('hello.html', name = name)
   else:
       print('<print メソッド> Request for hello page received with no name or blank name -- redirecting')
       logger.info("<logger> Request for hello page received with no name or blank name -- redirecting")
       return redirect(url_for('index'))


if __name__ == '__main__':
   app.run()
```

# 収集されたログの確認

## 診断ログ

診断ログは数種類のテーブルに分かれて収集されます。各テーブルのスキーマや説明については下記公式ドキュメントをご確認ください。

https://learn.microsoft.com/ja-jp/azure/app-service/monitor-app-service-reference#resource-logs

コンソール出力のログは AppServiceConsoleLogs に書き込まれます。実際にテーブルの中身を見てみると、print メソッドと logger で出力された内容が入っていることが確認できます。加えて、それ以外にアプリケーションがコンソールに出力している内容も取りこまれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/971f44be-5e1c-b4b9-5eb3-740c470501b3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/008664af-83e6-f62d-2eaf-7aade2ec4980.png)

一方、AppServiceHTTPLogs には HTTP のリクエストログが記録されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/cdb117ab-57f7-fb5d-5984-3ce49a4f71a1.png)

とりあえずアクセスログをとってみたいという場合はこちらが便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/1aaef324-c783-b503-1220-818e99296807.png)

また、AppServicePlatform ログにはコンテナーのログが入っています。App Service の Linux 版はコンテナーベースなのでその基盤のログになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/01289c73-af5d-23cf-d928-4fc213c343e9.png)

## Application Insights 

Application Insights のログは、Application Insights の画面から検索できます。（実体は Log Analytics ワークスペースに入っています。) アプリケーションログはその traces テーブルで確認できます。こちらは、logger 経由で出力されたログだけが入っています。その分、AppServiceConsoleLogs よりも限定的で意図されたログだけを扱うことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/8cd28151-059f-6d95-00f2-305db598da2e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276225/6bdce1ce-49e3-ca2c-89b2-21898a9b31d8.png)

# Application Insights についての補足

今回はログ取得のみにフォーカスして記載しましたが、Application Insights は分散トレーシングはじめ、多くのアプリケーション監視の仕組みを利用できるサービスです。そのため、アプリケーションの監視という観点だと、まずは Application Insights をご導入いただき、足りない部分があれば診断ログで補っていただくというのが良いかと個人的には思います。


# 最後に

本記事では、App Service (Web Apps) 上の Python アプリのログの取得方法と活用を見ていきました。Application Insights と診断ログという Azure Monitor に属する2つの方式を比較し、それぞれでどういったテーブルにログが出力され、どのように見えるかを確認しました。本稿が、Python アプリを App Service (Web Apps) に展開される際の運用設計やトラブルシューティングの一助となれば幸いです。

＊本稿は、個人の見解に基づいた内容であり、所属する会社の公式見解ではありません。また、いかなる保証を与えるものでもありません。正式な情報は、各製品の販売元にご確認ください。