Skip to content
This repository has been archived by the owner on Jun 7, 2019. It is now read-only.

Commit

Permalink
Merge branch 'korfuri-master'
Browse files Browse the repository at this point in the history
  • Loading branch information
saady committed Oct 23, 2017
2 parents 780eb5e + 4de791d commit f9c1884
Show file tree
Hide file tree
Showing 14 changed files with 91 additions and 51 deletions.
22 changes: 6 additions & 16 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,21 @@ python:
- "3.3"
- "2.7"
env:
- DJANGO_VERSION=1.11
- DJANGO_VERSION=1.10
- DJANGO_VERSION=1.9
- DJANGO_VERSION=1.8
- DJANGO_VERSION=1.7
- DJANGO_VERSION=1.6
- DJANGO_VERSION=1.5
- DJANGO_VERSION=1.4
services:
- mysql
- postgresql
matrix:
exclude:
- python: "3.3"
env: DJANGO_VERSION=1.4
- python: "3.3"
env: DJANGO_VERSION=1.9
- python: "3.4"
env: DJANGO_VERSION=1.4
- python: "3.5"
env: DJANGO_VERSION=1.4
- python: "3.5"
env: DJANGO_VERSION=1.5
- python: "3.5"
env: DJANGO_VERSION=1.6
- python: "3.5"
env: DJANGO_VERSION=1.7
- python: "3.3"
env: DJANGO_VERSION=1.10
- python: "3.3"
env: DJANGO_VERSION=1.11
install:
- pip install -r requirements.txt
- pip install -q Django==$DJANGO_VERSION
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Export Django monitoring metrics for Prometheus.io

### Requirements

* Django >= 1.4 (>= 1.8 is recommended)
* Django >= 1.8

### Installation

Expand Down
2 changes: 1 addition & 1 deletion django_prometheus/db/backends/mysql/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class DatabaseFeatures(base.DatabaseFeatures):
class DatabaseWrapper(DatabaseWrapperMixin, base.DatabaseWrapper):
CURSOR_CLASS = base.CursorWrapper

def create_cursor(self):
def create_cursor(self, name=None):
cursor = self.connection.cursor()
CursorWrapper = ExportingCursorWrapper(
self.CURSOR_CLASS, self.alias, self.vendor)
Expand Down
7 changes: 5 additions & 2 deletions django_prometheus/db/backends/postgresql/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ def get_connection_params(self):
)
return conn_params

def create_cursor(self):
def create_cursor(self, name=None):
# cursor_factory is a kwarg to connect() so restore create_cursor()'s
# default behavior
return base.DatabaseWrapper.create_cursor(self)
if django.VERSION >= (1, 11, 0):
return base.DatabaseWrapper.create_cursor(self, name=name)
else:
return base.DatabaseWrapper.create_cursor(self)
2 changes: 1 addition & 1 deletion django_prometheus/db/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def get_new_connection(self, *args, **kwargs):
connection_errors_total.labels(self.alias, self.vendor).inc()
raise

def create_cursor(self):
def create_cursor(self, name=None):
return self.connection.cursor(factory=ExportingCursorWrapper(
self.CURSOR_CLASS, self.alias, self.vendor))

Expand Down
9 changes: 7 additions & 2 deletions django_prometheus/exports.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.http import HttpResponse
from django.conf import settings
from prometheus_client import multiprocess

