diff --git a/Dockerfile b/Dockerfile index ce2c596..898609e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,13 @@ -FROM alpine:latest +FROM python:3.7-alpine -# Adds testing package to repositories -# Install needed packages. Notes: -# * build-base: used so we include the basic development packages (gcc) -# * python-dev: are used for gevent e.g. -# * bash: so we can access /bin/bash -RUN echo "@testing http://dl-4.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \ - && apk add --update \ - musl \ - build-base \ - bash \ - git \ - python \ - python-dev \ - py-pip \ - && pip install --upgrade pip \ - && rm /var/cache/apk/* +WORKDIR /usr/src/app -# make some useful symlinks that are expected to exist -RUN cd /usr/bin \ - && ln -sf easy_install-2.7 easy_install \ - && ln -sf python2.7 python \ - && ln -sf python2.7-config python-config \ - && ln -sf pip2.7 pip +COPY requirements.txt ./ -RUN pip install prometheus-client==0.0.14 +RUN pip install --no-cache-dir -r requirements.txt -EXPOSE 9184 COPY nexus_exporter.py /nexus_exporter.py -ENTRYPOINT ["/nexus_exporter.py"] +EXPOSE 9184 + +ENTRYPOINT ["/nexus_exporter.py"] \ No newline at end of file diff --git a/nexus_exporter.py b/nexus_exporter.py index f49cc3f..638b47e 100755 --- a/nexus_exporter.py +++ b/nexus_exporter.py @@ -4,19 +4,17 @@ import json import time import base64 -try: - import urllib2 - from urlparse import urlparse - from urllib2 import URLError, HTTPError -except ImportError: - # Python 3 - import urllib.request as urllib2 - from urllib.parse import urlparse - from urllib.error import URLError, HTTPError +import logging +import urllib.request as urllib2 +from urllib.parse import urlparse +from urllib.error import URLError, HTTPError from prometheus_client import start_http_server from prometheus_client.core import GaugeMetricFamily, REGISTRY import argparse +LOG = logging.getLogger('nexus-exporter') +logging.basicConfig(level=logging.INFO) + def valid_url(string): """Validate url input argument. @@ -39,56 +37,72 @@ def parse(): parser = argparse.ArgumentParser( description='Export Prometheus metrics for Sonatype Nexus > 3.6') parser.add_argument( - '--host', metavar='HOST', + '--host', + metavar='HOST', type=valid_url, help='address with port where Nexus is available. Defaults to\ http://localhost:8081', - default=os.environ.get("NEXUS_HOST", "http://localhost:8081") - ) - parser.add_argument( - "--password", "-p", help="admin password", - default=os.environ.get("NEXUS_ADMIN_PASSWORD", "admin123") - ) - parser.add_argument( - "--user", "-u", help="Nexus user name, defaults to admin", - default=os.environ.get("NEXUS_USERNAME", "admin") - ) + default=os.environ.get("NEXUS_HOST", "http://localhost:8081")) + parser.add_argument("--password", + "-p", + help="admin password", + default=os.environ.get("NEXUS_ADMIN_PASSWORD", + "admin123")) + parser.add_argument("--user", + "-u", + help="Nexus user name, defaults to admin", + default=os.environ.get("NEXUS_USERNAME", "admin")) + parser.add_argument("--port", + help="Exporter port: default 9184", + default=9184) return parser.parse_args() class NexusCollector(object): def __init__(self, target, user, password): self._target = target.rstrip("/") - self._auth = base64.standard_b64encode('%s:%s' % (user, password)) + auth_string = '%s:%s' % (user, password) + self._auth = base64.standard_b64encode(auth_string.encode()) self._info = {} self._data = {} def collect(self): # make requests + nexus_up = True try: self._request_data() except HTTPError as err: if err.code == 401: - fatal('Authentication failure, attempting to restart') - except URLError as err: - fatal(err) + LOG.error('Authentication failure, please check \ + username/password') + nexus_up = False + except URLError as e: + LOG.error('unable to fetch data from metric endpoint: %s', e) + nexus_up = False + + yield GaugeMetricFamily('nexus_up', + 'Whether the Nexus server is up.', + value=nexus_up) + + if not nexus_up: + return i = self._info['system-runtime'] - yield GaugeMetricFamily( - 'nexus_processors_available', - 'Available Processors', value=i['availableProcessors']) - yield GaugeMetricFamily( - 'nexus_free_memory_bytes', - 'Free Memory (bytes)', value=i['freeMemory']) - yield GaugeMetricFamily( - 'nexus_total_memory_bytes', - 'Total Memory (bytes)', value=i['totalMemory']) - yield GaugeMetricFamily( - 'nexus_max_memory_bytes', - 'Max Memory (bytes)', value=i['maxMemory']) - yield GaugeMetricFamily( - 'nexus_threads_used', - 'Threads Used', value=i['threads']) + yield GaugeMetricFamily('nexus_processors_available', + 'Available Processors', + value=i['availableProcessors']) + yield GaugeMetricFamily('nexus_free_memory_bytes', + 'Free Memory (bytes)', + value=i['freeMemory']) + yield GaugeMetricFamily('nexus_total_memory_bytes', + 'Total Memory (bytes)', + value=i['totalMemory']) + yield GaugeMetricFamily('nexus_max_memory_bytes', + 'Max Memory (bytes)', + value=i['maxMemory']) + yield GaugeMetricFamily('nexus_threads_used', + 'Threads Used', + value=i['threads']) i = self._info['system-filestores'] for fsname, details in i.iteritems(): @@ -119,49 +133,51 @@ def collect(self): yield fas i = self._data['gauges'] - yield GaugeMetricFamily( - 'nexus_jvm_memory_heap_committed_bytes', - '', value=i['jvm.memory.heap.committed']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_heap_init_bytes', - '', value=i['jvm.memory.heap.init']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_heap_max_bytes', - '', value=i['jvm.memory.heap.max']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_heap_used_bytes', - '', value=i['jvm.memory.heap.used']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_heap_committed_bytes', + '', + value=i['jvm.memory.heap.committed']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_heap_init_bytes', + '', + value=i['jvm.memory.heap.init']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_heap_max_bytes', + '', + value=i['jvm.memory.heap.max']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_heap_used_bytes', + '', + value=i['jvm.memory.heap.used']['value']) yield GaugeMetricFamily( 'nexus_jvm_memory_nonheap_committed_bytes', - '', value=i['jvm.memory.non-heap.committed']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_nonheap_init_bytes', - '', value=i['jvm.memory.non-heap.init']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_nonheap_max_bytes', - '', value=i['jvm.memory.non-heap.max']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_nonheap_used_bytes', - '', value=i['jvm.memory.non-heap.used']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_total_committed_bytes', - '', value=i['jvm.memory.total.committed']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_total_init_bytes', - '', value=i['jvm.memory.total.init']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_total_max_bytes', - '', value=i['jvm.memory.total.max']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_memory_total_used_bytes', - '', value=i['jvm.memory.total.used']['value']) - yield GaugeMetricFamily( - 'nexus_jvm_uptime_seconds', - '', value=i['jvm.vm.uptime']['value']/1000.0) + '', + value=i['jvm.memory.non-heap.committed']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_nonheap_init_bytes', + '', + value=i['jvm.memory.non-heap.init']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_nonheap_max_bytes', + '', + value=i['jvm.memory.non-heap.max']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_nonheap_used_bytes', + '', + value=i['jvm.memory.non-heap.used']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_total_committed_bytes', + '', + value=i['jvm.memory.total.committed']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_total_init_bytes', + '', + value=i['jvm.memory.total.init']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_total_max_bytes', + '', + value=i['jvm.memory.total.max']['value']) + yield GaugeMetricFamily('nexus_jvm_memory_total_used_bytes', + '', + value=i['jvm.memory.total.used']['value']) + yield GaugeMetricFamily('nexus_jvm_uptime_seconds', + '', + value=i['jvm.vm.uptime']['value'] / 1000.0) i = self._data['meters'] - et = GaugeMetricFamily( - 'nexus_events_total', 'Nexus Events Count', labels=['level']) + et = GaugeMetricFamily('nexus_events_total', + 'Nexus Events Count', + labels=['level']) et.add_metric(['trace'], i['metrics.trace']['count']) et.add_metric(['debug'], i['metrics.debug']['count']) et.add_metric(['info'], i['metrics.info']['count']) @@ -169,9 +185,9 @@ def collect(self): et.add_metric(['error'], i['metrics.error']['count']) yield et - hr = GaugeMetricFamily( - 'nexus_webapp_http_response_total', - 'Nexus Webapp HTTP Response Count', labels=['code']) + hr = GaugeMetricFamily('nexus_webapp_http_response_total', + 'Nexus Webapp HTTP Response Count', + labels=['code']) hr.add_metric( ['1xx'], i['org.eclipse.jetty.webapp.WebAppContext.1xx-responses']['count']) @@ -190,19 +206,17 @@ def collect(self): yield hr i = self._data['timers'] - hq = GaugeMetricFamily( - 'nexus_webapp_http_request_total', - 'Nexus Webapp HTTP Request Count', labels=['method']) + hq = GaugeMetricFamily('nexus_webapp_http_request_total', + 'Nexus Webapp HTTP Request Count', + labels=['method']) hq.add_metric( ['connect'], - i[ - 'org.eclipse.jetty.webapp.WebAppContext.connect-requests' - ]['count']) + i['org.eclipse.jetty.webapp.WebAppContext.connect-requests'] + ['count']) hq.add_metric( ['delete'], - i[ - 'org.eclipse.jetty.webapp.WebAppContext.delete-requests' - ]['count']) + i['org.eclipse.jetty.webapp.WebAppContext.delete-requests'] + ['count']) hq.add_metric( ['get'], i['org.eclipse.jetty.webapp.WebAppContext.get-requests']['count']) @@ -214,25 +228,20 @@ def collect(self): i['org.eclipse.jetty.webapp.WebAppContext.move-requests']['count']) hq.add_metric( ['options'], - i[ - 'org.eclipse.jetty.webapp.WebAppContext.options-requests' - ]['count']) - hq.add_metric( - ['other'], - i[ - 'org.eclipse.jetty.webapp.WebAppContext.other-requests' - ]['count']) + i['org.eclipse.jetty.webapp.WebAppContext.options-requests'] + ['count']) + hq.add_metric([ + 'other' + ], i['org.eclipse.jetty.webapp.WebAppContext.other-requests']['count']) hq.add_metric( ['post'], i['org.eclipse.jetty.webapp.WebAppContext.post-requests']['count']) hq.add_metric( ['put'], i['org.eclipse.jetty.webapp.WebAppContext.put-requests']['count']) - hq.add_metric( - ['trace'], - i[ - 'org.eclipse.jetty.webapp.WebAppContext.trace-requests' - ]['count']) + hq.add_metric([ + 'trace' + ], i['org.eclipse.jetty.webapp.WebAppContext.trace-requests']['count']) yield hq def _mount_point(self, description): @@ -240,25 +249,21 @@ def _mount_point(self, description): def _request_data(self): info_request = urllib2.Request( - "{0}/service/rest/atlas/system-information".format( - self._target)) + "{0}/service/rest/atlas/system-information".format(self._target)) info_request.add_header("Authorization", "Basic %s" % self._auth) self._info = json.loads(urllib2.urlopen(info_request).read()) data_request = urllib2.Request("{0}/service/metrics/data".format( - self._target)) + self._target)) data_request.add_header("Authorization", "Basic %s" % self._auth) self._data = json.loads(urllib2.urlopen(data_request).read()) -def fatal(msg): - print(msg) - os._exit(1) # hard exit without throwing exception - if __name__ == "__main__": - print("starting...") args = parse() + LOG.info('starting nexus exporter on port %s' % args.port) + REGISTRY.register(NexusCollector(args.host, args.user, args.password)) - start_http_server(9184) + start_http_server(args.port) while True: time.sleep(1) diff --git a/requirements.txt b/requirements.txt index a73f525..c83ee35 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -prometheus-client==0.0.14 +prometheus-client==0.7.1