nixos-config/modules/qbittorrent/exporter/qbittorrent_exporter.go
Simon Bruder 8dc3558c7c
qbittorrent/exporter: Do not expose seeding time
It is not useful and just wastes storage space.
2022-03-25 21:54:07 +01:00

183 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"encoding/json"
"io/ioutil"
"log"
"net"
"net/http"
"net/url"
"os"
"strconv"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
type ApiClient struct {
apiSocket string
}
type ApiError struct {
ReturnCode int
}
type TorrentInfo struct {
Auto bool `json:"auto_tmm"`
DownloadLimit int `json:"dl_limit"`
Downloaded int `json:"completed"`
Hash string `json:"hash"`
LeechsConnected int `json:"num_leechs"`
LeechsSwarm int `json:"num_incomplete"`
Name string `json:"name"`
SeedsConnected int `json:"num_seeds"`
SeedsSwarm int `json:"num_complete"`
Size int `json:"size"`
TotalSize int `json:"total_size"`
UploadLimit int `json:"ul_limit"`
Uploaded int `json:"uploaded"`
}
type Exporter struct {
Api ApiClient
}
var (
qbittorrentTorrentDownloaded = prometheus.NewDesc(
"qbittorrent_torrent_downloaded_bytes_total",
"Amount of data downloaded",
[]string{"hash", "name"}, nil,
)
qbittorrentTorrentLeechsConnected = prometheus.NewDesc(
"qbittorrent_torrent_leechs_connected",
"Number of leechs connected to",
[]string{"hash", "name"}, nil,
)
qbittorrentTorrentLeechsSwarm = prometheus.NewDesc(
"qbittorrent_torrent_leechs_swarm",
"Number of leechs in the swarm",
[]string{"hash", "name"}, nil,
)
qbittorrentTorrentSeedsConnected = prometheus.NewDesc(
"qbittorrent_torrent_seeds_connected",
"Number of seeds connected to",
[]string{"hash", "name"}, nil,
)
qbittorrentTorrentSeedsSwarm = prometheus.NewDesc(
"qbittorrent_torrent_seeds_swarm",
"Number of seeds in the swarm",
[]string{"hash", "name"}, nil,
)
qbittorrentTorrentSize = prometheus.NewDesc(
"qbittorrent_torrent_size_bytes_total",
"Size of selected torrent data",
[]string{"hash", "name"}, nil,
)
qbittorrentTorrentUploaded = prometheus.NewDesc(
"qbittorrent_torrent_uploaded_bytes_total",
"Amount of data uploaded",
[]string{"hash", "name"}, nil,
)
)
func (e ApiError) Error() string {
return strconv.Itoa(e.ReturnCode)
}
func CreateApiClient(apiSocket string) (c ApiClient) {
return ApiClient{
apiSocket: apiSocket,
}
}
func (c ApiClient) doRequest(group string, method string, parameters url.Values) (body []byte, err error) {
destinationUrl, err := url.Parse("http://unix/api/v2/" + group + "/" + method)
if err != nil {
log.Println(err)
return []byte{}, err
}
req := http.Request{
Method: "GET",
URL: destinationUrl,
Header: http.Header{},
Form: parameters,
}
httpClient := http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", c.apiSocket)
},
},
}
res, err := httpClient.Do(&req)
if err != nil {
log.Println(err)
return []byte{}, err
}
bodyBytes, err := ioutil.ReadAll(res.Body)
if err != nil {
return []byte{}, err
}
body = bodyBytes
if res.StatusCode != 200 {
err = ApiError{ReturnCode: res.StatusCode}
}
return
}
func (c ApiClient) TorrentsInfo() (torrentsInfo []TorrentInfo, err error) {
torrentsInfo = []TorrentInfo{}
body, err := c.doRequest("torrents", "info", url.Values{})
if err != nil {
return []TorrentInfo{}, err
}
json.Unmarshal(body, &torrentsInfo)
return
}
func (e Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- qbittorrentTorrentDownloaded
ch <- qbittorrentTorrentLeechsConnected
ch <- qbittorrentTorrentLeechsSwarm
ch <- qbittorrentTorrentSeedsConnected
ch <- qbittorrentTorrentSeedsSwarm
ch <- qbittorrentTorrentSize
ch <- qbittorrentTorrentUploaded
}
func (e Exporter) Collect(ch chan<- prometheus.Metric) {
torrentsInfo, err := e.Api.TorrentsInfo()
if err != nil {
log.Println(err)
return
}
for _, torrentInfo := range torrentsInfo {
ch <- prometheus.MustNewConstMetric(qbittorrentTorrentDownloaded, prometheus.CounterValue, float64(torrentInfo.Downloaded), torrentInfo.Hash, torrentInfo.Name)
ch <- prometheus.MustNewConstMetric(qbittorrentTorrentLeechsConnected, prometheus.GaugeValue, float64(torrentInfo.LeechsConnected), torrentInfo.Hash, torrentInfo.Name)
ch <- prometheus.MustNewConstMetric(qbittorrentTorrentLeechsSwarm, prometheus.GaugeValue, float64(torrentInfo.LeechsSwarm), torrentInfo.Hash, torrentInfo.Name)
ch <- prometheus.MustNewConstMetric(qbittorrentTorrentSeedsConnected, prometheus.GaugeValue, float64(torrentInfo.SeedsConnected), torrentInfo.Hash, torrentInfo.Name)
ch <- prometheus.MustNewConstMetric(qbittorrentTorrentSeedsSwarm, prometheus.GaugeValue, float64(torrentInfo.SeedsSwarm), torrentInfo.Hash, torrentInfo.Name)
ch <- prometheus.MustNewConstMetric(qbittorrentTorrentSize, prometheus.GaugeValue, float64(torrentInfo.Size), torrentInfo.Hash, torrentInfo.Name)
ch <- prometheus.MustNewConstMetric(qbittorrentTorrentUploaded, prometheus.CounterValue, float64(torrentInfo.Uploaded), torrentInfo.Hash, torrentInfo.Name)
}
}
func main() {
e := Exporter{
Api: CreateApiClient(
os.Getenv("QBITTORRENT_API_SOCKET"),
),
}
prometheus.MustRegister(e)
http.Handle("/metrics", promhttp.Handler())
listenAddress := os.Getenv("QBITTORRENT_EXPORTER_LISTEN_ADDRESS")
if listenAddress == "" {
listenAddress = ":9561" // this reuses the port number of fru1tstands exporter
}
log.Printf("Starting HTTP server at at %s", listenAddress)
log.Fatalf("Failed to start http server: %v", http.ListenAndServe(listenAddress, nil))
}