Effective Python Debugging Techniques: Print, Logging, IDE Debuggers, GDB, Perf, Strace, and Inject Debugger
This article explores practical Python debugging methods—from simple print statements and structured logging to powerful IDE debuggers, GDB, perf, strace, and the inject debugger pylane—providing code examples, best‑practice guidelines, and tips for improving observability and performance in production systems.
Python has become a top language for web, operations, scientific computing, and game development, but as codebases grow, debugging consumes a large portion of development time, making efficient debugging a critical topic.
Print & Logging – Using print is quick for scripts but lacks structure; logging offers configurable formats, levels, and handlers. Example of a formatted logger:
LOG_FORMAT = '%(asctime)s %(levelname)8s: [%(filename)s:%(lineno)d] [%(processName)s:%(process)d %(threadName)s] - %(message)s'
DATE_FORMAT = '[%Y-%m-%d %H:%M:%S]'
def get_logger(name):
logger = logging.getLogger(name)
formatter = logging.Formatter(LOG_FORMAT, DATE_FORMAT)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
logger = get_logger(__name__)
logger.warning("test msg")Structured logging can be achieved with a custom logging.Filter that serialises dictionaries to JSON, enabling richer log analysis.
class JsonLogFilter(logging.Filter):
def filter(self, log):
if isinstance(log.msg, str):
return True
if isinstance(log.msg, (dict, list, tuple)):
log.msg = json.dumps(log.msg)
return True
return False
logger.addFilter(JsonLogFilter())
logger.warning({"action": "server_start", "port": 8080, "engine": {"name": "tornado", "version": "0.1.1"}})Logging levels (DEBUG, INFO, WARNING, ERROR, CRITICAL) should be used judiciously; for production, keep logs concise and enable detailed logs only when needed.
IDE Debugger – Modern IDEs like PyCharm provide breakpoints, step controls, variable inspection, and thread navigation, offering a more powerful debugging experience than raw prints.
GDB Series Debuggers – For low‑level issues such as core dumps, GDB combined with libpython or python-dbg can inspect both C and Python stacks, view variables, and trace execution paths.
# Example GDB session
(gdb) bt
#0 symtable_visit_expr ...
(gdb) py-bt
#0 Frame ... in raise_seg_fault()
(gdb) py-locals
self =Tracers – Linux tools like perf and strace help profile CPU usage and system calls. Example perf command to record a call graph:
perf record -g -p 6252 sleep 10
perf report --stdioExample strace to watch a blocked process:
# strace -p 1227
select(0, NULL, NULL, NULL, {0, 524306}) = 0 (Timeout)Inject Debugger (pylane) – By attaching to a running Python process, pylane can inject code, open an interactive IPython console, inspect objects, view source, call functions, print thread stacks, and even perform on‑the‑fly monkey‑patching. Sample usage:
pylane shell
# view variable
p = tools.get_insts("Processor")[0]
print(p._counter)
# view source
tools.print_source_code(tools.get_classes("Processor")[0])
# monkey‑patch a method
origin_func, p.handle = p.handle, tools.log_it(p.handle)Additional use cases include memory‑leak detection with objgraph , printing all thread stacks, and safely modifying runtime configuration.
Conclusion – Debugging in Python ranges from simple prints to sophisticated tracing and injection tools. Choosing the right technique, balancing log verbosity, and leveraging powerful debuggers can dramatically reduce time spent on bug fixing while keeping production systems stable.
NetEase Game Operations Platform
The NetEase Game Automated Operations Platform delivers stable services for thousands of NetEase titles, focusing on efficient ops workflows, intelligent monitoring, and virtualization.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.