initial commit.
This commit is contained in:
commit
7941a8eb60
4 changed files with 176 additions and 0 deletions
11
Dockerfile
Normal file
11
Dockerfile
Normal 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
22
README.md
Normal 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
25
pagespeed.conf
Normal 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
118
pagespeed.py
Normal 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()
|
Loading…
Reference in a new issue