Describe the bug
When a function uses LoggingConfig.LogFormat: JSON and bridges Microsoft.Extensions.Logging.ILogger to the Lambda logging API via Amazon.Lambda.Logging.AspNetCore, structured log events can come out with their JSON properties holding the wrong values (each property shifted to a neighboring placeholder's value).
Root cause, as far as I can tell, is a combination of two behaviors:
LambdaILogger extracts the message template from {OriginalFormat} and collects the state values in state enumeration order (LambdaILogger.cs).
JsonLogMessageFormatter then pairs template placeholders with arguments strictly by position (JsonLogMessageFormatter.cs).
The Microsoft.Extensions.Logging contract is that placeholders are resolved by name against the state key/value pairs; nothing guarantees that state enumeration order matches placeholder order. The most prominent violator ships in ASP.NET Core itself: HostingRequestStartingLog enumerates its state as Protocol, Method, ContentType, ContentLength, Scheme, Host, PathBase, Path, QueryString, while its template is:
Request starting {Protocol} {Method} {Scheme}://{Host}{PathBase}{Path}{QueryString} - {ContentType} {ContentLength}
So every Request starting log line of every ASP.NET Core app running on Lambda with JSON log format is silently garbled from the third placeholder on.
Regression Issue
Expected Behavior
Template placeholders resolved by name, as with every other Microsoft.Extensions.Logging sink:
{
"message": "Request starting HTTP/1.1 DELETE https://api.example.com/events/123?preserve=true - application/json 0",
"Method": "DELETE",
"Scheme": "https",
"Host": "api.example.com",
"Path": "/events/123",
"QueryString": "?preserve=true",
"ContentType": "application/json",
"ContentLength": 0
}
Current Behavior
Actual CloudWatch log record (values anonymized). Note Scheme holding the content type, Host holding the content length, Path holding the host, ContentType holding the path, etc.:
{
"timestamp": "2026-07-04T22:43:57.529Z",
"level": "Information",
"requestId": "188cbe50-087d-4465-acf4-d4a6067ef0da",
"message": "Request starting {Protocol} DELETE application/json://0httpsapi.example.com - /events/123 ?preserve=true",
"Method": "DELETE",
"Scheme": "application/json",
"Host": 0,
"PathBase": "https",
"Path": "api.example.com",
"QueryString": "",
"ContentType": "/events/123",
"ContentLength": "?preserve=true"
}
Each placeholder received the state value at the same index instead of the value with the matching name ({Scheme} ← ContentType, {Host} ← ContentLength, {PathBase} ← Scheme, ...). The rendered message is garbled the same way.
Reproduction Steps
Minimal ASP.NET Core app:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);
builder.Logging.ClearProviders();
builder.Logging.AddLambdaLogger();
var app = builder.Build();
app.MapGet("/", () => "ok");
app.Run();
Deploy behind API Gateway with:
LoggingConfig:
LogFormat: JSON
Invoke any endpoint and inspect the Request starting record in CloudWatch: the Scheme, Host, PathBase, Path, QueryString, ContentType and ContentLength properties are shifted as shown above.
Possible Solution
Either:
- In
LambdaILogger (JSON branch): parse the placeholder names from {OriginalFormat} and emit the arguments in placeholder order, resolving each one by key against the state; or
- In
JsonLogMessageFormatter: when arguments come from an ILogger state, match them to placeholders by name instead of by position.
Additional Information/Context
Any ILogger event whose state enumeration order differs from its template placeholder order is affected — ASP.NET Core hosting logs are just the guaranteed-out-of-the-box case. Logs written through the standard LoggerExtensions/LoggerMessage paths happen to be unaffected because FormattedLogValues keeps both orders aligned, which is probably why this went unnoticed.
AWS .NET SDK and/or Package version used
Amazon.Lambda.AspNetCoreServer 10.1.1
Amazon.Lambda.AspNetCoreServer.Hosting 2.1.0
Amazon.Lambda.Logging.AspNetCore 5.0.0
Amazon.Lambda.Core 3.1.0
Targeted .NET Platform
.NET 10 (managed dotnet10 runtime)
Operating System and version
Amazon Linux 2023 (Lambda managed runtime)
Describe the bug
When a function uses
LoggingConfig.LogFormat: JSONand bridgesMicrosoft.Extensions.Logging.ILoggerto the Lambda logging API viaAmazon.Lambda.Logging.AspNetCore, structured log events can come out with their JSON properties holding the wrong values (each property shifted to a neighboring placeholder's value).Root cause, as far as I can tell, is a combination of two behaviors:
LambdaILoggerextracts the message template from{OriginalFormat}and collects the state values in state enumeration order (LambdaILogger.cs).JsonLogMessageFormatterthen pairs template placeholders with arguments strictly by position (JsonLogMessageFormatter.cs).The
Microsoft.Extensions.Loggingcontract is that placeholders are resolved by name against the state key/value pairs; nothing guarantees that state enumeration order matches placeholder order. The most prominent violator ships in ASP.NET Core itself:HostingRequestStartingLogenumerates its state asProtocol, Method, ContentType, ContentLength, Scheme, Host, PathBase, Path, QueryString, while its template is:So every
Request startinglog line of every ASP.NET Core app running on Lambda with JSON log format is silently garbled from the third placeholder on.Regression Issue
Expected Behavior
Template placeholders resolved by name, as with every other
Microsoft.Extensions.Loggingsink:{ "message": "Request starting HTTP/1.1 DELETE https://api.example.com/events/123?preserve=true - application/json 0", "Method": "DELETE", "Scheme": "https", "Host": "api.example.com", "Path": "/events/123", "QueryString": "?preserve=true", "ContentType": "application/json", "ContentLength": 0 }Current Behavior
Actual CloudWatch log record (values anonymized). Note
Schemeholding the content type,Hostholding the content length,Pathholding the host,ContentTypeholding the path, etc.:{ "timestamp": "2026-07-04T22:43:57.529Z", "level": "Information", "requestId": "188cbe50-087d-4465-acf4-d4a6067ef0da", "message": "Request starting {Protocol} DELETE application/json://0httpsapi.example.com - /events/123 ?preserve=true", "Method": "DELETE", "Scheme": "application/json", "Host": 0, "PathBase": "https", "Path": "api.example.com", "QueryString": "", "ContentType": "/events/123", "ContentLength": "?preserve=true" }Each placeholder received the state value at the same index instead of the value with the matching name (
{Scheme}←ContentType,{Host}←ContentLength,{PathBase}←Scheme, ...). The renderedmessageis garbled the same way.Reproduction Steps
Minimal ASP.NET Core app:
Deploy behind API Gateway with:
Invoke any endpoint and inspect the
Request startingrecord in CloudWatch: theScheme,Host,PathBase,Path,QueryString,ContentTypeandContentLengthproperties are shifted as shown above.Possible Solution
Either:
LambdaILogger(JSON branch): parse the placeholder names from{OriginalFormat}and emit the arguments in placeholder order, resolving each one by key against the state; orJsonLogMessageFormatter: when arguments come from anILoggerstate, match them to placeholders by name instead of by position.Additional Information/Context
Any
ILoggerevent whose state enumeration order differs from its template placeholder order is affected — ASP.NET Core hosting logs are just the guaranteed-out-of-the-box case. Logs written through the standardLoggerExtensions/LoggerMessagepaths happen to be unaffected becauseFormattedLogValueskeeps both orders aligned, which is probably why this went unnoticed.AWS .NET SDK and/or Package version used
Amazon.Lambda.AspNetCoreServer 10.1.1
Amazon.Lambda.AspNetCoreServer.Hosting 2.1.0
Amazon.Lambda.Logging.AspNetCore 5.0.0
Amazon.Lambda.Core 3.1.0
Targeted .NET Platform
.NET 10 (managed
dotnet10runtime)Operating System and version
Amazon Linux 2023 (Lambda managed runtime)