Azure Functionsの実行時に、そのログをApplication Insightsにデータを送る

# 概要

Azureには[Azure Functions](https://azure.microsoft.com/ja-jp/services/functions/)という[AWS Lambda](https://aws.amazon.com/jp/documentation/lambda/)のようなサービスがあります。
Azure FunctionsはLambdaと同じく、一定時間毎やあるWebHookが呼ばれた際など、なんらかのイベントが発生したときに、サーバー上にあるC#やJavaScript（NodeJS）のコードが実行されるというものです。

その際、Functions自体の動作ログを[Application Insights](https://azure.microsoft.com/ja-jp/services/application-insights/)に送信しておき、Application Insights上で見られたら良いなと思ったので、その方法を調べました。

上手く送信出来るようになると、下図のようにApplication Insightsでログが見られるようになります。
![result.PNG](https://qiita-image-store.s3.amazonaws.com/0/10682/255786c6-6137-8c72-3987-c7f3427dd1a2.png)



# 下準備

## FunctionsでApplication Insightsライブラリを使えるようにする

まずは、Azure FunctionsでApplication Insightsのライブラリを読み込めるようにします。
wwwroot以下に対象Function名の付いたフォルダがあります。そのフォルダ内にproject.jsonを作成し、次のような内容を書いておきます。project.jsonを保存すると、勝手にライブラリがNuGetからダウンロードされ、当該Functionで利用可能になります。
今のところ、Azure Portal上ではproject.jsonを作成できないので、ここだけはKuduを利用したり、Visual Studio経由でアップロードするなどの手間がかかります。

```json
{
  "frameworks": {
    "net46": {
      "dependencies": {
        "Microsoft.ApplicationInsights": "2.1.0"
      }
    }
  }
}
```

## Application InsightsのINSTRUMENTATIONKEYをFunctionsから参照出来るようにする

Azure FunctionsのApp Service Settingsを開き、『アプリケーション設定』の項目で`APPINSIGHTS_INSTRUMENTATIONKEY`というキーを作成し、値にはApplication InsightsのINSTRUMENTATIONKEYを設定します。
![function_app_settings.PNG](https://qiita-image-store.s3.amazonaws.com/0/10682/3abb4560-d2b9-0236-384b-1af8d3e904a4.png)

ここで設定したキーは`Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY")`というコードで参照出来ます。


# 実際にFunctionsのコードから利用する

実際のコードは次のような形です。
ポイントとして、`TelemetryClient`の`Context.Operation.Id`に、当該Functionの`InvocationId`（呼び出しID）を設定しています。
この`InvocationId`はFunctionが呼び出される度にユニークな値となるので、どの呼び出しに関するテレメトリかを後から追跡しやすくなります。Application Insightsも、`Operation.Id`毎にテレメトリをグルーピングしてくれます。

```csharp
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;

public static void Run(TimerInfo myTimer, TraceWriter log, ExecutionContext executionContext)
{
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    var processStartTime = DateTimeOffset.UtcNow;
    var functionSucceed = true;

    var telemetryClient = new TelemetryClient { InstrumentationKey = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY") };
    telemetryClient.Context.Operation.Id = executionContext.InvocationId.ToString();
    telemetryClient.TrackTrace(
                message: "The function has started",
                severityLevel: SeverityLevel.Information,
                properties: new Dictionary<string, string>() {
                    { "somePropertyName", "somePropertyValue"},
                });
    try
    {
        //Functionsで行う処理などを書く
        //適宜telemetryClient.TrackTrace()やtelemetryClient.TrackMetric()などを呼び出してもよい。
    }
    catch (Exception e)
    {
        functionSucceed = false;
        telemetryClient.TrackException(
            exception: e,
            properties: new Dictionary<string, string>() {
                    { "somePropertyName", "somePropertyValue"},
                });
    }
    finally
    {
        stopwatch.Stop();

        telemetryClient.TrackRequest(
            new RequestTelemetry(
                name: "何かわかりやすい名前。このAzureFunctionの名前など設定すること",
                startTime: processStartTime,
                duration: sw.Elapsed,
                responseCode: "",
                success: functionSucceed));
    }
    telemetryClient.Flush();

}
```


# 参考にしたサイト

- [Azure Functions - C# で Nuget パッケージを利用してみた](http://tech.guitarrapc.com/entry/2016/04/05/043723)
- [Azure Functions で実行時のIDを取得する](https://buchizo.wordpress.com/2016/08/04/azure-functions-%E3%81%A7%E5%AE%9F%E8%A1%8C%E6%99%82%E3%81%AEid%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B/)
- [Azure Functions C# developer reference](https://azure.microsoft.com/en-us/documentation/articles/functions-reference-csharp/)
- [TelemetryContext](https://azure.microsoft.com/en-us/documentation/articles/app-insights-api-custom-events-metrics/#telemetrycontext)