Skip to main content

logstr

PyPI version Python versions License: MIT

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 pylogstr

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 logging infrastructure
  • 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:

  1. Direct parameter (nsec=, recipient_npub=)
  2. File (nsec_file=, recipient_npub_file=)
  3. 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.io
  • wss://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 pytest to 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.

Metadata

Release files for pylogstr 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pylogstr 0.1.1
File Size Uploaded
pylogstr-0.1.1.tar.gz 23.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pylogstr 0.1.1
File Interpreter ABI Platform
pylogstr-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 39.5 kB

Release files / pylogstr-0.1.1.tar.gz

Download URL pylogstr-0.1.1.tar.gz
Size 23.0 kB
Tags Source
SHA-256 checksum
How to use checksums
8901f526f82da2422157fcf2d5aff4bcb2cb61cbe130c9080a31d4a45cf03d55
BLAKE2b-256 checksum
How to use checksums
b9563153756905f20a3eb64d7833edb45994e8964bf336763da59480ac857fbf
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release files / pylogstr-0.1.1-py3-none-any.whl

Download URL pylogstr-0.1.1-py3-none-any.whl
Size 16.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0801e7dcf907ebc81047c857e6ed5753a333cf47b48c8c127bf2fd3340d85805
BLAKE2b-256 checksum
How to use checksums
1500bf934944ebf812251ff34caec554436950f22a918cebffbbcae2fa2c5806
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page