
Research
/Security News
Laravel Lang Compromised with RCE Backdoor Across 700+ Versions
Laravel Lang packages were compromised with an RCE backdoor across hundreds of versions, exposing cloud, CI/CD, and developer secrets.
pylogft
Advanced tools
A friendly, zero-config Python logger with pretty output, structured context, file rotation, and clean stdlib interop.
A friendly, zero-configuration Python logger.
One import. Beautiful colored output. Structured context. File rotation.
Pretty tracebacks. Thread-safe. Plays nicely with the standard logging
module. Pure Python, no dependencies.
from pylogft import log
log.info("hello world")
log.warning("careful now", user_id=42)
log.success("all done!")
2026-05-21 12:34:56.789 | INFO | __main__:<module>:3 | hello world
2026-05-21 12:34:56.790 | WARNING | __main__:<module>:4 | careful now user_id=42
2026-05-21 12:34:56.790 | SUCCESS | __main__:<module>:5 | all done!
pip install pylogft
| Feature | Example |
|---|---|
| Zero config | from pylogft import log; log.info("hi") |
| Levels | trace · debug · info · success · warning · error · critical |
| Structured context | log.info("done", request_id="abc", ms=42) |
| Bound context | req_log = log.bind(request_id="abc") |
| Scoped context | with log.contextualize(user="alice"): ... |
| File output | log.add("app.log") |
| Size rotation | log.add("app.log", rotate="10 MB", keep=5) |
| Time rotation | log.add("app.log", rotate="daily", keep="7 days", compress=True) |
| JSON output | log.add("app.jsonl", serialize=True) |
| Custom format | log.add(sys.stderr, format="{time} {level} {message}") |
| Exception catching | @log.catch / with log.catch(): ... |
| Exception with TB | try: ... except: log.exception("oops") |
| stdlib interop | log.intercept_stdlib() |
| Custom sink | log.add(my_callable) |
| Per-sink filter | log.add(..., filter=lambda r: "secret" not in r["message"]) |
| Per-sink level | log.add(..., level="WARNING") |
from pylogft import log
log.trace("very fine detail")
log.debug("debug detail")
log.info("informational")
log.success("operation succeeded")
log.warning("watch out")
log.error("something failed")
log.critical("crashing now")
Pass keyword arguments to attach structured context to a record:
log.info("payment processed", order_id=1234, amount=29.99, currency="USD")
# ... payment processed order_id=1234 amount=29.99 currency=USD
The message itself can also reference kwargs by name:
log.info("user {name} logged in from {ip}", name="alice", ip="10.0.0.1")
req_log = log.bind(request_id="abc-123", user="alice")
req_log.info("started")
req_log.info("finished", ms=42)
with log.contextualize(job="cleanup"):
log.info("removed temp files") # job=cleanup appears in the record
log.add(...) registers a new sink. The default stderr sink stays in
place; you do not need to remove it.
# Plain file
log.add("app.log")
# Rotate every 10 megabytes, keep the 5 most recent
log.add("app.log", rotate="10 MB", keep=5)
# Rotate every day, keep a week, gzip the rotated files
log.add("app.log", rotate="daily", keep="7 days", compress=True)
# Errors only
log.add("errors.log", level="ERROR")
# JSON lines (great for log shippers and analytics)
log.add("events.jsonl", serialize=True)
# Custom format string
log.add("simple.log", format="{time} [{level}] {message}")
# Custom callable sink — receives the fully-rendered line
log.add(lambda line: my_queue.put(line))
log.add(...) returns an integer id. Use it to remove a single sink, or
call log.remove() to remove all sinks.
sid = log.add("app.log")
log.remove(sid)
log.remove() # remove everything (incl. the default stderr sink)
Rotation accepts:
"10 MB", "500 KB", "1 GB", or a raw byte count 10_000_000"hourly", "daily", "weekly", "monthly"Retention accepts:
keep=5 keeps the five most recent rotated fileskeep="7 days" removes anything olderAs a context manager:
with log.catch():
risky_call()
As a decorator:
@log.catch
def task():
1 / 0
@log.catch(reraise=True, message="task crashed")
def task():
...
Inside an except block:
try:
risky_call()
except Exception:
log.exception("call failed", endpoint="/api/x")
All three produce a clean, color-coded traceback.
If a third-party library uses logging.getLogger(...), just reroute it:
from pylogft import log
log.intercept_stdlib() # everything now flows through pylogft
import some_library
some_library.do_thing()
The default format may be a str template or a callable.
Available placeholders for string templates:
{time} {level} {name} {function} {line} {file}
{message} {thread} {process}
{extra.key} # any key attached via bind / contextualize / kwargs
For complete control, pass a callable that receives the record dict and returns a string:
def my_format(record):
return f"[{record['level']}] {record['message']}"
log.add(sys.stderr, format=my_format)
There are good loggers in the Python ecosystem already. pylogft aims for
the shortest path from "I want logs" to "I have nice logs":
kwargs on every call.rotate="10 MB", keep="7 days".MIT.
FAQs
A friendly, zero-config Python logger with pretty output, structured context, file rotation, and clean stdlib interop.
We found that pylogft demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Laravel Lang packages were compromised with an RCE backdoor across hundreds of versions, exposing cloud, CI/CD, and developer secrets.

Security News
Socket found a malicious postinstall hook across 700+ GitHub repos, including PHP packages on Packagist and Node.js project repositories.

Security News
Vibe coding at scale is reshaping how packages are created, contributed, and selected across the software supply chain