Merge branch 'state_timeline'

* state_timeline:
  fix flask_metrics ansible role
  add state timeline dashboard to grafana
  move state timeline to one single flask that gets astate for all services
This commit is contained in:
Samuel Clay 2021-12-15 14:02:27 -05:00
commit 83e4609af1
7 changed files with 457 additions and 580 deletions

View file

@ -51,7 +51,8 @@
file: /srv/newsblur/flask_metrics/flask_metrics_mongo.py file: /srv/newsblur/flask_metrics/flask_metrics_mongo.py
- service_name: redis - service_name: redis
file: /srv/newsblur/flask_metrics/flask_metrics_redis.py file: /srv/newsblur/flask_metrics/flask_metrics_redis.py
- service_name: metrics
file: /srv/newsblur/flask_metrics/state_timeline.py
- name: Restart flask_metrics - name: Restart flask_metrics
become: yes become: yes
shell: shell:

View file

@ -69,6 +69,20 @@ services:
- nginx - nginx
volumes: volumes:
- ${PWD}:/srv/newsblur - ${PWD}:/srv/newsblur
flask_metrics_state:
container_name: flask_metrics_state
image: newsblur/newsblur_monitor:latest
command: bash -c "python /srv/newsblur/flask_metrics/state_timeline.py"
environment:
- DOCKERBUILD=True
ports:
- 5599:5569
depends_on:
- haproxy
- newsblur_web
- nginx
volumes:
- ${PWD}:/srv/newsblur
elasticsearch_exporter: elasticsearch_exporter:
container_name: elasticsearch_exporter container_name: elasticsearch_exporter
image: prometheuscommunity/elasticsearch-exporter:latest image: prometheuscommunity/elasticsearch-exporter:latest

View file

