Compare commits

...

13 Commits

Author SHA1 Message Date
usbharu 79255b1fa9
fix dashboard2
docker-ci / docker (push) Successful in 55s Details
2025-07-07 16:06:16 +09:00
usbharu 79a0844d7a
fix dashboard
docker-ci / docker (push) Successful in 54s Details
2025-07-07 16:02:48 +09:00
usbharu 38867f0c67
使用されていないメトリクスを削除
docker-ci / docker (push) Successful in 55s Details
2025-07-07 14:50:54 +09:00
usbharu 8054429f40
fix: プロトコルを指定した場合一部の計測が指定されたプロトコルを使用しない問題を修正
docker-ci / docker (push) Successful in 1m10s Details
2025-05-09 10:45:04 +09:00
usbharu b94aa5e9aa
feat: 環境変数でログレベルとログフォーマットを変更できるように
docker-ci / docker (push) Successful in 1m3s Details
2025-04-25 10:13:23 +09:00
usbharu 78009e495d
feat: ジョブキュー監視用のダッシュボードを追加
docker-ci / docker (push) Successful in 1m3s Details
2025-04-20 21:26:40 +09:00
usbharu 9049c07f3a
feat: ジョブが失敗しているホストを取得できるように 2025-04-20 21:25:18 +09:00
usbharu 6bb1d7cd0e
fix: オンラインユーザーの取得に失敗したときエラーで終了する問題を修正
docker-ci / docker (push) Successful in 1m1s Details
2025-04-20 18:16:22 +09:00
usbharu 2fe3084d80
feat: バージョンを表示できるように 2025-04-20 18:11:31 +09:00
usbharu f34672c826
fix: log 2025-04-20 17:39:15 +09:00
usbharu 0bc7588984
feat: Misskeyのプロトコルとパスを指定できるように 2025-04-20 17:34:53 +09:00
usbharu 390880343b
fix ci
docker-ci / docker (push) Successful in 1m9s Details
2025-04-20 17:03:29 +09:00
usbharu 31b7d2bfa1
release v0.1.1
docker-ci / docker (push) Successful in 1m9s Details
2025-04-20 16:59:38 +09:00
9 changed files with 2060 additions and 254 deletions

View File

@ -4,6 +4,8 @@ on:
push:
branches:
- 'master'
tags:
- "**"
jobs:
docker:
@ -30,6 +32,7 @@ jobs:
with:
images: git.usbharu.dev/usbharu/misskey-exporter
tags: |
type=semver,pattern=v{{version}}
type=raw,value=latest
type=sha,prefix=,suffix=,format=short
- name: build

View File

