diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index a13fd050..6805edcb 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -11,7 +11,7 @@ jobs: - name: Configure GPG Key run: echo "${{secrets.SIGNING_KEY}}" | base64 --decode > /tmp/keyring.gpg - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: zulu @@ -39,7 +39,7 @@ jobs: - name: Configure GPG Key run: echo "${{secrets.SIGNING_KEY}}" | base64 --decode > /tmp/keyring.gpg - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: zulu diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1bffd253..49fd4ade 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: - name: Configure GPG Key run: echo "${{secrets.SIGNING_KEY}}" | base64 --decode > /tmp/keyring.gpg - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: zulu @@ -38,7 +38,7 @@ jobs: - name: Configure GPG Key run: echo "${{secrets.SIGNING_KEY}}" | base64 --decode > /tmp/keyring.gpg - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: zulu diff --git a/.gitignore b/.gitignore index e240c6d5..165f3dbc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ .gradle build -**/run*/ \ No newline at end of file +**/run*/ +.kotlin +.vscode/ diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/UnifiedMetrics.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/UnifiedMetrics.kt new file mode 100644 index 00000000..426d8515 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/UnifiedMetrics.kt @@ -0,0 +1,58 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api + +import dev.cubxity.plugins.metrics.api.logging.Logger +import dev.cubxity.plugins.metrics.api.metric.MetricsManager +import dev.cubxity.plugins.metrics.api.platform.Platform +import kotlinx.coroutines.CoroutineDispatcher + +/** + * The UnifiedMetrics API + */ +interface UnifiedMetrics { + /** + * The platform UnifiedMetrics is running on. + */ + val platform: Platform + + /** + * The name of this server. + * + * This is defined in the UnifiedMetrics configuration file, and is used for + * grouping server data. + * + * The default server name is "global" + */ + val serverName: String + + /** + * The platform's logger. + */ + val logger: Logger + + /** + * The platform's dispatcher. + */ + val dispatcher: CoroutineDispatcher + + /** + * The metrics api. + */ + val metricsManager: MetricsManager +} \ No newline at end of file diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/UnifiedMetricsProvider.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/UnifiedMetricsProvider.kt new file mode 100644 index 00000000..daa58cb5 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/UnifiedMetricsProvider.kt @@ -0,0 +1,40 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api + +/** + * Provides static access to the [UnifiedMetrics] service. + */ +object UnifiedMetricsProvider { + @JvmStatic + private var instance: UnifiedMetrics? = null + + @JvmStatic + fun get(): UnifiedMetrics = + instance ?: error("The UnifiedMetrics API is not loaded.") + + @JvmStatic + fun register(instance: UnifiedMetrics) { + UnifiedMetricsProvider.instance = instance + } + + @JvmStatic + fun unregister() { + instance = null + } +} \ No newline at end of file diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/logging/Logger.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/logging/Logger.kt new file mode 100644 index 00000000..39a93f32 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/logging/Logger.kt @@ -0,0 +1,30 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.logging + +interface Logger { + fun info(message: String) + + fun warn(message: String) + + fun warn(message: String, error: Throwable) + + fun severe(message: String) + + fun severe(message: String, error: Throwable) +} \ No newline at end of file diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/MetricsDriver.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/MetricsDriver.kt new file mode 100644 index 00000000..9ac94007 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/MetricsDriver.kt @@ -0,0 +1,24 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric + +import java.io.Closeable + +interface MetricsDriver : Closeable { + fun initialize() +} \ No newline at end of file diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/MetricsDriverFactory.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/MetricsDriverFactory.kt new file mode 100644 index 00000000..80b3b080 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/MetricsDriverFactory.kt @@ -0,0 +1,29 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import kotlinx.serialization.KSerializer + +interface MetricsDriverFactory { + val configSerializer: KSerializer + + val defaultConfig: T + + fun createDriver(api: UnifiedMetrics, config: T): MetricsDriver +} \ No newline at end of file diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/MetricsManager.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/MetricsManager.kt new file mode 100644 index 00000000..e0aad34d --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/MetricsManager.kt @@ -0,0 +1,40 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric + +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.data.Metric + +interface MetricsManager { + val collections: List + + fun initialize() + + fun registerCollection(collection: CollectorCollection) + + fun unregisterCollection(collection: CollectorCollection) + + fun registerDriver(name: String, factory: MetricsDriverFactory) + + /** + * This should be called asynchronously + */ + suspend fun collect(): List + + fun dispose() +} diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/Collector.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/Collector.kt new file mode 100644 index 00000000..66fc243c --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/Collector.kt @@ -0,0 +1,33 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric.collector + +import dev.cubxity.plugins.metrics.api.metric.data.Metric + +const val NANOSECONDS_PER_MILLISECOND: Double = 1E6 +const val NANOSECONDS_PER_SECOND: Double = 1E9 +const val MILLISECONDS_PER_SECOND: Double = 1E3 + +interface Collector { + /** + * Collects the metric and returns a list of samples. + * + * @return [List] of [Metric] + */ + fun collect(): List +} \ No newline at end of file diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/CollectorCollection.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/CollectorCollection.kt new file mode 100644 index 00000000..3f5a61c8 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/CollectorCollection.kt @@ -0,0 +1,44 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric.collector + +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.api.util.fastFlatMap + +interface CollectorCollection { + /** + * List of collectors associated with this metric. + */ + val collectors: List + + /** + * Whether the collection should be collected asynchronously. + */ + val isAsync: Boolean get() = false + + fun initialize() { + // Do nothing + } + + fun dispose() { + // Do nothing + } +} + +fun CollectorCollection.collect(): List = + collectors.fastFlatMap { it.collect() } \ No newline at end of file diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/Counter.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/Counter.kt new file mode 100644 index 00000000..599a5a87 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/Counter.kt @@ -0,0 +1,53 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric.collector + +import dev.cubxity.plugins.metrics.api.metric.data.CounterMetric +import dev.cubxity.plugins.metrics.api.metric.data.Labels +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.api.metric.store.DoubleAdderStore +import dev.cubxity.plugins.metrics.api.metric.store.DoubleStoreFactory + +/** + * @param name name of the sample. Should end with '_total' + */ +class Counter( + private val name: String, + private val labels: Labels = emptyMap(), + valueStoreFactory: DoubleStoreFactory = DoubleAdderStore +) : Collector { + private val count = valueStoreFactory.create() + + override fun collect(): List { + return listOf( + CounterMetric(name, labels, count.get()) + ) + } + + operator fun inc(): Counter = apply { + count.add(1.0) + } + + operator fun plusAssign(delta: Double) { + count.add(delta) + } + + operator fun plusAssign(delta: Number) { + count.add(delta.toDouble()) + } +} \ No newline at end of file diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/Histogram.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/Histogram.kt new file mode 100644 index 00000000..e44b2a55 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/collector/Histogram.kt @@ -0,0 +1,100 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric.collector + +import dev.cubxity.plugins.metrics.api.metric.data.Bucket +import dev.cubxity.plugins.metrics.api.metric.data.HistogramMetric +import dev.cubxity.plugins.metrics.api.metric.data.Labels +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.api.metric.store.DoubleAdderStore +import dev.cubxity.plugins.metrics.api.metric.store.DoubleStoreFactory +import dev.cubxity.plugins.metrics.api.metric.store.LongAdderStore +import dev.cubxity.plugins.metrics.api.metric.store.LongStoreFactory + +private val defaultBuckets = doubleArrayOf( + .001, // 1 ms + .005, // 5 ms + .01, // 10 ms + .02, // 20 ms + .03, // 30 ms + .04, // 40 ms + .05, // 50 ms + .075, // 75 ms + .1, // 100 ms + .25, // 250 ms + .5, // 500 ms + .75, // 750 ms + 1.0, // 1 s + 2.5, // 2.5 s + 5.0, // 5 s + 7.5, // 7.5 s + 10.0, // 10 s + 60.0 // 1 m +) + +/** + * @param name name of the sample. + */ +class Histogram( + private val name: String, + private val labels: Labels = emptyMap(), + upperBounds: DoubleArray = defaultBuckets, + sumStoreFactory: DoubleStoreFactory = DoubleAdderStore, + countStoreFactory: LongStoreFactory = LongAdderStore +) : Collector { + private val upperBounds = upperBounds + Double.POSITIVE_INFINITY + + private val sum = sumStoreFactory.create() + private val counts = Array(this.upperBounds.size) { + countStoreFactory.create() + } + + override fun collect(): List { + val bucketSize = counts.size + val bucket = arrayOfNulls(bucketSize) + var count = 0L + + for (i in bucketSize downTo 1) { + val index = bucketSize - i + + count += counts[index].get() + bucket[index] = Bucket(upperBounds[index], count.toDouble()) + } + + @Suppress("UNCHECKED_CAST") + return listOf( + HistogramMetric(name, labels, count.toDouble(), sum.get(), bucket as Array) + ) + } + + operator fun plusAssign(value: Double) { + val size = upperBounds.size + for (i in size downTo 1) { + val index = size - i + if (value <= upperBounds[index]) { + counts[index].add(1) + break + } + } + sum.add(value) + } + + operator fun plusAssign(value: Number) { + plusAssign(value.toDouble()) + } +} diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/data/Metric.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/data/Metric.kt new file mode 100644 index 00000000..3a2190a0 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/data/Metric.kt @@ -0,0 +1,69 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +@file:Suppress("FunctionName") + +package dev.cubxity.plugins.metrics.api.metric.data + +typealias Labels = Map + +sealed class Metric( + val name: String, + val labels: Labels = emptyMap() +) + +class GaugeMetric( + name: String, + labels: Labels = emptyMap(), + val value: Double +) : Metric(name, labels) { + constructor( + name: String, + labels: Labels = emptyMap(), + value: Number + ) : this(name, labels, value.toDouble()) +} + +class CounterMetric( + name: String, + labels: Labels = emptyMap(), + val value: Double +) : Metric(name, labels) { + constructor( + name: String, + labels: Labels = emptyMap(), + value: Number + ) : this(name, labels, value.toDouble()) +} + +class HistogramMetric( + name: String, + labels: Labels = emptyMap(), + val sampleCount: Double, + val sampleSum: Double, + val bucket: Array +) : Metric(name, labels) { + constructor( + name: String, + labels: Labels = emptyMap(), + sampleCount: Number, + sampleSum: Number, + bucket: Array + ) : this(name, labels, sampleCount.toDouble(), sampleSum.toDouble(), bucket) +} + +data class Bucket(val upperBound: Double, val cumulativeCount: Double) diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/data/MetricType.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/data/MetricType.kt new file mode 100644 index 00000000..01a187c8 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/data/MetricType.kt @@ -0,0 +1,28 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric.data + +/** + * Prometheus-compatible metric types. The Prometheus server does not yet make use of the type information. + */ +sealed class MetricType { + object Unknown : MetricType() + object Counter : MetricType() + object Gauge : MetricType() + object Histogram : MetricType() +} diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/store/Double.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/store/Double.kt new file mode 100644 index 00000000..5a1587b9 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/store/Double.kt @@ -0,0 +1,53 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric.store + +import java.util.concurrent.atomic.DoubleAdder + +typealias DoubleStore = Store + +typealias DoubleStoreFactory = StoreFactory + +class VolatileDoubleStore : DoubleStore { + @Volatile + private var value: Double = 0.0 + + override fun add(delta: Double) { + value += delta + } + + override fun get(): Double = value + + companion object Factory : DoubleStoreFactory { + override fun create(): DoubleStore = VolatileDoubleStore() + } +} + +class DoubleAdderStore : DoubleStore { + private val value = DoubleAdder() + + override fun add(delta: Double) { + value.add(delta) + } + + override fun get(): Double = value.sum() + + companion object Factory : DoubleStoreFactory { + override fun create(): DoubleStore = DoubleAdderStore() + } +} diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/store/Long.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/store/Long.kt new file mode 100644 index 00000000..9a4e09cf --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/store/Long.kt @@ -0,0 +1,53 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric.store + +import java.util.concurrent.atomic.LongAdder + +typealias LongStore = Store + +typealias LongStoreFactory = StoreFactory + +class VolatileLongStore : LongStore { + @Volatile + private var value: Long = 0 + + override fun add(delta: Long) { + value += delta + } + + override fun get(): Long = value + + companion object Factory : LongStoreFactory { + override fun create(): LongStore = VolatileLongStore() + } +} + +class LongAdderStore : LongStore { + private val value = LongAdder() + + override fun add(delta: Long) { + value.add(delta) + } + + override fun get(): Long = value.sum() + + companion object Factory : LongStoreFactory { + override fun create(): LongStore = LongAdderStore() + } +} diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/metric/store/Store.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/store/Store.kt new file mode 100644 index 00000000..eab7fd6e --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/metric/store/Store.kt @@ -0,0 +1,28 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.metric.store + +interface Store { + fun add(delta: T) + + fun get(): T +} + +interface StoreFactory> { + fun create(): T +} diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/platform/Platform.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/platform/Platform.kt new file mode 100644 index 00000000..b2ed2a97 --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/platform/Platform.kt @@ -0,0 +1,30 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.platform + +/** + * Represents the platform that UnifiedMetrics is running on. + */ +interface Platform { + /** + * The platform that UnifiedMetrics is running on. + * + * @see PlatformType + */ + val type: PlatformType +} \ No newline at end of file diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/platform/PlatformType.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/platform/PlatformType.kt new file mode 100644 index 00000000..d593e11f --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/platform/PlatformType.kt @@ -0,0 +1,29 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.platform + +sealed class PlatformType(val name: String) { + // Server implementations + object Bukkit : PlatformType("Bukkit") + object Minestom : PlatformType("Minestom") + object Fabric : PlatformType("Fabric") + + // Proxies + object Velocity : PlatformType("Velocity") + object BungeeCord : PlatformType("BungeeCord") +} diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/util/Arrays.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/util/Arrays.kt new file mode 100644 index 00000000..fb74a26c --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/util/Arrays.kt @@ -0,0 +1,25 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.util + +inline fun Array.fastForEach(block: (T) -> Unit) { + val size = size + for (index in size downTo 1) { + block(get(size - index)) + } +} diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/util/Collections.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/util/Collections.kt new file mode 100644 index 00000000..8bdccafc --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/util/Collections.kt @@ -0,0 +1,33 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.util + +inline fun List.fastForEach(block: (T) -> Unit) { + val size = size + for (i in size downTo 1) { + block(get(size - i)) + } +} + +inline fun List.fastFlatMap(block: (T) -> Collection): List { + val list = ArrayList() + fastForEach { + list.addAll(block(it)) + } + return list +} diff --git a/api/bin/main/dev/cubxity/plugins/metrics/api/util/Numbers.kt b/api/bin/main/dev/cubxity/plugins/metrics/api/util/Numbers.kt new file mode 100644 index 00000000..bbe5cfac --- /dev/null +++ b/api/bin/main/dev/cubxity/plugins/metrics/api/util/Numbers.kt @@ -0,0 +1,24 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.api.util + +fun Double.toGoString(): String = when (this) { + Double.POSITIVE_INFINITY -> "+Inf" + Double.NEGATIVE_INFINITY -> "-Inf" + else -> toString() +} diff --git a/api/build.gradle.kts b/api/build.gradle.kts index fdfbd6c3..cb3fa361 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -18,8 +18,8 @@ dependencies { api(platform(kotlin("bom"))) api(kotlin("stdlib")) - api("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3") - api("org.jetbrains.kotlinx", "kotlinx-serialization-core", "1.6.0") + api("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.8.1") + api("org.jetbrains.kotlinx", "kotlinx-serialization-core", "1.7.1") } java { diff --git a/build.gradle.kts b/build.gradle.kts index ef55a3b0..0ee24b7e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,21 +15,23 @@ * along with UnifiedMetrics. If not, see . */ -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "1.9.10" apply false - kotlin("kapt") version "1.9.10" apply false - kotlin("plugin.serialization") version "1.9.10" apply false + kotlin("jvm") version "2.0.0" apply false + kotlin("kapt") version "2.0.0" apply false + kotlin("plugin.serialization") version "2.0.0" apply false id("com.github.johnrengelman.shadow") version "8.1.1" apply false - id("net.kyori.blossom") version "1.3.1" apply false + + // The fabric-loom plugin must be defined in the root project for it to function properly. + id("fabric-loom") version "1.9-SNAPSHOT" apply false } allprojects { group = "dev.cubxity.plugins" description = "Fully featured metrics collector agent for Minecraft servers." - version = "0.3.9-SNAPSHOT" + version = "0.3.10-SNAPSHOT" repositories { mavenCentral() @@ -43,13 +45,13 @@ subprojects { apply(plugin = "maven-publish") tasks.withType { - kotlinOptions { - kotlinOptions.jvmTarget = "1.8" + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn") } } configure { - targetCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_21 } configure { repositories { diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/UnifiedMetricsBootstrap.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/UnifiedMetricsBootstrap.kt new file mode 100644 index 00000000..e7f0dff7 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/UnifiedMetricsBootstrap.kt @@ -0,0 +1,60 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common + +import dev.cubxity.plugins.metrics.api.logging.Logger +import dev.cubxity.plugins.metrics.api.platform.PlatformType +import kotlinx.coroutines.CoroutineDispatcher +import java.nio.file.Path + +interface UnifiedMetricsBootstrap { + /** + * The plugin's platform type + */ + val type: PlatformType + + /** + * The installed plugin's version + */ + val version: String + + /** + * The server's brand + */ + val serverBrand: String + + /** + * The plugin's data directory + */ + val dataDirectory: Path + + /** + * The plugin's config directory + */ + val configDirectory: Path + + /** + * The platform's logger + */ + val logger: Logger + + /** + * The platform's dispatcher + */ + val dispatcher: CoroutineDispatcher +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/api/MetricsManagerImpl.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/api/MetricsManagerImpl.kt new file mode 100644 index 00000000..d1d20de6 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/api/MetricsManagerImpl.kt @@ -0,0 +1,156 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.api + +import com.charleskorn.kaml.Yaml +import com.charleskorn.kaml.YamlConfiguration +import dev.cubxity.plugins.metrics.api.metric.MetricsDriver +import dev.cubxity.plugins.metrics.api.metric.MetricsDriverFactory +import dev.cubxity.plugins.metrics.api.metric.MetricsManager +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.collect +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.api.util.fastForEach +import dev.cubxity.plugins.metrics.common.plugin.UnifiedMetricsPlugin +import dev.cubxity.plugins.metrics.common.plugin.dispatcher.CurrentThreadDispatcher +import kotlinx.coroutines.withContext +import java.nio.file.Files +import kotlin.system.measureTimeMillis + +class MetricsManagerImpl(private val plugin: UnifiedMetricsPlugin) : MetricsManager { + private val yaml = Yaml(configuration = YamlConfiguration(strictMode = false)) + private val driverDirectory = plugin.bootstrap.configDirectory.resolve("driver") + + private val metricDrivers: MutableMap> = HashMap() + private val _collections: MutableList = ArrayList() + + private var shouldInitialize: Boolean = false + private var driver: MetricsDriver? = null + + override val collections: List + get() = _collections + + override fun initialize() { + shouldInitialize = true + + val driverName = plugin.config.metrics.driver + val factory = metricDrivers[driverName] + + Files.createDirectories(driverDirectory) + + if (factory !== null) { + initializeDriver(driverName, factory) + } else { + plugin.bootstrap.logger.warn("Driver '$driverName' not found. Metrics will be enabled when the driver is loaded.") + } + } + + override fun registerCollection(collection: CollectorCollection) { + try { + collection.initialize() + _collections.add(collection) + } catch (error: Throwable) { + plugin.bootstrap.logger.warn("An error occurred whilst registering metric", error) + } + } + + override fun unregisterCollection(collection: CollectorCollection) { + try { + _collections.remove(collection) + collection.dispose() + } catch (error: Throwable) { + plugin.bootstrap.logger.warn("An error occurred whilst unregistering metric", error) + } + } + + @Suppress("UNCHECKED_CAST") + override fun registerDriver(name: String, factory: MetricsDriverFactory) { + metricDrivers[name] = factory as MetricsDriverFactory + + if (shouldInitialize && driver === null) { + if (name == plugin.config.metrics.driver) { + initializeDriver(name, factory) + } + } + } + + override suspend fun collect(): List { + val list = ArrayList() + val dispatcher = plugin.apiProvider.dispatcher + + if (dispatcher is CurrentThreadDispatcher) { + collections.fastForEach { collection -> + list.addAll(collection.collect()) + } + } else { + withContext(dispatcher) { + collections.fastForEach { collection -> + if (!collection.isAsync) { + list.addAll(collection.collect()) + } + } + } + collections.fastForEach { collection -> + if (collection.isAsync) { + list.addAll(collection.collect()) + } + } + } + return list + } + + override fun dispose() { + shouldInitialize = false + + collections.toList().fastForEach { collection -> + unregisterCollection(collection) + } + + driver?.close() + driver = null + } + + private fun initializeDriver(name: String, factory: MetricsDriverFactory) { + plugin.bootstrap.logger.info("Initializing driver '$name'.") + val time = measureTimeMillis { + try { + val file = driverDirectory.toFile().resolve("$name.yml") + + val serializer = factory.configSerializer + val config = when { + file.exists() -> yaml.decodeFromString(serializer, file.readText()) + else -> factory.defaultConfig + } + + try { + file.writeText(yaml.encodeToString(serializer, config)) + } catch (exception: Exception) { + plugin.apiProvider.logger.severe("An error occurred whilst saving driver config file ", exception) + } + + val driver = factory.createDriver(plugin.apiProvider, config) + driver.initialize() + + this.driver = driver + } catch (error: Throwable) { + plugin.apiProvider.logger.severe("An error occurred whilst initializing metrics driver $name", error) + } + } + plugin.bootstrap.logger.info("Driver '$name' initialized ($time ms).") + } +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/api/PlatformImpl.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/api/PlatformImpl.kt new file mode 100644 index 00000000..57a95365 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/api/PlatformImpl.kt @@ -0,0 +1,27 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.api + +import dev.cubxity.plugins.metrics.common.plugin.UnifiedMetricsPlugin +import dev.cubxity.plugins.metrics.api.platform.Platform +import dev.cubxity.plugins.metrics.api.platform.PlatformType + +class PlatformImpl(private val plugin: UnifiedMetricsPlugin) : Platform { + override val type: PlatformType + get() = plugin.bootstrap.type +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/api/UnifiedMetricsApiProvider.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/api/UnifiedMetricsApiProvider.kt new file mode 100644 index 00000000..7bcc7272 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/api/UnifiedMetricsApiProvider.kt @@ -0,0 +1,41 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.api + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.api.logging.Logger +import dev.cubxity.plugins.metrics.api.metric.MetricsManager +import dev.cubxity.plugins.metrics.api.platform.Platform +import dev.cubxity.plugins.metrics.common.plugin.UnifiedMetricsPlugin +import kotlinx.coroutines.CoroutineDispatcher + +open class UnifiedMetricsApiProvider(val plugin: UnifiedMetricsPlugin) : UnifiedMetrics { + override val platform: Platform = PlatformImpl(plugin) + + override val serverName: String + get() = plugin.config.server.name + + override val logger: Logger + get() = plugin.bootstrap.logger + + override val dispatcher: CoroutineDispatcher + get() = plugin.bootstrap.dispatcher + + override val metricsManager: MetricsManager = + MetricsManagerImpl(plugin) +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/config/UnifiedMetricsConfig.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/config/UnifiedMetricsConfig.kt new file mode 100644 index 00000000..ffd2abbb --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/config/UnifiedMetricsConfig.kt @@ -0,0 +1,54 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.config + +import kotlinx.serialization.Serializable + +@Serializable +data class UnifiedMetricsConfig( + val server: UnifiedMetricsServerConfig = UnifiedMetricsServerConfig(), + val metrics: UnifiedMetricsMetricsConfig = UnifiedMetricsMetricsConfig() +) + +@Serializable +data class UnifiedMetricsServerConfig( + val name: String = env("SERVER_NAME", "global") +) + +@Serializable +data class UnifiedMetricsMetricsConfig( + val enabled: Boolean = true, + val driver: String = "prometheus", + val collectors: UnifiedMetricsCollectorsConfig = UnifiedMetricsCollectorsConfig() +) + +@Serializable +data class UnifiedMetricsCollectorsConfig( + val systemGc: Boolean = true, + val systemMemory: Boolean = true, + val systemProcess: Boolean = true, + val systemThread: Boolean = true, + val server: Boolean = true, + val world: Boolean = true, + val tick: Boolean = true, + val events: Boolean = true, + val regionizedServer: Boolean = true +) + +private fun env(name: String, default: String): String = + System.getenv("UNIFIEDMETRICS_$name") ?: default diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/metric/Metrics.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/Metrics.kt new file mode 100644 index 00000000..8bfbeadb --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/Metrics.kt @@ -0,0 +1,46 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.metric + +object Metrics { + object Events { + const val Login = "minecraft_events_login_total" + const val Join = "minecraft_events_join_total" + const val Quit = "minecraft_events_quit_total" + const val Chat = "minecraft_events_chat_total" + const val Ping = "minecraft_events_ping_total" + } + + object Server { + const val Plugins = "minecraft_plugins" + const val PlayersCount = "minecraft_players_count" + const val PlayersMax = "minecraft_players_max" + const val TickDurationSeconds = "minecraft_tick_duration_seconds" + const val WorldEntitiesCount = "minecraft_world_entities_count" + const val WorldPlayersCount = "minecraft_world_players_count" + const val WorldLoadedChunks = "minecraft_world_loaded_chunks" + } + + object RegionizedServer { + const val RegionCount = "minecraft_regionized_region_count" + const val RegionTick = "minecraft_regionized_region_tick_total" + const val RegionEntitiesCount = "minecraft_regionized_region_entities_count" + const val RegionPlayersCount = "minecraft_regionized_region_players_count" + const val RegionChunksCount = "minecraft_regionized_region_chunks_count" + } +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/gc/GCCollection.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/gc/GCCollection.kt new file mode 100644 index 00000000..03e1f4ee --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/gc/GCCollection.kt @@ -0,0 +1,77 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.metric.system.gc + +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.Histogram +import dev.cubxity.plugins.metrics.api.util.fastForEach +import java.lang.management.GarbageCollectorMXBean +import java.lang.management.ManagementFactory +import java.util.* +import javax.management.NotificationEmitter + +private val byteBuckets = doubleArrayOf( + 25_000_000.0, // 25 MB + 50_000_000.0, // 50 MB + 100_000_000.0, // 100 MB + 250_000_000.0, // 250 MB + 500_000_000.0, // 500 MB + 1_000_000_000.0, // 1 GB + 2_000_000_000.0, // 2 GB + 3_000_000_000.0, // 3 GB + 5_000_000_000.0, // 5 GB +) + +class GCCollection : CollectorCollection { + private val monitors = WeakHashMap() + + override val collectors = ArrayList() + + override val isAsync: Boolean + get() = true + + override fun initialize() { + ManagementFactory.getGarbageCollectorMXBeans().fastForEach { bean -> + if (bean is NotificationEmitter) { + val labels = mapOf("gc" to bean.name) + val durationHistogram = Histogram("jvm_gc_duration_seconds", labels) + val freedHistogram = Histogram("jvm_gc_freed_bytes", labels, byteBuckets) + + collectors += durationHistogram + collectors += freedHistogram + + val monitor = GCMonitor(durationHistogram, freedHistogram) + monitors[bean] = monitor + + bean.addNotificationListener(monitor, null, null) + } + } + } + + override fun dispose() { + ManagementFactory.getGarbageCollectorMXBeans().fastForEach { bean -> + if (bean is NotificationEmitter) { + val monitor = monitors.remove(bean) + if (monitor !== null) { + bean.removeNotificationListener(monitor) + } + } + } + collectors.clear() + } +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/gc/GCMonitor.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/gc/GCMonitor.kt new file mode 100644 index 00000000..b50e8df4 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/gc/GCMonitor.kt @@ -0,0 +1,52 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.metric.system.gc + +import com.sun.management.GarbageCollectionNotificationInfo +import dev.cubxity.plugins.metrics.api.metric.collector.Histogram +import dev.cubxity.plugins.metrics.api.metric.collector.MILLISECONDS_PER_SECOND +import javax.management.Notification +import javax.management.NotificationListener +import javax.management.openmbean.CompositeData + +class GCMonitor( + private val durationHistogram: Histogram, + private val freedHistogram: Histogram +) : NotificationListener { + override fun handleNotification(notification: Notification, handback: Any?) { + if (notification.type != GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION) { + return + } + + val info = (notification.userData as? CompositeData) + ?.let { GarbageCollectionNotificationInfo.from(it) }?.gcInfo ?: return + + durationHistogram += info.duration / MILLISECONDS_PER_SECOND + + var diff = 0L + + for (usage in info.memoryUsageBeforeGc.values) { + diff += usage.used + } + for (usage in info.memoryUsageAfterGc.values) { + diff -= usage.used + } + + freedHistogram += diff + } +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/memory/MemoryCollection.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/memory/MemoryCollection.kt new file mode 100644 index 00000000..69ab722d --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/memory/MemoryCollection.kt @@ -0,0 +1,28 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.metric.system.memory + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection + +class MemoryCollection : CollectorCollection { + override val collectors: List = listOf(MemoryCollector()) + + override val isAsync: Boolean + get() = true +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/memory/MemoryCollector.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/memory/MemoryCollector.kt new file mode 100644 index 00000000..a320ce1e --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/memory/MemoryCollector.kt @@ -0,0 +1,46 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.metric.system.memory + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import java.lang.management.ManagementFactory + +class MemoryCollector : Collector { + private val bean = ManagementFactory.getMemoryMXBean() + + override fun collect(): List { + val heapUsage = bean.heapMemoryUsage + val nonHeapUsage = bean.nonHeapMemoryUsage + + val heapTags = mapOf("area" to "heap") + val nonHeapTags = mapOf("area" to "nonheap") + + return listOf( + GaugeMetric("jvm_memory_bytes_used", heapTags, heapUsage.used), + GaugeMetric("jvm_memory_bytes_used", nonHeapTags, nonHeapUsage.used), + GaugeMetric("jvm_memory_bytes_committed", heapTags, heapUsage.committed), + GaugeMetric("jvm_memory_bytes_committed", nonHeapTags, nonHeapUsage.committed), + GaugeMetric("jvm_memory_bytes_max", heapTags, heapUsage.max), + GaugeMetric("jvm_memory_bytes_max", nonHeapTags, nonHeapUsage.max), + GaugeMetric("jvm_memory_bytes_init", heapTags, heapUsage.init), + GaugeMetric("jvm_memory_bytes_init", nonHeapTags, nonHeapUsage.init) + ) + } +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/process/ProcessCollection.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/process/ProcessCollection.kt new file mode 100644 index 00000000..d1c85550 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/process/ProcessCollection.kt @@ -0,0 +1,28 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.metric.system.process + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection + +class ProcessCollection : CollectorCollection { + override val collectors: List = listOf(ProcessCollector()) + + override val isAsync: Boolean + get() = true +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/process/ProcessCollector.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/process/ProcessCollector.kt new file mode 100644 index 00000000..bfd08018 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/process/ProcessCollector.kt @@ -0,0 +1,41 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.metric.system.process + +import com.sun.management.OperatingSystemMXBean +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.MILLISECONDS_PER_SECOND +import dev.cubxity.plugins.metrics.api.metric.collector.NANOSECONDS_PER_SECOND +import dev.cubxity.plugins.metrics.api.metric.data.CounterMetric +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import java.lang.management.ManagementFactory + +class ProcessCollector : Collector { + private val osBean = ManagementFactory.getOperatingSystemMXBean() + private val runtimeBean = ManagementFactory.getRuntimeMXBean() + + override fun collect(): List = ArrayList(3).apply { + (osBean as? OperatingSystemMXBean)?.apply { + add(GaugeMetric("process_cpu_load_ratio", value = processCpuLoad)) + add(CounterMetric("process_cpu_seconds_total", value = processCpuTime / NANOSECONDS_PER_SECOND)) + } + + add(GaugeMetric("process_start_time_seconds", value = runtimeBean.startTime / MILLISECONDS_PER_SECOND)) + } +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/thread/ThreadCollection.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/thread/ThreadCollection.kt new file mode 100644 index 00000000..acb525b0 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/thread/ThreadCollection.kt @@ -0,0 +1,28 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.metric.system.thread + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection + +class ThreadCollection : CollectorCollection { + override val collectors: List = listOf(ThreadCollector()) + + override val isAsync: Boolean + get() = true +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/thread/ThreadCollector.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/thread/ThreadCollector.kt new file mode 100644 index 00000000..0e48972c --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/metric/system/thread/ThreadCollector.kt @@ -0,0 +1,55 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.metric.system.thread + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.NANOSECONDS_PER_SECOND +import dev.cubxity.plugins.metrics.api.metric.data.CounterMetric +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import java.lang.management.ManagementFactory + +class ThreadCollector : Collector { + private val bean = ManagementFactory.getThreadMXBean() + + override fun collect(): List { + val ids = bean.allThreadIds + val list = ArrayList(4 + 2 * ids.size) + + list += GaugeMetric("jvm_threads_current_count", value = bean.threadCount) + list += GaugeMetric("jvm_threads_daemon_count", value = bean.daemonThreadCount) + list += CounterMetric("jvm_threads_started_total", value = bean.totalStartedThreadCount) + list += GaugeMetric("jvm_threads_peak", value = bean.peakThreadCount) + + ids.forEach { id -> + val info = bean.getThreadInfo(id) ?: return@forEach + val labels = mapOf("thread" to info.threadName) + list += CounterMetric( + "jvm_threads_cpu_time_total", + value = bean.getThreadCpuTime(id) / NANOSECONDS_PER_SECOND, + labels = labels + ) + list += CounterMetric( + "jvm_threads_user_time_total", + value = bean.getThreadUserTime(id) / NANOSECONDS_PER_SECOND, + labels = labels + ) + } + return list + } +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/AbstractUnifiedMetricsPlugin.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/AbstractUnifiedMetricsPlugin.kt new file mode 100644 index 00000000..86d7ad6c --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/AbstractUnifiedMetricsPlugin.kt @@ -0,0 +1,108 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.plugin + +import com.charleskorn.kaml.Yaml +import com.charleskorn.kaml.YamlConfiguration +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.api.UnifiedMetricsProvider +import dev.cubxity.plugins.metrics.common.api.UnifiedMetricsApiProvider +import dev.cubxity.plugins.metrics.common.config.UnifiedMetricsConfig +import dev.cubxity.plugins.metrics.common.metric.system.gc.GCCollection +import dev.cubxity.plugins.metrics.common.metric.system.memory.MemoryCollection +import dev.cubxity.plugins.metrics.common.metric.system.process.ProcessCollection +import dev.cubxity.plugins.metrics.common.metric.system.thread.ThreadCollection +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import java.nio.file.Files + +abstract class AbstractUnifiedMetricsPlugin : UnifiedMetricsPlugin { + private val yaml = Yaml(configuration = YamlConfiguration(strictMode = false)) + + private var _config: UnifiedMetricsConfig? = null + private var _apiProvider: UnifiedMetricsApiProvider? = null + + override val config: UnifiedMetricsConfig + get() = _config ?: error("The UnifiedMetrics plugin is not loaded.") + + override val apiProvider: UnifiedMetricsApiProvider + get() = _apiProvider ?: error("The UnifiedMetrics plugin is not loaded.") + + open fun enable() { + Files.createDirectories(bootstrap.dataDirectory) + Files.createDirectories(bootstrap.configDirectory) + + _config = loadConfig() + _apiProvider = UnifiedMetricsApiProvider(this) + + try { + saveConfig() + } catch (exception: Exception) { + apiProvider.logger.severe("An error occurred whilst saving plugin config file", exception) + } + + UnifiedMetricsProvider.register(apiProvider) + registerPlatformService(apiProvider) + + if (config.metrics.enabled) { + registerMetricsDrivers() + registerPlatformMetrics() + apiProvider.metricsManager.initialize() + } + } + + open fun disable() { + if (config.metrics.enabled) { + apiProvider.metricsManager.dispose() + } + + UnifiedMetricsProvider.unregister() + _apiProvider = null + } + + abstract fun registerPlatformService(api: UnifiedMetrics) + + open fun registerMetricsDrivers() { + + } + + open fun registerPlatformMetrics() { + apiProvider.metricsManager.apply { + with(config.metrics.collectors) { + if (systemGc) registerCollection(GCCollection()) + if (systemMemory) registerCollection(MemoryCollection()) + if (systemProcess) registerCollection(ProcessCollection()) + if (systemThread) registerCollection(ThreadCollection()) + } + } + } + + private fun loadConfig(): UnifiedMetricsConfig { + val file = bootstrap.configDirectory.toFile().resolve("config.yml") + + return when { + file.exists() -> yaml.decodeFromString(file.readText()) + else -> UnifiedMetricsConfig() + } + } + + private fun saveConfig() { + val file = bootstrap.configDirectory.toFile().resolve("config.yml") + file.writeText(yaml.encodeToString(config)) + } +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/UnifiedMetricsPlugin.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/UnifiedMetricsPlugin.kt new file mode 100644 index 00000000..33026884 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/UnifiedMetricsPlugin.kt @@ -0,0 +1,30 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.plugin + +import dev.cubxity.plugins.metrics.common.UnifiedMetricsBootstrap +import dev.cubxity.plugins.metrics.common.api.UnifiedMetricsApiProvider +import dev.cubxity.plugins.metrics.common.config.UnifiedMetricsConfig + +interface UnifiedMetricsPlugin { + val bootstrap: UnifiedMetricsBootstrap + + val config: UnifiedMetricsConfig + + val apiProvider: UnifiedMetricsApiProvider +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/dispatcher/CurrentThreadDispatcher.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/dispatcher/CurrentThreadDispatcher.kt new file mode 100644 index 00000000..4ab2681c --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/dispatcher/CurrentThreadDispatcher.kt @@ -0,0 +1,28 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.plugin.dispatcher + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Runnable +import kotlin.coroutines.CoroutineContext + +object CurrentThreadDispatcher : CoroutineDispatcher() { + override fun dispatch(context: CoroutineContext, block: Runnable) { + block.run() + } +} \ No newline at end of file diff --git a/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/logger/JavaLogger.kt b/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/logger/JavaLogger.kt new file mode 100644 index 00000000..e9a96860 --- /dev/null +++ b/common/bin/main/dev/cubxity/plugins/metrics/common/plugin/logger/JavaLogger.kt @@ -0,0 +1,43 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.common.plugin.logger + +import dev.cubxity.plugins.metrics.api.logging.Logger +import java.util.logging.Level + +class JavaLogger(private val logger: java.util.logging.Logger) : Logger { + override fun info(message: String) { + logger.log(Level.INFO, message) + } + + override fun warn(message: String) { + logger.log(Level.WARNING, message) + } + + override fun warn(message: String, error: Throwable) { + logger.log(Level.WARNING, message, error) + } + + override fun severe(message: String) { + logger.log(Level.SEVERE, message) + } + + override fun severe(message: String, error: Throwable) { + logger.log(Level.SEVERE, message, error) + } +} \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts index e4b76d3b..fa41e02b 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -19,5 +19,5 @@ apply(plugin = "kotlinx-serialization") dependencies { api(project(":unifiedmetrics-api")) - implementation("com.charleskorn.kaml:kaml:0.55.0") + implementation("com.charleskorn.kaml:kaml:0.60.0") } diff --git a/core/bin/main/dev/cubxity/plugins/metrics/core/plugin/CoreUnifiedMetricsPlugin.kt b/core/bin/main/dev/cubxity/plugins/metrics/core/plugin/CoreUnifiedMetricsPlugin.kt new file mode 100644 index 00000000..bdf077cb --- /dev/null +++ b/core/bin/main/dev/cubxity/plugins/metrics/core/plugin/CoreUnifiedMetricsPlugin.kt @@ -0,0 +1,31 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.core.plugin + +import dev.cubxity.plugins.metrics.common.plugin.AbstractUnifiedMetricsPlugin +import dev.cubxity.plugins.metrics.influx.InfluxMetricsDriverFactory +import dev.cubxity.plugins.metrics.prometheus.PrometheusMetricsDriverFactory + +abstract class CoreUnifiedMetricsPlugin : AbstractUnifiedMetricsPlugin() { + override fun registerMetricsDrivers() { + apiProvider.metricsManager.apply { + registerDriver("influx", InfluxMetricsDriverFactory) + registerDriver("prometheus", PrometheusMetricsDriverFactory) + } + } +} \ No newline at end of file diff --git a/drivers/influx/bin/main/dev/cubxity/plugins/metrics/influx/InfluxMetricsDriver.kt b/drivers/influx/bin/main/dev/cubxity/plugins/metrics/influx/InfluxMetricsDriver.kt new file mode 100644 index 00000000..09c9d2ae --- /dev/null +++ b/drivers/influx/bin/main/dev/cubxity/plugins/metrics/influx/InfluxMetricsDriver.kt @@ -0,0 +1,112 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.influx + +import com.influxdb.client.InfluxDBClient +import com.influxdb.client.InfluxDBClientFactory +import com.influxdb.client.InfluxDBClientOptions +import com.influxdb.client.WriteApi +import com.influxdb.client.write.Point +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.api.metric.MetricsDriver +import dev.cubxity.plugins.metrics.api.metric.data.CounterMetric +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.HistogramMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.api.util.fastForEach +import dev.cubxity.plugins.metrics.api.util.toGoString +import dev.cubxity.plugins.metrics.influx.config.InfluxConfig +import kotlinx.coroutines.* +import kotlin.math.max +import kotlin.system.measureTimeMillis + +class InfluxMetricsDriver(private val api: UnifiedMetrics, private val config: InfluxConfig) : MetricsDriver { + private val coroutineScope = CoroutineScope(Dispatchers.Default) + SupervisorJob() + + private var influxDBClient: InfluxDBClient? = null + private var writeApi: WriteApi? = null + + override fun initialize() { + val options = InfluxDBClientOptions.builder() + .url(config.output.url) + .apply { + when (config.authentication.scheme) { + InfluxDBClientOptions.AuthScheme.TOKEN -> + authenticateToken(config.authentication.token.toCharArray()) + InfluxDBClientOptions.AuthScheme.SESSION -> + authenticate(config.authentication.username, config.authentication.password.toCharArray()) + } + } + .bucket(config.output.bucket) + .org(config.output.organization) + .build() + + influxDBClient = InfluxDBClientFactory.create(options) + writeApi = influxDBClient?.makeWriteApi() + + scheduleTasks() + } + + override fun close() { + coroutineScope.cancel() + writeApi?.close() + influxDBClient?.close() + influxDBClient = null + } + + private fun scheduleTasks() { + val interval = config.output.interval * 1000 + + coroutineScope.launch { + while (true) { + val time = measureTimeMillis { + try { + val metrics = api.metricsManager.collect() + writeMetrics(metrics) + } catch (error: Throwable) { + api.logger.severe("An error occurred whilst writing samples to InfluxDB", error) + } + } + delay(max(0, interval - time)) + } + } + } + + private fun writeMetrics(metrics: List) { + val writeApi = writeApi ?: return + metrics.fastForEach { metric -> + val point = Point(metric.name) + point.addTags(metric.labels) + point.addTag("server", api.serverName) + + when (metric) { + is GaugeMetric -> point.addField("gauge", metric.value) + is CounterMetric -> point.addField("counter", metric.value) + is HistogramMetric -> { + metric.bucket.fastForEach { bucket -> + point.addField(bucket.upperBound.toGoString(), bucket.cumulativeCount) + } + point.addField("count", metric.sampleCount) + point.addField("sum", metric.sampleSum) + } + } + + writeApi.writePoint(point) + } + } +} diff --git a/drivers/influx/bin/main/dev/cubxity/plugins/metrics/influx/InfluxMetricsDriverFactory.kt b/drivers/influx/bin/main/dev/cubxity/plugins/metrics/influx/InfluxMetricsDriverFactory.kt new file mode 100644 index 00000000..d9a1aed1 --- /dev/null +++ b/drivers/influx/bin/main/dev/cubxity/plugins/metrics/influx/InfluxMetricsDriverFactory.kt @@ -0,0 +1,35 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.influx + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.api.metric.MetricsDriver +import dev.cubxity.plugins.metrics.api.metric.MetricsDriverFactory +import dev.cubxity.plugins.metrics.influx.config.InfluxConfig +import kotlinx.serialization.KSerializer + +object InfluxMetricsDriverFactory : MetricsDriverFactory { + override val configSerializer: KSerializer + get() = InfluxConfig.serializer() + + override val defaultConfig: InfluxConfig + get() = InfluxConfig() + + override fun createDriver(api: UnifiedMetrics, config: InfluxConfig): MetricsDriver = + InfluxMetricsDriver(api, config) +} \ No newline at end of file diff --git a/drivers/influx/bin/main/dev/cubxity/plugins/metrics/influx/config/InfluxSpec.kt b/drivers/influx/bin/main/dev/cubxity/plugins/metrics/influx/config/InfluxSpec.kt new file mode 100644 index 00000000..2ca3736b --- /dev/null +++ b/drivers/influx/bin/main/dev/cubxity/plugins/metrics/influx/config/InfluxSpec.kt @@ -0,0 +1,43 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.influx.config + +import com.influxdb.client.InfluxDBClientOptions +import kotlinx.serialization.Serializable + +@Serializable +data class InfluxConfig( + val output: InfluxOutputConfig = InfluxOutputConfig(), + val authentication: InfluxAuthenticationConfig = InfluxAuthenticationConfig() +) + +@Serializable +data class InfluxOutputConfig( + val url: String = "http://influxdb:8086", + val organization: String = "-", + val bucket: String = "unifiedmetrics", + val interval: Long = 10 +) + +@Serializable +data class InfluxAuthenticationConfig( + val scheme: InfluxDBClientOptions.AuthScheme = InfluxDBClientOptions.AuthScheme.TOKEN, + val username: String = "influx", + val password: String = "influx", + val token: String = "insert_your_token" +) diff --git a/drivers/influx/build.gradle.kts b/drivers/influx/build.gradle.kts index 86a9b852..98d8b6a1 100644 --- a/drivers/influx/build.gradle.kts +++ b/drivers/influx/build.gradle.kts @@ -19,5 +19,5 @@ apply(plugin = "kotlinx-serialization") dependencies { compileOnly(project(":unifiedmetrics-api")) - api("com.influxdb:influxdb-client-java:6.10.0") + api("com.influxdb:influxdb-client-java:7.1.0") } diff --git a/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/PrometheusMetricsDriver.kt b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/PrometheusMetricsDriver.kt new file mode 100644 index 00000000..321c973b --- /dev/null +++ b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/PrometheusMetricsDriver.kt @@ -0,0 +1,41 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.prometheus + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.api.metric.MetricsDriver +import dev.cubxity.plugins.metrics.prometheus.config.PrometheusConfig +import dev.cubxity.plugins.metrics.prometheus.config.PrometheusMode +import dev.cubxity.plugins.metrics.prometheus.exporter.PrometheusExporter +import dev.cubxity.plugins.metrics.prometheus.exporter.PrometheusHTTPExporter +import dev.cubxity.plugins.metrics.prometheus.exporter.PushGatewayExporter + +class PrometheusMetricsDriver(api: UnifiedMetrics, val config: PrometheusConfig) : MetricsDriver { + private val exporter: PrometheusExporter = when (config.mode) { + PrometheusMode.Http -> PrometheusHTTPExporter(api, this) + PrometheusMode.PushGateway -> PushGatewayExporter(api, this) + } + + override fun initialize() { + exporter.initialize() + } + + override fun close() { + exporter.close() + } +} \ No newline at end of file diff --git a/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/PrometheusMetricsDriverFactory.kt b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/PrometheusMetricsDriverFactory.kt new file mode 100644 index 00000000..2d0b4fca --- /dev/null +++ b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/PrometheusMetricsDriverFactory.kt @@ -0,0 +1,35 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.prometheus + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.api.metric.MetricsDriver +import dev.cubxity.plugins.metrics.api.metric.MetricsDriverFactory +import dev.cubxity.plugins.metrics.prometheus.config.PrometheusConfig +import kotlinx.serialization.KSerializer + +object PrometheusMetricsDriverFactory : MetricsDriverFactory { + override val configSerializer: KSerializer + get() = PrometheusConfig.serializer() + + override val defaultConfig: PrometheusConfig + get() = PrometheusConfig() + + override fun createDriver(api: UnifiedMetrics, config: PrometheusConfig): MetricsDriver = + PrometheusMetricsDriver(api, config) +} \ No newline at end of file diff --git a/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/collector/MetricSamplesCollection.kt b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/collector/MetricSamplesCollection.kt new file mode 100644 index 00000000..681fbf9c --- /dev/null +++ b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/collector/MetricSamplesCollection.kt @@ -0,0 +1,24 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.prometheus.collector + +import io.prometheus.client.Collector + +class MetricSamplesCollection(private val collection: List) : Collector() { + override fun collect(): List = collection +} \ No newline at end of file diff --git a/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/collector/UnifiedMetricsCollector.kt b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/collector/UnifiedMetricsCollector.kt new file mode 100644 index 00000000..a5ce6515 --- /dev/null +++ b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/collector/UnifiedMetricsCollector.kt @@ -0,0 +1,38 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.prometheus.collector + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.prometheus.exporter.toPrometheus +import io.prometheus.client.Collector +import kotlinx.coroutines.runBlocking + +class UnifiedMetricsCollector(private val api: UnifiedMetrics) : Collector() { + override fun collect(): List { + return try { + val metrics = runBlocking { + api.metricsManager.collect() + } + + metrics.toPrometheus() + } catch (exception: Exception) { + api.logger.severe("An error occurred whilst collecting metrics", exception) + emptyList() + } + } +} \ No newline at end of file diff --git a/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/config/PrometheusConfig.kt b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/config/PrometheusConfig.kt new file mode 100644 index 00000000..9203d0cb --- /dev/null +++ b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/config/PrometheusConfig.kt @@ -0,0 +1,68 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.prometheus.config + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PrometheusConfig( + val mode: PrometheusMode = PrometheusMode.Http, + val http: PrometheusHttpConfig = PrometheusHttpConfig(), + val pushGateway: PushGatewayConfig = PushGatewayConfig() +) + +@Serializable +enum class PrometheusMode { + @SerialName("HTTP") + Http, + + @SerialName("PUSHGATEWAY") + PushGateway +} + +@Serializable +data class PrometheusHttpConfig( + val host: String = "0.0.0.0", + val port: Int = 9100, + val authentication: AuthenticationConfig = AuthenticationConfig() +) + +@Serializable +data class PushGatewayConfig( + val job: String = "unifiedmetrics", + val url: String = "http://pushgateway:9091", + val authentication: AuthenticationConfig = AuthenticationConfig(), + val interval: Long = 10 +) + +@Serializable +enum class AuthenticationScheme { + @SerialName("NONE") + None, + + @SerialName("BASIC") + Basic +} + +@Serializable +data class AuthenticationConfig( + val scheme: AuthenticationScheme = AuthenticationScheme.None, + val username: String = "username", + val password: String = "password" +) diff --git a/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/exporter/PrometheusExporter.kt b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/exporter/PrometheusExporter.kt new file mode 100644 index 00000000..b70cbbfd --- /dev/null +++ b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/exporter/PrometheusExporter.kt @@ -0,0 +1,99 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.prometheus.exporter + +import dev.cubxity.plugins.metrics.api.metric.data.CounterMetric +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.HistogramMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.api.util.fastForEach +import dev.cubxity.plugins.metrics.api.util.toGoString +import io.prometheus.client.Collector +import java.io.Closeable + +interface PrometheusExporter : Closeable { + fun initialize() +} + +fun List.toPrometheus(): List { + val map = LinkedHashMap>(size) + + fastForEach { metric -> + map.computeIfAbsent(metric.name) { ArrayList() }.add(metric) + } + + return map.map { (name, metrics) -> + val type: Collector.Type + val samples: MutableList + + when (val metric = metrics.first()) { + is CounterMetric -> { + type = Collector.Type.COUNTER + samples = ArrayList(metrics.size) + } + is GaugeMetric -> { + type = Collector.Type.GAUGE + samples = ArrayList(metrics.size) + } + is HistogramMetric -> { + type = Collector.Type.HISTOGRAM + samples = ArrayList((2 + metric.bucket.size) * metrics.size) + } + } + + metrics.fastForEach { metric -> + val keys = metric.labels.keys.toList() + val values = metric.labels.values.toList() + + when (metric) { + is CounterMetric -> { + samples += Collector.MetricFamilySamples.Sample(metric.name, keys, values, metric.value) + } + is GaugeMetric -> { + samples += Collector.MetricFamilySamples.Sample(metric.name, keys, values, metric.value) + } + is HistogramMetric -> { + val keysWithLe = keys.toMutableList() + keysWithLe += "le" + + val bucketName = "${metric.name}_bucket" + + metric.bucket.fastForEach { bucket -> + val valuesWithLe = values.toMutableList() + valuesWithLe += bucket.upperBound.toGoString() + + samples += Collector.MetricFamilySamples.Sample( + bucketName, + keysWithLe, + valuesWithLe, + bucket.cumulativeCount + ) + } + + samples += + Collector.MetricFamilySamples.Sample("${metric.name}_count", keys, values, metric.sampleCount) + + samples += + Collector.MetricFamilySamples.Sample("${metric.name}_sum", keys, values, metric.sampleSum) + } + } + } + + Collector.MetricFamilySamples(name, type, "", samples) + } +} \ No newline at end of file diff --git a/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/exporter/PrometheusHTTPExporter.kt b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/exporter/PrometheusHTTPExporter.kt new file mode 100644 index 00000000..a2b39bdd --- /dev/null +++ b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/exporter/PrometheusHTTPExporter.kt @@ -0,0 +1,64 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.prometheus.exporter + +import com.sun.net.httpserver.BasicAuthenticator +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.prometheus.PrometheusMetricsDriver +import dev.cubxity.plugins.metrics.prometheus.collector.UnifiedMetricsCollector +import dev.cubxity.plugins.metrics.prometheus.config.AuthenticationScheme +import io.prometheus.client.CollectorRegistry +import io.prometheus.client.exporter.HTTPServer + +class PrometheusHTTPExporter( + private val api: UnifiedMetrics, + private val driver: PrometheusMetricsDriver +) : PrometheusExporter { + private var server: HTTPServer? = null + + override fun initialize() { + val registry = CollectorRegistry() + registry.register(UnifiedMetricsCollector(api)) + + server = HTTPServer.Builder() + .withHostname(driver.config.http.host) + .withPort(driver.config.http.port) + .withRegistry(registry) + .apply { + with(driver.config.http.authentication) { + if (scheme == AuthenticationScheme.Basic) { + withAuthenticator(Authenticator(username, password)) + } + } + } + .build() + } + + override fun close() { + server?.close() + server = null + } + + private class Authenticator( + private val username: String, + private val password: String + ) : BasicAuthenticator("unifiedmetrics") { + override fun checkCredentials(username: String?, password: String?): Boolean = + this.username == username && this.password == password + } +} \ No newline at end of file diff --git a/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/exporter/PushGatewayExporter.kt b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/exporter/PushGatewayExporter.kt new file mode 100644 index 00000000..ae14dcd2 --- /dev/null +++ b/drivers/prometheus/bin/main/dev/cubxity/plugins/metrics/prometheus/exporter/PushGatewayExporter.kt @@ -0,0 +1,78 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.prometheus.exporter + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.prometheus.PrometheusMetricsDriver +import dev.cubxity.plugins.metrics.prometheus.collector.MetricSamplesCollection +import dev.cubxity.plugins.metrics.prometheus.config.AuthenticationScheme +import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory +import io.prometheus.client.exporter.PushGateway +import kotlinx.coroutines.* +import java.net.URL +import kotlin.math.max +import kotlin.system.measureTimeMillis + +class PushGatewayExporter( + private val api: UnifiedMetrics, + private val driver: PrometheusMetricsDriver +) : PrometheusExporter { + private val coroutineScope = CoroutineScope(Dispatchers.IO) + SupervisorJob() + private val groupingKey = mapOf("server" to api.serverName) + + private var gateway: PushGateway? = null + + override fun initialize() { + val config = driver.config.pushGateway + + gateway = PushGateway(URL(config.url)).apply { + with(config.authentication) { + if (scheme == AuthenticationScheme.Basic) { + setConnectionFactory(BasicAuthHttpConnectionFactory(username, password)) + } + } + } + + scheduleTasks() + } + + override fun close() { + coroutineScope.cancel() + gateway = null + } + + private fun scheduleTasks() { + val interval = driver.config.pushGateway.interval * 1000 + + coroutineScope.launch { + while (true) { + val time = measureTimeMillis { + try { + val samples = api.metricsManager.collect() + val collection = MetricSamplesCollection(samples.toPrometheus()) + + gateway?.push(collection, driver.config.pushGateway.job, groupingKey) + } catch (error: Throwable) { + api.logger.severe("An error occurred whilst writing samples to Prometheus", error) + } + } + delay(max(0, interval - time)) + } + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c..2c352119 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3fa8f862..e0fd0202 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 6689b85b..9b42019c 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/Dispatchers.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/Dispatchers.kt new file mode 100644 index 00000000..5cbea24a --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/Dispatchers.kt @@ -0,0 +1,62 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit + +import io.papermc.paper.threadedregions.RegionizedServer +import kotlinx.coroutines.* +import org.bukkit.Bukkit +import org.bukkit.plugin.java.JavaPlugin +import kotlin.coroutines.CoroutineContext + +@OptIn(InternalCoroutinesApi::class) +class BukkitDispatcher(private val plugin: JavaPlugin) : CoroutineDispatcher(), Delay { + @OptIn(ExperimentalCoroutinesApi::class) + override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { + val task = plugin.server.scheduler.runTaskLater( + plugin, + Runnable { + continuation.apply { resumeUndispatched(Unit) } + }, + timeMillis / 50 + ) + continuation.invokeOnCancellation { task.cancel() } + } + + override fun dispatch(context: CoroutineContext, block: Runnable) { + if (!context.isActive) { + return + } + + if (Bukkit.isPrimaryThread()) { + block.run() + } else { + plugin.server.scheduler.runTask(plugin, block) + } + } +} + +@OptIn(InternalCoroutinesApi::class) +class FoliaDispatcher : CoroutineDispatcher(), Delay { + override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { + TODO("Not yet implemented") + } + + override fun dispatch(context: CoroutineContext, block: Runnable) { + RegionizedServer.getInstance().addTask(block) + } +} diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/UnifiedMetricsBukkitPlugin.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/UnifiedMetricsBukkitPlugin.kt new file mode 100644 index 00000000..141f0dbf --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/UnifiedMetricsBukkitPlugin.kt @@ -0,0 +1,62 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap +import dev.cubxity.plugins.metrics.bukkit.metric.events.EventsCollection +import dev.cubxity.plugins.metrics.bukkit.metric.regionized.FoliaRegionCollection +import dev.cubxity.plugins.metrics.bukkit.metric.server.ServerCollection +import dev.cubxity.plugins.metrics.bukkit.metric.tick.TickCollection +import dev.cubxity.plugins.metrics.bukkit.metric.world.WorldCollection +import dev.cubxity.plugins.metrics.bukkit.util.BukkitPlatform +import dev.cubxity.plugins.metrics.core.plugin.CoreUnifiedMetricsPlugin +import org.bukkit.plugin.ServicePriority +import java.util.concurrent.Executors + +class UnifiedMetricsBukkitPlugin( + override val bootstrap: UnifiedMetricsBukkitBootstrap +) : CoreUnifiedMetricsPlugin() { + private val executor = Executors.newScheduledThreadPool(1) + + override fun disable() { + executor.shutdownNow() + super.disable() + } + + override fun registerPlatformService(api: UnifiedMetrics) { + bootstrap.server.servicesManager.register(UnifiedMetrics::class.java, api, bootstrap, ServicePriority.Normal) + } + + override fun registerPlatformMetrics() { + super.registerPlatformMetrics() + + apiProvider.metricsManager.apply { + with(config.metrics.collectors) { + if (server) registerCollection(ServerCollection(bootstrap)) + if (world) registerCollection(WorldCollection(bootstrap)) + if (tick) registerCollection(TickCollection(bootstrap)) + if (events) registerCollection(EventsCollection(bootstrap)) + + if (regionizedServer && BukkitPlatform.current == BukkitPlatform.Folia) { + registerCollection(FoliaRegionCollection()) + } + } + } + } +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/bootstrap/UnifiedMetricsBukkitBootstrap.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/bootstrap/UnifiedMetricsBukkitBootstrap.kt new file mode 100644 index 00000000..a7fe9c43 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/bootstrap/UnifiedMetricsBukkitBootstrap.kt @@ -0,0 +1,65 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.bootstrap + +import dev.cubxity.plugins.metrics.api.platform.PlatformType +import dev.cubxity.plugins.metrics.bukkit.BukkitDispatcher +import dev.cubxity.plugins.metrics.bukkit.FoliaDispatcher +import dev.cubxity.plugins.metrics.bukkit.UnifiedMetricsBukkitPlugin +import dev.cubxity.plugins.metrics.bukkit.util.BukkitPlatform +import dev.cubxity.plugins.metrics.common.UnifiedMetricsBootstrap +import dev.cubxity.plugins.metrics.common.plugin.logger.JavaLogger +import kotlinx.coroutines.CoroutineDispatcher +import org.bukkit.plugin.java.JavaPlugin +import java.nio.file.Path + +@Suppress("MemberVisibilityCanBePrivate") +class UnifiedMetricsBukkitBootstrap : JavaPlugin(), UnifiedMetricsBootstrap { + private val plugin = UnifiedMetricsBukkitPlugin(this) + + override val type: PlatformType + get() = PlatformType.Bukkit + + override val version: String + get() = description.version + + override val serverBrand: String + get() = server.name + + override val dataDirectory: Path + get() = dataFolder.toPath() + + override val configDirectory: Path + get() = dataFolder.toPath() + + override val logger = JavaLogger(getLogger()) + + override val dispatcher: CoroutineDispatcher = when (BukkitPlatform.current) { + BukkitPlatform.Folia -> FoliaDispatcher() + else -> BukkitDispatcher(this) + } + + override fun onEnable() { + (this as UnifiedMetricsBootstrap).logger.info("Running on Bukkit platform ${BukkitPlatform.current}") + plugin.enable() + } + + override fun onDisable() { + plugin.disable() + } +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/events/EventsCollection.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/events/EventsCollection.kt new file mode 100644 index 00000000..fd4e30b0 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/events/EventsCollection.kt @@ -0,0 +1,83 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +@file:Suppress("DEPRECATION") // Using Paper's event will cause incompatibility with non-paper bukkit/spigot servers + +package dev.cubxity.plugins.metrics.bukkit.metric.events + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.Counter +import dev.cubxity.plugins.metrics.api.metric.store.VolatileDoubleStore +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap +import dev.cubxity.plugins.metrics.common.metric.Metrics +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.player.AsyncPlayerChatEvent +import org.bukkit.event.player.AsyncPlayerPreLoginEvent +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerQuitEvent +import org.bukkit.event.server.ServerListPingEvent + +@Suppress("UNUSED_PARAMETER") +class EventsCollection(private val bootstrap: UnifiedMetricsBukkitBootstrap) : CollectorCollection, Listener { + private val loginCounter = Counter(Metrics.Events.Login) + private val joinCounter = Counter(Metrics.Events.Join, valueStoreFactory = VolatileDoubleStore) + private val quitCounter = Counter(Metrics.Events.Quit, valueStoreFactory = VolatileDoubleStore) + private val chatCounter = Counter(Metrics.Events.Chat) + private val pingCounter = Counter(Metrics.Events.Ping) // TODO: is this async? + + override val collectors: List = + listOf(loginCounter, joinCounter, quitCounter, chatCounter, pingCounter) + + override val isAsync: Boolean + get() = true + + override fun initialize() { + bootstrap.server.pluginManager.registerEvents(this, bootstrap) + } + + override fun dispose() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onLogin(event: AsyncPlayerPreLoginEvent) { + loginCounter.inc() + } + + @EventHandler + fun onJoin(event: PlayerJoinEvent) { + joinCounter.inc() + } + + @EventHandler + fun onQuit(event: PlayerQuitEvent) { + quitCounter.inc() + } + + @EventHandler + fun onChat(event: AsyncPlayerChatEvent) { + chatCounter.inc() + } + + @EventHandler + fun onPing(event: ServerListPingEvent) { + pingCounter.inc() + } +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/regionized/FoliaRegionCollection.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/regionized/FoliaRegionCollection.kt new file mode 100644 index 00000000..54ad8b72 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/regionized/FoliaRegionCollection.kt @@ -0,0 +1,27 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.regionized + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection + +class FoliaRegionCollection : CollectorCollection { + override val collectors: List = listOf(FoliaRegionCollector()) + override val isAsync: Boolean + get() = true +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/regionized/FoliaRegionCollector.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/regionized/FoliaRegionCollector.kt new file mode 100644 index 00000000..93a97c97 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/regionized/FoliaRegionCollector.kt @@ -0,0 +1,56 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.regionized + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.data.CounterMetric +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.bukkit.util.regioniser +import dev.cubxity.plugins.metrics.common.metric.Metrics +import io.papermc.paper.threadedregions.ThreadedRegionizer.ThreadedRegion +import io.papermc.paper.threadedregions.TickRegions.TickRegionData +import io.papermc.paper.threadedregions.TickRegions.TickRegionSectionData +import org.bukkit.Bukkit + + +class FoliaRegionCollector : Collector { + override fun collect(): List { + val worlds = Bukkit.getWorlds() + + val regions = ArrayList>() + for (world in worlds) { + world.regioniser.computeForAllRegions(regions::add) + } + + val samples = ArrayList(regions.size * 4 + 1) + for (region in regions) { + val tags = mapOf("world" to region.data.world.serverLevelData.levelName, "region" to "${region.id}") + samples.add(CounterMetric(Metrics.RegionizedServer.RegionTick, tags, region.data.currentTick)) + + val stats = region.data.regionStats + samples.add(GaugeMetric(Metrics.RegionizedServer.RegionEntitiesCount, tags, stats.entityCount)) + samples.add(GaugeMetric(Metrics.RegionizedServer.RegionPlayersCount, tags, stats.playerCount)) + samples.add(GaugeMetric(Metrics.RegionizedServer.RegionChunksCount, tags, stats.chunkCount)) + } + + samples.add(GaugeMetric(Metrics.RegionizedServer.RegionCount, value = regions.size)) + + return samples + } +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/server/ServerCollection.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/server/ServerCollection.kt new file mode 100644 index 00000000..8f58d85c --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/server/ServerCollection.kt @@ -0,0 +1,26 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.server + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap + +class ServerCollection(bootstrap: UnifiedMetricsBukkitBootstrap) : CollectorCollection { + override val collectors: List = listOf(ServerCollector(bootstrap)) +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/server/ServerCollector.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/server/ServerCollector.kt new file mode 100644 index 00000000..7f6068c6 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/server/ServerCollector.kt @@ -0,0 +1,35 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.server + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap +import dev.cubxity.plugins.metrics.common.metric.Metrics + +class ServerCollector(private val bootstrap: UnifiedMetricsBukkitBootstrap) : Collector { + override fun collect(): List { + val server = bootstrap.server + return listOf( + GaugeMetric(Metrics.Server.Plugins, value = server.pluginManager.plugins.size), + GaugeMetric(Metrics.Server.PlayersCount, value = server.onlinePlayers.size), + GaugeMetric(Metrics.Server.PlayersMax, value = server.maxPlayers) + ) + } +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/BukkitTickReporter.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/BukkitTickReporter.kt new file mode 100644 index 00000000..32cf49ea --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/BukkitTickReporter.kt @@ -0,0 +1,43 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.tick + +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap + +class BukkitTickReporter( + private val metric: TickCollection, + private val bootstrap: UnifiedMetricsBukkitBootstrap +) : TickReporter, Runnable { + private var taskId: Int? = null + + override fun initialize() { + taskId = bootstrap.server.scheduler.runTaskTimer(bootstrap, this, 1, 1).taskId + } + + override fun dispose() { + val taskId = taskId + if (taskId !== null) { + bootstrap.server.scheduler.cancelTask(taskId) + this.taskId = null + } + } + + override fun run() { + metric.onTick(0.0) + } +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/PaperTickReporter.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/PaperTickReporter.kt new file mode 100644 index 00000000..1410c6b5 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/PaperTickReporter.kt @@ -0,0 +1,43 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.tick + +import com.destroystokyo.paper.event.server.ServerTickEndEvent +import dev.cubxity.plugins.metrics.api.metric.collector.MILLISECONDS_PER_SECOND +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener + +class PaperTickReporter( + private val metric: TickCollection, + private val bootstrap: UnifiedMetricsBukkitBootstrap +) : TickReporter, Listener { + override fun initialize() { + bootstrap.server.pluginManager.registerEvents(this, bootstrap) + } + + override fun dispose() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onTick(event: ServerTickEndEvent) { + metric.onTick(event.tickDuration / MILLISECONDS_PER_SECOND) + } +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/TickCollection.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/TickCollection.kt new file mode 100644 index 00000000..93759d47 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/TickCollection.kt @@ -0,0 +1,61 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.tick + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.Histogram +import dev.cubxity.plugins.metrics.api.metric.store.VolatileDoubleStore +import dev.cubxity.plugins.metrics.api.metric.store.VolatileLongStore +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap +import dev.cubxity.plugins.metrics.bukkit.util.BukkitPlatform +import dev.cubxity.plugins.metrics.common.metric.Metrics + +class TickCollection(bootstrap: UnifiedMetricsBukkitBootstrap) : CollectorCollection { + private val reporter = when (BukkitPlatform.current) { + BukkitPlatform.Folia, BukkitPlatform.Paper -> PaperTickReporter(this, bootstrap) + else -> BukkitTickReporter(this, bootstrap) + } + + // The callback is called from a single thread + private val tickDuration = Histogram( + Metrics.Server.TickDurationSeconds, + sumStoreFactory = VolatileDoubleStore, + countStoreFactory = VolatileLongStore + ) + + override val collectors: List = listOf(tickDuration) + + override val isAsync: Boolean + get() = true + + override fun initialize() { + reporter.initialize() + } + + override fun dispose() { + reporter.dispose() + } + + /** + * @param duration tick duration in seconds + */ + fun onTick(duration: Double) { + tickDuration += duration + } +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/TickReporter.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/TickReporter.kt new file mode 100644 index 00000000..11a603e9 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/tick/TickReporter.kt @@ -0,0 +1,24 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.tick + +interface TickReporter { + fun initialize() + + fun dispose() +} \ No newline at end of file diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/world/BukkitWorldCollector.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/world/BukkitWorldCollector.kt new file mode 100644 index 00000000..878bf108 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/world/BukkitWorldCollector.kt @@ -0,0 +1,41 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.world + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.api.util.fastForEach +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap +import dev.cubxity.plugins.metrics.common.metric.Metrics + +class BukkitWorldCollector(private val bootstrap: UnifiedMetricsBukkitBootstrap) : Collector { + override fun collect(): List { + val worlds = bootstrap.server.worlds + val samples = ArrayList(worlds.size * 3) + + worlds.fastForEach { world -> + val tags = mapOf("world" to world.name) + samples.add(GaugeMetric(Metrics.Server.WorldEntitiesCount, tags, world.entities.size)) + samples.add(GaugeMetric(Metrics.Server.WorldPlayersCount, tags, world.players.size)) + samples.add(GaugeMetric(Metrics.Server.WorldLoadedChunks, tags, world.loadedChunks.size)) + } + + return samples + } +} diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/world/PaperWorldCollector.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/world/PaperWorldCollector.kt new file mode 100644 index 00000000..156fa43b --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/world/PaperWorldCollector.kt @@ -0,0 +1,41 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.world + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.api.util.fastForEach +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap +import dev.cubxity.plugins.metrics.common.metric.Metrics + +class PaperWorldCollector(private val bootstrap: UnifiedMetricsBukkitBootstrap) : Collector { + override fun collect(): List { + val worlds = bootstrap.server.worlds + val samples = ArrayList(worlds.size * 3) + + worlds.fastForEach { world -> + val tags = mapOf("world" to world.name) + samples.add(GaugeMetric(Metrics.Server.WorldEntitiesCount, tags, world.entityCount)) + samples.add(GaugeMetric(Metrics.Server.WorldPlayersCount, tags, world.playerCount)) + samples.add(GaugeMetric(Metrics.Server.WorldLoadedChunks, tags, world.chunkCount)) + } + + return samples + } +} diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/world/WorldCollection.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/world/WorldCollection.kt new file mode 100644 index 00000000..1cd9864f --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/metric/world/WorldCollection.kt @@ -0,0 +1,33 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.metric.world + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap +import dev.cubxity.plugins.metrics.bukkit.util.declaredMethodExists + +class WorldCollection(bootstrap: UnifiedMetricsBukkitBootstrap) : CollectorCollection { + private val collector = if (declaredMethodExists("org.bukkit.World", "getEntityCount")) { + PaperWorldCollector(bootstrap) + } else { + BukkitWorldCollector(bootstrap) + } + + override val collectors: List = listOf(collector) +} diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/util/BukkitPlatform.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/util/BukkitPlatform.kt new file mode 100644 index 00000000..aec8dd4d --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/util/BukkitPlatform.kt @@ -0,0 +1,32 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.util + +enum class BukkitPlatform { + Bukkit, + Paper, + Folia; + + companion object { + val current = when { + classExists("io.papermc.paper.threadedregions.RegionizedServer") -> Folia + classExists("com.destroystokyo.paper.event.server.ServerTickStartEvent") -> Paper + else -> Bukkit + } + } +} diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/util/ClassUtils.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/util/ClassUtils.kt new file mode 100644 index 00000000..907a4812 --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/util/ClassUtils.kt @@ -0,0 +1,32 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.util + +fun classExists(className: String): Boolean = try { + Class.forName(className) + true +} catch (e: ClassNotFoundException) { + false +} + +fun declaredMethodExists(className: String, methodName: String, vararg parameterTypes: Class<*>): Boolean = try { + Class.forName(className).getDeclaredMethod(methodName, *parameterTypes) + true +} catch (e: ReflectiveOperationException) { + false +} diff --git a/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/util/FoliaExt.kt b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/util/FoliaExt.kt new file mode 100644 index 00000000..05ffbd8b --- /dev/null +++ b/platforms/bukkit/bin/main/dev/cubxity/plugins/metrics/bukkit/util/FoliaExt.kt @@ -0,0 +1,27 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bukkit.util + +import io.papermc.paper.threadedregions.ThreadedRegionizer +import io.papermc.paper.threadedregions.TickRegions.TickRegionData +import io.papermc.paper.threadedregions.TickRegions.TickRegionSectionData +import org.bukkit.World +import org.bukkit.craftbukkit.CraftWorld; + +val World.regioniser: ThreadedRegionizer + get() = (this as CraftWorld).handle.regioniser diff --git a/platforms/bukkit/bin/main/plugin.yml b/platforms/bukkit/bin/main/plugin.yml new file mode 100644 index 00000000..bf6c80fd --- /dev/null +++ b/platforms/bukkit/bin/main/plugin.yml @@ -0,0 +1,8 @@ +name: UnifiedMetrics +main: dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap +description: "Fully-featured metrics plugin for Minecraft servers" +api-version: 1.21 +folia-supported: true +author: Cubxity +version: ${version} +load: STARTUP \ No newline at end of file diff --git a/platforms/bukkit/build.gradle.kts b/platforms/bukkit/build.gradle.kts index 75bed03a..1b64ceb4 100644 --- a/platforms/bukkit/build.gradle.kts +++ b/platforms/bukkit/build.gradle.kts @@ -17,7 +17,7 @@ plugins { id("com.github.johnrengelman.shadow") - id("io.papermc.paperweight.userdev") version "1.5.3" + id("io.papermc.paperweight.userdev") version "2.0.0-beta.8" } repositories { @@ -27,14 +27,11 @@ repositories { dependencies { api(project(":unifiedmetrics-core")) -// compileOnly("com.destroystokyo.paper", "paper-api", "1.16.5-R0.1-SNAPSHOT") - paperweight.devBundle("dev.folia", "1.20.1-R0.1-SNAPSHOT") + // compileOnly("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT") + paperweight.devBundle("dev.folia", "1.21.4-R0.1-SNAPSHOT") } tasks { - assemble { - dependsOn(reobfJar) - } shadowJar { archiveClassifier.set("") relocate("retrofit2", "dev.cubxity.plugins.metrics.libs.retrofit2") @@ -43,6 +40,10 @@ tasks { relocate("okhttp", "dev.cubxity.plugins.metrics.libs.okhttp") relocate("okio", "dev.cubxity.plugins.metrics.libs.okio") relocate("io.prometheus", "dev.cubxity.plugins.metrics.libs.io.prometheus") + + manifest { + attributes(mapOf("paperweight-mappings-namespace" to "mojang")) + } } processResources { filesMatching("plugin.yml") { diff --git a/platforms/bukkit/src/main/kotlin/dev/cubxity/plugins/metrics/bukkit/util/FoliaExt.kt b/platforms/bukkit/src/main/kotlin/dev/cubxity/plugins/metrics/bukkit/util/FoliaExt.kt index 85f98282..05ffbd8b 100644 --- a/platforms/bukkit/src/main/kotlin/dev/cubxity/plugins/metrics/bukkit/util/FoliaExt.kt +++ b/platforms/bukkit/src/main/kotlin/dev/cubxity/plugins/metrics/bukkit/util/FoliaExt.kt @@ -21,7 +21,7 @@ import io.papermc.paper.threadedregions.ThreadedRegionizer import io.papermc.paper.threadedregions.TickRegions.TickRegionData import io.papermc.paper.threadedregions.TickRegions.TickRegionSectionData import org.bukkit.World -import org.bukkit.craftbukkit.v1_20_R1.CraftWorld +import org.bukkit.craftbukkit.CraftWorld; val World.regioniser: ThreadedRegionizer get() = (this as CraftWorld).handle.regioniser diff --git a/platforms/bukkit/src/main/resources/plugin.yml b/platforms/bukkit/src/main/resources/plugin.yml index d336f5cb..bf6c80fd 100644 --- a/platforms/bukkit/src/main/resources/plugin.yml +++ b/platforms/bukkit/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: UnifiedMetrics main: dev.cubxity.plugins.metrics.bukkit.bootstrap.UnifiedMetricsBukkitBootstrap description: "Fully-featured metrics plugin for Minecraft servers" -api-version: 1.16 +api-version: 1.21 folia-supported: true author: Cubxity version: ${version} diff --git a/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/UnifiedMetricsBungeePlugin.kt b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/UnifiedMetricsBungeePlugin.kt new file mode 100644 index 00000000..56a24511 --- /dev/null +++ b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/UnifiedMetricsBungeePlugin.kt @@ -0,0 +1,43 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bungee + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.bungee.bootstrap.UnifiedMetricsBungeeBootstrap +import dev.cubxity.plugins.metrics.bungee.metric.events.EventsCollection +import dev.cubxity.plugins.metrics.bungee.metric.server.ServerCollection +import dev.cubxity.plugins.metrics.core.plugin.CoreUnifiedMetricsPlugin + +class UnifiedMetricsBungeePlugin( + override val bootstrap: UnifiedMetricsBungeeBootstrap +) : CoreUnifiedMetricsPlugin() { + override fun registerPlatformService(api: UnifiedMetrics) { + // Bungee doesn't have a service manager + } + + override fun registerPlatformMetrics() { + super.registerPlatformMetrics() + + apiProvider.metricsManager.apply { + with(config.metrics.collectors) { + if (server) registerCollection(ServerCollection(bootstrap)) + if (events) registerCollection(EventsCollection(bootstrap)) + } + } + } +} \ No newline at end of file diff --git a/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/bootstrap/UnifiedMetricsBungeeBootstrap.kt b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/bootstrap/UnifiedMetricsBungeeBootstrap.kt new file mode 100644 index 00000000..4f6cb5bf --- /dev/null +++ b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/bootstrap/UnifiedMetricsBungeeBootstrap.kt @@ -0,0 +1,58 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bungee.bootstrap + +import dev.cubxity.plugins.metrics.api.platform.PlatformType +import dev.cubxity.plugins.metrics.bungee.UnifiedMetricsBungeePlugin +import dev.cubxity.plugins.metrics.common.UnifiedMetricsBootstrap +import dev.cubxity.plugins.metrics.common.plugin.dispatcher.CurrentThreadDispatcher +import dev.cubxity.plugins.metrics.common.plugin.logger.JavaLogger +import kotlinx.coroutines.CoroutineDispatcher +import net.md_5.bungee.api.plugin.Plugin +import java.nio.file.Path + +class UnifiedMetricsBungeeBootstrap : Plugin(), UnifiedMetricsBootstrap { + private val plugin = UnifiedMetricsBungeePlugin(this) + + override val type: PlatformType + get() = PlatformType.BungeeCord + + override val version: String + get() = description.version + + override val serverBrand: String + get() = proxy.name + + override val dataDirectory: Path + get() = dataFolder.toPath() + + override val configDirectory: Path + get() = dataFolder.toPath() + + override val logger = JavaLogger(getLogger()) + + override val dispatcher: CoroutineDispatcher = CurrentThreadDispatcher + + override fun onEnable() { + plugin.enable() + } + + override fun onDisable() { + plugin.disable() + } +} \ No newline at end of file diff --git a/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/metric/events/EventsCollection.kt b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/metric/events/EventsCollection.kt new file mode 100644 index 00000000..eeb7e0c1 --- /dev/null +++ b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/metric/events/EventsCollection.kt @@ -0,0 +1,72 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bungee.metric.events + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.Counter +import dev.cubxity.plugins.metrics.api.metric.store.VolatileDoubleStore +import dev.cubxity.plugins.metrics.bungee.bootstrap.UnifiedMetricsBungeeBootstrap +import net.md_5.bungee.api.event.* +import net.md_5.bungee.api.plugin.Listener +import net.md_5.bungee.event.EventHandler + +@Suppress("UNUSED_PARAMETER") +class EventsCollection(private val bootstrap: UnifiedMetricsBungeeBootstrap) : CollectorCollection, Listener { + private val loginCounter = Counter("minecraft_events_login_total") + private val joinCounter = Counter("minecraft_events_join_total", valueStoreFactory = VolatileDoubleStore) + private val quitCounter = Counter("minecraft_events_quit_total", valueStoreFactory = VolatileDoubleStore) + private val chatCounter = Counter("minecraft_events_chat_total", valueStoreFactory = VolatileDoubleStore) + private val pingCounter = Counter("minecraft_events_ping_total") + + override val collectors: List = + listOf(loginCounter, joinCounter, quitCounter, chatCounter, pingCounter) + + override fun initialize() { + bootstrap.proxy.pluginManager.registerListener(bootstrap, this) + } + + override fun dispose() { + bootstrap.proxy.pluginManager.unregisterListener(this) + } + + @EventHandler + fun onLogin(event: PreLoginEvent) { + loginCounter.inc() + } + + @EventHandler + fun onJoin(event: PostLoginEvent) { + joinCounter.inc() + } + + @EventHandler + fun onQuit(event: PlayerDisconnectEvent) { + quitCounter.inc() + } + + @EventHandler + fun onChat(event: ChatEvent) { + chatCounter.inc() + } + + @EventHandler + fun onPing(event: ProxyPingEvent) { + pingCounter.inc() + } +} \ No newline at end of file diff --git a/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/metric/server/ServerCollection.kt b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/metric/server/ServerCollection.kt new file mode 100644 index 00000000..544ecc0d --- /dev/null +++ b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/metric/server/ServerCollection.kt @@ -0,0 +1,26 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bungee.metric.server + +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.bungee.bootstrap.UnifiedMetricsBungeeBootstrap + +class ServerCollection(bootstrap: UnifiedMetricsBungeeBootstrap) : CollectorCollection { + override val collectors: List = listOf(ServerCollector(bootstrap)) +} \ No newline at end of file diff --git a/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/metric/server/ServerCollector.kt b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/metric/server/ServerCollector.kt new file mode 100644 index 00000000..2145a077 --- /dev/null +++ b/platforms/bungee/bin/main/dev/cubxity/plugins/metrics/bungee/metric/server/ServerCollector.kt @@ -0,0 +1,34 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.bungee.metric.server + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.bungee.bootstrap.UnifiedMetricsBungeeBootstrap + +class ServerCollector(private val bootstrap: UnifiedMetricsBungeeBootstrap) : Collector { + override fun collect(): List { + val proxy = bootstrap.proxy + return listOf( + GaugeMetric("minecraft_plugins", value = proxy.pluginManager.plugins.size), + GaugeMetric("minecraft_players_count", value = proxy.onlineCount), + GaugeMetric("minecraft_players_max", value = proxy.config.playerLimit) + ) + } +} \ No newline at end of file diff --git a/platforms/bungee/bin/main/plugin.yml b/platforms/bungee/bin/main/plugin.yml new file mode 100644 index 00000000..be91f537 --- /dev/null +++ b/platforms/bungee/bin/main/plugin.yml @@ -0,0 +1,5 @@ +name: UnifiedMetrics +main: dev.cubxity.plugins.metrics.bungee.bootstrap.UnifiedMetricsBungeeBootstrap +description: "Fully-featured metrics plugin for Minecraft servers" +author: Cubxity +version: ${version} \ No newline at end of file diff --git a/platforms/bungee/build.gradle.kts b/platforms/bungee/build.gradle.kts index 7e336d01..5d22ff8b 100644 --- a/platforms/bungee/build.gradle.kts +++ b/platforms/bungee/build.gradle.kts @@ -21,12 +21,13 @@ plugins { repositories { maven("https://oss.sonatype.org/content/repositories/snapshots/") + maven("https://libraries.minecraft.net") } dependencies { api(project(":unifiedmetrics-core")) - compileOnly("net.md-5", "bungeecord-api", "1.20-R0.1") + compileOnly("net.md-5", "bungeecord-api", "1.21-R0.1-SNAPSHOT") } tasks { diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/UnifiedMetricsFabricPlugin.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/UnifiedMetricsFabricPlugin.kt new file mode 100644 index 00000000..39365f9c --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/UnifiedMetricsFabricPlugin.kt @@ -0,0 +1,49 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.core.plugin.CoreUnifiedMetricsPlugin +import dev.cubxity.plugins.metrics.fabric.bootstrap.UnifiedMetricsFabricBootstrap +import dev.cubxity.plugins.metrics.fabric.metrics.events.EventsCollection +import dev.cubxity.plugins.metrics.fabric.metrics.server.ServerCollection +import dev.cubxity.plugins.metrics.fabric.metrics.tick.TickCollection +import dev.cubxity.plugins.metrics.fabric.metrics.world.WorldCollection +import java.util.concurrent.Executors + +class UnifiedMetricsFabricPlugin( + override val bootstrap: UnifiedMetricsFabricBootstrap +): CoreUnifiedMetricsPlugin() { + + override fun registerPlatformService(api: UnifiedMetrics) { + + } + + override fun registerPlatformMetrics() { + super.registerPlatformMetrics() + + apiProvider.metricsManager.apply { + with(config.metrics.collectors) { + if (server) registerCollection(ServerCollection(bootstrap)) + if (world) registerCollection(WorldCollection(bootstrap)) + if (tick) registerCollection(TickCollection()) + if (events) registerCollection(EventsCollection()) + } + } + } +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/bootstrap/UnifiedMetricsFabricBootstrap.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/bootstrap/UnifiedMetricsFabricBootstrap.kt new file mode 100644 index 00000000..a102dd92 --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/bootstrap/UnifiedMetricsFabricBootstrap.kt @@ -0,0 +1,66 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.bootstrap + +import dev.cubxity.plugins.metrics.api.platform.PlatformType +import dev.cubxity.plugins.metrics.common.UnifiedMetricsBootstrap +import dev.cubxity.plugins.metrics.common.plugin.dispatcher.CurrentThreadDispatcher +import dev.cubxity.plugins.metrics.fabric.UnifiedMetricsFabricPlugin +import dev.cubxity.plugins.metrics.fabric.logger.Log4jLogger +import kotlinx.coroutines.CoroutineDispatcher +import net.fabricmc.api.DedicatedServerModInitializer +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents +import net.fabricmc.loader.api.FabricLoader +import net.minecraft.server.MinecraftServer +import org.apache.logging.log4j.LogManager +import java.nio.file.Path +import kotlin.jvm.optionals.getOrNull + +class UnifiedMetricsFabricBootstrap : DedicatedServerModInitializer, UnifiedMetricsBootstrap { + private val plugin = UnifiedMetricsFabricPlugin(this) + lateinit var server: MinecraftServer + + override val type: PlatformType + get() = PlatformType.Fabric + + override val version: String = FabricLoader.getInstance() + .getModContainer("unifiedmetrics").getOrNull() + ?.metadata?.version?.friendlyString ?: "" + + override val serverBrand: String + get() = server.serverModName + + override val dataDirectory: Path = FabricLoader.getInstance().configDir.resolve("unifiedmetrics") + + override val configDirectory: Path = FabricLoader.getInstance().configDir.resolve("unifiedmetrics") + + override val logger = Log4jLogger(LogManager.getLogger("UnifiedMetrics")) + + override val dispatcher: CoroutineDispatcher = CurrentThreadDispatcher + + override fun onInitializeServer() { + ServerLifecycleEvents.SERVER_STARTED.register { + server = it + plugin.enable() + } + + ServerLifecycleEvents.SERVER_STOPPING.register { + plugin.disable() + } + } +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/events/ChatEvent.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/events/ChatEvent.kt new file mode 100644 index 00000000..ac4d7c83 --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/events/ChatEvent.kt @@ -0,0 +1,35 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.events + +import net.fabricmc.fabric.api.event.Event +import net.fabricmc.fabric.api.event.EventFactory + +fun interface ChatEvent { + + fun onChat() + + companion object { + val event: Event = EventFactory.createArrayBacked(ChatEvent::class.java) { events -> + ChatEvent { + events.forEach(ChatEvent::onChat) + } + } + } + +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/events/PingEvent.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/events/PingEvent.kt new file mode 100644 index 00000000..dad3b23d --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/events/PingEvent.kt @@ -0,0 +1,35 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.events + +import net.fabricmc.fabric.api.event.Event +import net.fabricmc.fabric.api.event.EventFactory + +fun interface PingEvent { + + fun onPing() + + companion object { + val event: Event = EventFactory.createArrayBacked(PingEvent::class.java) { events -> + PingEvent { + events.forEach(PingEvent::onPing) + } + } + } + +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/events/TickEvent.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/events/TickEvent.kt new file mode 100644 index 00000000..26ad7a03 --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/events/TickEvent.kt @@ -0,0 +1,37 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.events + +import net.fabricmc.fabric.api.event.Event +import net.fabricmc.fabric.api.event.EventFactory + +fun interface TickEvent { + + fun onTick(tickTime: Double) + + companion object { + + val event: Event = EventFactory.createArrayBacked(TickEvent::class.java) { events -> + TickEvent { tickTime -> + events.forEach { it.onTick(tickTime) } + } + } + + } + +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/logger/Log4jLogger.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/logger/Log4jLogger.kt new file mode 100644 index 00000000..d15fb9d6 --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/logger/Log4jLogger.kt @@ -0,0 +1,43 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.logger + +import dev.cubxity.plugins.metrics.api.logging.Logger +import org.apache.logging.log4j.Level + +class Log4jLogger(private val logger: org.apache.logging.log4j.Logger): Logger { + override fun info(message: String) { + logger.log(Level.INFO, message) + } + + override fun warn(message: String) { + logger.log(Level.WARN, message) + } + + override fun warn(message: String, error: Throwable) { + logger.log(Level.WARN, message, error) + } + + override fun severe(message: String) { + logger.log(Level.ERROR, message) + } + + override fun severe(message: String, error: Throwable) { + logger.log(Level.ERROR, message, error) + } +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/events/EventsCollection.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/events/EventsCollection.kt new file mode 100644 index 00000000..7c7c7ef7 --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/events/EventsCollection.kt @@ -0,0 +1,57 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.metrics.events + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.Counter +import dev.cubxity.plugins.metrics.api.metric.store.VolatileDoubleStore +import dev.cubxity.plugins.metrics.common.metric.Metrics +import dev.cubxity.plugins.metrics.fabric.events.ChatEvent +import dev.cubxity.plugins.metrics.fabric.events.PingEvent +import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents + +class EventsCollection : CollectorCollection { + private val loginCounter = Counter(Metrics.Events.Login) + private val joinCounter = Counter(Metrics.Events.Join) + private val quitCounter = Counter(Metrics.Events.Quit) + private val chatCounter = Counter(Metrics.Events.Chat, valueStoreFactory = VolatileDoubleStore) + private val pingCounter = Counter(Metrics.Events.Ping) + + override val collectors: List = + listOf(loginCounter, joinCounter, quitCounter, chatCounter, pingCounter) + + override fun initialize() { + ServerLoginConnectionEvents.INIT.register { _, _ -> + loginCounter.inc() + } + ServerPlayConnectionEvents.JOIN.register { _, _, _ -> + joinCounter.inc() + } + ServerPlayConnectionEvents.DISCONNECT.register { _, _ -> + quitCounter.inc() + } + ChatEvent.event.register { + chatCounter.inc() + } + PingEvent.event.register { + pingCounter.inc() + } + } +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/server/ServerCollection.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/server/ServerCollection.kt new file mode 100644 index 00000000..13257673 --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/server/ServerCollection.kt @@ -0,0 +1,26 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.metrics.server + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.fabric.bootstrap.UnifiedMetricsFabricBootstrap + +class ServerCollection(bootstrap: UnifiedMetricsFabricBootstrap) : CollectorCollection { + override val collectors: List = listOf(ServerCollector(bootstrap)) +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/server/ServerCollector.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/server/ServerCollector.kt new file mode 100644 index 00000000..84aa3a6c --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/server/ServerCollector.kt @@ -0,0 +1,36 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.metrics.server + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.common.metric.Metrics +import dev.cubxity.plugins.metrics.fabric.bootstrap.UnifiedMetricsFabricBootstrap +import net.fabricmc.loader.api.FabricLoader + +class ServerCollector(private val bootstrap: UnifiedMetricsFabricBootstrap): Collector { + override fun collect(): List { + val server = bootstrap.server + return listOf( + GaugeMetric(Metrics.Server.Plugins, value = FabricLoader.getInstance().allMods.size), + GaugeMetric(Metrics.Server.PlayersCount, value = server.currentPlayerCount), + GaugeMetric(Metrics.Server.PlayersMax, value = server.maxPlayerCount) + ) + } +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/tick/TickCollection.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/tick/TickCollection.kt new file mode 100644 index 00000000..af85d389 --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/tick/TickCollection.kt @@ -0,0 +1,48 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.metrics.tick + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.Histogram +import dev.cubxity.plugins.metrics.api.metric.collector.MILLISECONDS_PER_SECOND +import dev.cubxity.plugins.metrics.api.metric.store.VolatileDoubleStore +import dev.cubxity.plugins.metrics.api.metric.store.VolatileLongStore +import dev.cubxity.plugins.metrics.common.metric.Metrics +import dev.cubxity.plugins.metrics.fabric.events.TickEvent + +class TickCollection : CollectorCollection { + private val tickDuration = Histogram( + Metrics.Server.TickDurationSeconds, + sumStoreFactory = VolatileDoubleStore, + countStoreFactory = VolatileLongStore + ) + + override val collectors: List = listOf(tickDuration) + + + override fun initialize() { + TickEvent.event.register { duration -> + onTick(duration / MILLISECONDS_PER_SECOND) + } + } + + fun onTick(duration: Double) { + tickDuration += duration + } +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/world/WorldCollection.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/world/WorldCollection.kt new file mode 100644 index 00000000..88ebde12 --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/world/WorldCollection.kt @@ -0,0 +1,26 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.metrics.world + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.fabric.bootstrap.UnifiedMetricsFabricBootstrap + +class WorldCollection(bootstrap: UnifiedMetricsFabricBootstrap) : CollectorCollection { + override val collectors: List = listOf(WorldCollector(bootstrap)) +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/world/WorldCollector.kt b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/world/WorldCollector.kt new file mode 100644 index 00000000..31af9020 --- /dev/null +++ b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/metrics/world/WorldCollector.kt @@ -0,0 +1,40 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.metrics.world + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.common.metric.Metrics +import dev.cubxity.plugins.metrics.fabric.bootstrap.UnifiedMetricsFabricBootstrap + +class WorldCollector(private val bootstrap: UnifiedMetricsFabricBootstrap) : Collector { + override fun collect(): List { + val worlds = bootstrap.server.worlds + val samples = ArrayList(worlds.count() * 3) + + worlds.forEach { world -> + val tags = mapOf("world" to world.registryKey.value.toString()) + samples.add(GaugeMetric(Metrics.Server.WorldEntitiesCount, tags, world.iterateEntities().count())) + samples.add(GaugeMetric(Metrics.Server.WorldPlayersCount, tags, world.players.size)) + samples.add(GaugeMetric(Metrics.Server.WorldLoadedChunks, tags, world.chunkManager.loadedChunkCount)) + } + + return samples + } +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/mixins/MinecraftServerMixin.class b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/mixins/MinecraftServerMixin.class new file mode 100644 index 00000000..3d403889 Binary files /dev/null and b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/mixins/MinecraftServerMixin.class differ diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/mixins/ServerPlayNetworkHandlerMixin.class b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/mixins/ServerPlayNetworkHandlerMixin.class new file mode 100644 index 00000000..043763a2 Binary files /dev/null and b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/mixins/ServerPlayNetworkHandlerMixin.class differ diff --git a/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/mixins/ServerQueryNetworkHandlerMixin.class b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/mixins/ServerQueryNetworkHandlerMixin.class new file mode 100644 index 00000000..043e45ad Binary files /dev/null and b/platforms/fabric/bin/main/dev/cubxity/plugins/metrics/fabric/mixins/ServerQueryNetworkHandlerMixin.class differ diff --git a/platforms/fabric/bin/main/fabric.mod.json b/platforms/fabric/bin/main/fabric.mod.json new file mode 100644 index 00000000..3637b6eb --- /dev/null +++ b/platforms/fabric/bin/main/fabric.mod.json @@ -0,0 +1,35 @@ +{ + "schemaVersion": 1, + "id": "unifiedmetrics", + "version": "${version}", + + "name": "UnifiedMetrics", + "description": "Fully-featured metrics plugin for Minecraft servers", + "authors": [ + "Cubxity" + ], + "contact": { + "homepage": "https://github.com/Cubxity/UnifiedMetrics/", + "sources": "https://github.com/Cubxity/UnifiedMetrics/", + "issues": "https://github.com/Cubxity/UnifiedMetrics/issues" + }, + + "license": "LGPL-3.0", + + "environment": "server", + "entrypoints": { + "server": [ + "dev.cubxity.plugins.metrics.fabric.bootstrap.UnifiedMetricsFabricBootstrap" + ] + }, + "mixins": [ + "unifiedmetrics.mixins.json" + ], + + "depends": { + "fabricloader": ">=0.11.3", + "fabric": "*", + "minecraft": ">=1.16", + "fabric-language-kotlin": ">=1.6.0+kotlin.1.5.0" + } +} \ No newline at end of file diff --git a/platforms/fabric/bin/main/unifiedmetrics.mixins.json b/platforms/fabric/bin/main/unifiedmetrics.mixins.json new file mode 100644 index 00000000..984a76fa --- /dev/null +++ b/platforms/fabric/bin/main/unifiedmetrics.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.cubxity.plugins.metrics.fabric.mixins", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "ServerPlayNetworkHandlerMixin", + "ServerQueryNetworkHandlerMixin", + "MinecraftServerMixin" + ] +} \ No newline at end of file diff --git a/platforms/fabric/build.gradle.kts b/platforms/fabric/build.gradle.kts index 8d2fc2a4..917f91ae 100644 --- a/platforms/fabric/build.gradle.kts +++ b/platforms/fabric/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + /* * This file is part of UnifiedMetrics. * @@ -16,8 +18,7 @@ */ plugins { - id("fabric-loom") version "1.4.1" - id("net.kyori.blossom") + id("fabric-loom") } val transitiveInclude: Configuration by configurations.creating { @@ -28,12 +29,12 @@ val transitiveInclude: Configuration by configurations.creating { dependencies { // https://fabricmc.net/versions.html - minecraft("com.mojang:minecraft:1.17.1") - mappings("net.fabricmc:yarn:1.17.1+build.65:v2") - modImplementation("net.fabricmc:fabric-loader:0.14.23") + minecraft("com.mojang:minecraft:1.21.4") + mappings("net.fabricmc:yarn:1.21.4+build.4") + modImplementation("net.fabricmc:fabric-loader:0.16.9") - modImplementation("net.fabricmc.fabric-api:fabric-api:0.46.1+1.17") - modImplementation("net.fabricmc:fabric-language-kotlin:1.10.10+kotlin.1.9.10") + modImplementation("net.fabricmc.fabric-api:fabric-api:0.113.0+1.21.4") + modImplementation("net.fabricmc:fabric-language-kotlin:1.11.0+kotlin.2.0.0") api(project(":unifiedmetrics-core")) @@ -55,11 +56,12 @@ loom { isIdeConfigGenerated = true } } + serverOnlyMinecraftJar() } tasks { compileKotlin { - kotlinOptions.jvmTarget = "16" + compilerOptions.jvmTarget.set(JvmTarget.JVM_21) } processResources { filesMatching("fabric.mod.json") { @@ -74,11 +76,6 @@ tasks { } java { - sourceCompatibility = JavaVersion.VERSION_16 - targetCompatibility = JavaVersion.VERSION_16 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } - -blossom { - replaceTokenIn("src/main/kotlin/dev/cubxity/plugins/metrics/fabric/bootstrap/UnifiedMetricsFabricBootstrap.kt") - replaceToken("@version@", version) -} \ No newline at end of file diff --git a/platforms/fabric/remappedSrc/dev/cubxity/plugins/metrics/fabric/mixins/MinecraftServerMixin.java b/platforms/fabric/remappedSrc/dev/cubxity/plugins/metrics/fabric/mixins/MinecraftServerMixin.java new file mode 100644 index 00000000..32ca3ef6 --- /dev/null +++ b/platforms/fabric/remappedSrc/dev/cubxity/plugins/metrics/fabric/mixins/MinecraftServerMixin.java @@ -0,0 +1,69 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.mixins; + +import dev.cubxity.plugins.metrics.fabric.events.TickEvent; +import net.minecraft.server.MinecraftServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import static dev.cubxity.plugins.metrics.api.metric.collector.CollectorKt.NANOSECONDS_PER_MILLISECOND; +import static dev.cubxity.plugins.metrics.api.metric.collector.CollectorKt.NANOSECONDS_PER_SECOND; + +/** + * Designed to emulate paper's tick event as closely as possible + * however some changes had to be made to work with fabric-carpet + */ +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin { + + private long lastTick = 0; + + @Inject( + method = "runServer", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/MinecraftServer;setFavicon(Lnet/minecraft/server/ServerMetadata;)V" + ) + ) + private void onRunServerBeforeLoop(CallbackInfo ci) { + lastTick = System.nanoTime() - ((long) NANOSECONDS_PER_SECOND / 20); + } + + @Inject( + method = "tick", + at = @At("HEAD") + ) + private void onTickStart(CallbackInfo ci) { + lastTick = System.nanoTime(); + } + + @Inject( + method = "tick", + at = @At( + value = "CONSTANT", + args = "stringValue=tallying" + ) + ) + private void onTickEnd(CallbackInfo ci) { + TickEvent.Companion.getEvent().invoker().onTick((double)(System.nanoTime() - lastTick) / NANOSECONDS_PER_MILLISECOND); + } + +} diff --git a/platforms/fabric/remappedSrc/dev/cubxity/plugins/metrics/fabric/mixins/ServerPlayNetworkHandlerMixin.java b/platforms/fabric/remappedSrc/dev/cubxity/plugins/metrics/fabric/mixins/ServerPlayNetworkHandlerMixin.java new file mode 100644 index 00000000..39cfb248 --- /dev/null +++ b/platforms/fabric/remappedSrc/dev/cubxity/plugins/metrics/fabric/mixins/ServerPlayNetworkHandlerMixin.java @@ -0,0 +1,35 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.mixins; + +import dev.cubxity.plugins.metrics.fabric.events.ChatEvent; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerPlayNetworkHandler.class) +public class ServerPlayNetworkHandlerMixin { + + @Inject(method = "handleMessage", at = @At("HEAD")) + private void onHandleMessage(CallbackInfo ci) { + ChatEvent.Companion.getEvent().invoker().onChat(); + } + +} diff --git a/platforms/fabric/remappedSrc/dev/cubxity/plugins/metrics/fabric/mixins/ServerQueryNetworkHandlerMixin.java b/platforms/fabric/remappedSrc/dev/cubxity/plugins/metrics/fabric/mixins/ServerQueryNetworkHandlerMixin.java new file mode 100644 index 00000000..ae99e618 --- /dev/null +++ b/platforms/fabric/remappedSrc/dev/cubxity/plugins/metrics/fabric/mixins/ServerQueryNetworkHandlerMixin.java @@ -0,0 +1,35 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.fabric.mixins; + +import dev.cubxity.plugins.metrics.fabric.events.PingEvent; +import net.minecraft.server.network.ServerQueryNetworkHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerQueryNetworkHandler.class) +public class ServerQueryNetworkHandlerMixin { + + @Inject(method = "onRequest", at = @At("HEAD")) + private void handleOnRequest(CallbackInfo ci) { + PingEvent.Companion.getEvent().invoker().onPing(); + } + +} diff --git a/platforms/minestom/build.gradle.kts b/platforms/minestom/build.gradle.kts index 669620cc..ff1a8524 100644 --- a/platforms/minestom/build.gradle.kts +++ b/platforms/minestom/build.gradle.kts @@ -42,10 +42,10 @@ tasks { relocate("io.prometheus", "dev.cubxity.plugins.metrics.libs.io.prometheus") } compileKotlin { - kotlinOptions.jvmTarget = "17" + kotlinOptions.jvmTarget = "21" } compileTestKotlin { - kotlinOptions.jvmTarget = "17" + kotlinOptions.jvmTarget = "21" } processResources { filesMatching("extension.json") { @@ -57,5 +57,5 @@ tasks { } java { - targetCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_21 } diff --git a/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/UnifiedMetricsVelocityPlugin.kt b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/UnifiedMetricsVelocityPlugin.kt new file mode 100644 index 00000000..19e2fb63 --- /dev/null +++ b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/UnifiedMetricsVelocityPlugin.kt @@ -0,0 +1,43 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.velocity + +import dev.cubxity.plugins.metrics.api.UnifiedMetrics +import dev.cubxity.plugins.metrics.core.plugin.CoreUnifiedMetricsPlugin +import dev.cubxity.plugins.metrics.velocity.bootstrap.UnifiedMetricsVelocityBootstrap +import dev.cubxity.plugins.metrics.velocity.metric.events.EventsCollection +import dev.cubxity.plugins.metrics.velocity.metric.server.ServerCollection + +class UnifiedMetricsVelocityPlugin( + override val bootstrap: UnifiedMetricsVelocityBootstrap +) : CoreUnifiedMetricsPlugin() { + override fun registerPlatformService(api: UnifiedMetrics) { + // Velocity doesn't have a service manager + } + + override fun registerPlatformMetrics() { + super.registerPlatformMetrics() + + apiProvider.metricsManager.apply { + with(config.metrics.collectors) { + if (server) registerCollection(ServerCollection(bootstrap)) + if (events) registerCollection(EventsCollection(bootstrap)) + } + } + } +} \ No newline at end of file diff --git a/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/bootstrap/UnifiedMetricsVelocityBootstrap.kt b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/bootstrap/UnifiedMetricsVelocityBootstrap.kt new file mode 100644 index 00000000..91a3df82 --- /dev/null +++ b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/bootstrap/UnifiedMetricsVelocityBootstrap.kt @@ -0,0 +1,78 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.velocity.bootstrap + +import com.google.inject.Inject +import com.velocitypowered.api.event.PostOrder +import com.velocitypowered.api.event.Subscribe +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent +import com.velocitypowered.api.event.proxy.ProxyShutdownEvent +import com.velocitypowered.api.plugin.Plugin +import com.velocitypowered.api.plugin.PluginDescription +import com.velocitypowered.api.plugin.annotation.DataDirectory +import com.velocitypowered.api.proxy.ProxyServer +import dev.cubxity.plugins.metrics.api.platform.PlatformType +import dev.cubxity.plugins.metrics.common.UnifiedMetricsBootstrap +import dev.cubxity.plugins.metrics.common.plugin.dispatcher.CurrentThreadDispatcher +import dev.cubxity.plugins.metrics.velocity.UnifiedMetricsVelocityPlugin +import dev.cubxity.plugins.metrics.velocity.logger.Slf4jLogger +import kotlinx.coroutines.CoroutineDispatcher +import java.nio.file.Path +import kotlin.jvm.optionals.getOrDefault + +@Plugin( + id = "unifiedmetrics", + name = "UnifiedMetrics", + description = "Fully-featured metrics plugin for Minecraft servers", + authors = ["Cubxity"] +) +class UnifiedMetricsVelocityBootstrap @Inject constructor( + @DataDirectory + override val dataDirectory: Path, + val server: ProxyServer, + pluginLogger: org.slf4j.Logger, + private val description: PluginDescription, +) : UnifiedMetricsBootstrap { + private val plugin = UnifiedMetricsVelocityPlugin(this) + + override val type: PlatformType + get() = PlatformType.Velocity + + override val version: String + get() = description.version.getOrDefault("") + + override val serverBrand: String + get() = server.version.name + + override val configDirectory: Path + get() = dataDirectory + + override val logger = Slf4jLogger(pluginLogger) + + override val dispatcher: CoroutineDispatcher = CurrentThreadDispatcher + + @Subscribe(order = PostOrder.FIRST) + fun onEnable(event: ProxyInitializeEvent) { + plugin.enable() + } + + @Subscribe(order = PostOrder.LAST) + fun onDisable(event: ProxyShutdownEvent) { + plugin.disable() + } +} \ No newline at end of file diff --git a/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/logger/Slf4jLogger.kt b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/logger/Slf4jLogger.kt new file mode 100644 index 00000000..b7bd3f5b --- /dev/null +++ b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/logger/Slf4jLogger.kt @@ -0,0 +1,42 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.velocity.logger + +import dev.cubxity.plugins.metrics.api.logging.Logger + +class Slf4jLogger(private val logger: org.slf4j.Logger) : Logger { + override fun info(message: String) { + logger.info(message) + } + + override fun warn(message: String) { + logger.warn(message) + } + + override fun warn(message: String, error: Throwable) { + logger.warn(message, error) + } + + override fun severe(message: String) { + logger.error(message) + } + + override fun severe(message: String, error: Throwable) { + logger.error(message, error) + } +} \ No newline at end of file diff --git a/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/metric/events/EventsCollection.kt b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/metric/events/EventsCollection.kt new file mode 100644 index 00000000..703272ae --- /dev/null +++ b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/metric/events/EventsCollection.kt @@ -0,0 +1,75 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.velocity.metric.events + +import com.velocitypowered.api.event.Subscribe +import com.velocitypowered.api.event.connection.DisconnectEvent +import com.velocitypowered.api.event.connection.PreLoginEvent +import com.velocitypowered.api.event.player.PlayerChatEvent +import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent +import com.velocitypowered.api.event.proxy.ProxyPingEvent +import dev.cubxity.plugins.metrics.api.metric.collector.Counter +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.velocity.bootstrap.UnifiedMetricsVelocityBootstrap + +@Suppress("UNUSED_PARAMETER") +class EventsCollection(private val bootstrap: UnifiedMetricsVelocityBootstrap) : CollectorCollection { + private val loginCounter = Counter("minecraft_events_login_total") + private val joinCounter = Counter("minecraft_events_join_total") + private val quitCounter = Counter("minecraft_events_quit_total") + private val chatCounter = Counter("minecraft_events_chat_total") + private val pingCounter = Counter("minecraft_events_ping_total") + + override val collectors: List = + listOf(loginCounter, joinCounter, quitCounter, chatCounter, pingCounter) + + override fun initialize() { + bootstrap.server.eventManager.register(bootstrap, this) + } + + override fun dispose() { + bootstrap.server.eventManager.unregisterListener(bootstrap, this) + } + + + @Subscribe + fun onLogin(event: PreLoginEvent) { + loginCounter.inc() + } + + @Subscribe + fun onConnect(event: PlayerChooseInitialServerEvent) { + joinCounter.inc() + } + + @Subscribe + fun onDisconnect(event: DisconnectEvent) { + quitCounter.inc() + } + + @Subscribe + fun onChat(event: PlayerChatEvent) { + chatCounter.inc() + } + + @Subscribe + fun onPing(event: ProxyPingEvent) { + pingCounter.inc() + } +} \ No newline at end of file diff --git a/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/metric/server/ServerCollection.kt b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/metric/server/ServerCollection.kt new file mode 100644 index 00000000..9c8ee54b --- /dev/null +++ b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/metric/server/ServerCollection.kt @@ -0,0 +1,26 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.velocity.metric.server + +import dev.cubxity.plugins.metrics.api.metric.collector.CollectorCollection +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.velocity.bootstrap.UnifiedMetricsVelocityBootstrap + +class ServerCollection(bootstrap: UnifiedMetricsVelocityBootstrap) : CollectorCollection { + override val collectors: List = listOf(ServerCollector(bootstrap)) +} \ No newline at end of file diff --git a/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/metric/server/ServerCollector.kt b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/metric/server/ServerCollector.kt new file mode 100644 index 00000000..cd404a80 --- /dev/null +++ b/platforms/velocity/bin/main/dev/cubxity/plugins/metrics/velocity/metric/server/ServerCollector.kt @@ -0,0 +1,34 @@ +/* + * This file is part of UnifiedMetrics. + * + * UnifiedMetrics is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UnifiedMetrics is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UnifiedMetrics. If not, see . + */ + +package dev.cubxity.plugins.metrics.velocity.metric.server + +import dev.cubxity.plugins.metrics.api.metric.collector.Collector +import dev.cubxity.plugins.metrics.api.metric.data.GaugeMetric +import dev.cubxity.plugins.metrics.api.metric.data.Metric +import dev.cubxity.plugins.metrics.velocity.bootstrap.UnifiedMetricsVelocityBootstrap + +class ServerCollector(private val bootstrap: UnifiedMetricsVelocityBootstrap) : Collector { + override fun collect(): List { + val server = bootstrap.server + return listOf( + GaugeMetric("minecraft_plugins", value = server.pluginManager.plugins.size), + GaugeMetric("minecraft_players_count", value = server.playerCount), + GaugeMetric("minecraft_players_max", value = server.configuration.showMaxPlayers) + ) + } +} \ No newline at end of file diff --git a/platforms/velocity/bin/main/velocity-plugin.json b/platforms/velocity/bin/main/velocity-plugin.json new file mode 100644 index 00000000..2811a9b1 --- /dev/null +++ b/platforms/velocity/bin/main/velocity-plugin.json @@ -0,0 +1,11 @@ +{ + "id": "unifiedmetrics", + "name": "UnifiedMetrics", + "version": "${version}", + "description": "Fully-featured metrics plugin for Minecraft servers", + "authors": [ + "Cubxity" + ], + "dependencies": [], + "main": "dev.cubxity.plugins.metrics.velocity.bootstrap.UnifiedMetricsVelocityBootstrap" +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 3daa9cf9..8d91924d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,7 +26,7 @@ include(modulePrefix + "common") include(modulePrefix + "core") include(modulePrefix + platformPrefix + "bukkit") -include(modulePrefix + platformPrefix + "minestom") +//include(modulePrefix + platformPrefix + "minestom") include(modulePrefix + platformPrefix + "velocity") include(modulePrefix + platformPrefix + "bungee") include(modulePrefix + platformPrefix + "fabric") @@ -40,7 +40,7 @@ project(modulePrefix + "core").projectDir = File(rootDir, "core") val platformsDir = File(rootDir, "platforms") project(modulePrefix + platformPrefix + "bukkit").projectDir = File(platformsDir, "bukkit") -project(modulePrefix + platformPrefix + "minestom").projectDir = File(platformsDir, "minestom") +//project(modulePrefix + platformPrefix + "minestom").projectDir = File(platformsDir, "minestom") project(modulePrefix + platformPrefix + "velocity").projectDir = File(platformsDir, "velocity") project(modulePrefix + platformPrefix + "bungee").projectDir = File(platformsDir, "bungee") project(modulePrefix + platformPrefix + "fabric").projectDir = File(platformsDir, "fabric") @@ -58,3 +58,10 @@ pluginManagement { } } } + + + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0") +} +