Skip to main content

How to Write JSON Logs in ASP.NET Core

· 4 min read
Jagdish Kumawat
Founder @ Dewiride

Plain-text logs are nice to read, and hard for a log tool to search. AddJsonConsole writes one JSON object per entry, with each value in a field of its own. Keep plain text in Development, and use JSON everywhere else.

What You Need​

The .NET 10 SDK, and a small project: dotnet new web --no-https -n WebDemo. It's built in.

Step 1: Turn It On Outside Development​

Replace Program.cs:

Program.cs
var builder = WebApplication.CreateBuilder(args);

// One JSON object per log entry outside Development, for log tools to read.
if (!builder.Environment.IsDevelopment())
{
builder.Logging.AddJsonConsole(options =>
{
options.UseUtcTimestamp = true;
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss.fffZ";
});
}

var app = builder.Build();

app.MapGet("/orders/{id:int}", (int id, ILogger<Program> logger) =>
{
logger.LogInformation("Looking up order {OrderId}", id);
return id;
});

app.Run();

The AddJsonConsole code in a macOS-style code window: JSON console logs outside Development, with UTC timestamps, and an endpoint that logs an order id.

  • IsDevelopment leaves your own terminal readable.
  • UseUtcTimestamp keeps every server on one clock.
  • TimestampFormat adds a Timestamp field, which isn't there by default.

Step 2: Compare the Two​

With no launch profile and no ASPNETCORE_ENVIRONMENT, the app runs as Production:

Terminal 1
dotnet run --no-launch-profile --urls http://localhost:5300

Send a request to http://localhost:5300/orders/42, and the log says:

Output, as Production
{"Timestamp":"2026-11-06T06:24:41.702Z","EventId":0,"LogLevel":"Information","Category":"Program","Message":"Looking up order 42","State":{"OrderId":42,"{OriginalFormat}":"Looking up order {OrderId}"}}

As Development, in plain text:

Output, as Development
info: Program[0]
Looking up order 42

The same log entry as one JSON object with Timestamp, LogLevel, Category, Message and State fields, and as plain text in Development.

Look at State: the value from {OrderId} has a field of its own, as a number. So a log tool can find State.OrderId = 42, which is what you want from structured logging. That only works if you log with named placeholders. With $"…{id}", there's nothing to separate.

Do It in Configuration Instead​

Microsoft's documentation shows the same through appsettings.json:

appsettings.json
"Logging": {
"Console": {
"FormatterName": "json",
"FormatterOptions": {
"UseUtcTimestamp": true,
"TimestampFormat": "yyyy-MM-ddTHH:mm:ss.fffZ"
}
}
}

That applies in every environment, so Development has to switch back, with "FormatterName": "simple" in appsettings.Development.json. When I did that, the timestamp options carried over into the plain logs, and the timestamp stuck to the log level. Setting it in code, once, is simpler.

Keep Secrets Out​

JSON makes logs easier to search, including for whoever reads them later. So:

  • Never log a connection string. How to Log the Database Settings When an ASP.NET Core App Starts logs what you need without it.
  • Keep EnableSensitiveDataLogging off, so that EF Core logs @p0='?' and not the values.
  • Don't log people's names and emails. I searched a production run's log for a customer's email, and for each password: none were there.

Common Mistakes​

MistakeWhat happensDo this instead
Interpolating with $"…" in a log callNo fields, and an analyzer warningUse named placeholders
JSON in Development tooYour terminal's hard to readCheck the environment
Assuming Timestamp is thereIt isn't, without TimestampFormatSet it

Troubleshooting​

SymptomLikely causeFix
Plain text in productionThe environment is DevelopmentCheck it
' in messagesThe JSON writer escapes ' by defaultValid JSON: tools read it correctly
CA1848The analyzer wants LoggerMessageThe fix
2026-11-06T07:50:17.723Zinfo:A timestamp format with no space, in plain logsThe fix

Conclusion​

builder.Logging.AddJsonConsole outside Development gives log tools a field for each value, and leaves your own terminal readable.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.