diff --git a/docker/grafana/dashboards/mangadex_at_home.json b/docker/grafana/dashboards/mangadex_at_home.json
index 2eb3f07..a0a57cf 100644
--- a/docker/grafana/dashboards/mangadex_at_home.json
+++ b/docker/grafana/dashboards/mangadex_at_home.json
@@ -14,10 +14,10 @@
}
]
},
- "editable": false,
+ "editable": true,
"gnetId": null,
"graphTooltip": 1,
- "iteration": 1612974883967,
+ "iteration": 1617066952719,
"links": [],
"panels": [
{
@@ -115,7 +115,7 @@
},
"gridPos": {
"h": 8,
- "w": 8,
+ "w": 6,
"x": 0,
"y": 1
},
@@ -250,8 +250,8 @@
},
"gridPos": {
"h": 8,
- "w": 8,
- "x": 8,
+ "w": 6,
+ "x": 6,
"y": 1
},
"id": 19,
@@ -310,6 +310,167 @@
"title": "RAM",
"type": "timeseries"
},
+ {
+ "cacheTimeout": null,
+ "datasource": "Prometheus",
+ "description": "Cache size allocated, does not include metadata or other overhead, but does somewhat account for block sizes.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 30,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "graph": false,
+ "legend": false,
+ "tooltip": false
+ },
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Max"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "dark-red",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Used"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "dark-green",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Max"
+ },
+ "properties": [
+ {
+ "id": "custom.fillOpacity",
+ "value": 0
+ },
+ {
+ "id": "custom.lineWidth",
+ "value": 2
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 6,
+ "x": 12,
+ "y": 1
+ },
+ "id": 57,
+ "interval": null,
+ "links": [],
+ "options": {
+ "graph": {},
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "mean",
+ "last"
+ ],
+ "displayMode": "table",
+ "placement": "bottom"
+ },
+ "tooltipOptions": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "7.4.0",
+ "targets": [
+ {
+ "aggregation": "Last",
+ "decimals": 2,
+ "displayAliasType": "Warning / Critical",
+ "displayType": "Regular",
+ "displayValueWithAlias": "Never",
+ "expr": "sum(cache_used_bytes)",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "Used",
+ "refId": "A",
+ "units": "none",
+ "valueHandler": "Number Threshold"
+ },
+ {
+ "aggregation": "Last",
+ "decimals": 2,
+ "displayAliasType": "Warning / Critical",
+ "displayType": "Regular",
+ "displayValueWithAlias": "Never",
+ "expr": "sum(cache_max_bytes)",
+ "hide": false,
+ "instant": false,
+ "interval": "",
+ "legendFormat": "Max",
+ "refId": "B",
+ "units": "none",
+ "valueHandler": "Number Threshold"
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Cache",
+ "type": "timeseries"
+ },
{
"cacheTimeout": null,
"datasource": "Prometheus",
@@ -408,8 +569,8 @@
},
"gridPos": {
"h": 8,
- "w": 8,
- "x": 16,
+ "w": 6,
+ "x": 18,
"y": 1
},
"id": 20,
@@ -1792,7 +1953,7 @@
"type": "timeseries"
},
{
- "collapsed": true,
+ "collapsed": false,
"datasource": null,
"gridPos": {
"h": 1,
@@ -1801,181 +1962,180 @@
"y": 27
},
"id": 53,
- "panels": [
- {
- "circleMaxSize": "10",
- "circleMinSize": "1",
- "colors": [
- "#73BF69",
- "#FADE2A",
- "#C4162A"
- ],
- "datasource": "Prometheus",
- "decimals": 0,
- "esMetric": "Count",
- "fieldConfig": {
- "defaults": {
- "custom": {}
- },
- "overrides": []
- },
- "gridPos": {
- "h": 15,
- "w": 11,
- "x": 0,
- "y": 28
- },
- "hideEmpty": true,
- "hideZero": true,
- "id": 39,
- "initialZoom": "2",
- "locationData": "countries",
- "mapCenter": "custom",
- "mapCenterLatitude": "30",
- "mapCenterLongitude": "20",
- "maxDataPoints": 1,
- "mouseWheelZoom": false,
- "pluginVersion": "7.3.4",
- "showLegend": true,
- "stickyLabels": false,
- "tableQueryOptions": {
- "geohashField": "geohash",
- "labelField": "country",
- "latitudeField": "latitude",
- "longitudeField": "longitude",
- "metricField": "metric",
- "queryType": "geohash"
- },
- "targets": [
- {
- "expr": "sum(increase(requests_country_counts_total[$__range])) by (country)",
- "format": "time_series",
- "instant": true,
- "interval": "",
- "legendFormat": "{{ country }}",
- "refId": "A"
- }
- ],
- "thresholds": "1000,10000",
- "timeFrom": null,
- "timeShift": null,
- "title": "Origin of requests over timerange",
- "type": "grafana-worldmap-panel",
- "unitPlural": "",
- "unitSingle": "",
- "unitSingular": "",
- "valueName": "current"
- },
- {
- "aliasColors": {},
- "bars": true,
- "dashLength": 10,
- "dashes": false,
- "datasource": null,
- "fieldConfig": {
- "defaults": {
- "custom": {},
- "thresholds": {
- "mode": "absolute",
- "steps": []
- }
- },
- "overrides": []
- },
- "fill": 8,
- "fillGradient": 0,
- "gridPos": {
- "h": 15,
- "w": 13,
- "x": 11,
- "y": 28
- },
- "hiddenSeries": false,
- "id": 41,
- "legend": {
- "alignAsTable": true,
- "avg": true,
- "current": true,
- "hideEmpty": true,
- "hideZero": true,
- "max": false,
- "min": false,
- "rightSide": true,
- "show": true,
- "sort": "avg",
- "sortDesc": true,
- "total": false,
- "values": true
- },
- "lines": false,
- "linewidth": 2,
- "nullPointMode": "null as zero",
- "options": {
- "alertThreshold": false
- },
- "percentage": false,
- "pluginVersion": "7.4.0",
- "pointradius": 0.5,
- "points": false,
- "renderer": "flot",
- "seriesOverrides": [],
- "spaceLength": 10,
- "stack": true,
- "steppedLine": false,
- "targets": [
- {
- "expr": "topk(5, sum(rate(requests_country_counts_total[$itvl])) by (country))",
- "interval": "$itvl",
- "legendFormat": "{{ country }}",
- "refId": "A"
- }
- ],
- "thresholds": [],
- "timeFrom": null,
- "timeRegions": [],
- "timeShift": null,
- "title": "Country spread [$itvl]",
- "tooltip": {
- "shared": true,
- "sort": 2,
- "value_type": "individual"
- },
- "type": "graph",
- "xaxis": {
- "buckets": null,
- "mode": "time",
- "name": null,
- "show": true,
- "values": []
- },
- "yaxes": [
- {
- "$$hashKey": "object:104",
- "format": "reqps",
- "label": null,
- "logBase": 1,
- "max": null,
- "min": "0",
- "show": true
- },
- {
- "$$hashKey": "object:105",
- "format": "short",
- "label": null,
- "logBase": 1,
- "max": null,
- "min": null,
- "show": false
- }
- ],
- "yaxis": {
- "align": false,
- "alignLevel": null
- }
- }
- ],
+ "panels": [],
"title": "GeoIP",
"type": "row"
+ },
+ {
+ "circleMaxSize": "10",
+ "circleMinSize": "1",
+ "colors": [
+ "#73BF69",
+ "#FADE2A",
+ "#C4162A"
+ ],
+ "datasource": "Prometheus",
+ "decimals": 0,
+ "esMetric": "Count",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {}
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 15,
+ "w": 11,
+ "x": 0,
+ "y": 28
+ },
+ "hideEmpty": true,
+ "hideZero": true,
+ "id": 39,
+ "initialZoom": "2",
+ "locationData": "countries",
+ "mapCenter": "custom",
+ "mapCenterLatitude": "30",
+ "mapCenterLongitude": "20",
+ "maxDataPoints": 1,
+ "mouseWheelZoom": false,
+ "pluginVersion": "7.3.4",
+ "showLegend": true,
+ "stickyLabels": false,
+ "tableQueryOptions": {
+ "geohashField": "geohash",
+ "labelField": "country",
+ "latitudeField": "latitude",
+ "longitudeField": "longitude",
+ "metricField": "metric",
+ "queryType": "geohash"
+ },
+ "targets": [
+ {
+ "expr": "sum(increase(requests_country_counts_total[$__range])) by (country)",
+ "format": "time_series",
+ "instant": true,
+ "interval": "",
+ "legendFormat": "{{ country }}",
+ "refId": "A"
+ }
+ ],
+ "thresholds": "1000,10000",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Origin of requests over timerange",
+ "type": "grafana-worldmap-panel",
+ "unitPlural": "",
+ "unitSingle": "",
+ "unitSingular": "",
+ "valueName": "current"
+ },
+ {
+ "aliasColors": {},
+ "bars": true,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": null,
+ "fieldConfig": {
+ "defaults": {
+ "custom": {},
+ "thresholds": {
+ "mode": "absolute",
+ "steps": []
+ }
+ },
+ "overrides": []
+ },
+ "fill": 8,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 15,
+ "w": 13,
+ "x": 11,
+ "y": 28
+ },
+ "hiddenSeries": false,
+ "id": 41,
+ "legend": {
+ "alignAsTable": true,
+ "avg": true,
+ "current": true,
+ "hideEmpty": true,
+ "hideZero": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "sort": "avg",
+ "sortDesc": true,
+ "total": false,
+ "values": true
+ },
+ "lines": false,
+ "linewidth": 2,
+ "nullPointMode": "null as zero",
+ "options": {
+ "alertThreshold": false
+ },
+ "percentage": false,
+ "pluginVersion": "7.4.0",
+ "pointradius": 0.5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": true,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "topk(5, sum(rate(requests_country_counts_total[$itvl])) by (country))",
+ "interval": "$itvl",
+ "legendFormat": "{{ country }}",
+ "refId": "A"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Country spread [$itvl]",
+ "tooltip": {
+ "shared": true,
+ "sort": 2,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "$$hashKey": "object:104",
+ "format": "reqps",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": "0",
+ "show": true
+ },
+ {
+ "$$hashKey": "object:105",
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": false
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
}
],
"refresh": "30s",
@@ -2069,12 +2229,12 @@
]
},
"time": {
- "from": "now-30m",
+ "from": "now-5m",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "MangaDex@Home - Personal client dashboard",
"uid": "a7sZAw2Mk",
- "version": 4
-}
+ "version": 2
+}
\ No newline at end of file
diff --git a/src/main/kotlin/mdnet/ServerManager.kt b/src/main/kotlin/mdnet/ServerManager.kt
index b7859d8..03f7427 100644
--- a/src/main/kotlin/mdnet/ServerManager.kt
+++ b/src/main/kotlin/mdnet/ServerManager.kt
@@ -18,6 +18,8 @@ along with this MangaDex@Home. If not, see .
*/
package mdnet
+import io.micrometer.core.instrument.Gauge
+import io.micrometer.core.instrument.binder.BaseUnits
import io.micrometer.prometheus.PrometheusConfig
import io.micrometer.prometheus.PrometheusMeterRegistry
import mdnet.cache.ImageStorage
@@ -105,7 +107,19 @@ class ServerManager(
fun start() {
LOGGER.info { "Image server starting" }
- DefaultMicrometerMetrics(registry, storage.cacheDirectory)
+ DefaultMicrometerMetrics(registry)
+ Gauge.builder(
+ "cache.used",
+ storage,
+ { it.size.toDouble() }
+ ).baseUnit(BaseUnits.BYTES).register(registry)
+
+ Gauge.builder(
+ "cache.max",
+ storage,
+ { it.maxSize.toDouble() }
+ ).baseUnit(BaseUnits.BYTES).register(registry)
+
loginAndStartServer()
var lastBytesSent = statistics.bytesSent.get()
diff --git a/src/main/kotlin/mdnet/cache/ImageStorage.kt b/src/main/kotlin/mdnet/cache/ImageStorage.kt
index 40c2a30..a798f5d 100644
--- a/src/main/kotlin/mdnet/cache/ImageStorage.kt
+++ b/src/main/kotlin/mdnet/cache/ImageStorage.kt
@@ -55,7 +55,7 @@ data class Image(val data: ImageMetadata, val stream: InputStream)
*/
class ImageStorage(
var maxSize: Long,
- val cacheDirectory: Path,
+ private val cacheDirectory: Path,
private val database: Database,
autoPrune: Boolean = true
) {
diff --git a/src/main/kotlin/mdnet/data/Token.kt b/src/main/kotlin/mdnet/data/Token.kt
index 3ea1fc4..9d55169 100644
--- a/src/main/kotlin/mdnet/data/Token.kt
+++ b/src/main/kotlin/mdnet/data/Token.kt
@@ -23,4 +23,4 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming
import java.time.OffsetDateTime
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
-data class Token(val expires: OffsetDateTime, val hash: String, val clientId: String? = null, val ip: String? = null)
+data class Token(val expires: OffsetDateTime, val hash: String, val clientId: String)
diff --git a/src/main/kotlin/mdnet/metrics/DefaultMicrometerMetrics.kt b/src/main/kotlin/mdnet/metrics/DefaultMicrometerMetrics.kt
index 8d67a45..3f4a309 100644
--- a/src/main/kotlin/mdnet/metrics/DefaultMicrometerMetrics.kt
+++ b/src/main/kotlin/mdnet/metrics/DefaultMicrometerMetrics.kt
@@ -19,7 +19,6 @@ along with this MangaDex@Home. If not, see .
package mdnet.metrics
import io.micrometer.core.instrument.Tag
-import io.micrometer.core.instrument.binder.jvm.DiskSpaceMetrics
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics
import io.micrometer.core.instrument.binder.jvm.JvmHeapPressureMetrics
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics
@@ -30,9 +29,8 @@ import io.micrometer.core.instrument.binder.system.ProcessorMetrics
import io.micrometer.core.instrument.binder.system.UptimeMetrics
import io.micrometer.prometheus.PrometheusMeterRegistry
import mdnet.BuildInfo
-import java.nio.file.Path
-class DefaultMicrometerMetrics(registry: PrometheusMeterRegistry, cacheDirectory: Path) {
+class DefaultMicrometerMetrics(registry: PrometheusMeterRegistry) {
init {
UptimeMetrics(
mutableListOf(
@@ -47,6 +45,5 @@ class DefaultMicrometerMetrics(registry: PrometheusMeterRegistry, cacheDirectory
JvmHeapPressureMetrics().bindTo(registry)
FileDescriptorMetrics().bindTo(registry)
LogbackMetrics().bindTo(registry)
- DiskSpaceMetrics(cacheDirectory.toFile()).bindTo(registry)
}
}
diff --git a/src/main/kotlin/mdnet/server/ImageHandler.kt b/src/main/kotlin/mdnet/server/ImageHandler.kt
index f8267df..5c922f4 100644
--- a/src/main/kotlin/mdnet/server/ImageHandler.kt
+++ b/src/main/kotlin/mdnet/server/ImageHandler.kt
@@ -44,7 +44,7 @@ class ImageServer(
registry: PrometheusMeterRegistry
) {
private val executor = Executors.newCachedThreadPool()
- private val cacheLookupTimer = Timer.builder("cache_lookup")
+ private val cacheLookupTimer = Timer.builder("cache.lookup")
.publishPercentiles(0.5, 0.75, 0.9, 0.99)
.register(registry)
diff --git a/src/main/kotlin/mdnet/server/ImageServer.kt b/src/main/kotlin/mdnet/server/ImageServer.kt
index 582b9eb..1af0f1a 100644
--- a/src/main/kotlin/mdnet/server/ImageServer.kt
+++ b/src/main/kotlin/mdnet/server/ImageServer.kt
@@ -22,6 +22,7 @@ import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry
import io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics
import io.micrometer.core.instrument.FunctionCounter
+import io.micrometer.core.instrument.binder.BaseUnits
import io.micrometer.prometheus.PrometheusMeterRegistry
import mdnet.cache.ImageStorage
import mdnet.data.Statistics
@@ -98,10 +99,10 @@ fun getServer(
)
FunctionCounter.builder(
- "client_sent_bytes",
+ "client.sent",
statistics,
{ it.bytesSent.get().toDouble() }
- ).register(registry)
+ ).baseUnit(BaseUnits.BYTES).register(registry)
val verifier = TokenVerifier(
tokenKey = remoteSettings.tokenKey,
diff --git a/src/main/kotlin/mdnet/settings/PingResult.kt b/src/main/kotlin/mdnet/settings/PingResult.kt
index b9f368a..68ced00 100644
--- a/src/main/kotlin/mdnet/settings/PingResult.kt
+++ b/src/main/kotlin/mdnet/settings/PingResult.kt
@@ -36,6 +36,7 @@ data class RemoteSettings(
val imageServer: Uri,
val latestBuild: Int,
val url: Uri,
+ val clientId: String,
@field:Secret val tokenKey: ByteArray,
val compromised: Boolean,
val paused: Boolean,
@@ -51,6 +52,7 @@ data class RemoteSettings(
if (imageServer != other.imageServer) return false
if (latestBuild != other.latestBuild) return false
if (url != other.url) return false
+ if (clientId != other.clientId) return false
if (!tokenKey.contentEquals(other.tokenKey)) return false
if (compromised != other.compromised) return false
if (paused != other.paused) return false
@@ -64,6 +66,7 @@ data class RemoteSettings(
var result = imageServer.hashCode()
result = 31 * result + latestBuild
result = 31 * result + url.hashCode()
+ result = 31 * result + clientId.hashCode()
result = 31 * result + tokenKey.contentHashCode()
result = 31 * result + compromised.hashCode()
result = 31 * result + paused.hashCode()