AWS Lambdaで自前JSONログがINFO扱いになる仕様と対処法
はじめに
AWS Lambda のログレベルフィルタリングを使うと、アプリケーションログを WARN 以上のみ Amazon CloudWatch Logs へ送る、といった柔軟な制御が可能です。不要なログの転送を抑えることで、ログの保管コスト削減や調査時のノイズ低減に役立ちます。
先日、Lambda 関数で JSON 形式のログを出力していた際、「"level": "error" を指定しているはずなのに、なぜか CloudWatch Logs にログが届かない」という問題に遭遇しました。
コードや設定を見直してもエラーは見当たらず、原因を調べていくと、Lambda が自前の JSON ログを解析する際の「タイムスタンプの仕様」に意外な落とし穴があることが分かりました。
本記事では、自前で JSON ログを出力する際に ERROR ログが届かなくなる原因と、Lambda がログレベルを判定する仕様の詳細を解説します。Lambda で JSON ログを出力している方や、ログレベルフィルタリングの導入を検討している方の参考になれば幸いです。
ログレベルフィルタリングのおさらい
Lambda のログレベルフィルタリングは、関数が出力するログのうち、指定したレベル以上のものだけを CloudWatch Logs へ送信する機能です[1]。Lambda が生成する「システムログ」と、関数コードが出力する「アプリケーションログ」で、それぞれ独立してレベルを設定できます。
アプリケーションログのレベルは、詳細度が高い順に次の 6 段階が用意されています。
| ログレベル | 用途 |
|---|---|
| TRACE | コードの実行経路を追跡するための最も詳細な情報 |
| DEBUG | デバッグ用の詳細情報 |
| INFO | 通常の動作を記録するメッセージ |
| WARN | 放置すると想定外の動作につながりうる警告 |
| ERROR | 処理が期待どおりに動かないエラー |
| FATAL | アプリケーションが停止する重大な障害 |
たとえばアプリケーションログレベルを WARN に設定した場合、INFO や DEBUG のログは CloudWatch Logs に送信されません(デフォルト設定は INFO です)。
このログレベルフィルタリングを利用するには、関数のログ形式を JSON に設定する必要があります。Lambda マネージドランタイムのデフォルトログ形式はプレーンテキストであるため、あらかじめ設定変更が必要です[1:1]。
aws lambda update-function-configuration \
--function-name myFunction \
--logging-config LogFormat=JSON,ApplicationLogLevel=WARN,SystemLogLevel=INFO
RFC 3339 形式の timestamp が必要になるケース
ログ形式を JSON に変更した際、出力方法によって Lambda 側の判定挙動が異なります。
サポート対象の標準ロガーなら自動で判定される
Python の logging モジュールや Node.js の console など、Lambda ランタイムが標準でサポートするログ出力方法を使う場合、関数コード側でタイムスタンプを意識しなくても Lambda が自動的にログレベルを判定してくれます[1:2][2]。サポート対象の組み合わせについては、公式ドキュメントで確認できます。
自前で JSON を出力する場合は level と timestamp が必須
一方、標準ロガーを使わずに自前で JSON ログを出力する場合、関数コード側で stdout や stderr へ "level" キーを含む JSON オブジェクトを出力する必要があります[1:3]。print() 関数で JSON 文字列を直接書き出すケースや、サポート対象外のサードパーティ製ログライブラリを使うケースがこれに該当します。
公式ドキュメントでは、次の JSON 出力を DEBUG レベルのログとして解釈する例が紹介されています。
print('{"level": "debug", "msg": "my debug log", "timestamp": "2024-11-02T16:51:31.587199Z"}')
このとき、Lambda がログを解析する際の重要なルールは次の 2 つです[1:4]。
"level"の値が不正、または"level"キーが存在しない場合は INFO に設定するtimestampが RFC 3339 形式でない、またはtimestampキーが存在しない場合は、レベルを強制的に INFO にした上で、Lambda が自動で timestamp を付与する
注目すべきは 2 つ目のルールです。"level": "error" と正しく指定した場合でも、timestamp の形式が満たされていないとログレベルは INFO へ強制的に上書きされます。
なお、timestamp のキー名は使用しているランタイムの命名規則に従います。一般的なランタイムで広く使われているキー名の大半には Lambda 側で対応しています[1:5]。
INFO 扱いになると何が起きるか
では、実際にログレベルが INFO に上書きされてしまうと、どのような問題が起きるのでしょうか。
例えば、アプリケーションログレベルを WARN に設定した関数で、次のようにログを出力したとします。
print('{"level": "error", "msg": "payment failed", "timestamp": 1790000000}')
上記のコードでは、timestamp に UNIX 時間の数値(エポック秒)を指定しており、RFC 3339 形式になっていません。
この場合、Lambda の仕様により、ログは強制的に INFO として扱われます。設定されているログレベルは WARN のため、INFO レベルのログは CloudWatch Logs へ送信されずに破棄されます。
つまり、「重大なエラーが発生してログを出力したはずなのに、CloudWatch Logs を探してもどこにも見当たらない」 という事態に陥ります。当然、CloudWatch Logs のメトリクスフィルターやアラームで ERROR ログを監視していたとしても、アラームは一切検知できません。
また、逆方向のトラブルも起こり得ます。アプリケーションログレベルを INFO に設定している関数で、"level": "debug" のログに不正な timestamp を付与した場合も同様です。本来なら除外される DEBUG ログが INFO 扱いとなり、CloudWatch Logs に転送されてしまいます。これによりログ転送量が増大し、取り込み料金の増加につながります。
まとめ
今回は、Lambda のログレベルフィルタリングにおいて、自前の JSON ログに RFC 3339 形式の timestamp を含めないとログレベルが強制的に INFO 扱いになる仕様を紹介しました。
エラーログを出力しているつもりが CloudWatch Logs に届いていなかった、というのは本番の障害調査で最も気付きにくい落とし穴だと感じます。ログレベルフィルタリングを利用する際は、意図したログが正しく届いているか、事前に確認しておくことをおすすめします。
本ブログが Lambda のログレベルフィルタリングを導入したい方の参考になれば幸いです。
クラスメソッドオペレーションズ株式会社について
クラスメソッドグループのオペレーション企業です。
運用・保守開発・サポート・情シス・バックオフィスの専門チームが、IT・AIをフル活用した「しくみ」を通じて、お客様の業務代行から課題解決や高付加価値サービスまでを提供するエキスパート集団です。
当社は様々な職種でメンバーを募集しています。
「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、クラスメソッドオペレーションズ株式会社 コーポレートサイト をぜひご覧ください。※2026年1月 アノテーション㈱から社名変更しました
Log-level filtering - AWS Lambda(2026年9月26日参照) ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Configuring JSON and plain text log formats - AWS Lambda(2026年9月26日参照) ↩︎









