refac
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
from opentelemetry.semconv.trace import SpanAttributes as _SpanAttributes
|
||||
|
||||
# Span Tags
|
||||
SPAN_DB_TYPE = "mysql"
|
||||
SPAN_REDIS_TYPE = "redis"
|
||||
SPAN_DURATION = "duration"
|
||||
SPAN_SQL_STR = "sql"
|
||||
SPAN_SQL_EXPLAIN = "explain"
|
||||
SPAN_ERROR_TYPE = "error"
|
||||
SPAN_DB_TYPE = 'mysql'
|
||||
SPAN_REDIS_TYPE = 'redis'
|
||||
SPAN_DURATION = 'duration'
|
||||
SPAN_SQL_STR = 'sql'
|
||||
SPAN_SQL_EXPLAIN = 'explain'
|
||||
SPAN_ERROR_TYPE = 'error'
|
||||
|
||||
|
||||
class SpanAttributes(_SpanAttributes):
|
||||
@@ -14,13 +14,13 @@ class SpanAttributes(_SpanAttributes):
|
||||
Span Attributes
|
||||
"""
|
||||
|
||||
DB_INSTANCE = "db.instance"
|
||||
DB_TYPE = "db.type"
|
||||
DB_IP = "db.ip"
|
||||
DB_PORT = "db.port"
|
||||
ERROR_KIND = "error.kind"
|
||||
ERROR_OBJECT = "error.object"
|
||||
ERROR_MESSAGE = "error.message"
|
||||
RESULT_CODE = "result.code"
|
||||
RESULT_MESSAGE = "result.message"
|
||||
RESULT_ERRORS = "result.errors"
|
||||
DB_INSTANCE = 'db.instance'
|
||||
DB_TYPE = 'db.type'
|
||||
DB_IP = 'db.ip'
|
||||
DB_PORT = 'db.port'
|
||||
ERROR_KIND = 'error.kind'
|
||||
ERROR_OBJECT = 'error.object'
|
||||
ERROR_MESSAGE = 'error.message'
|
||||
RESULT_CODE = 'result.code'
|
||||
RESULT_MESSAGE = 'result.message'
|
||||
RESULT_ERRORS = 'result.errors'
|
||||
|
||||
@@ -38,7 +38,7 @@ def requests_hook(span: Span, request: PreparedRequest):
|
||||
Http Request Hook
|
||||
"""
|
||||
|
||||
span.update_name(f"{request.method} {request.url}")
|
||||
span.update_name(f'{request.method} {request.url}')
|
||||
span.set_attributes(
|
||||
attributes={
|
||||
SpanAttributes.HTTP_URL: request.url,
|
||||
@@ -70,8 +70,8 @@ def redis_request_hook(span: Span, instance: Union[Redis | RedisCluster], args,
|
||||
# - redis.cluster.RedisCluster
|
||||
# Instead of checking the type, we check if the instance has a nodes_manager attribute.
|
||||
try:
|
||||
db = ""
|
||||
if hasattr(instance, "nodes_manager"):
|
||||
db = ''
|
||||
if hasattr(instance, 'nodes_manager'):
|
||||
default_node = instance.nodes_manager.default_node
|
||||
if not default_node:
|
||||
return
|
||||
@@ -79,17 +79,17 @@ def redis_request_hook(span: Span, instance: Union[Redis | RedisCluster], args,
|
||||
port = default_node.port
|
||||
else:
|
||||
connection_kwargs: dict = instance.connection_pool.connection_kwargs
|
||||
host = connection_kwargs.get("host")
|
||||
port = connection_kwargs.get("port")
|
||||
db = connection_kwargs.get("db")
|
||||
host = connection_kwargs.get('host')
|
||||
port = connection_kwargs.get('port')
|
||||
db = connection_kwargs.get('db')
|
||||
span.set_attributes(
|
||||
{
|
||||
SpanAttributes.DB_INSTANCE: f"{host}/{db}",
|
||||
SpanAttributes.DB_NAME: f"{host}/{db}",
|
||||
SpanAttributes.DB_INSTANCE: f'{host}/{db}',
|
||||
SpanAttributes.DB_NAME: f'{host}/{db}',
|
||||
SpanAttributes.DB_TYPE: SPAN_REDIS_TYPE,
|
||||
SpanAttributes.DB_PORT: port,
|
||||
SpanAttributes.DB_IP: host,
|
||||
SpanAttributes.DB_STATEMENT: " ".join([str(i) for i in args]),
|
||||
SpanAttributes.DB_STATEMENT: ' '.join([str(i) for i in args]),
|
||||
SpanAttributes.DB_OPERATION: str(args[0]),
|
||||
}
|
||||
)
|
||||
@@ -102,7 +102,7 @@ def httpx_request_hook(span: Span, request: RequestInfo):
|
||||
HTTPX Request Hook
|
||||
"""
|
||||
|
||||
span.update_name(f"{request.method.decode()} {str(request.url)}")
|
||||
span.update_name(f'{request.method.decode()} {str(request.url)}')
|
||||
span.set_attributes(
|
||||
attributes={
|
||||
SpanAttributes.HTTP_URL: str(request.url),
|
||||
@@ -117,11 +117,7 @@ def httpx_response_hook(span: Span, request: RequestInfo, response: ResponseInfo
|
||||
"""
|
||||
|
||||
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, response.status_code)
|
||||
span.set_status(
|
||||
StatusCode.ERROR
|
||||
if response.status_code >= status.HTTP_400_BAD_REQUEST
|
||||
else StatusCode.OK
|
||||
)
|
||||
span.set_status(StatusCode.ERROR if response.status_code >= status.HTTP_400_BAD_REQUEST else StatusCode.OK)
|
||||
|
||||
|
||||
async def httpx_async_request_hook(span: Span, request: RequestInfo):
|
||||
@@ -132,9 +128,7 @@ async def httpx_async_request_hook(span: Span, request: RequestInfo):
|
||||
httpx_request_hook(span, request)
|
||||
|
||||
|
||||
async def httpx_async_response_hook(
|
||||
span: Span, request: RequestInfo, response: ResponseInfo
|
||||
):
|
||||
async def httpx_async_response_hook(span: Span, request: RequestInfo, response: ResponseInfo):
|
||||
"""
|
||||
Async Response Hook
|
||||
"""
|
||||
@@ -147,7 +141,7 @@ def aiohttp_request_hook(span: Span, request: TraceRequestStartParams):
|
||||
Aiohttp Request Hook
|
||||
"""
|
||||
|
||||
span.update_name(f"{request.method} {str(request.url)}")
|
||||
span.update_name(f'{request.method} {str(request.url)}')
|
||||
span.set_attributes(
|
||||
attributes={
|
||||
SpanAttributes.HTTP_URL: str(request.url),
|
||||
@@ -156,20 +150,14 @@ def aiohttp_request_hook(span: Span, request: TraceRequestStartParams):
|
||||
)
|
||||
|
||||
|
||||
def aiohttp_response_hook(
|
||||
span: Span, response: Union[TraceRequestExceptionParams, TraceRequestEndParams]
|
||||
):
|
||||
def aiohttp_response_hook(span: Span, response: Union[TraceRequestExceptionParams, TraceRequestEndParams]):
|
||||
"""
|
||||
Aiohttp Response Hook
|
||||
"""
|
||||
|
||||
if isinstance(response, TraceRequestEndParams):
|
||||
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, response.response.status)
|
||||
span.set_status(
|
||||
StatusCode.ERROR
|
||||
if response.response.status >= status.HTTP_400_BAD_REQUEST
|
||||
else StatusCode.OK
|
||||
)
|
||||
span.set_status(StatusCode.ERROR if response.response.status >= status.HTTP_400_BAD_REQUEST else StatusCode.OK)
|
||||
elif isinstance(response, TraceRequestExceptionParams):
|
||||
span.set_status(StatusCode.ERROR)
|
||||
span.set_attribute(SpanAttributes.ERROR_MESSAGE, str(response.exception))
|
||||
@@ -191,9 +179,7 @@ class Instrumentor(BaseInstrumentor):
|
||||
instrument_fastapi(app=self.app)
|
||||
SQLAlchemyInstrumentor().instrument(engine=self.db_engine)
|
||||
RedisInstrumentor().instrument(request_hook=redis_request_hook)
|
||||
RequestsInstrumentor().instrument(
|
||||
request_hook=requests_hook, response_hook=response_hook
|
||||
)
|
||||
RequestsInstrumentor().instrument(request_hook=requests_hook, response_hook=response_hook)
|
||||
LoggingInstrumentor().instrument()
|
||||
HTTPXClientInstrumentor().instrument(
|
||||
request_hook=httpx_request_hook,
|
||||
@@ -208,7 +194,7 @@ class Instrumentor(BaseInstrumentor):
|
||||
SystemMetricsInstrumentor().instrument()
|
||||
|
||||
def _uninstrument(self, **kwargs):
|
||||
if getattr(self, "instrumentors", None) is None:
|
||||
if getattr(self, 'instrumentors', None) is None:
|
||||
return
|
||||
for instrumentor in self.instrumentors:
|
||||
instrumentor.uninstrument()
|
||||
|
||||
@@ -24,12 +24,12 @@ from open_webui.env import (
|
||||
def setup_logging():
|
||||
headers = []
|
||||
if OTEL_LOGS_BASIC_AUTH_USERNAME and OTEL_LOGS_BASIC_AUTH_PASSWORD:
|
||||
auth_string = f"{OTEL_LOGS_BASIC_AUTH_USERNAME}:{OTEL_LOGS_BASIC_AUTH_PASSWORD}"
|
||||
auth_string = f'{OTEL_LOGS_BASIC_AUTH_USERNAME}:{OTEL_LOGS_BASIC_AUTH_PASSWORD}'
|
||||
auth_header = b64encode(auth_string.encode()).decode()
|
||||
headers = [("authorization", f"Basic {auth_header}")]
|
||||
headers = [('authorization', f'Basic {auth_header}')]
|
||||
resource = Resource.create(attributes={SERVICE_NAME: OTEL_SERVICE_NAME})
|
||||
|
||||
if OTEL_LOGS_OTLP_SPAN_EXPORTER == "http":
|
||||
if OTEL_LOGS_OTLP_SPAN_EXPORTER == 'http':
|
||||
exporter = HttpOTLPLogExporter(
|
||||
endpoint=OTEL_LOGS_EXPORTER_OTLP_ENDPOINT,
|
||||
headers=headers,
|
||||
|
||||
@@ -53,19 +53,15 @@ def _build_meter_provider(resource: Resource) -> MeterProvider:
|
||||
"""Return a configured MeterProvider."""
|
||||
headers = []
|
||||
if OTEL_METRICS_BASIC_AUTH_USERNAME and OTEL_METRICS_BASIC_AUTH_PASSWORD:
|
||||
auth_string = (
|
||||
f"{OTEL_METRICS_BASIC_AUTH_USERNAME}:{OTEL_METRICS_BASIC_AUTH_PASSWORD}"
|
||||
)
|
||||
auth_string = f'{OTEL_METRICS_BASIC_AUTH_USERNAME}:{OTEL_METRICS_BASIC_AUTH_PASSWORD}'
|
||||
auth_header = b64encode(auth_string.encode()).decode()
|
||||
headers = [("authorization", f"Basic {auth_header}")]
|
||||
headers = [('authorization', f'Basic {auth_header}')]
|
||||
|
||||
# Periodic reader pushes metrics over OTLP/gRPC to collector
|
||||
if OTEL_METRICS_OTLP_SPAN_EXPORTER == "http":
|
||||
if OTEL_METRICS_OTLP_SPAN_EXPORTER == 'http':
|
||||
readers: List[PeriodicExportingMetricReader] = [
|
||||
PeriodicExportingMetricReader(
|
||||
OTLPHttpMetricExporter(
|
||||
endpoint=OTEL_METRICS_EXPORTER_OTLP_ENDPOINT, headers=headers
|
||||
),
|
||||
OTLPHttpMetricExporter(endpoint=OTEL_METRICS_EXPORTER_OTLP_ENDPOINT, headers=headers),
|
||||
export_interval_millis=OTEL_METRICS_EXPORT_INTERVAL_MILLIS,
|
||||
)
|
||||
]
|
||||
@@ -84,21 +80,21 @@ def _build_meter_provider(resource: Resource) -> MeterProvider:
|
||||
# Optional view to limit cardinality: drop user-agent etc.
|
||||
views: List[View] = [
|
||||
View(
|
||||
instrument_name="http.server.duration",
|
||||
attribute_keys=["http.method", "http.route", "http.status_code"],
|
||||
instrument_name='http.server.duration',
|
||||
attribute_keys=['http.method', 'http.route', 'http.status_code'],
|
||||
),
|
||||
View(
|
||||
instrument_name="http.server.requests",
|
||||
attribute_keys=["http.method", "http.route", "http.status_code"],
|
||||
instrument_name='http.server.requests',
|
||||
attribute_keys=['http.method', 'http.route', 'http.status_code'],
|
||||
),
|
||||
View(
|
||||
instrument_name="webui.users.total",
|
||||
instrument_name='webui.users.total',
|
||||
),
|
||||
View(
|
||||
instrument_name="webui.users.active",
|
||||
instrument_name='webui.users.active',
|
||||
),
|
||||
View(
|
||||
instrument_name="webui.users.active.today",
|
||||
instrument_name='webui.users.active.today',
|
||||
),
|
||||
]
|
||||
|
||||
@@ -118,14 +114,14 @@ def setup_metrics(app: FastAPI, resource: Resource) -> None:
|
||||
|
||||
# Instruments
|
||||
request_counter = meter.create_counter(
|
||||
name="http.server.requests",
|
||||
description="Counts the total number of inbound HTTP requests.",
|
||||
unit="1",
|
||||
name='http.server.requests',
|
||||
description='Counts the total number of inbound HTTP requests.',
|
||||
unit='1',
|
||||
)
|
||||
duration_histogram = meter.create_histogram(
|
||||
name="http.server.duration",
|
||||
description="Measures the duration of inbound HTTP requests.",
|
||||
unit="ms",
|
||||
name='http.server.duration',
|
||||
description='Measures the duration of inbound HTTP requests.',
|
||||
unit='ms',
|
||||
)
|
||||
|
||||
def observe_active_users(
|
||||
@@ -150,16 +146,16 @@ def setup_metrics(app: FastAPI, resource: Resource) -> None:
|
||||
]
|
||||
|
||||
meter.create_observable_gauge(
|
||||
name="webui.users.total",
|
||||
description="Total number of registered users",
|
||||
unit="users",
|
||||
name='webui.users.total',
|
||||
description='Total number of registered users',
|
||||
unit='users',
|
||||
callbacks=[observe_total_registered_users],
|
||||
)
|
||||
|
||||
meter.create_observable_gauge(
|
||||
name="webui.users.active",
|
||||
description="Number of currently active users",
|
||||
unit="users",
|
||||
name='webui.users.active',
|
||||
description='Number of currently active users',
|
||||
unit='users',
|
||||
callbacks=[observe_active_users],
|
||||
)
|
||||
|
||||
@@ -169,21 +165,21 @@ def setup_metrics(app: FastAPI, resource: Resource) -> None:
|
||||
return [metrics.Observation(value=Users.get_num_users_active_today())]
|
||||
|
||||
meter.create_observable_gauge(
|
||||
name="webui.users.active.today",
|
||||
description="Number of users active since midnight today",
|
||||
unit="users",
|
||||
name='webui.users.active.today',
|
||||
description='Number of users active since midnight today',
|
||||
unit='users',
|
||||
callbacks=[observe_users_active_today],
|
||||
)
|
||||
|
||||
# FastAPI middleware
|
||||
@app.middleware("http")
|
||||
@app.middleware('http')
|
||||
async def _metrics_middleware(request: Request, call_next):
|
||||
start_time = time.perf_counter()
|
||||
|
||||
status_code = None
|
||||
try:
|
||||
response = await call_next(request)
|
||||
status_code = getattr(response, "status_code", 500)
|
||||
status_code = getattr(response, 'status_code', 500)
|
||||
return response
|
||||
except Exception:
|
||||
status_code = 500
|
||||
@@ -192,13 +188,13 @@ def setup_metrics(app: FastAPI, resource: Resource) -> None:
|
||||
elapsed_ms = (time.perf_counter() - start_time) * 1000.0
|
||||
|
||||
# Route template e.g. "/items/{item_id}" instead of real path.
|
||||
route = request.scope.get("route")
|
||||
route_path = getattr(route, "path", request.url.path)
|
||||
route = request.scope.get('route')
|
||||
route_path = getattr(route, 'path', request.url.path)
|
||||
|
||||
attrs: Dict[str, str | int] = {
|
||||
"http.method": request.method,
|
||||
"http.route": route_path,
|
||||
"http.status_code": status_code,
|
||||
'http.method': request.method,
|
||||
'http.route': route_path,
|
||||
'http.status_code': status_code,
|
||||
}
|
||||
|
||||
request_counter.add(1, attrs)
|
||||
|
||||
@@ -34,12 +34,12 @@ def setup(app: FastAPI, db_engine: Engine):
|
||||
# Add basic auth header only if both username and password are not empty
|
||||
headers = []
|
||||
if OTEL_BASIC_AUTH_USERNAME and OTEL_BASIC_AUTH_PASSWORD:
|
||||
auth_string = f"{OTEL_BASIC_AUTH_USERNAME}:{OTEL_BASIC_AUTH_PASSWORD}"
|
||||
auth_string = f'{OTEL_BASIC_AUTH_USERNAME}:{OTEL_BASIC_AUTH_PASSWORD}'
|
||||
auth_header = b64encode(auth_string.encode()).decode()
|
||||
headers = [("authorization", f"Basic {auth_header}")]
|
||||
headers = [('authorization', f'Basic {auth_header}')]
|
||||
|
||||
# otlp export
|
||||
if OTEL_OTLP_SPAN_EXPORTER == "http":
|
||||
if OTEL_OTLP_SPAN_EXPORTER == 'http':
|
||||
exporter = HttpOTLPSpanExporter(
|
||||
endpoint=OTEL_EXPORTER_OTLP_ENDPOINT,
|
||||
headers=headers,
|
||||
|
||||
Reference in New Issue
Block a user