Pythonランタイム環境のCloud FunctionsでNew Relic APMを導入しトレースデータを取得する方法

# 概要
Google Cloudのサーバーレスサービスの１つであるCloud Functions(Pythonランタイム環境)のトレースデータをNew Relicを使って取得できたので、その内容をまとめようと思います。 

# 今回の検証環境
Cloud Functions側は以下の条件で検証。

- リージョン: us-central1
- Cloud Functions: V2(第二世代)
- ランタイム: Python　3.12
- トリガー: HTTPS

New Relic
- フリーアカウントの作成
    - お持ちでない場合は、[こちら](https://qiita.com/ktst79/items/5ee74b34e6adb8a7a6fa "New Relic")から作成
- ライセンスキー(INGEST Type)
    - [こちら]( https://qiita.com/kooohei/items/00da439f280adc44cdca
"Qiita")から作成

# 手順
## ①New Relic APMライブラリのインストール

PythonランタイムのCloud Functionではrequirements.txtが用意されております。
このrequirements.txtにNew Relicライブラリを読み込むように追記します。

```requirements.txt
functions-framework==3.*
newrelic==9.7.0  <---追加
```

![スクリーンショット 2024-04-18 21.52.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820651/979b18a3-1e70-211a-8968-0b834f06cb8d.png)

※現時点(2024/04)での最新のライブラリバージョンを指定しております。適宜変更ください。


## ②New Relic APMエージェントの設定
New Relic APMエージェントは通常newrelic.iniというファイルで細かな設定を行いますが、環境変数でも設定可能です。

設定する項目は以下の３つです。

```環境変数で行うNew Relicエージェントの設定行項目.
NEW_RELIC_LICENSE_KEY: xxxx  <--- (New RelicのLicense keyを指定)
NEW_RELIC_APP_NAME: yyy  <--- (ご自身のCloud Functionsの名前を指定)
NEW_RELIC_DISTRIBUTED_TRACING_ENABLED: TRUE
```

![スクリーンショット 2024-04-18 22.21.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820651/7d783e07-4dd1-3e6f-3aa1-417bd508d920.png)

## ③main.pyにコードを追記する

最低限の設定としては3つあり、New Relicライブラリの読み込みとNew Relic Agentの初期化、そしてデコレーターの設定を行います。これで最低限動きます。
以下のサンプルコードでは、Cloud FunctionのPythonランタイムで提供される初期のコードを使ってNew Relicで計装するときの例です。

```diff_python
  import functions_framework
+ import newrelic.agent # newrelicライブラリの読み込み

+ newrelic.agent.initialize() #newrelic-agentの初期化

  @functions_framework.http
+ @newrelic.agent.function_trace() #計測対象の関数に対しデコレーターを設定
  def hello_http(request):
      """HTTP Cloud Function.
      Args:
          request (flask.Request): The request object.
          <https://flask.palletsprojects.com/en/1.1.x/api/#incoming-request-data>
      Returns:
          The response text, or any set of values that can be turned into a
          Response object using `make_response`
          <https://flask.palletsprojects.com/en/1.1.x/api/#flask.e_response>.
      """
      request_json = request.get_json(silent=True)
      request_args = request.args

      if request_json and 'name' in request_json:
          name = request_json['name']
      elif request_args and 'name' in request_args:
          name = request_args['name']
      else:
          name = 'World'

      return 'Hello {}!'.format(name)
```

## ④リクエスト発生させて、テレメトリデータをNew Relic上で確認する
リクエストを発生させて、New Relic APMエージェントにテレメトリデータを収集させます。
収集されたデータはNew Relic上で以下のように確認できます。ただし、初回アクセスに限りリクエスト発生か約5分ほどの時間がかかりますが、それ以降はリアルタイムにデータが表示されます。

![スクリーンショット 2024-04-18 22.31.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820651/37616b9f-f970-04ea-d137-e9711f8a14e5.png)

また、以下はCloud Function でエラーが観測された時の画面になります。この画面からメトリック、スタックトレース、ログなどの基本的なテレメトリデータが計測できていることがわかります。

![スクリーンショット 2024-04-18 22.37.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820651/50cedd6c-ab0a-dae7-7a80-de5d28e72417.png)


# まとめ
New Relic APMを使ってCloud FunctionsのPythonランタイム環境を計測できました。
ライブラリのインストール、環境変数の設定、コードへの追加(今回は2行の追記)のみで実装できたので、作業負荷は思ったよりも高くなかったです。今回のケースは、Google Cloudと他のクラウドプラットフォーム、またはオンプレを併用している場合、New Relicのようなサービスを使うことで各種プラットフォームを跨いで観測できるケースがマッチすると思います。ご参考になれば幸いです。