@ -6,7 +6,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/yitsushi/go-misskey/services/notes/timeline"
"io"
"net/http"
"math"
"time"
)
@ -45,35 +45,86 @@ func collectJobqueue() {
}
}
func collectDelayed() {
collectDelayedInternal()
for {
time.Sleep(5 * time.Minute)
collectDelayedInternal()
}
}
func collectDelayedInternal() {
delayed, err := client.Admin().Queue().InboxDelayed()
if err != nil {
logrus.WithField("type", "delayed").Warning(err)
} else {
for i := range delayed {
m := delayed[i]
misskeyJobQueueDelayed.WithLabelValues("inbox", m.Host).Set(float64(m.Count))
}
}
deliverDelayed, err := client.Admin().Queue().DeliverDelayed()
if err != nil {
logrus.WithField("type", "delayed").Warning(err)
return
}
for i := range deliverDelayed {
m := deliverDelayed[i]
misskeyJobQueueDelayed.WithLabelValues("deliver", m.Host).Set(float64(m.Count))
}
}
func collectPing() {
for {
time.Sleep(5 * time.Second)
var buf = bytes.NewBuffer([]byte(`{}`))
start := time.Now()
resp, err := http.Post("https://"+endpoint+"/api/ping", "application/json", buf)
resp, err := httpClient.Post(protocol+"://"+endpoint+"/api/ping", "application/json", buf)
if err != nil {
logrus.WithField("type", "ping").Warning(err)
misskeyPingResponseCode.Set(math.NaN())
misskeyApiResponseTime.WithLabelValues("ping", "").Observe(math.NaN())
misskeyApiResponseTimeRaw.WithLabelValues("ping", "").Set(math.NaN())
continue
}
err = resp.Body.Close()
if err != nil {
logrus.WithField("type", "ping").Warning(err)
misskeyPingResponseCode.Set(math.NaN())
misskeyApiResponseTime.WithLabelValues("ping", "").Observe(math.NaN())
misskeyApiResponseTimeRaw.WithLabelValues("ping", "").Set(math.NaN())
continue
}
misskeyPingResponseCode.Set(float64(resp.StatusCode))
t := float64(time.Since(start).Milliseconds()) / 1000
misskeyPingResponseTime.Observe(t)
misskeyPingResponseTimeRaw.Set(t)
misskeyApiResponseTime.WithLabelValues("ping", "").Observe(t)
misskeyApiResponseTimeRaw.WithLabelValues("ping", "").Set(t)
}
}
func collectMeta() {
for {
time.Sleep(1 * time.Minute)
meta, err := client.Meta().InstanceMeta(true)
if err != nil {
logrus.WithField("type", "meta").Warning(err)
misskeyMeta.WithLabelValues("UNKNOWN").Set(math.NaN())
continue
}
misskeyMeta.WithLabelValues(*meta.Version).Set(1)
}
}
func collectStats() {
for {
time.Sleep(30 * time.Second)
stats, err := client.Meta().Stats()
if err != nil {
logrus.WithField("type", "stats").Warning(err)
misskeyNotesCount.Set(math.NaN())
misskeyUsersCount.Set(math.NaN())
misskeyOriginalNotesCount.Set(math.NaN())
misskeyOriginalUsersCount.Set(math.NaN())
continue
}
misskeyNotesCount.Set(float64(stats.NotesCount))
@ -90,7 +141,7 @@ type OnlineUsers struct {
func collectOnlineUsers() {
for {
time.Sleep(1 * time.Minute)
response, err := http.Get("https://" + endpoint + "/api/get-online-users-count")
response, err := httpClient.Get(protocol + "://" + endpoint + "/api/get-online-users-count")
if err != nil {
logrus.WithField("type", "online-users").Warning(err)
continue
@ -111,6 +162,7 @@ func collectOnlineUsers() {
err = json.Unmarshal(all, &onlineUsers)
if err != nil {
logrus.WithField("type", "online-users").Warning(err)
continue
}
misskeyOnlineUsers.Set(float64(onlineUsers.Count))
}
@ -124,11 +176,12 @@ func collectTimeline() {
global, err := client.Notes().Timeline().Global(timeline.GlobalRequest{Limit: 10})
if err != nil {
logrus.WithField("type", "timeline").Warning(err)
misskeyApiResponseTime.WithLabelValues("timeline", "global").Observe(math.NaN())
misskeyApiResponseTimeRaw.WithLabelValues("timeline", "global").Set(math.NaN())
misskeyGlobalTimelineLastNotePublished.Set(math.NaN())
return
}
t := float64(time.Since(start).Milliseconds()) / 1000
misskeyGlobalTimelineResponseTime.Observe(t)
misskeyGlobalTimelineResponseTimeRaw.Set(t)
misskeyApiResponseTime.WithLabelValues("timeline", "global").Observe(t)
misskeyApiResponseTimeRaw.WithLabelValues("timeline", "global").Set(t)
if len(global) != 0 {
@ -140,11 +193,12 @@ func collectTimeline() {
global, err := client.Notes().Timeline().Local(timeline.LocalRequest{Limit: 10})
if err != nil {
logrus.WithField("type", "timeline").Warning(err)
misskeyApiResponseTime.WithLabelValues("timeline", "local").Observe(math.NaN())
misskeyApiResponseTimeRaw.WithLabelValues("timeline", "local").Set(math.NaN())
misskeyLocalTimelineLastNotePublished.Set(math.NaN())
return
}
t := float64(time.Since(start).Milliseconds()) / 1000
misskeyLocalTimelineResponseTime.Observe(t)
misskeyLocalTimelineResponseTimeRaw.Set(t)
misskeyApiResponseTime.WithLabelValues("timeline", "local").Observe(t)
misskeyApiResponseTimeRaw.WithLabelValues("timeline", "local").Set(t)
if len(global) != 0 {
@ -156,11 +210,12 @@ func collectTimeline() {
global, err := client.Notes().Timeline().Get(timeline.GetRequest{Limit: 10})
if err != nil {
logrus.WithField("type", "timeline").Warning(err)
misskeyApiResponseTime.WithLabelValues("timeline", "home").Observe(math.NaN())
misskeyApiResponseTimeRaw.WithLabelValues("timeline", "home").Set(math.NaN())
misskeyHomeTimelineLastNotePublished.Set(math.NaN())
return
}
t := float64(time.Since(start).Milliseconds()) / 1000
misskeyHomeTimelineResponseTime.Observe(t)
misskeyHomeTimelineResponseTimeRaw.Set(t)
misskeyApiResponseTime.WithLabelValues("timeline", "home").Observe(t)
misskeyApiResponseTimeRaw.WithLabelValues("timeline", "home").Set(t)
if len(global) != 0 {
@ -172,11 +227,12 @@ func collectTimeline() {
global, err := client.Notes().Timeline().Hybrid(timeline.HybridRequest{Limit: 10})
if err != nil {
logrus.WithField("type", "timeline").Warning(err)
misskeyApiResponseTime.WithLabelValues("timeline", "social").Observe(math.NaN())
misskeyApiResponseTimeRaw.WithLabelValues("timeline", "social").Set(math.NaN())
misskeySocialTimelineLastNotePublished.Set(math.NaN())
return
}
t := float64(time.Since(start).Milliseconds()) / 1000
misskeySocialTimelineResponseTime.Observe(t)
misskeySocialTimelineResponseTimeRaw.Set(t)
misskeyApiResponseTime.WithLabelValues("timeline", "social").Observe(t)
misskeyApiResponseTimeRaw.WithLabelValues("timeline", "social").Set(t)
if len(global) != 0 {

1165
dashboard.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,6 @@
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"fieldConfig": {
@ -31,36 +30,23 @@
"color": {
"mode": "thresholds"
},
"fieldMinMax": false,
"mappings": [
{
"options": {
"0": {
"index": 1,
"text": "DOWN"
},
"1": {
"index": 0,
"text": "UP"
}
},
"type": "value"
}
],
"noValue": "UNKNOWN",
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red"
"color": "green"
},
{
"color": "red",
"value": 0
},
{
"color": "green",
"value": 1
"value": 80
}
]
}
@ -68,140 +54,60 @@
"overrides": []
},
"gridPos": {
"h": 6,
"w": 3,
"h": 3,
"w": 6,
"x": 0,
"y": 0
},
"id": 1,
"id": 12,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": [
"last"
],
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"values": false
"reducer": [
"sum"
],
"show": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
"showHeader": false
},
"pluginVersion": "11.6.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "misskey_health",
"exemplar": false,
"expr": "misskey_meta{instance=\"$instance\", job=\"$job\"}",
"format": "table",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": true,
"legendFormat": "__auto",
"range": true,
"range": false,
"refId": "A",
"useBackend": false
}
],
"title": "Health",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"fieldMinMax": false,
"mappings": [
{
"options": {
"0": {
"index": 1,
"text": "DOWN"
},
"1": {
"index": 0,
"text": "UP"
}
},
"type": "value"
}
],
"noValue": "UNKNOWN",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red"
},
{
"color": "red",
"value": 0
},
{
"color": "green",
"value": 1
}
]
"title": "Misskey Version",
"transformations": [
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"Value": true,
"__name__": true,
"instance": true,
"job": true
},
"includeByName": {},
"indexByName": {},
"renameByName": {}
}
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 3,
"x": 3,
"y": 0
},
"id": 7,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "value",
"wideLayout": true
},
"pluginVersion": "11.6.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "up",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "Exporter Health",
"type": "stat"
"type": "table"
},
{
"datasource": {
@ -261,14 +167,18 @@
"pluginVersion": "11.6.0",
"targets": [
{
"disableTextWrap": false,
"editorMode": "code",
"exemplar": false,
"expr": "100 * (sum_over_time(misskey_health[30d]) / count_over_time(misskey_health[30d]))",
"expr": "100 * (sum_over_time(misskey_health{instance=\"$instance\", job=\"$job\"}[30d]) / count_over_time(misskey_health{instance=\"$instance\", job=\"$job\"}[30d]))",
"format": "time_series",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A"
"refId": "A",
"useBackend": false
}
],
"title": "Availability(30d)",
@ -311,7 +221,7 @@
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": [
"lastNotNull"
"last"
],
"fields": "",
"values": false
@ -384,26 +294,34 @@
"pluginVersion": "11.6.0",
"targets": [
{
"disableTextWrap": false,
"editorMode": "builder",
"expr": "misskey_notes_count",
"expr": "misskey_notes_count{instance=\"$instance\", job=\"$job\"}",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "All",
"range": true,
"refId": "A"
"refId": "A",
"useBackend": false
},
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"disableTextWrap": false,
"editorMode": "builder",
"exemplar": false,
"expr": "misskey_original_notes_count",
"expr": "misskey_original_notes_count{instance=\"$instance\", job=\"$job\"}",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "Local",
"range": true,
"refId": "B"
"refId": "B",
"useBackend": false
}
],
"title": "Note Count",
@ -459,29 +377,219 @@
"pluginVersion": "11.6.0",
"targets": [
{
"editorMode": "code",
"expr": "misskey_users_count",
"disableTextWrap": false,
"editorMode": "builder",
"expr": "misskey_users_count{instance=\"$instance\", job=\"$job\"}",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "All",
"range": true,
"refId": "A"
"refId": "A",
"useBackend": false
},
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"editorMode": "code",
"expr": "misskey_original_users_count",
"disableTextWrap": false,
"editorMode": "builder",
"expr": "misskey_original_users_count{instance=\"$instance\", job=\"$job\"}",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "Local",
"range": true,
"refId": "B"
"refId": "B",
"useBackend": false
}
],
"title": "User Count",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"fieldMinMax": false,
"mappings": [
{
"options": {
"0": {
"index": 1,
"text": "DOWN"
},
"1": {
"index": 0,
"text": "UP"
}
},
"type": "value"
}
],
"noValue": "UNKNOWN",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red"
},
{
"color": "red",
"value": 0
},
{
"color": "green",
"value": 1
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 3,
"w": 3,
"x": 0,
"y": 3
},
"id": 1,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "11.6.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "misskey_health{instance=\"$instance\", job=\"$job\"}",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "Health",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"fieldMinMax": false,
"mappings": [
{
"options": {
"0": {
"index": 1,
"text": "DOWN"
},
"1": {
"index": 0,
"text": "UP"
}
},
"type": "value"
}
],
"noValue": "UNKNOWN",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red"
},
{
"color": "red",
"value": 0
},
{
"color": "green",
"value": 1
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 3,
"w": 3,
"x": 3,
"y": 3
},
"id": 7,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "value",
"wideLayout": true
},
"pluginVersion": "11.6.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "up{instance=\"$instance\", job=\"$job\"}",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "Exporter Health",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
@ -711,7 +819,7 @@
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "misskey_api_response_time_raw",
"expr": "misskey_api_response_time_raw{instance=\"$instance\", job=\"$job\"}",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": false,
@ -780,7 +888,7 @@
{
"disableTextWrap": false,
"editorMode": "builder",
"expr": "sum by(status) (misskey_jobqueue_jobs{type=\"deliver\"})",
"expr": "sum by(status) (misskey_jobqueue_jobs{type=\"deliver\", instance=\"$instance\", job=\"$job\"})",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "{{type}} {{status}}",
@ -847,7 +955,7 @@
{
"disableTextWrap": false,
"editorMode": "builder",
"expr": "sum by(status) (misskey_jobqueue_jobs{type=\"inbox\"})",
"expr": "sum by(status) (misskey_jobqueue_jobs{type=\"inbox\", instance=\"$instance\", job=\"$job\"})",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "{{type}} {{status}}",
@ -934,9 +1042,7 @@
],
"displayMode": "table",
"placement": "right",
"showLegend": true,
"sortBy": "Max",
"sortDesc": true
"showLegend": true
},
"tooltip": {
"hideZeros": false,
@ -949,7 +1055,7 @@
{
"disableTextWrap": false,
"editorMode": "builder",
"expr": "misskey_jobqueue_jobs",
"expr": "misskey_jobqueue_jobs{instance=\"$instance\", job=\"$job\"}",
"fullMetaSearch": false,
"includeNullMetadata": true,
"interval": "1",
@ -964,19 +1070,53 @@
}
],
"preload": false,
"refresh": "auto",
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
"list": [
{
"current": {
"text": "misskey-exporter:8080",
"value": "misskey-exporter:8080"
},
"definition": "label_values(instance)",
"name": "instance",
"options": [],
"query": {
"qryType": 1,
"query": "label_values(instance)",
"refId": "PrometheusVariableQueryEditor-VariableQuery"
},
"refresh": 1,
"regex": "",
"type": "query"
},
{
"current": {
"text": "process_exporter",
"value": "process_exporter"
},
"definition": "label_values(job)",
"name": "job",
"options": [],
"query": {
"qryType": 1,
"query": "label_values(job)",
"refId": "PrometheusVariableQueryEditor-VariableQuery"
},
"refresh": 1,
"regex": "",
"type": "query"
}
]
},
"time": {
"from": "now-15m",
"from": "now-3h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "New dashboard",
"uid": "eej5avfu4yyo0f",
"version": 25
"version": 26
}

