Lambda の「高度なログ制御」は何をしているのか

Lambda の「高度なログ制御」は何をしているのか

AWS Lambda の高度なログ制御機能が、各ランタイムの Runtime Interface Client でどのように実装されているのかを、実際の実験を通じて徹底解説します。
2026.09.17

はじめに

こんにちは、AI 事業本部の Kanaru です。

AWS Lambda にはログ整形やログレベルの制御が可能な「高度なログ制御機能」があります。これは、特定のログの出力方法を利用していれば、Lambda 側の設定で、JSON で構造化したり出力するログレベルを変更できる機能です。
例えば、Node.js なら console、Python なら logging を使用してプログラムを書けば、プログラム側で整形やログレベルの設定をせずとも Lambda が自動的に制御してくれます。

しかし、このような既存の動作を変更するような仕様は、理解しないまま使用すると予期しない動作を引き起こす可能性があります。そこで本記事では、この「高度なログ制御機能」が内部的にどのような処理をしているのか解説します。

本記事の検証コードは以下の通りです。

https://github.com/kanaru0928/cm-lambda-log-test

対象読者

  • Lambda の高度なログ制御の利用を検討している/利用している方
  • 自分の使用言語で Lambda の高度なログ制御がどのように機能しているかを知りたい方

この記事で紹介すること

  • 高度なログ制御の基本的な仕組み
  • 各言語におけるログに関する RIC の実装
  • 各言語におけるログ出力の実験とその結果
  • 各言語のログレベルの対応

この記事で紹介しないこと

  • 高度なログ制御の使い方

実験

高度なログ制御がどのように機能するか、実際にやってみます。
次のような Python コードを用意し、デプロイします。

main.py
import logging
import sys

initialLogger = logging.getLogger(__name__)
initialLogger.setLevel(logging.DEBUG)


def handler(event, context):
    logger = logging.getLogger(__name__)
    logger.setLevel(level=logging.DEBUG)

    logger.debug("Hello, this is a DEBUG level message")
    logger.info("Hello, this is an INFO level message")
    logger.warning("Hello, this is a WARN level message")
    logger.error("Hello, this is an ERROR level message")
    logger.critical("Hello, this is a CRITICAL level message")

    print("Hello, this is a STDOUT message")
    print("Hello, this is a STDERR message", file=sys.stderr)

    initialLogger.debug("Hello, this is a DEBUG level message from initialLogger")

実行すると、次のようなログが得られました。

INIT_START Runtime Version: python:3.14.mainline.v63	Runtime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:eeb8a9516d399508120f0ea5616b892b0aa6f2f2e9c66961b3734b3ec62d8871
START RequestId: b549084d-5d31-4d61-8fd8-c70364a4d37f Version: $LATEST
[DEBUG]	2026-09-10T02:25:45.263Z	b549084d-5d31-4d61-8fd8-c70364a4d37f	Hello, this is a DEBUG level message
[INFO]	2026-09-10T02:25:45.264Z	b549084d-5d31-4d61-8fd8-c70364a4d37f	Hello, this is an INFO level message
[WARNING]	2026-09-10T02:25:45.264Z	b549084d-5d31-4d61-8fd8-c70364a4d37f	Hello, this is a WARN level message
[ERROR]	2026-09-10T02:25:45.264Z	b549084d-5d31-4d61-8fd8-c70364a4d37f	Hello, this is an ERROR level message
[CRITICAL]	2026-09-10T02:25:45.264Z	b549084d-5d31-4d61-8fd8-c70364a4d37f	Hello, this is a CRITICAL level message
Hello, this is a STDOUT message
Hello, this is a STDERR message
[INFO]	2026-09-10T02:25:45.264Z	b549084d-5d31-4d61-8fd8-c70364a4d37f	Hello, this is a INFO level message from initialLogger
END RequestId: b549084d-5d31-4d61-8fd8-c70364a4d37f
REPORT RequestId: b549084d-5d31-4d61-8fd8-c70364a4d37f	Duration: 2.79 ms	Billed Duration: 112 ms	Memory Size: 128 MB	Max Memory Used: 38 MB	Init Duration: 108.25 ms	

ここで、次のような設定をしました。

log-settings.png

実行すると、ログは次のように変わりました。

JSON ログ

