logger.debug("Hello, world! {}", 'aaaa')logger_loguru
Provides pre-configured logging setup using Loguru with notebook-friendly defaults and hierarchical indentation support for tracking nested operations.
Key features:
config_logger() — One-line setup returning configured logger + tree logger
tree_logger — Hierarchical logging with automatic indentation (llogger.push, llogger.pop)
LogFormatter — Customizable format with indentation support
LogLevelFilter — Dynamic level filtering with temporary override
When to use: Need readable, structured logging in notebooks or scripts with hierarchical context tracking (e.g., nested async operations, recursive algorithms).
Typical usage:
from loguru import logger
from pote.logger_loguru import config_logger
logger, llogger = config_logger(logger)
llogger.info("root")
with llogger.inc_indent():
llogger.info("nested")loguru basics
Quick exploration of loguru’s core features and rationale for this module’s configuration choices.
Default handler:
- logs are emitted to sys.stderr by default
- messages can be logged with different severity levels
- messages are formatted using curly braces (it uses str.format() under the hood)
In notebooks, traces are not good:
@logger.catch
def f(x): 100 / x # type: ignore
def g():
f(10)
f(0)
g() ERROR | An error has been caught in function 'g', process 'MainProcess' (43630), thread 'MainThread' (8320000192):
filter traceback in Jupyter when using @logger.catch
In notebooks contexts, configure your handler with backtrace option disabled:
logger.remove()
logger.add(sys.stderr, backtrace=False)7
g()2025-11-13 14:40:05.849 | ERROR | __main__:g:6 - An error has been caught in function 'g', process 'MainProcess' (43630), thread 'MainThread' (8320000192): Traceback (most recent call last): File "/var/folders/np/k2wj6f4s3rj0m9n0yt8pkk680000gn/T/ipykernel_43630/4019600944.py", line 6, in g f(0) └ <function f> File "/var/folders/np/k2wj6f4s3rj0m9n0yt8pkk680000gn/T/ipykernel_43630/4019600944.py", line 2, in f def f(x): 100 / x # type: ignore │ │ └ 0 │ └ 0 └ <function f> ZeroDivisionError: division by zero
Add some colors and formatting to the output:
logger.remove()
i = logger.add(sys.stderr, colorize=True, format="[<fg #66a3ff>{time:YYYY-MM-DD HH:mm:ss}</fg #66a3ff>] [<fg #00ff00>{level}</fg #00ff00>] {message}")
logger.info("test {}", i)
logger.debug("Hello, world!")[2025-11-13 14:40:05] [INFO] test 8 [2025-11-13 14:40:05] [DEBUG] Hello, world!
formatter
Custom formatter that adds indentation tracking for hierarchical log output.
LogFormatter
def LogFormatter(
)->None:
Formats log records with indentation for tree-structured output
fmt = LogFormatter()
test_eq(fmt.indent, '')
test_eq(LogFormatter._ind_level, 0)
record = {"extra": {}, "name": "test", "function": "fn", "line": 10}
fmt.format(record)
test_eq(record["extra"]["indent"], '')
record{'extra': {'indent': ''}, 'name': 'test', 'function': 'fn', 'line': 10}
LogFormatter._ind_level = 2
fmt2 = LogFormatter()
test_eq(fmt2.indent, ' ') # 2 levels * 2 spacesLogFormatter._ind_level = 0 # reset for other testsLogLevelFilter
def LogLevelFilter(
level:int | str
):
Filter log records by minimum level with context manager for temporary override
# ⎸
# LEFT VERTICAL BOX LINE
# Unicode: U+23B8, UTF-8: E2 8E B8
# ⏐
# VERTICAL LINE EXTENSION
# Unicode: U+23D0, UTF-8: E2 8F 90
# │
# BOX DRAWINGS LIGHT VERTICAL
# Unicode: U+2502, UTF-8: E2 94 82loggers
Wrapper that manages indentation levels for hierarchical log output.
tree_logger
def tree_logger(
logger:Any, fmt:LogFormatter
):
Logger wrapper managing hierarchical indentation for tree-structured output
setup
Configuration functions for quick logger setup in library modules
setup_logger
def setup_logger(
logger, name:str='__main__'
)->Logger:
Apply standard config (format, level filter, backtrace) to logger
Note logging is disabled after setup_logger if called from a module distinct to ‘main’.
logger = setup_logger(logger)
logger.info('test') INFO | test
config_logger
def config_logger(
logger:Logger, # Logger instance from each module
)->tuple[Logger, tree_logger]:
Configure logger with colors and return (logger, tree_logger) tuple
Module usage pattern:
Each module imports and configures its own logger:
from loguru import logger
from pote.logger_loguru import config_logger
logger, llogger = config_logger(logger)
# Standard logging
logger.info("Processing started")
# Hierarchical logging
llogger.info("Main operation")
with llogger.inc_indent():
llogger.info("Sub-operation")This gives each module independent logger configuration while maintaining consistent formatting.
logger, llogger = config_logger(logger)
logger.info('configured') INFO | configured
test_eq(llogger.level, 0)
test_eq(llogger.indent, '')
llogger.info('root level') INFO | root level
_ = llogger.push
llogger.info("pushed once")
test_eq(llogger.level, 1)
test_eq(llogger.indent, '│ ') INFO | │ pushed once
_ = llogger.push
llogger.info("pushed twice")
test_eq(llogger.level, 2)
test_eq(llogger.indent, '│ │ ') INFO | │ │ pushed twice
_ = llogger.pop
test_eq(llogger.level, 1)_ = llogger.reset()
test_eq(llogger.level, 0)llogger.info('root')
with llogger.inc_indent():
test_eq(llogger.level, 1)
llogger.info('child')
with llogger.inc_indent():
test_eq(llogger.level, 2)
llogger.info('grandchild')
test_eq(llogger.level, 1)
llogger.info('child')
test_eq(llogger.level, 0)
llogger.info('root') INFO | root
INFO | │ child
INFO | │ │ grandchild
INFO | │ child
INFO | root
class _Test:
a = 'a'
b = 'b'
@llogger.bracket_logging("<n>{}</> <y>{}</>", 'test', b'b')
async def test_bracket_logging_b(self):
llogger.info("inside test_bracket_logging_b")
test_eq(llogger.level, 2) # nested 2 levels deep
@llogger.bracket_logging("<n>{}</> <y>{}</>", 'test', b'a')
async def test_bracket_logging_a(self):
llogger.info("inside test_bracket_logging_a")
test_eq(llogger.level, 1) # nested 1 level
await self.test_bracket_logging_b() # type: ignore
test_eq(llogger.level, 1) # back to 1 after inner returns
_t = _Test()
llogger.reset()
test_eq(llogger.level, 0)
await _t.test_bracket_logging_a() # type: ignore
test_eq(llogger.level, 0) # back to root INFO | test a: >>>> test_bracket_logging_a...
INFO | │ inside test_bracket_logging_a
INFO | │ test b: >>>> test_bracket_logging_b...
INFO | │ │ inside test_bracket_logging_b
bracket_logging decorator with nested calls