424
dashboards2.json Normal file
View File

@ -0,0 +1,424 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 2,
"links": [],
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": [
{
"desc": true,
"displayName": "Delayed"
}
]
},
"pluginVersion": "11.6.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"disableTextWrap": false,
"editorMode": "builder",
"exemplar": false,
"expr": "misskey_jobqueue_delayed{instance=\"$instance\", job=\"$job\", type=\"inbox\"}",
"format": "table",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "A",
"useBackend": false
}
],
"title": "Inbox Delayed",
"transformations": [
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"__name__": true,
"instance": true,
"job": true,
"type": true
},
"includeByName": {},
"indexByName": {},
"renameByName": {
"Value": "Delayed"
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": [
{
"desc": true,
"displayName": "Delayed"
}
]
},
"pluginVersion": "11.6.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"disableTextWrap": false,
"editorMode": "builder",
"exemplar": false,
"expr": "misskey_jobqueue_delayed{instance=\"$instance\", job=\"$job\", type=\"deliver\"}",
"format": "table",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "A",
"useBackend": false
}
],
"title": "Deliver Delayed",
"transformations": [
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"__name__": true,
"instance": true,
"job": true,
"type": true
},
"includeByName": {},
"indexByName": {},
"renameByName": {
"Value": "Delayed"
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 0,
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 15,
"w": 24,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": {
"calcs": [
"last",
"max"
],
"displayMode": "list",
"placement": "right",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.6.0",
"targets": [
{
"disableTextWrap": false,
"editorMode": "builder",
"exemplar": false,
"expr": "misskey_jobqueue_delayed{instance=\"$instance\", job=\"$job\", host=~\"$host\", type=\"inbox\"}",
"format": "time_series",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "{{host}} {{type}}",
"range": true,
"refId": "A",
"useBackend": false
},
{
"datasource": {
"type": "prometheus",
"uid": "cej25be7w5af4e"
},
"disableTextWrap": false,
"editorMode": "builder",
"exemplar": false,
"expr": "misskey_jobqueue_delayed{instance=\"$instance\", job=\"$job\", host=~\"$host\", type=\"deliver\"}",
"format": "time_series",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "{{host}} {{type}}",
"range": true,
"refId": "B",
"useBackend": false
}
],
"title": "Delayed Host",
"type": "timeseries"
}
],
"preload": false,
"refresh": "auto",
"schemaVersion": 41,
"tags": [],
"templating": {
"list": [
{
"current": {
"text": "misskey-exporter:8080",
"value": "misskey-exporter:8080"
},
"definition": "label_values(instance)",
"name": "instance",
"options": [],
"query": {
"qryType": 1,
"query": "label_values(instance)",
"refId": "PrometheusVariableQueryEditor-VariableQuery"
},
"refresh": 1,
"regex": "",
"type": "query"
},
{
"current": {
"text": "process_exporter",
"value": "process_exporter"
},
"definition": "label_values(job)",
"name": "job",
"options": [],
"query": {
"qryType": 1,
"query": "label_values(job)",
"refId": "PrometheusVariableQueryEditor-VariableQuery"
},
"refresh": 1,
"regex": "",
"type": "query"
},
{
"current": {
"text": [
],
"value": [
]
},
"definition": "label_values(misskey_jobqueue_delayed,host)",
"includeAll": false,
"multi": true,
"name": "host",
"options": [],
"query": {
"qryType": 1,
"query": "label_values(misskey_jobqueue_delayed,host)",
"refId": "PrometheusVariableQueryEditor-VariableQuery"
},
"refresh": 1,
"regex": "",
"type": "query"
}
]
},
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "New dashboard2",
"uid": "aejhaokfyvj0gb",
"version": 9
}

