├── .actrc
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── build.yml
│ └── release.yml
├── .gitignore
├── .whitesource
├── LICENSE
├── README.jp.md
├── README.md
├── README.zh-Hans.md
├── bungeecord-prometheus-exporter.iml
├── dashboards
└── default_dashboard.json
├── docker
├── bungeecord
│ └── config
│ │ └── config.yml
├── docker-compose-bungeecord.yml
├── docker-compose-velocity.yml
└── velocity
│ └── config
│ └── velocity.toml
├── images
└── dashboard.png
├── pom.xml
└── src
├── main
├── assembly
│ └── package.xml
├── java
│ └── org
│ │ └── akadia
│ │ └── prometheus
│ │ ├── MetricRegistry.java
│ │ ├── MetricsServer.java
│ │ ├── PrometheusExporter.java
│ │ ├── bungeecord
│ │ ├── PrometheusBungeeCordExporter.java
│ │ ├── listeners
│ │ │ ├── LoginEventListener.java
│ │ │ ├── PlayerChatEventListener.java
│ │ │ ├── PlayerCommandEventListener.java
│ │ │ ├── PlayerDisconnectEventListener.java
│ │ │ ├── PlayerJoinedNetworkEventListener.java
│ │ │ ├── PlayerKickEventListener.java
│ │ │ ├── PlayerLeftNetworkEventListener.java
│ │ │ └── ProxyPingEventListener.java
│ │ └── metrics
│ │ │ ├── InstalledNetworkPlugins.java
│ │ │ ├── ManagedServers.java
│ │ │ ├── OnlinePlayer.java
│ │ │ ├── OnlinePlayerLatency.java
│ │ │ ├── RedisBungeeOnlinePlayer.java
│ │ │ └── RedisBungeeOnlineProxies.java
│ │ ├── config
│ │ └── ConfigManager.java
│ │ ├── interfaces
│ │ ├── Configurable.java
│ │ ├── CountableMetrics.java
│ │ ├── GauageMetric.java
│ │ ├── Metric.java
│ │ ├── MetricWrapper.java
│ │ └── SummaryMetric.java
│ │ ├── metrics
│ │ ├── JvmGarbageCollectorWrapper.java
│ │ ├── JvmMemory.java
│ │ └── JvmThreadsWrapper.java
│ │ ├── utils
│ │ └── Util.java
│ │ └── velocity
│ │ ├── PrometheusVelocityExporter.java
│ │ ├── listeners
│ │ ├── LoginEventListener.java
│ │ ├── PlayerChatEventListener.java
│ │ ├── PlayerCommandEventListener.java
│ │ ├── PlayerDisconnectEventListener.java
│ │ ├── PlayerKickEventListener.java
│ │ └── ProxyPingEventListener.java
│ │ └── metrics
│ │ ├── InstalledNetworkPlugins.java
│ │ ├── ManagedServers.java
│ │ ├── OnlinePlayer.java
│ │ └── OnlinePlayersLatency.java
└── resources
│ ├── bungee.yml
│ ├── config.json
│ └── velocity-plugin.json
└── test
└── java
├── .gitignore
└── org
└── akadia
└── prometheus
└── exporter
└── PrometheusExporterTest.java
/.actrc:
--------------------------------------------------------------------------------
1 | -P ubuntu-latest=nektos/act-environments-ubuntu:18.04
2 | -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: maven
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "10:00"
8 | open-pull-requests-limit: 10
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 | pull_request:
8 | branches:
9 | - '*'
10 |
11 | jobs:
12 | build:
13 |
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@master
18 | - name: Set up JDK
19 | uses: actions/setup-java@master
20 | with:
21 | java-version: '17'
22 | distribution: 'adopt'
23 | - name: Build with Maven
24 | run: mvn -B package --file pom.xml
25 | - name: Upload a Build Artifact
26 | uses: actions/upload-artifact@master
27 | with:
28 | name: jar artifact
29 | path: /home/runner/work/bungeecord-prometheus-exporter/bungeecord-prometheus-exporter/target/bungeecord-prometheus-exporter-*.jar
30 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: Release
5 |
6 | on:
7 | workflow_dispatch:
8 | release:
9 | types: [ created ]
10 |
11 | jobs:
12 | build:
13 |
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@master
18 | - name: Set up JDK
19 | uses: actions/setup-java@master
20 | with:
21 | java-version: '17'
22 | distribution: 'adopt'
23 | - name: Build with Maven
24 | run: mvn -B package --file pom.xml
25 | - name: Release
26 | uses: qcastel/github-actions-maven-release@master
27 | env:
28 | JAVA_HOME: /usr/lib/jvm/java-17-openjdk/
29 | with:
30 | access-token: ${{ secrets.GITHUB_ACCESS_TOKEN }}
31 | release-branch-name: "main"
32 | maven-options: "-DignoreSnapshots=true"
33 | ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
34 | maven-repo-server-id: github
35 | maven-repo-server-username: ${{ secrets.MVN_REPO_PRIVATE_REPO_USER }}
36 | maven-repo-server-password: ${{ secrets.MVN_REPO_PRIVATE_REPO_PASSWORD }}
37 | git-release-bot-name: ${{ secrets.BOT_NAME }}
38 | git-release-bot-email: ${{ secrets.BOT_EMAIL }}
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
25 | .idea
26 | target
27 |
28 | act
29 |
30 | .attach_pid*
31 |
32 | # plugins
33 | plugins
--------------------------------------------------------------------------------
/.whitesource:
--------------------------------------------------------------------------------
1 | {
2 | "scanSettings": {
3 | "baseBranches": []
4 | },
5 | "checkRunSettings": {
6 | "vulnerableCheckRunConclusionLevel": "failure",
7 | "displayMode": "diff"
8 | },
9 | "issueSettings": {
10 | "minSeverityLevel": "LOW"
11 | }
12 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Weihao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.jp.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/README.md)
2 | [](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/README.zh-Hans.md)
3 | [](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/README.jp.md)
4 | [](https://github.com/weihao/bungeecord-prometheus-exporter/actions/workflows/release.yml)
5 | 
6 |
7 |
8 | # BungeeCord Prometheus Exporter
9 |
10 | 翻訳の助けてお願いします
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/README.md)
2 | [](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/README.zh-Hans.md)
3 | [](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/README.jp.md)
4 | [](https://github.com/weihao/bungeecord-prometheus-exporter/actions/workflows/release.yml)
5 | 
6 |
7 |
8 | # BungeeCord Prometheus Exporter
9 |
10 | A **plugin** that exports network stats for Prometheus.
11 |
12 | > If you don't run a network proxy, you might also be interested
13 | > in [Prometheus Exporter](https://github.com/sladkoff/minecraft-prometheus-exporter) for a `single server` metrics!
14 |
15 | ## Why BungeeCord Prometheus Exporter?
16 |
17 | - monitor your server infrastructure
18 | - track your players, events, and servers
19 | - player pings histogram
20 | - online player list
21 | - server list ping, connects, disconnects, kicks, and chat event counters
22 | - automates the collection, management and viewing of your data
23 | - get alerts for service outages
24 |
25 | ## Runtime Requirement
26 |
27 | - Java 17+
28 |
29 | ## Compatible Proxy
30 |
31 | - [x] Velocity
32 | - [x] BungeeCord / Waterfall
33 | - [x] RedisBungee
34 |
35 | ## Quick Start
36 |
37 | Drop the bungeecord-prometheus-exporter.jar into your plugins directory and start your proxy server.
38 |
39 | After startup, the Prometheus metrics endpoint should be available at ``localhost:9985/metrics`` (assuming localhost is
40 | the server hostname).
41 |
42 | If running inside the docker, change the host to `0.0.0.0` to allow Prometheus and other services to reach the endpoint.
43 |
44 | The metrics port can be customized in the plugin's config.json (a default config will be created after the first use).
45 |
46 | ## Prometheus config
47 |
48 | Add the following job to the ``scrape_configs`` section of your Prometheus configuration `prometheus.yml`:
49 |
50 | ### Single Proxy
51 |
52 | ```yml
53 | - job_name: 'bungeecord'
54 | scrape_interval: 5s
55 |
56 | static_configs:
57 | - targets: [ 'localhost:9985' ]
58 | labels:
59 | proxy_name: 'proxy'
60 | ```
61 |
62 | ### Multiple proxies
63 |
64 | You can use labels in your Prometheus scrape configuration to distinguish between multiple proxies:
65 |
66 | ```yml
67 | - job_name: 'bungeecord'
68 | scrape_interval: 5s
69 |
70 | static_configs:
71 | - targets: [ 'localhost:9985' ]
72 | labels:
73 | proxy_name: 'proxy1'
74 | - targets: [ 'localhost:9226' ]
75 | labels:
76 | proxy_name: 'proxy2'
77 | ```
78 |
79 | ## Import Grafana Dashboard
80 |
81 | 1. Navigate to Grafana -> Dashboards -> Import
82 | 1. Paste in or upload [default dashboard](https://github.com/weihao/bungeecord-prometheus-exporter/tree/main/dashboards)
83 | 1. 
84 |
85 | ## Notes
86 |
87 | RedisBungee is supported
88 | but [disabled by default](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/src/main/resources/config.json)
89 | .
90 | RedisBungee metrics are not used in the dashboard because we are
91 | already collecting metrics from single instances. However, if you still want to integrate with RedisBungee, free feel to
92 | enable it and modify the dashboard.
93 |
94 | ## Links
95 |
96 | This project is indexed at:
97 |
98 | - [exporters](https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exporters.md#miscellaneous)
99 | - [ports](https://github.com/prometheus/prometheus/wiki/Default-port-allocations#exporters-starting-at-9100)
100 |
--------------------------------------------------------------------------------
/README.zh-Hans.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/README.md)
2 | [](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/README.zh-Hans.md)
3 | [](https://github.com/weihao/bungeecord-prometheus-exporter/blob/main/README.jp.md)
4 | [](https://github.com/weihao/bungeecord-prometheus-exporter/actions/workflows/release.yml)
5 | 
6 |
7 |
8 | # BungeeCord Prometheus Exporter
9 |
10 | 有没有好心的志愿者维护中文文档呢?
--------------------------------------------------------------------------------
/bungeecord-prometheus-exporter.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | BUNGEECORD
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/dashboards/default_dashboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "__inputs": [
3 | {
4 | "name": "DS_PROMETHEUS",
5 | "label": "Prometheus",
6 | "description": "",
7 | "type": "datasource",
8 | "pluginId": "prometheus",
9 | "pluginName": "Prometheus"
10 | }
11 | ],
12 | "annotations": {
13 | "list": [
14 | {
15 | "builtIn": 1,
16 | "datasource": {
17 | "type": "datasource",
18 | "uid": "grafana"
19 | },
20 | "enable": true,
21 | "hide": true,
22 | "iconColor": "rgba(0, 211, 255, 1)",
23 | "name": "Annotations & Alerts",
24 | "target": {
25 | "limit": 100,
26 | "matchAny": false,
27 | "tags": [],
28 | "type": "dashboard"
29 | },
30 | "type": "dashboard"
31 | }
32 | ]
33 | },
34 | "description": "",
35 | "editable": true,
36 | "fiscalYearStartMonth": 0,
37 | "graphTooltip": 0,
38 | "id": 22,
39 | "links": [],
40 | "liveNow": false,
41 | "panels": [
42 | {
43 | "datasource": "${DS_PROMETHEUS}",
44 | "fieldConfig": {
45 | "defaults": {
46 | "color": {
47 | "mode": "palette-classic"
48 | },
49 | "custom": {
50 | "axisCenteredZero": false,
51 | "axisColorMode": "text",
52 | "axisLabel": "",
53 | "axisPlacement": "auto",
54 | "barAlignment": 0,
55 | "drawStyle": "line",
56 | "fillOpacity": 25,
57 | "gradientMode": "none",
58 | "hideFrom": {
59 | "legend": false,
60 | "tooltip": false,
61 | "viz": false
62 | },
63 | "lineInterpolation": "linear",
64 | "lineWidth": 1,
65 | "pointSize": 5,
66 | "scaleDistribution": {
67 | "type": "linear"
68 | },
69 | "showPoints": "never",
70 | "spanNulls": false,
71 | "stacking": {
72 | "group": "A",
73 | "mode": "normal"
74 | },
75 | "thresholdsStyle": {
76 | "mode": "off"
77 | }
78 | },
79 | "decimals": 0,
80 | "mappings": [],
81 | "noValue": "0",
82 | "thresholds": {
83 | "mode": "absolute",
84 | "steps": [
85 | {
86 | "color": "green",
87 | "value": null
88 | },
89 | {
90 | "color": "red",
91 | "value": 80
92 | }
93 | ]
94 | },
95 | "unit": "short"
96 | },
97 | "overrides": []
98 | },
99 | "gridPos": {
100 | "h": 12,
101 | "w": 12,
102 | "x": 0,
103 | "y": 0
104 | },
105 | "id": 4,
106 | "options": {
107 | "legend": {
108 | "calcs": [
109 | "min",
110 | "max",
111 | "mean",
112 | "last"
113 | ],
114 | "displayMode": "table",
115 | "placement": "bottom",
116 | "showLegend": true
117 | },
118 | "tooltip": {
119 | "mode": "multi",
120 | "sort": "none"
121 | }
122 | },
123 | "pluginVersion": "8.2.1",
124 | "targets": [
125 | {
126 | "datasource": "${DS_PROMETHEUS}",
127 | "editorMode": "code",
128 | "exemplar": false,
129 | "expr": "sum by (server) (bungeecord_online_player{proxy_name=~\"$proxy\"})",
130 | "hide": false,
131 | "instant": false,
132 | "interval": "",
133 | "legendFormat": "{{server}}",
134 | "range": true,
135 | "refId": "A"
136 | }
137 | ],
138 | "title": "Online Players",
139 | "type": "timeseries"
140 | },
141 | {
142 | "datasource": "${DS_PROMETHEUS}",
143 | "description": "",
144 | "fieldConfig": {
145 | "defaults": {
146 | "color": {
147 | "mode": "palette-classic"
148 | },
149 | "custom": {
150 | "axisCenteredZero": false,
151 | "axisColorMode": "text",
152 | "axisLabel": "",
153 | "axisPlacement": "auto",
154 | "barAlignment": 0,
155 | "drawStyle": "line",
156 | "fillOpacity": 25,
157 | "gradientMode": "none",
158 | "hideFrom": {
159 | "legend": false,
160 | "tooltip": false,
161 | "viz": false
162 | },
163 | "lineInterpolation": "linear",
164 | "lineStyle": {
165 | "fill": "solid"
166 | },
167 | "lineWidth": 1,
168 | "pointSize": 5,
169 | "scaleDistribution": {
170 | "type": "linear"
171 | },
172 | "showPoints": "never",
173 | "spanNulls": false,
174 | "stacking": {
175 | "group": "A",
176 | "mode": "none"
177 | },
178 | "thresholdsStyle": {
179 | "mode": "off"
180 | }
181 | },
182 | "decimals": 0,
183 | "mappings": [],
184 | "thresholds": {
185 | "mode": "absolute",
186 | "steps": [
187 | {
188 | "color": "green",
189 | "value": null
190 | },
191 | {
192 | "color": "red",
193 | "value": 80
194 | }
195 | ]
196 | },
197 | "unit": "short"
198 | },
199 | "overrides": []
200 | },
201 | "gridPos": {
202 | "h": 12,
203 | "w": 12,
204 | "x": 12,
205 | "y": 0
206 | },
207 | "id": 2,
208 | "options": {
209 | "legend": {
210 | "calcs": [
211 | "mean",
212 | "max",
213 | "last"
214 | ],
215 | "displayMode": "table",
216 | "placement": "bottom",
217 | "showLegend": true
218 | },
219 | "tooltip": {
220 | "mode": "multi",
221 | "sort": "desc"
222 | }
223 | },
224 | "pluginVersion": "8.2.1",
225 | "targets": [
226 | {
227 | "datasource": "${DS_PROMETHEUS}",
228 | "editorMode": "code",
229 | "expr": "increase(bungeecord_player_chats_total{proxy_name=~\"$proxy\"}[1m])",
230 | "hide": false,
231 | "legendFormat": "chats",
232 | "range": true,
233 | "refId": "A"
234 | },
235 | {
236 | "datasource": "${DS_PROMETHEUS}",
237 | "editorMode": "code",
238 | "exemplar": false,
239 | "expr": "increase(bungeecord_server_list_pings_total{proxy_name=~\"$proxy\"}[1m])",
240 | "hide": false,
241 | "interval": "",
242 | "legendFormat": "server list pings",
243 | "range": true,
244 | "refId": "B"
245 | },
246 | {
247 | "datasource": "${DS_PROMETHEUS}",
248 | "editorMode": "code",
249 | "exemplar": false,
250 | "expr": "increase(bungeecord_player_connects_total{proxy_name=~\"$proxy\"}[1m])",
251 | "hide": false,
252 | "interval": "",
253 | "legendFormat": "connects",
254 | "range": true,
255 | "refId": "C"
256 | },
257 | {
258 | "datasource": "${DS_PROMETHEUS}",
259 | "editorMode": "code",
260 | "expr": "increase(bungeecord_player_kicks_total{proxy_name=~\"$proxy\"}[1m])",
261 | "hide": false,
262 | "legendFormat": "kicks",
263 | "range": true,
264 | "refId": "D"
265 | },
266 | {
267 | "datasource": "${DS_PROMETHEUS}",
268 | "editorMode": "code",
269 | "exemplar": false,
270 | "expr": "increase(bungeecord_player_disconnects_total{proxy_name=~\"$proxy\"}[1m])",
271 | "hide": false,
272 | "interval": "",
273 | "legendFormat": "disconnects",
274 | "range": true,
275 | "refId": "F"
276 | }
277 | ],
278 | "title": "Events per Minute",
279 | "type": "timeseries"
280 | },
281 | {
282 | "datasource": "${DS_PROMETHEUS}",
283 | "fieldConfig": {
284 | "defaults": {
285 | "color": {
286 | "mode": "palette-classic"
287 | },
288 | "custom": {
289 | "axisCenteredZero": false,
290 | "axisColorMode": "text",
291 | "axisLabel": "",
292 | "axisPlacement": "auto",
293 | "barAlignment": 0,
294 | "drawStyle": "line",
295 | "fillOpacity": 10,
296 | "gradientMode": "hue",
297 | "hideFrom": {
298 | "legend": false,
299 | "tooltip": false,
300 | "viz": false
301 | },
302 | "lineInterpolation": "smooth",
303 | "lineStyle": {
304 | "fill": "solid"
305 | },
306 | "lineWidth": 1,
307 | "pointSize": 5,
308 | "scaleDistribution": {
309 | "type": "linear"
310 | },
311 | "showPoints": "never",
312 | "spanNulls": false,
313 | "stacking": {
314 | "group": "A",
315 | "mode": "none"
316 | },
317 | "thresholdsStyle": {
318 | "mode": "off"
319 | }
320 | },
321 | "links": [],
322 | "mappings": [],
323 | "min": 0,
324 | "thresholds": {
325 | "mode": "absolute",
326 | "steps": [
327 | {
328 | "color": "blue",
329 | "value": null
330 | }
331 | ]
332 | },
333 | "unit": "bytes"
334 | },
335 | "overrides": []
336 | },
337 | "gridPos": {
338 | "h": 10,
339 | "w": 6,
340 | "x": 0,
341 | "y": 12
342 | },
343 | "id": 5,
344 | "links": [],
345 | "options": {
346 | "legend": {
347 | "calcs": [
348 | "min",
349 | "max",
350 | "mean",
351 | "last"
352 | ],
353 | "displayMode": "table",
354 | "placement": "bottom",
355 | "showLegend": true
356 | },
357 | "tooltip": {
358 | "mode": "single",
359 | "sort": "none"
360 | }
361 | },
362 | "pluginVersion": "8.0.6",
363 | "targets": [
364 | {
365 | "datasource": "${DS_PROMETHEUS}",
366 | "exemplar": false,
367 | "expr": "sum by (type) (bungeecord_jvm_memory{type='used',proxy_name=~\"$proxy\"})",
368 | "hide": false,
369 | "interval": "",
370 | "legendFormat": "{{server_name}} used",
371 | "refId": "C"
372 | },
373 | {
374 | "datasource": "${DS_PROMETHEUS}",
375 | "exemplar": false,
376 | "expr": "sum by (type) (bungeecord_jvm_memory{type='free',proxy_name=~\"$proxy\"})",
377 | "hide": false,
378 | "interval": "",
379 | "legendFormat": "{{server_name}} free",
380 | "refId": "A"
381 | }
382 | ],
383 | "title": "JVM Memory",
384 | "type": "timeseries"
385 | },
386 | {
387 | "aliasColors": {},
388 | "bars": false,
389 | "dashLength": 10,
390 | "dashes": false,
391 | "datasource": "${DS_PROMETHEUS}",
392 | "fill": 1,
393 | "fillGradient": 1,
394 | "gridPos": {
395 | "h": 10,
396 | "w": 6,
397 | "x": 6,
398 | "y": 12
399 | },
400 | "hiddenSeries": false,
401 | "id": 11,
402 | "legend": {
403 | "avg": false,
404 | "current": false,
405 | "max": false,
406 | "min": false,
407 | "show": true,
408 | "total": false,
409 | "values": false
410 | },
411 | "lines": true,
412 | "linewidth": 1,
413 | "nullPointMode": "null",
414 | "options": {
415 | "alertThreshold": true
416 | },
417 | "percentage": false,
418 | "pluginVersion": "9.4.3",
419 | "pointradius": 2,
420 | "points": false,
421 | "renderer": "flot",
422 | "seriesOverrides": [],
423 | "spaceLength": 10,
424 | "stack": false,
425 | "steppedLine": false,
426 | "targets": [
427 | {
428 | "datasource": "${DS_PROMETHEUS}",
429 | "exemplar": false,
430 | "expr": "sum(bungeecord_jvm_threads_current{proxy_name=~\"$proxy\"})",
431 | "interval": "",
432 | "legendFormat": "TOTAL",
433 | "refId": "A"
434 | },
435 | {
436 | "datasource": "${DS_PROMETHEUS}",
437 | "exemplar": false,
438 | "expr": "sum by (state) (bungeecord_jvm_threads_state{proxy_name=~\"$proxy\"})",
439 | "hide": false,
440 | "interval": "",
441 | "legendFormat": "{{state}}",
442 | "refId": "B"
443 | }
444 | ],
445 | "thresholds": [],
446 | "timeRegions": [],
447 | "title": "Threads",
448 | "tooltip": {
449 | "shared": true,
450 | "sort": 0,
451 | "value_type": "individual"
452 | },
453 | "type": "graph",
454 | "xaxis": {
455 | "mode": "time",
456 | "show": true,
457 | "values": []
458 | },
459 | "yaxes": [
460 | {
461 | "format": "short",
462 | "logBase": 1,
463 | "show": true
464 | },
465 | {
466 | "format": "short",
467 | "logBase": 1,
468 | "show": true
469 | }
470 | ],
471 | "yaxis": {
472 | "align": false
473 | }
474 | },
475 | {
476 | "datasource": "${DS_PROMETHEUS}",
477 | "description": "",
478 | "fieldConfig": {
479 | "defaults": {
480 | "color": {
481 | "mode": "thresholds"
482 | },
483 | "displayName": "",
484 | "links": [],
485 | "mappings": [],
486 | "max": 4000000000,
487 | "min": 0,
488 | "thresholds": {
489 | "mode": "absolute",
490 | "steps": [
491 | {
492 | "color": "blue",
493 | "value": null
494 | },
495 | {
496 | "color": "red",
497 | "value": 3000000000
498 | }
499 | ]
500 | },
501 | "unit": "bytes"
502 | },
503 | "overrides": []
504 | },
505 | "gridPos": {
506 | "h": 3,
507 | "w": 3,
508 | "x": 12,
509 | "y": 12
510 | },
511 | "id": 9,
512 | "options": {
513 | "orientation": "auto",
514 | "reduceOptions": {
515 | "calcs": [
516 | "last"
517 | ],
518 | "fields": "",
519 | "values": false
520 | },
521 | "showThresholdLabels": false,
522 | "showThresholdMarkers": true,
523 | "text": {}
524 | },
525 | "pluginVersion": "9.4.3",
526 | "repeat": "proxies",
527 | "repeatDirection": "v",
528 | "targets": [
529 | {
530 | "datasource": "${DS_PROMETHEUS}",
531 | "exemplar": false,
532 | "expr": "sum(bungeecord_jvm_memory{type='allocated', proxy_name=~\"$proxy\"}) - scalar(sum(bungeecord_jvm_memory{type='free', proxy_name=~\"$proxy\"}))",
533 | "instant": false,
534 | "interval": "",
535 | "intervalFactor": 1,
536 | "legendFormat": "",
537 | "refId": "A"
538 | }
539 | ],
540 | "title": "$proxy RAM",
541 | "type": "gauge"
542 | },
543 | {
544 | "datasource": "${DS_PROMETHEUS}",
545 | "fieldConfig": {
546 | "defaults": {
547 | "color": {
548 | "mode": "thresholds"
549 | },
550 | "mappings": [],
551 | "thresholds": {
552 | "mode": "absolute",
553 | "steps": [
554 | {
555 | "color": "blue",
556 | "value": null
557 | }
558 | ]
559 | }
560 | },
561 | "overrides": []
562 | },
563 | "gridPos": {
564 | "h": 3,
565 | "w": 2,
566 | "x": 15,
567 | "y": 12
568 | },
569 | "id": 18,
570 | "options": {
571 | "colorMode": "value",
572 | "graphMode": "none",
573 | "justifyMode": "center",
574 | "orientation": "auto",
575 | "reduceOptions": {
576 | "calcs": [
577 | "lastNotNull"
578 | ],
579 | "fields": "",
580 | "values": false
581 | },
582 | "text": {},
583 | "textMode": "auto"
584 | },
585 | "pluginVersion": "9.4.3",
586 | "targets": [
587 | {
588 | "datasource": "${DS_PROMETHEUS}",
589 | "exemplar": false,
590 | "expr": "count(count(bungeecord_jvm_memory{proxy_name=~\".*\"}) by (proxy_name))",
591 | "interval": "",
592 | "legendFormat": "",
593 | "refId": "A"
594 | }
595 | ],
596 | "title": "Proxies",
597 | "type": "stat"
598 | },
599 | {
600 | "datasource": "${DS_PROMETHEUS}",
601 | "fieldConfig": {
602 | "defaults": {
603 | "color": {
604 | "mode": "thresholds"
605 | },
606 | "mappings": [],
607 | "min": 0,
608 | "thresholds": {
609 | "mode": "absolute",
610 | "steps": [
611 | {
612 | "color": "blue",
613 | "value": null
614 | }
615 | ]
616 | },
617 | "unit": "none"
618 | },
619 | "overrides": []
620 | },
621 | "gridPos": {
622 | "h": 3,
623 | "w": 2,
624 | "x": 17,
625 | "y": 12
626 | },
627 | "id": 20,
628 | "options": {
629 | "colorMode": "value",
630 | "graphMode": "none",
631 | "justifyMode": "center",
632 | "orientation": "auto",
633 | "reduceOptions": {
634 | "calcs": [
635 | "lastNotNull"
636 | ],
637 | "fields": "",
638 | "values": false
639 | },
640 | "text": {},
641 | "textMode": "auto"
642 | },
643 | "pluginVersion": "9.4.3",
644 | "targets": [
645 | {
646 | "datasource": "${DS_PROMETHEUS}",
647 | "exemplar": false,
648 | "expr": "bungeecord_managed_servers{proxy_name=~\"$proxy\"}",
649 | "interval": "",
650 | "legendFormat": "{{proxy_name}}",
651 | "refId": "A"
652 | }
653 | ],
654 | "title": "Servers",
655 | "type": "stat"
656 | },
657 | {
658 | "datasource": "${DS_PROMETHEUS}",
659 | "fieldConfig": {
660 | "defaults": {
661 | "color": {
662 | "mode": "thresholds"
663 | },
664 | "mappings": [],
665 | "min": 0,
666 | "thresholds": {
667 | "mode": "absolute",
668 | "steps": [
669 | {
670 | "color": "blue",
671 | "value": null
672 | }
673 | ]
674 | },
675 | "unit": "none"
676 | },
677 | "overrides": []
678 | },
679 | "gridPos": {
680 | "h": 3,
681 | "w": 2,
682 | "x": 19,
683 | "y": 12
684 | },
685 | "id": 23,
686 | "options": {
687 | "colorMode": "value",
688 | "graphMode": "none",
689 | "justifyMode": "center",
690 | "orientation": "auto",
691 | "reduceOptions": {
692 | "calcs": [
693 | "lastNotNull"
694 | ],
695 | "fields": "",
696 | "values": false
697 | },
698 | "text": {},
699 | "textMode": "auto"
700 | },
701 | "pluginVersion": "9.4.3",
702 | "targets": [
703 | {
704 | "datasource": "${DS_PROMETHEUS}",
705 | "editorMode": "code",
706 | "exemplar": false,
707 | "expr": "bungeecord_installed_network_plugins{proxy_name=~\"$proxy\"}",
708 | "interval": "",
709 | "legendFormat": "{{proxy_name}}",
710 | "range": true,
711 | "refId": "A"
712 | }
713 | ],
714 | "title": "Plugins",
715 | "type": "stat"
716 | },
717 | {
718 | "datasource": "${DS_PROMETHEUS}",
719 | "fieldConfig": {
720 | "defaults": {
721 | "color": {
722 | "mode": "thresholds"
723 | },
724 | "mappings": [],
725 | "min": 0,
726 | "thresholds": {
727 | "mode": "absolute",
728 | "steps": [
729 | {
730 | "color": "blue",
731 | "value": null
732 | }
733 | ]
734 | },
735 | "unit": "none"
736 | },
737 | "overrides": []
738 | },
739 | "gridPos": {
740 | "h": 3,
741 | "w": 3,
742 | "x": 21,
743 | "y": 12
744 | },
745 | "id": 13,
746 | "options": {
747 | "colorMode": "value",
748 | "graphMode": "area",
749 | "justifyMode": "center",
750 | "orientation": "auto",
751 | "reduceOptions": {
752 | "calcs": [
753 | "lastNotNull"
754 | ],
755 | "fields": "",
756 | "values": false
757 | },
758 | "text": {},
759 | "textMode": "auto"
760 | },
761 | "pluginVersion": "9.4.3",
762 | "targets": [
763 | {
764 | "datasource": "${DS_PROMETHEUS}",
765 | "editorMode": "code",
766 | "exemplar": false,
767 | "expr": "sum(bungeecord_online_player{proxy_name=~\"$proxy\"})",
768 | "instant": false,
769 | "interval": "",
770 | "legendFormat": "Online Player",
771 | "range": true,
772 | "refId": "A"
773 | }
774 | ],
775 | "title": "Player Count",
776 | "transformations": [],
777 | "type": "stat"
778 | },
779 | {
780 | "datasource": "${DS_PROMETHEUS}",
781 | "fieldConfig": {
782 | "defaults": {
783 | "color": {
784 | "mode": "thresholds"
785 | },
786 | "custom": {
787 | "fillOpacity": 100,
788 | "gradientMode": "none",
789 | "hideFrom": {
790 | "legend": false,
791 | "tooltip": false,
792 | "viz": false
793 | },
794 | "lineWidth": 1
795 | },
796 | "mappings": [],
797 | "max": 1000,
798 | "min": 0,
799 | "thresholds": {
800 | "mode": "absolute",
801 | "steps": [
802 | {
803 | "color": "green",
804 | "value": null
805 | }
806 | ]
807 | },
808 | "unit": "ms"
809 | },
810 | "overrides": []
811 | },
812 | "gridPos": {
813 | "h": 7,
814 | "w": 9,
815 | "x": 12,
816 | "y": 15
817 | },
818 | "id": 22,
819 | "options": {
820 | "bucketOffset": 0,
821 | "bucketSize": 20,
822 | "combine": false,
823 | "legend": {
824 | "calcs": [],
825 | "displayMode": "list",
826 | "placement": "bottom",
827 | "showLegend": false
828 | }
829 | },
830 | "pluginVersion": "8.2.1",
831 | "targets": [
832 | {
833 | "datasource": "${DS_PROMETHEUS}",
834 | "exemplar": false,
835 | "expr": "clamp_max(avg(bungeecord_online_player_latency > 0),1000)",
836 | "interval": "",
837 | "legendFormat": "average player ping",
838 | "refId": "A"
839 | }
840 | ],
841 | "title": "Ping",
842 | "type": "histogram"
843 | },
844 | {
845 | "datasource": "${DS_PROMETHEUS}",
846 | "fieldConfig": {
847 | "defaults": {
848 | "color": {
849 | "mode": "thresholds"
850 | },
851 | "custom": {
852 | "align": "auto",
853 | "cellOptions": {
854 | "type": "auto"
855 | },
856 | "inspect": false
857 | },
858 | "mappings": [],
859 | "noValue": "No One 💤",
860 | "thresholds": {
861 | "mode": "absolute",
862 | "steps": [
863 | {
864 | "color": "green",
865 | "value": null
866 | },
867 | {
868 | "color": "red",
869 | "value": 80
870 | }
871 | ]
872 | }
873 | },
874 | "overrides": []
875 | },
876 | "gridPos": {
877 | "h": 7,
878 | "w": 3,
879 | "x": 21,
880 | "y": 15
881 | },
882 | "id": 25,
883 | "options": {
884 | "footer": {
885 | "countRows": false,
886 | "fields": "",
887 | "reducer": [
888 | "sum"
889 | ],
890 | "show": false
891 | },
892 | "showHeader": true,
893 | "sortBy": []
894 | },
895 | "pluginVersion": "9.4.3",
896 | "targets": [
897 | {
898 | "datasource": "${DS_PROMETHEUS}",
899 | "editorMode": "code",
900 | "exemplar": false,
901 | "expr": "bungeecord_online_player > 0",
902 | "format": "table",
903 | "instant": true,
904 | "interval": "",
905 | "legendFormat": "{{label_name}}",
906 | "range": false,
907 | "refId": "A"
908 | }
909 | ],
910 | "title": "Online Player List",
911 | "transformations": [
912 | {
913 | "id": "organize",
914 | "options": {
915 | "excludeByName": {
916 | "Time": true,
917 | "Value": true,
918 | "__name__": true,
919 | "instance": true,
920 | "job": true,
921 | "name": false,
922 | "proxy_name": true,
923 | "quantile": true,
924 | "server": false
925 | },
926 | "indexByName": {},
927 | "renameByName": {}
928 | }
929 | }
930 | ],
931 | "type": "table"
932 | }
933 | ],
934 | "refresh": "5s",
935 | "revision": 1,
936 | "schemaVersion": 38,
937 | "style": "dark",
938 | "tags": [
939 | "bungeecord"
940 | ],
941 | "templating": {
942 | "list": [
943 | {
944 | "current": {
945 | "selected": false,
946 | "text": "All",
947 | "value": "$__all"
948 | },
949 | "datasource": "${DS_PROMETHEUS}",
950 | "definition": "label_values(bungeecord_managed_servers, proxy_name)",
951 | "hide": 0,
952 | "includeAll": true,
953 | "multi": false,
954 | "name": "proxy",
955 | "options": [],
956 | "query": {
957 | "query": "label_values(bungeecord_managed_servers, proxy_name)",
958 | "refId": "StandardVariableQuery"
959 | },
960 | "refresh": 1,
961 | "regex": "",
962 | "skipUrlSync": false,
963 | "sort": 0,
964 | "tagValuesQuery": "",
965 | "tagsQuery": "",
966 | "type": "query",
967 | "useTags": false
968 | }
969 | ]
970 | },
971 | "time": {
972 | "from": "now-24h",
973 | "to": "now"
974 | },
975 | "timepicker": {},
976 | "timezone": "",
977 | "title": "Minecraft Network",
978 | "uid": "AltFNh7nz",
979 | "version": 8,
980 | "weekStart": ""
981 | }
--------------------------------------------------------------------------------
/docker/bungeecord/config/config.yml:
--------------------------------------------------------------------------------
1 | server_connect_timeout: 5000
2 | enforce_secure_profile: false
3 | remote_ping_cache: -1
4 | forge_support: true
5 | player_limit: -1
6 | permissions:
7 | default:
8 | - bungeecord.command.server
9 | - bungeecord.command.list
10 | admin:
11 | - bungeecord.command.alert
12 | - bungeecord.command.end
13 | - bungeecord.command.ip
14 | - bungeecord.command.reload
15 | - bungeecord.command.kick
16 | timeout: 30000
17 | log_commands: false
18 | network_compression_threshold: 256
19 | online_mode: false
20 | disabled_commands:
21 | - disabledcommandhere
22 | servers:
23 | lobby:
24 | motd: '&1Just another Waterfall - Forced Host'
25 | address: lobby:25565
26 | restricted: false
27 | smp:
28 | motd: '&1Just another Waterfall - Forced Host'
29 | address: smp:25565
30 | restricted: false
31 | listeners:
32 | - query_port: 25565
33 | motd: '&1Another Bungee server to test Prometheus'
34 | tab_list: GLOBAL_PING
35 | query_enabled: false
36 | proxy_protocol: false
37 | forced_hosts:
38 | pvp.md-5.net: pvp
39 | ping_passthrough: false
40 | priorities:
41 | - lobby
42 | - smp
43 | bind_local_address: true
44 | host: 0.0.0.0:25565
45 | max_players: 1
46 | tab_size: 60
47 | force_default_server: false
48 | ip_forward: false
49 | remote_ping_timeout: 5000
50 | reject_transfers: false
51 | prevent_proxy_connections: false
52 | groups:
53 | md_5:
54 | - admin
55 | connection_throttle: 4000
56 | stats: 6ef14939-5f39-49f5-9a56-e652a1bf4680
57 | connection_throttle_limit: 3
58 | log_pings: true
59 |
--------------------------------------------------------------------------------
/docker/docker-compose-bungeecord.yml:
--------------------------------------------------------------------------------
1 | services:
2 | bungeecord:
3 | image: itzg/mc-proxy
4 | ports:
5 | - "25565:25565"
6 | - "9985:9985"
7 | volumes:
8 | - "./bungeecord/plugins:/server/plugins" # <- read/write
9 | - "./bungeecord/config:/config:ro"
10 | depends_on:
11 | - smp
12 | - lobby
13 | environment:
14 | TYPE: "WATERFALL"
15 | VELOCITY_VERSION: "latest"
16 | MEMORY: "256m"
17 |
18 | smp:
19 | image: itzg/minecraft-server
20 | environment:
21 | EULA: "TRUE"
22 | ONLINE_MODE: "FALSE"
23 | TYPE: "PAPER"
24 | MAX_MEMORY: "1G"
25 | COPY_CONFIG_DEST: "/data"
26 | USE_AIKAR_FLAGS: "FALSE"
27 |
28 | lobby:
29 | image: itzg/minecraft-server
30 | environment:
31 | EULA: "TRUE"
32 | ONLINE_MODE: "FALSE"
33 | TYPE: "PAPER"
34 | MAX_MEMORY: "1G"
35 | COPY_CONFIG_DEST: "/data"
36 | USE_AIKAR_FLAGS: "FALSE"
37 |
--------------------------------------------------------------------------------
/docker/docker-compose-velocity.yml:
--------------------------------------------------------------------------------
1 | services:
2 | velocity:
3 | image: itzg/mc-proxy
4 | ports:
5 | - "25565:25565"
6 | - "9985:9985"
7 | volumes:
8 | - "./velocity/plugins:/server/plugins" # <- read/write
9 | - "./velocity/config:/config:ro"
10 | depends_on:
11 | - smp
12 | - lobby
13 | environment:
14 | TYPE: "VELOCITY"
15 | VELOCITY_VERSION: "latest"
16 | MEMORY: "256m"
17 |
18 |
19 | smp:
20 | image: itzg/minecraft-server
21 | environment:
22 | EULA: "TRUE"
23 | ONLINE_MODE: "FALSE"
24 | TYPE: "PAPER"
25 | MAX_MEMORY: "1G"
26 | COPY_CONFIG_DEST: "/data"
27 | USE_AIKAR_FLAGS: "FALSE"
28 |
29 | lobby:
30 | image: itzg/minecraft-server
31 | environment:
32 | EULA: "TRUE"
33 | ONLINE_MODE: "FALSE"
34 | TYPE: "PAPER"
35 | MAX_MEMORY: "1G"
36 | COPY_CONFIG_DEST: "/data"
37 | USE_AIKAR_FLAGS: "FALSE"
38 |
--------------------------------------------------------------------------------
/docker/velocity/config/velocity.toml:
--------------------------------------------------------------------------------
1 | # Config version. Do not change this
2 | config-version = "2.7"
3 |
4 | # What port should the proxy be bound to? By default, we'll bind to all addresses on port 25565.
5 | bind = "0.0.0.0:25565"
6 |
7 | # What should be the MOTD? This gets displayed when the player adds your server to
8 | # their server list. Only MiniMessage format is accepted.
9 | motd = "<#09add3>A Velocity Server to test Prometheus Exporter"
10 |
11 | # What should we display for the maximum number of players? (Velocity does not support a cap
12 | # on the number of players online.)
13 | show-max-players = 500
14 |
15 | # Should we authenticate players with Mojang? By default, this is on.
16 | online-mode = false
17 |
18 | # Should the proxy enforce the new public key security standard? By default, this is on.
19 | force-key-authentication = true
20 |
21 | # If client's ISP/AS sent from this proxy is different from the one from Mojang's
22 | # authentication server, the player is kicked. This disallows some VPN and proxy
23 | # connections but is a weak form of protection.
24 | prevent-client-proxy-connections = false
25 |
26 | # Should we forward IP addresses and other data to backend servers?
27 | # Available options:
28 | # - "none": No forwarding will be done. All players will appear to be connecting
29 | # from the proxy and will have offline-mode UUIDs.
30 | # - "legacy": Forward player IPs and UUIDs in a BungeeCord-compatible format. Use this
31 | # if you run servers using Minecraft 1.12 or lower.
32 | # - "bungeeguard": Forward player IPs and UUIDs in a format supported by the BungeeGuard
33 | # plugin. Use this if you run servers using Minecraft 1.12 or lower, and are
34 | # unable to implement network level firewalling (on a shared host).
35 | # - "modern": Forward player IPs and UUIDs as part of the login process using
36 | # Velocity's native forwarding. Only applicable for Minecraft 1.13 or higher.
37 | player-info-forwarding-mode = "NONE"
38 |
39 | # If you are using modern or BungeeGuard IP forwarding, configure a file that contains a unique secret here.
40 | # The file is expected to be UTF-8 encoded and not empty.
41 | forwarding-secret-file = "forwarding.secret"
42 |
43 | # Announce whether or not your server supports Forge. If you run a modded server, we
44 | # suggest turning this on.
45 | #
46 | # If your network runs one modpack consistently, consider using ping-passthrough = "mods"
47 | # instead for a nicer display in the server list.
48 | announce-forge = false
49 |
50 | # If enabled (default is false) and the proxy is in online mode, Velocity will kick
51 | # any existing player who is online if a duplicate connection attempt is made.
52 | kick-existing-players = false
53 |
54 | # Should Velocity pass server list ping requests to a backend server?
55 | # Available options:
56 | # - "disabled": No pass-through will be done. The velocity.toml and server-icon.png
57 | # will determine the initial server list ping response.
58 | # - "mods": Passes only the mod list from your backend server into the response.
59 | # The first server in your try list (or forced host) with a mod list will be
60 | # used. If no backend servers can be contacted, Velocity won't display any
61 | # mod information.
62 | # - "description": Uses the description and mod list from the backend server. The first
63 | # server in the try (or forced host) list that responds is used for the
64 | # description and mod list.
65 | # - "all": Uses the backend server's response as the proxy response. The Velocity
66 | # configuration is used if no servers could be contacted.
67 | ping-passthrough = "DISABLED"
68 |
69 | # If not enabled (default is true) player IP addresses will be replaced by in logs
70 | enable-player-address-logging = true
71 |
72 | [servers]
73 | # Configure your servers here. Each key represents the server's name, and the value
74 | # represents the IP address of the server to connect to.
75 | lobby = "lobby:25565"
76 | smp = "smp:25565"
77 |
78 | # In what order we should try servers when a player logs in or is kicked from a server.
79 | try = [
80 | "lobby",
81 | "smp"
82 | ]
83 |
84 | [forced-hosts]
85 | # Configure your forced hosts here.
86 |
87 | [advanced]
88 | # How large a Minecraft packet has to be before we compress it. Setting this to zero will
89 | # compress all packets, and setting it to -1 will disable compression entirely.
90 | compression-threshold = 256
91 |
92 | # How much compression should be done (from 0-9). The default is -1, which uses the
93 | # default level of 6.
94 | compression-level = -1
95 |
96 | # How fast (in milliseconds) are clients allowed to connect after the last connection? By
97 | # default, this is three seconds. Disable this by setting this to 0.
98 | login-ratelimit = 3000
99 |
100 | # Specify a custom timeout for connection timeouts here. The default is five seconds.
101 | connection-timeout = 5000
102 |
103 | # Specify a read timeout for connections here. The default is 30 seconds.
104 | read-timeout = 30000
105 |
106 | # Enables compatibility with HAProxy's PROXY protocol. If you don't know what this is for, then
107 | # don't enable it.
108 | haproxy-protocol = false
109 |
110 | # Enables TCP fast open support on the proxy. Requires the proxy to run on Linux.
111 | tcp-fast-open = false
112 |
113 | # Enables BungeeCord plugin messaging channel support on Velocity.
114 | bungee-plugin-message-channel = true
115 |
116 | # Shows ping requests to the proxy from clients.
117 | show-ping-requests = false
118 |
119 | # By default, Velocity will attempt to gracefully handle situations where the user unexpectedly
120 | # loses connection to the server without an explicit disconnect message by attempting to fall the
121 | # user back, except in the case of read timeouts. BungeeCord will disconnect the user instead. You
122 | # can disable this setting to use the BungeeCord behavior.
123 | failover-on-unexpected-server-disconnect = true
124 |
125 | # Declares the proxy commands to 1.13+ clients.
126 | announce-proxy-commands = true
127 |
128 | # Enables the logging of commands
129 | log-command-executions = false
130 |
131 | # Enables logging of player connections when connecting to the proxy, switching servers
132 | # and disconnecting from the proxy.
133 | log-player-connections = true
134 |
135 | # Allows players transferred from other hosts via the
136 | # Transfer packet (Minecraft 1.20.5) to be received.
137 | accepts-transfers = false
138 |
139 | [query]
140 | # Whether to enable responding to GameSpy 4 query responses or not.
141 | enabled = false
142 |
143 | # If query is enabled, on what port should the query protocol listen on?
144 | port = 25565
145 |
146 | # This is the map name that is reported to the query services.
147 | map = "Velocity"
148 |
149 | # Whether plugins should be shown in query response by default or not
150 | show-plugins = false
151 |
--------------------------------------------------------------------------------
/images/dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weihao/bungeecord-prometheus-exporter/993acbc55dd597cefb23b621f83906440904eec0/images/dashboard.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | org.akadia
6 | bungeecord-prometheus-exporter
7 | BungeeCordPrometheusExporter
8 | 3.2.8-SNAPSHOT
9 | jar
10 | https://akadia.org
11 |
12 | BungeeCord Prometheus Exporter
13 |
14 |
15 | https://github.com/weihao/bungeecord-prometheus-exporter
16 | scm:git:git://github.com/weihao/bungeecord-prometheus-exporter.git
17 | scm:git:git@github.com:weihao/bungeecord-prometheus-exporter.git
18 | HEAD
19 |
20 |
21 |
22 |
23 | github
24 | GitHub Packages
25 | https://maven.pkg.github.com/weihao/bungeecord-prometheus-exporter
26 |
27 |
28 |
29 |
30 |
31 | 11.0.24
32 | 0.16.0
33 | 2.9.0
34 | 5.15.2
35 | 5.10.3
36 | 3.5.2
37 | 3.14.0
38 |
39 |
40 |
41 |
42 | bungeecord-repo
43 | https://oss.sonatype.org/content/repositories/snapshots
44 |
45 |
46 | jitpack.io
47 | https://jitpack.io
48 |
49 |
50 | velocity
51 | https://nexus.velocitypowered.com/repository/maven-public/
52 |
53 |
54 |
55 |
56 |
57 | com.google.code.gson
58 | gson
59 | 2.11.0
60 |
61 |
62 | com.velocitypowered
63 | velocity-api
64 | 3.3.0-SNAPSHOT
65 | provided
66 |
67 |
68 | net.md-5
69 | bungeecord-api
70 | 1.20-R0.2
71 | jar
72 | provided
73 |
74 |
75 | net.md-5
76 | bungeecord-api
77 | 1.20-R0.2
78 | javadoc
79 | provided
80 |
81 |
82 | org.eclipse.jetty
83 | jetty-server
84 | ${jetty.version}
85 |
86 |
87 | io.prometheus
88 | simpleclient_common
89 | ${prometheus-client.version}
90 |
91 |
92 | io.prometheus
93 | simpleclient_hotspot
94 | ${prometheus-client.version}
95 |
96 |
97 | com.jayway.jsonpath
98 | json-path
99 | ${json-path.version}
100 |
101 |
102 |
103 | io.prometheus
104 | simpleclient
105 | 0.16.0
106 |
107 |
108 |
109 | io.prometheus
110 | simpleclient_hotspot
111 | 0.16.0
112 |
113 |
114 |
115 | io.prometheus
116 | simpleclient_httpserver
117 | 0.16.0
118 |
119 |
120 |
121 | io.prometheus
122 | simpleclient_pushgateway
123 | 0.16.0
124 |
125 |
126 | org.bstats
127 | bstats-bungeecord
128 | 3.1.0
129 | compile
130 |
131 |
132 |
133 | org.bstats
134 | bstats-velocity
135 | 3.1.0
136 | compile
137 |
138 |
139 |
140 | com.github.proxiodev.redisbungee
141 | RedisBungee-Bungee
142 | 0.13.0
143 | provided
144 |
145 |
146 |
147 | org.junit.jupiter
148 | junit-jupiter
149 | ${junit.jupiter.version}
150 | test
151 |
152 |
153 | org.mockito
154 | mockito-core
155 | ${org.mockito.version}
156 | test
157 |
158 |
159 | org.mockito
160 | mockito-junit-jupiter
161 | ${org.mockito.version}
162 | test
163 |
164 |
165 | io.rest-assured
166 | rest-assured
167 | 5.5.0
168 | test
169 |
170 |
171 |
172 |
173 |
174 | src/main/java
175 | src/test/java
176 |
177 |
178 |
179 | pl.project13.maven
180 | git-commit-id-plugin
181 | 4.9.10
182 |
183 |
184 | git-info
185 |
186 | revision
187 |
188 |
189 |
190 |
191 | true
192 | yyyy-MM-dd HH:mm:ss
193 | true
194 | true
195 |
196 | false
197 | false
198 | 7
199 | false
200 |
201 |
202 |
203 |
204 | org.apache.maven.plugins
205 | maven-shade-plugin
206 | 3.6.0
207 |
208 |
209 | package
210 |
211 | shade
212 |
213 |
214 | false
215 |
216 |
217 |
218 | ${project.artifactId}
219 | ${project.version}
220 |
221 |
222 |
223 |
224 |
225 | org.bstats
226 |
227 | org.akadia.prometheus.bstats
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 | org.apache.maven.plugins
236 | maven-release-plugin
237 | 3.1.1
238 |
239 | [ci skip]
240 | @{project.version}
241 |
242 |
243 |
244 | maven-assembly-plugin
245 |
246 |
247 | src/main/assembly/package.xml
248 |
249 |
250 |
251 |
252 | build
253 | package
254 |
255 | single
256 |
257 |
258 |
259 |
260 |
261 | org.apache.maven.plugins
262 | maven-compiler-plugin
263 | ${maven-compiler-plugin.version}
264 |
265 | 17
266 |
267 |
268 |
269 | org.apache.maven.plugins
270 | maven-surefire-plugin
271 | ${maven-surefire-plugin.version}
272 |
273 |
274 |
275 |
276 | src/main/resources/
277 | true
278 |
279 |
280 |
281 |
282 |
--------------------------------------------------------------------------------
/src/main/assembly/package.xml:
--------------------------------------------------------------------------------
1 |
4 | bin
5 | false
6 |
7 | zip
8 |
9 |
10 |
11 | ${project.build.directory}/${artifactId}-${version}.jar
12 | /
13 | BungeeCordPrometheusExporter.jar
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/MetricRegistry.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus;
2 |
3 | import org.akadia.prometheus.interfaces.Metric;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class MetricRegistry {
9 | private static final MetricRegistry INSTANCE = new MetricRegistry();
10 |
11 | private final List metrics = new ArrayList<>();
12 |
13 | private MetricRegistry() {
14 |
15 | }
16 |
17 | public static MetricRegistry getInstance() {
18 | return INSTANCE;
19 | }
20 |
21 | public void register(Metric metric) {
22 | this.metrics.add(metric);
23 | }
24 |
25 | void collectMetrics() {
26 | this.metrics.forEach(Metric::collect);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/MetricsServer.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus;
2 |
3 | import io.prometheus.client.Collector;
4 | import io.prometheus.client.CollectorRegistry;
5 | import io.prometheus.client.exporter.common.TextFormat;
6 | import jakarta.servlet.http.HttpServletRequest;
7 | import jakarta.servlet.http.HttpServletResponse;
8 | import org.eclipse.jetty.http.HttpStatus;
9 | import org.eclipse.jetty.server.Request;
10 | import org.eclipse.jetty.server.Server;
11 | import org.eclipse.jetty.server.handler.AbstractHandler;
12 | import org.eclipse.jetty.server.handler.gzip.GzipHandler;
13 |
14 | import java.io.IOException;
15 | import java.net.InetSocketAddress;
16 | import java.util.Enumeration;
17 |
18 | public class MetricsServer {
19 |
20 | private final String host;
21 | private final int port;
22 | private final PrometheusExporter prometheusExporter;
23 |
24 | private Server server;
25 |
26 | public MetricsServer(String host, int port, PrometheusExporter prometheusExporter) {
27 | this.host = host;
28 | this.port = port;
29 | this.prometheusExporter = prometheusExporter;
30 | }
31 |
32 | public void start() throws Exception {
33 | GzipHandler gzipHandler = new GzipHandler();
34 | gzipHandler.setHandler(new AbstractHandler() {
35 | @Override
36 | public void handle(String target, Request request, HttpServletRequest httpServletRequest, HttpServletResponse response) throws IOException {
37 | if (!target.equals("/metrics")) {
38 | response.sendError(HttpServletResponse.SC_NOT_FOUND);
39 | return;
40 | }
41 |
42 | try {
43 | MetricRegistry.getInstance().collectMetrics();
44 | response.setStatus(HttpStatus.OK_200);
45 | response.setContentType(TextFormat.CONTENT_TYPE_004);
46 | Enumeration metricFamilySamplesEnumeration = CollectorRegistry.defaultRegistry.metricFamilySamples();
47 |
48 | TextFormat.write004(response.getWriter(), CollectorRegistry.defaultRegistry.metricFamilySamples());
49 |
50 | request.setHandled(true);
51 | } catch (IOException e) {
52 | prometheusExporter.warn("Failed to read server statistic: " + e.getMessage());
53 | response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
54 | }
55 |
56 | }
57 | });
58 |
59 | InetSocketAddress address = new InetSocketAddress(host, port);
60 | server = new Server(address);
61 | server.setHandler(gzipHandler);
62 |
63 | server.start();
64 | }
65 |
66 | public void stop() throws Exception {
67 | if (server == null) {
68 | return;
69 | }
70 |
71 | server.stop();
72 | }
73 | }
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/PrometheusExporter.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus;
2 |
3 | public interface PrometheusExporter {
4 | void info(String info);
5 |
6 | void warn(String warning);
7 |
8 | String getPrefix();
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/PrometheusBungeeCordExporter.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord;
2 |
3 | import io.prometheus.client.CollectorRegistry;
4 | import net.md_5.bungee.api.plugin.Listener;
5 | import net.md_5.bungee.api.plugin.Plugin;
6 | import org.akadia.prometheus.MetricRegistry;
7 | import org.akadia.prometheus.MetricsServer;
8 | import org.akadia.prometheus.PrometheusExporter;
9 | import org.akadia.prometheus.bungeecord.listeners.LoginEventListener;
10 | import org.akadia.prometheus.bungeecord.listeners.PlayerChatEventListener;
11 | import org.akadia.prometheus.bungeecord.listeners.PlayerCommandEventListener;
12 | import org.akadia.prometheus.bungeecord.listeners.PlayerDisconnectEventListener;
13 | import org.akadia.prometheus.bungeecord.listeners.PlayerJoinedNetworkEventListener;
14 | import org.akadia.prometheus.bungeecord.listeners.PlayerKickEventListener;
15 | import org.akadia.prometheus.bungeecord.listeners.PlayerLeftNetworkEventListener;
16 | import org.akadia.prometheus.bungeecord.listeners.ProxyPingEventListener;
17 | import org.akadia.prometheus.bungeecord.metrics.InstalledNetworkPlugins;
18 | import org.akadia.prometheus.bungeecord.metrics.ManagedServers;
19 | import org.akadia.prometheus.bungeecord.metrics.OnlinePlayer;
20 | import org.akadia.prometheus.bungeecord.metrics.OnlinePlayerLatency;
21 | import org.akadia.prometheus.bungeecord.metrics.RedisBungeeOnlinePlayer;
22 | import org.akadia.prometheus.bungeecord.metrics.RedisBungeeOnlineProxies;
23 | import org.akadia.prometheus.config.ConfigManager;
24 | import org.akadia.prometheus.interfaces.Configurable;
25 | import org.akadia.prometheus.interfaces.CountableMetrics;
26 | import org.akadia.prometheus.interfaces.Metric;
27 | import org.akadia.prometheus.interfaces.MetricWrapper;
28 | import org.akadia.prometheus.metrics.JvmGarbageCollectorWrapper;
29 | import org.akadia.prometheus.metrics.JvmMemory;
30 | import org.akadia.prometheus.metrics.JvmThreadsWrapper;
31 | import org.bstats.bungeecord.Metrics;
32 |
33 | import java.io.IOException;
34 | import java.util.ArrayList;
35 | import java.util.List;
36 |
37 | public class PrometheusBungeeCordExporter extends Plugin implements PrometheusExporter {
38 | String prefix;
39 |
40 | @Override
41 | public void onEnable() {
42 | try {
43 | ConfigManager configManager = new ConfigManager(getDataFolder());
44 | startMetricsServer(configManager);
45 | } catch (IOException e) {
46 | e.printStackTrace();
47 | }
48 | }
49 |
50 | private void startMetricsServer(ConfigManager configManager) {
51 | if (configManager.getConfig().get("bstats").equals("true")) {
52 | try {
53 | new Metrics(this, 11269);
54 | } catch (IllegalStateException ex) {
55 | getLogger().info("bStats Metrics failed to start");
56 | }
57 | }
58 |
59 | this.prefix = configManager.getConfig().getOrDefault("prefix", "bungeecord_");
60 |
61 | String host = configManager.getConfig().getOrDefault("host", "127.0.0.1");
62 | int port = Integer.parseInt(configManager.getConfig().getOrDefault("port", "9985"));
63 |
64 | MetricsServer server = new MetricsServer(host, port, this);
65 |
66 | List configurables = new ArrayList<>();
67 | configurables.add(new LoginEventListener(this));
68 | configurables.add(new PlayerDisconnectEventListener(this));
69 | configurables.add(new PlayerChatEventListener(this));
70 | configurables.add(new PlayerCommandEventListener(this));
71 | configurables.add(new PlayerKickEventListener(this));
72 | configurables.add(new ProxyPingEventListener(this));
73 | configurables.add(new JvmGarbageCollectorWrapper(this));
74 | configurables.add(new JvmMemory(this));
75 | configurables.add(new JvmThreadsWrapper(this));
76 | configurables.add(new OnlinePlayer(this));
77 | configurables.add(new OnlinePlayerLatency(this));
78 | configurables.add(new ManagedServers(this));
79 | configurables.add(new InstalledNetworkPlugins(this));
80 |
81 | configurables.add(new PlayerJoinedNetworkEventListener(this));
82 | configurables.add(new PlayerLeftNetworkEventListener(this));
83 | configurables.add(new RedisBungeeOnlinePlayer(this));
84 | configurables.add(new RedisBungeeOnlineProxies(this));
85 |
86 | for (Configurable configurable : configurables) {
87 | if (configManager.getConfig().getOrDefault(configurable.getConfigKey(), "true").equals("false")) {
88 | this.info(configurable.getConfigKey() + " is disabled in the config");
89 | continue;
90 | }
91 | this.info(configurable.getConfigKey() + " is enabled in the config");
92 | if (configurable instanceof CountableMetrics) {
93 | this.getProxy().getPluginManager().registerListener(this, (Listener) configurable);
94 | } else if (configurable instanceof MetricWrapper) {
95 | CollectorRegistry.defaultRegistry.register(((MetricWrapper) configurable).getCollector());
96 | } else {
97 | MetricRegistry.getInstance().register((Metric) configurable);
98 | }
99 | }
100 |
101 | try {
102 | server.start();
103 | this.info("Started Prometheus metrics endpoint at: " + host + ":" + port);
104 | } catch (Exception e) {
105 | this.warn("Could not start embedded Jetty server");
106 | }
107 |
108 | this.info("Initialized completed");
109 | }
110 |
111 | @Override
112 | public void info(String info) {
113 | this.getLogger().info(info);
114 | }
115 |
116 | @Override
117 | public void warn(String warning) {
118 | this.getLogger().warning(warning);
119 | }
120 |
121 | @Override
122 | public String getPrefix() {
123 | return this.prefix;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/listeners/LoginEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.listeners;
2 |
3 | import net.md_5.bungee.api.event.LoginEvent;
4 | import net.md_5.bungee.api.plugin.Listener;
5 | import net.md_5.bungee.event.EventHandler;
6 | import org.akadia.prometheus.PrometheusExporter;
7 | import org.akadia.prometheus.interfaces.CountableMetrics;
8 |
9 | public class LoginEventListener extends CountableMetrics implements Listener {
10 |
11 | public LoginEventListener(PrometheusExporter plugin) {
12 | super(plugin);
13 | }
14 |
15 | @EventHandler
16 | public void onLoginEvent(LoginEvent event) {
17 | this.getCounter().inc();
18 | }
19 |
20 | @Override
21 | public String getHelp() {
22 | return "the number of player logins in BungeeCord";
23 | }
24 |
25 | @Override
26 | public String getConfigKey() {
27 | return "player_connects";
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/listeners/PlayerChatEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.listeners;
2 |
3 | import net.md_5.bungee.api.event.ChatEvent;
4 | import net.md_5.bungee.api.plugin.Listener;
5 | import net.md_5.bungee.event.EventHandler;
6 | import org.akadia.prometheus.PrometheusExporter;
7 | import org.akadia.prometheus.interfaces.CountableMetrics;
8 |
9 | public class PlayerChatEventListener extends CountableMetrics implements Listener {
10 |
11 | public PlayerChatEventListener(PrometheusExporter plugin) {
12 | super(plugin);
13 | }
14 |
15 | @EventHandler
16 | public void onPlayerChatEvent(ChatEvent event) {
17 | if (event.isCommand()) {
18 | return;
19 | }
20 | this.getCounter().inc();
21 | }
22 |
23 | @Override
24 | public String getHelp() {
25 | return "the number of player chat in BungeeCord";
26 | }
27 |
28 | @Override
29 | public String getConfigKey() {
30 | return "player_chats";
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/listeners/PlayerCommandEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.listeners;
2 |
3 | import net.md_5.bungee.api.event.ChatEvent;
4 | import net.md_5.bungee.api.plugin.Listener;
5 | import net.md_5.bungee.event.EventHandler;
6 | import org.akadia.prometheus.PrometheusExporter;
7 | import org.akadia.prometheus.interfaces.CountableMetrics;
8 |
9 | public class PlayerCommandEventListener extends CountableMetrics implements Listener {
10 |
11 | public PlayerCommandEventListener(PrometheusExporter plugin) {
12 | super(plugin);
13 | }
14 |
15 | @EventHandler
16 | public void onPlayerCommandEvent(ChatEvent event) {
17 | if (!event.isCommand()) {
18 | return;
19 | }
20 | this.getCounter().inc();
21 | }
22 |
23 | @Override
24 | public String getHelp() {
25 | return "the number of player commands in BungeeCord";
26 | }
27 |
28 | @Override
29 | public String getConfigKey() {
30 | return "player_commands";
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/listeners/PlayerDisconnectEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.listeners;
2 |
3 | import net.md_5.bungee.api.event.PlayerDisconnectEvent;
4 | import net.md_5.bungee.api.plugin.Listener;
5 | import net.md_5.bungee.event.EventHandler;
6 | import org.akadia.prometheus.PrometheusExporter;
7 | import org.akadia.prometheus.interfaces.CountableMetrics;
8 |
9 | public class PlayerDisconnectEventListener extends CountableMetrics implements Listener {
10 |
11 | public PlayerDisconnectEventListener(PrometheusExporter plugin) {
12 | super(plugin);
13 | }
14 |
15 | @EventHandler
16 | public void onPlayerDisconnectEvent(PlayerDisconnectEvent event) {
17 | this.getCounter().inc();
18 | }
19 |
20 | @Override
21 | public String getHelp() {
22 | return "the number of player disconnects in BungeeCord";
23 | }
24 |
25 | @Override
26 | public String getConfigKey() {
27 | return "player_disconnects";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/listeners/PlayerJoinedNetworkEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.listeners;
2 |
3 | import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
4 | import net.md_5.bungee.api.plugin.Listener;
5 | import net.md_5.bungee.event.EventHandler;
6 | import org.akadia.prometheus.PrometheusExporter;
7 | import org.akadia.prometheus.interfaces.CountableMetrics;
8 |
9 | public class PlayerJoinedNetworkEventListener extends CountableMetrics implements Listener {
10 |
11 | public PlayerJoinedNetworkEventListener(PrometheusExporter plugin) {
12 | super(plugin);
13 | }
14 |
15 | @EventHandler
16 | public void onPlayerDisconnectEvent(PlayerJoinedNetworkEvent event) {
17 | this.getCounter().inc();
18 | }
19 |
20 | @Override
21 | public String getHelp() {
22 | return "the number of players joined in redisbungee";
23 | }
24 |
25 | @Override
26 | public String getConfigKey() {
27 | return "redis_player_connects";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/listeners/PlayerKickEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.listeners;
2 |
3 | import net.md_5.bungee.api.event.ServerKickEvent;
4 | import net.md_5.bungee.api.plugin.Listener;
5 | import net.md_5.bungee.event.EventHandler;
6 | import org.akadia.prometheus.PrometheusExporter;
7 | import org.akadia.prometheus.interfaces.CountableMetrics;
8 |
9 | public class PlayerKickEventListener extends CountableMetrics implements Listener {
10 |
11 | public PlayerKickEventListener(PrometheusExporter plugin) {
12 | super(plugin);
13 | }
14 |
15 | @EventHandler
16 | public void onPlayerDisconnectEvent(ServerKickEvent event) {
17 | this.getCounter().inc();
18 | }
19 |
20 | @Override
21 | public String getHelp() {
22 | return "the number of player kicked in BungeeCord";
23 | }
24 |
25 | @Override
26 | public String getConfigKey() {
27 | return "player_kicks";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/listeners/PlayerLeftNetworkEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.listeners;
2 |
3 | import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
4 | import net.md_5.bungee.api.plugin.Listener;
5 | import net.md_5.bungee.event.EventHandler;
6 | import org.akadia.prometheus.PrometheusExporter;
7 | import org.akadia.prometheus.interfaces.CountableMetrics;
8 |
9 | public class PlayerLeftNetworkEventListener extends CountableMetrics implements Listener {
10 |
11 | public PlayerLeftNetworkEventListener(PrometheusExporter plugin) {
12 | super(plugin);
13 | }
14 |
15 | @EventHandler
16 | public void onPlayerDisconnectEvent(PlayerLeftNetworkEvent event) {
17 | this.getCounter().inc();
18 | }
19 |
20 | @Override
21 | public String getHelp() {
22 | return "the number of players disconnects in redisbungee";
23 | }
24 |
25 | @Override
26 | public String getConfigKey() {
27 | return "redis_player_disconnects";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/listeners/ProxyPingEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.listeners;
2 |
3 | import net.md_5.bungee.api.event.ProxyPingEvent;
4 | import net.md_5.bungee.api.plugin.Listener;
5 | import net.md_5.bungee.event.EventHandler;
6 | import org.akadia.prometheus.PrometheusExporter;
7 | import org.akadia.prometheus.interfaces.CountableMetrics;
8 |
9 | public class ProxyPingEventListener extends CountableMetrics implements Listener {
10 |
11 | public ProxyPingEventListener(PrometheusExporter plugin) {
12 | super(plugin);
13 | }
14 |
15 | @EventHandler
16 | public void onProxyPingEvent(ProxyPingEvent event) {
17 | this.getCounter().inc();
18 | }
19 |
20 | @Override
21 | public String getHelp() {
22 | return "the number of server list pings in BungeeCord";
23 | }
24 |
25 | @Override
26 | public String getConfigKey() {
27 | return "server_list_pings";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/metrics/InstalledNetworkPlugins.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.metrics;
2 |
3 | import org.akadia.prometheus.bungeecord.PrometheusBungeeCordExporter;
4 | import org.akadia.prometheus.interfaces.GauageMetric;
5 |
6 | public class InstalledNetworkPlugins extends GauageMetric {
7 |
8 | public InstalledNetworkPlugins(PrometheusBungeeCordExporter plugin) {
9 | super(plugin);
10 | }
11 |
12 | @Override
13 | public void doCollect() {
14 | this.getGauge()
15 | .set(
16 | ((PrometheusBungeeCordExporter) getPlugin())
17 | .getProxy()
18 | .getPluginManager()
19 | .getPlugins().size()
20 | );
21 | }
22 |
23 | @Override
24 | public String getConfigKey() {
25 | return "installed_network_plugins";
26 | }
27 |
28 | @Override
29 | public String getHelp() {
30 | return "the number of installed network plugins in BungeeCord";
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/metrics/ManagedServers.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.metrics;
2 |
3 | import org.akadia.prometheus.bungeecord.PrometheusBungeeCordExporter;
4 | import org.akadia.prometheus.interfaces.GauageMetric;
5 |
6 | public class ManagedServers extends GauageMetric {
7 |
8 | public ManagedServers(PrometheusBungeeCordExporter plugin) {
9 | super(plugin);
10 | }
11 |
12 | @Override
13 | public void doCollect() {
14 | this.getGauge()
15 | .set(
16 | ((PrometheusBungeeCordExporter) getPlugin())
17 | .getProxy()
18 | .getServers()
19 | .size()
20 | );
21 | }
22 |
23 | @Override
24 | public String getConfigKey() {
25 | return "managed_servers";
26 | }
27 |
28 | @Override
29 | public String getHelp() {
30 | return "the number of managed servers in BungeeCord";
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/metrics/OnlinePlayer.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.metrics;
2 |
3 | import net.md_5.bungee.api.ProxyServer;
4 | import org.akadia.prometheus.bungeecord.PrometheusBungeeCordExporter;
5 | import org.akadia.prometheus.interfaces.GauageMetric;
6 |
7 | public class OnlinePlayer extends GauageMetric {
8 |
9 | public OnlinePlayer(PrometheusBungeeCordExporter plugin) {
10 | super(plugin);
11 | }
12 |
13 | @Override
14 | public void doCollect() {
15 | this.getGauge().clear();
16 |
17 | ProxyServer proxy = ((PrometheusBungeeCordExporter) getPlugin()).getProxy();
18 | proxy.getServers().forEach((key, value) -> {
19 | this.getGauge().labels(key, "", "").set(0);
20 | value.getPlayers().forEach(proxiedPlayer ->
21 | this.getGauge().labels(key, proxiedPlayer.getName(), Boolean.toString(proxy.getConfig().isOnlineMode())).set(1));
22 | });
23 | }
24 |
25 | @Override
26 | public String getConfigKey() {
27 | return "online_player";
28 | }
29 |
30 | @Override
31 | public String getHelp() {
32 | return "the name of the online player in BungeeCord";
33 | }
34 |
35 | @Override
36 | public String[] getLabels() {
37 | return new String[]{"server", "player", "online_mode"};
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/metrics/OnlinePlayerLatency.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.metrics;
2 |
3 | import org.akadia.prometheus.bungeecord.PrometheusBungeeCordExporter;
4 | import org.akadia.prometheus.interfaces.SummaryMetric;
5 |
6 | public class OnlinePlayerLatency extends SummaryMetric {
7 |
8 | public OnlinePlayerLatency(PrometheusBungeeCordExporter plugin) {
9 | super(plugin);
10 | }
11 |
12 | @Override
13 | public void doCollect() {
14 | ((PrometheusBungeeCordExporter) getPlugin())
15 | .getProxy()
16 | .getPlayers()
17 | .forEach(proxiedPlayer ->
18 | this.getSummary().labels(proxiedPlayer.getName()).observe(proxiedPlayer.getPing())
19 | );
20 | }
21 |
22 | @Override
23 | public String getConfigKey() {
24 | return "online_player_latency";
25 | }
26 |
27 | @Override
28 | public String getHelp() {
29 | return "the latency of an online player in BungeeCord";
30 | }
31 |
32 | @Override
33 | public String[] getLabels() {
34 | return new String[]{"name"};
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/metrics/RedisBungeeOnlinePlayer.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.metrics;
2 |
3 | import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
4 | import org.akadia.prometheus.PrometheusExporter;
5 | import org.akadia.prometheus.interfaces.GauageMetric;
6 |
7 | public class RedisBungeeOnlinePlayer extends GauageMetric {
8 |
9 | public RedisBungeeOnlinePlayer(PrometheusExporter plugin) {
10 | super(plugin);
11 | }
12 |
13 | @Override
14 | public void doCollect() {
15 | this.getGauge().clear();
16 | RedisBungeeAPI.getRedisBungeeApi()
17 | .getServerToPlayers()
18 | .asMap()
19 | .forEach((server, players) -> {
20 | this.getGauge()
21 | .labels(server, "")
22 | .set(0);
23 | players.forEach(player -> {
24 | this.getGauge()
25 | .labels(server, player.toString())
26 | .set(1);
27 | });
28 | });
29 | }
30 |
31 | @Override
32 | public String getConfigKey() {
33 | return "redis_online_player";
34 | }
35 |
36 | @Override
37 | public String getHelp() {
38 | return "the name of online redisbungee player";
39 | }
40 |
41 | @Override
42 | public String[] getLabels() {
43 | return new String[]{"server", "player"};
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/bungeecord/metrics/RedisBungeeOnlineProxies.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.bungeecord.metrics;
2 |
3 | import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
4 | import org.akadia.prometheus.PrometheusExporter;
5 | import org.akadia.prometheus.interfaces.GauageMetric;
6 |
7 | public class RedisBungeeOnlineProxies extends GauageMetric {
8 |
9 | public RedisBungeeOnlineProxies(PrometheusExporter plugin) {
10 | super(plugin);
11 | }
12 |
13 | @Override
14 | public void doCollect() {
15 | this.getGauge().set(RedisBungeeAPI.getRedisBungeeApi().getAllProxies().size());
16 | }
17 |
18 | @Override
19 | public String getConfigKey() {
20 | return "redis_bungee_online_proxies";
21 | }
22 |
23 | @Override
24 | public String getHelp() {
25 | return "the number of online redisbungee proxy";
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/config/ConfigManager.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.config;
2 |
3 | import com.google.gson.Gson;
4 |
5 | import java.io.File;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.Reader;
9 | import java.nio.file.Files;
10 | import java.util.Map;
11 |
12 | public class ConfigManager {
13 | private static final String CONFIG_NAME = "config.json";
14 | private final Map config;
15 |
16 | public ConfigManager(File dataFolder) throws IOException {
17 | File configFile = createConfigFile(dataFolder, CONFIG_NAME, true);
18 | Reader reader = Files.newBufferedReader(configFile.toPath());
19 |
20 | Gson gson = new Gson();
21 | // convert JSON file to map
22 | config = gson.fromJson(reader, Map.class);
23 | }
24 |
25 | public Map getConfig() {
26 | return config;
27 | }
28 |
29 | public File createConfigFile(File dataFolder, String CONFIG_NAME, boolean copyFromResource) {
30 | File saveTo = null;
31 | try {
32 | if (!dataFolder.exists()) {
33 | dataFolder.mkdir();
34 | }
35 |
36 | saveTo = new File(dataFolder, CONFIG_NAME);
37 | if (!saveTo.exists()) {
38 | if (copyFromResource) {
39 | InputStream in = this.getClass().getClassLoader().getResourceAsStream(CONFIG_NAME);
40 | Files.copy(in, saveTo.toPath());
41 | } else {
42 | saveTo.createNewFile();
43 | }
44 | }
45 | } catch (IOException ex) {
46 | ex.printStackTrace();
47 | }
48 | return saveTo;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/interfaces/Configurable.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.interfaces;
2 |
3 | public interface Configurable {
4 | String getConfigKey();
5 |
6 | String getHelp();
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/interfaces/CountableMetrics.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.interfaces;
2 |
3 | import io.prometheus.client.Counter;
4 | import org.akadia.prometheus.PrometheusExporter;
5 | import org.akadia.prometheus.utils.Util;
6 |
7 | public abstract class CountableMetrics extends Metric {
8 | private final Counter counter;
9 |
10 | public CountableMetrics(PrometheusExporter plugin) {
11 | super(plugin);
12 |
13 | this.counter = Counter.build()
14 | .name(Util.prefix(plugin.getPrefix(), this.getConfigKey()))
15 | .labelNames(this.getLabels())
16 | .help(this.getHelp())
17 | .create()
18 | .register();
19 | }
20 |
21 | @Override
22 | public void doCollect() {
23 | }
24 |
25 | public Counter getCounter() {
26 | return counter;
27 | }
28 |
29 | public abstract String getConfigKey();
30 |
31 | public abstract String getHelp();
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/interfaces/GauageMetric.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.interfaces;
2 |
3 | import io.prometheus.client.Gauge;
4 | import org.akadia.prometheus.PrometheusExporter;
5 | import org.akadia.prometheus.utils.Util;
6 |
7 | public abstract class GauageMetric extends Metric {
8 |
9 | private final Gauge gauge;
10 |
11 | public GauageMetric(PrometheusExporter plugin) {
12 | super(plugin);
13 |
14 | this.gauge = Gauge.build()
15 | .name(Util.prefix(plugin.getPrefix(), this.getConfigKey()))
16 | .help(this.getHelp())
17 | .labelNames(this.getLabels())
18 | .create()
19 | .register();
20 | }
21 |
22 | public Gauge getGauge() {
23 | return gauge;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/interfaces/Metric.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.interfaces;
2 |
3 | import org.akadia.prometheus.PrometheusExporter;
4 |
5 | import java.io.PrintWriter;
6 | import java.io.StringWriter;
7 |
8 | public abstract class Metric implements Configurable {
9 |
10 | private final PrometheusExporter plugin;
11 |
12 | public Metric(PrometheusExporter plugin) {
13 | this.plugin = plugin;
14 | }
15 |
16 | public PrometheusExporter getPlugin() {
17 | return plugin;
18 | }
19 |
20 | public void collect() {
21 | try {
22 | doCollect();
23 | } catch (Exception e) {
24 | logException(e);
25 | }
26 | }
27 |
28 | public abstract void doCollect();
29 |
30 | private void logException(Exception e) {
31 | final String className = this.getClass().getSimpleName();
32 |
33 | StringWriter sw = new StringWriter();
34 | PrintWriter pw = new PrintWriter(sw);
35 | e.printStackTrace(pw);
36 |
37 | plugin.warn(String.format("Failed to collect metric '%s': %s",
38 | className, sw));
39 |
40 | plugin.warn(className + " collect:" + e);
41 | }
42 |
43 | public abstract String getConfigKey();
44 |
45 | public abstract String getHelp();
46 |
47 | public String[] getLabels() {
48 | return new String[]{};
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/interfaces/MetricWrapper.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.interfaces;
2 |
3 | import io.prometheus.client.Collector;
4 | import org.akadia.prometheus.PrometheusExporter;
5 |
6 | public abstract class MetricWrapper extends Metric {
7 | private final Collector collector;
8 |
9 | public MetricWrapper(PrometheusExporter plugin, Collector collector) {
10 | super(plugin);
11 | this.collector = collector;
12 | }
13 |
14 | public Collector getCollector() {
15 | return collector;
16 | }
17 |
18 | public abstract String getConfigKey();
19 |
20 | public abstract String getHelp();
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/interfaces/SummaryMetric.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.interfaces;
2 |
3 | import io.prometheus.client.Summary;
4 | import org.akadia.prometheus.PrometheusExporter;
5 | import org.akadia.prometheus.utils.Util;
6 |
7 | public abstract class SummaryMetric extends Metric {
8 |
9 | private final Summary summary;
10 |
11 | public SummaryMetric(PrometheusExporter plugin) {
12 | super(plugin);
13 |
14 | this.summary = Summary.build()
15 | .name(Util.prefix(plugin.getPrefix(), this.getConfigKey()))
16 | .help(this.getHelp())
17 | .quantile(0.95, 0.005) // 0.95 quantile with 0.005 allowed error
18 | .labelNames(this.getLabels())
19 | .create()
20 | .register();
21 | }
22 |
23 | public Summary getSummary() {
24 | return summary;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/metrics/JvmGarbageCollectorWrapper.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.metrics;
2 |
3 | import io.prometheus.client.Collector;
4 | import io.prometheus.client.hotspot.GarbageCollectorExports;
5 | import org.akadia.prometheus.PrometheusExporter;
6 | import org.akadia.prometheus.interfaces.MetricWrapper;
7 | import org.akadia.prometheus.utils.Util;
8 |
9 | import java.util.List;
10 |
11 | public class JvmGarbageCollectorWrapper extends MetricWrapper {
12 |
13 | public JvmGarbageCollectorWrapper(PrometheusExporter plugin) {
14 | super(plugin, new GarbageCollectorExportsCollector(plugin.getPrefix()));
15 | }
16 |
17 | @Override
18 | public void doCollect() {
19 | }
20 |
21 | @Override
22 | public String getConfigKey() {
23 | return "jvm_gc";
24 | }
25 |
26 | @Override
27 | public String getHelp() {
28 | return "JVM garbage collection";
29 | }
30 |
31 | private static class GarbageCollectorExportsCollector extends Collector {
32 | private static final GarbageCollectorExports garbageCollectorExports = new GarbageCollectorExports();
33 |
34 | private final String prefix;
35 |
36 | public GarbageCollectorExportsCollector(String prefix) {
37 | this.prefix = prefix;
38 | }
39 |
40 | @Override
41 | public List collect() {
42 | return Util.prefixFromCollector(garbageCollectorExports, prefix);
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/metrics/JvmMemory.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.metrics;
2 |
3 | import org.akadia.prometheus.PrometheusExporter;
4 | import org.akadia.prometheus.interfaces.GauageMetric;
5 |
6 | public class JvmMemory extends GauageMetric {
7 |
8 |
9 | public JvmMemory(PrometheusExporter plugin) {
10 | super(plugin);
11 | }
12 |
13 | @Override
14 | public void doCollect() {
15 | this.getGauge().labels("max").set(Runtime.getRuntime().maxMemory());
16 | this.getGauge().labels("free").set(Runtime.getRuntime().freeMemory());
17 | this.getGauge().labels("allocated").set(Runtime.getRuntime().totalMemory());
18 | this.getGauge().labels("used").set(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
19 | }
20 |
21 | @Override
22 | public String getConfigKey() {
23 | return "jvm_memory";
24 | }
25 |
26 | @Override
27 | public String getHelp() {
28 | return "JVM memory usage";
29 | }
30 |
31 | @Override
32 | public String[] getLabels() {
33 | return new String[]{"type"};
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/metrics/JvmThreadsWrapper.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.metrics;
2 |
3 | import io.prometheus.client.Collector;
4 | import io.prometheus.client.hotspot.ThreadExports;
5 | import org.akadia.prometheus.PrometheusExporter;
6 | import org.akadia.prometheus.interfaces.MetricWrapper;
7 | import org.akadia.prometheus.utils.Util;
8 |
9 | import java.util.List;
10 |
11 | public class JvmThreadsWrapper extends MetricWrapper {
12 |
13 | public JvmThreadsWrapper(PrometheusExporter plugin) {
14 | super(plugin, new ThreadExportsCollector(plugin.getPrefix()));
15 | }
16 |
17 | @Override
18 | public void doCollect() {
19 | }
20 |
21 |
22 | @Override
23 | public String getConfigKey() {
24 | return "jvm_threads";
25 | }
26 |
27 | @Override
28 | public String getHelp() {
29 | return "JVM threads usage";
30 | }
31 |
32 | private static class ThreadExportsCollector extends Collector {
33 | private static final ThreadExports threadExports = new ThreadExports();
34 | private final String prefix;
35 |
36 | public ThreadExportsCollector(String prefix) {
37 | this.prefix = prefix;
38 | }
39 |
40 | @Override
41 | public List collect() {
42 | return Util.prefixFromCollector(threadExports, prefix);
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/utils/Util.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.utils;
2 |
3 | import io.prometheus.client.Collector;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class Util {
9 |
10 | public static String prefix(String prefix, String name) {
11 | return prefix + name;
12 | }
13 |
14 | public static List prefixFromCollector(Collector collector, String prefix) {
15 | List collected = collector.collect();
16 | List mfs = new ArrayList<>();
17 |
18 | for (Collector.MetricFamilySamples mSample : collected) {
19 | List samples = new ArrayList<>(mSample.samples.size());
20 | for (Collector.MetricFamilySamples.Sample sample : mSample.samples) {
21 | samples.add(new Collector.MetricFamilySamples.Sample(Util.prefix(prefix, sample.name), sample.labelNames, sample.labelValues, sample.value));
22 | }
23 |
24 | Collector.MetricFamilySamples prefixed = new Collector.MetricFamilySamples(Util.prefix(prefix, mSample.name), mSample.type, mSample.help, samples);
25 | mfs.add(prefixed);
26 | }
27 |
28 | return mfs;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/PrometheusVelocityExporter.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity;
2 |
3 | import com.google.inject.Inject;
4 | import com.velocitypowered.api.event.Subscribe;
5 | import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
6 | import com.velocitypowered.api.plugin.Plugin;
7 | import com.velocitypowered.api.plugin.annotation.DataDirectory;
8 | import com.velocitypowered.api.proxy.ProxyServer;
9 | import io.prometheus.client.CollectorRegistry;
10 | import org.akadia.prometheus.MetricRegistry;
11 | import org.akadia.prometheus.MetricsServer;
12 | import org.akadia.prometheus.PrometheusExporter;
13 | import org.akadia.prometheus.config.ConfigManager;
14 | import org.akadia.prometheus.interfaces.Configurable;
15 | import org.akadia.prometheus.interfaces.CountableMetrics;
16 | import org.akadia.prometheus.interfaces.Metric;
17 | import org.akadia.prometheus.interfaces.MetricWrapper;
18 | import org.akadia.prometheus.metrics.JvmGarbageCollectorWrapper;
19 | import org.akadia.prometheus.metrics.JvmMemory;
20 | import org.akadia.prometheus.metrics.JvmThreadsWrapper;
21 | import org.akadia.prometheus.velocity.listeners.LoginEventListener;
22 | import org.akadia.prometheus.velocity.listeners.PlayerChatEventListener;
23 | import org.akadia.prometheus.velocity.listeners.PlayerDisconnectEventListener;
24 | import org.akadia.prometheus.velocity.listeners.ProxyPingEventListener;
25 | import org.akadia.prometheus.velocity.listeners.PlayerCommandEventListener;
26 | import org.akadia.prometheus.velocity.metrics.InstalledNetworkPlugins;
27 | import org.akadia.prometheus.velocity.metrics.ManagedServers;
28 | import org.akadia.prometheus.velocity.metrics.OnlinePlayer;
29 | import org.akadia.prometheus.velocity.metrics.OnlinePlayersLatency;
30 | import org.bstats.velocity.Metrics;
31 | import org.slf4j.Logger;
32 |
33 | import java.io.IOException;
34 | import java.nio.file.Path;
35 | import java.util.ArrayList;
36 | import java.util.List;
37 |
38 | @Plugin(id = "velocity-prometheus-exporter", name = "Velocity Prometheus Exporter", version = "1.0.0", authors = "akadia")
39 | public class PrometheusVelocityExporter implements PrometheusExporter {
40 | @Inject
41 | @DataDirectory
42 | public Path configDir;
43 | @Inject
44 | private Metrics.Factory metricsFactory;
45 | @Inject
46 | private ProxyServer proxyServer;
47 |
48 | @Inject
49 | private Logger logger;
50 | private String prefix;
51 |
52 | public ProxyServer getProxyServer() {
53 | return proxyServer;
54 | }
55 |
56 | @Subscribe
57 | public void onProxyInitialization(ProxyInitializeEvent event) {
58 | try {
59 | ConfigManager configManager = new ConfigManager(configDir.toFile());
60 | startMetricsServer(configManager);
61 |
62 | } catch (IOException e) {
63 | e.printStackTrace();
64 | }
65 | }
66 |
67 | private void startMetricsServer(ConfigManager configManager) {
68 | if (configManager.getConfig().get("bstats").equals("true")) {
69 | try {
70 | metricsFactory.make(this, 11269);
71 | } catch (IllegalStateException ex) {
72 | this.info("bStats Metrics failed to start");
73 | }
74 | }
75 |
76 | this.prefix = configManager.getConfig().getOrDefault("prefix", "bungeecord_");
77 |
78 | String host = configManager.getConfig().getOrDefault("host", "127.0.0.1");
79 | int port = Integer.parseInt(configManager.getConfig().getOrDefault("port", "9985"));
80 |
81 | MetricsServer server = new MetricsServer(host, port, this);
82 |
83 | List configurables = new ArrayList<>();
84 | configurables.add(new LoginEventListener(this));
85 | configurables.add(new PlayerDisconnectEventListener(this));
86 | configurables.add(new PlayerChatEventListener(this));
87 | configurables.add(new PlayerCommandEventListener(this));
88 | configurables.add(new ProxyPingEventListener(this));
89 | configurables.add(new JvmGarbageCollectorWrapper(this));
90 | configurables.add(new JvmMemory(this));
91 | configurables.add(new JvmThreadsWrapper(this));
92 | configurables.add(new OnlinePlayer(this));
93 | configurables.add(new OnlinePlayersLatency(this));
94 | configurables.add(new ManagedServers(this));
95 | configurables.add(new InstalledNetworkPlugins(this));
96 |
97 | for (Configurable configurable : configurables) {
98 | if (configManager.getConfig().getOrDefault(configurable.getConfigKey(), "true").equals("false")) {
99 | logger.info(configurable.getConfigKey() + " is disabled in the config");
100 | continue;
101 | }
102 | this.info(configurable.getConfigKey() + " is enabled in the config");
103 | if (configurable instanceof CountableMetrics) {
104 | proxyServer.getEventManager().register(this, configurable);
105 | } else if (configurable instanceof MetricWrapper) {
106 | CollectorRegistry.defaultRegistry.register(((MetricWrapper) configurable).getCollector());
107 | } else {
108 | MetricRegistry.getInstance().register((Metric) configurable);
109 | }
110 | }
111 |
112 | try {
113 | server.start();
114 | this.info("Started Prometheus metrics endpoint at: " + host + ":" + port);
115 | } catch (Exception e) {
116 | this.warn("Could not start embedded Jetty server");
117 | }
118 |
119 | this.info("Initialized completed");
120 | }
121 |
122 |
123 | @Override
124 | public void info(String info) {
125 | this.logger.info(info);
126 | }
127 |
128 | @Override
129 | public void warn(String warning) {
130 | this.logger.warn(warning);
131 | }
132 |
133 | @Override
134 | public String getPrefix() {
135 | return this.prefix;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/listeners/LoginEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.listeners;
2 |
3 | import com.velocitypowered.api.event.Subscribe;
4 | import com.velocitypowered.api.event.connection.LoginEvent;
5 | import org.akadia.prometheus.PrometheusExporter;
6 | import org.akadia.prometheus.interfaces.CountableMetrics;
7 |
8 | public class LoginEventListener extends CountableMetrics {
9 |
10 | public LoginEventListener(PrometheusExporter plugin) {
11 | super(plugin);
12 | }
13 |
14 | @Subscribe
15 | public void onLoginEvent(LoginEvent event) {
16 | this.getCounter().inc();
17 | }
18 |
19 | @Override
20 | public String getHelp() {
21 | return "the number of player logins in Velocity";
22 | }
23 |
24 | @Override
25 | public String getConfigKey() {
26 | return "player_connects";
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/listeners/PlayerChatEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.listeners;
2 |
3 | import com.velocitypowered.api.event.Subscribe;
4 | import com.velocitypowered.api.event.player.PlayerChatEvent;
5 | import org.akadia.prometheus.PrometheusExporter;
6 | import org.akadia.prometheus.interfaces.CountableMetrics;
7 |
8 | public class PlayerChatEventListener extends CountableMetrics {
9 |
10 | public PlayerChatEventListener(PrometheusExporter plugin) {
11 | super(plugin);
12 | }
13 |
14 | @Subscribe
15 | public void onPlayerChatEvent(PlayerChatEvent event) {
16 | this.getCounter().inc();
17 | }
18 |
19 | @Override
20 | public String getHelp() {
21 | return "the number of player chats in Velocity";
22 | }
23 |
24 | @Override
25 | public String getConfigKey() {
26 | return "player_chats";
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/listeners/PlayerCommandEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.listeners;
2 |
3 | import com.velocitypowered.api.event.Subscribe;
4 | import com.velocitypowered.api.event.command.CommandExecuteEvent;
5 | import org.akadia.prometheus.PrometheusExporter;
6 | import org.akadia.prometheus.interfaces.CountableMetrics;
7 |
8 | public class PlayerCommandEventListener extends CountableMetrics {
9 |
10 | public PlayerCommandEventListener(PrometheusExporter plugin) {
11 | super(plugin);
12 | }
13 |
14 | @Subscribe
15 | public void onPlayerCommandEvent(CommandExecuteEvent event) {
16 | this.getCounter().inc();
17 | }
18 |
19 | @Override
20 | public String getHelp() {
21 | return "the number of player commands in Velocity";
22 | }
23 |
24 | @Override
25 | public String getConfigKey() {
26 | return "player_commands";
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/listeners/PlayerDisconnectEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.listeners;
2 |
3 | import com.velocitypowered.api.event.Subscribe;
4 | import com.velocitypowered.api.event.connection.DisconnectEvent;
5 | import org.akadia.prometheus.PrometheusExporter;
6 | import org.akadia.prometheus.interfaces.CountableMetrics;
7 |
8 | public class PlayerDisconnectEventListener extends CountableMetrics {
9 |
10 | public PlayerDisconnectEventListener(PrometheusExporter plugin) {
11 | super(plugin);
12 | }
13 |
14 | @Subscribe
15 | public void onPlayerDisconnectEvent(DisconnectEvent event) {
16 | this.getCounter().inc();
17 | }
18 |
19 | @Override
20 | public String getHelp() {
21 | return "the number of player disconnects in Velocity";
22 | }
23 |
24 | @Override
25 | public String getConfigKey() {
26 | return "player_disconnects";
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/listeners/PlayerKickEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.listeners;
2 |
3 | import com.velocitypowered.api.event.Subscribe;
4 | import com.velocitypowered.api.event.player.KickedFromServerEvent;
5 | import org.akadia.prometheus.PrometheusExporter;
6 | import org.akadia.prometheus.interfaces.CountableMetrics;
7 |
8 | public class PlayerKickEventListener extends CountableMetrics {
9 |
10 | public PlayerKickEventListener(PrometheusExporter plugin) {
11 | super(plugin);
12 | }
13 |
14 | @Subscribe
15 | public void onPlayerDisconnectEvent(KickedFromServerEvent event) {
16 | this.getCounter().inc();
17 | }
18 |
19 | @Override
20 | public String getHelp() {
21 | return "the number of player kicked in Velocity";
22 | }
23 |
24 | @Override
25 | public String getConfigKey() {
26 | return "player_kicks";
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/listeners/ProxyPingEventListener.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.listeners;
2 |
3 | import com.velocitypowered.api.event.Subscribe;
4 | import com.velocitypowered.api.event.proxy.ProxyPingEvent;
5 | import org.akadia.prometheus.PrometheusExporter;
6 | import org.akadia.prometheus.interfaces.CountableMetrics;
7 |
8 | public class ProxyPingEventListener extends CountableMetrics {
9 |
10 | public ProxyPingEventListener(PrometheusExporter plugin) {
11 | super(plugin);
12 | }
13 |
14 | @Subscribe
15 | public void onProxyPingEvent(ProxyPingEvent event) {
16 | this.getCounter().inc();
17 | }
18 |
19 | @Override
20 | public String getHelp() {
21 | return "the number of server list pings in Velocity";
22 | }
23 |
24 | @Override
25 | public String getConfigKey() {
26 | return "server_list_pings";
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/metrics/InstalledNetworkPlugins.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.metrics;
2 |
3 | import org.akadia.prometheus.interfaces.GauageMetric;
4 | import org.akadia.prometheus.velocity.PrometheusVelocityExporter;
5 |
6 | public class InstalledNetworkPlugins extends GauageMetric {
7 |
8 | public InstalledNetworkPlugins(PrometheusVelocityExporter plugin) {
9 | super(plugin);
10 | }
11 |
12 | @Override
13 | public void doCollect() {
14 | this.getGauge().set(((PrometheusVelocityExporter) getPlugin()).getProxyServer().getPluginManager().getPlugins().size());
15 | }
16 |
17 | @Override
18 | public String getConfigKey() {
19 | return "installed_network_plugins";
20 | }
21 |
22 | @Override
23 | public String getHelp() {
24 | return "the number of installed network plugins in Velocity";
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/metrics/ManagedServers.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.metrics;
2 |
3 | import org.akadia.prometheus.interfaces.GauageMetric;
4 | import org.akadia.prometheus.velocity.PrometheusVelocityExporter;
5 |
6 | public class ManagedServers extends GauageMetric {
7 |
8 | public ManagedServers(PrometheusVelocityExporter plugin) {
9 | super(plugin);
10 | }
11 |
12 | @Override
13 | public void doCollect() {
14 | this.getGauge().set(((PrometheusVelocityExporter) getPlugin()).getProxyServer().getAllServers().size());
15 | }
16 |
17 | @Override
18 | public String getConfigKey() {
19 | return "managed_servers";
20 | }
21 |
22 | @Override
23 | public String getHelp() {
24 | return "the number of managed servers in Velocity";
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/metrics/OnlinePlayer.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.metrics;
2 |
3 | import org.akadia.prometheus.interfaces.GauageMetric;
4 | import org.akadia.prometheus.velocity.PrometheusVelocityExporter;
5 |
6 | public class OnlinePlayer extends GauageMetric {
7 |
8 | public OnlinePlayer(PrometheusVelocityExporter plugin) {
9 | super(plugin);
10 | }
11 |
12 | @Override
13 | public void doCollect() {
14 | this.getGauge().clear();
15 |
16 |
17 | ((PrometheusVelocityExporter) getPlugin()).getProxyServer().getAllServers().forEach(registeredServer -> {
18 | String serverName = registeredServer.getServerInfo().getName();
19 | this.getGauge().labels(serverName, "", "").set(0);
20 | registeredServer.getPlayersConnected().forEach(player ->
21 | this.getGauge().labels(serverName, player.getUsername(), Boolean.toString(player.isOnlineMode())).set(1));
22 | });
23 | }
24 |
25 | @Override
26 | public String getConfigKey() {
27 | return "online_player";
28 | }
29 |
30 | @Override
31 | public String getHelp() {
32 | return "the name of the online player in Velocity";
33 | }
34 |
35 | @Override
36 | public String[] getLabels() {
37 | return new String[]{"server", "player", "online_mode"};
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/akadia/prometheus/velocity/metrics/OnlinePlayersLatency.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.velocity.metrics;
2 |
3 | import org.akadia.prometheus.interfaces.SummaryMetric;
4 | import org.akadia.prometheus.velocity.PrometheusVelocityExporter;
5 |
6 | public class OnlinePlayersLatency extends SummaryMetric {
7 |
8 | public OnlinePlayersLatency(PrometheusVelocityExporter plugin) {
9 | super(plugin);
10 | }
11 |
12 | @Override
13 | public void doCollect() {
14 | ((PrometheusVelocityExporter) getPlugin()).getProxyServer().getAllPlayers().forEach(player -> this.getSummary().labels(player.getUsername()).observe(player.getPing()));
15 | }
16 |
17 |
18 | @Override
19 | public String getConfigKey() {
20 | return "online_player_latency";
21 | }
22 |
23 | @Override
24 | public String getHelp() {
25 | return "the latency of an online player in Velocity";
26 | }
27 |
28 | @Override
29 | public String[] getLabels() {
30 | return new String[]{"name"};
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/resources/bungee.yml:
--------------------------------------------------------------------------------
1 | name: ${project.name}
2 | version: ${project.version}
3 | author: akadia
4 | authors: [ akdaia, sldk ]
5 | main: org.akadia.prometheus.bungeecord.PrometheusBungeeCordExporter
6 | website: akadia.org
7 | description: ${project.description}
8 |
--------------------------------------------------------------------------------
/src/main/resources/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "bstats": "true",
3 | "host": "0.0.0.0",
4 | "port": "9985",
5 | "prefix": "bungeecord_",
6 | "jvm_gc": "true",
7 | "jvm_memory": "true",
8 | "jvm_threads": "true",
9 | "player_connects": "true",
10 | "player_disconnects": "true",
11 | "player_kicks": "true",
12 | "player_chats": "true",
13 | "player_commands": "true",
14 | "server_list_pings": "true",
15 | "managed_servers": "true",
16 | "installed_network_plugins": "true",
17 | "online_player": "true",
18 | "online_player_latency": "true",
19 | "redis_player_connects": "false",
20 | "redis_player_disconnects": "false",
21 | "redis_online_player": "false",
22 | "redis_bungee_online_proxies": "false"
23 | }
--------------------------------------------------------------------------------
/src/main/resources/velocity-plugin.json:
--------------------------------------------------------------------------------
1 | {"id":"velocity-prometheus-exporter","name":"Velocity Prometheus Exporter","version":"1.0.0","authors":["akadia"],"dependencies":[],"main":"org.akadia.prometheus.velocity.PrometheusVelocityExporter"}
--------------------------------------------------------------------------------
/src/test/java/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weihao/bungeecord-prometheus-exporter/993acbc55dd597cefb23b621f83906440904eec0/src/test/java/.gitignore
--------------------------------------------------------------------------------
/src/test/java/org/akadia/prometheus/exporter/PrometheusExporterTest.java:
--------------------------------------------------------------------------------
1 | package org.akadia.prometheus.exporter;
2 |
3 |
4 | import io.prometheus.client.CollectorRegistry;
5 | import io.prometheus.client.Counter;
6 | import io.prometheus.client.exporter.common.TextFormat;
7 | import io.restassured.RestAssured;
8 | import org.akadia.prometheus.MetricsServer;
9 | import org.akadia.prometheus.PrometheusExporter;
10 | import org.eclipse.jetty.http.HttpStatus;
11 | import org.eclipse.jetty.util.URIUtil;
12 | import org.junit.jupiter.api.AfterEach;
13 | import org.junit.jupiter.api.BeforeEach;
14 | import org.junit.jupiter.api.Test;
15 | import org.junit.jupiter.api.extension.ExtendWith;
16 | import org.mockito.Mock;
17 | import org.mockito.junit.jupiter.MockitoExtension;
18 |
19 | import java.io.IOException;
20 | import java.net.ServerSocket;
21 |
22 | import static org.junit.jupiter.api.Assertions.assertEquals;
23 |
24 | @ExtendWith(MockitoExtension.class)
25 | public class PrometheusExporterTest {
26 |
27 | @Mock
28 | private PrometheusExporter exporterMock;
29 |
30 | private int metricsServerPort;
31 | private MetricsServer metricsServer;
32 |
33 | @BeforeEach
34 | void setup() throws Exception {
35 | CollectorRegistry.defaultRegistry.clear();
36 | metricsServerPort = getRandomFreePort();
37 | metricsServer = new MetricsServer("localhost", metricsServerPort, exporterMock);
38 | metricsServer.start();
39 | }
40 |
41 | private int getRandomFreePort() throws IOException {
42 | try (ServerSocket serverSocket = new ServerSocket(0)) {
43 | return serverSocket.getLocalPort();
44 | }
45 | }
46 |
47 | @AfterEach
48 | void cleanup() throws Exception {
49 | metricsServer.stop();
50 | }
51 |
52 | @Test
53 | void metrics_server_should_return_valid_prometheus_response() {
54 | mockPrometheusCounter("bungeecord_online_players", "This is a mock metric of online bungeecord players", 233);
55 |
56 | String requestPath = URIUtil.newURI("http", "localhost", metricsServerPort, "/metrics", null);
57 | String responseText = RestAssured.when()
58 | .get(requestPath)
59 | .then()
60 | .statusCode(HttpStatus.OK_200)
61 | .contentType(TextFormat.CONTENT_TYPE_004)
62 | .extract()
63 | .asString();
64 |
65 | String[] lines = responseText.split("\n");
66 | assertEquals(lines[0], "# HELP bungeecord_online_players_total This is a mock metric of online bungeecord players");
67 | assertEquals(lines[1], "# TYPE bungeecord_online_players_total counter");
68 | assertEquals(lines[2], "bungeecord_online_players_total 233.0");
69 | }
70 |
71 | private void mockPrometheusCounter(String name, String help, int value) {
72 | Counter mockPrometheusCounter = Counter.build().name(name).help(help).register();
73 | mockPrometheusCounter.inc(value);
74 | }
75 |
76 | @Test
77 | void metrics_server_should_return_404_on_unknown_paths() {
78 | String requestPath = URIUtil.newURI("http", "localhost", metricsServerPort, "/unknown-path", null);
79 |
80 | RestAssured.when()
81 | .get(requestPath)
82 | .then()
83 | .statusCode(HttpStatus.NOT_FOUND_404);
84 | }
85 |
86 | }
--------------------------------------------------------------------------------