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
- service_name: redis
file: /srv/newsblur/flask_metrics/flask_metrics_redis.py
- service_name: metrics
file: /srv/newsblur/flask_metrics/state_timeline.py
- name: Restart flask_metrics
become: yes
shell:

View file

@ -69,6 +69,20 @@ services:
- nginx
volumes:
- ${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:
container_name: elasticsearch_exporter
image: prometheuscommunity/elasticsearch-exporter:latest

View file

@ -144,8 +144,8 @@
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"h": 27,
"w": 24,
"x": 0,
"y": 0
},
@ -166,162 +166,26 @@
"targets": [
{
"exemplar": true,
"expr": "redis_state",
"expr": "state",
"interval": "",
"legendFormat": "{{ servername }}",
"refId": "A"
}
],
"title": "Redis State",
"title": "Service State",
"type": "state-timeline"
},
{
"datasource": "Prometheus",
"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,
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 8
"y": 27
},
"id": 46,
"panels": [],
"title": "Top Line",
"type": "row"
},
"panels": [
{
"aliasColors": {},
"bars": false,
@ -340,7 +204,7 @@
"h": 6,
"w": 8,
"x": 0,
"y": 9
"y": 28
},
"hiddenSeries": false,
"id": 22,
@ -453,7 +317,7 @@
"h": 6,
"w": 8,
"x": 8,
"y": 9
"y": 28
},
"hiddenSeries": false,
"id": 44,
@ -579,7 +443,7 @@
"h": 6,
"w": 8,
"x": 16,
"y": 9
"y": 28
},
"hiddenSeries": false,
"id": 38,
@ -676,6 +540,10 @@
"align": false,
"alignLevel": null
}
}
],
"title": "Top Line",
"type": "row"
},
{
"collapsed": false,
@ -684,7 +552,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 15
"y": 28
},
"id": 20,
"panels": [],
@ -711,7 +579,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 16
"y": 29
},
"hiddenSeries": false,
"id": 2,
@ -817,7 +685,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 16
"y": 29
},
"hiddenSeries": false,
"id": 6,
@ -913,7 +781,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 24
"y": 37
},
"id": 4,
"panels": [],
@ -950,7 +818,7 @@
"h": 21,
"w": 24,
"x": 0,
"y": 25
"y": 38
},
"id": 10,
"options": {
@ -1004,7 +872,7 @@
"h": 7,
"w": 24,
"x": 0,
"y": 46
"y": 59
},
"hiddenSeries": false,
"id": 48,
@ -1095,7 +963,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 53
"y": 66
},
"id": 42,
"panels": [],
@ -1114,7 +982,7 @@
"h": 9,
"w": 24,
"x": 0,
"y": 54
"y": 67
},
"hiddenSeries": false,
"id": 33,
@ -1217,7 +1085,7 @@
"h": 8,
"w": 24,
"x": 0,
"y": 63
"y": 76
},
"hiddenSeries": false,
"id": 30,
@ -1412,7 +1280,7 @@
"h": 8,
"w": 24,
"x": 0,
"y": 71
"y": 84
},
"hiddenSeries": false,
"id": 32,
@ -1511,7 +1379,7 @@
"h": 7,
"w": 24,
"x": 0,
"y": 79
"y": 92
},
"hiddenSeries": false,
"id": 28,
@ -1624,7 +1492,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 86
"y": 99
},
"hiddenSeries": false,
"id": 12,
@ -1718,7 +1586,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 86
"y": 99
},
"hiddenSeries": false,
"id": 26,
@ -1809,7 +1677,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 94
"y": 107
},
"hiddenSeries": false,
"id": 14,
@ -1895,7 +1763,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 102
"y": 115
},
"id": 50,
"panels": [],
@ -1914,7 +1782,7 @@
"h": 8,
"w": 24,
"x": 0,
"y": 103
"y": 116
},
"hiddenSeries": false,
"id": 56,
@ -2009,7 +1877,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 111
"y": 124
},
"hiddenSeries": false,
"id": 52,
@ -2102,7 +1970,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 111
"y": 124
},
"hiddenSeries": false,
"id": 54,
@ -2195,7 +2063,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 119
"y": 132
},
"hiddenSeries": false,
"id": 58,
@ -2288,7 +2156,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 119
"y": 132
},
"hiddenSeries": false,
"id": 60,
@ -2376,7 +2244,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 127
"y": 140
},
"id": 62,
"panels": [],
@ -2395,7 +2263,7 @@
"h": 8,
"w": 24,
"x": 0,
"y": 128
"y": 141
},
"hiddenSeries": false,
"id": 75,
@ -2490,7 +2358,7 @@
"h": 8,
"w": 24,
"x": 0,
"y": 136
"y": 149
},
"hiddenSeries": false,
"id": 66,
@ -2651,7 +2519,7 @@
"h": 8,
"w": 24,
"x": 0,
"y": 144
"y": 157
},
"hiddenSeries": false,
"id": 71,
@ -2797,7 +2665,7 @@
"h": 8,
"w": 24,
"x": 0,
"y": 152
"y": 165
},
"hiddenSeries": false,
"id": 72,
@ -2929,7 +2797,7 @@
"h": 7,
"w": 24,
"x": 0,
"y": 160
"y": 173
},
"hiddenSeries": false,
"id": 73,
@ -3029,7 +2897,7 @@
"h": 8,
"w": 24,
"x": 0,
"y": 167
"y": 180
},
"hiddenSeries": false,
"id": 78,
@ -3122,7 +2990,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 175
"y": 188
},
"hiddenSeries": false,
"id": 80,
@ -3215,7 +3083,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 175
"y": 188
},
"hiddenSeries": false,
"id": 64,
@ -3303,7 +3171,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 183
"y": 196
},
"id": 68,
"panels": [],
@ -3322,7 +3190,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 184
"y": 197
},
"hiddenSeries": false,
"id": 70,
@ -3415,7 +3283,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 184
"y": 197
},
"hiddenSeries": false,
"id": 76,
@ -3503,7 +3371,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 192
"y": 205
},
"id": 16,
"panels": [],
@ -3522,7 +3390,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 193
"y": 206
},
"hiddenSeries": false,
"id": 18,
@ -3613,7 +3481,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 193
"y": 206
},
"hiddenSeries": false,
"id": 24,
@ -3704,7 +3572,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 201
"y": 214
},
"hiddenSeries": false,
"id": 35,
@ -3795,7 +3663,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 201
"y": 214
},
"hiddenSeries": false,
"id": 8,
@ -3881,7 +3749,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 209
"y": 222
},
"id": 40,
"panels": [
@ -3961,12 +3829,12 @@
"list": []
},
"time": {
"from": "now-7d",
"from": "now-5m",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "NewsBlur",
"uid": "T86VjXrG2",
"version": 17
"version": 22
}

View file

@ -238,19 +238,7 @@ scrape_configs:
- source_labels: ['__meta_consul_node']
target_label: instance
- job_name: 'redis state'
consul_sd_configs:
- server: 'consul.service.nyc1.consul:8500'
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
- job_name: 'haproxy state'
static_configs:
- targets: ['localhost:5569']:
metrics_path: /state/

View file

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

View file

@ -1,6 +1,5 @@
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
@ -166,20 +165,6 @@ 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...")

View file

@ -1,7 +1,19 @@
from flask import render_template
from flask import Flask, render_template, Response
import requests
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 = {
"UNK": 0, # unknown
@ -27,16 +39,32 @@ def format_state_data(label, data):
formatted_data[k] = f'{label}{{servername="{k}"}} {STATUS_MAPPING[v.strip()]}'
return formatted_data
def get_state(backend_name):
def get_state():
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]
backends = [line.split(",") for line in lines][1:]
check_status_index = lines[0].split(",").index('check_status')
servername_index = lines[0].split(",").index('svname')
data = {}
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("*", "")
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)