Azureの小ネタ (改)

~Azureネタを中心に、色々とその他の技術的なことなどを~

Application Insights for Java 3.2 が GA してた

別のBlogとかにちょっと浮気していたら、コチラに書くのは一年以上たってしましたが、今回はApplication Insights についてです。

あまりウォッチしていない間に、3.2が GAしていて、現時点で3.2.3 がリリース中です。前回、Application Insights にふれたときは、3.0 が Preview 中でしたの、リリースが重ねられ改良されてきたことがうかがえます。

JavaからAzure Application Insightsを使う(3) - Azureの小ネタ (改)

おさらい

3.0 系のApplication Insights は エージェント式で、なんらアプリケーションに組み込まなくても問題ありません。Java起動時に、-javaagent:/applicationinsights.jar のようにライブラリを指定すればかってにJVMの仕組みをつかって、メトリクスやログをフックして出力してくれます。

さらに、Web Apps/Functionsでは、 プラットフォーム上で組み込んでくれるので、なんら意識することすらありませんでした。

とはいえ、アプリケーションに組み込む 2.6系と比べて、劣る部分もあって、3.0系のエージェント方式では独自のテレメトリを送信できませんでした。どうするかというと、3.0系のエージェントを組み込みつつ、2.6系のSDKでカスタムするという、2.6系と3.0系を混合するような方法しかありませんでした。これでも、Application Insights 側がよしなにやってくれるので、問題はなかったのです。

カスタムテレメトリ対応

ようやく 3.2 でカスタムテレメトリに対応しました。リリース履歴をみると、PREVIEW機能として提供されていたっぽいのですが知りませんでした。 3.0系では、アプリケーションに組み込むような Application Insights SDK は提供されないそうで、標準APIでの対応となるそうです。そこで使われるのが Open Telemetry とのことです。

github.com

このOpen Telemetry をつかってカスタムテレメトリに対応するそうですが、現時点ですべての機能に対応しているわけではなさそうで、2.6系のSDKにくらべて、Open Telemetry は対応がすくないです。

f:id:StateMachine:20211111230106p:plain

https://docs.microsoft.com/en-us/azure/azure-monitor/app/java-in-process-agent より

ユースケースとしては、リクエストテレメトリに何らかの情報を追加したい場合が多いので、とりあえずこれがサポートされたことは喜ばし感じです。

試してみる

現時点で、WebApps で利用される Application Insights for Javaのバージョンは、 3.1.1 ですので、すぐに利用することはできません。どうしてもというなら、コンテナ化してデプロイするとよいでしょう。ただし、コンテナ側のApplication Insights をDisable にする必要があると思います(適当)

Docsをみると依存は、 1.6.0 でしたが、最新の1.7.1 で問題なさそうです。

        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-api</artifactId>
            <version>1.7.1</version>
        </dependency>

リクエストテレメトリのカスタムディメンジョンにプロパティを追加するには、io.opentelemetry.api.trace.Span から current を取り出し属性を追加すれば、このリクエストに対応するテレメトリに記録されます。

    @GetMapping("/")
    String root() {
        Span.current().setAttribute("foo","bar");
        Span.current().setAttribute("user.name","shibayan");
        Span.current().setAttribute("user.age","40");

        retrun ...;
    }

結果、このようにカスタムディメンジョンに記録されます。

f:id:StateMachine:20211111232142p:plain

ライブメトリクス の サンプルテレメトリ

ライブメトリクスはサポートされていましたが、右側のペインに表示されるべき サンプルテレメトリは、長い間対応していませんでしたが、ようやく対応されました。

f:id:StateMachine:20211111231815p:plain

リクエストを送ると、ほぼリアルタイムでテレメトリがあがってきて、ログを眺めるのに便利です。

というわけで、ログは出さないと出ませんので(進次郎構文)、転ばぬ先の杖としてちゃんと設定しておきましょう。

Postgresql のログをApplication Insightsに出力する

.NETフレームワークの場合、SQL DBの情報は勝手にApplication Insightsに送信してくれますが、Postgresの場合は自動で送信してくれません。ちょっと調べたのですが、執筆時点でそういう仕組みとかサンプルも無さそうでした。

