diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/dashboards/player-stats.json b/dashboards/player-stats.json new file mode 100644 index 0000000..c45da85 --- /dev/null +++ b/dashboards/player-stats.json @@ -0,0 +1,711 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "6.4.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "iteration": 1572099073069, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "legend": { + "avg": false, + "current": false, + "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": [ + { + "expr": "sum(blocks_mined{player=\"$player\"}) by (block)", + "legendFormat": "{{block}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Blocks mined", + "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": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 3, + "legend": { + "avg": false, + "current": false, + "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": [ + { + "expr": "sum(blocks_crafted{player=\"$player\"}) by (block)", + "legendFormat": "{{block}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "items crafted", + "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": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 4, + "legend": { + "avg": false, + "current": false, + "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": [ + { + "expr": "sum(blocks_picked_up{player=\"$player\"}) by (block)", + "legendFormat": "{{block}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Blocks collected", + "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 + } + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_PROMETHEUS}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 9, + "w": 5, + "x": 12, + "y": 9 + }, + "id": 5, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "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, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(player_deaths{player=\"$player\"})", + "instant": true, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Deaths", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "${DS_PROMETHEUS}", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 9, + "w": 5, + "x": 17, + "y": 9 + }, + "id": 6, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "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, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "player_jumps{player=\"$player\"}", + "instant": true, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Jumps", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 7, + "legend": { + "avg": false, + "current": false, + "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": [ + { + "expr": "cm_traveled{player=\"$player\"} / 1000", + "legendFormat": "{{ method }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Meters travelled", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "lengthm", + "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": "${DS_PROMETHEUS}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 8, + "legend": { + "avg": false, + "current": false, + "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": [ + { + "expr": "entities_killed{player=\"$player\"}", + "legendFormat": "{{ entity }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Mobs killed", + "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": false, + "schemaVersion": 20, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(blocks_mined,player)", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "player", + "options": [], + "query": "label_values(blocks_mined,player)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "minecraft Player stats", + "uid": "gAy914AZk", + "version": 8 +} diff --git a/dashboards/server-stats.json b/dashboards/server-stats.json new file mode 100644 index 0000000..d9591e2 --- /dev/null +++ b/dashboards/server-stats.json @@ -0,0 +1,881 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": [], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "8.3.0" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph (old)", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table-old", + "name": "Table (old)", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "for usage \r\nhttps://github.com/Joshi425/minecraft-exporter", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 11993, + "graphTooltip": 0, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "columns": [], + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fontSize": "100%", + "gridPos": { + "h": 17, + "w": 3, + "x": 0, + "y": 0 + }, + "id": 9, + "showHeader": true, + "sort": { + "col": 0, + "desc": true + }, + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "hidden" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/Value/", + "thresholds": [], + "type": "hidden", + "unit": "short" + } + ], + "targets": [ + { + "expr": "player_online", + "instant": true, + "legendFormat": "{{player}}", + "refId": "A" + } + ], + "title": "Players Online", + "transform": "timeseries_to_rows", + "type": "table-old" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 9, + "x": 3, + "y": 0 + }, + "hiddenSeries": false, + "id": 7, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.0", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "dim_ticktime", + "interval": "", + "legendFormat": "{{dimension_name}}", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "overall_ticktime", + "legendFormat": "Overall", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Ticktime", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:70", + "format": "ms", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:71", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 5, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.0", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dim_tps", + "legendFormat": "{{ dimension_name }}", + "refId": "A" + }, + { + "expr": "overall_tps", + "legendFormat": "Overall", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "TPS ", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:276", + "format": "short", + "logBase": 1, + "max": "20", + "min": "0", + "show": true + }, + { + "$$hashKey": "object:277", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.0", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(paper_tps_1m)", + "interval": "", + "legendFormat": "1 Minute", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(paper_tps_5m)", + "hide": false, + "interval": "", + "legendFormat": "5 Minute", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(paper_tps_15m)", + "hide": false, + "interval": "", + "legendFormat": "15 Minute", + "refId": "D" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Paper TPS", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:276", + "format": "short", + "label": "", + "logBase": 1, + "max": "20", + "min": "0", + "show": true + }, + { + "$$hashKey": "object:277", + "format": "short", + "label": "", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 9, + "x": 3, + "y": 9 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.0", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "entities", + "legendFormat": "{{ entity }}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Entities Loaded", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 16 + }, + "hiddenSeries": false, + "id": 3, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.0", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "player_playtime / 20", + "legendFormat": "{{ player }}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Play Time", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "logBase": 1, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "hiddenSeries": false, + "id": 17, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.0", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(dynmap_tile_render_statistics) by (type)", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Dynmap Tile render Statistics", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 25 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.0", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "player_score", + "legendFormat": "{{ player }}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Score", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 34 + }, + "hiddenSeries": false, + "id": 15, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.3.0", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dynmap_chunk_loading_duration", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Dynmap Chunk loading duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "logBase": 1, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + } + ], + "refresh": "5s", + "schemaVersion": 33, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Minecraft Server", + "uid": "LhW0bV0Wz", + "version": 3, + "weekStart": "" +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 81ac37b..5a7f246 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,6 +30,7 @@ services: MOTD: "§l§cPatataPack§r" ONLINE_MODE: "FALSE" ALLOW_FLIGHT: "TRUE" + RCON_PASSWORD: ${RCON_PASSWORD} OPS: | Bailongo Amimi @@ -73,6 +74,17 @@ services: - ./dashboards:/etc/grafana/dashboards depends_on: - prometheus + minecraft-exporter: + build: + context: ./minecraft-exporter + environment: + RCON_HOST: localhost + RCON_PORT: 25575 + RCON_PASSWORD: ${RCON_PASSWORD} + ports: + - 9700:8000 + volumes: + - ./world:/world:ro volumes: patata-data: {} prometheus-tsdb: {} diff --git a/minecraft-exporter/Dockerfile b/minecraft-exporter/Dockerfile new file mode 100644 index 0000000..8f7738f --- /dev/null +++ b/minecraft-exporter/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.10-alpine + +COPY requirements.txt minecraft_exporter.py / +RUN pip install --no-cache-dir -r requirements.txt + +EXPOSE 8000 + +ENTRYPOINT ["python","-u","minecraft_exporter.py"] diff --git a/minecraft-exporter/egg-minecraft-prom-exporter.json b/minecraft-exporter/egg-minecraft-prom-exporter.json new file mode 100644 index 0000000..3db4a8f --- /dev/null +++ b/minecraft-exporter/egg-minecraft-prom-exporter.json @@ -0,0 +1,70 @@ +{ + "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", + "meta": { + "version": "PTDL_v1", + "update_url": null + }, + "exported_at": "2022-02-21T08:57:54+00:00", + "name": "Minecraft Prom Exporter", + "author": "jack.stupple@protonmail.com", + "description": "Minecraft Prom Exporter", + "features": null, + "images": [ + "joshi425\/minecraft_exporter", + "ghcr.io\/joshi425\/minecraft-exporter:latest", + "ghcr.io\/surdaft\/minecraft-exporter:latest" + ], + "file_denylist": [], + "startup": "python3 minecraft_exporter.py", + "config": { + "files": "{}", + "startup": "{\r\n \"done\": \"Exporter started on Port\"\r\n}", + "logs": "{}", + "stop": "^C" + }, + "scripts": { + "installation": { + "script": null, + "container": "alpine:3.4", + "entrypoint": "ash" + } + }, + "variables": [ + { + "name": "RCON Host", + "description": "Host of the minecraft server", + "env_variable": "RCON_HOST", + "default_value": "host.docker.internal", + "user_viewable": false, + "user_editable": false, + "rules": "string|max:32" + }, + { + "name": "RCON Port", + "description": "Port of minecraft server RCON", + "env_variable": "RCON_PORT", + "default_value": "25575", + "user_viewable": false, + "user_editable": false, + "rules": "numeric|min:0|max:35000" + }, + { + "name": "RCON Password", + "description": "Password for minecraft server RCON", + "env_variable": "RCON_PASSWORD", + "default_value": "", + "user_viewable": false, + "user_editable": false, + "rules": "string|min:0" + }, + { + "name": "HTTP Port", + "description": "Port to host metric server on", + "env_variable": "HTTP_PORT", + "default_value": "8000", + "user_viewable": false, + "user_editable": false, + "rules": "numeric|min:0|max:35000" + } + ] +} diff --git a/minecraft-exporter/minecraft_exporter.py b/minecraft-exporter/minecraft_exporter.py new file mode 100644 index 0000000..e49003f --- /dev/null +++ b/minecraft-exporter/minecraft_exporter.py @@ -0,0 +1,385 @@ +import json +import os +import re +import time +from os import listdir +from os.path import isfile, join + +import nbt +import requests +import schedule +from mcipc.rcon.je import Client +from prometheus_client import Metric, REGISTRY, start_http_server + + +class MinecraftCollector(object): + def __init__(self): + self.stats_directory = "/world/stats" + self.player_directory = "/world/playerdata" + self.advancements_directory = "/world/advancements" + self.better_questing = "/world/betterquesting" + self.player_map = dict() + self.quests_enabled = False + + self.rcon = None + self.rcon_connected = False + if all(x in os.environ for x in ['RCON_HOST', 'RCON_PASSWORD']): + self.rcon = Client(os.environ['RCON_HOST'], int(os.environ['RCON_PORT']),passwd=os.environ['RCON_PASSWORD']) + print("RCON is enabled for " + os.environ['RCON_HOST']) + + if os.path.isdir(self.better_questing): + self.quests_enabled = True + + schedule.every().day.at("01:00").do(self.flush_playernamecache) + + def get_players(self): + return [f[:-5] for f in listdir(self.stats_directory) if isfile(join(self.stats_directory, f))] + + def flush_playernamecache(self): + print("flushing playername cache") + self.player_map = dict() + + def uuid_to_player(self, uuid): + if uuid in self.player_map: + return self.player_map[uuid] + else: + try: + result = requests.get('https://sessionserver.mojang.com/session/minecraft/profile/' + uuid) + self.player_map[uuid] = result.json()['name'] + return (result.json()['name']) + except: + return + + def rcon_connect(self): + try: + self.rcon.__enter__() # https://github.com/conqp/mcipc/issues/16 + self.rcon_connected = True + print("Successfully connected to RCON") + return True + except Exception as e: + print("Failed to connect to RCON") + print(e) + return False + + def rcon_disconnect(self): + self.rcon.close() + self.rcon_connected = False + + def rcon_command(self, command): + try: + response = self.rcon.run(command) + except Exception as e: + response = None + if e == "Connection timeout error": + print("Lost RCON Connection") + self.rcon_disconnect() + else: + print("RCON command failed:",e) + except (ConnectionResetError, ConnectionAbortedError, BrokenPipeError): + print("Lost RCON Connection") + self.rcon_disconnect() + response = None + + return response + + def get_server_stats(self): + if self.rcon is None or (not self.rcon_connected and not self.rcon_connect()): + return [] + + metrics = [] + + dim_tps = Metric('dim_tps', 'TPS of a dimension', "counter") + dim_ticktime = Metric('dim_ticktime', "Time a Tick took in a Dimension", "counter") + overall_tps = Metric('overall_tps', 'overall TPS', "counter") + overall_ticktime = Metric('overall_ticktime', "overall Ticktime", "counter") + player_online = Metric('player_online', "is 1 if player is online", "counter") + entities = Metric('entities', "type and count of active entites", "counter") + tps_1m = Metric('paper_tps_1m', '1 Minute TPS', "counter") + tps_5m = Metric('paper_tps_5m', '5 Minute TPS', "counter") + tps_15m = Metric('paper_tps_15m', '15 Minute TPS', "counter") + + metrics.extend( + [dim_tps, dim_ticktime, overall_tps, overall_ticktime, player_online, entities, tps_1m, tps_5m, tps_15m]) + if 'PAPER_SERVER' in os.environ and os.environ['PAPER_SERVER'] == "True": + resp = str(self.rcon_command("tps")).strip().replace("§a", "") + tpsregex = re.compile("TPS from last 1m, 5m, 15m: (\d*\.\d*), (\d*\.\d*), (\d*\.\d*)") + for m1, m5, m15 in tpsregex.findall(resp): + tps_1m.add_sample('paper_tps_1m', value=m1, labels={'tps': '1m'}) + tps_5m.add_sample('paper_tps_5m', value=m5, labels={'tps': '5m'}) + tps_15m.add_sample('paper_tps_15m', value=m15, labels={'tps': '15m'}) + if 'FORGE_SERVER' in os.environ and os.environ['FORGE_SERVER'] == "True": + # dimensions + resp = self.rcon_command("forge tps") + dimtpsregex = re.compile("Dim (.*?)\s\((.*?)\):\sMean tick time:\s(.*?) ms\. Mean TPS: (\d*\.\d*)") + for dimid, dimname, meanticktime, meantps in dimtpsregex.findall(resp): + dim_tps.add_sample('dim_tps', value=meantps, labels={'dimension_id': dimid, 'dimension_name': dimname}) + dim_ticktime.add_sample('dim_ticktime', value=meanticktime, + labels={'dimension_id': dimid, 'dimension_name': dimname}) + overallregex = re.compile("Overall\s?: Mean tick time: (.*) ms. Mean TPS: (.*)") + overall_tps.add_sample('overall_tps', value=overallregex.findall(resp)[0][1], labels={}) + overall_ticktime.add_sample('overall_ticktime', value=overallregex.findall(resp)[0][0], labels={}) + + # entites + resp = self.rcon_command("forge entity list") + entityregex = re.compile("(\d+): (.*?:.*?)\s") + for entitycount, entityname in entityregex.findall(resp): + entities.add_sample('entities', value=entitycount, labels={'entity': entityname}) + + # dynmap + if 'DYNMAP_ENABLED' in os.environ and os.environ['DYNMAP_ENABLED'] == "True": + dynmap_tile_render_statistics = Metric('dynmap_tile_render_statistics', + 'Tile Render Statistics reported by Dynmap', "counter") + dynmap_chunk_loading_statistics_count = Metric('dynmap_chunk_loading_statistics_count', + 'Chunk Loading Statistics reported by Dynmap', "counter") + dynmap_chunk_loading_statistics_duration = Metric('dynmap_chunk_loading_statistics_duration', + 'Chunk Loading Statistics reported by Dynmap', "counter") + metrics.extend([dynmap_tile_render_statistics, dynmap_chunk_loading_statistics_count, + dynmap_chunk_loading_statistics_duration]) + + resp = self.rcon_command("dynmap stats") + + dynmaptilerenderregex = re.compile(" (.*?): processed=(\d*), rendered=(\d*), updated=(\d*)") + for dim, processed, rendered, updated in dynmaptilerenderregex.findall(resp): + dynmap_tile_render_statistics.add_sample('dynmap_tile_render_statistics', value=processed, + labels={'type': 'processed', 'file': dim}) + dynmap_tile_render_statistics.add_sample('dynmap_tile_render_statistics', value=rendered, + labels={'type': 'rendered', 'file': dim}) + dynmap_tile_render_statistics.add_sample('dynmap_tile_render_statistics', value=updated, + labels={'type': 'updated', 'file': dim}) + + dynmapchunkloadingregex = re.compile("Chunks processed: (.*?): count=(\d*), (\d*.\d*)") + for state, count, duration_per_chunk in dynmapchunkloadingregex.findall(resp): + dynmap_chunk_loading_statistics_count.add_sample('dynmap_chunk_loading_statistics', value=count, + labels={'type': state}) + dynmap_chunk_loading_statistics_duration.add_sample('dynmap_chunk_loading_duration', + value=duration_per_chunk, labels={'type': state}) + + # player + resp = self.rcon_command("list") + playerregex = re.compile("players online:(.*)") + if playerregex.findall(resp): + for player in playerregex.findall(resp)[0].split(","): + if not player.isspace(): + player_online.add_sample('player_online', value=1, labels={'player': player.lstrip()}) + + return metrics + + def get_player_quests_finished(self, uuid): + with open(self.better_questing + "/QuestProgress.json") as json_file: + data = json.load(json_file) + json_file.close() + counter = 0 + for _, value in data['questProgress:9'].items(): + for _, u in value['tasks:9']['0:10']['completeUsers:9'].items(): + if u == uuid: + counter += 1 + return counter + + def get_player_stats(self, uuid): + with open(self.stats_directory + "/" + uuid + ".json") as json_file: + data = json.load(json_file) + json_file.close() + nbtfile = nbt.nbt.NBTFile(self.player_directory + "/" + uuid + ".dat", 'rb') + data["stat.XpTotal"] = nbtfile.get("XpTotal").value + data["stat.XpLevel"] = nbtfile.get("XpLevel").value + data["stat.Score"] = nbtfile.get("Score").value + data["stat.Health"] = nbtfile.get("Health").value + data["stat.foodLevel"] = nbtfile.get("foodLevel").value + with open(self.advancements_directory + "/" + uuid + ".json") as json_file: + count = 0 + advancements = json.load(json_file) + for key, value in advancements.items(): + if key in ("DataVersion"): + continue + if value["done"] == True: + count += 1 + data["stat.advancements"] = count + if self.quests_enabled: + data["stat.questsFinished"] = self.get_player_quests_finished(uuid) + return data + + def update_metrics_for_player(self, uuid): + name = self.uuid_to_player(uuid) + if not name: + return + + data = self.get_player_stats(uuid) + + blocks_mined = Metric('blocks_mined', 'Blocks a Player mined', "counter") + blocks_picked_up = Metric('blocks_picked_up', 'Blocks a Player picked up', "counter") + player_deaths = Metric('player_deaths', 'How often a Player died', "counter") + player_jumps = Metric('player_jumps', 'How often a Player has jumped', "counter") + cm_traveled = Metric('cm_traveled', 'How many cm a Player traveled, whatever that means', "counter") + player_xp_total = Metric('player_xp_total', "How much total XP a player has", "counter") + player_current_level = Metric('player_current_level', "How much current XP a player has", "counter") + player_food_level = Metric('player_food_level', "How much food the player currently has", "counter") + player_health = Metric('player_health', "How much Health the player currently has", "counter") + player_score = Metric('player_score', "The Score of the player", "counter") + entities_killed = Metric('entities_killed', "Entities killed by player", "counter") + damage_taken = Metric('damage_taken', "Damage Taken by Player", "counter") + damage_dealt = Metric('damage_dealt', "Damage dealt by Player", "counter") + blocks_crafted = Metric('blocks_crafted', "Items a Player crafted", "counter") + player_playtime = Metric('player_playtime', "Time in Minutes a Player was online", "counter") + player_advancements = Metric('player_advancements', "Number of completed advances of a player", "counter") + player_slept = Metric('player_slept', "Times a Player slept in a bed", "counter") + player_quests_finished = Metric('player_quests_finished', 'Number of quests a Player has finished', 'counter') + player_used_crafting_table = Metric('player_used_crafting_table', "Times a Player used a Crafting Table", + "counter") + mc_custom = Metric('mc_custom', "Custom Minecraft stat", "counter") + for key, value in data.items(): # pre 1.15 + if key in ("stats", "DataVersion"): + continue + stat = key.split(".")[1] # entityKilledBy + if stat == "mineBlock": + blocks_mined.add_sample("blocks_mined", value=value, labels={'player': name, 'block': '.'.join( + (key.split(".")[2], key.split(".")[3]))}) + elif stat == "pickup": + blocks_picked_up.add_sample("blocks_picked_up", value=value, labels={'player': name, 'block': '.'.join( + (key.split(".")[2], key.split(".")[3]))}) + elif stat == "entityKilledBy": + if len(key.split(".")) == 4: + player_deaths.add_sample('player_deaths', value=value, labels={'player': name, 'cause': '.'.join( + (key.split(".")[2], key.split(".")[3]))}) + else: + player_deaths.add_sample('player_deaths', value=value, + labels={'player': name, 'cause': key.split(".")[2]}) + elif stat == "jump": + player_jumps.add_sample("player_jumps", value=value, labels={'player': name}) + elif stat == "walkOneCm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "walking"}) + elif stat == "swimOneCm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "swimming"}) + elif stat == "sprintOneCm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "sprinting"}) + elif stat == "diveOneCm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "diving"}) + elif stat == "fallOneCm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "falling"}) + elif stat == "flyOneCm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "flying"}) + elif stat == "boatOneCm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "boat"}) + elif stat == "horseOneCm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "horse"}) + elif stat == "climbOneCm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "climbing"}) + elif stat == "XpTotal": + player_xp_total.add_sample('player_xp_total', value=value, labels={'player': name}) + elif stat == "XpLevel": + player_current_level.add_sample('player_current_level', value=value, labels={'player': name}) + elif stat == "foodLevel": + player_food_level.add_sample('player_food_level', value=value, labels={'player': name}) + elif stat == "Health": + player_health.add_sample('player_health', value=value, labels={'player': name}) + elif stat == "Score": + player_score.add_sample('player_score', value=value, labels={'player': name}) + elif stat == "killEntity": + entities_killed.add_sample('entities_killed', value=value, + labels={'player': name, "entity": key.split(".")[2]}) + elif stat == "damageDealt": + damage_dealt.add_sample('damage_dealt', value=value, labels={'player': name}) + elif stat == "damageTaken": + damage_dealt.add_sample('damage_taken', value=value, labels={'player': name}) + elif stat == "craftItem": + blocks_crafted.add_sample('blocks_crafted', value=value, labels={'player': name, 'block': '.'.join( + (key.split(".")[2], key.split(".")[3]))}) + elif stat == "playOneMinute": + player_playtime.add_sample('player_playtime', value=value, labels={'player': name}) + elif stat == "advancements": + player_advancements.add_sample('player_advancements', value=value, labels={'player': name}) + elif stat == "sleepInBed": + player_slept.add_sample('player_slept', value=value, labels={'player': name}) + elif stat == "craftingTableInteraction": + player_used_crafting_table.add_sample('player_used_crafting_table', value=value, + labels={'player': name}) + elif stat == "questsFinished": + player_quests_finished.add_sample('player_quests_finished', value=value, labels={'player': name}) + + if "stats" in data: # Minecraft > 1.15 + if "minecraft:crafted" in data["stats"]: + for block, value in data["stats"]["minecraft:crafted"].items(): + blocks_crafted.add_sample('blocks_crafted', value=value, labels={'player': name, 'block': block}) + if "minecraft:mined" in data["stats"]: + for block, value in data["stats"]["minecraft:mined"].items(): + blocks_mined.add_sample("blocks_mined", value=value, labels={'player': name, 'block': block}) + if "minecraft:picked_up" in data["stats"]: + for block, value in data["stats"]["minecraft:picked_up"].items(): + blocks_picked_up.add_sample("blocks_picked_up", value=value, + labels={'player': name, 'block': block}) + if "minecraft:killed" in data["stats"]: + for entity, value in data["stats"]["minecraft:killed"].items(): + entities_killed.add_sample('entities_killed', value=value, + labels={'player': name, "entity": entity}) + if "minecraft:killed_by" in data["stats"]: + for entity, value in data["stats"]["minecraft:killed_by"].items(): + player_deaths.add_sample('player_deaths', value=value, labels={'player': name, 'cause': entity}) + for stat, value in data["stats"]["minecraft:custom"].items(): + if stat == "minecraft:jump": + player_jumps.add_sample("player_jumps", value=value, labels={'player': name}) + elif stat == "minecraft:deaths": + player_deaths.add_sample('player_deaths', value=value, labels={'player': name}) + elif stat == "minecraft:damage_taken": + damage_taken.add_sample('damage_taken', value=value, labels={'player': name}) + elif stat == "minecraft:damage_dealt": + damage_dealt.add_sample('damage_dealt',value=value,labels={'player':name}) + elif stat == "minecraft:play_time": + player_playtime.add_sample('player_playtime',value=value,labels={'player':name}) + elif stat == "minecraft:play_one_minute": # pre 1.17 + player_playtime.add_sample('player_playtime',value=value,labels={'player':name}) + elif stat == "minecraft:walk_one_cm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "walking"}) + elif stat == "minecraft:walk_on_water_one_cm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "swimming"}) + elif stat == "minecraft:sprint_one_cm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "sprinting"}) + elif stat == "minecraft:walk_under_water_one_cm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "diving"}) + elif stat == "minecraft:fall_one_cm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "falling"}) + elif stat == "minecraft:fly_one_cm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "flying"}) + elif stat == "minecraft:boat_one_cm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "boat"}) + elif stat == "minecraft:horse_one_cm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "horse"}) + elif stat == "minecraft:climb_one_cm": + cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "climbing"}) + elif stat == "minecraft:sleep_in_bed": + player_slept.add_sample('player_slept', value=value, labels={'player': name}) + elif stat == "minecraft:interact_with_crafting_table": + player_used_crafting_table.add_sample('player_used_crafting_table', value=value, + labels={'player': name}) + else: + mc_custom.add_sample('mc_custom', value=value, labels={'stat': stat}) + return [blocks_mined, blocks_picked_up, player_deaths, player_jumps, cm_traveled, player_xp_total, + player_current_level, player_food_level, player_health, player_score, entities_killed, damage_taken, + damage_dealt, blocks_crafted, player_playtime, player_advancements, player_slept, + player_used_crafting_table, player_quests_finished, mc_custom] + + def collect(self): + for player in self.get_players(): + metrics = self.update_metrics_for_player(player) + if not metrics: + continue + + for metric in metrics: + yield metric + + for metric in self.get_server_stats(): + yield metric + + +if __name__ == '__main__': + try: + HTTP_PORT = int(os.environ.get('HTTP_PORT')) + except: + HTTP_PORT = 8000 + + start_http_server(HTTP_PORT) + REGISTRY.register(MinecraftCollector()) + + print(f'Exporter started on Port {HTTP_PORT}') + + while True: + time.sleep(1) + schedule.run_pending() diff --git a/minecraft-exporter/requirements.txt b/minecraft-exporter/requirements.txt new file mode 100644 index 0000000..d971c02 --- /dev/null +++ b/minecraft-exporter/requirements.txt @@ -0,0 +1,5 @@ +mcipc==2.4.2 +NBT==1.5.1 +prometheus-client==0.12.0 +requests==2.27.1 +schedule==1.1.0