From 156b8bcb781ff4216769a7fffed147e8a80d8ee8 Mon Sep 17 00:00:00 2001 From: edpyt <125578104+edpyt@users.noreply.github.com> Date: Tue, 7 May 2024 19:09:31 +0300 Subject: [PATCH] feat: add logfire (#16) --- .env | 2 + docker-compose.yml | 7 +- poetry.lock | 319 +++++++++++++++++++++- pyproject.toml | 1 + requirements/dev.txt | 1 + src/__main__.py | 5 + src/api/main.py | 17 +- src/infrastructure/di/main.py | 3 +- src/infrastructure/di/message_queue.py | 5 +- src/infrastructure/log/main.py | 5 +- src/infrastructure/message_broker/main.py | 2 +- src/infrastructure/persistence/db/main.py | 1 + tests/integration/conftest.py | 3 + 13 files changed, 357 insertions(+), 14 deletions(-) diff --git a/.env b/.env index 458d019..6bac14b 100644 --- a/.env +++ b/.env @@ -1,4 +1,6 @@ # for example +LOGFIRE_TOKEN= + POSTGRES_USER=buber_dinner POSTGRES_PASSWORD=buber_dinner POSTGRES_DB=buber_dinner diff --git a/docker-compose.yml b/docker-compose.yml index a843cf8..691723d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,12 +5,7 @@ services: context: . dockerfile: Dockerfile target: dev - command: > - uvicorn src.api.main:build_api - --factory - --reload - --host 0.0.0.0 - --port 8000 + command: python -m src volumes: - ./src:/app/src/:ro - ./config_dist:/app/config_dist:ro diff --git a/poetry.lock b/poetry.lock index 3f56d8e..3725414 100644 --- a/poetry.lock +++ b/poetry.lock @@ -288,6 +288,23 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + [[package]] name = "distlib" version = "0.3.8" @@ -366,6 +383,23 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] +[[package]] +name = "googleapis-common-protos" +version = "1.63.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, +] + +[package.dependencies] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + [[package]] name = "greenlet" version = "3.0.3" @@ -538,6 +572,25 @@ files = [ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] +[[package]] +name = "importlib-metadata" +version = "7.0.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-7.0.0-py3-none-any.whl", hash = "sha256:d97503976bb81f40a193d41ee6570868479c69d5068651eb039c40d850c59d67"}, + {file = "importlib_metadata-7.0.0.tar.gz", hash = "sha256:7fc841f8b8332803464e5dc1c63a2e59121f46ca186c0e2e182e80bf8c1319f7"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -560,6 +613,43 @@ files = [ {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, ] +[[package]] +name = "logfire" +version = "0.30.0" +description = "The best Python observability tool! 🪵🔥" +optional = false +python-versions = "*" +files = [ + {file = "logfire-0.30.0-py2.py3-none-any.whl", hash = "sha256:c83c8df8d97ee918932e3ce6e040012def3b8b184504439857e26b6d8510507e"}, + {file = "logfire-0.30.0.tar.gz", hash = "sha256:5c802bf633fa56f44f23bcdb22ea1f2b7968cc5a0c29eee7b58d91dc4a66c6e1"}, +] + +[package.dependencies] +opentelemetry-exporter-otlp-proto-http = ">=1.21.0" +opentelemetry-instrumentation = ">=0.41b0" +opentelemetry-instrumentation-asyncpg = {version = ">=0.42b0", optional = true, markers = "extra == \"asyncpg\""} +opentelemetry-sdk = ">=1.21.0" +protobuf = ">=4.23.4" +rich = ">=13.4.2" +typing-extensions = ">=4.1.0" + +[package.extras] +aiohttp = ["opentelemetry-instrumentation-aiohttp-client (>=0.42b0)"] +asyncpg = ["opentelemetry-instrumentation-asyncpg (>=0.42b0)"] +celery = ["opentelemetry-instrumentation-celery (>=0.42b0)"] +django = ["opentelemetry-instrumentation-django (>=0.42b0)"] +fastapi = ["opentelemetry-instrumentation-fastapi (>=0.42b0)"] +flask = ["opentelemetry-instrumentation-flask (>=0.42b0)"] +httpx = ["opentelemetry-instrumentation-httpx (>=0.42b0)"] +psycopg = ["opentelemetry-instrumentation-psycopg (>=0.42b0)", "packaging"] +psycopg2 = ["opentelemetry-instrumentation-psycopg2 (>=0.42b0)", "packaging"] +pymongo = ["opentelemetry-instrumentation-pymongo (>=0.42b0)"] +redis = ["opentelemetry-instrumentation-redis (>=0.42b0)"] +requests = ["opentelemetry-instrumentation-requests (>=0.42b0)"] +sqlalchemy = ["opentelemetry-instrumentation-sqlalchemy (>=0.42b0)"] +starlette = ["opentelemetry-instrumentation-starlette (>=0.42b0)"] +system-metrics = ["opentelemetry-instrumentation-system-metrics (>=0.42b0)"] + [[package]] name = "mako" version = "1.3.3" @@ -579,6 +669,30 @@ babel = ["Babel"] lingua = ["lingua"] testing = ["pytest"] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" version = "2.1.5" @@ -648,6 +762,17 @@ files = [ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mediatr" version = "1.3.2" @@ -687,6 +812,131 @@ files = [ [package.dependencies] setuptools = "*" +[[package]] +name = "opentelemetry-api" +version = "1.24.0" +description = "OpenTelemetry Python API" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_api-1.24.0-py3-none-any.whl", hash = "sha256:0f2c363d98d10d1ce93330015ca7fd3a65f60be64e05e30f557c61de52c80ca2"}, + {file = "opentelemetry_api-1.24.0.tar.gz", hash = "sha256:42719f10ce7b5a9a73b10a4baf620574fb8ad495a9cbe5c18d76b75d8689c67e"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +importlib-metadata = ">=6.0,<=7.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.24.0" +description = "OpenTelemetry Protobuf encoding" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_common-1.24.0-py3-none-any.whl", hash = "sha256:e51f2c9735054d598ad2df5d3eca830fecfb5b0bda0a2fa742c9c7718e12f641"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.24.0.tar.gz", hash = "sha256:5d31fa1ff976cacc38be1ec4e3279a3f88435c75b38b1f7a099a1faffc302461"}, +] + +[package.dependencies] +opentelemetry-proto = "1.24.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.24.0" +description = "OpenTelemetry Collector Protobuf over HTTP Exporter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_http-1.24.0-py3-none-any.whl", hash = "sha256:25af10e46fdf4cd3833175e42f4879a1255fc01655fe14c876183a2903949836"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.24.0.tar.gz", hash = "sha256:704c066cc96f5131881b75c0eac286cd73fc735c490b054838b4513254bd7850"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +googleapis-common-protos = ">=1.52,<2.0" +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.24.0" +opentelemetry-proto = "1.24.0" +opentelemetry-sdk = ">=1.24.0,<1.25.0" +requests = ">=2.7,<3.0" + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.45b0" +description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_instrumentation-0.45b0-py3-none-any.whl", hash = "sha256:06c02e2c952c1b076e8eaedf1b82f715e2937ba7eeacab55913dd434fbcec258"}, + {file = "opentelemetry_instrumentation-0.45b0.tar.gz", hash = "sha256:6c47120a7970bbeb458e6a73686ee9ba84b106329a79e4a4a66761f933709c7e"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.4,<2.0" +setuptools = ">=16.0" +wrapt = ">=1.0.0,<2.0.0" + +[[package]] +name = "opentelemetry-instrumentation-asyncpg" +version = "0.45b0" +description = "OpenTelemetry instrumentation for AsyncPG" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_instrumentation_asyncpg-0.45b0-py3-none-any.whl", hash = "sha256:a5ff319c09bfb2beeccdf4b5b2401930a3fd07a4e34aaa7eefd9e7f206507263"}, + {file = "opentelemetry_instrumentation_asyncpg-0.45b0.tar.gz", hash = "sha256:1782ea1b2b563db4ed821b1a6b7f1e2e6147256e5f3cde92cd20961378d378e9"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.12,<2.0" +opentelemetry-instrumentation = "0.45b0" +opentelemetry-semantic-conventions = "0.45b0" + +[package.extras] +instruments = ["asyncpg (>=0.12.0)"] + +[[package]] +name = "opentelemetry-proto" +version = "1.24.0" +description = "OpenTelemetry Python Proto" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_proto-1.24.0-py3-none-any.whl", hash = "sha256:bcb80e1e78a003040db71ccf83f2ad2019273d1e0828089d183b18a1476527ce"}, + {file = "opentelemetry_proto-1.24.0.tar.gz", hash = "sha256:ff551b8ad63c6cabb1845ce217a6709358dfaba0f75ea1fa21a61ceddc78cab8"}, +] + +[package.dependencies] +protobuf = ">=3.19,<5.0" + +[[package]] +name = "opentelemetry-sdk" +version = "1.24.0" +description = "OpenTelemetry Python SDK" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_sdk-1.24.0-py3-none-any.whl", hash = "sha256:fa731e24efe832e98bcd90902085b359dcfef7d9c9c00eb5b9a18587dae3eb59"}, + {file = "opentelemetry_sdk-1.24.0.tar.gz", hash = "sha256:75bc0563affffa827700e0f4f4a68e1e257db0df13372344aebc6f8a64cde2e5"}, +] + +[package.dependencies] +opentelemetry-api = "1.24.0" +opentelemetry-semantic-conventions = "0.45b0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.45b0" +description = "OpenTelemetry Semantic Conventions" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_semantic_conventions-0.45b0-py3-none-any.whl", hash = "sha256:a4a6fb9a7bacd9167c082aa4681009e9acdbfa28ffb2387af50c2fef3d30c864"}, + {file = "opentelemetry_semantic_conventions-0.45b0.tar.gz", hash = "sha256:7c84215a44ac846bc4b8e32d5e78935c5c43482e491812a0bb8aaf87e4d92118"}, +] + [[package]] name = "orjson" version = "3.10.2" @@ -802,6 +1052,26 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + [[package]] name = "pydantic" version = "2.7.1" @@ -912,6 +1182,20 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pyjwt" version = "2.8.0" @@ -1102,6 +1386,24 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "rodi" version = "2.0.6" @@ -1458,7 +1760,22 @@ files = [ {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, ] +[[package]] +name = "zipp" +version = "3.18.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "5107c32a8f0fad461ac022261e1ff4612d6eb56b9f4bed1d49789920180fb8f5" +content-hash = "1bef971205ddf671add10fdc29634d83fbf365e7b0d4040b77fbba78354f8f5c" diff --git a/pyproject.toml b/pyproject.toml index d3dc0f2..618d85e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ adaptix = "^3.0.0b3" guardpost = "^1.0.2" orjson = "^3.10.2" nats-py = "^2.7.2" +logfire = {extras = ["asyncpg"], version = "^0.30.0"} [tool.poetry.group.db.dependencies] asyncpg = "^0.29.0" diff --git a/requirements/dev.txt b/requirements/dev.txt index 0c8ec26..41991c6 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -10,4 +10,5 @@ structlog==24.1.0 ; python_version >= "3.11" and python_version < "4.0" uvicorn==0.27.1 ; python_version >= "3.11" and python_version < "4.0" nats-py==2.7.2 ; python_version >= "3.11" and python_version < "4.0" orjson==3.10.2 ; python_version >= "3.11" and python_version < "4.0" +logfire[asyncpg]==0.30.0; python_version >= "3.11" and python_version < "4.0" -r db.txt diff --git a/src/__main__.py b/src/__main__.py index 24021d1..7840105 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -1,12 +1,17 @@ import os +import logfire import uvicorn def start_application() -> None: + logfire.configure() + logfire.instrument_asyncpg() + uvicorn.run( "src.api.main:build_api", reload=bool(os.environ.get("APP_RELOAD", True)), + host="0.0.0.0", # noqa: S104 factory=True, ) diff --git a/src/api/main.py b/src/api/main.py index ee75f2f..bb2d4db 100644 --- a/src/api/main.py +++ b/src/api/main.py @@ -1,3 +1,5 @@ +from logging import Logger + from blacksheep import Application from nats import NATS @@ -14,7 +16,9 @@ def build_api() -> Application: app = Application(show_error_details=True) setup_app(app) - app.on_start += setup_di + app.on_start += on_start + + app.on_stop += on_stop app.on_stop += close_connections return app @@ -32,8 +36,15 @@ def setup_app(app: Application) -> None: app.use_authorization() -async def setup_di(app: Application) -> None: - await build_application_container(app.services) +async def on_start(app: Application) -> None: + container = await build_application_container(app.services) + logger = container.resolve(Logger) + logger.info("Start buber_dinner application") + + +async def on_stop(app: Application) -> None: + logger = app.services.resolve(Logger) + logger.info("Stop buber_dinner application.") async def close_connections(app: Application) -> None: diff --git a/src/infrastructure/di/main.py b/src/infrastructure/di/main.py index 5cdc418..a8680b5 100644 --- a/src/infrastructure/di/main.py +++ b/src/infrastructure/di/main.py @@ -1,6 +1,7 @@ import logging from functools import lru_cache +import structlog from rodi import Container from src.infrastructure.converter.retort import setup_retort @@ -18,7 +19,7 @@ async def build_application_container(container: Container | None = None) -> Con if container is None: container = Container() - container.add_instance(logging.getLogger(__name__), logging.Logger) + container.add_scoped_by_factory(lambda: structlog.get_logger(), return_type=logging.Logger) container.add_instance(setup_retort()) config = setup_config_di(container) diff --git a/src/infrastructure/di/message_queue.py b/src/infrastructure/di/message_queue.py index 6d97396..345fd4a 100644 --- a/src/infrastructure/di/message_queue.py +++ b/src/infrastructure/di/message_queue.py @@ -10,7 +10,10 @@ async def setup_message_queue_di(container: Container, config: Config) -> None: - nats_conn = await make_broker_connection(conn_url=config.broker_config.full_url) + try: + nats_conn = await make_broker_connection(conn_url=config.broker_config.full_url) + except: # noqa: E722 + nats_conn = None setup_events_di(container) diff --git a/src/infrastructure/log/main.py b/src/infrastructure/log/main.py index 2615cee..5eea190 100644 --- a/src/infrastructure/log/main.py +++ b/src/infrastructure/log/main.py @@ -1,6 +1,7 @@ import logging.config import structlog +from logfire.integrations.structlog import LogfireProcessor from structlog.processors import CallsiteParameter, CallsiteParameterAdder from .processors import get_render_processor @@ -28,6 +29,8 @@ def configure_logging() -> None: structlog.processors.UnicodeDecoder(), structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ) + logfire_processor = (LogfireProcessor(),) + logging_processors = (structlog.stdlib.ProcessorFormatter.remove_processors_meta,) logging_console_processors = ( *logging_processors, @@ -47,7 +50,7 @@ def configure_logging() -> None: logging.basicConfig(handlers=handlers, level="DEBUG") structlog.configure( - processors=common_processors + structlog_processors, + processors=logfire_processor + common_processors + structlog_processors, logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True, diff --git a/src/infrastructure/message_broker/main.py b/src/infrastructure/message_broker/main.py index 2f89be9..58d25e9 100644 --- a/src/infrastructure/message_broker/main.py +++ b/src/infrastructure/message_broker/main.py @@ -2,4 +2,4 @@ async def make_broker_connection(conn_url: str) -> nats.NATS: - return await nats.connect(conn_url) + return await nats.connect(conn_url, allow_reconnect=False, max_reconnect_attempts=1) diff --git a/src/infrastructure/persistence/db/main.py b/src/infrastructure/persistence/db/main.py index 0274ad6..ca0f249 100644 --- a/src/infrastructure/persistence/db/main.py +++ b/src/infrastructure/persistence/db/main.py @@ -17,6 +17,7 @@ def create_sa_engine(db_config: DBConfig) -> AsyncEngine: # BUG: rodi sends `ActivationScope` here if isinstance(db_config, ActivationScope): db_config = db_config.get(DBConfig) + return create_async_engine(db_config.full_url) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 355808d..6fd346f 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,5 +1,6 @@ from typing import AsyncGenerator +import logfire import pytest from blacksheep import Application from pytest_mock import MockerFixture @@ -9,6 +10,8 @@ from src.infrastructure.config.db import DBConfig from src.infrastructure.config.jwt import JWTConfig +logfire.configure(send_to_logfire=False) + @pytest.fixture(name="jwt_config", scope="session") def create_jwt_config() -> JWTConfig: