Send notifications over Nostr
Project description
logstr
Send log messages and status updates as encrypted Nostr direct messages.
Logstr bridges Python's standard logging module with the Nostr protocol, allowing you to receive encrypted log notifications on any Nostr client that supports direct messages. Perfect for monitoring long-running jobs from your phone.
Installation
pip install logstr
Requires Python 3.10+.
For development (includes test dependencies):
pip install -e ".[dev]"
Quick Start
import logstr
# Initialize with your logstr's nsec and the receiving npub
logstr.init(
nsec_file="~/.config/logstr/nsec", # Logstr's private nsec
recipient_npub="npub...", # Where to send notifications
expiry_in_days=7 # Expire messages after 7 days
)
# Standard logging can now also send encrypted DMs
import logging
logging.error("Database connection failed!", extra={"nostr": True})
# Or bypass logging and only send to over Nostr
logstr.notify("Server is on fire!")
Features
- Encrypted: Uses NIP-04 encrypted direct messages (upgrade to NIP-17 is planned)
- Relay-friendly: NIP-40 expiration timestamps reduce storage load
- Flexible key management: Use CLI args, files, or environment variables
- Optional key generation: Generate a new nsec for your project
- Standard logging: Works with existing
logginginfrastructure - Mobile alerts: Receive logs on any Nostr client
- Async by default: Non-blocking message queue with background flushing
- Decorators: Auto-notify on function entry/exit or exceptions
Usage
Initialization
The init() function sets up the Nostr connection and optionally attaches a handler to the root logger:
import logstr
service = logstr.init(
# Key sources (checked in order: arg > file > env)
nsec="nsec...", # Direct key (optional)
nsec_file="/path/to/nsec", # File containing key (optional)
recipient_npub="npub...", # Recipient's public key or
recipient_npub_file="/path/to/npub", # File containing recipient
# Behavior
auto_create_keys=True, # Generate and store new keys if missing
relays=["wss://relay.damus.io"], # Use custom relays (optional)
expiry_in_days=14, # Message lifetime (None = forever)
flush_interval=20, # Seconds between message flushes
# Logging integration
attach_to_root=True, # Auto-attach NostrHandler to root handler
format_str="%(asctime)s - %(message)s" # Message format
)
Key environment variables: LOGSTR_NSEC, LOGSTR_RECIPIENT_NPUB
Sending Messages
Via standard logging (requires extra={"nostr": True}):
import logging
logging.info("Job started", extra={"nostr": True})
logging.warning("Disk space low", extra={"nostr": True})
logging.error("Payment failed", extra={"nostr": True})
Direct notification (bypasses logging):
logstr.notify("Critical: API is down!")
Decorators
Auto-notify when functions run:
@logstr.notify_on_call
def process_data():
"""Notifies on success and failure"""
pass
@logstr.notify_on_call(only_on_exception=True)
def risky_operation():
"""Only notifies if an exception is raised"""
pass
@logstr.notify_on_call(include_args=False)
def handle_sensitive(user_password):
"""Notifies but redacts arguments"""
pass
Sensitive parameters (password, token, api_key, secret, nsec) are automatically redacted. A custom list can be used via the sensitive_params parameter.
Manual Setup
Alternatively bypass init() and configure manually:
import logging
from logstr import Logstr, NostrHandler
# Create service
service = Logstr(
nsec_file="~/.logstr/nsec",
recipient_npub="npub...",
relays=["wss://relay.damus.io"],
expiry_in_days=7
)
# Create handler
handler = NostrHandler(service)
handler.setLevel(logging.ERROR)
# Attach to specific logger
logger = logging.getLogger("myapp")
logger.addHandler(handler)
Context Manager
with Logstr(nsec_file="nsec.txt", recipient_npub="npub...") as service:
service.queue_message("Hello from context manager")
Cleanup
logstr.close() # Flush pending messages and disconnect
Message Destinations
| Function | Logging handlers | Nostr | When to Use |
|---|---|---|---|
logging.info(..., extra={"nostr": True}) |
✅ | ✅ | Standard logging that also alerts you |
logstr.info() / error() / warning() |
✅ | ✅ | Shorthand for the above |
logstr.notify() |
❌ | ✅ | Nostr only: bypasses logging. Use for urgent alerts you don't want in log files. |
Configuration
Key Management Priority
Keys are loaded in this priority order:
- Direct parameter (
nsec=,recipient_npub=) - File (
nsec_file=,recipient_npub_file=) - Environment variable (
LOGSTR_NSEC,LOGSTR_RECIPIENT_NPUB)
Auto-Generated Keys
If auto_create_keys=True and no key is found, logstr generates a new identity and saves it to ./.logstr/nsec (or the specified nsec_file). You should immediately add it to .gitignore.
Relays
Default relays:
wss://relay.damus.iowss://nos.lol
NIP-40 (expiration) support is automatically detected per relay. Messages sent to non-supporting relays may persist indefinitely.
Message Expiration
Set expiry_in_days to automatically tag messages with NIP-40 expiration timestamps. After this period, relays may delete the messages. This is mostly to reduce storage load on relays. Depending on the Nostr clients even expired message are still shown if they are received before expiry.
This is not a security feature: Relays may retain expired messages
Rate-Limiting
Use flush_interval to set how often messages are sent to relays. Make sure to be conservative, to reduce the risk of rate-limiting. We strongly advise against using logstr for high-frequency logging. Most relays will quickly block your messages.
Examples
Production Error Alerting
import logstr
import logging
logstr.init(
nsec_file="/etc/myapp/nsec",
recipient_npub="npub...", # Your mobile client's npub
expiry_in_days=30
)
try:
process_payment()
except Exception as e:
logging.critical(f"Payment processing failed: {e}", extra={"nostr": True})
raise
Long-Running Job Monitoring
@logstr.notify_on_call
def nightly_backup():
"""Get notified when backup starts and finishes"""
# ... backup logic ...
pass
Status update
import time
while True:
if not check_system_health():
logstr.notify("Health check failed!")
time.sleep(60)
Advanced
Custom Formatting
logstr.init(
format_str="%(name)s | %(levelname)s | %(message)s"
)
Contributing
git clone https://github.com/GitHappens2Me/logstr.git
cd logstr
pip install -e ".[dev]"
pytest
Before submitting a PR:
- Lint: Run
ruff check --fix .to auto-fix style issues (trailing whitespace, import sorting, etc.) - Type check: Run
mypy logstr/to verify type annotations are correct - Test: Run
pytestto ensure all tests pass
Or run all checks at once with task check.
All contributions (suggestions, issues, PRs) are welcome!
License
MIT License - see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pylogstr-0.1.0.tar.gz.
File metadata
- Download URL: pylogstr-0.1.0.tar.gz
- Upload date:
- Size: 23.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a5515d86a95491dbd4dd292dfdca117fbe17656cab949a966e0ee353e601ed6
|
|
| MD5 |
952e4f278d0ff2dcc9abb7b2e8416fc9
|
|
| BLAKE2b-256 |
4c616c2bdbe31f6394330f3abfc1c46fcef6b01672ada11802a4688191bb7d46
|
File details
Details for the file pylogstr-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pylogstr-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6490d2e038d15117994e7e6af7d678528660777fc1ac76169b9cddb7d27168aa
|
|
| MD5 |
a5604666fe5b7e1d99af5fb8dcc2d100
|
|
| BLAKE2b-256 |
8549aacca04ef4252ae149974af5bda9284d1786ccb4de5dceef2f1fa7cda63c
|