Metrics: collect application metrics via vector #246
89
blender_extensions/middleware.py
Normal file
89
blender_extensions/middleware.py
Normal file
@ -0,0 +1,89 @@
|
||||
from contextlib import ExitStack
|
||||
import json
|
||||
import socket
|
||||
import time
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import connections
|
||||
|
||||
|
||||
class VectorClient:
|
||||
def __init__(self):
|
||||
self._socket = None
|
||||
self.hostname = socket.gethostname()
|
||||
|
||||
@property
|
||||
def socket(self):
|
||||
if not self._socket:
|
||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
return self._socket
|
||||
|
||||
def send(self, data):
|
||||
self.socket.sendto(
|
||||
bytes(json.dumps(data), "utf-8"),
|
||||
("127.0.0.1", settings.VECTOR_UDP_PORT),
|
||||
)
|
||||
|
||||
|
||||
vector_client = VectorClient()
|
||||
|
||||
|
||||
class QueryLogger:
|
||||
def __init__(self, alias):
|
||||
self.alias = alias
|
||||
self.durations = []
|
||||
|
||||
def __call__(self, execute, sql, params, many, context):
|
||||
start = time.perf_counter()
|
||||
try:
|
||||
return execute(sql, params, many, context)
|
||||
finally:
|
||||
duration = time.perf_counter() - start
|
||||
self.durations.append(duration)
|
||||
|
||||
|
||||
def metrics_middleware(get_response):
|
||||
"""All timings reported as integer milliseconds."""
|
||||
|
||||
def middleware(request):
|
||||
# pre-request setup
|
||||
start = time.perf_counter()
|
||||
context_managers = []
|
||||
query_loggers = []
|
||||
for connection in connections.all():
|
||||
query_logger = QueryLogger(connection.alias)
|
||||
query_loggers.append(query_logger)
|
||||
context_managers.append(connection.execute_wrapper(query_logger))
|
||||
|
||||
# request processing
|
||||
with ExitStack() as stack:
|
||||
for context_manager in context_managers:
|
||||
stack.enter_context(context_manager)
|
||||
response = get_response(request)
|
||||
|
||||
# post-request reporting
|
||||
request_time = int(1000 * (time.perf_counter() - start))
|
||||
db_metrics = {}
|
||||
for query_logger in query_loggers:
|
||||
db_metrics[query_logger.alias] = {
|
||||
"query_count": len(query_logger.durations),
|
||||
"query_time_sum": int(1000 * sum(query_logger.durations)),
|
||||
}
|
||||
|
||||
data = {
|
||||
"db": db_metrics,
|
||||
"hostname": vector_client.hostname,
|
||||
"http": {
|
||||
"path": request.path,
|
||||
"request_time": request_time,
|
||||
"remote_addr": request.META.get("REMOTE_ADDR"),
|
||||
"status_code": response.status_code,
|
||||
"user_agent": request.headers.get("user-agent"),
|
||||
},
|
||||
"service_name": settings.SERVICE_NAME,
|
||||
}
|
||||
vector_client.send(data)
|
||||
|
||||
return response
|
||||
|
||||
return middleware
|
@ -79,6 +79,7 @@ INSTALLED_APPS = [
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'blender_extensions.middleware.metrics_middleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
@ -354,3 +355,6 @@ if os.environ.get('ADMINS') is not None:
|
||||
ADMINS = [[_.strip() for _ in adm.split(':')] for adm in os.environ.get('ADMINS').split(',')]
|
||||
EMAIL_SUBJECT_PREFIX = f'[{ALLOWED_HOSTS[0]}]'
|
||||
SERVER_EMAIL = f'django@{ALLOWED_HOSTS[0]}'
|
||||
|
||||
SERVICE_NAME = os.getenv('SERVICE_NAME', 'dummy_service_name')
|
||||
VECTOR_UDP_PORT = 18125
|
||||
|
Loading…
Reference in New Issue
Block a user