
Logging is one of those topics where the advice you find online is written for companies with a dedicated observability team and a budget to match. For an indie developer, the calculus is different. You want enough visibility to debug problems quickly, without paying to store gigabytes of noise you will never read.
The good news is that the principles of useful logging are the same at any scale. You just apply them with a smaller budget and a stronger bias toward simplicity. The goal is not to log everything. It is to log the things that will actually help you when something goes wrong at an inconvenient hour.
The most common mistake is logging too much at the wrong level. If every request writes ten lines, your logs become a haystack where the needle is impossible to find. A better approach is to log at boundaries: when a request comes in, when it goes out, and when something notable happens in between. Errors, warnings, and important state changes deserve a line. Routine internal steps usually do not.
Structure matters as much as volume. A log line that reads Error processing order is nearly useless. A log line that includes the order ID, the user ID, the error type, and a timestamp is something you can actually search. If you use structured logging, which most languages support through a library, you get key-value pairs that are easy to filter and aggregate. Even if you never set up a fancy log viewer, structured logs are far easier to grep than freeform text.
Include a request ID or correlation ID on every log line tied to a single request. When a user reports a problem, you can search for that ID and see the entire story of what happened, across services if needed. This one habit turns debugging from archaeology into a simple lookup. It costs almost nothing to implement and saves hours over the life of a project.
Be careful with personally identifiable information. Logs are often stored in less protected places than your database, and they have a way of being copied around. Avoid logging full email addresses, tokens, passwords, or anything else you would not want to see in a support ticket. If you need to correlate a user, log an internal ID instead.
Every log line you keep has a cost, whether it is storage, ingestion, or the mental overhead of searching through it. Decide deliberately how long you keep different kinds of logs. Error logs are worth keeping for weeks or months, because they are the ones you will search when a user reports a problem. Debug logs are worth keeping for days at most, and often only when you turn them on temporarily to investigate something specific.
Most logging services let you set retention per stream or per index. Use that. Send application errors to a long-retention stream and verbose debug output to a short one. If you are self-hosting, a simple rotation policy that deletes files after a set number of days does the same job. The important thing is that the policy is explicit, not accidental.
Finally, do not forget that logs are only half the picture. For anything you care about over time, metrics are usually a better fit. A metric like requests_per_minute or error_rate is compact, cheap to store, and easy to graph. Logs are for the details of a specific event. Metrics are for the shape of your system over time. Using each for its strength keeps both your bill and your debugging time under control.