When debugging a production issue at 2 AM, the difference between solving it in minutes versus hours often comes down to one thing: log quality. Yet many developers treat logging as an afterthought, sprinkling console.log or logger.info statements only when something breaks. This is a costly mistake.

Good application logs, especially debug logs that trace control flow, aren’t just helpful for troubleshooting. They’re a force multiplier for development efficiency. Let me show you why with a real example from a tax calculation system.

The Tale of Two Log Levels

Consider this scenario: You’re calculating profit and loss for stock trades with option assignments. Here’s the test case that captures the behavior:

Scenario: Simple roundtrip loss due to exchange rate
  Given Exchange rate on "10/01/24" is "3.0" USD to EUR
  And Exchange rate on "15/01/24" is "3.0" USD to EUR
  And Exchange rate on "20/01/24" is "2.0" USD to EUR
  And Exchange rate on "25/01/24" is "2.0" USD to EUR
  When Sell option "CLF 15/01/24 Put 20.00 @ 0.10" on "10/01/24"
  And Assignment "CLF 15/01/24 Put 20.00 @ 0.10"
  And Sell option "CLF 25/01/24 Call 20.00 @ 0.10" on "20/01/24"
  And Assignment "CLF 25/01/24 Call 20.00 @ 0.10"
  # buy for $2000 at rate 3.0 = 6000€, sell at rate 2.0 = 4000€ -> loss 2000€
  Then Profits for fiscal year 2024 should be options profits 50.0 losses 0.0 stocks -2000.0

A user reports their tax calculation shows -€2,000.00 profit, but they expected a different number. With only INFO-level logs, here’s what you see:

INFO c.e.t.portfolio.Portfolio      : Option STO position='option=CLF PUT 2024-01-15@USD 20, stoTx date=2024-01-10T11:00:00Z quantity=1'
INFO c.e.t.fiscalyear.FiscalYear    : 2024 Option STO: CLF premium €30.00
INFO c.e.t.portfolio.Portfolio      : Position assigned. position='option=CLF PUT 2024-01-15@USD 20, stoTx date=2024-01-10T11:00:00Z quantity=1'
INFO c.e.t.portfolio.Portfolio      : Stock BTO symbol='CLF' stcTx date 2024-01-15T11:00:00Z quantity 100 price USD -20
INFO c.e.t.portfolio.Portfolio      : Option STO position='option=CLF CALL 2024-01-25@USD 20, stoTx date=2024-01-20T11:00:00Z quantity=1'
INFO c.e.t.fiscalyear.FiscalYear    : 2024 Option STO: CLF premium €20.00
INFO c.e.t.portfolio.Portfolio      : Position assigned. position='option=CLF CALL 2024-01-25@USD 20, stoTx date=2024-01-20T11:00:00Z quantity=1'
INFO c.e.t.portfolio.Portfolio      : Stock STC symbol='CLF' stcTx date 2024-01-25T11:00:00Z quantity 100 price USD 20
INFO c.e.t.fiscalyear.FiscalYear    : 2024 Stock STC: randomDescription profitAndLoss='-€2,000.00', profitFromStocks='-€2,000.00'

You can see what happened: options were sold, positions assigned, stock bought and sold, final P&L calculated. But you’re left guessing at how it happened. Questions immediately arise:

  • What were the exact input values to each calculation step?
  • How did USD amounts convert to EUR? What exchange rates were used?
  • Did the position close completely or partially?
  • Was the profit calculation (sell price - buy price) × quantity? What about the option premiums?

You’d need to either reproduce the issue locally (if you can), add more logging and redeploy, or start inserting breakpoints and debugging. Time wasted: hours.

The Debug Log Advantage

Now enable DEBUG level and run the same scenario:

DEBUG c.e.t.portfolio.Portfolio     : onNewTransaction event='NewTransactionEvent(tx=OptionTrade(date=2024-01-10T11:00:00Z, symbol=CLF, expirationDate=2024-01-15, strikePrice=USD 20, callOrPut=PUT, quantity=1, averagePrice=USD 0.1, description=, action=SELL_TO_OPEN, ...))'
INFO c.e.t.portfolio.Portfolio      : Option STO position='option=CLF PUT 2024-01-15@USD 20, stoTx date=2024-01-10T11:00:00Z quantity=1'
DEBUG c.e.t.convert.CurrencyExchange : usdToEur value='USD 10', date='2024-01-10T11:00:00Z'
INFO c.e.t.convert.CurrencyExchange  : valueUsd='USD 10', date='2024-01-10', rate='3.0', valueEur='EUR 30'
INFO c.e.t.fiscalyear.FiscalYear    : 2024 Option STO: CLF premium €30.00
...
DEBUG c.e.t.portfolio.stock.StockPosition : sellToClose btoTx.quantity='100', sold='100', quantity='100', numSold='100'
DEBUG c.e.t.portfolio.stock.StockPosition : sellToClose quantityLeft='0'
DEBUG c.e.t.fiscalyear.FiscalYear   : netProfit quantity='100', buyPrice='-$20.00', sellPrice='$20.00'
DEBUG c.e.t.convert.CurrencyExchange : usdToEur value='USD -2000', date='2024-01-15T11:00:00Z'
INFO c.e.t.convert.CurrencyExchange  : valueUsd='USD -2000', date='2024-01-15', rate='3.0', valueEur='EUR -6000'
DEBUG c.e.t.convert.CurrencyExchange : usdToEur value='USD 2000', date='2024-01-25T11:00:00Z'
INFO c.e.t.convert.CurrencyExchange  : valueUsd='USD 2000', date='2024-01-25', rate='2.0', valueEur='EUR 4000'
DEBUG c.e.t.fiscalyear.FiscalYear   : 2024 profit -€2,000.00 100 CLF buy 2024-01-15 -€6,000.00 sell 2024-01-25 €4,000.00