{
    "time": "2026-09-10T02:42:03.871Z",
    "type": "platform.initStart",
    "record": {
        "initializationType": "on-demand",
        "phase": "init",
        "runtimeVersion": "python:3.14.mainline.v63",
        "runtimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:eeb8a9516d399508120f0ea5616b892b0aa6f2f2e9c66961b3734b3ec62d8871",
        "functionName": "CmLambdaLogTestStack-PythonFunctionWithJSON5D56DB6-KG5M4QGZwbWj",
        "functionVersion": "$LATEST",
        "instanceId": "2026/09/10/[$LATEST]d57bac85628a4aa79915225fc452216e",
        "instanceMaxMemory": 134217728
    }
}
{
    "time": "2026-09-10T02:42:03.968Z",
    "type": "platform.start",
    "record": {
        "requestId": "838e723e-787e-49f9-baf5-e6ebde28c6a4",
        "functionArn": "arn:aws:lambda:ap-northeast-1:<account_id>:function:CmLambdaLogTestStack-PythonFunctionWithJSON5D56DB6-KG5M4QGZwbWj",
        "version": "$LATEST"
    }
}
{
    "timestamp": "2026-09-10T02:42:03Z",
    "level": "INFO",
    "message": "Hello, this is an INFO level message",
    "logger": "main",
    "requestId": "838e723e-787e-49f9-baf5-e6ebde28c6a4"
}

{
    "timestamp": "2026-09-10T02:42:03Z",
    "level": "WARNING",
    "message": "Hello, this is a WARN level message",
    "logger": "main",
    "requestId": "838e723e-787e-49f9-baf5-e6ebde28c6a4"
}

{
    "timestamp": "2026-09-10T02:42:03Z",
    "level": "ERROR",
    "message": "Hello, this is an ERROR level message",
    "logger": "main",
    "requestId": "838e723e-787e-49f9-baf5-e6ebde28c6a4"
}

{
    "timestamp": "2026-09-10T02:42:03Z",
    "level": "CRITICAL",
    "message": "Hello, this is a CRITICAL level message",
    "logger": "main",
    "requestId": "838e723e-787e-49f9-baf5-e6ebde28c6a4"
}

Hello, this is a STDOUT message
Hello, this is a STDERR message
{
    "time": "2026-09-10T02:42:03.972Z",
    "type": "platform.report",
    "record": {
        "requestId": "838e723e-787e-49f9-baf5-e6ebde28c6a4",
        "metrics": {
            "durationMs": 3.391,
            "billedDurationMs": 97,
            "memorySizeMB": 128,
            "maxMemoryUsedMB": 38,
            "initDurationMs": 92.967
        },
        "status": "success"
    }
}

ログが JSON で構造化され、debug レベルのログが表示されなくなりました。

ここから分かることは次の通りです。

  • コードを変更しなくても出力形式や出力範囲を変えられる
  • logging の setLevel は意味がなくなる
  • logger はどこで定義しても良い

設定できるログレベル

ちなみに、高度なログ制御では、詳細な方から順に以下の 6 種類のレベルに対応しています。

  1. TRACE
  2. DEBUG
  3. INFO
  4. WARN
  5. ERROR
  6. FATAL

ただし、Python の場合、TRACE に当たるレベルはなく、CRITICAL が FATAL に該当します。

ここからは、なぜこのような挙動になるのか説明します。

Lambda の仕組み

Runtime API

Lambda のシステムは、以下のように説明されています。

lambda-service.png
Lambda のシステム構成(AWS より引用)

このうち、Lambda のコードをデプロイするのは赤で示された「Runtime + Function」の部分です。

ここで、入力や出力、ロギングなどは図の通り Runtime API とやり取りします。Runtime API の実体は通常の HTTP サーバーです。具体的に、ランタイムからは http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next のような形でアクセスできます。

Lambda はこのエンドポイントに対してリクエストを送ることで、次の関数の実行内容を取得したり、実行結果を送信したりしているという仕組みです。

インターフェースの詳細は以下のドキュメントで説明されています。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-api.html

Runtime Interface Client

しかし、ユーザーである我々は、多くの場合 Runtime API を意識することはありません。例えば、以下のように記述すれば、リクエストごとに入出力を処理できます。

def handler(event, context):
    print("Hello, world!")
    return event

このギャップを埋めているのが、Runtime Interface Client(RIC)です。RIC は Runtime API とやり取りし、デプロイしたコードを呼び出します。

以下で RIC のコードが公開されています。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-open-source.html

さらに、マネージドランタイムには初めから RIC が組み込まれており、ユーザー側が RIC を触ることはありません。
本記事で取り上げる高度なログ制御も、マネージドランタイムに組み込まれた RIC の機能になります。

RIC によるロガーの上書き

RIC は高度なログ制御により JSON 形式のログが指定されると、ロガーを上書きして目的の挙動になるように強制します。

Python

Python の RIC では以下のような実装がされています。

