Skip to main content

Send notifications over Nostr

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pylogstr-0.1.1.tar.gz (23.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pylogstr-0.1.1-py3-none-any.whl (16.5 kB view details)

Uploaded Python 3

File details

Details for the file pylogstr-0.1.1.tar.gz.

File metadata

  • Download URL: pylogstr-0.1.1.tar.gz
  • Upload date:
  • Size: 23.0 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

Hashes for pylogstr-0.1.1.tar.gz
Algorithm Hash digest
SHA256 8901f526f82da2422157fcf2d5aff4bcb2cb61cbe130c9080a31d4a45cf03d55
MD5 fee645102b08d62ab7fb647f950e1dcf
BLAKE2b-256 b9563153756905f20a3eb64d7833edb45994e8964bf336763da59480ac857fbf

See more details on using hashes here.

File details

Details for the file pylogstr-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: pylogstr-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 16.5 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

Hashes for pylogstr-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0801e7dcf907ebc81047c857e6ed5753a333cf47b48c8c127bf2fd3340d85805
MD5 96b678f0aa26d2b0bb823eab02ef9f42
BLAKE2b-256 1500bf934944ebf812251ff34caec554436950f22a918cebffbbcae2fa2c5806

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page