Testing, debugging, and concurrency · Lesson 2 of 3
Debugging, logging, and application configuration
Diagnose failures with tracebacks and structured logs without leaking secrets.
Lesson
1. Concept and mental model
When an exception occurs, read its type and message, then inspect the traceback from the failing line toward the caller. Reproduce the problem with the smallest input that still fails. A debugger can inspect values and step through code, but temporary print statements should not become a substitute for understanding control flow. Remove sensitive values from debugging output before sharing logs.
2. How it works
Use the logging module for operational diagnostics. Log a useful event, severity, and context such as a job identifier; avoid logging passwords, API keys, authorization headers, or unnecessary personal data. Choose levels consistently: debug for details, info for normal milestones, warning for recoverable unexpected conditions, and error for failed operations. Do not catch an exception and log it as success.
3. Use it safely and effectively
Configuration belongs outside source code. Read environment variables or a settings file at the application boundary, validate required values at startup, and provide safe defaults only when they have well-understood behavior. Separate development and production configuration, and never place secrets in a version-controlled example. For difficult incidents, preserve the exact exception chain and safe request identifiers to support diagnosis without exposing confidential data.
4. Syntax reference
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info('Import completed', extra={'batch_id': 'batch-42'})5. Guided walkthrough
Reproduce the problem with a small input, inspect the complete traceback, and add a breakpoint or targeted log at the point where the value changes unexpectedly. Keep configuration outside the code and validate required settings when the application starts.
6. Common mistakes to avoid
- Logging passwords, API tokens, personal records, or complete request bodies.
- Replacing useful exception context with a generic message that makes diagnosis harder.
Visual guide
Investigate a failure
-
Step 01
Observe
Capture a safe exception and request context.
-
Step 02
Reproduce
Reduce the issue to a minimal case.
-
Step 03
Correct
Fix the cause and add a regression test.
Code examples
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info('Import completed', extra={'batch_id': 'batch-42'})
An informational log event with a safe batch identifier.
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info('Import job started')
INFO:__main__:Import job started
import os
api_url = os.environ.get('API_URL')
if not api_url:
raise RuntimeError('API_URL is required')
print(api_url)
Prints the configured URL or raises a clear startup error.
Key points
- Diagnose failures with tracebacks and structured logs without leaking secrets.
- Prefer explicit, testable behavior over hidden side effects.
- Validate assumptions at system boundaries and handle failure deliberately.
Check your understanding
Practice
Your learning progress