Now you can see the complete control flow:

  1. Event triggers: Each transaction enters via onNewTransaction with full input data
  2. Currency conversions: USD values, dates, exchange rates, and EUR results at each step
  3. Position mechanics: Exactly how quantities were calculated (btoTx.quantity='100', sold='100')
  4. Calculation breakdown: The profit formula in action (buy 2024-01-15 -€6,000.00 sell 2024-01-25 €4,000.00)

Within minutes, you can trace the issue: exchange rates differed between buy (3.0) and sell (2.0) dates, causing a €2,000 loss despite the stock trade being net-zero in USD. Time wasted: minutes.

The ROI of Good Logging

This isn’t just about debugging faster. Well-designed debug logs deliver value across the entire development lifecycle:

1. Faster Onboarding

New team members can enable DEBUG logs and watch the system explain itself. Instead of reading stale documentation or bothering senior developers, they trace through actual execution flows to understand how components interact.

2. Better Code Reviews

When reviewing a complex PR, reviewers can ask: “Can I trace this logic through the logs?” If the answer is no, the logging needs improvement. This creates a culture where observability is part of the definition of done.

3. Production Diagnostics Without Deploy Cycles

When production issues arise, you can often solve them by adjusting log levels dynamically, no code changes or redeployments needed. This is only possible if you’ve already instrumented your debug logs properly.

4. Living Documentation

Debug logs showing control flow are self-documenting. They capture not just what your code does, but how it does it, with real data. This documentation never goes stale because it’s generated from actual execution.

Tests are another form of living documentation, and they pair beautifully with debug logs. Tests document expected behavior through examples, while debug logs document actual behavior through execution traces. When a test fails, the debug logs explain exactly why, showing you the precise execution path that led to the failure. Together, they create a complete picture: tests show what should happen, logs show what did happen.

5. Hypothesis Testing

When investigating unexpected behavior, debug logs let you quickly validate or reject hypotheses. “I think the currency conversion happens before fee calculation”—one glance at the logs confirms it.

6. Reduced Cognitive Load

With comprehensive debug logs, you don’t need to fumble with debuggers, step through code, dig into variable watches, inspect nested data structures, or hunt for the one relevant field buried five levels deep. The information you need is already formatted and waiting in the logs. This dramatically reduces the mental overhead of debugging—you can stay focused on solving the problem rather than wrestling with tools to understand the problem.

Best Practices for Debug Logging

Based on the example above, here are principles that make debug logs truly valuable:

Log entry and exit points: Capture when control enters/exits significant methods (onNewTransaction event=...)

Include input parameters: Show the complete input state, not just IDs ( value='USD 10', date='2024-01-10T11:00:00Z')

Log intermediate calculations: Make the math visible ( netProfit quantity='100', buyPrice='-$20.00', sellPrice='$20.00')

Show decision branches: When code takes different paths, log which path and why

Use structured data: Key-value pairs are easier to parse than prose (rate='3.0' beats "using rate 3.0")

Keep INFO focused: Reserve INFO for significant business events that users care about; use DEBUG for developer-facing control flow

Log idempotently: Debug logs should be safe to enable in production without performance degradation (within reason)

The Bottom Line

Think of debug logs as an investment. Yes, they require time upfront to design and implement thoughtfully. But they pay dividends every time you:

  • Debug an issue in minutes instead of hours
  • Onboard a developer in days instead of weeks
  • Diagnose a production problem without a redeploy
  • Review code with confidence about its runtime behavior

In our tax calculation example, the difference between INFO and DEBUG logging literally meant the difference between hunting through source code for hours versus understanding the issue in a quick scan of the logs.

Good logs don’t just record what happened—they tell you the story of how it happened. And in software development, understanding the “how” is what transforms guesswork into certainty.


I learned the value of comprehensive logging for production the hard way, after too many frustrating experiences being unable to diagnose problems in production environments. But it’s only recently that I discovered logging’s value extends far beyond production diagnostics into everyday development work. The moment I started relying on debug logs during local development, my productivity jumped noticeably.

It’s a win-win-win situation: you need the logs for production anyway, so by using them actively during development, you can verify they actually work before deployment. You’ll catch issues early—like objects without proper toString() implementations that render as useless [Object object] in logs, or missing context that seemed obvious while writing code but becomes cryptic when reading logs later. The logs become battle-tested long before they reach production.

For more insights on effective logging practices, I highly recommend this classic article: 10 Tips for Proper Application Logging. Despite being from 2011, its principles remain remarkably relevant today.