Skip to content

Commit

Permalink
Implement a direct to prometheus export for c2cwsgiutils_stats_db
Browse files Browse the repository at this point in the history
Redirect warnings to logging
  • Loading branch information
pvalsecc committed Jun 12, 2017
1 parent 29f311d commit 1e20e90
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 8 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
!rel_requirements.txt
!setup.py
!setup.cfg
!tests
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ RUN pip install --no-cache-dir -r /c2cwsgiutils/rel_requirements.txt

COPY . /c2cwsgiutils/
RUN flake8 /c2cwsgiutils && \
pip install --no-cache-dir -e /c2cwsgiutils
pip install --no-cache-dir -e /c2cwsgiutils && \
(cd /c2cwsgiutils/ && pytest -vv tests && rm -r tests)

ENV LOG_TYPE=console \
LOG_HOST=localhost \
Expand Down
1 change: 0 additions & 1 deletion acceptance_tests/app/test_app.iml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/c2cwsgiutils_app" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/c2cwsgiutils" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
Expand Down
1 change: 0 additions & 1 deletion acceptance_tests/tests/acceptance.iml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/c2cwsgiutils" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
Expand Down
38 changes: 38 additions & 0 deletions c2cwsgiutils/prometheus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
Implement parts of the Prometheus Pushgateway protocol, as defined here:
https://github.com/prometheus/pushgateway
"""
import requests


class PushgatewayGroupPublisher(object):
def __init__(self, base_url, job, instance=None):
if not base_url.endswith('/'):
base_url += '/'
self._url = "%smetrics/job/%s" % (base_url, job)
if instance is not None:
self._url += '/instance/' + instance
self._reset()

def add(self, metric_name, metric_value, metric_type='gauge', metric_labels=None):
if metric_name in self._types:
if self._types[metric_name] != metric_type:
raise ValueError("Cannot change the type of a given metric")
else:
self._types[metric_name] = metric_type
self._to_send += '# TYPE %s %s\n' % (metric_name, metric_type)
self._to_send += metric_name
if metric_labels is not None:
self._to_send += '{' + ', '.join('%s="%s"' % (k, v) for k, v in metric_labels.items()) + '}'
self._to_send += ' %s\n' % metric_value

def commit(self):
requests.put(self._url, data=self._to_send).raise_for_status()
self._reset()

def _reset(self):
self._to_send = ''
self._types = {}

def __str__(self):
return self._url + ' ->\n' + self._to_send
1 change: 1 addition & 0 deletions c2cwsgiutils/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def create_application(configfile=None):
:param config: The configuration file to use
:return: The application
"""
logging.captureWarnings(True)
if configfile is None:
configfile = os.environ.get('C2CWSGIUTILS_CONFIG', "/app/production.ini")
# Load the logging config without using pyramid to be able to use environment variables in there.
Expand Down
25 changes: 22 additions & 3 deletions c2cwsgiutils_stats_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from zope.sqlalchemy import ZopeTransactionExtension

from c2cwsgiutils import stats
from c2cwsgiutils.prometheus import PushgatewayGroupPublisher

logging.basicConfig(level=logging.INFO,
format="%(asctime)-15s %(levelname)5s %(name)s %(message)s",
Expand All @@ -28,6 +29,9 @@ def _parse_args():
help='A SQL query that returns a metric name and a value')
parser.add_argument('--statsd_address', type=str, help='address:port for the statsd daemon')
parser.add_argument('--statsd_prefix', type=str, default='c2c', help='prefix for the statsd metrics')
parser.add_argument('--prometheus_url', type=str, help='Base URL for the Prometheus Pushgateway')
parser.add_argument('--prometheus_instance', type=str,
help='Instance name for the Prometheus Pushgateway')
parser.add_argument('--verbosity', type=str, default='INFO')
args = parser.parse_args()
logging.root.setLevel(args.verbosity)
Expand All @@ -41,12 +45,28 @@ def __init__(self, args):
else:
self.statsd = None

if args.prometheus_url:
self.prometheus = PushgatewayGroupPublisher(args.prometheus_url, 'db_counts',
instance=args.prometheus_instance)
else:
self.prometheus = None

def do_report(self, metric, count):
LOG.info("%s -> %d", ".".join(metric), count)
if self.statsd is not None:
self.statsd.gauge(metric, count)
if self.prometheus is not None:

self.prometheus.add('database_table_count', count, metric_labels={
'metric': ".".join(v.replace('.', '_') for v in metric)
})

def commit(self):
if self.prometheus is not None:
self.prometheus.commit()


def error(self, metric):
def error(self, metric):
if self.statsd is not None:
self.statsd.counter(['error'] + metric, 1)

Expand Down Expand Up @@ -83,16 +103,15 @@ def main():
do_table(session, schema, table, reporter)
except Exception:
reporter.error([schema, table])
raise

if args.extra:
for pos, extra in enumerate(args.extra):
try:
do_extra(session, extra, reporter)
except Exception:
reporter.error(['extra', str(pos + 1)])
raise

reporter.commit()
transaction.abort()


Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from setuptools import setup, find_packages


VERSION = '0.16.1'
VERSION = '0.16.2'
HERE = os.path.abspath(os.path.dirname(__file__))
INSTALL_REQUIRES = open(os.path.join(HERE, 'rel_requirements.txt')).read().splitlines()

Expand Down
Empty file added tests/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions tests/test_prometheus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest

from c2cwsgiutils.prometheus import PushgatewayGroupPublisher


def test_pushgateway_group_publisher():
publisher = PushgatewayGroupPublisher('http://example.com/', 'test')
publisher.add('metric1', 12)
publisher.add('metric2', 13, metric_labels={'toto': 'TOTO', 'tutu': 'TUTU'})
publisher.add('metric2', 14, metric_labels={'toto': 'TOTO', 'tutu': 'TITI'})
with pytest.raises(ValueError):
publisher.add('metric1', 12, metric_type='counter')

assert str(publisher) == 'http://example.com/metrics/job/test ->\n' \
'# TYPE metric1 gauge\n' \
'metric1 12\n' \
'# TYPE metric2 gauge\n' \
'metric2{toto="TOTO", tutu="TUTU"} 13\n' \
'metric2{toto="TOTO", tutu="TITI"} 14\n'

0 comments on commit 1e20e90

Please sign in to comment.