Python
logging module
file writing
programming tutorial
error handling

How to write to a file, using the logging Python module?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

The Python logging module can write directly to a file through a FileHandler or through basicConfig with a filename. The key difference from plain file writing is that logging adds levels, formatting, timestamps, and handler management automatically.

If all you want is "append text to a file," open(..., "a") works. If you want structured application logs, logging is the right tool.

The Simplest File Logging Setup

python
1import logging
2
3logging.basicConfig(
4    filename="app.log",
5    level=logging.INFO,
6    format="%(asctime)s %(levelname)s %(message)s",
7)
8
9logging.info("Application started")
10logging.warning("Disk usage is high")

This creates or appends to app.log and writes formatted log lines there.

The important options are:

  • 'filename for the output file'
  • 'level for the minimum severity'
  • 'format for the log line structure'

A More Explicit FileHandler Setup

For anything beyond the simplest script, building the logger explicitly is clearer:

python
1import logging
2
3logger = logging.getLogger("demo")
4logger.setLevel(logging.INFO)
5
6handler = logging.FileHandler("app.log", encoding="utf-8")
7formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s")
8handler.setFormatter(formatter)
9
10logger.addHandler(handler)
11
12logger.info("File logging is configured")

This gives you direct control over handlers and makes it easier to combine file logging with console logging later.

It also scales better when the application grows and different modules need different logger names or handler combinations.

That explicit setup is also easier to test, because you can inspect which handlers and formatters were attached instead of relying on hidden global configuration.

It also makes later refactoring easier when the project grows from one script into a larger application with several logging destinations.

Log to File and Console at the Same Time

python
1import logging
2
3logger = logging.getLogger("demo")
4logger.setLevel(logging.INFO)
5
6file_handler = logging.FileHandler("app.log", encoding="utf-8")
7console_handler = logging.StreamHandler()
8
9formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
10file_handler.setFormatter(formatter)
11console_handler.setFormatter(formatter)
12
13logger.addHandler(file_handler)
14logger.addHandler(console_handler)
15
16logger.info("This message goes to both destinations")

This is a common production pattern because developers want logs on screen during local runs and persisted to disk in longer-running environments.

It also helps during debugging because the console gives immediate feedback while the file provides a historical record you can inspect later.

Avoid Duplicate Log Lines

One frequent problem is accidentally adding handlers multiple times, especially in notebooks, REPL sessions, or code that reinitializes logging.

Before attaching handlers, check whether they already exist:

python
if not logger.handlers:
    logger.addHandler(file_handler)

Otherwise each log message may appear two or three times in the file.

Rotating Log Files

If the file can grow indefinitely, use a rotating handler:

python
1import logging
2from logging.handlers import RotatingFileHandler
3
4logger = logging.getLogger("rotating-demo")
5logger.setLevel(logging.INFO)
6
7handler = RotatingFileHandler("app.log", maxBytes=1024 * 1024, backupCount=3)
8handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
9logger.addHandler(handler)
10
11logger.info("Rotation is enabled")

This prevents one log file from growing forever.

For long-running services, that matters a lot. Logging to a file is useful only if the file remains manageable and does not quietly consume all available disk space.

Common Pitfalls

  • Using print() for application logging when you actually need timestamps and levels.
  • Calling basicConfig after logging has already been configured elsewhere and expecting it to reconfigure everything.
  • Adding the same handler multiple times and getting duplicate log lines.
  • Forgetting encoding when the log file may contain non-ASCII text.
  • Letting one log file grow forever when rotation would be safer.

Summary

  • Use logging.basicConfig(filename=...) for the quickest file logging setup.
  • Use FileHandler when you want explicit control over handlers and formatters.
  • Combine file and console handlers when you need both persistent and visible logs.
  • Guard against duplicate handlers in reusable code.
  • Use rotating handlers when log growth needs to be controlled.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.