18
go.mod
View File

@ -1,10 +1,11 @@
module github.com/usbharu/misskey-exporter
module git.usbharu.dev/usbharu/misskey-exporter
go 1.23.0
toolchain go1.23.3
require (
github.com/prometheus/client_golang v1.22.0
github.com/sirupsen/logrus v1.9.3
github.com/yitsushi/go-misskey v1.1.6
)
@ -12,13 +13,12 @@ require (
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.32.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
)

18
go.sum
View File

@ -7,18 +7,30 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
@ -36,6 +48,8 @@ golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
@ -44,8 +58,12 @@ golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=

142
main.go
View File

@ -1,13 +1,15 @@
package main
import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/sirupsen/logrus"
"github.com/yitsushi/go-misskey"
"net"
"net/http"
"os"
"strings"
"time"
)
//TIP <p>To run your code, right-click the code and select <b>Run</b>.</p> <p>Alternatively, click
@ -17,7 +19,22 @@ import (
var client *misskey.Client
var endpoint = os.Getenv("MISSKEY_ENDPOINT")
var protocol, hasProtocolEnv = os.LookupEnv("MISSKEY_PROTOCOL")
var path = os.Getenv("MISSKEY_PATH")
var apiKey = os.Getenv("MISSKEY_API_TOKEN")
var logLevelEnv = os.Getenv("LOGGER_LEVEL")
var logFormatEnv = os.Getenv("LOGGER_FORMAT")
var httpClient = &http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: time.Second,
}).DialContext,
TLSHandshakeTimeout: time.Second,
ResponseHeaderTimeout: time.Second,
IdleConnTimeout: time.Second,
},
}
var (
misskeyJobQueueJobsCount = prometheus.NewGaugeVec(
@ -27,6 +44,12 @@ var (
},
[]string{"type", "status"},
)
misskeyJobQueueDelayed = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "misskey_jobqueue_delayed",
Help: "Delayed Job",
},
[]string{"type", "host"})
misskeyNotesCount = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_notes_count",
@ -51,35 +74,13 @@ var (
Help: "Original Users Coount",
},
)
misskeyPingResponseTimeRaw = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_ping_response_time_raw",
Help: "Response Time for misskey Ping",
},
)
misskeyPingResponseTime = prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: "misskey_ping_response_time",
Help: "Response Time for misskey Ping",
Buckets: prometheus.DefBuckets,
},
)
misskeyPingResponseCode = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_ping_response_code",
Help: "Response Code for misskey Ping",
},
)
misskeyGlobalTimelineResponseTime = prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: "misskey_global_timeline_response_time",
Help: "Response Time for misskey Global Timeline",
})
misskeyLocalTimelineResponseTime = prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: "misskey_local_timeline_response_time",
Help: "Response Time for misskey Local Timeline",
})
misskeyGlobalTimelineLastNotePublished = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_global_timeline_last_note_published",
@ -90,42 +91,14 @@ var (
Name: "misskey_local_timeline_last_note_published",
Help: "Last Note Published on Local Timeline",
})
misskeyGlobalTimelineResponseTimeRaw = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_global_timeline_response_time_raw",
Help: "Response Time for misskey Global Timeline",
})
misskeyLocalTimelineResponseTimeRaw = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_local_timeline_response_time_raw",
Help: "Response Time for misskey Local Timeline",
})
misskeyHomeTimelineResponseTime = prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: "misskey_home_timeline_response_time",
Help: "Response Time for misskey Home Timeline",
})
misskeyHomeTimelineResponseTimeRaw = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_home_timeline_response_time_raw",
Help: "Response Time for misskey Home Timeline",
})
misskeyHomeTimelineLastNotePublished = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_home_timeline_last_note_published",
Help: "Last Note Published on Home Timeline",
},
)
misskeySocialTimelineResponseTime = prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: "misskey_social_timeline_response_time",
Help: "Response Time for misskey Home Timeline",
})
misskeySocialTimelineResponseTimeRaw = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_social_timeline_response_time_raw",
Help: "Response Time for misskey Home Timeline",
})
misskeySocialTimelineLastNotePublished = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "misskey_social_timeline_last_note_published",
@ -152,49 +125,72 @@ var (
Name: "misskey_online_users",
Help: "Online Users",
})
misskeyMeta = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "misskey_meta",
Help: "Misskey Instance Metadata",
},
[]string{"version"})
)
func main() {
if !hasProtocolEnv {
protocol = "https"
}
options, err := misskey.NewClientWithOptions(
misskey.WithAPIToken(apiKey),
misskey.WithBaseURL("https", endpoint, ""),
misskey.WithLogLevel(logrus.InfoLevel),
misskey.WithBaseURL(protocol, endpoint, path),
)
var logLevel logrus.Level
switch {
case strings.EqualFold("DEBUG", logLevelEnv):
logLevel = logrus.DebugLevel
break
case strings.EqualFold("INFO", logLevelEnv):
logLevel = logrus.InfoLevel
break
case strings.EqualFold("WARN", logLevelEnv):
logLevel = logrus.WarnLevel
break
case strings.EqualFold("ERROR", logLevelEnv):
logLevel = logrus.ErrorLevel
break
case strings.EqualFold("FATAL", logLevelEnv):
logLevel = logrus.FatalLevel
break
case strings.EqualFold("TRACE", logLevelEnv):
logLevel = logrus.TraceLevel
break
default:
logLevel = logrus.InfoLevel
}
if strings.EqualFold("json", logFormatEnv) {
logrus.SetFormatter(&logrus.JSONFormatter{})
}
logrus.SetLevel(logLevel)
client = options
if err != nil {
logrus.Error(err)
return
}
stats, err := client.Admin().Queue().Stats()
if err != nil {
logrus.Error(err)
return
}
fmt.Println(stats)
prometheus.MustRegister(misskeyJobQueueJobsCount)
prometheus.MustRegister(misskeyJobQueueDelayed)
prometheus.MustRegister(misskeyNotesCount)
prometheus.MustRegister(misskeyUsersCount)
prometheus.MustRegister(misskeyOriginalNotesCount)
prometheus.MustRegister(misskeyOriginalUsersCount)
prometheus.MustRegister(misskeyPingResponseTime)
prometheus.MustRegister(misskeyPingResponseTimeRaw)
prometheus.MustRegister(misskeyPingResponseCode)
prometheus.MustRegister(misskeyGlobalTimelineResponseTime)
prometheus.MustRegister(misskeyLocalTimelineResponseTime)
prometheus.MustRegister(misskeyGlobalTimelineLastNotePublished)
prometheus.MustRegister(misskeyLocalTimelineLastNotePublished)
prometheus.MustRegister(misskeyGlobalTimelineResponseTimeRaw)
prometheus.MustRegister(misskeyLocalTimelineResponseTimeRaw)
prometheus.MustRegister(misskeyHomeTimelineResponseTime)
prometheus.MustRegister(misskeyHomeTimelineResponseTimeRaw)
prometheus.MustRegister(misskeyHomeTimelineLastNotePublished)
prometheus.MustRegister(misskeySocialTimelineResponseTime)
prometheus.MustRegister(misskeySocialTimelineResponseTimeRaw)
prometheus.MustRegister(misskeySocialTimelineLastNotePublished)
prometheus.MustRegister(misskeyApiResponseTime)
prometheus.MustRegister(misskeyApiResponseTimeRaw)
prometheus.MustRegister(misskeyOnlineUsers)
prometheus.MustRegister(misskeyMeta)
handler := promhttp.Handler()
@ -205,10 +201,12 @@ func main() {
go func() {
go collectJobqueue()
go collectDelayed()
go collectPing()
go collectStats()
go collectTimeline()
go collectOnlineUsers()
go collectMeta()
}()
logrus.Error(server.ListenAndServe())

View File

@ -5,5 +5,7 @@ scrape_configs:
- job_name: 'process_exporter'
static_configs:
- targets: ['misskey-exporter:8080']
labels:
misskey_instance: misskey.usbharu.dev
rule_files:
- rules.yaml