1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
|
from __future__ import annotations
from typing import Any, TYPE_CHECKING
from .logging import Logger
if TYPE_CHECKING:
from .config import Config
from .typing import ResponseSummary, WWWScope
METRIC_VAR = "metric"
VALUE_VAR = "value"
MTYPE_VAR = "mtype"
GAUGE_TYPE = "gauge"
COUNTER_TYPE = "counter"
HISTOGRAM_TYPE = "histogram"
class StatsdLogger(Logger):
def __init__(self, config: "Config") -> None:
super().__init__(config)
self.dogstatsd_tags = config.dogstatsd_tags
self.prefix = config.statsd_prefix
if len(self.prefix) and self.prefix[-1] != ".":
self.prefix += "."
async def critical(self, message: str, *args: Any, **kwargs: Any) -> None:
await super().critical(message, *args, **kwargs)
await self.increment("hypercorn.log.critical", 1)
async def error(self, message: str, *args: Any, **kwargs: Any) -> None:
await super().error(message, *args, **kwargs)
await self.increment("hypercorn.log.error", 1)
async def warning(self, message: str, *args: Any, **kwargs: Any) -> None:
await super().warning(message, *args, **kwargs)
await self.increment("hypercorn.log.warning", 1)
async def info(self, message: str, *args: Any, **kwargs: Any) -> None:
await super().info(message, *args, **kwargs)
async def debug(self, message: str, *args: Any, **kwargs: Any) -> None:
await super().debug(message, *args, **kwargs)
async def exception(self, message: str, *args: Any, **kwargs: Any) -> None:
await super().exception(message, *args, **kwargs)
await self.increment("hypercorn.log.exception", 1)
async def log(self, level: int, message: str, *args: Any, **kwargs: Any) -> None:
try:
extra = kwargs.get("extra", None)
if extra is not None:
metric = extra.get(METRIC_VAR, None)
value = extra.get(VALUE_VAR, None)
type_ = extra.get(MTYPE_VAR, None)
if metric and value and type_:
if type_ == GAUGE_TYPE:
await self.gauge(metric, value)
elif type_ == COUNTER_TYPE:
await self.increment(metric, value)
elif type_ == HISTOGRAM_TYPE:
await self.histogram(metric, value)
if message:
await super().log(level, message, *args, **kwargs)
except Exception:
await super().warning("Failed to log to statsd", exc_info=True)
async def access(
self, request: "WWWScope", response: "ResponseSummary", request_time: float
) -> None:
await super().access(request, response, request_time)
await self.histogram("hypercorn.request.duration", request_time * 1_000)
await self.increment("hypercorn.requests", 1)
await self.increment(f"hypercorn.request.status.{response['status']}", 1)
async def gauge(self, name: str, value: int) -> None:
await self._send(f"{self.prefix}{name}:{value}|g")
async def increment(self, name: str, value: int, sampling_rate: float = 1.0) -> None:
await self._send(f"{self.prefix}{name}:{value}|c|@{sampling_rate}")
async def decrement(self, name: str, value: int, sampling_rate: float = 1.0) -> None:
await self._send(f"{self.prefix}{name}:-{value}|c|@{sampling_rate}")
async def histogram(self, name: str, value: float) -> None:
await self._send(f"{self.prefix}{name}:{value}|ms")
async def _send(self, message: str) -> None:
if self.dogstatsd_tags:
message = f"{message}|#{self.dogstatsd_tags}"
await self._socket_send(message.encode("ascii"))
async def _socket_send(self, message: bytes) -> None:
raise NotImplementedError()
|