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.
- 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
Logis 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(), andLog.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 withs=True. - File logging — Add a color-free file output with
Log.save(). - Standard stream redirection — Capture
print()and writes tostderrinside a context manager. - Progress bars —
Log.tqdm()keeps log messages from overwriting activetqdmprogress bars. - Multiprocessing support — Route logs from worker processes through a
shared listener, including
multiprocessing.Pooland PyTorchDataLoaderworkers.
pip install logcatterLogcatter requires Python 3.7 or later.
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)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.
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.
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")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()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.
Logcatter is available under the MIT License.