逆にJavaなどは、JDBCレベルでApplication Insightsにデータを送信してくれるので、DBがMySQLやPostgresの場合でも問題なく情報を送信してくれます。

ということで、ここでは NET + Postgres を使った場合のデータ送信の方法について試したことを備忘録としての残してオコますが、もっと良いやり方があるかもしれまえんので、ご参考程度に。

Postgres

Windows環境を汚したくないので、Dockerとかでインストールして接続しましょう。以下の例ですと、postgresというDBが作成されます。適当にテーブル作成し、データをINSERTしておきましょう。

docker run --name my-postgres -e POSTGRES_PASSWORD=mypassword -p 5432:5432 -d postgres
docker container exec -it my-postgres bash

NET

Postgresへの接続ライブラリは以下を利用します。

www.npgsql.org

また幸いEntity Frameworkに対応しているので試してみます。以下のコマンドでコンテキストクラスとPOCOクラスが生成されでしょう。

dotnet ef dbcontext scaffold "Host=127.0.0.1;Database=postgres;Username=postgres;Password=mypassword" Npgsql.EntityFrameworkCore.PostgreSQL -o database

ログクラスの実装

ログについては、以下の通りですが、ログプロパイダとログ出力クラス自身を実装する必要があります。

www.npgsql.org

適当にログを垂れ流すサンプルを作成してみました。単純にStringが渡されてくるだけなので、ログに意味を持たせることは難しいです。

以下の実装では、

  • 例外は例外テレメトリへ
  • 普通のメッセージは、トレーステレメトリに出力すればいいのですが、Application Insightsで依存関係を見たい場合は、依存テレメトリにも出力できます(例では2つ書いてかります)

ただ、依存テレメトリ出力する場合、Durationが分らないのでゼロを指定していますが、Azure ポータルで見たときにちょっとかっこ悪いです。

sing System;
using Microsoft.ApplicationInsights;
using Npgsql.Logging;

namespace webapi.Logger
{
    public class PgsqlAppInsightsLoggingProvider : INpgsqlLoggingProvider
    {
        private TelemetryClient _client;
        private NpgsqlLogLevel _minLevel;

        public PgsqlAppInsightsLoggingProvider(TelemetryClient client, NpgsqlLogLevel minLevel)
        {
            _client = client;
            _minLevel = minLevel;
        }

        public NpgsqlLogger CreateLogger(string name)
        {
            return new PgsqlAppInsightLogger(name, _client, _minLevel);
        }
    }

    public class PgsqlAppInsightLogger : NpgsqlLogger
    {
        private string _name;
        private TelemetryClient _client;
        private NpgsqlLogLevel _minLevel;

        public PgsqlAppInsightLogger(string name, TelemetryClient client, NpgsqlLogLevel minLevel)
        {
            _name = name;
            _client = client;
            _minLevel = minLevel;
        }

        public override bool IsEnabled(NpgsqlLogLevel level) => level >= _minLevel;


        public override void Log(NpgsqlLogLevel level, int connectorId, string msg, Exception exception = null)
        {
            if (!IsEnabled(level))
            {
                return;
            }

            if (exception == null)
            {
                _client.TrackTrace(msg);
                _client.TrackDependency("postgres", "sql", msg, DateTimeOffset.UtcNow, TimeSpan.Zero, true);
            }
            else
            {
                _client.TrackException(exception);
            }

            Console.WriteLine(msg);
        }
    }
}

ポータルで確認

依存テレメトリに出した場合は以下ように、リクエスト配下にちゃんと表示されます。3つ出ているのは、Connection Open/クエリ実行/Connection Closeとなっています。

そして、Durationがゼロなので時間軸の帯が正しく出力されなくて、ちょっとかっこ悪いです。

f:id:StateMachine:20201029110840p:plain

まとめ

たんに文字列の垂れ流しなので、いまいちSQL DBと同等のメトリクスにはならないのが残念ですが、もうちょっとましなイベントを拾ったりできるのかもしれません。

パフォーマンスカウンタ機能もあるようなのですが、今後その機能は廃止されそうなので使ってません。

Performance | Npgsql Documentation