@ -144,8 +144,8 @@
"overrides": [] "overrides": []
}, },
"gridPos": { "gridPos": {
"h": 8, "h": 27,
"w": 12, "w": 24,
"x": 0, "x": 0,
"y": 0 "y": 0
}, },
@ -166,162 +166,26 @@
"targets": [ "targets": [
{ {
"exemplar": true, "exemplar": true,
"expr": "redis_state", "expr": "state",
"interval": "", "interval": "",
"legendFormat": "{{ servername }}", "legendFormat": "{{ servername }}",
"refId": "A" "refId": "A"
} }
], ],
"title": "Redis State", "title": "Service State",
"type": "state-timeline" "type": "state-timeline"
}, },
{ {
"datasource": "Prometheus", "collapsed": true,
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"fillOpacity": 70,
"lineWidth": 0
},
"mappings": [
{
"options": {
"0": {
"color": "super-light-red",
"index": 0,
"text": "UNK"
},
"1": {
"color": "semi-dark-yellow",
"index": 1,
"text": "INI"
},
"2": {
"color": "semi-dark-red",
"index": 2,
"text": "SOCKERR"
},
"3": {
"color": "dark-green",
"index": 3,
"text": "L4OK"
},
"4": {
"color": "dark-red",
"index": 4,
"text": "L4TOUT"
},
"5": {
"color": "dark-red",
"index": 5,
"text": "L4CON"
},
"6": {
"color": "dark-green",
"index": 6,
"text": "L6OK"
},
"7": {
"color": "dark-red",
"index": 7,
"text": "L6TOUT"
},
"8": {
"color": "dark-red",
"index": 8,
"text": "L6RSP"
},
"9": {
"color": "dark-green",
"index": 9,
"text": "L7OK"
},
"10": {
"color": "semi-dark-red",
"index": 10,
"text": "L7OKC"
},
"11": {
"color": "dark-red",
"index": 11,
"text": "L7TOUT"
},
"12": {
"color": "dark-red",
"index": 12,
"text": "L7RSP"
},
"13": {
"color": "dark-red",
"index": 13,
"text": "L7STS"
}
},
"type": "value"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 83,
"options": {
"alignValue": "left",
"legend": {
"displayMode": "table",
"placement": "bottom"
},
"mergeValues": true,
"rowHeight": 0.9,
"showValue": "auto",
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"exemplar": true,
"expr": "mongo_state",
"interval": "",
"legendFormat": "{{ servername }}",
"refId": "A"
}
],
"title": "Mongo State",
"type": "state-timeline"
},
{
"collapsed": false,
"datasource": null, "datasource": null,
"gridPos": { "gridPos": {
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 8 "y": 27
}, },
"id": 46, "id": 46,
"panels": [], "panels": [
"title": "Top Line",
"type": "row"
},
{ {
"aliasColors": {}, "aliasColors": {},
"bars": false, "bars": false,
@ -340,7 +204,7 @@
"h": 6, "h": 6,
"w": 8, "w": 8,
"x": 0, "x": 0,
"y": 9 "y": 28
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 22, "id": 22,
@ -453,7 +317,7 @@
"h": 6, "h": 6,
"w": 8, "w": 8,
"x": 8, "x": 8,
"y": 9 "y": 28
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 44, "id": 44,
@ -579,7 +443,7 @@
"h": 6, "h": 6,
"w": 8, "w": 8,
"x": 16, "x": 16,
"y": 9 "y": 28
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 38, "id": 38,
@ -676,6 +540,10 @@
"align": false, "align": false,
"alignLevel": null "alignLevel": null
} }
}
],
"title": "Top Line",
"type": "row"
}, },
{ {
"collapsed": false, "collapsed": false,
@ -684,7 +552,7 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 15 "y": 28
}, },
"id": 20, "id": 20,
"panels": [], "panels": [],
@ -711,7 +579,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 16 "y": 29
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 2, "id": 2,
@ -817,7 +685,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 16 "y": 29
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 6, "id": 6,
@ -913,7 +781,7 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 24 "y": 37
}, },
"id": 4, "id": 4,
"panels": [], "panels": [],
@ -950,7 +818,7 @@
"h": 21, "h": 21,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 25 "y": 38
}, },
"id": 10, "id": 10,
"options": { "options": {
@ -1004,7 +872,7 @@
"h": 7, "h": 7,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 46 "y": 59
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 48, "id": 48,
@ -1095,7 +963,7 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 53 "y": 66
}, },
"id": 42, "id": 42,
"panels": [], "panels": [],
@ -1114,7 +982,7 @@
"h": 9, "h": 9,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 54 "y": 67
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 33, "id": 33,
@ -1217,7 +1085,7 @@
"h": 8, "h": 8,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 63 "y": 76
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 30, "id": 30,
@ -1412,7 +1280,7 @@
"h": 8, "h": 8,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 71 "y": 84
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 32, "id": 32,
@ -1511,7 +1379,7 @@
"h": 7, "h": 7,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 79 "y": 92
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 28, "id": 28,
@ -1624,7 +1492,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 86 "y": 99
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 12, "id": 12,
@ -1718,7 +1586,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 86 "y": 99
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 26, "id": 26,
@ -1809,7 +1677,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 94 "y": 107
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 14, "id": 14,
@ -1895,7 +1763,7 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 102 "y": 115
}, },
"id": 50, "id": 50,
"panels": [], "panels": [],
@ -1914,7 +1782,7 @@
"h": 8, "h": 8,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 103 "y": 116
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 56, "id": 56,
@ -2009,7 +1877,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 111 "y": 124
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 52, "id": 52,
@ -2102,7 +1970,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 111 "y": 124
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 54, "id": 54,
@ -2195,7 +2063,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 119 "y": 132
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 58, "id": 58,
@ -2288,7 +2156,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 119 "y": 132
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 60, "id": 60,
@ -2376,7 +2244,7 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 127 "y": 140
}, },
"id": 62, "id": 62,
"panels": [], "panels": [],
@ -2395,7 +2263,7 @@
"h": 8, "h": 8,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 128 "y": 141
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 75, "id": 75,
@ -2490,7 +2358,7 @@
"h": 8, "h": 8,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 136 "y": 149
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 66, "id": 66,
@ -2651,7 +2519,7 @@
"h": 8, "h": 8,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 144 "y": 157
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 71, "id": 71,
@ -2797,7 +2665,7 @@
"h": 8, "h": 8,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 152 "y": 165
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 72, "id": 72,
@ -2929,7 +2797,7 @@
"h": 7, "h": 7,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 160 "y": 173
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 73, "id": 73,
@ -3029,7 +2897,7 @@
"h": 8, "h": 8,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 167 "y": 180
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 78, "id": 78,
@ -3122,7 +2990,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 175 "y": 188
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 80, "id": 80,
@ -3215,7 +3083,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 175 "y": 188
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 64, "id": 64,
@ -3303,7 +3171,7 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 183 "y": 196
}, },
"id": 68, "id": 68,
"panels": [], "panels": [],
@ -3322,7 +3190,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 184 "y": 197
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 70, "id": 70,
@ -3415,7 +3283,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 184 "y": 197
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 76, "id": 76,
@ -3503,7 +3371,7 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 192 "y": 205
}, },
"id": 16, "id": 16,
"panels": [], "panels": [],
@ -3522,7 +3390,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 193 "y": 206
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 18, "id": 18,
@ -3613,7 +3481,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 193 "y": 206
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 24, "id": 24,
@ -3704,7 +3572,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 201 "y": 214
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 35, "id": 35,
@ -3795,7 +3663,7 @@
"h": 8, "h": 8,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 201 "y": 214
}, },
"hiddenSeries": false, "hiddenSeries": false,
"id": 8, "id": 8,
@ -3881,7 +3749,7 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 209 "y": 222
}, },
"id": 40, "id": 40,
"panels": [ "panels": [
@ -3961,12 +3829,12 @@
"list": [] "list": []
}, },
"time": { "time": {
"from": "now-7d", "from": "now-5m",
"to": "now" "to": "now"
}, },
"timepicker": {}, "timepicker": {},
"timezone": "", "timezone": "",
"title": "NewsBlur", "title": "NewsBlur",
"uid": "T86VjXrG2", "uid": "T86VjXrG2",
"version": 17 "version": 22
} }