bootstrap.py
def _setup_logging(log_format, log_level, log_sink):
    logging.Formatter.converter = time.gmtime
    logger = logging.getLogger()


    if log_format == LogFormat.JSON or log_level:
        logger_handler = LambdaLoggerHandlerWithFrameType(log_sink)
    else:
        logger_handler = LambdaLoggerHandler(log_sink)


    if log_format == LogFormat.JSON:
        logger_handler.setFormatter(JsonFormatter())
    else:
        logger_handler.setFormatter(
            logging.Formatter(
                "[%(levelname)s]\t%(asctime)s.%(msecs)03dZ\t%(aws_request_id)s\t%(message)s\n",
                "%Y-%m-%dT%H:%M:%S",
            )
        )


    if log_level in logging._nameToLevel:
        logger.setLevel(log_level)


    logger_handler.addFilter(LambdaLoggerFilter())
    logger.addHandler(logger_handler)

引用:https://github.com/aws/aws-lambda-python-runtime-interface-client/blob/3810eedb72d3503468ed115345974e26f097c241/awslambdaric/bootstrap.py#L455-L479

このように、Lambda の設定によって Formatter や Level を変更しています。

ログレベルと関数とログとして出力される JSON の level プロパティの対応は次の通りです。

レベル 関数 level プロパティ
TRACE なし -
DEBUG logger.debug DEBUG
INFO logger.info INFO
WARN logger.warning WARNING
ERROR logger.error ERROR
FATAL logger.critical CRITICAL

Node.js

RIC の実装

Node.js には Python の logging のようなロガーは標準にないので、別の方法で対応されています。

log-patch.ts
private static patchConsoleMethods(logger: Logger): void {
  const createLogFunction = (level: LogLevel) => {
    if (!logger.shouldLog(level)) {
      return this.NopLog;
    }
    return (message: unknown, ...params: unknown[]): void => {
      logger.log(level, message, ...params);
    };
  };
  console.trace = createLogFunction(LOG_LEVEL.TRACE);
  console.debug = createLogFunction(LOG_LEVEL.DEBUG);
  console.info = createLogFunction(LOG_LEVEL.INFO);
  console.warn = createLogFunction(LOG_LEVEL.WARN);
  console.error = createLogFunction(LOG_LEVEL.ERROR);
  console.fatal = createLogFunction(LOG_LEVEL.FATAL);
  console.log = console.info;
}

引用:https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/f86a7a89575639f865bb850c7558c4198245689f/src/logging/log-patch.ts#L66-L84

このように、ログレベルに合わせて console オブジェクトのメソッドを上書きしています。従来の log レベルのログは info レベルに合わせられます。

実験

以下のコードで実験しました。

index.mjs
import { Console } from "node:console";

export const handler = async () => {
  console.trace("Hello, this is a TRACE level message");
  console.debug("Hello, this is a DEBUG level message");
  console.log("Hello, this is a LOG level message");
  console.info("Hello, this is an INFO level message");
  console.warn("Hello, this is a WARN level message");
  console.error("Hello, this is an ERROR level message");
  console.fatal("Hello, this is a FATAL level message");

  process.stdout.write("Hello, this is a STDOUT message\n");
  process.stderr.write("Hello, this is a STDERR message\n");

  const myConsole = new Console(process.stdout, process.stderr);
  myConsole.log("Hello, this is a message from a custom console instance");
};

以下の出力を得ました(システムログは省略)。

テキスト形式

2026-09-10T06:23:18.777Z	b11e5c61-f156-41bd-b405-c06386433327	TRACE	Hello, this is a TRACE level message
2026-09-10T06:23:18.797Z	b11e5c61-f156-41bd-b405-c06386433327	DEBUG	Hello, this is a DEBUG level message
2026-09-10T06:23:18.798Z	b11e5c61-f156-41bd-b405-c06386433327	INFO	Hello, this is a LOG level message
2026-09-10T06:23:18.798Z	b11e5c61-f156-41bd-b405-c06386433327	INFO	Hello, this is an INFO level message
2026-09-10T06:23:18.798Z	b11e5c61-f156-41bd-b405-c06386433327	WARN	Hello, this is a WARN level message
2026-09-10T06:23:18.798Z	b11e5c61-f156-41bd-b405-c06386433327	ERROR	Hello, this is an ERROR level message
2026-09-10T06:23:18.798Z	b11e5c61-f156-41bd-b405-c06386433327	FATAL	Hello, this is a FATAL level message
Hello, this is a STDOUT message
Hello, this is a STDERR message
Hello, this is a message from a custom console instance

JSON 形式

