BOARD: THATSJET.COM
DOC: BLOG

Every App Invents Its Own Security Log Format. Stop That.

Picture the moment you actually need your logs. Someone’s poking at your login endpoint at three in the morning, and you’re trying to answer a simple question: is this a real attack, and where else is it happening? So you go to the logs.

And every service tells the story in a different dialect.

The Python service calls it login_failed. The Node service calls it auth failure. The Go service logs event=401 and calls it a day. Same event, three vocabularies, zero ability to write one alert that catches all three. You end up building a little translation layer in your head, at three in the morning, which is exactly when your head is worst at it.

Here’s the frustrating part: OWASP already solved this.

The vocabulary almost nobody implements

There’s an OWASP Logging Vocabulary Cheat Sheet — a standardized set of names for security events so that authn_login_success, authn_login_fail, and their cousins mean the same thing in every app you own. It’s good. It’s thorough. And in the wild, almost nobody implements it, because doing it consistently across a polyglot stack is a chore, and chores lose to feature work every time.

So I built security_event_logger to make the chore disappear.

One vocabulary, four languages, one source of truth

The core idea is a single security_events.yaml — language-agnostic event definitions that serve as the one source of truth. From there, libraries in Python, Node.js, Go, and Java all emit events using the same names and the same shape. Write the definition once; every stack speaks it.

The event categories cover the ground you actually care about:

  • Authentication and Authorization — login success and failure, password changes, token management, access-control violations, permission changes.
  • Sessions and Users — session creation, renewal, and expiration; user creation, updates, and deletion.
  • The interesting stuff — input validation failures, rate-limiting and resource exhaustion, and outright malicious behavior like CSRF and CORS violations.

Every implementation emits the same OWASP standard JSON: a datetime, an appid, an event field like authn_login_success:joebob1, plus the source IP, the request URI, the method, the region. Structured, greppable, and — this is the whole point — identical whether it came out of the Go service or the Java one.

Using it is boring, which is the highest compliment I can pay a logging library:

logger = SecurityEventLogger(appid="myapp.auth")
logger.log_event("authn_login_success", ["joebob1"], source_ip="192.168.1.100")

Why bother

Consistent event names are institutional memory for your security events. When every app agrees on what a failed login is called, you can write one detection rule instead of four, onboard a new service without teaching your SIEM a new dialect, and answer that three-in-the-morning question in one query instead of a scavenger hunt.

You don’t get that from good intentions. You get it from everybody speaking the same language. This is the library that makes them.