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 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 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.0.tar.gz (23.1 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.0-py3-none-any.whl (16.6 kB view details)

Uploaded Python 3

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

Hashes for pylogstr-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4a5515d86a95491dbd4dd292dfdca117fbe17656cab949a966e0ee353e601ed6
MD5 952e4f278d0ff2dcc9abb7b2e8416fc9
BLAKE2b-256 4c616c2bdbe31f6394330f3abfc1c46fcef6b01672ada11802a4688191bb7d46

See more details on using hashes here.

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

Hashes for pylogstr-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6490d2e038d15117994e7e6af7d678528660777fc1ac76169b9cddb7d27168aa
MD5 a5604666fe5b7e1d99af5fb8dcc2d100
BLAKE2b-256 8549aacca04ef4252ae149974af5bda9284d1786ccb4de5dceef2f1fa7cda63c

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