New Relic APMをJava (Spring)アプリケーションに導入してみる

# はじめに

New Relic APMの Java agent をdocker-composeで立ち上げたSpringのアプリケーションに入れて遊んでみたので、導入手順や結果・感想について簡単にまとめようと思います。

（追記）ついでに「[New Relic CodeStream](https://docs.newrelic.com/jp/docs/codestream/start-here/what-is-codestream/)」でも遊んでみました。

※今回はNew Relic APMで遊ぶことがメインなので、アプリの作りには詳しく言及しません。

# 環境

- OS：Windows 11 / WSL (Ubuntu 22.04)
- アプリケーション：Java 17
  - Spring Boot 3.2.0
- DB：PostgreSQL 15

New Relic APM Java agentは `8.7.0` を利用しています。
```sh
$ java -jar newrelic.jar -v
8.7.0
```

# 構成

マイクロサービスチックにした方が楽しそうだったので、今回は以下のような構成を組んでみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/419684f1-1725-642c-17ac-b35f46c0306a.png)

「Product Service」はPostgreSQLからproduct情報を取得します。
そして、**わざわざ**そのProduct Serviceを「Caller」から呼ぶという構成にしていますｗ

# APMの設定

## 接続確認

New Relicでアカウント登録してログインすると、以下のような画面が表示されるので、まずはこの画面に従って接続テストをしてみます。

- 「Application monitoring」でJavaを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/4ac16651-4835-606c-66e9-aad853ac237d.png)

- 今回はdocker-composeでアプリケーションを動かすので、Docker Compose > Default Bridge と選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/0e6265bd-251d-1a74-ca36-586f9c81fc5d.png)

- コマンドが表示されるので`docker-compose up`まで無心で実行していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/64deebf8-187a-3b00-1bee-cc2a4d22c011.png)

表示されるコマンドは以下です。
```sh
mkdir ~/newrelic-infra && cd ~/newrelic-infra && echo "license_key: ***" > newrelic-infra.yml
```

```sh
cat >> newrelic-infra.dockerfile << EOF
FROM newrelic/infrastructure:latest
ADD newrelic-infra.yml /etc/newrelic-infra.yml
EOF
```

```sh
cat >> docker-compose.yaml << EOF
version: '3'

services:
  agent:
    container_name: newrelic-infra
    build:
      context: .
      dockerfile: newrelic-infra.dockerfile
    cap_add:
      - SYS_PTRACE
    network_mode: bridge
    pid: host
    privileged: true
    volumes:
      - "/:/host:ro"
      - "/var/run/docker.sock:/var/run/docker.sock"
    restart: unless-stopped
EOF
```

```sh
docker-compose -f docker-compose.yaml up -d
```

- 「newrelic-infra」のコンテナが無事に起動したら「Test Connection」します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/5cfed326-4b95-1f8b-2a5c-08be69df53d0.png)

- 「Successful」になったらOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/3970877a-7160-8747-b1a0-421c0f0ec75f.png)

## APM Java agent (newrelic-java.zip) のダウンロード

**※ココから微妙に画面に従いません。**

Test Connectionが完了し「Continue」を押すと、そのままAPMの設定画面に進めます。

- 今回は「Docker」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/ae45264a-8160-fe61-3ec2-bb12e6e17c03.png)

- WSLで作業するので、**敢えて「No, running on Linux」を選択**して、表示されたコマンドで「newrelic-java.zip」をダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/6f13f0a8-b3db-a51d-f63b-510998a87145.png)

「newrelic-java.zip」を解凍した結果はこんな感じです。この中にある「newrelic.jar」を最終的に利用します。
```sh
$ ls
LICENSE                 extension-example.xml  newrelic-api-javadoc.jar  newrelic-api.jar  newrelic.yml
THIRD_PARTY_NOTICES.md  extension.xsd          newrelic-api-sources.jar  newrelic.jar
```

## イメージの作成

以下の画面を参考に、アプリケーションのイメージを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/330ef02e-e5d7-421f-fdd0-935c07daa92c.png)

