Lambda の「高度なログ制御」は何をしているのか
はじめに
こんにちは、AI 事業本部の Kanaru です。
AWS Lambda にはログ整形やログレベルの制御が可能な「高度なログ制御機能」があります。これは、特定のログの出力方法を利用していれば、Lambda 側の設定で、JSON で構造化したり出力するログレベルを変更できる機能です。
例えば、Node.js なら console、Python なら logging を使用してプログラムを書けば、プログラム側で整形やログレベルの設定をせずとも Lambda が自動的に制御してくれます。
しかし、このような既存の動作を変更するような仕様は、理解しないまま使用すると予期しない動作を引き起こす可能性があります。そこで本記事では、この「高度なログ制御機能」が内部的にどのような処理をしているのか解説します。
本記事の検証コードは以下の通りです。
対象読者
- Lambda の高度なログ制御の利用を検討している/利用している方
- 自分の使用言語で Lambda の高度なログ制御がどのように機能しているかを知りたい方
この記事で紹介すること
- 高度なログ制御の基本的な仕組み
- 各言語におけるログに関する RIC の実装
- 各言語におけるログ出力の実験とその結果
- 各言語のログレベルの対応
この記事で紹介しないこと
- 高度なログ制御の使い方
実験
高度なログ制御がどのように機能するか、実際にやってみます。
次のような Python コードを用意し、デプロイします。
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
ここで、次のような設定をしました。

実行すると、ログは次のように変わりました。
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 種類のレベルに対応しています。
- TRACE
- DEBUG
- INFO
- WARN
- ERROR
- FATAL
ただし、Python の場合、TRACE に当たるレベルはなく、CRITICAL が FATAL に該当します。
ここからは、なぜこのような挙動になるのか説明します。
Lambda の仕組み
Runtime API
Lambda のシステムは、以下のように説明されています。

Lambda のシステム構成(AWS より引用)
このうち、Lambda のコードをデプロイするのは赤で示された「Runtime + Function」の部分です。
ここで、入力や出力、ロギングなどは図の通り Runtime API とやり取りします。Runtime API の実体は通常の HTTP サーバーです。具体的に、ランタイムからは http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next のような形でアクセスできます。
Lambda はこのエンドポイントに対してリクエストを送ることで、次の関数の実行内容を取得したり、実行結果を送信したりしているという仕組みです。
インターフェースの詳細は以下のドキュメントで説明されています。
Runtime Interface Client
しかし、ユーザーである我々は、多くの場合 Runtime API を意識することはありません。例えば、以下のように記述すれば、リクエストごとに入出力を処理できます。
def handler(event, context):
print("Hello, world!")
return event
このギャップを埋めているのが、Runtime Interface Client(RIC)です。RIC は Runtime API とやり取りし、デプロイしたコードを呼び出します。
以下で RIC のコードが公開されています。
さらに、マネージドランタイムには初めから RIC が組み込まれており、ユーザー側が RIC を触ることはありません。
本記事で取り上げる高度なログ制御も、マネージドランタイムに組み込まれた RIC の機能になります。
RIC によるロガーの上書き
RIC は高度なログ制御により JSON 形式のログが指定されると、ロガーを上書きして目的の挙動になるように強制します。
Python
Python の RIC では以下のような実装がされています。
bootstrap.pydef _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)
このように、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.tsprivate 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; }
このように、ログレベルに合わせて console オブジェクトのメソッドを上書きしています。従来の log レベルのログは info レベルに合わせられます。
実験
以下のコードで実験しました。
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.logとconsole.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.rbLogger.class_eval do prepend LoggerPatch end
LoggerPatch は以下のように、設定に応じてフォーマットを切り替えています。
logger_patch.rbif !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)
実験
以下のコードで実験しました。
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 に寄せられています。
このロガーは、ハンドラーの引数に与えられるコンテキストから取得できます。
また、以下のような形で 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)); }
実験
以下のコードで実験しました。
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.Logger や Console を使用したログは、全て構造化されて出力されていました。特に、Console.WriteLine は Information レベル、Console.Error.WriteLine は Error レベルで出力されました。
一方、Microsoft.Extensions.Logging の Logger を使用した出力では、構造化されずにそのまま出力されました。
ログレベルの対応
ログレベルの対応は以下の通りです。
| レベル | 関数 | 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 と同様、コンテキストからアクセスできる独自のロガーを定義しています。
また、log4j2 利用者向けに、プラグイン(Lambda Appender)も提供しています。
実験
次のコードで実験しました。
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);
実験
以下のコードで実験しました。
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! マクロでは構造化されませんでした。
また、同じ出力方法であれば出力順序は保たれましたが、tracing と println!/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 に違和感を覚えたため詳細に検証しました。全体としては、言語の自由度によって様々な方法が取られているという印象でした。中にはモンキーパッチのような方法が取られており、デザインの綺麗さと使いやすさのトレードオフが考えられている機能だと感じました。
機能自体は非常に便利なため、今後も積極的に使っていきたいです。しかし、各言語によって挙動の微差があるため、何が行われているのかを理解することが重要だと分かりました。


