この記事はReviCo Advent Calendar 2025 の9日目の記事です。
はじめに
こんにちは、ReviCoの開発者のうっちです😎
ReviCoでは、サービスの監視・可観測性の実現にNewRelicを活用しています。
アプリケーションパフォーマンス監視(APM)、インフラ監視、ログ管理など、NewRelicの各種機能を使ってシステム全体の健全性を保っています。
マルチテナント型のSaaSを運用していると、テナント毎にログを監視したいというニーズが出てきます。
今回は、NewRelicのLog Parsing機能を使ってこの課題を解決した事例を紹介します。
NewRelicとは?
NewRelicは、アプリケーションやインフラストラクチャの監視・可観測性を提供するSaaSプラットフォームです。
主な機能:
- APM (Application Performance Monitoring): アプリケーションのパフォーマンスをリアルタイムで監視
- Infrastructure Monitoring: サーバー、コンテナ、クラウドリソースの監視
- Log Management: ログの収集、検索、分析
- Alerts & Anomaly Detection: 異常検知とアラート通知
- Distributed Tracing: マイクロサービス間のリクエストフローを可視化
- Custom Dashboards: カスタマイズ可能なダッシュボードで重要なメトリクスを一元管理
ReviCoでは特に、ログ可視化/アラート通知/APMを活用してサービス品質の向上に取り組んでいます。
課題
テナント毎のログ監視がしたい
ReviCoはマルチテナント型のSaaSであるため、テナント毎に監視を分けることが重要です。
NewRelicでアラート設定をする際、テナント毎に監視を分けたいというケースがありました。
例えば:
- 特定テナントでエラーが多発した場合にアラートを発報
- テナントAとテナントBで異なる閾値を設定
- ダッシュボードでテナント毎の集計を表示
これを実現するには、ログにTenantIdのような属性を持たせる必要があります。
従来の方法
Log API (v1) でカスタム属性を追加
当初は、NewRelicのLog API (v1)を使ってカスタム属性を追加していました。
{
"logs": [
{
"message": "商品登録でエラーが発生しました。",
"timestamp": 1701388800000,
"attributes": {
"tenantId": "00000000-440d-444e-a84c-d0017a33a001"
}
}
]
}
このアプローチの問題点:
- レート制限(スロットリング): Log APIには、1分あたり300,000リクエストの制限がある
- 実装コスト: 各サービスでAPIコールを実装する必要がある
- ログ量が多いとAPI呼び出しが膨大になり、制限に引っかかるリスク
実際、ログ量が増えてくるとレート制限が懸念され、乱用できない状況でした。
解決策
Log Parsing機能を活用
そこで見つけたのが、NewRelicのLog Parsing機能です。
Log Parsingとは?
Log Parsingは、既にNewRelicに取り込まれているログのmessageフィールドから、特定のパターンを抽出してカスタム属性として追加できる機能です。
メリット:
- ✅ ログ取り込み後に属性を追加できる(API呼び出し不要)
- ✅ Grokパターンや正規表現で柔軟に抽出可能
- ✅ 既存のログフォーマットを変更せずに対応可能
- ✅ レート制限の心配がない
前提条件: AWS CloudWatch LogsとNewRelicの連携
Log Parsing機能を使用するには、AWS CloudWatch LogsのログがNewRelicに連携されている必要があります。
ReviCoでは既に連携が済んでいるので、詳細は割愛します。
実際のログ構造例:
{
"aws.logGroup": "/aws/lambda/ReviCo-ProductRegister",
"aws.logStream": "2025/12/01/[$LATEST]aaacf1d44f22483bbda588ab53065028",
"message": "2025-12-01T06:02:17.172Z\taaa1a644-c3d9-471e-871d-5a6c2a060c2d\tinfo\t{ \"time\": \"2025-12-01 06:02:17.1721\", \"level\": \"INFO\", \"logger\": \"ReviCo.Job.ProductRegisterUseCase\", \"message\": \"9件の商品を登録します。 TenantId: 00000000-440d-444e-a84c-d0017a33a001\" }\n",
"plugin.type": "lambda",
"timestamp": 1764568937172
}
やりたいこと:
messageフィールドの中に埋め込まれたJSONから、以下の情報を抽出してログのトップレベル属性として利用したい:
-
TenantId: テナント識別子(例:00000000-440d-444e-a84c-d0017a33a001) -
level: ログレベル(例:INFO,ERROR,WARN)
Log Parsingの設定手順
1. Logsページにアクセス
NewRelicのUIで、左メニューからLogs→Parsingを選択します。
2. 新しいParsing Ruleを作成
Create parsing ruleボタンをクリックします。
設定項目
-
Name
わかりやすい名前を付けます。Extract TenantId from application logs -
Filter logs based on NRQL
どのログにこのParsingルールを適用するかをNRQL形式で指定します。
例: 特定のLambda関数のログのみ対象にする場合aws.logGroup = '/aws/lambda/ReviCo-ProductRegister' AND message LIKE '%TenantId%'または、plugin.typeでLambdaログのみを対象にする場合:
plugin.type = 'lambda' AND message LIKE '%TenantId%'
-
Parsing rule
実際の抽出ロジックを記述します。
NewRelicではGrokパターンまたは正規表現が使用できます。今回はGrokパターンを使用します。-
TenantIdを抽出するパターン
Lambdaログのmessage内のJSON文字列からTenantId:というテキストの後に続くUUID形式の値を抽出します。TenantId: %{UUID:tenantId} -
levelを抽出するパターン
JSON内の"level": "INFO"のような形式から値を抽出します。"level": "%{WORD:level}" -
複数のフィールドを抽出するパターン
1つのParsingルールで複数のフィールドを抽出したい場合は、パターンを組み合わせることができます。
ただし、管理のしやすさを考慮して、TenantIdとlevelで別々のルールを作成することも推奨されます。"level": "%{WORD:level}".*TenantId: %{UUID:tenantId}
-
TenantIdを抽出するパターン
3. テストしてみる
設定画面のPaste logエリアにログを直接貼り付けることで、テストができます。
テストログ(messageフィールドの値):
2025-12-01T06:02:17.172Z\taaa1a644-c3d9-471e-871d-5a6c2a060c2d\tinfo\t{ \"time\": \"2025-12-01 06:02:17.1721\", \"level\": \"INFO\", \"logger\": \"ReviCo.Job.ProductRegisterUseCase\", \"message\": \"9件の商品を登録します。 TenantId: 00000000-440d-444e-a84c-d0017a33a001\" }
Grokパターン:
"level": "%{WORD:level}".*TenantId: %{UUID:tenantId}
結果:
{
"level": "INFO",
"tenantId": "00000000-440d-444e-a84c-d0017a33a001"
}
4. 保存して有効化
Create ruleをクリックすると、ルールが有効化されます。
Parsingルールは保存後、新しく取り込まれるログにのみ適用されます。
過去のログには遡って適用されませんのでご注意ください。
Parsingの効果: before/after
Parsing前のログ構造
{
"aws.logGroup": "/aws/lambda/ReviCo-ProductRegister",
"aws.logStream": "2025/12/01/[$LATEST]aaacf1d44f22483bbda588ab53065028",
"message": "2025-12-01T06:02:17.172Z\taaa1a644-c3d9-471e-871d-5a6c2a060c2d\tinfo\t{ \"time\": \"2025-12-01 06:02:17.1721\", \"level\": \"INFO\", \"logger\": \"ReviCo.Job.ProductRegisterUseCase\", \"message\": \"9件の商品を登録します。 TenantId: 00000000-440d-444e-a84c-d0017a33a001\" }\n",
"plugin.type": "lambda",
"timestamp": 1764568937172
}
tenantIdやlevelの情報はmessageフィールドの中に埋もれており、NRQLで直接クエリできない状態でしたが…![]()
Parsing後のログ構造
{
"aws.logGroup": "/aws/lambda/ReviCo-ProductRegister",
"aws.logStream": "2025/12/01/[$LATEST]aaacf1d44f22483bbda588ab53065028",
"message": "2025-12-01T06:02:17.172Z\taaa1a644-c3d9-471e-871d-5a6c2a060c2d\tinfo\t{ \"time\": \"2025-12-01 06:02:17.1721\", \"level\": \"INFO\", \"logger\": \"ReviCo.Job.ProductRegisterUseCase\", \"message\": \"9件の商品を登録します。 TenantId: 00000000-440d-444e-a84c-d0017a33a001\" }\n",
"plugin.type": "lambda",
"timestamp": 1764568937172,
"tenantId": "00000000-440d-444e-a84c-d0017a33a001",
"level": "INFO"
}
tenantIdとlevelがトップレベル属性として追加されました![]()
これらの属性を使ってNRQLでフィルタリング、集計、アラート設定が可能になります!
まとめ
NewRelicのLog Parsing機能を使うことで:
- ✅ Log APIのレート制限を気にせずテナント毎の監視が実現
- ✅ 既存のログフォーマットを変更せず属性を追加
- ✅ NRQL/アラート/ダッシュボードで柔軟に活用
マルチテナントSaaSの監視において、Parsingは非常に強力なツールです。ぜひ活用してみてください!