Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

statsd.py 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. # -*- coding: utf-8 -
  2. #
  3. # This file is part of gunicorn released under the MIT license.
  4. # See the NOTICE for more information.
  5. "Bare-bones implementation of statsD's protocol, client-side"
  6. import socket
  7. import logging
  8. from re import sub
  9. from gunicorn.glogging import Logger
  10. from gunicorn import six
  11. # Instrumentation constants
  12. STATSD_DEFAULT_PORT = 8125
  13. METRIC_VAR = "metric"
  14. VALUE_VAR = "value"
  15. MTYPE_VAR = "mtype"
  16. GAUGE_TYPE = "gauge"
  17. COUNTER_TYPE = "counter"
  18. HISTOGRAM_TYPE = "histogram"
  19. class Statsd(Logger):
  20. """statsD-based instrumentation, that passes as a logger
  21. """
  22. def __init__(self, cfg):
  23. """host, port: statsD server
  24. """
  25. Logger.__init__(self, cfg)
  26. self.prefix = sub(r"^(.+[^.]+)\.*$", "\g<1>.", cfg.statsd_prefix)
  27. try:
  28. host, port = cfg.statsd_host
  29. self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  30. self.sock.connect((host, int(port)))
  31. except Exception:
  32. self.sock = None
  33. # Log errors and warnings
  34. def critical(self, msg, *args, **kwargs):
  35. Logger.critical(self, msg, *args, **kwargs)
  36. self.increment("gunicorn.log.critical", 1)
  37. def error(self, msg, *args, **kwargs):
  38. Logger.error(self, msg, *args, **kwargs)
  39. self.increment("gunicorn.log.error", 1)
  40. def warning(self, msg, *args, **kwargs):
  41. Logger.warning(self, msg, *args, **kwargs)
  42. self.increment("gunicorn.log.warning", 1)
  43. def exception(self, msg, *args, **kwargs):
  44. Logger.exception(self, msg, *args, **kwargs)
  45. self.increment("gunicorn.log.exception", 1)
  46. # Special treatement for info, the most common log level
  47. def info(self, msg, *args, **kwargs):
  48. self.log(logging.INFO, msg, *args, **kwargs)
  49. # skip the run-of-the-mill logs
  50. def debug(self, msg, *args, **kwargs):
  51. self.log(logging.DEBUG, msg, *args, **kwargs)
  52. def log(self, lvl, msg, *args, **kwargs):
  53. """Log a given statistic if metric, value and type are present
  54. """
  55. try:
  56. extra = kwargs.get("extra", None)
  57. if extra is not None:
  58. metric = extra.get(METRIC_VAR, None)
  59. value = extra.get(VALUE_VAR, None)
  60. typ = extra.get(MTYPE_VAR, None)
  61. if metric and value and typ:
  62. if typ == GAUGE_TYPE:
  63. self.gauge(metric, value)
  64. elif typ == COUNTER_TYPE:
  65. self.increment(metric, value)
  66. elif typ == HISTOGRAM_TYPE:
  67. self.histogram(metric, value)
  68. else:
  69. pass
  70. # Log to parent logger only if there is something to say
  71. if msg is not None and len(msg) > 0:
  72. Logger.log(self, lvl, msg, *args, **kwargs)
  73. except Exception:
  74. Logger.warning(self, "Failed to log to statsd", exc_info=True)
  75. # access logging
  76. def access(self, resp, req, environ, request_time):
  77. """Measure request duration
  78. request_time is a datetime.timedelta
  79. """
  80. Logger.access(self, resp, req, environ, request_time)
  81. duration_in_ms = request_time.seconds * 1000 + float(request_time.microseconds) / 10 ** 3
  82. status = resp.status
  83. if isinstance(status, str):
  84. status = int(status.split(None, 1)[0])
  85. self.histogram("gunicorn.request.duration", duration_in_ms)
  86. self.increment("gunicorn.requests", 1)
  87. self.increment("gunicorn.request.status.%d" % status, 1)
  88. # statsD methods
  89. # you can use those directly if you want
  90. def gauge(self, name, value):
  91. self._sock_send("{0}{1}:{2}|g".format(self.prefix, name, value))
  92. def increment(self, name, value, sampling_rate=1.0):
  93. self._sock_send("{0}{1}:{2}|c|@{3}".format(self.prefix, name, value, sampling_rate))
  94. def decrement(self, name, value, sampling_rate=1.0):
  95. self._sock_send("{0){1}:-{2}|c|@{3}".format(self.prefix, name, value, sampling_rate))
  96. def histogram(self, name, value):
  97. self._sock_send("{0}{1}:{2}|ms".format(self.prefix, name, value))
  98. def _sock_send(self, msg):
  99. try:
  100. if isinstance(msg, six.text_type):
  101. msg = msg.encode("ascii")
  102. if self.sock:
  103. self.sock.send(msg)
  104. except Exception:
  105. Logger.warning(self, "Error sending message to statsd", exc_info=True)