やる作業は以下の2つ。
- 「newrelic.jar」, 「newrelic.yml」を`/usr/local/newrelic`に配置
- `ENTRYPOINT` にJava agentの起動を追加

が、**「newrelic.jar」, 「newrelic.yml」はボリュームマウントする方が使い勝手が良さそうだった**ので、Dockerfileは以下のようにしました。

```dockerfile:Dockerfile
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu

RUN mkdir -p /usr/local/newrelic
COPY ./build/libs/product-0.0.1-SNAPSHOT.jar app.jar

ENTRYPOINT ["java","-javaagent:/usr/local/newrelic/newrelic.jar","-jar","/app.jar"]
```

## newrelic.ymlの作成

Java agentの設定ファイルである「newrelic.yml」はダウンロードしたファイル一式の中にはあり、それをそのまま使っても問題ないのですが、一応、**ガイダンス画面に表示されたものをコピーして利用**しました。

また、今回は以下のように**２つの設定ファイルを作成しました。**
- Caller用：newrelic_caller.yml
- Product Service用：newrelic_product.yml

## docker-compose.yamlの作成＆コンテナ起動

先述の通り、「newrelic.jar」と「newrelic.yml」のボリュームマウントを設定したdocker-compose.yamlを作成します。

```yaml:docker-compose.yaml
version: '3'
services:
  product:
    image: product-service
    ports:
    - 8081:8081
    volumes:
    - ./newrelic/newrelic.jar:/usr/local/newrelic/newrelic.jar
    - ./newrelic/newrelic_product.yml:/usr/local/newrelic/newrelic.yml
    depends_on:
    - postgresql
  caller:
    image: caller
    ports:
    - 8080:8080
    volumes:
    - ./newrelic/newrelic.jar:/usr/local/newrelic/newrelic.jar
    - ./newrelic/newrelic_caller.yml:/usr/local/newrelic/newrelic.yml
  postgresql:
    image: postgres:15.0
    restart: always
    environment:
      POSTGRES_PASSWORD: PASSWORD
    ports:
    - 5000:5432
    volumes:
    - ./pgdata:/var/lib/postgresql/data
```

作成したら`docker-compose up -d` でコンテナを起動します。

## 結果確認まで

- Optionalのinfrastructure agentの設定は今回スキップしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/f71a873a-9a8b-f799-93e5-b33f49b7d519.png)

- 「Test Connection」で「Successful」になったらOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/e2c8c6b8-e889-28ec-6b45-5944aab03853.png)

- See your data
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/3379900c-691b-3823-0c26-096bf43b22ff.png)

- 「Services - APM」に２つのアプリケーションが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/3b8af1cc-e8c3-679c-7e97-4254d2694efe.png)

# 結果詳細確認

実際にAPIを実行していくつかの画面を見てみます。

## Service map

Caller > Product Service > PostgreSQL という経路を確認することができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/a0faf04e-c65c-28ff-ee53-c25a4eb7a648.png)

## Transactions

実行したAPIの概要

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/34f7f4b1-fc57-b7fb-aa80-207f55b169aa.png)

どの処理がどの程度の時間を占めているのかも確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/d8f069f1-bd1f-f14a-58bc-cf75f310ae86.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/8e50c5b5-7145-6ab6-bf70-94bece9898b8.png)

「Transaction traces」から選択すると更に詳細を見ることができます！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/8db65d32-e100-c86e-944e-496674140db8.png)

「View distributed trace」で関連サービスをまとめて見ることも可能です！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/a666e4c1-38bb-9ea0-409c-75f562705481.png)

勝手にTrace IDを振ってくれるので、複数サービスに跨ってまとめてログを見ることもできます！！（便利）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/f6aa2f47-c48e-f13f-23b7-a35b5af5f0fd.png)

どんなクエリが実行されていたかも確認可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/f9ecf81b-0244-3100-fcea-4c06fa07da9b.png)

## JVMs

latency、throughput、メモリ使用量などが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/213af5f4-6404-c595-e2f2-8b474016ea1b.png)

