Your logs are garbage: why you need structured, machine-readable logging
By Seaworthy · September 26, 2026
Photo by Scott Rodgerson on Unsplash
Your logs are garbage: why you need structured, machine-readable logging
I've seen it too many times. A developer spins up a new service, adds a few console.log statements, and calls it a day. The logs look fine in the terminal. Then something breaks in production, and they're grepping through thousands of lines of semi-random text, trying to piece together what happened. That's when the pain starts.
Here's the thing: if your logs aren't structured, they're not really logs. They're just noise. And in 2025, with AI coding assistants writing half your code, you can't afford noise. You need data.
The problem with unstructured logs
Unstructured logs are lines of text. Something like:
2025-03-22 14:35:12 INFO User 12345 logged in from 192.168.1.1
2025-03-22 14:35:13 ERROR Failed to process order 67890 for user 12345
That looks readable, right? But try to answer a simple question: How many failed orders did user 12345 have in the last hour? You can't just query that. You have to write a regex, parse timestamps, extract user IDs, and hope the format never changes. And it will change, because someone will add a new field or change the wording. Then your regex breaks, and you're back to square one.
Worse, from a security perspective, unstructured logs are a gift to attackers. They can inject newlines, fake log entries, or hide their tracks in the noise. If you can't reliably parse your logs, you can't reliably detect intrusions. You can't build alerts. You can't do forensics. You're flying blind.
I get it. Structured logging sounds like extra work. But it's not. It's actually less work once you set it up. And with AI assistants generating code, you have no excuse. They can write the logging boilerplate for you. But you have to ask for it. If you just say "add logging," you'll get console.log. You need to be specific.
What structured logging looks like
Structured logging means each log entry is a machine-readable object, usually JSON. Instead of a string, you emit something like:
{
"timestamp": "2025-03-22T14:35:12Z",
"level": "INFO",
"event": "user_login",
"user_id": 12345,
"ip": "192.168.1.1",
"service": "auth"
}
Now you can query this. You can say: show me all user_login events for user 12345, or count ERROR events per service. Your log management system (ELK, Loki, Datadog, whatever) can index these fields automatically. No regex. No parsing. Just data.
And here's the kicker: it's not just for ops people. As a developer, you benefit. When you're debugging, you can filter by request_id and see the full story of a single request across services. That's impossible with plain text unless you've carefully formatted every line. Even then, it's fragile.
The fix: a simple pattern
You don't need a fancy library. You can do this with any language. Here's a tiny example in Python using the standard logging module with a JSON formatter:
import logging
import json
import sys
class JsonFormatter(logging.Formatter):
def format(self, record):
log_entry = {
"timestamp": self.formatTime(record),
"level": record.levelname,
"message": record.getMessage(),
"logger": record.name,
}
# include any extra fields passed via 'extra'
if hasattr(record, "extra_fields"):
log_entry.update(record.extra_fields)
return json.dumps(log_entry)
logger = logging.getLogger("myapp")
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)
logger.setLevel(logging.INFO)
# log with extra fields
logger.info("User logged in", extra={"extra_fields": {"user_id": 12345, "ip": "192.168.1.1", "event": "user_login"}})
That's it. Now every log line is JSON. Your log shipper can parse it. Your alerts can trigger on user_id or event. You can even use jq on the command line to filter logs in real time.
But wait, what about readability?
Yes, JSON logs are harder to read in a terminal. But you shouldn't be reading raw logs in a terminal anyway. Use a tool that pretty-prints JSON, or pipe through jq. For local development, you can use a human-readable formatter and switch to JSON in production. Most logging libraries support that with a single environment variable.
The trade-off is worth it. You lose a little terminal readability and gain the ability to actually understand what your system is doing. That's a win for security and ops.
Don't let AI assistants skip this
If you're using an AI coding assistant, it will happily generate print statements all day. You need to explicitly ask for structured logging. Tell it: "Use JSON logging with fields for timestamp, level, message, and context. Include request IDs and user IDs where relevant." The assistant will comply. It's not magic, but it's close.
And please, don't log sensitive data. Structured logging makes it easy to accidentally include passwords or tokens in a field. Review what you log. Redact as needed.
The bottom line
Unstructured logs are a liability. They slow down debugging, break security monitoring, and make you look like an amateur. Structured logs are the baseline for any serious service. They're not hard to implement. They're not expensive. They just require a decision.
Make that decision today. Your future self (and your on-call rotation) will thank you.
If you want to go further, tools like Seaworthy can help you audit your logging practices and catch gaps you might have missed. It's a static analysis tool that checks for structured logging patterns, among other things. Worth a look if you care about doing this right.
This article was generated by AI and summarises publicly available sources.
Seaworthy scans your repo for the issues covered in articles like this one.
Security gaps, exposed secrets, and misconfigurations — caught before you deploy. Free to run, no account needed.