
Research
/Security News
Coruna Respawned: Compromised art-template npm Package Leads to iOS Browser Exploit Kit
Compromised npm package art-template delivered a Coruna-like iOS Safari exploit framework through a watering-hole attack.
lognest
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 lognest 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 lognest
| Feature | Example |
|---|---|
| Zero config | from lognest 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 lognest 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 lognest import log
log.intercept_stdlib() # everything now flows through lognest
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. lognest 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 lognest 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
Compromised npm package art-template delivered a Coruna-like iOS Safari exploit framework through a watering-hole attack.

Company News
As AI accelerates how code is written and shipped, Socket is scaling to protect the software supply chain from the growing wave of attacks targeting open source dependencies.

Company News
Socket is scaling to defend open source against supply chain attacks as AI accelerates software development.