From a98d327da17ffbcc12acf38d3785d9bd81d7908a Mon Sep 17 00:00:00 2001 From: Jonathan Math Date: Fri, 10 Dec 2021 16:54:17 -0500 Subject: [PATCH] add state timeline metric for redis and mongo in flask_metrics --- flask_metrics/flask_metrics_mongo.py | 15 +++++++ flask_metrics/flask_metrics_redis.py | 14 +++++++ flask_metrics/state_timeline.py | 42 ++++++++++++++++++++ flask_metrics/templates/prometheus_data.html | 2 +- 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 flask_metrics/state_timeline.py diff --git a/flask_metrics/flask_metrics_mongo.py b/flask_metrics/flask_metrics_mongo.py index 9b8e4b2c5..e28c0bc4f 100644 --- a/flask_metrics/flask_metrics_mongo.py +++ b/flask_metrics/flask_metrics_mongo.py @@ -1,5 +1,6 @@ from flask import Flask, render_template, Response import pymongo +from flask_metrics.state_timeline import format_state_data, get_state from newsblur_web import settings import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration @@ -165,6 +166,20 @@ def page_queues(): html_body = render_template('prometheus_data.html', **context) return Response(html_body, content_type="text/plain") +@app.route("/state/") +def mongo_state(): + mongo_data = get_state("mongo") + if 'BACKEND' in mongo_data: + del mongo_data['BACKEND'] + formatted_data = format_state_data("mongo_state", mongo_data) + context = { + 'chart_name': 'mongo_state', + 'chart_type': 'gauge', + 'data': formatted_data + } + html_body = render_template('prometheus_data.html', **context) + return Response(html_body, content_type="text/plain") + if __name__ == "__main__": print(" ---> Starting NewsBlur Flask Metrics server...") diff --git a/flask_metrics/flask_metrics_redis.py b/flask_metrics/flask_metrics_redis.py index fbaa5cab2..d1f08b345 100644 --- a/flask_metrics/flask_metrics_redis.py +++ b/flask_metrics/flask_metrics_redis.py @@ -1,5 +1,6 @@ from flask import Flask, render_template, Response from newsblur_web import settings +from flask_metrics.state_timeline import format_state_data, get_state import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration import redis @@ -186,6 +187,19 @@ def memory_used(): return Response(html_body, content_type="text/plain") +@app.route("/state/") +def redis_state(): + redis_data = get_state("db_redis") + formatted_data = format_state_data("redis_state", redis_data) + context = { + 'chart_name': 'redis_state', + 'chart_type': 'gauge', + 'data': formatted_data + } + html_body = render_template('prometheus_data.html', **context) + return Response(html_body, content_type="text/plain") + + if __name__ == "__main__": print(" ---> Starting NewsBlur Flask Metrics server...") app.run(host="0.0.0.0", port=5569) diff --git a/flask_metrics/state_timeline.py b/flask_metrics/state_timeline.py new file mode 100644 index 000000000..ff277e03a --- /dev/null +++ b/flask_metrics/state_timeline.py @@ -0,0 +1,42 @@ +from flask import render_template +import requests +from requests.auth import HTTPBasicAuth + + +STATUS_MAPPING = { + "UNK": 0, # unknown + "INI": 1, # initializing + "SOCKERR": 2, # socket error + "L4OK": 3, # check passed on layer 4, no upper layers testing enabled + "L4TOUT": 4, # layer 1-4 timeout + "L4CON": 5, # layer 1-4 connection problem, for example "Connection refused" (tcp rst) or "No route to host" (icmp) + "L6OK": 6, # check passed on layer 6 + "L6TOUT": 7, # layer 6 (SSL) timeout + "L6RSP": 8, # layer 6 invalid response - protocol error + "L7OK": 9, # check passed on layer 7 + "L7OKC": 10, # check conditionally passed on layer 7, for example 404 with disable-on-404 + "L7TOUT": 11, # layer 7 (HTTP/SMTP) timeout + "L7RSP": 12, # layer 7 invalid response - protocol error + "L7STS": 13, # layer 7 response error, for example HTTP 5xx +} + +def format_state_data(label, data): + formatted_data = {} + for k, v in data.items(): + if v: + formatted_data[k] = f'{label}{{servername="{k}"}} {STATUS_MAPPING[v]}' + return formatted_data + +def get_state(backend_name): + res = requests.get('https://newsblur.com:1936/;csv', auth=HTTPBasicAuth('gimmiestats', 'StatsGiver')) + lines = res.content.decode('utf-8').split('\n') + backends = [line.split(",") for line in lines if backend_name in line] + + check_status_index = lines[0].split(",").index('check_status') + servername_index = lines[0].split(",").index('svname') + + data = {} + + for backend_data in backends: + data[backend_data[servername_index]] = backend_data[check_status_index].replace("*", "") + return data diff --git a/flask_metrics/templates/prometheus_data.html b/flask_metrics/templates/prometheus_data.html index 128e4d624..05fc6df71 100644 --- a/flask_metrics/templates/prometheus_data.html +++ b/flask_metrics/templates/prometheus_data.html @@ -1,4 +1,4 @@ -# TYPE {{chart_name}} {{chart_type}} {% for k, v in data.items() %} +# TYPE {{chart_name}} {{chart_type}} {{ v |safe }} {% endfor %} \ No newline at end of file