commit c0d87f1c0ca402c5758749bbc0e67bc4e5e2a3fd Author: usbharu Date: Sat Feb 1 16:26:12 2025 +0900 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10165ac --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/test.env diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..cc5c502 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# デフォルトの無視対象ファイル +/shelf/ +/workspace.xml +# エディターベースの HTTP クライアントリクエスト +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..08c396b --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +misskey-exporter \ No newline at end of file diff --git a/.idea/misskey-exporter.iml b/.idea/misskey-exporter.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/misskey-exporter.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9854b96 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e0d3ebc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +# ステージ1 +FROM golang:1.23.5-alpine3.21 AS go +WORKDIR /app +COPY go.mod go.sum main.go collector.go ./ +RUN go mod download \ +&& go build -o main . + +# ステージ2 +FROM alpine:3.21 +WORKDIR /app +COPY --from=go /app/main . +USER 1001 +CMD [ "/app/main" ] \ No newline at end of file diff --git a/collector.go b/collector.go new file mode 100644 index 0000000..aee6b5a --- /dev/null +++ b/collector.go @@ -0,0 +1,114 @@ +package main + +import ( + "bytes" + "github.com/sirupsen/logrus" + "github.com/yitsushi/go-misskey/services/notes/timeline" + "net/http" + "time" +) + +func collectJobqueue() { + for { + time.Sleep(1 * time.Second) + stats, err := client.Admin().Queue().Stats() + if err != nil { + logrus.Warning(err) + continue + } + misskeyJobQueueJobsCount.WithLabelValues("deliver", "waiting").Set(float64(stats.Deliver.Waiting)) + misskeyJobQueueJobsCount.WithLabelValues("deliver", "active").Set(float64(stats.Deliver.Active)) + misskeyJobQueueJobsCount.WithLabelValues("deliver", "completed").Set(float64(stats.Deliver.Completed)) + misskeyJobQueueJobsCount.WithLabelValues("deliver", "failed").Set(float64(stats.Deliver.Failed)) + misskeyJobQueueJobsCount.WithLabelValues("deliver", "delayed").Set(float64(stats.Deliver.Delayed)) + misskeyJobQueueJobsCount.WithLabelValues("deliver", "paused").Set(float64(stats.Deliver.Paused)) + misskeyJobQueueJobsCount.WithLabelValues("inbox", "waiting").Set(float64(stats.Inbox.Waiting)) + misskeyJobQueueJobsCount.WithLabelValues("inbox", "active").Set(float64(stats.Inbox.Active)) + misskeyJobQueueJobsCount.WithLabelValues("inbox", "completed").Set(float64(stats.Inbox.Completed)) + misskeyJobQueueJobsCount.WithLabelValues("inbox", "failed").Set(float64(stats.Inbox.Failed)) + misskeyJobQueueJobsCount.WithLabelValues("inbox", "delayed").Set(float64(stats.Inbox.Delayed)) + misskeyJobQueueJobsCount.WithLabelValues("inbox", "paused").Set(float64(stats.Inbox.Paused)) + misskeyJobQueueJobsCount.WithLabelValues("db", "waiting").Set(float64(stats.DB.Waiting)) + misskeyJobQueueJobsCount.WithLabelValues("db", "active").Set(float64(stats.DB.Active)) + misskeyJobQueueJobsCount.WithLabelValues("db", "completed").Set(float64(stats.DB.Completed)) + misskeyJobQueueJobsCount.WithLabelValues("db", "failed").Set(float64(stats.DB.Failed)) + misskeyJobQueueJobsCount.WithLabelValues("db", "delayed").Set(float64(stats.DB.Delayed)) + misskeyJobQueueJobsCount.WithLabelValues("db", "paused").Set(float64(stats.DB.Paused)) + misskeyJobQueueJobsCount.WithLabelValues("objectstorage", "waiting").Set(float64(stats.ObjectStorage.Waiting)) + misskeyJobQueueJobsCount.WithLabelValues("objectstorage", "active").Set(float64(stats.ObjectStorage.Active)) + misskeyJobQueueJobsCount.WithLabelValues("objectstorage", "completed").Set(float64(stats.ObjectStorage.Completed)) + misskeyJobQueueJobsCount.WithLabelValues("objectstorage", "failed").Set(float64(stats.ObjectStorage.Failed)) + misskeyJobQueueJobsCount.WithLabelValues("objectstorage", "delayed").Set(float64(stats.ObjectStorage.Delayed)) + misskeyJobQueueJobsCount.WithLabelValues("objectstorage", "paused").Set(float64(stats.ObjectStorage.Paused)) + } +} + +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) + if err != nil { + logrus.Warning(err) + continue + } + err = resp.Body.Close() + if err != nil { + logrus.Warning(err) + continue + } + misskeyPingResponseCode.Set(float64(resp.StatusCode)) + misskeyPingResponseTime.Observe(float64(time.Since(start).Milliseconds()) / 1000) + misskeyPingResponseTimeRaw.Set(float64(time.Since(start).Milliseconds()) / 1000) + } +} + +func collectStats() { + for { + time.Sleep(30 * time.Second) + stats, err := client.Meta().Stats() + if err != nil { + continue + } + misskeyNotesCount.Set(float64(stats.NotesCount)) + misskeyUsersCount.Set(float64(stats.UsersCount)) + misskeyOriginalNotesCount.Set(float64(stats.OriginalNotesCount)) + misskeyOriginalUsersCount.Set(float64(stats.OriginalUsersCount)) + } +} + +func collectTimeline() { + for { + time.Sleep(30 * time.Second) + go func() { + start := time.Now() + global, err := client.Notes().Timeline().Global(timeline.GlobalRequest{Limit: 10}) + if err != nil { + logrus.Warning(err) + return + } + misskeyGlobalTimelineResponseTime.Observe(float64(time.Since(start).Milliseconds()) / 1000) + misskeyGlobalTimelineResponseTimeRaw.Set(float64(time.Since(start).Milliseconds()) / 1000) + if len(global) != 0 { + misskeyGlobalTimelineLastNotePublished.Set(float64(global[0].CreatedAt.UnixMilli())) + + } + }() + go func() { + start := time.Now() + global, err := client.Notes().Timeline().Local(timeline.LocalRequest{Limit: 10}) + if err != nil { + logrus.Warning(err) + return + } + misskeyLocalTimelineResponseTime.Observe(float64(time.Since(start).Milliseconds()) / 1000) + misskeyLocalTimelineResponseTimeRaw.Set(float64(time.Since(start).Milliseconds()) / 1000) + if len(global) != 0 { + misskeyLocalTimelineLastNotePublished.Set(float64(global[0].CreatedAt.UnixMilli())) + + } + }() + + } +} diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..c7a95df --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,22 @@ +services: + misskey-exporter: + env_file: + - test.env + build: . + ports: + - "8080:8080" + + prometheus: + image: prom/prometheus + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + ports: + - 9090:9090 + grafana: + image: grafana/grafana-enterprise + ports: + - '3011:3000' + volumes: + - 'grafana_storage:/var/lib/grafana' +volumes: + grafana_storage: { } \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b4f542f --- /dev/null +++ b/go.mod @@ -0,0 +1,22 @@ +module misskey-exporter + +go 1.23 + +require ( + github.com/sirupsen/logrus v1.7.0 + github.com/yitsushi/go-misskey v1.1.6 +) + +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/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.26.0 // indirect + golang.org/x/sys v0.22.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..801885b --- /dev/null +++ b/go.sum @@ -0,0 +1,40 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/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_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +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/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yitsushi/go-misskey v1.1.6 h1:aMfu7N9RzS2JO2QP9TPBXQyvudwU7NTr6jiOeUcho3Q= +github.com/yitsushi/go-misskey v1.1.6/go.mod h1:FeLNMTVebkWTbjRt5X1YqkKc61dOjgTy2hyUDaOC+zc= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +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/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..1095eb6 --- /dev/null +++ b/main.go @@ -0,0 +1,163 @@ +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/http" + "os" +) + +//TIP

