Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Prometheus metrics #110

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 47 additions & 139 deletions pdm.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies = [
"structlog>=23.1.0",
"asgi-correlation-id>=4.2.0",
"sentry-sdk[fastapi]>=1.25.1",
"prometheus-client>=0.20.0",
"structlog-sentry>=2.0.3",
"requests>=2.30.0",
"httpx>=0.24.1",
Expand Down
11 changes: 11 additions & 0 deletions src/mainframe/endpoints/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
from mainframe.database import get_db
from mainframe.dependencies import get_pypi_client, validate_token
from mainframe.json_web_token import AuthenticationData
from mainframe.metrics import (
package_ingest_counter,
package_scan_fail_counter,
package_scan_success_counter,
package_scanned_counter,
)
from mainframe.models.orm import DownloadURL, Rule, Scan, Status
from mainframe.models.schemas import (
Error,
Expand Down Expand Up @@ -42,6 +48,7 @@ def submit_results(
name = result.name
version = result.version

package_scanned_counter.inc()
scan = session.scalar(
select(Scan).where(Scan.name == name).where(Scan.version == version).options(joinedload(Scan.rules))
)
Expand All @@ -65,6 +72,7 @@ def submit_results(
if isinstance(result, PackageScanResultFail):
scan.status = Status.FAILED
scan.fail_reason = result.reason
package_scan_fail_counter.inc()

session.commit()
return
Expand Down Expand Up @@ -101,6 +109,7 @@ def submit_results(
tag="scan_submitted",
)

package_scan_success_counter.inc()
session.commit()


Expand Down Expand Up @@ -221,6 +230,7 @@ def batch_queue_package(
],
)

package_ingest_counter.inc()
session.add(scan)

session.commit()
Expand Down Expand Up @@ -281,6 +291,7 @@ def queue_package(

try:
session.commit()
package_ingest_counter.inc()
except IntegrityError:
log.warn(f"Package {name}@{version} already queued for scanning.", tag="already_queued")
raise HTTPException(409, f"Package {name}@{version} is already queued for scanning")
Expand Down
2 changes: 2 additions & 0 deletions src/mainframe/endpoints/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from mainframe.database import get_db
from mainframe.dependencies import get_pypi_client, validate_token
from mainframe.json_web_token import AuthenticationData
from mainframe.metrics import package_scan_report_counter
from mainframe.models.orm import Scan
from mainframe.models.schemas import (
EmailReport,
Expand Down Expand Up @@ -248,4 +249,5 @@ def report_package(

scan.reported_by = auth.subject
scan.reported_at = dt.datetime.now(dt.timezone.utc)
package_scan_report_counter.inc()
session.commit()
12 changes: 12 additions & 0 deletions src/mainframe/metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from prometheus_client import Counter, Histogram, make_asgi_app # type: ignore

package_ingest_counter = Counter("packages_ingested", "Total amount of packages ingested so far")
package_scanned_counter = Counter("packages_scanned", "Total amount of packages scanned")
package_scan_success_counter = Counter("package_scan_success", "Total amount of packages successfully scanned")
package_scan_fail_counter = Counter("package_scan_fail", "Total amount of packages that failed while scanning")
package_scan_report_counter = Counter("package_scan_report", "Total amount of packages that have been reported")

request_time = Histogram("request_time", "Time taken to serve a request")
request_counter = Counter("requests", "Total amount of requests served")

metrics_app = make_asgi_app() # type: ignore
5 changes: 5 additions & 0 deletions src/mainframe/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from mainframe.constants import GIT_SHA, Sentry
from mainframe.dependencies import validate_token, validate_token_override
from mainframe.endpoints import routers
from mainframe.metrics import metrics_app, request_counter, request_time # type: ignore
from mainframe.models.schemas import ServerMetadata
from mainframe.rules import Rules, fetch_rules

Expand Down Expand Up @@ -119,6 +120,8 @@ async def lifespan(app_: FastAPI):
version=__version__,
)

app.mount("/metrics", metrics_app) # type: ignore

if GIT_SHA in ("development", "testing"):
app.dependency_overrides[validate_token] = validate_token_override

Expand Down Expand Up @@ -148,6 +151,8 @@ async def logging_middleware(request: Request, call_next: Callable[[Request], Aw
raise
finally:
process_time = time.perf_counter_ns() - start_time
request_time.observe(process_time / 1_000_000_000)
request_counter.inc()
status_code = response.status_code
http_method = request.method
http_version = request.scope["http_version"]
Expand Down