Make Your .NET AWS Lambda Logs Searchable with JSON Structured Logging
Learn how to implement JSON structured logging in .NET Lambda functions to make your logs easier to query, filter, and analyze in CloudWatch.
Have you ever struggled to find useful information in your .NET Lambda Function logs?
That's usually because they're just plain text — hard to filter, hard to search, and even harder to analyze.
In this post, let's explore the different ways to log in .NET Lambda functions, and how switching to JSON structured logging makes everything easier to query, filter, and understand.
In this post, let's
- Explore logging in .NET Lambda Functions
 - Learn how to switch to JSON structured logging
 - Discover parameterized logging and its advantages
 - See how to query structured logs in CloudWatch Logs Insights
 
Thanks to AWS for sponsoring this article in my .NET on AWS Series.
Logging in .NET Lambda Functions
Logging in .NET Lambda functions is done through the Log methods from the context.Logger property of the ILambdaContext.
Alternatively, the Write methods from System.Console and System.Console.Error are captured as informational log messages and error log messages, respectively.
Here's a simple example of logging using string interpolation in a Lambda function:
public string FunctionHandler(Order order, ILambdaContext context){context.Logger.LogInformation($"Processing order {order.OrderId} placed on {order.OrderDate}");// Process the order...return "Order processed successfully";}
If you're completely new to AWS Lambda, I highly recommend checking out my getting started guide linked below.
AWS Lambda For The .NET Developer: How To Easily Get Started

Once deployed, you can navigate to your AWS Lambda function in the AWS Console and execute it to see these logs in CloudWatch.
Default Text-Based Logging
By default, the log format of Lambda functions is Text.
When you navigate to the Monitor tab and view the CloudWatch logs for your function, you'll see plain text entries like this:
INFO Processing order 3fa85f64-5717-4562-b3fc-2c963f66afa6 placed on 2025-10-21
As you can see, this is purely text-based logging. While this works, it makes it hard to extract meaningful information from your logs, especially when you need to filter or query specific data.
Switching to JSON-Based Logging
To switch to JSON-based logging, navigate to your Lambda function in the AWS Console, go to the Configuration tab, and under Monitoring and operations tools, click on the logging configuration.
Click Edit and switch the log format to JSON. You can also configure the log levels here — let's leave it at Info for now.

Save the changes, and your Lambda function will now output logs in JSON format.
When you invoke the function again and check CloudWatch, you'll see a new log stream with JSON-formatted entries. Expanding one of these records shows:
{"timestamp": "2025-10-21T10:30:45.123Z","level": "Information","message": "Processing order 3fa85f64-5717-4562-b3fc-2c963f66afa6 placed on 2025-10-21",...}
However, just by switching to JSON-based logging, you're not gaining much yet. The real power comes when you start using parameterized logging.
Parameterized Logging
Instead of using string interpolation in your log statements, you can pass parameters to make individual values available as properties in the JSON log.
Here's how to update the previous example:
public string FunctionHandler(Order order, ILambdaContext context){context.Logger.LogInformation("Processing order {OrderId} placed on {OrderDate}",order.OrderId,order.OrderDate);// Process the order...return "Order processed successfully";}
Notice how we replaced the string interpolation with placeholders ({OrderId} and {OrderDate}) and passed the actual values as parameters.
Advantages of Parameterized Logging
There are two key advantages to parameterizing your log statements:
- 
Performance: If the log level isn't going to be processed (e.g., a
LogDebugstatement when logging is set toInformation), the entire statement gets filtered out without doing any string allocations that would have happened with string interpolation. - 
Structured Properties: Each parameter becomes a property in the JSON log document, making it independently queryable and filterable.
 
When you deploy this change and invoke the function, the CloudWatch log entry now looks like this:
{"timestamp": "2025-10-21T10:30:45.123Z","level": "Information","message": "Processing order 3fa85f64-5717-4562-b3fc-2c963f66afa6 placed on 2025-10-21","OrderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6","OrderDate": "2025-10-21T00:00:00Z",...}
Now OrderId and OrderDate are separate properties in the JSON log, available for querying and filtering.
Composite Formatting
You can also format your log parameters using composite formatting.
For example, if you want to format the OrderDate to display only the year, month, and day:
context.Logger.LogInformation("Processing order {OrderId} placed on {OrderDate:yyyy-MM-dd}",order.OrderId,order.OrderDate);
The format string yyyy-MM-dd is applied to the OrderDate parameter, and the log output will show:
{"OrderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6","OrderDate": "2025-10-21",...}
Logging Complex Data
Let's say you want to log more details about the order, such as the shipping address. In your Order class, you have an Address property with fields like City, Country, and PostCode.
Using ToString()
If your Address class has an overridden ToString() method:
public class Address{public string City { get; set; }public string Country { get; set; }public string PostCode { get; set; }public override string ToString() => $"{City}, {Country}";}
You can log it like this:
context.Logger.LogInformation("Processing order {OrderId} placed on {OrderDate:yyyy-MM-dd} shipped to {Address}",order.OrderId,order.OrderDate,order.ShippingAddress);
The log output will use the ToString() representation:
{"OrderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6","OrderDate": "2025-10-21","Address": "Brisbane, Australia",...}
Logging Full Objects
If you want to log all the properties of the Address object (not just the ToString() representation), use the @ symbol before the parameter name:
context.Logger.LogInformation("Processing order {OrderId} placed on {OrderDate:yyyy-MM-dd} shipped to {@Address}",order.OrderId,order.OrderDate,order.ShippingAddress);
Now the log includes the full object structure:
{"OrderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6","OrderDate": "2025-10-21","Address": {"City": "Brisbane","Country": "Australia","PostCode": "4000"},...}
Be careful not to log PII (Personally Identifiable Information)
unintentionally. You can use attributes like [JsonIgnore] on your class
properties to prevent sensitive data from being logged.
Logging Collections
You can also log collections of complex objects using the same @ syntax.
For example, if your Order has a list of OrderItems:
context.Logger.LogInformation("Processing order {OrderId} placed on {OrderDate:yyyy-MM-dd} shipped to {@Address} with Items {@Items}",order.OrderId,order.OrderDate,order.ShippingAddress,order.Items);
The log output will include the items as an array:
{"OrderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6","OrderDate": "2025-10-21","Address": {"City": "Brisbane","Country": "Australia","PostCode": "4000"},"Items": [{"ProductId": "ABC123","Quantity": 2,"Price": 29.99},{"ProductId": "XYZ789","Quantity": 1,"Price": 49.99}],...}
Querying Logs with CloudWatch Logs Insights
So, what's the big deal about writing logs in JSON?
It's all about making your logs easier to query, filter, and analyze.
With structured logging, each property in your log becomes a searchable field. Let's see how to use CloudWatch Logs Insights to query these logs.
Navigate to CloudWatch in the AWS Console, and go to Logs Insights. This allows you to query across all log streams for your Lambda function.
Filtering by OrderId
Let's say you want to find all logs for a specific OrderId. You can write a query like this:
fields @timestamp, @message| filter OrderId = "3fa85f64-5717-4562-b3fc-2c963f66afa6"| sort @timestamp desc
This will return all log entries that have the matching OrderId, making it easy to track specific orders through your system.
If you only want to see the message field, you can specify that:
fields @message| filter OrderId = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
Practical Use Cases
Depending on your application's use case, you can:
- Pass an 
OrderIdto various log statements throughout your application to track an order's journey - Use a 
RequestIdto filter all logs related to a specific request - Query by date ranges, error types, or any other structured property
 
This makes debugging and troubleshooting significantly easier compared to plain text logs.
If you want a deeper dive into CloudWatch Logs and analysis, check out the post below.
CloudWatch Logs Insights Deep Dive

Summary
Switching to JSON structured logging in your .NET Lambda functions is straightforward and provides significant benefits:
- Enable JSON logging in your Lambda configuration
 - Use parameterized logging instead of string interpolation
 - Leverage the 
@symbol to log complex objects and collections - Query your logs efficiently using CloudWatch Logs Insights
 
These changes make your logs more powerful, searchable, and actionable — helping you debug issues faster and understand your application's behavior better.
The complete source code is available on my GitHub repository.
Thanks for reading! If you found this helpful, make sure to check out more content on .NET and AWS on my channel.