View file

@ -238,19 +238,7 @@ scrape_configs:
- source_labels: ['__meta_consul_node'] - source_labels: ['__meta_consul_node']
target_label: instance target_label: instance
- job_name: 'redis state' - job_name: 'haproxy state'
consul_sd_configs: static_configs:
- server: 'consul.service.nyc1.consul:8500' - targets: ['localhost:5569']:
services: ['flask_metrics_redis']
relabel_configs:
- source_labels: ['__meta_consul_node']
target_label: instance
metrics_path: /state/
- job_name: 'mongo state'
consul_sd_configs:
- server: 'consul.service.nyc1.consul:8500'
services: ['flask_metrics_mongo']
relabel_configs:
- source_labels: ['__meta_consul_node']
target_label: instance
metrics_path: /state/ metrics_path: /state/

View file

@ -230,16 +230,9 @@ scrape_configs:
tls_config: tls_config:
insecure_skip_verify: true insecure_skip_verify: true
- job_name: 'redis state' - job_name: 'service state'
static_configs: static_configs:
- targets: ['flask_metrics_redis:5569'] - targets: ['flask_metrics_state:5569']
metrics_path: /state/
scheme: http
tls_config:
insecure_skip_verify: true
- job_name: 'mongo state'
static_configs:
- targets: ['flask_metrics_mongo:5569']
metrics_path: /state/ metrics_path: /state/
scheme: http scheme: http
tls_config: tls_config:

View file

@ -1,6 +1,5 @@
from flask import Flask, render_template, Response from flask import Flask, render_template, Response
import pymongo import pymongo
from flask_metrics.state_timeline import format_state_data, get_state
from newsblur_web import settings from newsblur_web import settings
import sentry_sdk import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration from sentry_sdk.integrations.flask import FlaskIntegration
@ -166,20 +165,6 @@ def page_queues():
html_body = render_template('prometheus_data.html', **context) html_body = render_template('prometheus_data.html', **context)
return Response(html_body, content_type="text/plain") 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__": if __name__ == "__main__":
print(" ---> Starting NewsBlur Flask Metrics server...") print(" ---> Starting NewsBlur Flask Metrics server...")

View file

@ -1,7 +1,19 @@
from flask import render_template from flask import Flask, render_template, Response
import requests import requests
from requests.auth import HTTPBasicAuth from requests.auth import HTTPBasicAuth
from newsblur_web import settings
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
if settings.FLASK_SENTRY_DSN is not None:
sentry_sdk.init(
dsn=settings.FLASK_SENTRY_DSN,
integrations=[FlaskIntegration()],
traces_sample_rate=1.0,
)
app = Flask(__name__)
STATUS_MAPPING = { STATUS_MAPPING = {
"UNK": 0, # unknown "UNK": 0, # unknown
@ -27,16 +39,32 @@ def format_state_data(label, data):
formatted_data[k] = f'{label}{{servername="{k}"}} {STATUS_MAPPING[v.strip()]}' formatted_data[k] = f'{label}{{servername="{k}"}} {STATUS_MAPPING[v.strip()]}'
return formatted_data return formatted_data
def get_state(backend_name): def get_state():
res = requests.get('https://newsblur.com:1936/;csv', auth=HTTPBasicAuth('gimmiestats', 'StatsGiver')) res = requests.get('https://newsblur.com:1936/;csv', auth=HTTPBasicAuth('gimmiestats', 'StatsGiver'))
lines = res.content.decode('utf-8').split('\n') lines = res.content.decode('utf-8').split('\n')
backends = [line.split(",") for line in lines if backend_name in line] backends = [line.split(",") for line in lines][1:]
check_status_index = lines[0].split(",").index('check_status') check_status_index = lines[0].split(",").index('check_status')
servername_index = lines[0].split(",").index('svname') servername_index = lines[0].split(",").index('svname')
data = {} data = {}
for backend_data in backends: for backend_data in backends:
if backend_data != [''] and backend_data[servername_index] != 'FRONTEND':
data[backend_data[servername_index]] = backend_data[check_status_index].replace("*", "") data[backend_data[servername_index]] = backend_data[check_status_index].replace("*", "")
return data return data
@app.route("/state/")
def state():
state_data = get_state()
formatted_data = format_state_data("state", state_data)
context = {
'chart_name': '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 State Timeline Flask Metrics server...")
app.run(host="0.0.0.0", port=5569)