Logging goes wrong in two directions at once. Too little, and an incident becomes guesswork. Too much, and the bill grows, the signal drowns, and - the part that gets least attention - personal data ends up in a system with far weaker access controls than the database it came from. Both failures are common in the same codebase.
The most consequential rule is about what must never be written. Passwords, even wrong ones, because users mistype their password into the username field. Full card numbers, CVVs, authentication tokens, session identifiers, API keys, one-time codes. Government identifiers such as Aadhaar or PAN. Full request bodies on authentication endpoints, which is how passwords usually arrive in logs - nobody logged the password, they logged the request that contained it.
Personal data more broadly needs a decision rather than a default. Under DPDP and GDPR alike, a log line containing an email address is personal data you are processing, in a system that is often retained longer, replicated more widely and access-controlled more loosely than your database. And when a user exercises their right to erasure, those logs are within scope. Log user identifiers rather than user details, and you keep the ability to trace a request without accumulating an obligation.
On what to log: every entry should be structured, and every entry should carry a request identifier propagated through every service the request touches. That single field is what makes logs searchable during an incident rather than a haystack. Log the boundaries - request received, external call made and its outcome, job started and finished - along with the identifiers needed to correlate them.
Use levels with actual meaning, because a codebase where everything is INFO has no levels at all. ERROR is something a human must act on. WARN is something recoverable that would matter if it became frequent. INFO is a business-significant event. DEBUG is for development and off in production. The test for ERROR is simple: if nobody would be paged or would investigate, it is not an error, and using it that way trains everyone to ignore the level that matters.
Finally, set retention deliberately, because logs are usually the largest and least examined data you retain. Thirty days of detailed logs covers nearly every investigation anyone actually performs; anything longer is normally kept because nobody chose a number. If you need long-term records for audit, keep aggregates or a specific audit log with its own controlled schema, rather than retaining everything indefinitely and calling it compliance.