Logduo
Easy logging and output management for Python scripts and interactive sessions.
Simple by default, configurable for advanced logging workflows.
Platforms: Tested through GitHub Actions on macOS, Windows, and Ubuntu.
Features
- No setup required — safe defaults applied
- Manages output directories and log files automatically
- Provides
help()documentation and actionable error messages - Validates arguments to all Logduo methods and functions
- Safely prunes old run directories containing a Logduo marker file
- Generates optional session artifacts:
config_table.txt,config.json - Emits ANSI-styled and Rich output to console while preserving plain-text logs
- Creates dedicated log files via
new_logger() - Creates advanced pass-through Loguru sinks via
new_loguru_sink() - Creates custom logging levels via
new_level() - Supports nested scripts via
run()andlog.join() - Captures JSONL event streams
- Reports log-generated files in console and log footers
Quick interactive session
>>> from logduo import log, run
>>> log("hello world")
Logging started: 2026-07-31 17:28:27
| INFO | hello world
>>> log.info("hello world again; INFO is the default logging level.")
| INFO | hello world again; INFO is the default logging level.
>>> log.warning("The logging level 'WARNING' is displayed in orange on the console.")
| WARNING | The logging level 'WARNING' is displayed in orange on the console.
>>> help(log.configure)
>>> help(run)
>>> log.close()
───────────────────────────────────────────────────────
Logging ended: 2026-07-31 17:29:12 (duration 45 sec)
Output directory:
/Users/my_name/my_project/logs/session/run_2026_07_31__17_28_27
Log-generated files in output directory:
config_table.txt
session.log
- If a log statement, such as
log("hello world"), is called beforelog.configure(), Logduo applies configuration settings from[tool.logduo]inpyproject.toml. - If
[tool.logduo]settings are not provided, Logduo applies its own configuration defaults. help()output appears on the console only and is not written to log files.- Use focused calls such as
help(log.configure)orhelp(log.new_logger). help(log)displays the complete logger API and is therefore lengthy.
- Use focused calls such as
log.close()is required to end logging in interactive sessions
Key Logduo default configuration settings
console_theme = "dark"console_wrap_width = 120andlog_wrap_width = "off"console_verbosity = 3andlog_verbosity = 3- verbosity = 3, all logging levels emitted (including DEBUG and TRACE)
- verbosity = 2, only CRITICAL, ERROR, WARNING, INFO, SUCCESS are emitted
- verbosity = 1, only CRITICAL, ERROR, WARNING are emitted
- verbosity = 0, output is suppressed
console_prefix = "level"andlog_prefix = "timestamp"- Prefix options layer cumulatively:
"off","level","timestamp","source" - Example prefix =
"source": 16:30:40.371 | WARNING | example_2.py:382
- Prefix options layer cumulatively:
log_dir_path = "auto""auto"→- If pyproject.toml is not detected:
log_dir_path= current working directory / "logs" - If pyproject.toml is detected:
log_dir_path= parent directory of pyproject.toml / "logs"
- If pyproject.toml is not detected:
- Other option: provide an explicit absolute log-directory path.
log_file_mode = "write""write"→ Existing log files are overwritten.- Other options:
"append","timestamped"(adds timestamp to log file name before extension).
log_file_name = "auto""auto"→- If Logduo is initialized inside a script,
log_file_name= <script_stem>.log - If Logduo is initialized in an interactive session,
log_file_name= session.log
- If Logduo is initialized inside a script,
- Other option: provide an explicit file name (".log" will be appended if no extension is given)
log_file_layout = "run""run":log_dir_path/script_stem/run_yyyy_mm_dd__hh_mm_ss/log_file_name"script":log_dir_path/script_stem/log_file_name"flat":log_dir_path/log_file_name- If Logduo is initialized in an interactive session, script_stem = "session"
log_file_path = "auto""auto"→ the log file name and location are determined bylog_dir_path,log_file_name, andlog_file_layout.- Other option: provide an explicit log file path (overrides
log_dir_path,log_file_name, andlog_file_layout)
keep = "off"- Previous run directories are not pruned automatically.
- If
keepis set to a positive integern, the newestnLogduo-marked run directories are kept and older run directories are pruned.
write_config_table = Trueconfig_table.txtwritten to output directory (useful reference for all configuration settings).
Quick script with log.configure()
from pathlib import Path
from logduo import log, run
my_log_dir = Path.cwd() / "logs"
log.configure(log_dir_path=my_log_dir, keep=3, console_theme="light")
log("hello world")
log(f"output directory path = {log.output_dir_path}")
log(f"main log file path = {log.main_log_file_path}")
log.export_logduo_docs()
var = 3 * 3
log.debug(f"made it here: var = {var}")
output_dir_path = log.output_dir_path
assert isinstance(output_dir_path, Path) # Satisfy static type checkers.
myplot_output_path = output_dir_path / "myplot.png"
log.close()
-
If
log.configure()is called after logging has started or after a previouslog.configure()call, a warning is issued and the new configuration is ignored. -
If Logduo is initialized inside a script,
log.debug()includes the source (calling file name and line number) at the start of each debug message unless disabled with:`log.configure(show_debug_source=False)`. -
A logging session must be closed, and a new session started, to change Logduo settings.
-
Logging sessions in scripts close automatically during normal interpreter shutdown using best-effort cleanup.
-
While not required, explicit
log.close()is supported in scripts and is useful when subsequent code needs the completed log files immediately.
Export Logduo docs: log.export_logduo_docs()
Exports bundled documentation and example scripts to a local logduo_docs/ directory.
Exported files include examples of advanced workflows:
README.txtexamples/first_script.pyexamples/console_rendering.pyexamples/data_analysis.pyexamples/math_report_notation.pyexamples/script_parent.pyexamples/script_child.py
Logduo Methods, Functions, and Properties
-
Manage session:
log.configure()log.close()
-
Logging levels:
log()orlog.info()log.trace()log.debug()log.success()log.warning()log.error()log.critical()log.exception()# Error message and traceback
-
Create custom logging level:
log.new_level()# Maps a custom display label to an existing severity level; default = "INFO"
-
Create additional output:
log.new_logger()# Logduo-managed extra log filelog.new_loguru_sink()# Advanced Loguru sinklog.export_logduo_docs()
-
Execute a nested script or importable module:
- Inside script_parent.py or interactive session:
run(<path to script_child.py>) - Inside script_child.py:
log = log.join()
- Inside script_parent.py or interactive session:
-
Access paths.
-
log.output_dir_path
-
log.main_log_file_path
These properties are
Noneuntil Logduo is initialized bylog.configure()or a logging call such aslog("message").
-
Message Rendering
- Strings without
\n: Displayed inline with the prefix.- Console: Wrapped to
console_wrap_width). - Log files: Wrapped only if
log_wrap_widthis set to a positive integer. Default is"off".
- Console: Wrapped to
- Strings containing
\n: Displayed as block flush left below prefix. Line breaks are honored.-
This preserves the full available line width for tables, panels, JSON, tracebacks, and other structured content.
-
Use manual indenting or Rich
Paddingif indent behavior is desired:indent = " " log( f"{indent}Step 1: Load data\n" f"{indent}Step 2: Clean data\n" )
-
- ANSI-styled strings and Rich
Textobjects are rendered on the console and written as plain text in log files. - Other Rich objects, such as
Panel, are rendered on the console but displayed as placeholders in log files.- For more examples, use
log.export_logduo_docs()and seeconsole_rendering.py.
- For more examples, use
Loguru Integration
- Logduo uses Loguru as its underlying file-sink engine.
- Logduo performs message formatting, wrapping, routing, session management, and Rich integration before messages reach Loguru.
- The following Loguru sink options can be passed through
log.configure():rotation: start a new log file when a size/time rule is met. Example:rotation="10 MB"orrotation="1 week". Userotation="off"for no rotation (default ="off").retention: remove older rotated log files when a retention rule is met. Example:retention="14 days"orretention=5. This applies to rotated files, not to Logduo run-directory pruning (default ="off").compression: compress rotated log files. Example:compression="zip". This applies to rotated files, not the active log file (default ="off").enqueue: write logs through a background queue. Useful for thread/process safety (default =True).catch: catch logging errors instead of letting them crash the program (default =True).backtrace: show extended traceback context for exceptions (default =False).diagnose: include extra variable/context information in exception tracebacks (default =False).
- Use
log.new_logger()when you want a normal Logduo-managed extra log file. - Use
log.new_loguru_sink()when you want direct Loguru control, such as:- using custom Loguru filters
- adding extra Loguru sinks
- sending selected events to separate destinations
- passing options directly to
logger.add()
- Sinks added with
log.new_loguru_sink()are advanced pass-through sinks. Logduo manages their creation and session lifecycle, but messages sent directly through Loguru bypass normal Logduo formatting, wrapping, routing, headers, and footers.
Console compatibility
- Logduo supports modern Unicode-capable terminals on Windows, macOS, and Linux.
- Some older or restricted terminals may not display every Rich character correctly.
- Log files are always written as UTF-8.
Release files for logduo 0.1.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| logduo-0.1.5.tar.gz | 115.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| logduo-0.1.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 257.3 kB
Release files / logduo-0.1.5.tar.gz
| Download URL | logduo-0.1.5.tar.gz |
|---|---|
| Size | 115.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
66069c67c785e576e4ffaf2cf89c77c43f549f273d3fbc73fa72905afb151cd7
|
|
BLAKE2b-256 checksum How to use checksums |
0dc2e52a1302ea1dd6a50739f245dd4a8e1916308f18da3087dd112d3fc6d7bf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / logduo-0.1.5-py3-none-any.whl
| Download URL | logduo-0.1.5-py3-none-any.whl |
|---|---|
| Size | 142.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d9c96413c574551d3777b72ec74dc6e20e236ce324626234c25486c5dbc83454
|
|
BLAKE2b-256 checksum How to use checksums |
daf3d731b9d8ff9305d6e5d4bce13930cc437fde6943210ebec628b3d50ea7e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|