{
    "timestamp": "2026-09-10T06:24:28.941Z",
    "level": "INFO",
    "requestId": "c949bef0-1500-4854-8e12-19c4afa1c91a",
    "message": "Hello, this is a LOG level message"
}
{
    "timestamp": "2026-09-10T06:24:28.944Z",
    "level": "INFO",
    "requestId": "c949bef0-1500-4854-8e12-19c4afa1c91a",
    "message": "Hello, this is an INFO level message"
}
{
    "timestamp": "2026-09-10T06:24:28.962Z",
    "level": "WARN",
    "requestId": "c949bef0-1500-4854-8e12-19c4afa1c91a",
    "message": "Hello, this is a WARN level message"
}
{
    "timestamp": "2026-09-10T06:24:28.962Z",
    "level": "ERROR",
    "requestId": "c949bef0-1500-4854-8e12-19c4afa1c91a",
    "message": "Hello, this is an ERROR level message"
}
{
    "timestamp": "2026-09-10T06:24:28.962Z",
    "level": "FATAL",
    "requestId": "c949bef0-1500-4854-8e12-19c4afa1c91a",
    "message": "Hello, this is a FATAL level message"
}
Hello, this is a STDOUT message
Hello, this is a STDERR message
Hello, this is a message from a custom console instance

実験から、以下のことが分かりました。

  • console.logconsole.info は完全に同一
  • process.stdout 等で直接標準出力に出したものは構造化されない
  • new Console などで console オブジェクトを作成した場合には構造化が適用されない

ログレベルの対応

ログレベルの対応は以下の通りです。

レベル 関数 level プロパティ
TRACE console.trace TRACE
DEBUG console.debug DEBUG
INFO console.info INFO
INFO console.log INFO
WARN console.warn WARN
ERROR console.error ERROR
FATAL console.fatal FATAL

Ruby

RIC の実装

aws_lambda_ric.rb で以下のように Logger クラスを上書きしています。

aws_lambda_ric.rb
Logger.class_eval do
  prepend LoggerPatch
end

引用:https://github.com/aws/aws-lambda-ruby-runtime-interface-client/blob/7ae6e6c3e8d49a3ceb306150429fc540334df867/lib/aws_lambda_ric.rb#L143-L145

LoggerPatch は以下のように、設定に応じてフォーマットを切り替えています。

logger_patch.rb
if !logdev || logdev == $stdout || logdev == $stderr
  telemetry_sink = AwsLambdaRIC::TelemetryLogger.telemetry_log_sink
  logdev_override = telemetry_sink || logdev
  kwargs[:formatter] ||= LoggerPatch.json_format? ? JsonLogFormatter.new : LogFormatter.new
  kwargs[:level] = LoggerPatch.aws_lambda_log_level if !level_was_provided && LoggerPatch.aws_lambda_log_level
end
super(logdev_override, shift_age, shift_size, **kwargs)

引用:https://github.com/aws/aws-lambda-ruby-runtime-interface-client/blob/7ae6e6c3e8d49a3ceb306150429fc540334df867/lib/aws_lambda_ric/logger_patch.rb#L48-L55

実験

以下のコードで実験しました。

main.rb
require 'logger'

def handler(event:, context:)
    logger = Logger.new($stdout)
    logger.debug("Hello, this is a DEBUG level message.")
    logger.info("Hello, this is an INFO level message.")
    logger.warn("Hello, this is a WARN level message.")
    logger.error("Hello, this is an ERROR level message.")
    logger.fatal("Hello, this is a FATAL level message.")

    $stdout.puts("Hello, this is a message to STDOUT.")
    $stderr.puts("Hello, this is a message to STDERR.")
end

以下の出力を得ました。

テキスト形式