To run your code, right-click the code and select Run.

Alternatively, click +// the icon in the gutter and select the Run menu item from here.

+ +//var now = time.Now() + +var client *misskey.Client +var endpoint = os.Getenv("MISSKEY_ENDPOINT") +var apiKey = os.Getenv("MISSKEY_API_TOKEN") + +var ( + misskeyJobQueueJobsCount = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "misskey_jobqueue_jobs", + Help: "misskey job queue jobs", + }, + []string{"type", "status"}, + ) + misskeyNotesCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "misskey_notes_count", + Help: "Notes Coount", + }, + ) + misskeyUsersCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "misskey_users_count", + Help: "Users Coount", + }, + ) + misskeyOriginalNotesCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "misskey_original_notes_count", + Help: "Original Notes Coount", + }, + ) + misskeyOriginalUsersCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "misskey_original_users_count", + 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", + Help: "Last Note Published on Global Timeline", + }) + misskeyLocalTimelineLastNotePublished = prometheus.NewGauge( + prometheus.GaugeOpts{ + 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", + }) +) + +type MetricGroup struct { + Basename string + Help string + Type string + Value []Metric +} + +type Metric struct { + Label map[string]string + Value string +} + +func main() { + options, err := misskey.NewClientWithOptions( + misskey.WithAPIToken(apiKey), + misskey.WithBaseURL("https", endpoint, ""), + misskey.WithLogLevel(logrus.DebugLevel), + ) + client = options + if err != nil { + return + } + stats, err := client.Admin().Queue().Stats() + if err != nil { + return + } + fmt.Println(stats) + + prometheus.MustRegister(misskeyJobQueueJobsCount) + 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) + + handler := promhttp.Handler() + + server := http.Server{ + Addr: ":8080", + Handler: handler, + } + + go func() { + go collectJobqueue() + go collectPing() + go collectStats() + go collectTimeline() + }() + + server.ListenAndServe() +} diff --git a/prometheus.yml b/prometheus.yml new file mode 100644 index 0000000..4d144b7 --- /dev/null +++ b/prometheus.yml @@ -0,0 +1,7 @@ +global: + scrape_interval: 1s + evaluation_interval: 1s +scrape_configs: + - job_name: 'process_exporter' + static_configs: + - targets: ['misskey-exporter:8080'] \ No newline at end of file