Skip to content

Commit b583483

Browse files
feat(medcat-service): Add optional metrics instrumentation with prometheus (#207)
1 parent d7c7d0a commit b583483

File tree

3 files changed

+29
-15
lines changed

3 files changed

+29
-15
lines changed

medcat-service/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ The following environment variables are available for tailoring the MedCAT Servi
328328
- `APP_MODEL_META_PATH_LIST` - the list of paths to meta-annotation models, each separated by `:` character (optional),
329329
- `APP_BULK_NPROC` - the number of threads used in bulk processing (default: `8`),
330330
- `APP_MEDCAT_MODEL_PACK` - MedCAT Model Pack path, if this parameter has a value IT WILL BE LOADED FIRST OVER EVERYTHING ELSE (CDB, Vocab, MetaCATs, etc.) declared above.
331+
- `APP_ENABLE_METRICS` - Enable prometheus metrics collection served on the path /metrics
331332

332333
### Shared Memory (`DOCKER_SHM_SIZE`)
333334

medcat-service/medcat_service/config.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from typing import Any, Optional, Tuple, Union
2+
from typing import Any
33

44
import torch
55
from pydantic import AliasChoices, Field, field_validator
@@ -19,13 +19,19 @@ def _coerce_loglevel(v: Any) -> int:
1919
return logging.INFO
2020

2121

22-
class Settings(BaseSettings):
22+
class ObservabilitySettings(BaseSettings):
23+
model_config = SettingsConfigDict(frozen=True, env_prefix="APP_")
24+
25+
enable_metrics: bool = Field(
26+
default=False, description="Enable prometheus metrics collection served on the path /metrics")
27+
2328

29+
class Settings(BaseSettings):
2430
model_config = SettingsConfigDict(
2531
frozen=True,
2632
env_prefix="", # no prefix; we specify full env names via alias
2733
case_sensitive=False,
28-
populate_by_name=True
34+
populate_by_name=True,
2935
)
3036

3137
app_root_path: str = Field(
@@ -34,23 +40,22 @@ class Settings(BaseSettings):
3440
examples=["/medcat-service"],
3541
)
3642

37-
deid_mode: bool = Field(default=False,
38-
validation_alias=AliasChoices("deid_mode", "MEDCAT_DEID_MODE"),
39-
description="Enable DEID mode"
40-
)
43+
deid_mode: bool = Field(
44+
default=False, validation_alias=AliasChoices("deid_mode", "MEDCAT_DEID_MODE"), description="Enable DEID mode"
45+
)
4146
deid_redact: bool = Field(
4247
default=True,
4348
validation_alias=AliasChoices("deid_redact", "MEDCAT_DEID_REDACT"),
44-
description="Enable DEID redaction. Returns text like [***] instead of [ANNOTATION]"
49+
description="Enable DEID redaction. Returns text like [***] instead of [ANNOTATION]",
4550
)
4651

4752
# Model paths
48-
model_cdb_path: Optional[str] = Field("/cat/models/medmen/cdb.dat", alias="APP_MODEL_CDB_PATH")
49-
model_vocab_path: Optional[str] = Field("/cat/models/medmen/vocab.dat", alias="APP_MODEL_VOCAB_PATH")
50-
model_meta_path_list: Union[str, Tuple[str, ...]] = Field(default=(), alias="APP_MODEL_META_PATH_LIST")
51-
model_rel_path_list: Union[str, Tuple[str, ...]] = Field(default=(), alias="APP_MODEL_REL_PATH_LIST")
52-
medcat_model_pack: Optional[str] = Field("", alias="APP_MEDCAT_MODEL_PACK")
53-
model_cui_filter_path: Optional[str] = Field("", alias="APP_MODEL_CUI_FILTER_PATH")
53+
model_cdb_path: str | None = Field("/cat/models/medmen/cdb.dat", alias="APP_MODEL_CDB_PATH")
54+
model_vocab_path: str | None = Field("/cat/models/medmen/vocab.dat", alias="APP_MODEL_VOCAB_PATH")
55+
model_meta_path_list: str | tuple[str, ...] = Field(default=(), alias="APP_MODEL_META_PATH_LIST")
56+
model_rel_path_list: str | tuple[str, ...] = Field(default=(), alias="APP_MODEL_REL_PATH_LIST")
57+
medcat_model_pack: str | None = Field("", alias="APP_MEDCAT_MODEL_PACK")
58+
model_cui_filter_path: str | None = Field("", alias="APP_MODEL_CUI_FILTER_PATH")
5459
spacy_model: str = Field("", alias="MEDCAT_SPACY_MODEL")
5560

5661
# ---- App logging & MedCAT logging ----
@@ -70,6 +75,8 @@ class Settings(BaseSettings):
7075
# e.g. "dict" | "list" | "json" (service currently uses "dict" default)
7176
annotations_entity_output_mode: str = Field(default="dict", alias="MEDCAT_ANNOTATIONS_ENTITY_OUTPUT_MODE")
7277

78+
observability: ObservabilitySettings = ObservabilitySettings()
79+
7380
# ---- Normalizers ---------------------------------------------------------
7481
@field_validator("app_log_level", "medcat_log_level", mode="before")
7582
@classmethod
@@ -99,7 +106,7 @@ def env_name(cls, field: str) -> str:
99106

100107
@field_validator("bulk_nproc", mode="before")
101108
def adjust_bulk_nproc(cls, num_procs: int) -> int:
102-
""" This method is used to adjust the number of processes to use for bulk processing.
109+
"""This method is used to adjust the number of processes to use for bulk processing.
103110
Set number of processes to 1 if MPS (Apple Sillicon) is available, as MPS does not support multiprocessing.
104111
105112
Args:

medcat-service/medcat_service/main.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@
3636

3737
gr.mount_gradio_app(app, io, path="/demo")
3838

39+
if settings.observability.enable_metrics:
40+
from prometheus_fastapi_instrumentator import Instrumentator
41+
42+
Instrumentator(excluded_handlers=["/api/health.*", "/metrics"],).instrument(app).expose(app, tags=["admin"])
43+
3944

4045
@app.exception_handler(HealthCheckFailedException)
4146
async def healthcheck_failed_exception_handler(request: Request, exc: HealthCheckFailedException):
@@ -45,6 +50,7 @@ async def healthcheck_failed_exception_handler(request: Request, exc: HealthChec
4550
if __name__ == "__main__":
4651
# Only run this when directly executing `python main.py` for local dev.
4752
import os
53+
4854
import uvicorn
4955

5056
uvicorn.run("medcat_service.main:app", host="0.0.0.0", port=int(os.environ.get("SERVER_PORT", 8000)))

0 commit comments

Comments
 (0)