D, [2026-09-10T06:46:54.456919 #2] DEBUG 2f14f300-fa60-4cbc-814a-f4f89f2a6013 -- : Hello, this is a DEBUG level message.
I, [2026-09-10T06:46:54.457101 #2]  INFO 2f14f300-fa60-4cbc-814a-f4f89f2a6013 -- : Hello, this is an INFO level message.
W, [2026-09-10T06:46:54.457132 #2]  WARN 2f14f300-fa60-4cbc-814a-f4f89f2a6013 -- : Hello, this is a WARN level message.
E, [2026-09-10T06:46:54.457156 #2] ERROR 2f14f300-fa60-4cbc-814a-f4f89f2a6013 -- : Hello, this is an ERROR level message.
F, [2026-09-10T06:46:54.457179 #2] FATAL 2f14f300-fa60-4cbc-814a-f4f89f2a6013 -- : Hello, this is a FATAL level message.
Hello, this is a message to STDERR.
Hello, this is a message to STDOUT.

JSON 形式

{
    "timestamp": "2026-09-10T06:47:31.039202Z",
    "level": "INFO",
    "message": "Hello, this is an INFO level message.",
    "requestId": "a7ec8491-48d2-4199-b0c9-4e38bcb01021"
}

{
    "timestamp": "2026-09-10T06:47:31.039339Z",
    "level": "WARN",
    "message": "Hello, this is a WARN level message.",
    "requestId": "a7ec8491-48d2-4199-b0c9-4e38bcb01021"
}

{
    "timestamp": "2026-09-10T06:47:31.039381Z",
    "level": "ERROR",
    "message": "Hello, this is an ERROR level message.",
    "requestId": "a7ec8491-48d2-4199-b0c9-4e38bcb01021"
}

{
    "timestamp": "2026-09-10T06:47:31.039428Z",
    "level": "FATAL",
    "message": "Hello, this is a FATAL level message.",
    "requestId": "a7ec8491-48d2-4199-b0c9-4e38bcb01021"
}

Hello, this is a message to STDOUT.
Hello, this is a message to STDERR.

Node.js と同様、$stdout.puts などで直接標準出力に出すと構造化されないことが分かりました。

ログレベルの対応

ログレベルの対応は以下の通りです。

レベル 関数 level プロパティ
TRACE なし -
DEBUG logger.debug DEBUG
INFO logger.info INFO
WARN logger.warn WARN
ERROR logger.error ERROR
FATAL logger.fatal FATAL

.NET

RIC の実装

.NET には Microsoft.Extensions.Logging というロギングライブラリが存在しますが、これを上書きするような方法は取られていません。実際には、下記ファイルの通り独自のロガーが定義されており、高度なログ制御がサポートする全てのレベルに対応しています。ただし、使い勝手自体は Microsoft.Extensions.Logging に寄せられています。

https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Core/ILambdaLogger.cs

このロガーは、ハンドラーの引数に与えられるコンテキストから取得できます。

また、以下のような形で Console を使用した際に、直接標準出力に流さず、フォーマッタを通してから流すようにされています。

public ConsoleOutWrapper(ILambdaLogger logger)
{
    _standardOut = Console.Out;
    Console.SetOut(new WrapperTextWriter(_standardOut, logger, false));
    
    _standardError = Console.Error;
    Console.SetError(new WrapperTextWriter(_standardError, logger, false));

}

引用:https://github.com/aws/aws-lambda-dotnet/blob/8ed93ce74341fc51d4725e0668b5a0cce4fd8e3a/Tools/LambdaTestTool/src/Amazon.Lambda.TestTool/Runtime/ConsoleOutWrapper.cs#L17-L25

実験

以下のコードで実験しました。

using Amazon.Lambda.Core;
using Microsoft.Extensions.Logging;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace dotnet_function;

public class Function
{

    /// <summary>
    /// A simple function that takes a string and does a ToUpper
    /// </summary>
    /// <param name="input">The event for the Lambda function handler to process.</param>
    /// <param name="context">The ILambdaContext that provides methods for logging and describing the Lambda environment.</param>
    /// <returns></returns>
    public string FunctionHandler(object input, ILambdaContext context)
    {
        context.Logger.LogTrace("Hello, this is a TRACE level message");
        context.Logger.LogDebug("Hello, this is a DEBUG level message");
        context.Logger.LogInformation("Hello, this is an INFO level message");
        context.Logger.LogWarning("Hello, this is a WARN level message");
        context.Logger.LogError("Hello, this is an ERROR level message");
        context.Logger.LogCritical("Hello, this is a CRITICAL level message");

        Console.WriteLine("Hello, this is a STDOUT message");
        Console.Error.WriteLine("Hello, this is a STDERR message");

        ILogger myLogger = LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger("MyLogger");
        myLogger.LogInformation("Hello, this is a INFO level message from MyLogger");

        return "Success";
    }
}

以下の出力を得ました。

テキスト形式
2026-09-14T07:04:50.841Z	21e2adf3-9b04-4755-9230-248ef3836592	info	Hello, this is an INFO level message
2026-09-14T07:04:50.843Z	21e2adf3-9b04-4755-9230-248ef3836592	warn	Hello, this is a WARN level message
2026-09-14T07:04:50.899Z	21e2adf3-9b04-4755-9230-248ef3836592	fail	Hello, this is an ERROR level message
2026-09-14T07:04:50.899Z	21e2adf3-9b04-4755-9230-248ef3836592	crit	Hello, this is a CRITICAL level message
2026-09-14T07:04:50.899Z	21e2adf3-9b04-4755-9230-248ef3836592	info	Hello, this is a STDOUT message
2026-09-14T07:04:50.899Z	21e2adf3-9b04-4755-9230-248ef3836592	fail	Hello, this is a STDERR message
info: MyLogger[0]
      Hello, this is a INFO level message from MyLogger

JSON形式
{
    "timestamp": "2026-09-14T07:06:15.376Z",
    "level": "Information",
    "requestId": "14b3e0d4-10e5-4096-8324-18a00cf41219",
    "traceId": "Root=1-6aa79ce6-282bc78e595c1270002a6eb9;Parent=739bac9c0106cfd6;Sampled=0;Lineage=1:e90e5035:0",
    "message": "Hello, this is an INFO level message"
}

{
    "timestamp": "2026-09-14T07:06:15.434Z",
    "level": "Warning",
    "requestId": "14b3e0d4-10e5-4096-8324-18a00cf41219",
    "traceId": "Root=1-6aa79ce6-282bc78e595c1270002a6eb9;Parent=739bac9c0106cfd6;Sampled=0;Lineage=1:e90e5035:0",
    "message": "Hello, this is a WARN level message"
}

{
    "timestamp": "2026-09-14T07:06:15.435Z",
    "level": "Error",
    "requestId": "14b3e0d4-10e5-4096-8324-18a00cf41219",
    "traceId": "Root=1-6aa79ce6-282bc78e595c1270002a6eb9;Parent=739bac9c0106cfd6;Sampled=0;Lineage=1:e90e5035:0",
    "message": "Hello, this is an ERROR level message"
}

{
    "timestamp": "2026-09-14T07:06:15.435Z",
    "level": "Critical",
    "requestId": "14b3e0d4-10e5-4096-8324-18a00cf41219",
    "traceId": "Root=1-6aa79ce6-282bc78e595c1270002a6eb9;Parent=739bac9c0106cfd6;Sampled=0;Lineage=1:e90e5035:0",
    "message": "Hello, this is a CRITICAL level message"
}

{
    "timestamp": "2026-09-14T07:06:15.435Z",
    "level": "Information",
    "requestId": "14b3e0d4-10e5-4096-8324-18a00cf41219",
    "traceId": "Root=1-6aa79ce6-282bc78e595c1270002a6eb9;Parent=739bac9c0106cfd6;Sampled=0;Lineage=1:e90e5035:0",
    "message": "Hello, this is a STDOUT message"
}

{
    "timestamp": "2026-09-14T07:06:15.435Z",
    "level": "Error",
    "requestId": "14b3e0d4-10e5-4096-8324-18a00cf41219",
    "traceId": "Root=1-6aa79ce6-282bc78e595c1270002a6eb9;Parent=739bac9c0106cfd6;Sampled=0;Lineage=1:e90e5035:0",
    "message": "Hello, this is a STDERR message"
}

info: MyLogger[0]
      Hello, this is a INFO level message from MyLogger

JSON 形式を見ると、context.LoggerConsole を使用したログは、全て構造化されて出力されていました。特に、Console.WriteLine は Information レベル、Console.Error.WriteLine は Error レベルで出力されました。

一方、Microsoft.Extensions.LoggingLogger を使用した出力では、構造化されずにそのまま出力されました。

ログレベルの対応

ログレベルの対応は以下の通りです。

レベル 関数 level プロパティ
TRACE context.Logger.LogTrace Trace
DEBUG context.Logger.LogDebug Debug
INFO Console.WriteLine Information
INFO context.Logger.LogInformation Information
WARN context.Logger.LogWarning Warning
ERROR Console.Error.WriteLine Error
ERROR context.Logger.LogError Error
FATAL context.Logger.LogCritical Critical

Java

RIC の実装

Java も .NET と同様、コンテキストからアクセスできる独自のロガーを定義しています。

https://github.com/aws/aws-lambda-java-libs/blob/e38423df9a875573861c0a9041bac6ef8b86f5c7/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LambdaContextLogger.java#L14

また、log4j2 利用者向けに、プラグイン(Lambda Appender)も提供しています。

https://github.com/aws/aws-lambda-java-libs/blob/e38423df9a875573861c0a9041bac6ef8b86f5c7/aws-lambda-java-log4j2/src/main/java/com/amazonaws/services/lambda/runtime/log4j2/LambdaAppender.java#L28-L30

実験

次のコードで実験しました。

package com.example.function;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.logging.LogLevel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Handler implements RequestHandler<Object, String> {

    private static final Logger log4jLogger = LogManager.getLogger(Handler.class);

    @Override
    public String handleRequest(Object input, Context context) {
        LambdaLogger logger = context.getLogger();

        logger.log("Hello, this is a TRACE level message", LogLevel.TRACE);
        logger.log("Hello, this is a DEBUG level message", LogLevel.DEBUG);
        logger.log("Hello, this is an INFO level message", LogLevel.INFO);
        logger.log("Hello, this is a WARN level message", LogLevel.WARN);
        logger.log("Hello, this is an ERROR level message", LogLevel.ERROR);
        logger.log("Hello, this is a FATAL level message", LogLevel.FATAL);

        System.out.println("Hello, this is a STDOUT message");
        System.err.println("Hello, this is a STDERR message");

        log4jLogger.trace("Hello, this is a TRACE level message from Log4j2");
        log4jLogger.debug("Hello, this is a DEBUG level message from Log4j2");
        log4jLogger.info("Hello, this is an INFO level message from Log4j2");
        log4jLogger.warn("Hello, this is a WARN level message from Log4j2");
        log4jLogger.error("Hello, this is an ERROR level message from Log4j2");
        log4jLogger.fatal("Hello, this is a FATAL level message from Log4j2");

        return "Success";
    }
}

以下の出力を得ました。

テキスト形式
[TRACE] Hello, this is a TRACE level message
[DEBUG] Hello, this is a DEBUG level message
[INFO] Hello, this is an INFO level message
[WARN] Hello, this is a WARN level message
[ERROR] Hello, this is an ERROR level message
[FATAL] Hello, this is a FATAL level message
Hello, this is a STDOUT message
Hello, this is a STDERR message
2026-09-14 08:21:20 9f294d99-8513-4f13-be96-36a5371f9471 TRACE Handler - Hello, this is a TRACE level message from Log4j2
2026-09-14 08:21:20 9f294d99-8513-4f13-be96-36a5371f9471 DEBUG Handler - Hello, this is a DEBUG level message from Log4j2
2026-09-14 08:21:20 9f294d99-8513-4f13-be96-36a5371f9471 INFO  Handler - Hello, this is an INFO level message from Log4j2
2026-09-14 08:21:20 9f294d99-8513-4f13-be96-36a5371f9471 WARN  Handler - Hello, this is a WARN level message from Log4j2
2026-09-14 08:21:20 9f294d99-8513-4f13-be96-36a5371f9471 ERROR Handler - Hello, this is an ERROR level message from Log4j2
2026-09-14 08:21:20 9f294d99-8513-4f13-be96-36a5371f9471 FATAL Handler - Hello, this is a FATAL level message from Log4j2
JSON 形式
{
    "timestamp": "2026-09-14T08:21:51.211Z",
    "message": "Hello, this is an INFO level message",
    "level": "INFO",
    "AWSRequestId": "d212fbcb-128c-4783-a0f0-e20547d1b028"
}

{
    "timestamp": "2026-09-14T08:21:51.254Z",
    "message": "Hello, this is a WARN level message",
    "level": "WARN",
    "AWSRequestId": "d212fbcb-128c-4783-a0f0-e20547d1b028"
}

{
    "timestamp": "2026-09-14T08:21:51.254Z",
    "message": "Hello, this is an ERROR level message",
    "level": "ERROR",
    "AWSRequestId": "d212fbcb-128c-4783-a0f0-e20547d1b028"
}

{
    "timestamp": "2026-09-14T08:21:51.254Z",
    "message": "Hello, this is a FATAL level message",
    "level": "FATAL",
    "AWSRequestId": "d212fbcb-128c-4783-a0f0-e20547d1b028"
}

Hello, this is a STDOUT message
Hello, this is a STDERR message
{
    "timestamp": "2026-09-14T08:21:51.653Z",
    "level": "INFO",
    "message": "Hello, this is an INFO level message from Log4j2",
    "logger": "com.example.function.Handler",
    "AWSRequestId": "d212fbcb-128c-4783-a0f0-e20547d1b028"
}

{
    "timestamp": "2026-09-14T08:21:51.671Z",
    "level": "WARN",
    "message": "Hello, this is a WARN level message from Log4j2",
    "logger": "com.example.function.Handler",
    "AWSRequestId": "d212fbcb-128c-4783-a0f0-e20547d1b028"
}

{
    "timestamp": "2026-09-14T08:21:51.672Z",
    "level": "ERROR",
    "message": "Hello, this is an ERROR level message from Log4j2",
    "logger": "com.example.function.Handler",
    "AWSRequestId": "d212fbcb-128c-4783-a0f0-e20547d1b028"
}

{
    "timestamp": "2026-09-14T08:21:51.672Z",
    "level": "FATAL",
    "message": "Hello, this is a FATAL level message from Log4j2",
    "logger": "com.example.function.Handler",
    "AWSRequestId": "d212fbcb-128c-4783-a0f0-e20547d1b028"
}

コンテキストから得た logger や log4j を使用したログは正しく構造化され、いずれも設定に応じたログレベルが出るようになりました。また、System.out.println などの通常の標準出力関数では構造化されませんでした。

ログレベルの対応

ログレベルの対応は以下の通りです。

レベル 関数 level プロパティ
TRACE LogLevel.TRACE / logger.trace TRACE
DEBUG LogLevel.DEBUG / logger.debug DEBUG
INFO LogLevel.INFO / logger.info INFO
WARN LogLevel.WARN / logger.warn WARN
ERROR LogLevel.ERROR / logger.error ERROR
FATAL LogLevel.FATAL / logger.fatal FATAL

マネージドランタイム以外の言語

Go や Rust のような言語では、マネージドランタイムは提供されていません。そのため、公開されている RIC をリンクして、OS 専用ランタイムで実行します。

一部の RIC は環境変数を通してログ設定を受け取ることで、設定に応じたログ出力ができます。

Rust

RIC の実装

以下の通り tracing_subscriber Crate のフォーマットを再構成し、tracing trait として再 export しています。

let collector = tracing_subscriber::fmt()
    .with_target(false)
    .without_time()
    .with_env_filter(
        EnvFilter::builder()
            .with_default_directive(log_level.into())
            .from_env_lossy(),
    )
    .with_writer(writer);

引用:https://github.com/aws/aws-lambda-rust-runtime/blob/831d4817db282a0e2f396c910bc1a1261898c947/lambda-runtime-api-client/src/tracing.rs#L69-L77

実験

以下のコードで実験しました。

use lambda_runtime::{run, service_fn, tracing, Error, LambdaEvent};
use serde_json::Value;

async fn function_handler(_event: LambdaEvent<Value>) -> Result<String, Error> {
    tracing::trace!("Hello, this is a TRACE level message");
    tracing::debug!("Hello, this is a DEBUG level message");
    tracing::info!("Hello, this is an INFO level message");
    tracing::warn!("Hello, this is a WARN level message");
    tracing::error!("Hello, this is an ERROR level message");

    println!("Hello, this is a STDOUT message");
    eprintln!("Hello, this is a STDERR message");

    Ok("Success".to_string())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing::init_default_subscriber();

    run(service_fn(function_handler)).await
}

以下の出力を得ました。

テキスト形式
TRACE Hello, this is a TRACE level message
DEBUG Hello, this is a DEBUG level message
INFO Hello, this is an INFO level message
WARN Hello, this is a WARN level message
ERROR Hello, this is an ERROR level message
Hello, this is a STDOUT message
Hello, this is a STDERR message
JSON 形式
Hello, this is a STDERR message
{
    "level": "INFO",
    "fields": {
        "message": "Hello, this is an INFO level message"
    }
}

{
    "level": "WARN",
    "fields": {
        "message": "Hello, this is a WARN level message"
    }
}

{
    "level": "ERROR",
    "fields": {
        "message": "Hello, this is an ERROR level message"
    }
}

Hello, this is a STDOUT message

tracing trait を使用したログでは構造化されましたが、println! マクロでは構造化されませんでした。
また、同じ出力方法であれば出力順序は保たれましたが、tracingprintln!/eprintln! の間で出力順序が保たれませんでした。ログの方式によって使用されるバッファーが異なっている可能性があります。詳細は、本記事の検証範囲外とします。

ログレベルの対応

ログレベルの対応は以下の通りです。

レベル マクロ level プロパティ
TRACE tracing::trace! TRACE
DEBUG tracing::debug! DEBUG
INFO tracing::info! INFO
WARN tracing::warn! WARN
ERROR tracing::error! ERROR
FATAL なし -

おわりに

Lambda で使用できる高度なログ制御機能について、対応している全ての RIC を検証しました。

はじめ、Python の logging や Node.js の console に違和感を覚えたため詳細に検証しました。全体としては、言語の自由度によって様々な方法が取られているという印象でした。中にはモンキーパッチのような方法が取られており、デザインの綺麗さと使いやすさのトレードオフが考えられている機能だと感じました。

機能自体は非常に便利なため、今後も積極的に使っていきたいです。しかし、各言語によって挙動の微差があるため、何が行われているのかを理解することが重要だと分かりました。

この記事をシェアする

関連記事