diff --git a/apps/grafana/1_General.json b/apps/grafana/1_General.json
new file mode 100644
index 000000000..b84dcb3df
--- /dev/null
+++ b/apps/grafana/1_General.json
@@ -0,0 +1,1319 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ },
+ {
+ "datasource": "Influx",
+ "enable": true,
+ "iconColor": "#C0C6BE",
+ "iconSize": 13,
+ "lineColor": "rgba(255, 96, 96, 0.592157)",
+ "name": "Global Events",
+ "query": "select title, text from events where $timeFilter and realm =~ /$realm$/",
+ "showLine": true,
+ "textColumn": "text",
+ "titleColumn": "title"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": 5,
+ "iteration": 1595020261725,
+ "links": [],
+ "panels": [
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 4,
+ "x": 0,
+ "y": 0
+ },
+ "hideTimeOverride": false,
+ "id": 5,
+ "interval": null,
+ "isNew": true,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": false
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "auto"
+ ],
+ "type": "time"
+ }
+ ],
+ "measurement": "online_players",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Online players",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 5,
+ "x": 4,
+ "y": 0
+ },
+ "id": 6,
+ "interval": null,
+ "isNew": true,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": false
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "auto"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "update_time_diff",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": "1m",
+ "title": "Update diff (avg)",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 5,
+ "x": 9,
+ "y": 0
+ },
+ "id": 7,
+ "interval": null,
+ "isNew": true,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": false
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "auto"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "update_time_diff",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": "5m",
+ "title": "Update diff (avg)",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": false,
+ "colors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 5,
+ "x": 14,
+ "y": 0
+ },
+ "id": 8,
+ "interval": null,
+ "isNew": true,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": false
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "auto"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "update_time_diff",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"update_time_diff\" WHERE (\"realm\" =~ /^$realm$/) AND $timeFilter GROUP BY time($__interval) fill(null)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": "",
+ "timeFrom": "15m",
+ "timeShift": null,
+ "title": "Update diff (avg)",
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "avg"
+ },
+ {
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 5,
+ "x": 19,
+ "y": 0
+ },
+ "id": 12,
+ "options": {
+ "showLabels": false,
+ "showTime": true,
+ "sortOrder": "Descending",
+ "wrapLogMessage": true
+ },
+ "targets": [
+ {
+ "groupBy": [],
+ "measurement": "events",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT \"title\" FROM \"events\" WHERE (\"realm\" =~ /^$realm$/) order by \"time\" desc limit 2",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "table",
+ "select": [
+ [
+ {
+ "params": [
+ "title"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Recent events",
+ "transformations": [
+ {
+ "id": "organize",
+ "options": {
+ "excludeByName": {
+ "title": false
+ },
+ "indexByName": {},
+ "renameByName": {
+ "Time": "",
+ "title": ""
+ }
+ }
+ }
+ ],
+ "type": "logs"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "grid": {
+ "leftLogBase": 1,
+ "leftMax": null,
+ "leftMin": null,
+ "rightLogBase": 1,
+ "rightMax": null,
+ "rightMin": null
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 3
+ },
+ "hiddenSeries": false,
+ "id": 1,
+ "isNew": true,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "links": [],
+ "nullPointMode": "connected",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Update diff",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "update_time_diff",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"update_time_diff\" WHERE (\"realm\" =~ /$realm$/) AND $timeFilter GROUP BY time($interval) fill(null)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Update diff",
+ "tooltip": {
+ "msResolution": false,
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "graph",
+ "x-axis": true,
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "y-axis": true,
+ "y_formats": [
+ "ms",
+ "short"
+ ],
+ "yaxes": [
+ {
+ "format": "ms",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 10
+ },
+ "hiddenSeries": false,
+ "id": 10,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "nullPointMode": "connected",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Map $tag_map_id",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "map_id"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "map_update_time_diff",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"map_update_time_diff\" WHERE (\"realm\" =~ /^$realm$/) AND $timeFilter GROUP BY time($__interval), \"map_id\" fill(none)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Map update",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ms",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "grid": {
+ "leftLogBase": 1,
+ "leftMax": null,
+ "leftMin": null,
+ "rightLogBase": 1,
+ "rightMax": null,
+ "rightMin": null
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 18
+ },
+ "hiddenSeries": false,
+ "id": 4,
+ "isNew": true,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "links": [],
+ "nullPointMode": "connected",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Online players",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "online_players",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"online_players\" WHERE \"realm\" =~ /$realm$/ AND $timeFilter GROUP BY time($interval) fill(null)",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Online players",
+ "tooltip": {
+ "msResolution": false,
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "graph",
+ "x-axis": true,
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "y-axis": true,
+ "y_formats": [
+ "short",
+ "short"
+ ],
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "grid": {
+ "leftLogBase": 1,
+ "leftMax": null,
+ "leftMin": null,
+ "rightLogBase": 1,
+ "rightMax": null,
+ "rightMin": null
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 25
+ },
+ "hiddenSeries": false,
+ "id": 3,
+ "isNew": true,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "links": [],
+ "nullPointMode": "connected",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [
+ {
+ "alias": "Logouts",
+ "transform": "negative-Y"
+ }
+ ],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Logins",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "player_events",
+ "policy": "default",
+ "query": "SELECT count(\"text\") FROM \"player_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'Login' AND $timeFilter GROUP BY time($interval) fill(0)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "text"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "count"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=",
+ "value": "Acore"
+ }
+ ]
+ },
+ {
+ "alias": "Logouts",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "policy": "default",
+ "query": "SELECT count(\"text\") FROM \"player_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'Logout' AND $timeFilter GROUP BY time($interval) fill(0)",
+ "rawQuery": true,
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Player login/logout",
+ "tooltip": {
+ "msResolution": false,
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "graph",
+ "x-axis": true,
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "y-axis": true,
+ "y_formats": [
+ "short",
+ "short"
+ ],
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ }
+ ],
+ "refresh": "1m",
+ "schemaVersion": 25,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "allFormat": "regex values",
+ "allValue": null,
+ "current": {
+ "selected": false,
+ "text": "Acore",
+ "value": "Acore"
+ },
+ "datasource": "Influx",
+ "definition": "",
+ "hide": 0,
+ "includeAll": false,
+ "label": null,
+ "multi": false,
+ "multiFormat": "regex values",
+ "name": "realm",
+ "options": [],
+ "query": "show tag values from events with key = realm",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tags": [],
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ }
+ ]
+ },
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "timepicker": {
+ "now": true,
+ "refresh_intervals": [
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "browser",
+ "title": "General info",
+ "uid": "BfuPZmZMz",
+ "version": 3
+}
\ No newline at end of file
diff --git a/apps/grafana/2_Maps.json b/apps/grafana/2_Maps.json
new file mode 100644
index 000000000..5da97972d
--- /dev/null
+++ b/apps/grafana/2_Maps.json
@@ -0,0 +1,691 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ },
+ {
+ "datasource": "Influx",
+ "enable": true,
+ "iconColor": "#C0C6BE",
+ "iconSize": 13,
+ "lineColor": "rgba(255, 96, 96, 0.592157)",
+ "name": "Global Events",
+ "query": "select title, text from events where $timeFilter and realm =~ /$realm$/",
+ "showLine": true,
+ "textColumn": "text",
+ "titleColumn": "title"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": 6,
+ "iteration": 1595939001794,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "grid": {},
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "hiddenSeries": false,
+ "id": 2,
+ "isNew": true,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "links": [],
+ "nullPointMode": "connected",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [
+ {
+ "alias": "Unload tile",
+ "transform": "negative-Y"
+ }
+ ],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Load tile",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "query": "SELECT count(\"title\") FROM \"map_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'LoadMapTile' AND $timeFilter GROUP BY time($interval) fill(0)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ },
+ {
+ "alias": "Unload tile",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "query": "SELECT count(\"title\") FROM \"map_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'UnloadMapTile' AND $timeFilter GROUP BY time($interval) fill(0)",
+ "rawQuery": true,
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Map",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "grid": {},
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 7
+ },
+ "hiddenSeries": false,
+ "id": 1,
+ "isNew": true,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "links": [],
+ "nullPointMode": "connected",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Pathfinding queries",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "query": "SELECT count(\"title\") FROM \"mmap_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'CalculatePath' AND $timeFilter GROUP BY time($interval) fill(0)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "MMap",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 5,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 14
+ },
+ "hiddenSeries": false,
+ "id": 4,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Map $tag_map_id Instance $tag_map_instanceid",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "map_id"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "map_instanceid"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "map_creatures",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Creatures",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 5,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 22
+ },
+ "hiddenSeries": false,
+ "id": 5,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Map $tag_map_id Instance $tag_map_instanceid",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "map_id"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "map_instanceid"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "map_gameobjects",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Gameobjects",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ }
+ ],
+ "refresh": "1m",
+ "schemaVersion": 25,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "allFormat": "regex values",
+ "allValue": null,
+ "current": {
+ "text": "Acore",
+ "value": "Acore"
+ },
+ "datasource": "Influx",
+ "definition": "",
+ "hide": 0,
+ "includeAll": false,
+ "label": null,
+ "multi": false,
+ "multiFormat": "regex values",
+ "name": "realm",
+ "options": [],
+ "query": "show tag values from events with key = realm",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tags": [],
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ }
+ ]
+ },
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "timepicker": {
+ "now": true,
+ "refresh_intervals": [
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "browser",
+ "title": "Maps, vmaps and mmaps",
+ "uid": "6IhqWiWGz",
+ "version": 2
+}
\ No newline at end of file
diff --git a/apps/grafana/3_Network.json b/apps/grafana/3_Network.json
new file mode 100644
index 000000000..327b7b5e1
--- /dev/null
+++ b/apps/grafana/3_Network.json
@@ -0,0 +1,280 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ },
+ {
+ "datasource": "Influx",
+ "enable": true,
+ "iconColor": "#C0C6BE",
+ "iconSize": 13,
+ "lineColor": "rgba(255, 96, 96, 0.592157)",
+ "name": "Global Events",
+ "query": "select title, text from events where $timeFilter and realm =~ /$realm$/",
+ "showLine": true,
+ "textColumn": "text",
+ "titleColumn": "title"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": 7,
+ "iteration": 1595939048589,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "grid": {},
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "hiddenSeries": false,
+ "id": 1,
+ "isNew": true,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "links": [],
+ "nullPointMode": "connected",
+ "options": {
+ "dataLinks": []
+ },
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Processed packets",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "processed_packets",
+ "query": "SELECT sum(\"value\") FROM \"processed_packets\" WHERE \"realm\" =~ /$realm$/ AND $timeFilter GROUP BY time($interval) fill(0)",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "sum"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/$realm$/"
+ }
+ ]
+ },
+ {
+ "alias": "Processed packets / mean per session",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "processed_packets",
+ "query": "SELECT mean(\"value\") FROM \"processed_packets\" WHERE \"realm\" =~ /$realm$/ AND $timeFilter GROUP BY time($interval) fill(0)",
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Processed packets",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ }
+ ],
+ "refresh": "1m",
+ "schemaVersion": 25,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "allFormat": "regex values",
+ "allValue": null,
+ "current": {
+ "text": "Acore",
+ "value": "Acore"
+ },
+ "datasource": "Influx",
+ "definition": "",
+ "hide": 0,
+ "includeAll": false,
+ "label": null,
+ "multi": false,
+ "multiFormat": "regex values",
+ "name": "realm",
+ "options": [],
+ "query": "show tag values from events with key = realm",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tags": [],
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ }
+ ]
+ },
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "timepicker": {
+ "now": true,
+ "refresh_intervals": [
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "browser",
+ "title": "Network",
+ "uid": "_QtkMmWMk",
+ "version": 2
+}
\ No newline at end of file
diff --git a/apps/grafana/4_Performance_profiling.json b/apps/grafana/4_Performance_profiling.json
new file mode 100644
index 000000000..04363a540
--- /dev/null
+++ b/apps/grafana/4_Performance_profiling.json
@@ -0,0 +1,1677 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ },
+ {
+ "datasource": "Influx",
+ "enable": true,
+ "hide": false,
+ "iconColor": "#c0c6be",
+ "limit": 100,
+ "name": "Global Events",
+ "query": "select title, text from events where $timeFilter and realm =~ /$realm$/",
+ "showIn": 0,
+ "tags": [],
+ "textColumn": "text",
+ "type": "tags"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": 15,
+ "iteration": 1626984445687,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Influx",
+ "editable": true,
+ "error": false,
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "grid": {
+ "leftLogBase": 1,
+ "leftMax": null,
+ "leftMin": null,
+ "rightLogBase": 1,
+ "rightMax": null,
+ "rightMin": null
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "hiddenSeries": false,
+ "id": 4,
+ "isNew": true,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "links": [],
+ "nullPointMode": "connected",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Update diff",
+ "dsType": "influxdb",
+ "groupBy": [
+ {
+ "params": [
+ "$interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "update_time_diff",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"update_time_diff\" WHERE (\"realm\" =~ /$realm$/) AND $timeFilter GROUP BY time($interval) fill(null)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Update diff",
+ "tooltip": {
+ "msResolution": false,
+ "shared": true,
+ "sort": 0,
+ "value_type": "cumulative"
+ },
+ "type": "graph",
+ "x-axis": true,
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "y-axis": true,
+ "y_formats": [
+ "ms",
+ "short"
+ ],
+ "yaxes": [
+ {
+ "format": "ms",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 7
+ },
+ "hiddenSeries": false,
+ "id": 6,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "nullPointMode": "connected",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Total",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "world_update_time_total",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"map_update_time_diff\" WHERE (\"realm\" =~ /^$realm$/) AND $timeFilter GROUP BY time($__interval), \"map_id\" fill(none)",
+ "rawQuery": false,
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ },
+ {
+ "alias": "$tag_type",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "type"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "world_update_time",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"map_update_time_diff\" WHERE (\"realm\" =~ /^$realm$/) AND $timeFilter GROUP BY time($__interval), \"map_id\" fill(none)",
+ "rawQuery": false,
+ "refId": "C",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "World Update - Split",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ms",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 10,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 7
+ },
+ "hiddenSeries": false,
+ "id": 7,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 0,
+ "nullPointMode": "connected",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "$tag_type",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "type"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "world_update_time",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"map_update_time_diff\" WHERE (\"realm\" =~ /^$realm$/) AND $timeFilter GROUP BY time($__interval), \"map_id\" fill(none)",
+ "rawQuery": false,
+ "refId": "C",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ },
+ {
+ "condition": "AND",
+ "key": "parent_type",
+ "operator": "!~",
+ "value": "/./"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "World Update - Stacked",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ms",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 15
+ },
+ "hiddenSeries": false,
+ "id": 2,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "nullPointMode": "connected",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Map $tag_map_id",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "map_id"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "map_update_time_diff",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"map_update_time_diff\" WHERE (\"realm\" =~ /^$realm$/) AND $timeFilter GROUP BY time($__interval), \"map_id\" fill(none)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Map update - Split",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ms",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 10,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 15
+ },
+ "hiddenSeries": false,
+ "id": 5,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 0,
+ "nullPointMode": "connected",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Map $tag_map_id",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "map_id"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "map_update_time_diff",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"map_update_time_diff\" WHERE (\"realm\" =~ /^$realm$/) AND $timeFilter GROUP BY time($__interval), \"map_id\" fill(none)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Map update - Stacked",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ms",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 23
+ },
+ "hiddenSeries": false,
+ "id": 9,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": true,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "max",
+ "sortDesc": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 2,
+ "nullPointMode": "connected",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Account ID $tag_account_id",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "account_id"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "world_update_sessions_time",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"map_update_time_diff\" WHERE (\"realm\" =~ /^$realm$/) AND $timeFilter GROUP BY time($__interval), \"map_id\" fill(none)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Update sessions - Split",
+ "tooltip": {
+ "shared": true,
+ "sort": 2,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ms",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": true,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 23
+ },
+ "hiddenSeries": false,
+ "id": 11,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "hideEmpty": true,
+ "hideZero": true,
+ "max": true,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "max",
+ "sortDesc": true,
+ "total": false,
+ "values": true
+ },
+ "lines": false,
+ "linewidth": 2,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 2,
+ "points": true,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Opcode $tag_opcode",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "opcode"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "worldsession_update_opcode_time",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT max(\"value\") FROM \"worldsession_update_opcode_time\" WHERE (\"realm\" =~ /^$realm$/) AND $timeFilter GROUP BY time($__interval), \"opcode\" fill(null)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Opcode handlers - Split",
+ "tooltip": {
+ "shared": false,
+ "sort": 2,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "ms",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 5,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 31
+ },
+ "hiddenSeries": false,
+ "id": 13,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Map $tag_map_id Instance $tag_map_instanceid",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "map_id"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "map_instanceid"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "map_creatures",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Creatures",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "links": []
+ },
+ "overrides": []
+ },
+ "fill": 5,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 31
+ },
+ "hiddenSeries": false,
+ "id": 15,
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Map $tag_map_id Instance $tag_map_instanceid",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "map_id"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "map_instanceid"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "none"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "map_gameobjects",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Gameobjects",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 39
+ },
+ "hiddenSeries": false,
+ "id": 17,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 2,
+ "nullPointMode": "connected",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "8.0.3",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "alias": "Login DB",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "db_queue_login",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ },
+ {
+ "alias": "Character DB",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "hide": false,
+ "measurement": "db_queue_character",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ },
+ {
+ "alias": "World DB",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "hide": false,
+ "measurement": "db_queue_world",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "C",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "value"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "max"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "realm",
+ "operator": "=~",
+ "value": "/^$realm$/"
+ }
+ ]
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Database queue",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "$$hashKey": "object:102",
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "$$hashKey": "object:103",
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ }
+ ],
+ "refresh": "1m",
+ "schemaVersion": 30,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "allValue": null,
+ "current": {
+ "selected": true,
+ "text": "Acore",
+ "value": "Acore"
+ },
+ "datasource": "Influx",
+ "definition": "show tag values from events with key = realm",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": false,
+ "label": null,
+ "multi": false,
+ "name": "realm",
+ "options": [],
+ "query": "show tag values from events with key = realm",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ }
+ ]
+ },
+ "time": {
+ "from": "now-1h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ]
+ },
+ "timezone": "",
+ "title": "Performance profiling",
+ "uid": "IRRL03nMk",
+ "version": 9
+}
\ No newline at end of file
diff --git a/conf/dist/config.cmake b/conf/dist/config.cmake
index 728323112..5b8779b06 100644
--- a/conf/dist/config.cmake
+++ b/conf/dist/config.cmake
@@ -67,6 +67,8 @@ option(WITHOUT_GIT "Disable the GIT testing routines"
option(ENABLE_VMAP_CHECKS "Enable Checks relative to DisableMgr system on vmap" 1)
option(WITH_DYNAMIC_LINKING "Enable dynamic library linking." 0)
option(WITH_STRICT_DATABASE_TYPE_CHECKS "Enable strict checking of database field value accessors" 0)
+option(WITHOUT_METRICS "Disable metrics reporting (i.e. InfluxDB and Grafana)" 0)
+option(WITH_DETAILED_METRICS "Enable detailed metrics reporting (i.e. time each session takes to update)" 0)
IsDynamicLinkingRequired(WITH_DYNAMIC_LINKING_FORCED)
IsDynamicLinkingModulesRequired(WITH_DYNAMIC_LINKING_FORCED)
diff --git a/src/cmake/showoptions.cmake b/src/cmake/showoptions.cmake
index 0a9a8362c..8e8112140 100644
--- a/src/cmake/showoptions.cmake
+++ b/src/cmake/showoptions.cmake
@@ -156,6 +156,18 @@ if(WITH_STRICT_DATABASE_TYPE_CHECKS)
add_definitions(-DACORE_STRICT_DATABASE_TYPE_CHECKS)
endif()
+if(WITHOUT_METRICS)
+ message("")
+ message(" *** WITHOUT_METRICS - WARNING!")
+ message(" *** Please note that this will disable all metrics output (i.e. InfluxDB and Grafana)")
+ add_definitions(-DWITHOUT_METRICS)
+elseif (WITH_DETAILED_METRICS)
+ message("")
+ message(" *** WITH_DETAILED_METRICS - WARNING!")
+ message(" *** Please note that this will enable detailed metrics output (i.e. time each session takes to update)")
+ add_definitions(-DWITH_DETAILED_METRICS)
+endif()
+
if(BUILD_SHARED_LIBS)
message("")
message(" *** WITH_DYNAMIC_LINKING - INFO!")
diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp
index 5a4b3b81d..7cf745090 100644
--- a/src/common/Collision/Maps/MapTree.cpp
+++ b/src/common/Collision/Maps/MapTree.cpp
@@ -18,6 +18,7 @@
#include "MapTree.h"
#include "Errors.h"
#include "Log.h"
+#include "Metric.h"
#include "ModelInstance.h"
#include "VMapDefinitions.h"
#include "VMapMgr2.h"
@@ -452,6 +453,10 @@ namespace VMAP
{
iLoadedTiles[packTileID(tileX, tileY)] = false;
}
+
+ METRIC_EVENT("map_events", "LoadMapTile",
+ "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
+
return result;
}
@@ -518,6 +523,9 @@ namespace VMAP
}
}
iLoadedTiles.erase(tile);
+
+ METRIC_EVENT("map_events", "UnloadMapTile",
+ "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
}
void StaticMapTree::GetModelInstances(ModelInstance*& models, uint32& count)
diff --git a/src/common/Metric/Metric.cpp b/src/common/Metric/Metric.cpp
new file mode 100644
index 000000000..fb0e39a8c
--- /dev/null
+++ b/src/common/Metric/Metric.cpp
@@ -0,0 +1,343 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+#include "Metric.h"
+#include "Common.h"
+#include "Config.h"
+#include "DeadlineTimer.h"
+#include "Log.h"
+#include "Strand.h"
+#include "Util.h"
+#include "Tokenize.h"
+#include
+#include
+
+Metric::Metric()
+{
+}
+
+Metric::~Metric()
+{
+}
+
+Metric* Metric::instance()
+{
+ static Metric instance;
+ return &instance;
+}
+
+void Metric::Initialize(std::string const& realmName, Acore::Asio::IoContext& ioContext, std::function overallStatusLogger)
+{
+ _dataStream = std::make_unique();
+ _realmName = FormatInfluxDBTagValue(realmName);
+ _batchTimer = std::make_unique(ioContext);
+ _overallStatusTimer = std::make_unique(ioContext);
+ _overallStatusLogger = overallStatusLogger;
+ LoadFromConfigs();
+}
+
+bool Metric::Connect()
+{
+ auto& stream = static_cast(GetDataStream());
+ stream.connect(_hostname, _port);
+
+ auto error = stream.error();
+ if (error)
+ {
+ FMT_LOG_ERROR("metric", "Error connecting to '{}:{}', disabling Metric. Error message: {}",
+ _hostname, _port, error.message());
+
+ _enabled = false;
+ return false;
+ }
+
+ stream.clear();
+ return true;
+}
+
+void Metric::LoadFromConfigs()
+{
+ bool previousValue = _enabled;
+ _enabled = sConfigMgr->GetOption("Metric.Enable", false);
+ _updateInterval = sConfigMgr->GetOption("Metric.Interval", 10);
+
+ if (_updateInterval < 1)
+ {
+ FMT_LOG_ERROR("metric", "'Metric.Interval' config set to {}, overriding to 1.", _updateInterval);
+ _updateInterval = 1;
+ }
+
+ _overallStatusTimerInterval = sConfigMgr->GetOption("Metric.OverallStatusInterval", 1);
+ if (_overallStatusTimerInterval < 1)
+ {
+ FMT_LOG_ERROR("metric", "'Metric.OverallStatusInterval' config set to {}, overriding to 1.", _overallStatusTimerInterval);
+ _overallStatusTimerInterval = 1;
+ }
+
+ _thresholds.clear();
+ std::vector thresholdSettings = sConfigMgr->GetKeysByString("Metric.Threshold.");
+ for (std::string const& thresholdSetting : thresholdSettings)
+ {
+ int64 thresholdValue = sConfigMgr->GetOption(thresholdSetting, 0);
+ std::string thresholdName = thresholdSetting.substr(strlen("Metric.Threshold."));
+ _thresholds[thresholdName] = thresholdValue;
+ }
+
+ // Schedule a send at this point only if the config changed from Disabled to Enabled.
+ // Cancel any scheduled operation if the config changed from Enabled to Disabled.
+ if (_enabled && !previousValue)
+ {
+ std::string connectionInfo = sConfigMgr->GetOption("Metric.ConnectionInfo", "");
+ if (connectionInfo.empty())
+ {
+ FMT_LOG_ERROR("metric", "'Metric.ConnectionInfo' not specified in configuration file.");
+ return;
+ }
+
+ std::vector tokens = Acore::Tokenize(connectionInfo, ';', true);
+ if (tokens.size() != 3)
+ {
+ FMT_LOG_ERROR("metric", "'Metric.ConnectionInfo' specified with wrong format in configuration file.");
+ return;
+ }
+
+ _hostname.assign(tokens[0]);
+ _port.assign(tokens[1]);
+ _databaseName.assign(tokens[2]);
+ Connect();
+
+ ScheduleSend();
+ ScheduleOverallStatusLog();
+ }
+}
+
+void Metric::Update()
+{
+ if (_overallStatusTimerTriggered)
+ {
+ _overallStatusTimerTriggered = false;
+ _overallStatusLogger();
+ }
+}
+
+bool Metric::ShouldLog(std::string const& category, int64 value) const
+{
+ auto threshold = _thresholds.find(category);
+
+ if (threshold == _thresholds.end())
+ {
+ return false;
+ }
+
+ return value >= threshold->second;
+}
+
+void Metric::LogEvent(std::string const& category, std::string const& title, std::string const& description)
+{
+ using namespace std::chrono;
+
+ MetricData* data = new MetricData;
+ data->Category = category;
+ data->Timestamp = system_clock::now();
+ data->Type = METRIC_DATA_EVENT;
+ data->Title = title;
+ data->Text = description;
+
+ _queuedData.Enqueue(data);
+}
+
+void Metric::SendBatch()
+{
+ using namespace std::chrono;
+
+ std::stringstream batchedData;
+ MetricData* data;
+ bool firstLoop = true;
+
+ while (_queuedData.Dequeue(data))
+ {
+ if (!firstLoop)
+ batchedData << "\n";
+
+ batchedData << data->Category;
+ if (!_realmName.empty())
+ batchedData << ",realm=" << _realmName;
+
+ for (MetricTag const& tag : data->Tags)
+ batchedData << "," << tag.first << "=" << FormatInfluxDBTagValue(tag.second);
+
+ batchedData << " ";
+
+ switch (data->Type)
+ {
+ case METRIC_DATA_VALUE:
+ batchedData << "value=" << data->Value;
+ break;
+ case METRIC_DATA_EVENT:
+ batchedData << "title=\"" << data->Title << "\",text=\"" << data->Text << "\"";
+ break;
+ }
+
+ batchedData << " " << std::to_string(duration_cast(data->Timestamp.time_since_epoch()).count());
+
+ firstLoop = false;
+ delete data;
+ }
+
+ // Check if there's any data to send
+ if (batchedData.tellp() == std::streampos(0))
+ {
+ ScheduleSend();
+ return;
+ }
+
+ if (!GetDataStream().good() && !Connect())
+ return;
+
+ GetDataStream() << "POST " << "/write?db=" << _databaseName << " HTTP/1.1\r\n";
+ GetDataStream() << "Host: " << _hostname << ":" << _port << "\r\n";
+ GetDataStream() << "Accept: */*\r\n";
+ GetDataStream() << "Content-Type: application/octet-stream\r\n";
+ GetDataStream() << "Content-Transfer-Encoding: binary\r\n";
+
+ GetDataStream() << "Content-Length: " << std::to_string(batchedData.tellp()) << "\r\n\r\n";
+ GetDataStream() << batchedData.rdbuf();
+
+ std::string http_version;
+ GetDataStream() >> http_version;
+ unsigned int status_code = 0;
+ GetDataStream() >> status_code;
+
+ if (status_code != 204)
+ {
+ FMT_LOG_ERROR("metric", "Error sending data, returned HTTP code: {}", status_code);
+ }
+
+ // Read and ignore the status description
+ std::string status_description;
+ std::getline(GetDataStream(), status_description);
+
+ // Read headers
+ std::string header;
+
+ while (std::getline(GetDataStream(), header) && header != "\r")
+ {
+ if (header == "Connection: close\r")
+ {
+ static_cast(GetDataStream()).close();
+ }
+ }
+
+ ScheduleSend();
+}
+
+void Metric::ScheduleSend()
+{
+ if (_enabled)
+ {
+ _batchTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
+ _batchTimer->async_wait(std::bind(&Metric::SendBatch, this));
+ }
+ else
+ {
+ static_cast(GetDataStream()).close();
+ MetricData* data;
+
+ // Clear the queue
+ while (_queuedData.Dequeue(data))
+ {
+ delete data;
+ }
+ }
+}
+
+void Metric::Unload()
+{
+ // Send what's queued only if IoContext is stopped (so only on shutdown)
+ if (_enabled && Acore::Asio::get_io_context(*_batchTimer).stopped())
+ {
+ _enabled = false;
+ SendBatch();
+ }
+
+ _batchTimer->cancel();
+ _overallStatusTimer->cancel();
+}
+
+void Metric::ScheduleOverallStatusLog()
+{
+ if (_enabled)
+ {
+ _overallStatusTimer->expires_from_now(boost::posix_time::seconds(_overallStatusTimerInterval));
+ _overallStatusTimer->async_wait([this](const boost::system::error_code&)
+ {
+ _overallStatusTimerTriggered = true;
+ ScheduleOverallStatusLog();
+ });
+ }
+}
+
+std::string Metric::FormatInfluxDBValue(bool value)
+{
+ return value ? "t" : "f";
+}
+
+template
+std::string Metric::FormatInfluxDBValue(T value)
+{
+ return std::to_string(value) + 'i';
+}
+
+std::string Metric::FormatInfluxDBValue(std::string const& value)
+{
+ return '"' + boost::replace_all_copy(value, "\"", "\\\"") + '"';
+}
+
+std::string Metric::FormatInfluxDBValue(char const* value)
+{
+ return FormatInfluxDBValue(std::string(value));
+}
+
+std::string Metric::FormatInfluxDBValue(double value)
+{
+ return std::to_string(value);
+}
+
+std::string Metric::FormatInfluxDBValue(float value)
+{
+ return FormatInfluxDBValue(double(value));
+}
+
+std::string Metric::FormatInfluxDBTagValue(std::string const& value)
+{
+ // ToDo: should handle '=' and ',' characters too
+ return boost::replace_all_copy(value, " ", "\\ ");
+}
+
+std::string Metric::FormatInfluxDBValue(std::chrono::nanoseconds value)
+{
+ return FormatInfluxDBValue(std::chrono::duration_cast(value).count());
+}
+
+template AC_COMMON_API std::string Metric::FormatInfluxDBValue(int8);
+template AC_COMMON_API std::string Metric::FormatInfluxDBValue(uint8);
+template AC_COMMON_API std::string Metric::FormatInfluxDBValue(int16);
+template AC_COMMON_API std::string Metric::FormatInfluxDBValue(uint16);
+template AC_COMMON_API std::string Metric::FormatInfluxDBValue(int32);
+template AC_COMMON_API std::string Metric::FormatInfluxDBValue(uint32);
+template AC_COMMON_API std::string Metric::FormatInfluxDBValue(int64);
+template AC_COMMON_API std::string Metric::FormatInfluxDBValue(uint64);
diff --git a/src/common/Metric/Metric.h b/src/common/Metric/Metric.h
new file mode 100644
index 000000000..550630328
--- /dev/null
+++ b/src/common/Metric/Metric.h
@@ -0,0 +1,227 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+#ifndef METRIC_H__
+#define METRIC_H__
+
+#include "Define.h"
+#include "Duration.h"
+#include "MPSCQueue.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Acore::Asio
+{
+ class IoContext;
+ class DeadlineTimer;
+}
+
+enum MetricDataType
+{
+ METRIC_DATA_VALUE,
+ METRIC_DATA_EVENT
+};
+
+typedef std::pair MetricTag;
+
+struct MetricData
+{
+ std::string Category;
+ SystemTimePoint Timestamp;
+ MetricDataType Type;
+ std::vector Tags;
+
+ // LogValue-specific fields
+ std::string Value;
+
+ // LogEvent-specific fields
+ std::string Title;
+ std::string Text;
+};
+
+class AC_COMMON_API Metric
+{
+private:
+ std::iostream& GetDataStream() { return *_dataStream; }
+ std::unique_ptr _dataStream;
+ MPSCQueue _queuedData;
+ std::unique_ptr _batchTimer;
+ std::unique_ptr _overallStatusTimer;
+ int32 _updateInterval = 0;
+ int32 _overallStatusTimerInterval = 0;
+ bool _enabled = false;
+ bool _overallStatusTimerTriggered = false;
+ std::string _hostname;
+ std::string _port;
+ std::string _databaseName;
+ std::function _overallStatusLogger;
+ std::string _realmName;
+ std::unordered_map _thresholds;
+
+ bool Connect();
+ void SendBatch();
+ void ScheduleSend();
+ void ScheduleOverallStatusLog();
+
+ static std::string FormatInfluxDBValue(bool value);
+
+ template
+ static std::string FormatInfluxDBValue(T value);
+
+ static std::string FormatInfluxDBValue(std::string const& value);
+ static std::string FormatInfluxDBValue(char const* value);
+ static std::string FormatInfluxDBValue(double value);
+ static std::string FormatInfluxDBValue(float value);
+ static std::string FormatInfluxDBValue(std::chrono::nanoseconds value);
+
+ static std::string FormatInfluxDBTagValue(std::string const& value);
+
+ // ToDo: should format TagKey and FieldKey too in the same way as TagValue
+
+public:
+ Metric();
+ ~Metric();
+
+ static Metric* instance();
+
+ void Initialize(std::string const& realmName, Acore::Asio::IoContext& ioContext, std::function overallStatusLogger);
+ void LoadFromConfigs();
+ void Update();
+ bool ShouldLog(std::string const& category, int64 value) const;
+
+ template
+ void LogValue(std::string const& category, T value, std::vector tags)
+ {
+ using namespace std::chrono;
+
+ MetricData* data = new MetricData;
+ data->Category = category;
+ data->Timestamp = system_clock::now();
+ data->Type = METRIC_DATA_VALUE;
+ data->Value = FormatInfluxDBValue(value);
+ data->Tags = std::move(tags);
+
+ _queuedData.Enqueue(data);
+ }
+
+ void LogEvent(std::string const& category, std::string const& title, std::string const& description);
+
+ void Unload();
+ bool IsEnabled() const { return _enabled; }
+};
+
+#define sMetric Metric::instance()
+
+template
+class MetricStopWatch
+{
+public:
+ MetricStopWatch(LoggerType&& loggerFunc) :
+ _logger(std::forward(loggerFunc)),
+ _startTime(sMetric->IsEnabled() ? std::chrono::steady_clock::now() : TimePoint())
+ {
+ }
+
+ ~MetricStopWatch()
+ {
+ if (sMetric->IsEnabled())
+ _logger(_startTime);
+ }
+
+private:
+ LoggerType _logger;
+ TimePoint _startTime;
+};
+
+template
+MetricStopWatch MakeMetricStopWatch(LoggerType&& loggerFunc)
+{
+ return { std::forward(loggerFunc) };
+}
+
+#define METRIC_TAG(name, value) { name, value }
+
+#define METRIC_DO_CONCAT(a, b) a##b
+#define METRIC_CONCAT(a, b) METRIC_DO_CONCAT(a, b)
+#define METRIC_UNIQUE_NAME(name) METRIC_CONCAT(name, __LINE__)
+
+#if defined PERFORMANCE_PROFILING || defined WITHOUT_METRICS
+#define METRIC_EVENT(category, title, description) ((void)0)
+#define METRIC_VALUE(category, value, ...) ((void)0)
+#define METRIC_TIMER(category, ...) ((void)0)
+#define METRIC_DETAILED_EVENT(category, title, description) ((void)0)
+#define METRIC_DETAILED_TIMER(category, ...) ((void)0)
+#define METRIC_DETAILED_NO_THRESHOLD_TIMER(category, ...) ((void)0)
+#else
+#if AC_PLATFORM != AC_PLATFORM_WINDOWS
+#define METRIC_EVENT(category, title, description) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogEvent(category, title, description); \
+ } while (0)
+#define METRIC_VALUE(category, value, ...) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogValue(category, value, { __VA_ARGS__ }); \
+ } while (0)
+#else
+#define METRIC_EVENT(category, title, description) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogEvent(category, title, description); \
+ } while (0) \
+ __pragma(warning(pop))
+#define METRIC_VALUE(category, value, ...) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogValue(category, value, { __VA_ARGS__ }); \
+ } while (0) \
+ __pragma(warning(pop))
+#endif
+#define METRIC_TIMER(category, ...) \
+ MetricStopWatch METRIC_UNIQUE_NAME(__ac_metric_stop_watch) = MakeMetricStopWatch([&](TimePoint start) \
+ { \
+ sMetric->LogValue(category, std::chrono::steady_clock::now() - start, { __VA_ARGS__ }); \
+ });
+#if defined WITH_DETAILED_METRICS
+#define METRIC_DETAILED_TIMER(category, ...) \
+ MetricStopWatch METRIC_UNIQUE_NAME(__ac_metric_stop_watch) = MakeMetricStopWatch([&](TimePoint start) \
+ { \
+ int64 duration = int64(std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count()); \
+ if (sMetric->ShouldLog(category, duration)) \
+ sMetric->LogValue(category, duration, { __VA_ARGS__ }); \
+ });
+#define METRIC_DETAILED_NO_THRESHOLD_TIMER(category, ...) METRIC_TIMER(category, __VA_ARGS__)
+#define METRIC_DETAILED_EVENT(category, title, description) METRIC_EVENT(category, title, description)
+#else
+#define METRIC_DETAILED_EVENT(category, title, description) ((void)0)
+#define METRIC_DETAILED_TIMER(category, ...) ((void)0)
+#define METRIC_DETAILED_NO_THRESHOLD_TIMER(category, ...) ((void)0)
+#endif
+
+#endif
+
+#endif // METRIC_H__
diff --git a/src/common/Threading/PCQueue.h b/src/common/Threading/PCQueue.h
index 390e3b328..040eed1be 100644
--- a/src/common/Threading/PCQueue.h
+++ b/src/common/Threading/PCQueue.h
@@ -51,6 +51,11 @@ public:
return _queue.empty();
}
+ size_t Size() const
+ {
+ return _queue.size();
+ }
+
bool Pop(T& value)
{
std::lock_guard lock(_queueLock);
diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp
index b21d4ab2f..836d82432 100644
--- a/src/server/database/Database/DatabaseWorkerPool.cpp
+++ b/src/server/database/Database/DatabaseWorkerPool.cpp
@@ -430,6 +430,12 @@ void DatabaseWorkerPool::Enqueue(SQLOperation* op)
_queue->Push(op);
}
+template
+size_t DatabaseWorkerPool::QueueSize() const
+{
+ return _queue->Size();
+}
+
template
T* DatabaseWorkerPool::GetFreeConnection()
{
diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h
index 1b45a0394..86c2662fb 100644
--- a/src/server/database/Database/DatabaseWorkerPool.h
+++ b/src/server/database/Database/DatabaseWorkerPool.h
@@ -213,6 +213,8 @@ public:
#endif
}
+ size_t QueueSize() const;
+
private:
uint32 OpenConnections(InternalIndex type, uint8 numConnections);
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 4af1ca5ea..1c9308d3e 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -29,12 +29,14 @@
#include "GuildMgr.h"
#include "Language.h"
#include "Log.h"
+#include "Metric.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "Pet.h"
#include "Player.h"
#include "PlayerDump.h"
+#include "QueryHolder.h"
#include "Realm.h"
#include "ReputationMgr.h"
#include "ScriptMgr.h"
@@ -43,15 +45,14 @@
#include "SocialMgr.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
+#include "StringConvert.h"
+#include "Tokenize.h"
#include "Transport.h"
#include "UpdateMask.h"
#include "Util.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
-#include "QueryHolder.h"
-#include "StringConvert.h"
-#include "Tokenize.h"
#ifdef ELUNA
#include "LuaEngine.h"
@@ -1087,6 +1088,8 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
pCurrChar->RemoveAtLoginFlag(AT_LOGIN_FIRST);
sScriptMgr->OnFirstLogin(pCurrChar);
}
+
+ METRIC_EVENT("player_events", "Login", pCurrChar->GetName());
}
void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar)
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 08d265231..037ba3eb1 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -28,6 +28,7 @@
#include "LFGMgr.h"
#include "Map.h"
#include "MapInstanced.h"
+#include "Metric.h"
#include "Object.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
@@ -868,6 +869,14 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
HandleDelayedVisibility();
sScriptMgr->OnMapUpdate(this, t_diff);
+
+ METRIC_VALUE("map_creatures", uint64(GetObjectsStore().Size()),
+ METRIC_TAG("map_id", std::to_string(GetId())),
+ METRIC_TAG("map_instanceid", std::to_string(GetInstanceId())));
+
+ METRIC_VALUE("map_gameobjects", uint64(GetObjectsStore().Size()),
+ METRIC_TAG("map_id", std::to_string(GetId())),
+ METRIC_TAG("map_instanceid", std::to_string(GetInstanceId())));
}
void Map::HandleDelayedVisibility()
diff --git a/src/server/game/Maps/MapUpdater.cpp b/src/server/game/Maps/MapUpdater.cpp
index ea58d9d19..75e6bc996 100644
--- a/src/server/game/Maps/MapUpdater.cpp
+++ b/src/server/game/Maps/MapUpdater.cpp
@@ -19,6 +19,7 @@
#include "LFGMgr.h"
#include "Map.h"
#include "MapUpdater.h"
+#include "Metric.h"
class UpdateRequest
{
@@ -39,9 +40,11 @@ public:
void call() override
{
+ METRIC_TIMER("map_update_time_diff", METRIC_TAG("map_id", std::to_string(m_map.GetId())));
m_map.Update(m_diff, s_diff);
m_updater.update_finished();
}
+
private:
Map& m_map;
MapUpdater& m_updater;
diff --git a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp
index 6f2dd59ee..a465d9658 100644
--- a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp
@@ -20,6 +20,7 @@
#include "Geometry.h"
#include "Log.h"
#include "Map.h"
+#include "Metric.h"
#include "MMapFactory.h"
#include "MMapMgr.h"
#include "PathGenerator.h"
@@ -61,6 +62,8 @@ bool PathGenerator::CalculatePath(float x, float y, float z, float destX, float
if (!Acore::IsValidMapCoord(destX, destY, destZ) || !Acore::IsValidMapCoord(x, y, z))
return false;
+ METRIC_DETAILED_EVENT("mmap_events", "CalculatePath", "");
+
G3D::Vector3 dest(destX, destY, destZ);
SetEndPosition(dest);
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 0cc33d330..b048bbbd3 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -30,6 +30,7 @@
#include "Hyperlinks.h"
#include "Log.h"
#include "MapMgr.h"
+#include "Metric.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
@@ -319,6 +320,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
OpcodeClient opcode = static_cast(packet->GetOpcode());
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
+ METRIC_DETAILED_TIMER("worldsession_update_opcode_time", METRIC_TAG("opcode", opHandle->Name));
+
try
{
switch (opHandle->Status)
@@ -441,6 +444,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
_recvQueue.readd(requeuePackets.begin(), requeuePackets.end());
+ METRIC_VALUE("processed_packets", processedPackets);
+
if (!updater.ProcessUnsafe()) // <=> updater is of type MapSessionFilter
{
// Send time sync packet every 10s.
@@ -671,6 +676,8 @@ void WorldSession::LogoutPlayer(bool save)
//! Call script hook before deletion
sScriptMgr->OnPlayerLogout(_player);
+ METRIC_EVENT("player_events", "Logout", _player->GetName());
+
LOG_INFO("entities.player", "Account: %d (IP: %s) Logout Character:[%s] (%s) Level: %d",
GetAccountId(), GetRemoteAddress().c_str(), _player->GetName().c_str(), _player->GetGUID().ToString().c_str(), _player->getLevel());
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 2e9f23716..d776ed3f1 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -60,6 +60,7 @@
#include "LootMgr.h"
#include "MMapFactory.h"
#include "MapMgr.h"
+#include "Metric.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "OutdoorPvPMgr.h"
@@ -437,6 +438,7 @@ void World::LoadConfigSettings(bool reload)
}
sLog->LoadFromConfig();
+ sMetric->LoadFromConfigs();
}
// Set realm id and enable db logging
@@ -2107,10 +2109,13 @@ void World::SetInitialWorldSettings()
}
uint32 startupDuration = GetMSTimeDiffToNow(startupBegin);
+
LOG_INFO("server.loading", " ");
LOG_INFO("server.loading", "WORLD: World initialized in %u minutes %u seconds", (startupDuration / 60000), ((startupDuration % 60000) / 1000)); // outError for red color in console
LOG_INFO("server.loading", " ");
+ METRIC_EVENT("events", "World initialized", "World initialized in " + std::to_string(startupDuration / 60000) + " minutes " + std::to_string((startupDuration % 60000) / 1000) + " seconds");
+
if (sConfigMgr->isDryRun())
{
LOG_INFO("server.loading", "AzerothCore dry run completed, terminating.");
@@ -2199,6 +2204,8 @@ void World::LoadAutobroadcasts()
/// Update the World !
void World::Update(uint32 diff)
{
+ METRIC_TIMER("world_update_time_total");
+
m_updateTime = diff;
if (m_int_configs[CONFIG_INTERVAL_LOG_UPDATE])
@@ -2238,26 +2245,45 @@ void World::Update(uint32 diff)
///- Update the game time and check for shutdown time
_UpdateGameTime();
- /// Handle daily quests reset time
- if (m_gameTime > m_NextDailyQuestReset)
- ResetDailyQuests();
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Check quest reset times"));
- /// Handle weekly quests reset time
- if (m_gameTime > m_NextWeeklyQuestReset)
- ResetWeeklyQuests();
+ /// Handle daily quests reset time
+ if (m_gameTime > m_NextDailyQuestReset)
+ {
+ ResetDailyQuests();
+ }
- /// Handle monthly quests reset time
- if (m_gameTime > m_NextMonthlyQuestReset)
- ResetMonthlyQuests();
+ /// Handle weekly quests reset time
+ if (m_gameTime > m_NextWeeklyQuestReset)
+ {
+ ResetWeeklyQuests();
+ }
+
+ /// Handle monthly quests reset time
+ if (m_gameTime > m_NextMonthlyQuestReset)
+ {
+ ResetMonthlyQuests();
+ }
+ }
if (m_gameTime > m_NextRandomBGReset)
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Reset random BG"));
ResetRandomBG();
+ }
if (m_gameTime > m_NextCalendarOldEventsDeletionTime)
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Delete old calendar events"));
CalendarDeleteOldEvents();
+ }
if (m_gameTime > m_NextGuildReset)
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Reset guild cap"));
ResetGuildCap();
+ }
// pussywizard:
// acquire mutex now, this is kind of waiting for listing thread to finish it's work (since it can't process next packet)
@@ -2269,6 +2295,8 @@ void World::Update(uint32 diff)
// pussywizard: handle auctions when the timer has passed
if (m_timers[WUPDATE_AUCTIONS].Passed())
{
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions"));
+
m_timers[WUPDATE_AUCTIONS].Reset();
// pussywizard: handle expired auctions, auctions expired when realm was offline are also handled here (not during loading when many required things aren't loaded yet)
@@ -2283,8 +2311,13 @@ void World::Update(uint32 diff)
mail_expire_check_timer = m_gameTime + 6 * 3600;
}
- UpdateSessions(diff);
+ {
+ /// Handle session updates when the timer has passed
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update sessions"));
+ UpdateSessions(diff);
+ }
}
+
// end of section with mutex
AsyncAuctionListingMgr::SetAuctionListingAllowed(true);
@@ -2300,6 +2333,8 @@ void World::Update(uint32 diff)
{
if (m_timers[WUPDATE_CLEANDB].Passed())
{
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Clean logs table"));
+
m_timers[WUPDATE_CLEANDB].Reset();
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_OLD_LOGS);
@@ -2311,33 +2346,58 @@ void World::Update(uint32 diff)
}
}
- sLFGMgr->Update(diff, 0); // pussywizard: remove obsolete stuff before finding compatibility during map update
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update LFG 0"));
+ sLFGMgr->Update(diff, 0); // pussywizard: remove obsolete stuff before finding compatibility during map update
+ }
- sMapMgr->Update(diff);
+ ///- Update objects when the timer has passed (maps, transport, creatures, ...)
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update maps"));
+ sMapMgr->Update(diff);
+ }
if (sWorld->getBoolConfig(CONFIG_AUTOBROADCAST))
{
if (m_timers[WUPDATE_AUTOBROADCAST].Passed())
{
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Send autobroadcast"));
m_timers[WUPDATE_AUTOBROADCAST].Reset();
SendAutoBroadcast();
}
}
- sBattlegroundMgr->Update(diff);
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update battlegrounds"));
+ sBattlegroundMgr->Update(diff);
+ }
- sOutdoorPvPMgr->Update(diff);
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update outdoor pvp"));
+ sOutdoorPvPMgr->Update(diff);
+ }
- sBattlefieldMgr->Update(diff);
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update battlefields"));
+ sBattlefieldMgr->Update(diff);
+ }
- sLFGMgr->Update(diff, 2); // pussywizard: handle created proposals
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update LFG 2"));
+ sLFGMgr->Update(diff, 2); // pussywizard: handle created proposals
+ }
- // execute callbacks from sql queries that were queued recently
- ProcessQueryCallbacks();
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Process query callbacks"));
+ // execute callbacks from sql queries that were queued recently
+ ProcessQueryCallbacks();
+ }
/// Update uptime table
if (m_timers[WUPDATE_UPTIME].Passed())
{
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update uptime"));
+
uint32 tmpDiff = uint32(m_gameTime - m_startTime);
uint32 maxOnlinePlayers = GetMaxPlayerCount();
@@ -2356,6 +2416,7 @@ void World::Update(uint32 diff)
///- Erase corpses once every 20 minutes
if (m_timers[WUPDATE_CORPSES].Passed())
{
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Remove old corpses"));
m_timers[WUPDATE_CORPSES].Reset();
sMapMgr->DoForAllMaps([](Map* map)
{
@@ -2366,6 +2427,7 @@ void World::Update(uint32 diff)
///- Process Game events when necessary
if (m_timers[WUPDATE_EVENTS].Passed())
{
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update game events"));
m_timers[WUPDATE_EVENTS].Reset(); // to give time for Update() to be processed
uint32 nextGameEvent = sGameEventMgr->Update();
m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent);
@@ -2375,6 +2437,7 @@ void World::Update(uint32 diff)
///- Ping to keep MySQL connections alive
if (m_timers[WUPDATE_PINGDB].Passed())
{
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Ping MySQL"));
m_timers[WUPDATE_PINGDB].Reset();
LOG_DEBUG("sql.driver", "Ping MySQL to keep connection alive");
CharacterDatabase.KeepAlive();
@@ -2382,15 +2445,34 @@ void World::Update(uint32 diff)
WorldDatabase.KeepAlive();
}
- // update the instance reset times
- sInstanceSaveMgr->Update();
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update instance reset times"));
+ // update the instance reset times
+ sInstanceSaveMgr->Update();
+ }
- // And last, but not least handle the issued cli commands
- ProcessCliCommands();
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Process cli commands"));
+ // And last, but not least handle the issued cli commands
+ ProcessCliCommands();
+ }
- sScriptMgr->OnWorldUpdate(diff);
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update world scripts"));
+ sScriptMgr->OnWorldUpdate(diff);
+ }
- SavingSystemMgr::Update(diff);
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update saving system"));
+ SavingSystemMgr::Update(diff);
+ }
+
+ {
+ METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update metrics"));
+ // Stats logger update
+ sMetric->Update();
+ METRIC_VALUE("update_time_diff", diff);
+ }
}
void World::ForceGameEventUpdate()
@@ -2710,10 +2792,18 @@ void World::SendServerMessage(ServerMessageType type, const char* text, Player*
void World::UpdateSessions(uint32 diff)
{
- ///- Add new sessions
- WorldSession* sess = nullptr;
- while (addSessQueue.next(sess))
- AddSession_ (sess);
+ {
+ METRIC_DETAILED_NO_THRESHOLD_TIMER("world_update_time",
+ METRIC_TAG("type", "Add sessions"),
+ METRIC_TAG("parent_type", "Update sessions"));
+
+ ///- Add new sessions
+ WorldSession* sess = nullptr;
+ while (addSessQueue.next(sess))
+ {
+ AddSession_(sess);
+ }
+ }
///- Then send an update signal to remaining ones
for (SessionMap::iterator itr = m_sessions.begin(), next; itr != m_sessions.end(); itr = next)
@@ -2745,6 +2835,9 @@ void World::UpdateSessions(uint32 diff)
continue;
}
+ [[maybe_unused]] uint32 currentSessionId = itr->first;
+ METRIC_DETAILED_TIMER("world_update_sessions_time", METRIC_TAG("account_id", std::to_string(currentSessionId)));
+
if (!pSession->Update(diff, updater))
{
if (!RemoveQueuedPlayer(pSession) && getIntConfig(CONFIG_INTERVAL_DISCONNECT_TOLERANCE))
diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp
index 9dae990b3..00efa83fa 100644
--- a/src/server/scripts/Commands/cs_server.cpp
+++ b/src/server/scripts/Commands/cs_server.cpp
@@ -219,6 +219,10 @@ public:
handler->PSendSysMessage("Using World DB Revision: %s", sWorld->GetWorldDBRevision());
handler->PSendSysMessage("Using Character DB Revision: %s", sWorld->GetCharacterDBRevision());
handler->PSendSysMessage("Using Auth DB Revision: %s", sWorld->GetAuthDBRevision());
+
+ handler->PSendSysMessage("LoginDatabase queue size: %zu", LoginDatabase.QueueSize());
+ handler->PSendSysMessage("CharacterDatabase queue size: %zu", CharacterDatabase.QueueSize());
+ handler->PSendSysMessage("WorldDatabase queue size: %zu", WorldDatabase.QueueSize());
return true;
}
diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp
index 1226336f6..37edfc1fd 100644
--- a/src/server/worldserver/Main.cpp
+++ b/src/server/worldserver/Main.cpp
@@ -37,6 +37,7 @@
#include "GitRevision.h"
#include "IoContext.h"
#include "MapMgr.h"
+#include "Metric.h"
#include "MySQLThreading.h"
#include "ObjectAccessor.h"
#include "OpenSSLCrypto.h"
@@ -288,6 +289,22 @@ int main(int argc, char** argv)
LoadRealmInfo(*ioContext);
+ sMetric->Initialize(realm.Name, *ioContext, []()
+ {
+ METRIC_VALUE("online_players", sWorld->GetPlayerCount());
+ METRIC_VALUE("db_queue_login", uint64(LoginDatabase.QueueSize()));
+ METRIC_VALUE("db_queue_character", uint64(CharacterDatabase.QueueSize()));
+ METRIC_VALUE("db_queue_world", uint64(WorldDatabase.QueueSize()));
+ });
+
+ METRIC_EVENT("events", "Worldserver started", "");
+
+ std::shared_ptr sMetricHandle(nullptr, [](void*)
+ {
+ METRIC_EVENT("events", "Worldserver shutdown", "");
+ sMetric->Unload();
+ });
+
// Loading modules configs
sConfigMgr->PrintLoadedModulesConfigs();
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 0919abb85..7a524d420 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -31,6 +31,7 @@
# LOGGING SYSTEM SETTINGS
# PACKET SPOOF PROTECTION SETTINGS
# DEBUG
+# METRIC SETTINGS
#
###################################################################################################
@@ -3821,3 +3822,58 @@ Debug.Arena = 0
#
###################################################################################################
+
+###################################################################################################
+# METRIC SETTINGS
+#
+# These settings control the statistics sent to the metric database (currently InfluxDB)
+#
+# Metric.Enable
+# Description: Enables statistics sent to the metric database.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+#
+
+Metric.Enable = 0
+
+#
+# Metric.Interval
+# Description: Interval between every batch of data sent in seconds
+# Default: 10 seconds
+#
+
+Metric.Interval = 10
+
+#
+# Metric.ConnectionInfo
+# Description: Connection settings for metric database (currently InfluxDB).
+# Example: "hostname;port;database"
+# Default: "127.0.0.1;8086;worldserver"
+#
+
+Metric.ConnectionInfo = "127.0.0.1;8086;worldserver"
+
+#
+# Metric.OverallStatusInterval
+# Description: Interval between every gathering of overall worldserver status data in seconds
+# Default: 1 second
+#
+
+Metric.OverallStatusInterval = 1
+
+#
+# Metric threshold values: Given a metric "name"
+# Metric.Threshold.name
+# Description: Skips sending statistics with a value lower than the config value.
+# If the threshold is commented out, the metric will be ignored.
+# Only metrics logged with METRIC_DETAILED_TIMER in the sources are affected.
+# Disabled by default. Requires WITH_DETAILED_METRICS CMake flag.
+#
+# Format: Value as integer
+#
+
+#Metric.Threshold.world_update_sessions_time = 100
+#Metric.Threshold.worldsession_update_opcode_time = 50
+
+#
+###################################################################################################