We all have a situation in our development where the time is ticking, and eyes are burning. By looking at constant error messages. And you don't have any clue what's going wrong, even after ur debugging. And the same thing happened to me.
The glow of the terminal was the only light in the room. I had been staring at the same function for four hours. The screen was scrolling through a wall of text that looked like absolute chaos.
checking variable x... 10inside loop...variable x is now... 12HERE!!!WHY IS THIS NULL???

I was debugging a race condition in a Python script… and my primary tool was the print() function. I had littered my beautiful code with dozens of print statements. It created a chaotic, noisy mess that was actually making it harder to find the problem.
That night, after finally fixing the bug (it was a typo, naturally), I made a vow. I was done with print(). I was going to learn how to do this properly. I was going to learn to log.
If you are still debugging by printing variables to the console, I am not here to judge you. I did it for years. It feels easy. It feels immediate. But it is also holding you back.
Moving from print() To have a proper logging framework is one of those quiet level-up moments in a developer's career. It marks the transition from writing scripts that work to building systems that survive.
The Illusion of Simplicity
The problem with it print() is that it is a blunt instrument. It screams everything at the same volume.
When you use print() An error message looks the same as a trivial status update. You have no way to filter the noise. You have no way to turn it off without deleting the code. And worst of all, when your code goes into production, those print statements disappear into the void of stdout. often lost forever.
Imagine a cockpit where the Engine Fire alarm sounded the same as the Seatbelt Sign chime. That is what debugging with print() is like. You miss the critical failures because you are drowning in trivial data.
Enter the Logger
The Python logging library (and its equivalents in JavaScript... Java... or Go) gives you something print() can never. It gives you Context and Control.
Context means knowing when something happened, where it happened, and how bad it is. Control means being able to decide what you want to see right now.
Let us look at a simple example. In my print statement days… I would write something like this.
print(f"User {user_id} attempted login")
if failed:
print("Login failed!")This gives me text. Just text. But with logging… we gain dimensions.
import logging
logging.info(f"User {user_id} attempted login")
if failed:
logging.warning("Login failed: Invalid password")At first glance, they look similar. But the magic happens when you run the app.
The Hierarchy of Importance
Logging introduces the concept of levels. This was the game-changer for me. Not every piece of information is equal, and logging allows you to categorise them.
DEBUG is for detailed information, typically of interest only when diagnosing problems. This is where you put your variable values and loop counters.
INFO is confirmation that things are working as expected. This is for tracking milestones like “Server started” or “Job completed”.
WARNING is an indication that something unexpected happened, but the software is still working. Maybe disk space is low… or an API call took longer than expected.
ERROR is for more serious problems. The software has not been able to perform a function.
CRITICAL is a serious error, indicating that the program itself may be unable to continue running.
Why does this matter?
Because on a Tuesday morning, when you are coding features, you can set your level to DEBUG and see everything. But on a Friday night when the app is running in production, you can set the level to ERROR.
Suddenly, your logs are clean. The noise is gone. You only see what is burning. You can sleep better knowing that if your phone buzzes, it is actually important.
Time Travel for Developers
The other superpower of logging is persistence.
When I used print()My debugging history vanished the moment I closed the terminal. If a user reported a bug from yesterday, I was blind. I had to try to recreate it.
With a logger, you can save that history to a file.
logging.basicConfig(filename='app.log', level=logging.INFO)Now, you have a time machine. You can go back and see exactly what the system was doing at 10:42 AM when the server crashed. You can see the sequence of events leading up to the disaster.
It transforms debugging from a guessing game into a forensic investigation. You are no longer asking “what happened?” You are looking at the evidence of what actually happened.
Performance Matters
There is another hidden cost print() that many new developers miss. It is slow.
Writing text to the standard output It stdout is a blocking input/output operation. If you have a loop running a million times, and you print inside that loop, you are forcing your fast CPU to wait for the slow terminal to display text. You are intentionally slowing down your application.
Proper logging frameworks are optimised. They can be configured to write to files asynchronously. They can be configured to rotate files so they do not eat up your entire hard drive. They handle the heavy lifting so your code can run fast.
A Touch of Humanity
There is a surprising emotional benefit to clean logs, too.
When you look at a log file that is structured, timestamped, and categorised, you feel a sense of order. You feel like a professional pilot scanning instruments, not a frantic mechanic banging on the engine with a wrench.
It respects your future self.
The “You” of three months from now, who has completely forgotten how this authentication module works, will be incredibly validated to open the logs and see this.
2025-12-06 10:00:01 [INFO] AuthModule: Validating token...2025-12-06 10:00:02 [ERROR] AuthModule: Token expired
It tells a story. It explains what happened without you having to read a single line of code.
How to Start Without Overthinking
You do not need to memorise the entire documentation today. Start small.
First, import the libraryimport logging.
Second, configure the basicslogging.basicConfig(level=logging.INFO).
Third, replace one print statement. Just one.
Next time you are about to type print("Connecting..."), stop. Type logging.info("Connecting...") instead.
It will feel strange at first. It might feel like overkill for a small script. But habits are built in the small moments.
Eventually, you will encounter a bug that you cannot reproduce locally. You will ask the server admin for the logs. You will open the file, and there, waiting for you, will be the exact error message you needed, timestamped and categorised.
And in that moment, you will realise you can never go back to print().
We build complex, beautiful, fragile things. The least we can do is leave ourselves a map to find our way out when they break.