## Errors Inbox

敢えてエラーを発生させてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/e0f8f9a5-54cc-eceb-4742-2fb091a42806.png)

エラーの詳細からDistributed traceを確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/31d08b4b-46e4-17a3-5831-615c9aef8e13.png)

ログも複数サービスのログを一気通貫で確認可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/5ce3f9ab-86e9-bc10-d2a1-a44e7249e2c9.png)

New Relic CodeStreamという拡張機能を利用すると、Stack traceから「Open in IDE」で該当のコードに速攻で飛ぶことも可能です。そちらについては「[New Relic CodeStream](https://qiita.com/bottle_gourd/items/74c722f0e230fb4ae6a2#new-relic-codestream)」に詳しく記載していますので、ご参照ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/356807e5-39cf-a055-8580-3817b127f531.png)

## Service level management

本当はサービスレベル管理（SLI, SLO, SLA）もできたりするのですが、今回は作ったアプリケーションがショボいので割愛します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/62eed7f3-687a-9054-6a18-a88405761f89.png)

参考

https://docs.newrelic.com/jp/docs/service-level-management/intro-slm/

# New Relic CodeStream

> New Relic CodeStream は、コードのパフォーマンスを開発プロセスの初期段階の一部にすることでシフトレフトを可能にする IDE 拡張機能です。

[New Relic CodeStream の紹介](https://docs.newrelic.com/jp/docs/codestream/start-here/what-is-codestream/)より引用。

こちらについても追加で設定・動作確認をしてみようと思います。

## リポジトリの接続

APMの画面からリポジトリとの接続を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/5903c2f7-aad1-527d-0b48-e578669a9bec.png)

## New Relic CodeStream（拡張機能）のインストール

[New Relic CodeStream](https://marketplace.visualstudio.com/items?itemName=CodeStream.codestream)をVisual Studio Codeにインストールします。
インストール後、拡張機能からサインインを行うと、左メニューに以下のようなものが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/cfb8826b-a629-8822-b483-8fe7a7c2aab0.png)

他IDEの拡張機能については、以下ドキュメントをご参照ください。（記事執筆時点では、Visual Studio Code、Visual Studio、IntelliJに対応していそうです。）

https://docs.newrelic.com/jp/docs/codestream/start-here/install-codestream/

## パフォーマンスの確認

設定後、APIを実行してみると、latency, throughput, Error rate, 依存サービスなどをVSCodeの拡張機能上で確認することができます。**コードの該当箇所にもlatency, Error rateが表示されるので、特定の処理がどのようなパフォーマンスなのかを確認するのが簡単にできそうです。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/c3c7ec85-b81d-8cea-0f7b-2f0f615e08e6.png)

コード上の結果をクリックすると、以下のようなlatencyとリクエスト数（サンプル数）のグラフを見ることもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/162d91d9-4d5d-908f-052f-bf59fdca3ec9.png)

## Stack traceの確認

Errors Inboxからエラーを確認し、「Open in IDE」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/a0d59b98-ab91-06b2-af90-088f6758aa9f.png)

以下の画面が表示されるので、ここからVSCodeを開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/ed2f2e4f-600e-dc58-017f-6521c03742a5.png)

すると、Stack traceから該当のコードの箇所を自動的に表示してくれます。Stack traceをじっくり眺める必要が無いので、これはめっちゃ楽ですね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655586/77fe6686-c035-964f-77b8-422c433074cb.png)

# まとめ

New Relic APM Java agentで遊んでみましたが、導入も簡単で非常に便利だなと思いました。
サービスの俯瞰的な状態から、コードレベルの状態・パフォーマンスまで確認できるので、開発者にとっては非常に有用なツールだと思いますし、**サービスオーナー、運用者とも同じ画面を見て会話できるのではないか**と感じました。

**Java agentを起動するため、パフォーマンスにどう影響するかが気になる**ところではありますが、本番前の環境であれば、気軽に導入してサービス・コードの状態を確認してみるのはアリな気がします。