Skip to content

Repository files navigation

Logcatter

Android Logcat-style logging for Python.

Logcatter provides a compact static API, automatic source-file tags, colored log levels, exception and stack trace output, file logging, and multiprocessing support without requiring a logging configuration file.

View Logcatter on PyPI

Features

  • Logcat-style output — Logs use the format YYYY-MM-DD HH:mm:ss SSS [L/filename.py] message.
  • Automatic source tags — The filename that called Log is included in every entry, making the source of a message easy to identify.
  • Six log levels — Use Log.v(), Log.d(), Log.i(), Log.w(), Log.e(), and Log.f() for verbose through fatal messages.
  • Colored console output — Each severity is color-coded for quick scanning.
  • Runtime level filtering — Change the minimum visible level with Log.set_level().
  • Exception and stack traces — Attach an exception with e= or include the current stack with s=True.
  • File logging — Add a color-free file output with Log.save().
  • Standard stream redirection — Capture print() and writes to stderr inside a context manager.
  • Progress barsLog.tqdm() keeps log messages from overwriting active tqdm progress bars.
  • Multiprocessing support — Route logs from worker processes through a shared listener, including multiprocessing.Pool and PyTorch DataLoader workers.

Installation

pip install logcatter

Logcatter requires Python 3.7 or later.

Quick start

Initialize Logcatter near the start of the program and dispose it before the program exits so queued messages are flushed.

from logcatter import Log

Log.init()

try:
    Log.d("Loading configuration")
    Log.i("Application started")

    Log.set_level(Log.WARNING)
    Log.i("This message is filtered out")
    Log.w("Only warnings and higher are now shown")

    try:
        raise ValueError("Invalid value")
    except ValueError as error:
        Log.e("Request failed", e=error)

    Log.f("Fatal error with the current stack", s=True)
finally:
    Log.dispose()

Available levels, from lowest to highest severity:

Level Method Constant
Verbose Log.v() Log.VERBOSE
Debug Log.d() Log.DEBUG
Info Log.i() Log.INFO
Warning Log.w() Log.WARNING
Error Log.e() Log.ERROR
Fatal Log.f() Log.FATAL

Logging methods also support standard logging-style arguments:

Log.i("Processed %d records", record_count)

Redirect stdout and stderr

Use Log.redirect() to apply Logcatter formatting to code that writes with print() or directly to a standard stream.

import sys

from logcatter import Log

Log.init()

try:
    with Log.redirect(stdout=Log.INFO, stderr=Log.ERROR):
        print("Captured as an INFO message")
        sys.stderr.write("Captured as an ERROR message\n")
finally:
    Log.dispose()

Set either argument to None to leave that stream unchanged. The defaults redirect stdout at VERBOSE level and leave stderr unchanged.

Output that uses carriage returns to redraw the current line is not reformatted. For progress bars, use Log.tqdm() instead.

Use with tqdm

Log.tqdm() accepts the same arguments as tqdm.tqdm and prevents log entries from being appended to the progress-bar line.

from logcatter import Log

Log.init()

try:
    for item in Log.tqdm(items, desc="Processing"):
        Log.i("Processing %s", item)
finally:
    Log.dispose()

It can also be used as a context manager for manual progress updates.

Save logs to a file

Call Log.save() to add a file handler. File output uses the same Logcat-style format without ANSI color codes.

from logcatter import Log

Log.init()
Log.save("application.log")

try:
    Log.i("Written to both the console and application.log")
finally:
    Log.dispose()

The default mode is "w". Pass mode="a" to append instead:

Log.save("application.log", mode="a")

Multiprocessing

Call Log.init() in the main process, then use the callable returned by Log.init_worker() as the pool initializer. Keep the entry-point guard when using multiprocessing.

import multiprocessing

from logcatter import Log


def process_item(item):
    Log.i("Processing %s", item)


if __name__ == "__main__":
    Log.init()

    try:
        with multiprocessing.Pool(
            processes=2,
            initializer=Log.init_worker(),
        ) as pool:
            pool.map(process_item, range(4))
    finally:
        Log.dispose()

PyTorch DataLoader

Pass Log.init_worker() to worker_init_fn so worker logs use the shared log queue.

from torch.utils.data import DataLoader

from logcatter import Log

Log.init()

train_loader = DataLoader(
    dataset,
    num_workers=4,
    worker_init_fn=Log.init_worker(),
)

Call Log.dispose() after the loader and its workers are no longer needed.

Output examples

Visual Studio Code

Logcatter output in Visual Studio Code

PyCharm

Logcatter output in PyCharm

PowerShell 7 in Windows Terminal

Logcatter output in PowerShell 7 on Windows Terminal

License

Logcatter is available under the MIT License.

About

Brings the familiar convenience and readability of Android's Logcat to your Python projects

Topics

Resources

Stars

Watchers

Forks

Releases

Contributors

Languages