initial commit.

This commit is contained in:
chris 2016-07-30 14:23:02 +02:00
commit 7941a8eb60
4 changed files with 176 additions and 0 deletions

11
Dockerfile Normal file
View file

@ -0,0 +1,11 @@
FROM alpine:3.4
RUN apk add --no-cache python3 &&\
pip3 install apscheduler &&\
pip3 install requests
COPY pagespeed.py pagespeed.conf /
EXPOSE 9113
CMD python3 /pagespeed.py

22
README.md Normal file
View file

@ -0,0 +1,22 @@
# pagespeed_exporter for providing Google PageSpeed Insights as metrics
## Requirements
Software:
* Python3
* APScheduler
* requests
Google PageSpeed API key. See here on how to [get a server API key for googles platform](https://developers.google.com/console/help/generating-dev-keys).
## Configuration
The exporter is currently able to check one URI, buffer the results
and serve them as metrics.
Configuration is done via pagespeed.conf, see example file for documentation and defaults.
All config values can be overriden with environment variables given the same names.
N.B.: Google caches it's results itself for 30 seconds, so a shorter fetch interval would
be useless.

25
pagespeed.conf Normal file
View file

@ -0,0 +1,25 @@
[EXPORTER]
# URI to run tests against. Make sure it is reachable from the public internet.
# ENV override: PAGESPEED_TEST_URI
test_uri = https://www.test.de/
# Your PageSpeed Insights API key from Google. See README.md for documentation link.
# ENV override: PAGESPEED_API_KEY
api_key =
# IP to bind the metrics server to.
# Default: empty, binds to all available interfaces.
# ENV override: PAGESPEED_BIND_IP
bind_ip =
# TCP port to bind the metrics server to.
# Default: 9113
# ENV override: PAGESPEED_BIND_PORT
bind_port = 9113
# Interval for instanciating pagespeed tests at google in seconds.
# Do not go below 30 seconds.
# Default: 300
# ENV override: PAGESPEED_FETCH_INTERVAL
fetch_interval = 300

118
pagespeed.py Normal file
View file

@ -0,0 +1,118 @@
import configparser
import json
import logging
import os
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler
from time import sleep, time
import requests
from pytz import utc
from apscheduler.schedulers.background import BackgroundScheduler
# try to read configfile
config = configparser.ConfigParser()
try:
config.read('pagespeed.conf')
exporter_config = config['EXPORTER']
except:
exporter_config = {'EXPORTER': {}}
# try to get config from ENV
TEST_URI = os.environ.get('PAGESPEED_URI', exporter_config.get('TEST_URI'))
API_KEY = os.environ.get('PAGESPEED_API_KEY', exporter_config.get('API_KEY'))
BIND_IP = os.environ.get('PAGESPEED_HOST', exporter_config.get('BIND_IP', ''))
BIND_PORT = os.environ.get('PAGESPEED_PORT', exporter_config.get('BIND_PORT', 9113))
FETCH_INTERVAL = os.environ.get('PAGESPEED_FETCH_INTERVAL', exporter_config.get('FETCH_INTERVAL', 300))
# input validation :)
if not TEST_URI:
logger.error('TEST_URI needs to be defined either in config file or in ENV.')
sys.exit(1)
if not API_KEY:
logger.error('API_KEY needs to be definer either in config file or in ENV.')
sys.exit(1)
try:
FETCH_INTERVAL = int(FETCH_INTERVAL)
BIND_PORT = int(BIND_PORT)
except:
logger.exception('Value error in FETCH_INTERVAL or BIND_PORT.')
sys.exit(1)
metric_data = ""
error_instances = 0
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s')
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
def fetch_pagespeed():
global metric_data
global error_instances
logger.debug('fetching pagespeed')
try:
req = requests.get('https://www.googleapis.com/pagespeedonline/v2/'
'runPagespeed?url={url}&key={key}&'
'prettyprint=false'.format(
url=TEST_URI,
key=API_KEY
))
result = req.json()
except:
logger.exception('error while doing request')
error_instances += 1
translation_table = {
'speed_score': result['ruleGroups']['SPEED']['score'],
'stats_resources_total': result['pageStats']['numberResources'],
'stats_hosts_total': result['pageStats']['numberHosts'],
'stats_request_bytes': result['pageStats']['totalRequestBytes'],
'stats_static_resources_total': result['pageStats']['numberStaticResources'],
'stats_html_resources_bytes': result['pageStats']['htmlResponseBytes'],
'stats_image_resources_bytes': result['pageStats']['imageResponseBytes'],
'stats_javascript_resources_bytes': result['pageStats']['javascriptResponseBytes'],
'stats_javascript_resources_total': result['pageStats']['numberJsResources'],
'stats_css_resources_bytes': result['pageStats']['cssResponseBytes'],
'stats_css_resources_total': result['pageStats']['numberCssResources'],
'stats_other_resources_bytes': result['pageStats']['otherResponseBytes'],
}
metric_data = ""
for metric, value in translation_table.items():
metric_data += 'pagespeed_{metric}{{site="{job}"}}' \
' {metric_value}\n'.format(
metric=metric,
job=result['id'],
metric_value=value,
)
metric_data += 'pagespeed_last_update {}\n'.format(time())
metric_data += 'pagespeed_metric_errors_total {}\n'.format(error_instances)
metric_data += 'up 1\n'
logger.debug('fetched pagespeed')
class AllGetHTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(s):
s.send_response(200)
s.send_header("Content-Type", "text/plain")
s.end_headers()
s.wfile.write(metric_data.encode('utf-8'))
scheduler = BackgroundScheduler(timezone=utc)
scheduler.add_job(fetch_pagespeed, 'interval', seconds=FETCH_INTERVAL, max_instances=1)
scheduler.start()
if __name__=='__main__':
logger.info('starting pagespeed_exporter')
if metric_data == '':
fetch_pagespeed()
server_address = (BIND_IP, BIND_PORT)
httpd = HTTPServer(server_address, AllGetHTTPRequestHandler)
logger.info('binding to {}'.format(server_address))
httpd.serve_forever()