1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【自己流】マイクロサービスでのログ管理のすゝめ

1
Posted at

はじめに

近年のソフトウェア開発では、システムの大規模化やクラウドサービスの普及に伴い、マイクロサービスアーキテクチャの採用が進んでいます。

マイクロサービスは、システムを独立した小さなサービスに分割することで、開発の柔軟性・スケーラビリティ・耐障害性を高めることができます。
しかし一方で、サービス間通信が複雑化するため、**ログの管理とトレーサビリティ(追跡性)**が課題となります。

本記事では、この「ログ管理の煩雑さ」を解消するために、実践的なベストプラクティスを紹介します。


マイクロサービスのメリットとデメリット

メリット

  • 独立性と柔軟性: サービスごとに開発・デプロイ可能。影響範囲を限定できる。
  • 技術選択の自由: サービスごとに最適な技術スタックを利用可能。
  • スケーラビリティ: 必要なサービスだけを水平スケールできる。
  • アジリティ: 小規模なチームが独立して機能追加・改善を進めやすい。

デメリット

  • リクエストが複数サービスを横断: 単一の処理フローを追うのが困難。
  • ログの分散: 各サービスが独立してログを出力し、全体像を掴みにくい。
  • 監視の複雑化: サービスが増えるほど、モニタリングや通知設計も複雑に。

実践

想定構成

例として、複数のAWS Lambda関数が連携し、最終的にデータベースや外部SaaSにアクセスする構成を考えます。
この場合、各Lambdaは独立してログを出力するため、単純にCloudWatchを見るだけではリクエスト全体の流れを追いにくくなります。


ログ出力(Python)

基本的な利用方法

標準ライブラリ logging を利用すれば簡単にログを出力可能です。

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

handler = logging.StreamHandler()
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)

これは小規模なサービスであれば十分ですが、マイクロサービス全体で追跡するには不十分です。


応用編: カスタムロガー & 構造化ログ

複数サービスを横断するリクエストを追跡するには、構造化ログ(JSON形式)と共通のリクエストIDが不可欠です。

import logging, json, os

class JsonFormatter(logging.Formatter):
    def format(self, record):
        log_entry = {
            "timestamp": self.formatTime(record, self.datefmt),
            "level": record.levelname,
            "service": os.getenv("SERVICE_NAME", "unknown"),
            "request_id": getattr(record, "request_id", "N/A"),
            "message": record.getMessage(),
            "details": getattr(record, "details", {})
        }
        return json.dumps(log_entry, ensure_ascii=False)

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter('%Y-%m-%dT%H:%M:%S%z'))
logger.addHandler(handler)

ポイント:

  • extra引数を活用し、request_iddetailsを動的に付与する。
  • JSON形式で出力することで、後段の分析や検索が容易になる。

ログ収集と通知

ログを出力するだけでは不十分で、収集・正規化・通知まで含めて設計する必要があります。

  1. CloudWatch Logs にLambdaごとのログを集約。
  2. Subscription Filter で特定のログを抽出し、別のLambdaへ転送。
  3. 正規化とS3集約: JSONフォーマットで整理し、長期保管やAthenaによる分析に備える。
  4. 通知連携:
    • CloudWatch Alarm → SNS → Slack / PagerDuty
    • 例: エラー件数が一定閾値を超えたら即通知

👉 こうした流れを取ると、リアルタイム検知 + 長期分析が両立できます。


分析の実践(CloudWatch Logs Insights)

CloudWatch Logs Insightsを活用すれば、クエリベースで柔軟にログを分析可能です。

サンプルクエリ

サービス別エラー件数

fields @timestamp, service, level
| filter level = "ERROR"
| stats count(*) as error_count by service
| sort error_count desc

リクエストIDでフローを追跡

fields @timestamp, service, message
| filter request_id = "test-request-id-123"
| sort @timestamp asc

特定エラータイプを調査

fields @timestamp, service, details.error_type
| filter level = "ERROR" and details.error_type = "TimeoutError"
| sort @timestamp desc

補足: 実務でさらに有効なポイント

  • OpenTelemetry導入: 単なるログに加え、分散トレーシング(Trace ID, Span ID)を仕込むと、リクエスト全体の可視化が格段に楽になる。
  • ELK / OpenSearch 連携: CloudWatchだけでなく、ElasticsearchやOpenSearchにログを転送してダッシュボード可視化。
  • 構造化ログの共通ライブラリ化: チーム内で統一されたフォーマットを持たせると、全サービス横断で解析可能に。
  • PIIやセキュリティ配慮: ユーザー情報や機密データをそのままログに出さないルールを設けることも重要。

まとめ

マイクロサービスでは、ログが分散するため従来以上に統合的なログ戦略が必要です。

  • 構造化ログ(JSON形式)で統一
  • 共通のリクエストIDを付与
  • CloudWatchやS3で収集・正規化
  • Insightsや外部ツールで分析
  • 通知とトレーシングを組み合わせる

これらを組み合わせることで、効率的なログ管理・迅速なトラブルシューティング・システム全体の可観測性向上を実現できます。


1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?