From 089313f613ca0ca6efc13b5c5185180f810ae659 Mon Sep 17 00:00:00 2001 From: calle <93376500+callebtc@users.noreply.github.com> Date: Thu, 7 Jul 2022 14:30:16 +0200 Subject: [PATCH] Logging with loguru (#708) * logging * requirements * add loguru dependency * restore it * add loguru * set log level in .env file * remove service fee print * set log level * more logging * more logging * more logging * pyament.checking_id * fix --- .env.example | 6 ++--- Pipfile | 1 + lnbits/__main__.py | 26 +++++++++++++-------- lnbits/app.py | 29 +++++++++++++++++------- lnbits/commands.py | 6 +++-- lnbits/core/models.py | 9 ++++++-- lnbits/core/services.py | 23 +++++++++++++++---- lnbits/core/tasks.py | 7 ++++-- lnbits/core/views/api.py | 8 +++++-- lnbits/core/views/generic.py | 11 ++++++++- lnbits/core/views/public_api.py | 4 +++- lnbits/db.py | 4 +++- lnbits/extensions/bleskomat/lnurl_api.py | 4 +++- lnbits/extensions/bleskomat/models.py | 4 +++- lnbits/extensions/bleskomat/views_api.py | 4 +++- lnbits/extensions/livestream/tasks.py | 6 +++-- lnbits/extensions/lnaddress/crud.py | 6 +++-- lnbits/extensions/lnaddress/lnurl.py | 6 +++-- lnbits/extensions/lnticket/tasks.py | 4 +++- lnbits/extensions/lnurldevice/lnurl.py | 4 ++-- lnbits/extensions/lnurlpayout/tasks.py | 5 +++- lnbits/extensions/satspay/tasks.py | 4 +++- lnbits/extensions/splitpayments/tasks.py | 6 ++++- lnbits/extensions/tpos/views_api.py | 4 +++- lnbits/extensions/withdraw/lnurl.py | 6 +++-- lnbits/settings.py | 4 ++-- lnbits/tasks.py | 11 +++++---- lnbits/utils/exchange_rates.py | 4 +++- lnbits/wallets/eclair.py | 6 +++-- lnbits/wallets/fake.py | 11 +++++---- lnbits/wallets/lnbits.py | 6 ++++- lnbits/wallets/lndgrpc.py | 9 ++++++-- lnbits/wallets/lndrest.py | 6 ++++- lnbits/wallets/lnpay.py | 4 +++- lnbits/wallets/lntxbot.py | 6 ++++- lnbits/wallets/macaroon/macaroon.py | 6 +++-- lnbits/wallets/opennode.py | 4 +++- lnbits/wallets/spark.py | 4 +++- lnbits/wallets/void.py | 4 +++- requirements.txt | 1 + 40 files changed, 202 insertions(+), 81 deletions(-) diff --git a/.env.example b/.env.example index be818c4b..4d2241e3 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,8 @@ -QUART_APP=lnbits.app:create_app() -QUART_ENV=development -QUART_DEBUG=true - HOST=127.0.0.1 PORT=5000 +DEBUG=true + LNBITS_ALLOWED_USERS="" LNBITS_ADMIN_USERS="" # Extensions only admin can access diff --git a/Pipfile b/Pipfile index 6e738367..7d676984 100644 --- a/Pipfile +++ b/Pipfile @@ -12,6 +12,7 @@ cerberus = "*" ecdsa = "*" environs = "*" lnurl = "==0.3.6" +loguru = "*" pyscss = "*" shortuuid = "*" typing-extensions = "*" diff --git a/lnbits/__main__.py b/lnbits/__main__.py index 8461eb42..e4f6ee0c 100644 --- a/lnbits/__main__.py +++ b/lnbits/__main__.py @@ -3,15 +3,19 @@ import asyncio import uvloop from starlette.requests import Request +from loguru import logger + from .commands import bundle_vendored, migrate_databases, transpile_scss from .settings import ( DEBUG, LNBITS_COMMIT, LNBITS_DATA_FOLDER, LNBITS_SITE_TITLE, + HOST, PORT, - SERVICE_FEE, WALLET, + LNBITS_DATABASE_URL, + LNBITS_DATA_FOLDER, ) uvloop.install() @@ -24,13 +28,15 @@ from .app import create_app app = create_app() -print( - f"""Starting LNbits with - - git version: {LNBITS_COMMIT} - - site title: {LNBITS_SITE_TITLE} - - debug: {DEBUG} - - data folder: {LNBITS_DATA_FOLDER} - - funding source: {WALLET.__class__.__name__} - - service fee: {SERVICE_FEE} -""" +logger.info("Starting LNbits") +logger.info(f"Host: {HOST}") +logger.info(f"Port: {PORT}") +logger.info(f"Debug: {DEBUG}") +logger.info(f"Site title: {LNBITS_SITE_TITLE}") +logger.info(f"Funding source: {WALLET.__class__.__name__}") +logger.info( + f"Database: {'PostgreSQL' if LNBITS_DATABASE_URL.startswith('postgres://') else 'CockroachDB' if LNBITS_DATABASE_URL.startswith('cockroachdb://') else 'SQLite'}" ) +logger.info(f"Data folder: {LNBITS_DATA_FOLDER}") +logger.info(f"Git version: {LNBITS_COMMIT}") +# logger.info(f"Service fee: {SERVICE_FEE}") diff --git a/lnbits/app.py b/lnbits/app.py index 748c49a6..3d75ed15 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -3,6 +3,9 @@ import importlib import sys import traceback import warnings + +from loguru import logger + from http import HTTPStatus from fastapi import FastAPI, Request @@ -41,6 +44,8 @@ def create_app(config_object="lnbits.settings") -> FastAPI: """Create application factory. :param config_object: The configuration object to use. """ + set_logging_level() + app = FastAPI() app.mount("/static", StaticFiles(directory="lnbits/static"), name="static") app.mount( @@ -94,14 +99,14 @@ def check_funding_source(app: FastAPI) -> None: error_message, balance = await WALLET.status() if not error_message: break - warnings.warn( - f" × The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'", + logger.error( + f"The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'", RuntimeWarning, ) - print("Retrying connection to backend in 5 seconds...") + logger.info("Retrying connection to backend in 5 seconds...") await asyncio.sleep(5) - print( - f" ✔️ {WALLET.__class__.__name__} seems to be connected and with a balance of {balance} msat." + logger.info( + f"✔️ Backend {WALLET.__class__.__name__} connected and with a balance of {balance} msat." ) @@ -124,9 +129,10 @@ def register_routes(app: FastAPI) -> None: for s in ext_statics: app.mount(s["path"], s["app"], s["name"]) + logger.trace(f"adding route for extension {ext_module}") app.include_router(ext_route) except Exception as e: - print(str(e)) + logger.error(str(e)) raise ImportError( f"Please make sure that the extension `{ext.code}` follows conventions." ) @@ -173,8 +179,8 @@ def register_async_tasks(app): def register_exception_handlers(app: FastAPI): @app.exception_handler(Exception) async def basic_error(request: Request, err): - print("handled error", traceback.format_exc()) - print("ERROR:", err) + logger.error("handled error", traceback.format_exc()) + logger.error("ERROR:", err) etype, _, tb = sys.exc_info() traceback.print_exception(etype, err, tb) exc = traceback.format_exc() @@ -188,3 +194,10 @@ def register_exception_handlers(app: FastAPI): status_code=HTTPStatus.NO_CONTENT, content={"detail": err}, ) + + +def set_logging_level() -> None: + """Set the logging level for the application.""" + logger.remove() + log_level: str = "DEBUG" if lnbits.settings.DEBUG else "INFO" + logger.add(sys.stderr, level=log_level) diff --git a/lnbits/commands.py b/lnbits/commands.py index 95950760..13e926e8 100644 --- a/lnbits/commands.py +++ b/lnbits/commands.py @@ -5,6 +5,8 @@ import importlib import re import os +from loguru import logger + from .db import SQLITE, POSTGRES, COCKROACH from .core import db as core_db, migrations as core_migrations from .helpers import ( @@ -69,7 +71,7 @@ async def migrate_databases(): if match: version = int(match.group(1)) if version > current_versions.get(db_name, 0): - print(f"running migration {db_name}.{version}") + logger.debug(f"running migration {db_name}.{version}") await migrate(db) if db.schema == None: @@ -110,4 +112,4 @@ async def migrate_databases(): async with ext_db.connect() as ext_conn: await run_migration(ext_conn, ext_migrations) - print(" ✔️ All migrations done.") + logger.info("✔️ All migrations done.") diff --git a/lnbits/core/models.py b/lnbits/core/models.py index 88963b2b..a6cdb23b 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -7,6 +7,9 @@ from lnurl import encode as lnurl_encode # type: ignore from typing import List, NamedTuple, Optional, Dict from sqlite3 import Row from pydantic import BaseModel + +from loguru import logger + from lnbits.settings import WALLET @@ -142,10 +145,12 @@ class Payment(BaseModel): status = await WALLET.get_invoice_status(self.checking_id) if self.is_out and status.failed: - print(f" - deleting outgoing failed payment {self.checking_id}: {status}") + logger.info( + f" - deleting outgoing failed payment {self.checking_id}: {status}" + ) await self.delete() elif not status.pending: - print( + logger.info( f" - marking '{'in' if self.is_in else 'out'}' {self.checking_id} as not pending anymore: {status}" ) await self.set_pending(status.pending) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 3d54e218..6a24348e 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -3,6 +3,9 @@ import json from binascii import unhexlify from io import BytesIO from typing import Dict, Optional, Tuple + +from loguru import logger + from urllib.parse import parse_qs, urlparse import httpx @@ -120,6 +123,7 @@ async def pay_invoice( # check_internal() returns the checking_id of the invoice we're waiting for internal_checking_id = await check_internal(invoice.payment_hash, conn=conn) if internal_checking_id: + logger.debug(f"creating temporary internal payment with id {internal_id}") # create a new payment from this wallet await create_payment( checking_id=internal_id, @@ -129,6 +133,7 @@ async def pay_invoice( **payment_kwargs, ) else: + logger.debug(f"creating temporary payment with id {temp_id}") # create a temporary payment here so we can check if # the balance is enough in the next step await create_payment( @@ -142,6 +147,7 @@ async def pay_invoice( wallet = await get_wallet(wallet_id, conn=conn) assert wallet if wallet.balance_msat < 0: + logger.debug("balance is too low, deleting temporary payment") if not internal_checking_id and wallet.balance_msat > -fee_reserve_msat: raise PaymentFailure( f"You must reserve at least 1% ({round(fee_reserve_msat/1000)} sat) to cover potential routing fees." @@ -149,6 +155,7 @@ async def pay_invoice( raise PermissionError("Insufficient balance.") if internal_checking_id: + logger.debug(f"marking temporary payment as not pending {internal_checking_id}") # mark the invoice from the other side as not pending anymore # so the other side only has access to his new money when we are sure # the payer has enough to deduct from @@ -163,11 +170,14 @@ async def pay_invoice( await internal_invoice_queue.put(internal_checking_id) else: + logger.debug(f"backend: sending payment {temp_id}") # actually pay the external invoice payment: PaymentResponse = await WALLET.pay_invoice( payment_request, fee_reserve_msat ) + logger.debug(f"backend: pay_invoice finished {temp_id}") if payment.checking_id: + logger.debug(f"creating final payment {payment.checking_id}") async with db.connect() as conn: await create_payment( checking_id=payment.checking_id, @@ -177,15 +187,18 @@ async def pay_invoice( conn=conn, **payment_kwargs, ) + logger.debug(f"deleting temporary payment {temp_id}") await delete_payment(temp_id, conn=conn) else: + logger.debug(f"backend payment failed, no checking_id {temp_id}") async with db.connect() as conn: + logger.debug(f"deleting temporary payment {temp_id}") await delete_payment(temp_id, conn=conn) raise PaymentFailure( payment.error_message or "Payment failed, but backend didn't give us an error message." ) - + logger.debug(f"payment successful {payment.checking_id}") return invoice.payment_hash @@ -216,7 +229,7 @@ async def redeem_lnurl_withdraw( conn=conn, ) except: - print( + logger.warn( f"failed to create invoice on redeem_lnurl_withdraw from {lnurl}. params: {res}" ) return None @@ -325,11 +338,11 @@ async def check_invoice_status( if not payment.pending: return status if payment.is_out and status.failed: - print(f" - deleting outgoing failed payment {payment.checking_id}: {status}") + logger.info(f"deleting outgoing failed payment {payment.checking_id}: {status}") await payment.delete() elif not status.pending: - print( - f" - marking '{'in' if payment.is_in else 'out'}' {payment.checking_id} as not pending anymore: {status}" + logger.info( + f"marking '{'in' if payment.is_in else 'out'}' {payment.checking_id} as not pending anymore: {status}" ) await payment.set_pending(status.pending) return status diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index 1e3eaeab..bb258958 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -2,6 +2,8 @@ import asyncio import httpx from typing import List +from loguru import logger + from lnbits.tasks import register_invoice_listener from . import db @@ -20,7 +22,7 @@ async def register_task_listeners(): async def wait_for_paid_invoices(invoice_paid_queue: asyncio.Queue): while True: payment = await invoice_paid_queue.get() - + logger.debug("received invoice paid event") # send information to sse channel await dispatch_invoice_listener(payment) @@ -44,7 +46,7 @@ async def dispatch_invoice_listener(payment: Payment): try: send_channel.put_nowait(payment) except asyncio.QueueFull: - print("removing sse listener", send_channel) + logger.debug("removing sse listener", send_channel) api_invoice_listeners.remove(send_channel) @@ -52,6 +54,7 @@ async def dispatch_webhook(payment: Payment): async with httpx.AsyncClient() as client: data = payment.dict() try: + logger.debug("sending webhook", payment.webhook) r = await client.post(payment.webhook, json=data, timeout=40) await mark_webhook_sent(payment, r.status_code) except (httpx.ConnectError, httpx.RequestError): diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index babdb2ce..947b24e1 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -7,6 +7,9 @@ from typing import Dict, List, Optional, Union from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx + +from loguru import logger + from fastapi import Header, Query, Request from fastapi.exceptions import HTTPException from fastapi.param_functions import Depends @@ -347,7 +350,7 @@ async def subscribe(request: Request, wallet: Wallet): payment_queue: asyncio.Queue[Payment] = asyncio.Queue(0) - print("adding sse listener", payment_queue) + logger.debug("adding sse listener", payment_queue) api_invoice_listeners.append(payment_queue) send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0) @@ -356,6 +359,7 @@ async def subscribe(request: Request, wallet: Wallet): while True: payment: Payment = await payment_queue.get() if payment.wallet_id == this_wallet_id: + logger.debug("payment receieved", payment) await send_queue.put(("payment-received", payment)) asyncio.create_task(payment_received()) @@ -391,7 +395,7 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)): wallet = None try: if X_Api_Key.extra: - print("No key") + logger.warn("No key") except: wallet = await get_wallet_for_key(X_Api_Key) payment = await get_standalone_payment(payment_hash) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index f8b98e4c..b6750c95 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -10,6 +10,8 @@ from fastapi.routing import APIRouter from pydantic.types import UUID4 from starlette.responses import HTMLResponse, JSONResponse +from loguru import logger + from lnbits.core import db from lnbits.core.models import User from lnbits.decorators import check_user_exists @@ -66,10 +68,12 @@ async def extensions( ) if extension_to_enable: + logger.info(f"Enabling extension: {extension_to_enable} for user {user.id}") await update_user_extension( user_id=user.id, extension=extension_to_enable, active=True ) elif extension_to_disable: + logger.info(f"Disabling extension: {extension_to_disable} for user {user.id}") await update_user_extension( user_id=user.id, extension=extension_to_disable, active=False ) @@ -109,6 +113,7 @@ async def wallet( if not user_id: user = await get_user((await create_account()).id) + logger.info(f"Created new account for user {user.id}") else: user = await get_user(user_id) if not user: @@ -126,12 +131,16 @@ async def wallet( wallet = user.wallets[0] else: wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name) + logger.info( + f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}" + ) return RedirectResponse( f"/wallet?usr={user.id}&wal={wallet.id}", status_code=status.HTTP_307_TEMPORARY_REDIRECT, ) + logger.info(f"Access wallet {wallet_name} of user {user.id}") wallet = user.get_wallet(wallet_id) if not wallet: return template_renderer().TemplateResponse( @@ -202,13 +211,13 @@ async def lnurl_full_withdraw_callback(request: Request): async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): user = await get_user(usr) user_wallet_ids = [u.id for u in user.wallets] - print("USR", user_wallet_ids) if wal not in user_wallet_ids: raise HTTPException(HTTPStatus.FORBIDDEN, "Not your wallet.") else: await delete_wallet(user_id=user.id, wallet_id=wal) user_wallet_ids.remove(wal) + logger.debug("Deleted wallet {wal} of user {user.id}") if user_wallet_ids: return RedirectResponse( diff --git a/lnbits/core/views/public_api.py b/lnbits/core/views/public_api.py index 5f8be4e2..0b84243e 100644 --- a/lnbits/core/views/public_api.py +++ b/lnbits/core/views/public_api.py @@ -7,6 +7,8 @@ from fastapi import HTTPException from starlette.requests import Request from starlette.responses import HTMLResponse +from loguru import logger + from lnbits import bolt11 from .. import core_app @@ -45,7 +47,7 @@ async def api_public_payment_longpolling(payment_hash): payment_queue = asyncio.Queue(0) - print("adding standalone invoice listener", payment_hash, payment_queue) + logger.debug("adding standalone invoice listener", payment_hash, payment_queue) api_invoice_listeners.append(payment_queue) response = None diff --git a/lnbits/db.py b/lnbits/db.py index 7bbfa5c5..bed9397f 100644 --- a/lnbits/db.py +++ b/lnbits/db.py @@ -5,6 +5,8 @@ import time from contextlib import asynccontextmanager from typing import Optional +from loguru import logger + from sqlalchemy import create_engine from sqlalchemy_aio.base import AsyncConnection from sqlalchemy_aio.strategy import ASYNCIO_STRATEGY # type: ignore @@ -139,7 +141,7 @@ class Database(Compat): f"LNBITS_DATA_FOLDER named {LNBITS_DATA_FOLDER} was not created" f" - please 'mkdir {LNBITS_DATA_FOLDER}' and try again" ) - + logger.trace(f"database {self.type} added for {self.name}") self.schema = self.name if self.name.startswith("ext_"): self.schema = self.name[4:] diff --git a/lnbits/extensions/bleskomat/lnurl_api.py b/lnbits/extensions/bleskomat/lnurl_api.py index 25ff0412..212f9b4a 100644 --- a/lnbits/extensions/bleskomat/lnurl_api.py +++ b/lnbits/extensions/bleskomat/lnurl_api.py @@ -5,6 +5,8 @@ from http import HTTPStatus from starlette.requests import Request +from loguru import logger + from . import bleskomat_ext from .crud import ( create_bleskomat_lnurl, @@ -122,7 +124,7 @@ async def api_bleskomat_lnurl(req: Request): except LnurlHttpError as e: return {"status": "ERROR", "reason": str(e)} except Exception as e: - print(str(e)) + logger.error(str(e)) return {"status": "ERROR", "reason": "Unexpected error"} return {"status": "OK"} diff --git a/lnbits/extensions/bleskomat/models.py b/lnbits/extensions/bleskomat/models.py index 267cc949..ab952b55 100644 --- a/lnbits/extensions/bleskomat/models.py +++ b/lnbits/extensions/bleskomat/models.py @@ -6,6 +6,8 @@ from fastapi.params import Query from pydantic import BaseModel, validator from starlette.requests import Request +from loguru import logger + from lnbits import bolt11 from lnbits.core.services import pay_invoice, PaymentFailure @@ -125,7 +127,7 @@ class BleskomatLnurl(BaseModel): except (ValueError, PermissionError, PaymentFailure) as e: raise LnurlValidationError("Failed to pay invoice: " + str(e)) except Exception as e: - print(str(e)) + logger.error(str(e)) raise LnurlValidationError("Unexpected error") async def use(self, conn) -> bool: diff --git a/lnbits/extensions/bleskomat/views_api.py b/lnbits/extensions/bleskomat/views_api.py index 0680de0d..81efa4ab 100644 --- a/lnbits/extensions/bleskomat/views_api.py +++ b/lnbits/extensions/bleskomat/views_api.py @@ -3,6 +3,8 @@ from http import HTTPStatus from fastapi import Depends, Query from starlette.exceptions import HTTPException +from loguru import logger + from lnbits.core.crud import get_user from lnbits.decorators import WalletTypeInfo, require_admin_key from lnbits.extensions.bleskomat.models import CreateBleskomat @@ -60,7 +62,7 @@ async def api_bleskomat_create_or_update( currency=fiat_currency, provider=exchange_rate_provider ) except Exception as e: - print(e) + logger.error(e) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=f'Failed to fetch BTC/{fiat_currency} currency pair from "{exchange_rate_provider}"', diff --git a/lnbits/extensions/livestream/tasks.py b/lnbits/extensions/livestream/tasks.py index c4cc3b57..5273a89e 100644 --- a/lnbits/extensions/livestream/tasks.py +++ b/lnbits/extensions/livestream/tasks.py @@ -1,6 +1,8 @@ import asyncio import json +from loguru import logger + from lnbits.core import db as core_db from lnbits.core.crud import create_payment from lnbits.core.models import Payment @@ -26,11 +28,11 @@ async def on_invoice_paid(payment: Payment) -> None: track = await get_track(payment.extra.get("track", -1)) if not track: - print("this should never happen", payment) + logger.error("this should never happen", payment) return if payment.extra.get("shared_with"): - print("payment was shared already", payment) + logger.error("payment was shared already", payment) return producer = await get_producer(track.producer) diff --git a/lnbits/extensions/lnaddress/crud.py b/lnbits/extensions/lnaddress/crud.py index 3b5822b4..25338215 100644 --- a/lnbits/extensions/lnaddress/crud.py +++ b/lnbits/extensions/lnaddress/crud.py @@ -1,6 +1,8 @@ from datetime import datetime, timedelta from typing import List, Optional, Union +from loguru import logger + from lnbits.helpers import urlsafe_short_hash from . import db @@ -186,9 +188,9 @@ async def purge_addresses(domain_id: str): ) # give user 1 day to topup is address if not paid and pay_expire: - print("DELETE UNP_PAY_EXP", r["username"]) + logger.debug("DELETE UNP_PAY_EXP", r["username"]) await delete_address(r["id"]) if paid and expired: - print("DELETE PAID_EXP", r["username"]) + logger.debug("DELETE PAID_EXP", r["username"]) await delete_address(r["id"]) diff --git a/lnbits/extensions/lnaddress/lnurl.py b/lnbits/extensions/lnaddress/lnurl.py index fa26fa91..2b064e0a 100644 --- a/lnbits/extensions/lnaddress/lnurl.py +++ b/lnbits/extensions/lnaddress/lnurl.py @@ -3,6 +3,9 @@ import json from datetime import datetime, timedelta import httpx + +from loguru import logger + from fastapi.params import Query from lnurl import ( # type: ignore LnurlErrorResponse, @@ -38,13 +41,12 @@ async def lnurl_response(username: str, domain: str, request: Request): "maxSendable": 1000000000, } - print("RESP", resp) + logger.debug("RESP", resp) return resp @lnaddress_ext.get("/lnurl/cb/{address_id}", name="lnaddress.lnurl_callback") async def lnurl_callback(address_id, amount: int = Query(...)): - print("PING") address = await get_address(address_id) if not address: return LnurlErrorResponse(reason=f"Address not found").dict() diff --git a/lnbits/extensions/lnticket/tasks.py b/lnbits/extensions/lnticket/tasks.py index 2d79fb51..23d485b4 100644 --- a/lnbits/extensions/lnticket/tasks.py +++ b/lnbits/extensions/lnticket/tasks.py @@ -1,5 +1,7 @@ import asyncio +from loguru import logger + from lnbits.core.models import Payment from lnbits.tasks import register_invoice_listener @@ -22,7 +24,7 @@ async def on_invoice_paid(payment: Payment) -> None: ticket = await get_ticket(payment.checking_id) if not ticket: - print("this should never happen", payment) + logger.error("this should never happen", payment) return await payment.set_pending(False) diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py index 520a85eb..26a127f1 100644 --- a/lnbits/extensions/lnurldevice/lnurl.py +++ b/lnbits/extensions/lnurldevice/lnurl.py @@ -150,7 +150,7 @@ async def lnurl_v1_params( "defaultDescription": device.title, } price_msat = int(price_msat * ((device.profit / 100) + 1) / 1000) - print(price_msat) + lnurldevicepayment = await create_lnurldevicepayment( deviceid=device.id, payload=p, @@ -204,7 +204,7 @@ async def lnurl_callback( extra={"tag": "withdraw"}, ) return {"status": "OK"} - print(lnurldevicepayment.sats) + payment_hash, payment_request = await create_invoice( wallet_id=device.wallet, amount=lnurldevicepayment.sats / 1000, diff --git a/lnbits/extensions/lnurlpayout/tasks.py b/lnbits/extensions/lnurlpayout/tasks.py index 7f2a8324..d2b3809b 100644 --- a/lnbits/extensions/lnurlpayout/tasks.py +++ b/lnbits/extensions/lnurlpayout/tasks.py @@ -2,6 +2,9 @@ import asyncio from http import HTTPStatus import httpx + +from loguru import logger + from starlette.exceptions import HTTPException from lnbits.core import db as core_db @@ -27,7 +30,7 @@ async def on_invoice_paid(payment: Payment) -> None: try: # Check its got a payout associated with it lnurlpayout_link = await get_lnurlpayout_from_wallet(payment.wallet_id) - print("LNURLpayout", lnurlpayout_link) + logger.debug("LNURLpayout", lnurlpayout_link) if lnurlpayout_link: # Check the wallet balance is more than the threshold diff --git a/lnbits/extensions/satspay/tasks.py b/lnbits/extensions/satspay/tasks.py index 87f74b7d..7ee6298c 100644 --- a/lnbits/extensions/satspay/tasks.py +++ b/lnbits/extensions/satspay/tasks.py @@ -1,5 +1,7 @@ import asyncio +from loguru import logger + from lnbits.core.models import Payment from lnbits.extensions.satspay.crud import check_address_balance, get_charge from lnbits.tasks import register_invoice_listener @@ -23,7 +25,7 @@ async def on_invoice_paid(payment: Payment) -> None: charge = await get_charge(payment.memo) if not charge: - print("this should never happen", payment) + logger.error("this should never happen", payment) return await payment.set_pending(False) diff --git a/lnbits/extensions/splitpayments/tasks.py b/lnbits/extensions/splitpayments/tasks.py index c54c067f..914e9bd2 100644 --- a/lnbits/extensions/splitpayments/tasks.py +++ b/lnbits/extensions/splitpayments/tasks.py @@ -1,6 +1,8 @@ import asyncio import json +from loguru import logger + from lnbits.core import db as core_db from lnbits.core.crud import create_payment from lnbits.core.models import Payment @@ -34,7 +36,9 @@ async def on_invoice_paid(payment: Payment) -> None: amount_left = payment.amount - sum([amount for _, amount in transfers]) if amount_left < 0: - print("splitpayments failure: amount_left is negative.", payment.payment_hash) + logger.error( + "splitpayments failure: amount_left is negative.", payment.payment_hash + ) return if not targets: diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py index fb4bd0ab..6a4e5c43 100644 --- a/lnbits/extensions/tpos/views_api.py +++ b/lnbits/extensions/tpos/views_api.py @@ -4,6 +4,8 @@ from fastapi import Query from fastapi.params import Depends from starlette.exceptions import HTTPException +from loguru import logger + from lnbits.core.crud import get_user from lnbits.core.services import create_invoice from lnbits.core.views.api import api_payment @@ -88,6 +90,6 @@ async def api_tpos_check_invoice(tpos_id: str, payment_hash: str): status = await api_payment(payment_hash) except Exception as exc: - print(exc) + logger.error(exc) return {"paid": False} return status diff --git a/lnbits/extensions/withdraw/lnurl.py b/lnbits/extensions/withdraw/lnurl.py index ad3fbf5f..05b3908c 100644 --- a/lnbits/extensions/withdraw/lnurl.py +++ b/lnbits/extensions/withdraw/lnurl.py @@ -5,6 +5,8 @@ import httpx from datetime import datetime from http import HTTPStatus +from loguru import logger + import shortuuid # type: ignore from fastapi import HTTPException from fastapi.param_functions import Query @@ -136,13 +138,13 @@ async def api_lnurl_callback( ) except Exception as exc: # webhook fails shouldn't cause the lnurlw to fail since invoice is already paid - print("Caught exception when dispatching webhook url:", exc) + logger.error("Caught exception when dispatching webhook url:", exc) return {"status": "OK"} except Exception as e: await update_withdraw_link(link.id, **changesback) - print(traceback.format_exc()) + logger.error(traceback.format_exc()) return {"status": "ERROR", "reason": "Link not working"} diff --git a/lnbits/settings.py b/lnbits/settings.py index 18537b84..efa0b8d5 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -13,8 +13,8 @@ wallet_class = getattr( wallets_module, env.str("LNBITS_BACKEND_WALLET_CLASS", default="VoidWallet") ) -ENV = env.str("QUART_ENV", default="production") -DEBUG = env.bool("QUART_DEBUG", default=False) or ENV == "development" +DEBUG = env.bool("DEBUG", default=False) + HOST = env.str("HOST", default="127.0.0.1") PORT = env.int("PORT", default=5000) diff --git a/lnbits/tasks.py b/lnbits/tasks.py index c5c3279f..69aa1906 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -4,6 +4,8 @@ import traceback from http import HTTPStatus from typing import List, Callable +from loguru import logger + from fastapi.exceptions import HTTPException from lnbits.settings import WALLET @@ -37,9 +39,9 @@ async def catch_everything_and_restart(func): except asyncio.CancelledError: raise # because we must pass this up except Exception as exc: - print("caught exception in background task:", exc) - print(traceback.format_exc()) - print("will restart the task in 5 seconds.") + logger.error("caught exception in background task:", exc) + logger.error(traceback.format_exc()) + logger.error("will restart the task in 5 seconds.") await asyncio.sleep(5) await catch_everything_and_restart(func) @@ -77,7 +79,7 @@ async def internal_invoice_listener(): async def invoice_listener(): async for checking_id in WALLET.paid_invoices_stream(): - print("> got a payment notification", checking_id) + logger.info("> got a payment notification", checking_id) asyncio.create_task(invoice_callback_dispatcher(checking_id)) @@ -117,6 +119,7 @@ async def perform_balance_checks(): async def invoice_callback_dispatcher(checking_id: str): payment = await get_standalone_payment(checking_id, incoming=True) if payment and payment.is_in: + logger.trace("sending invoice callback for payment", checking_id) await payment.set_pending(False) for send_chan in invoice_listeners: await send_chan.put(payment) diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py index 4de1da8a..b836a909 100644 --- a/lnbits/utils/exchange_rates.py +++ b/lnbits/utils/exchange_rates.py @@ -1,6 +1,8 @@ import asyncio from typing import Callable, NamedTuple +from loguru import logger + import httpx currencies = { @@ -280,7 +282,7 @@ async def btc_price(currency: str) -> float: if not rates: return 9999999999 elif len(rates) == 1: - print("Warning could only fetch one Bitcoin price.") + logger.warn("Could only fetch one Bitcoin price.") return sum([rate for rate in rates]) / len(rates) diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index aa7ddc39..9ae910e9 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -5,6 +5,8 @@ import urllib.parse from os import getenv from typing import AsyncGenerator, Dict, Optional +from loguru import logger + import httpx from websockets import connect from websockets.exceptions import ( @@ -193,8 +195,8 @@ class EclairWallet(Wallet): ConnectionClosedError, ConnectionClosed, ) as ose: - print("OSE", ose) + logger.error("OSE", ose) pass - print("lost connection to eclair's websocket, retrying in 5 seconds") + logger.error("lost connection to eclair's websocket, retrying in 5 seconds") await asyncio.sleep(5) diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index 331d5285..635e7510 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -1,11 +1,12 @@ import asyncio -import json -import httpx + from os import getenv -from datetime import datetime, timedelta +from datetime import datetime from typing import Optional, Dict, AsyncGenerator import random -import string + +from loguru import logger + from lnbits.helpers import urlsafe_short_hash import hashlib from ..bolt11 import encode, decode @@ -20,7 +21,7 @@ from .base import ( class FakeWallet(Wallet): async def status(self) -> StatusResponse: - print( + logger.info( "FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr." ) return StatusResponse(None, float("inf")) diff --git a/lnbits/wallets/lnbits.py b/lnbits/wallets/lnbits.py index 414e987b..7af25298 100644 --- a/lnbits/wallets/lnbits.py +++ b/lnbits/wallets/lnbits.py @@ -4,6 +4,8 @@ import httpx from os import getenv from typing import Optional, Dict, AsyncGenerator +from loguru import logger + from .base import ( StatusResponse, InvoiceResponse, @@ -144,5 +146,7 @@ class LNbitsWallet(Wallet): except (OSError, httpx.ReadError, httpx.ConnectError, httpx.ReadTimeout): pass - print("lost connection to lnbits /payments/sse, retrying in 5 seconds") + logger.error( + "lost connection to lnbits /payments/sse, retrying in 5 seconds" + ) await asyncio.sleep(5) diff --git a/lnbits/wallets/lndgrpc.py b/lnbits/wallets/lndgrpc.py index f9a0496a..137d4e33 100644 --- a/lnbits/wallets/lndgrpc.py +++ b/lnbits/wallets/lndgrpc.py @@ -11,6 +11,9 @@ import base64 import hashlib from os import environ, error, getenv from typing import Optional, Dict, AsyncGenerator + +from loguru import logger + from .macaroon import load_macaroon, AESCipher if imports_ok: @@ -187,6 +190,8 @@ class LndWallet(Wallet): checking_id = stringify_checking_id(i.r_hash) yield checking_id except error: - print(error) + logger.error(error) - print("lost connection to lnd InvoiceSubscription, please restart lnbits.") + logger.error( + "lost connection to lnd InvoiceSubscription, please restart lnbits." + ) diff --git a/lnbits/wallets/lndrest.py b/lnbits/wallets/lndrest.py index a107f749..3cf5d5b7 100644 --- a/lnbits/wallets/lndrest.py +++ b/lnbits/wallets/lndrest.py @@ -6,6 +6,8 @@ import base64 from os import getenv from typing import Optional, Dict, AsyncGenerator +from loguru import logger + from lnbits import bolt11 as lnbits_bolt11 from .macaroon import load_macaroon, AESCipher @@ -191,5 +193,7 @@ class LndRestWallet(Wallet): except (OSError, httpx.ConnectError, httpx.ReadError): pass - print("lost connection to lnd invoices stream, retrying in 5 seconds") + logger.error( + "lost connection to lnd invoices stream, retrying in 5 seconds" + ) await asyncio.sleep(5) diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py index 88e6a3a2..7089241e 100644 --- a/lnbits/wallets/lnpay.py +++ b/lnbits/wallets/lnpay.py @@ -6,6 +6,8 @@ from os import getenv from http import HTTPStatus from typing import Optional, Dict, AsyncGenerator +from loguru import logger + from .base import ( StatusResponse, InvoiceResponse, @@ -127,7 +129,7 @@ class LNPayWallet(Wallet): try: data = json.loads(text) except json.decoder.JSONDecodeError: - print(f"got something wrong on lnpay webhook endpoint: {text[:200]}") + logger.error(f"got something wrong on lnpay webhook endpoint: {text[:200]}") data = None if ( type(data) is not dict diff --git a/lnbits/wallets/lntxbot.py b/lnbits/wallets/lntxbot.py index bbd87a73..3ec571ec 100644 --- a/lnbits/wallets/lntxbot.py +++ b/lnbits/wallets/lntxbot.py @@ -4,6 +4,8 @@ import httpx from os import getenv from typing import Optional, Dict, AsyncGenerator +from loguru import logger + from .base import ( StatusResponse, InvoiceResponse, @@ -143,5 +145,7 @@ class LntxbotWallet(Wallet): except (OSError, httpx.ReadError, httpx.ReadTimeout, httpx.ConnectError): pass - print("lost connection to lntxbot /payments/stream, retrying in 5 seconds") + logger.error( + "lost connection to lntxbot /payments/stream, retrying in 5 seconds" + ) await asyncio.sleep(5) diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py index 3548e9e2..5e62da0c 100644 --- a/lnbits/wallets/macaroon/macaroon.py +++ b/lnbits/wallets/macaroon/macaroon.py @@ -4,6 +4,8 @@ import base64 from hashlib import md5 import getpass +from loguru import logger + BLOCK_SIZE = 16 import getpass @@ -103,5 +105,5 @@ if __name__ == "__main__": macaroon = input("Enter macaroon: ") macaroon = load_macaroon(macaroon) macaroon = AESCipher(description="encryption").encrypt(macaroon.encode()) - print("Encrypted macaroon:") - print(macaroon) + logger.info("Encrypted macaroon:") + logger.info(macaroon) diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py index 0ac205e2..f1008e62 100644 --- a/lnbits/wallets/opennode.py +++ b/lnbits/wallets/opennode.py @@ -8,6 +8,8 @@ from http import HTTPStatus from os import getenv from typing import Optional, AsyncGenerator +from loguru import logger + from .base import ( StatusResponse, InvoiceResponse, @@ -139,7 +141,7 @@ class OpenNodeWallet(Wallet): x = hmac.new(self.auth["Authorization"].encode("ascii"), digestmod="sha256") x.update(charge_id.encode("ascii")) if x.hexdigest() != data["hashed_order"]: - print("invalid webhook, not from opennode") + logger.error("invalid webhook, not from opennode") raise HTTPException(status_code=HTTPStatus.NO_CONTENT) await self.queue.put(charge_id) diff --git a/lnbits/wallets/spark.py b/lnbits/wallets/spark.py index 7b9a23e2..49892a82 100644 --- a/lnbits/wallets/spark.py +++ b/lnbits/wallets/spark.py @@ -5,6 +5,8 @@ import random from os import getenv from typing import Optional, AsyncGenerator +from loguru import logger + from .base import ( StatusResponse, InvoiceResponse, @@ -204,5 +206,5 @@ class SparkWallet(Wallet): except (OSError, httpx.ReadError, httpx.ConnectError, httpx.ReadTimeout): pass - print("lost connection to spark /stream, retrying in 5 seconds") + logger.error("lost connection to spark /stream, retrying in 5 seconds") await asyncio.sleep(5) diff --git a/lnbits/wallets/void.py b/lnbits/wallets/void.py index c5cc08b5..1139f7a8 100644 --- a/lnbits/wallets/void.py +++ b/lnbits/wallets/void.py @@ -1,5 +1,7 @@ from typing import AsyncGenerator, Optional +from loguru import logger + from .base import ( InvoiceResponse, PaymentResponse, @@ -20,7 +22,7 @@ class VoidWallet(Wallet): raise Unsupported("") async def status(self) -> StatusResponse: - print( + logger.info( "This backend does nothing, it is here just as a placeholder, you must configure an actual backend before being able to do anything useful with LNbits." ) return StatusResponse(None, 0) diff --git a/requirements.txt b/requirements.txt index 4062bc25..a31e8070 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,6 +21,7 @@ idna==3.2 importlib-metadata==4.8.1 jinja2==3.0.1 lnurl==0.3.6 +loguru==0.6.0 markupsafe==2.0.1 marshmallow==3.13.0 outcome==1.1.0