mirror of
https://github.com/viq/NewsBlur.git
synced 2025-04-13 09:38:09 +00:00
211 lines
7.4 KiB
Python
211 lines
7.4 KiB
Python
import pymongo
|
|
import sentry_sdk
|
|
from flask import Flask, Response, render_template
|
|
from sentry_sdk.integrations.flask import FlaskIntegration
|
|
|
|
from newsblur_web import settings
|
|
|
|
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__)
|
|
|
|
if settings.DOCKERBUILD:
|
|
connection = pymongo.MongoClient(f"mongodb://{settings.MONGO_DB['host']}")
|
|
else:
|
|
connection = pymongo.MongoClient(
|
|
f"mongodb://{settings.MONGO_DB['username']}:{settings.MONGO_DB['password']}@{settings.SERVER_NAME}.node.consul/?authSource=admin"
|
|
)
|
|
|
|
MONGO_HOST = settings.SERVER_NAME
|
|
|
|
|
|
@app.route("/objects/")
|
|
def objects():
|
|
try:
|
|
stats = connection.newsblur.command("dbstats")
|
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
|
return Response(f"Server selection timeout: {e}", 500)
|
|
except pymongo.errors.OperationFailure as e:
|
|
return Response(f"Operation failure: {e}", 500)
|
|
except pymongo.errors.NotMasterError as e:
|
|
return Response(f"NotMaster error: {e}", 500)
|
|
data = dict(objects=stats["objects"])
|
|
formatted_data = {}
|
|
for k, v in data.items():
|
|
formatted_data[k] = f'mongo_objects{{db="{MONGO_HOST}"}} {v}'
|
|
|
|
context = {
|
|
"data": formatted_data,
|
|
"chart_name": "objects",
|
|
"chart_type": "gauge",
|
|
}
|
|
html_body = render_template("prometheus_data.html", **context)
|
|
return Response(html_body, content_type="text/plain")
|
|
|
|
|
|
@app.route("/mongo-replset-lag/")
|
|
def repl_set_lag():
|
|
def _get_oplog_length():
|
|
oplog = connection.rs.command("printReplicationInfo")
|
|
last_op = oplog.find({}, {"ts": 1}).sort([("$natural", -1)]).limit(1)[0]["ts"].time
|
|
first_op = oplog.find({}, {"ts": 1}).sort([("$natural", 1)]).limit(1)[0]["ts"].time
|
|
oplog_length = last_op - first_op
|
|
return oplog_length
|
|
|
|
def _get_max_replication_lag():
|
|
PRIMARY_STATE = 1
|
|
SECONDARY_STATE = 2
|
|
status = connection.admin.command("replSetGetStatus")
|
|
members = status["members"]
|
|
primary_optime = None
|
|
oldest_secondary_optime = None
|
|
for member in members:
|
|
member_state = member["state"]
|
|
optime = member["optime"]
|
|
if member_state == PRIMARY_STATE:
|
|
primary_optime = optime["ts"].time
|
|
elif member_state == SECONDARY_STATE:
|
|
if not oldest_secondary_optime or optime["ts"].time < oldest_secondary_optime:
|
|
oldest_secondary_optime = optime["ts"].time
|
|
|
|
if not primary_optime or not oldest_secondary_optime:
|
|
raise Exception("Replica set is not healthy")
|
|
|
|
return primary_optime - oldest_secondary_optime
|
|
|
|
try:
|
|
# no such item for Cursor instance
|
|
oplog_length = _get_oplog_length()
|
|
# not running with --replSet
|
|
replication_lag = _get_max_replication_lag()
|
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
|
return Response(f"Server selection timeout: {e}", 500)
|
|
except pymongo.errors.OperationFailure as e:
|
|
return Response(f"Operation failure: {e}", 500)
|
|
except pymongo.errors.NotMasterError as e:
|
|
return Response(f"NotMaster error: {e}", 500)
|
|
|
|
formatted_data = {}
|
|
for k, v in oplog_length.items():
|
|
formatted_data[k] = f'mongo_oplog{{type="length", db="{MONGO_HOST}"}} {v}'
|
|
for k, v in replication_lag.items():
|
|
formatted_data[k] = f'mongo_oplog{{type="lag", db="{MONGO_HOST}"}} {v}'
|
|
|
|
context = {
|
|
"data": formatted_data,
|
|
"chart_name": "oplog_metrics",
|
|
"chart_type": "gauge",
|
|
}
|
|
html_body = render_template("prometheus_data.html", **context)
|
|
return Response(html_body, content_type="text/plain")
|
|
|
|
|
|
@app.route("/size/")
|
|
def size():
|
|
try:
|
|
stats = connection.newsblur.command("dbstats")
|
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
|
return Response(f"Server selection timeout: {e}", 500)
|
|
except pymongo.errors.OperationFailure as e:
|
|
return Response(f"Operation failure: {e}", 500)
|
|
except pymongo.errors.NotMasterError as e:
|
|
return Response(f"NotMaster error: {e}", 500)
|
|
data = dict(size=stats["fsUsedSize"])
|
|
formatted_data = {}
|
|
for k, v in data.items():
|
|
formatted_data[k] = f'mongo_db_size{{db="{MONGO_HOST}"}} {v}'
|
|
|
|
context = {
|
|
"data": formatted_data,
|
|
"chart_name": "db_size_bytes",
|
|
"chart_type": "gauge",
|
|
}
|
|
html_body = render_template("prometheus_data.html", **context)
|
|
return Response(html_body, content_type="text/plain")
|
|
|
|
|
|
@app.route("/ops/")
|
|
def ops():
|
|
try:
|
|
status = connection.admin.command("serverStatus")
|
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
|
return Response(f"Server selection timeout: {e}", 500)
|
|
except pymongo.errors.OperationFailure as e:
|
|
return Response(f"Operation failure: {e}", 500)
|
|
except pymongo.errors.NotMasterError as e:
|
|
return Response(f"NotMaster error: {e}", 500)
|
|
data = dict((q, status["opcounters"][q]) for q in status["opcounters"].keys())
|
|
|
|
formatted_data = {}
|
|
for k, v in data.items():
|
|
formatted_data[k] = f'mongo_ops{{type="{k}", db="{MONGO_HOST}"}} {v}'
|
|
|
|
context = {
|
|
"data": formatted_data,
|
|
"chart_name": "ops",
|
|
"chart_type": "gauge",
|
|
}
|
|
html_body = render_template("prometheus_data.html", **context)
|
|
return Response(html_body, content_type="text/plain")
|
|
|
|
|
|
@app.route("/page-faults/")
|
|
def page_faults():
|
|
try:
|
|
status = connection.admin.command("serverStatus")
|
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
|
return Response(f"Server selection timeout: {e}", 500)
|
|
except pymongo.errors.OperationFailure as e:
|
|
return Response(f"Operation failure: {e}", 500)
|
|
except pymongo.errors.NotMasterError as e:
|
|
return Response(f"NotMaster error: {e}", 500)
|
|
try:
|
|
value = status["extra_info"]["page_faults"]
|
|
except KeyError:
|
|
value = "U"
|
|
data = dict(page_faults=value)
|
|
formatted_data = {}
|
|
for k, v in data.items():
|
|
formatted_data[k] = f'mongo_page_faults{{db="{MONGO_HOST}"}} {v}'
|
|
|
|
context = {
|
|
"data": formatted_data,
|
|
"chart_name": "page_faults",
|
|
"chart_type": "counter",
|
|
}
|
|
html_body = render_template("prometheus_data.html", **context)
|
|
return Response(html_body, content_type="text/plain")
|
|
|
|
|
|
@app.route("/page-queues/")
|
|
def page_queues():
|
|
try:
|
|
status = connection.admin.command("serverStatus")
|
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
|
return Response(f"Server selection timeout: {e}", 500)
|
|
except pymongo.errors.OperationFailure as e:
|
|
return Response(f"Operation failure: {e}", 500)
|
|
except pymongo.errors.NotMasterError as e:
|
|
return Response(f"NotMaster error: {e}", 500)
|
|
data = dict((q, status["globalLock"]["currentQueue"][q]) for q in ("readers", "writers"))
|
|
formatted_data = {}
|
|
for k, v in data.items():
|
|
formatted_data[k] = f'mongo_page_queues{{type="{k}", db="{MONGO_HOST}"}} {v}'
|
|
|
|
context = {
|
|
"data": formatted_data,
|
|
"chart_name": "queues",
|
|
"chart_type": "gauge",
|
|
}
|
|
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)
|