mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Incorporate feedback from PR
Use extra keyword in logger, more pythonic capture response code as a rate metric
This commit is contained in:
parent
de9b05c774
commit
89a3c96700
@ -479,9 +479,9 @@ class Arbiter(object):
|
|||||||
self.kill_worker(pid, signal.SIGQUIT)
|
self.kill_worker(pid, signal.SIGQUIT)
|
||||||
|
|
||||||
self.log.info("%d workers",
|
self.log.info("%d workers",
|
||||||
metric="gunicorn.workers",
|
extra={"metric": "gunicorn.workers",
|
||||||
value=len(self.WORKERS),
|
"value": len(self.WORKERS),
|
||||||
mtype="gauge")
|
"mtype": "gauge"})
|
||||||
|
|
||||||
def spawn_worker(self):
|
def spawn_worker(self):
|
||||||
self.worker_age += 1
|
self.worker_age += 1
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
# This file is part of gunicorn released under the MIT license.
|
# This file is part of gunicorn released under the MIT license.
|
||||||
# See the NOTICE for more information.
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
"""Bare-bones implementation of statsD's protocol, client-side
|
"Bare-bones implementation of statsD's protocol, client-side"
|
||||||
"""
|
|
||||||
import socket
|
import socket
|
||||||
from gunicorn.glogging import Logger
|
from gunicorn.glogging import Logger
|
||||||
|
|
||||||
@ -33,22 +33,18 @@ class Statsd(Logger):
|
|||||||
|
|
||||||
# Log errors and warnings
|
# Log errors and warnings
|
||||||
def critical(self, msg, *args, **kwargs):
|
def critical(self, msg, *args, **kwargs):
|
||||||
self._sanitize(kwargs)
|
|
||||||
Logger.critical(self, msg, *args, **kwargs)
|
Logger.critical(self, msg, *args, **kwargs)
|
||||||
self.increment("gunicorn.log.critical", 1)
|
self.increment("gunicorn.log.critical", 1)
|
||||||
|
|
||||||
def error(self, msg, *args, **kwargs):
|
def error(self, msg, *args, **kwargs):
|
||||||
self._sanitize(kwargs)
|
|
||||||
Logger.error(self, msg, *args, **kwargs)
|
Logger.error(self, msg, *args, **kwargs)
|
||||||
self.increment("gunicorn.log.error", 1)
|
self.increment("gunicorn.log.error", 1)
|
||||||
|
|
||||||
def warning(self, msg, *args, **kwargs):
|
def warning(self, msg, *args, **kwargs):
|
||||||
self._sanitize(kwargs)
|
|
||||||
Logger.warning(self, msg, *args, **kwargs)
|
Logger.warning(self, msg, *args, **kwargs)
|
||||||
self.increment("gunicorn.log.warning", 1)
|
self.increment("gunicorn.log.warning", 1)
|
||||||
|
|
||||||
def exception(self, msg, *args, **kwargs):
|
def exception(self, msg, *args, **kwargs):
|
||||||
self._sanitize(kwargs)
|
|
||||||
Logger.exception(self, msg, *args, **kwargs)
|
Logger.exception(self, msg, *args, **kwargs)
|
||||||
self.increment("gunicorn.log.exception", 1)
|
self.increment("gunicorn.log.exception", 1)
|
||||||
|
|
||||||
@ -56,28 +52,27 @@ class Statsd(Logger):
|
|||||||
def info(self, msg, *args, **kwargs):
|
def info(self, msg, *args, **kwargs):
|
||||||
"""Log a given statistic if metric, value and type are present
|
"""Log a given statistic if metric, value and type are present
|
||||||
"""
|
"""
|
||||||
metric = kwargs.get(METRIC_VAR, None)
|
extra = kwargs.get("extra", None)
|
||||||
value = kwargs.get(VALUE_VAR, None)
|
if extra is not None:
|
||||||
typ = kwargs.get(MTYPE_VAR, None)
|
metric = extra.get(METRIC_VAR, None)
|
||||||
if metric and value and typ:
|
value = extra.get(VALUE_VAR, None)
|
||||||
self._sanitize(kwargs)
|
typ = extra.get(MTYPE_VAR, None)
|
||||||
if typ == GAUGE_TYPE:
|
if metric and value and typ:
|
||||||
self.gauge(metric, value)
|
if typ == GAUGE_TYPE:
|
||||||
elif typ == COUNTER_TYPE:
|
self.gauge(metric, value)
|
||||||
self.increment(metric, value)
|
elif typ == COUNTER_TYPE:
|
||||||
elif typ == HISTOGRAM_TYPE:
|
self.increment(metric, value)
|
||||||
self.histogram(metric, value)
|
elif typ == HISTOGRAM_TYPE:
|
||||||
else:
|
self.histogram(metric, value)
|
||||||
pass
|
else:
|
||||||
|
pass
|
||||||
Logger.info(self, msg, *args, **kwargs)
|
Logger.info(self, msg, *args, **kwargs)
|
||||||
|
|
||||||
# skip the run-of-the-mill logs
|
# skip the run-of-the-mill logs
|
||||||
def debug(self, msg, *args, **kwargs):
|
def debug(self, msg, *args, **kwargs):
|
||||||
self._sanitize(kwargs)
|
|
||||||
Logger.debug(self, msg, *args, **kwargs)
|
Logger.debug(self, msg, *args, **kwargs)
|
||||||
|
|
||||||
def log(self, lvl, msg, *args, **kwargs):
|
def log(self, lvl, msg, *args, **kwargs):
|
||||||
self._sanitize(kwargs)
|
|
||||||
Logger.log(self, lvl, msg, *args, **kwargs)
|
Logger.log(self, lvl, msg, *args, **kwargs)
|
||||||
|
|
||||||
# access logging
|
# access logging
|
||||||
@ -89,14 +84,7 @@ class Statsd(Logger):
|
|||||||
duration_in_s = request_time.seconds + float(request_time.microseconds)/10**6
|
duration_in_s = request_time.seconds + float(request_time.microseconds)/10**6
|
||||||
self.histogram("gunicorn.request.duration", duration_in_s)
|
self.histogram("gunicorn.request.duration", duration_in_s)
|
||||||
self.increment("gunicorn.requests", 1)
|
self.increment("gunicorn.requests", 1)
|
||||||
|
self.increment("gunicorn.request.status.%d" % int(resp.status), 1)
|
||||||
def _sanitize(self, kwargs):
|
|
||||||
"""Drop stasd keywords from the logger"""
|
|
||||||
for k in (METRIC_VAR, VALUE_VAR, MTYPE_VAR):
|
|
||||||
try:
|
|
||||||
kwargs.pop(k)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# statsD methods
|
# statsD methods
|
||||||
# you can use those directly if you want
|
# you can use those directly if you want
|
||||||
|
|||||||
54
tests/test_009-statsd.py
Normal file
54
tests/test_009-statsd.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
import t
|
||||||
|
|
||||||
|
from gunicorn.config import Config
|
||||||
|
from gunicorn.instrument.statsd import Statsd
|
||||||
|
|
||||||
|
class TestException(Exception): pass
|
||||||
|
|
||||||
|
class MockSocket(object):
|
||||||
|
"Pretend to be a UDP socket"
|
||||||
|
def __init__(self, failp):
|
||||||
|
self.failp = failp
|
||||||
|
self.msgs = [] # accumulate messages for later inspection
|
||||||
|
|
||||||
|
def send(self, msg):
|
||||||
|
if self.failp:
|
||||||
|
raise TestException("Should not interrupt the logger")
|
||||||
|
self.msgs.append(msg)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.msgs = []
|
||||||
|
|
||||||
|
class MockResponse(object):
|
||||||
|
def __init__(self, status):
|
||||||
|
self.status = status
|
||||||
|
|
||||||
|
def test_statsd_fail():
|
||||||
|
"UDP socket fails"
|
||||||
|
logger = Statsd(Config())
|
||||||
|
logger.sock = MockSocket(True)
|
||||||
|
logger.info("No impact on logging")
|
||||||
|
logger.debug("No impact on logging")
|
||||||
|
logger.critical("No impact on logging")
|
||||||
|
logger.error("No impact on logging")
|
||||||
|
logger.warning("No impact on logging")
|
||||||
|
logger.exception("No impact on logging")
|
||||||
|
|
||||||
|
def test_instrument():
|
||||||
|
logger = Statsd(Config())
|
||||||
|
logger.sock = MockSocket(False)
|
||||||
|
|
||||||
|
logger.info("Blah", extra={"mtype": "gauge", "metric": "gunicorn.test", "value": 666})
|
||||||
|
t.eq(logger.sock.msgs[0], "gunicorn.test:666|g")
|
||||||
|
logger.sock.reset()
|
||||||
|
|
||||||
|
logger.critical("Boom")
|
||||||
|
t.eq(logger.sock.msgs[0], "gunicorn.log.critical:1|c|@1.0")
|
||||||
|
logger.sock.reset()
|
||||||
|
|
||||||
|
logger.access(MockResponse(200), None, {}, timedelta(seconds=7))
|
||||||
|
t.eq(logger.sock.msgs[0], "gunicorn.request.duration:7.0|ms")
|
||||||
|
t.eq(logger.sock.msgs[1], "gunicorn.requests:1|c|@1.0")
|
||||||
|
t.eq(logger.sock.msgs[2], "gunicorn.request.status.200:1|c|@1.0")
|
||||||
Loading…
x
Reference in New Issue
Block a user