Npgsql 3.2 includes support for performance counters, which provide visibility into connections and the connection pool - this helps you understand what your application is doing in real-time, whether there's a connection leak, etc. Npgsql counter support is very similar to that of other ADO.NET providers, such as SqlClient, it's recommended that your read that page first. The feature was removed in Npgsql 5.0.

と言うわけで以上です。

Spring Boot と Azure AD B2Cの連携

前回はAzure Active Directoryで認証を行いましたが、今回はAzure Active Directory B2C(以下B2C)でやっていきたいと思います。

B2Cそのものの説明はしないので、Azure Active Directory B2C のドキュメント | Microsoft Docs あたりを参考にしてください。

連携方法

連携方法としては、Spring Boot の標準ライブラリのみで行うか、Microsoftが提供しているB2C向けStarterを使うかのいずれかになります。

標準ライブラリを使った方法

構成は前回と同じです。同一プロジェクトに設定を同居できますので、プロジェクトを流用すればいいでしょう。次にプロパティファイルに以下を追加します。 azureadb2cという名前でプロバイダを登録します。この名前は任意なので、変更しても構いません。

spring:
  security:
    oauth2:
      client:
        registration:
          azureadb2c:
            client-id: <<b2c client id>>
            client-secret : <<b2c client secret>>
            client-name: Azure Active Directory B2C
            scope: 
            - openid
            - <<b2c client id>>
            client-authentication-method: post
            authorization-grant-type: authorization_code
            redirect-uri: http://localhost:8080/login/oauth2/code/azureadb2c
        provider:
          azureadb2c:
            token-uri: https://<<b2c tenant name>>.b2clogin.com/morisb2c.onmicrosoft.com/<<user flow name>>/oauth2/v2.0/token
            authorization-uri: https://<<b2c tenant name>>.b2clogin.com/morisb2c.onmicrosoft.com/<<user flow name>>/oauth2/v2.0/authorize
            user-name-attribute: name
            jwk-set-uri: https://<<b2c tenant name>>.b2clogin.com/morisb2c.onmicrosoft.com/<<user flow name>>/discovery/v2.0/keys

注意点としては、以下。

  • Scopeには openid の他にクライアントIDを指定しないと認証に失敗します。ここで少しはまりましたが、B2CのDocsにもどこかにこっそり書いてあるはずです。
  • URIの一部にサインイン用のユーザーフロー名が入るので、B2C側で作成したフロー名を指定してください。

この例では、パスワードリセットとユーザープロファイル編集は無視しています。 設定をするとログイン画面にB2C向けのリンクが表示されるはずなので、あとは普通に認証後にログイン出来ると思います。

B2C Starterを使った方法

以下の依存を追加するとB2C Starterを利用できます。

        <dependency>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure-active-directory-b2c-spring-boot-starter</artifactId>
            <version>2.2.5</version>
        </dependency>

この場合は以下の通りプロパティファイルには独自の設定をする必要があります。

azure:
  activedirectory:
    b2c:
      tenant: <<your tenant name>>
      oidc-enabled: true
      client-id: <<client id>>
      client-secret: <<client secret>>
      reply-url: http://localhost:8080/home
      logout-success-url: http://localhost:8080/logout
      user-flows:
        sign-up-or-sign-in: <<sign in flow name>>
        profile-edit: <<profile edit flow name>>
        password-reset: <<password reset flow name>.

こちらのStarterを使うとサインイン、ユーザープロファイル編集、パスワードリセットの3つのフローを設定するだけで扱えるようになります。URIも内部で処理してくれるので面倒くさくはありません。 あと、reply-urlhttp://localhost:8080/にするとうまく動作しなくなるので、 homeで受けて同じページを返すような処理が必要です(いまいちこのあたりの処理に精通していないので失敗原因がわかりません)

実行すると、例のログイン画面に3つでてくるでしょう。実際は、この画面を使うこともないでしょうし、うまく隠蔽してください。

f:id:StateMachine:20200806103142p:plain

まとめ

すべては後半のStarerを使ったやつはDocsにチュートリアルがありますので、そちらを参考にしてくれたほうがはかどると思います。とはいえ、内容が微妙に間違ってたり古くなってたり誤訳だったり、説明不足だったりすることが多いので、実際動かすのは手間がかかる場合もありあmす。

docs.microsoft.com

以上