try:
# Python 2
from BaseHTTPServer import HTTPServer
Expand Down Expand Up @@ -58,7 +60,7 @@ def SetupPrometheusEndpointOnPortRange(port_range, addr=''):
This is useful when you're running Django as a WSGI application
with multiple processes and you want Prometheus to discover all
workers. Each worker will grab a port and you can use Prometheus
to aggregate accross workers.
to aggregate across workers.
port_range may be any iterable object that contains a list of
ports. Typically this would be an xrange of contiguous ports.
Expand Down Expand Up @@ -105,7 +107,10 @@ def ExportToDjangoView(request):
You can use django_prometheus.urls to map /metrics to this view.
"""
metrics_page = prometheus_client.generate_latest()
registry = prometheus_client.CollectorRegistry()
if 'prometheus_multiproc_dir' in os.environ:
multiprocess.MultiProcessCollector(registry)
metrics_page = prometheus_client.generate_latest(registry)
return HttpResponse(
metrics_page,
content_type=prometheus_client.CONTENT_TYPE_LATEST)
35 changes: 34 additions & 1 deletion django_prometheus/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ def process_response(self, request, response):
requests_latency = Histogram(
'django_http_requests_latency_seconds',
'Histogram of requests processing time.', ['endpoint'])

requests_latency_by_view_method = Histogram(
'django_http_requests_latency_seconds_by_view_method',
'Histogram of request processing time labelled by view.',
['view', 'method'])

requests_unknown_latency = Counter(
'django_http_requests_unknown_latency_total',
'Count of requests for which the latency was unknown.')
Expand Down Expand Up @@ -117,9 +123,18 @@ def process_request(self, request):
requests_by_transport.labels(transport).inc()
if request.is_ajax():
ajax_requests.inc()
requests_body_bytes.observe(len(request.body))
content_length = int(request.META.get('CONTENT_LENGTH') or 0)
requests_body_bytes.observe(content_length)
request.prometheus_after_middleware_event = Time()

def _get_view_name(self, request):
view_name = "<unnamed view>"
if hasattr(request, 'resolver_match'):
if request.resolver_match is not None:
if request.resolver_match.view_name is not None:
view_name = request.resolver_match.view_name
return view_name

def process_view(self, request, view_func, *view_args, **view_kwargs):
transport = self._transport(request)
method = self._method(request)
Expand All @@ -142,8 +157,17 @@ def process_response(self, request, response):
if hasattr(response, 'content'):
responses_body_bytes.observe(len(response.content))
if hasattr(request, 'prometheus_after_middleware_event'):

requests_latency.labels(endpoint=request.path).observe(TimeSince(
request.prometheus_after_middleware_event))

requests_latency_by_view_method\
.labels(
view=self._get_view_name(request),
method=request.method)\
.observe(TimeSince(
request.prometheus_after_middleware_event
))
else:
requests_unknown_latency.inc()
return response
Expand All @@ -154,7 +178,16 @@ def process_exception(self, request, exception):
name = request.resolver_match.view_name or '<unnamed view>'
exceptions_by_view.labels(name).inc()
if hasattr(request, 'prometheus_after_middleware_event'):

requests_latency.labels(endpoint=request.path).observe(TimeSince(
request.prometheus_after_middleware_event))

requests_latency_by_view_method\
.labels(
view=self._get_view_name(request),
method=request.method)\
.observe(TimeSince(
request.prometheus_after_middleware_event
))
else:
requests_unknown_latency.inc()
8 changes: 3 additions & 5 deletions django_prometheus/tests/end2end/testapp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,12 @@ def GetMiddlewareClasses():
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware'])
if django.VERSION >= (1, 7):
classes.append(
'django.contrib.auth.middleware.SessionAuthenticationMiddleware')
classes.append(
'django.contrib.auth.middleware.SessionAuthenticationMiddleware')
classes.extend([
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware'])
if django.VERSION >= (1, 8):
classes.append('django.middleware.security.SecurityMiddleware')
classes.append('django.middleware.security.SecurityMiddleware')
classes.append('django_prometheus.middleware.PrometheusAfterMiddleware')
return classes

Expand Down
32 changes: 21 additions & 11 deletions django_prometheus/tests/end2end/testapp/test_middleware.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import django
from django_prometheus.testutils import PrometheusTestCaseMixin
from testapp.views import ObjectionException
from django.test import SimpleTestCase
import unittest

Expand Down Expand Up @@ -65,10 +66,9 @@ def test_request_counters(self):
r, 3, M('responses_body_total_bytes_bucket'), le='128.0')
self.assertMetricDiff(
r, 4, M('responses_body_total_bytes_bucket'), le='8192.0')
if django.VERSION >= (1, 8):
self.assertMetricDiff(
r, 4, M('responses_total_by_charset'), charset='utf-8')
self.assertMetricDiff(r, 0, M('responses_streaming_total'))
self.assertMetricDiff(
r, 4, M('responses_total_by_charset'), charset='utf-8')
self.assertMetricDiff(r, 0, M('responses_streaming_total'))

def test_latency_histograms(self):
# Caution: this test is timing-based. This is not ideal. It
Expand All @@ -82,16 +82,26 @@ def test_latency_histograms(self):
# buckets is fine.
self.client.get('/slow')
self.assertMetricDiff(
r, 0, M('requests_latency_seconds_bucket'), le='0.05')
r, 0,
M("requests_latency_seconds_by_view_method_bucket"),
le='0.05', view="slow", method="GET")
self.assertMetricDiff(
r, 1, M('requests_latency_seconds_bucket'), le='5.0')
r, 1,
M("requests_latency_seconds_by_view_method_bucket"),
le='5.0', view="slow", method="GET")

self.client.get('/')
self.assertMetricDiff(
r, 2, M('requests_latency_seconds_bucket'), le='+Inf')
def test_exception_latency_histograms(self):
r = self.saveRegistry()

try:
self.client.get('/objection')
except ObjectionException:
pass
self.assertMetricDiff(
r, 2,
M("requests_latency_seconds_by_view_method_bucket"),
le='0.05', view="testapp.views.objection", method="GET")

@unittest.skipIf(django.VERSION < (1, 8),
'Streaming responses are not supported before Django 1.8')
def test_streaming_responses(self):
r = self.saveRegistry()
self.client.get('/')
Expand Down
2 changes: 0 additions & 2 deletions django_prometheus/tests/end2end/testapp/test_migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ def M(metric_name):
return 'django_migrations_%s' % metric_name


@unittest.skipIf(django.VERSION < (1, 7),
'Migrations are not supported before Django 1.7')
class TestMigrations(PrometheusTestCaseMixin, SimpleTestCase):
"""Test migration counters."""

Expand Down
2 changes: 1 addition & 1 deletion django_prometheus/tests/end2end/testapp/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
urlpatterns = [
url(r'^$', views.index),
url(r'^help$', views.help),
url(r'^slow$', views.slow),
url(r'^slow$', views.slow, name="slow"),
url(r'^objection$', views.objection),
url(r'^sql$', views.sql),
url(r'^newlawn/(?P<location>[a-zA-Z0-9 ]+)$', views.newlawn),
Expand Down
5 changes: 1 addition & 4 deletions django_prometheus/tests/end2end/testapp/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
from testapp.models import Lawn
import os
import time
if django.VERSION >= (1, 8):
from django.http import FileResponse
from django.http import FileResponse


def index(request):
Expand Down Expand Up @@ -63,6 +62,4 @@ def sql(request):


def file(request):
assert django.VERSION >= (1, 8), (
'FileResponse is only supported afted Django 1.8')
return FileResponse(open(os.devnull, 'rb'))
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Django>=1.4
Django>=1.8
pep8>=1.6.2
prometheus-client>=0.0.15
prometheus-client>=0.0.21
pip-prometheus>=1.0.0
mock>=1.0.1
mysqlclient
Expand Down
10 changes: 8 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

setup(
name="django-prometheus",
version="1.0.8",
version="1.0.10",
author="Uriel Corfa",
author_email="uriel@corfa.fr",
description=(
Expand All @@ -24,13 +24,19 @@
test_suite="django_prometheus.tests",
long_description=LONG_DESCRIPTION,
install_requires=[
"prometheus_client>=0.0.13",
"prometheus_client>=0.0.21",
],
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Framework :: Django",
"Topic :: System :: Monitoring",
"License :: OSI Approved :: Apache Software License",
Expand Down

0 comments on commit f9c1884

Please sign in to comment.