diff --git a/api/bludv.go b/api/bludv.go index 567241c..3eae22f 100644 --- a/api/bludv.go +++ b/api/bludv.go @@ -22,6 +22,12 @@ var bludv = IndexerMeta{ } func (i *Indexer) HandlerBluDVIndexer(w http.ResponseWriter, r *http.Request) { + start := time.Now() + defer func() { + i.metrics.IndexerDuration.WithLabelValues("bludv").Observe(time.Since(start).Seconds()) + i.metrics.IndexerRequests.WithLabelValues("bludv").Inc() + }() + ctx := r.Context() // supported query params: q, season, episode q := r.URL.Query().Get("q") @@ -38,6 +44,7 @@ func (i *Indexer) HandlerBluDVIndexer(w http.ResponseWriter, r *http.Request) { if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + i.metrics.IndexerErrors.WithLabelValues("bludv").Inc() return } defer resp.Body.Close() @@ -46,6 +53,7 @@ func (i *Indexer) HandlerBluDVIndexer(w http.ResponseWriter, r *http.Request) { if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + i.metrics.IndexerErrors.WithLabelValues("bludv").Inc() return } @@ -174,7 +182,7 @@ func getTorrentsBluDV(ctx context.Context, i *Indexer, link string) ([]IndexedTo magnetAudio = append(magnetAudio, audio...) } - peer, seed, err := goscrape.GetLeechsAndSeeds(ctx, i.redis, infoHash, trackers) + peer, seed, err := goscrape.GetLeechsAndSeeds(ctx, i.redis, i.metrics, infoHash, trackers) if err != nil { fmt.Println(err) } diff --git a/api/comando_torrents.go b/api/comando_torrents.go index 2e78a84..60e372e 100644 --- a/api/comando_torrents.go +++ b/api/comando_torrents.go @@ -39,6 +39,12 @@ var replacer = strings.NewReplacer( ) func (i *Indexer) HandlerComandoIndexer(w http.ResponseWriter, r *http.Request) { + start := time.Now() + defer func() { + i.metrics.IndexerDuration.WithLabelValues("comando").Observe(time.Since(start).Seconds()) + i.metrics.IndexerRequests.WithLabelValues("comando").Inc() + }() + ctx := r.Context() // supported query params: q, season, episode q := r.URL.Query().Get("q") @@ -55,6 +61,7 @@ func (i *Indexer) HandlerComandoIndexer(w http.ResponseWriter, r *http.Request) if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + i.metrics.IndexerErrors.WithLabelValues("comando").Inc() return } defer resp.Body.Close() @@ -63,6 +70,7 @@ func (i *Indexer) HandlerComandoIndexer(w http.ResponseWriter, r *http.Request) if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) + i.metrics.IndexerErrors.WithLabelValues("comando").Inc() return } @@ -206,7 +214,7 @@ func getTorrents(ctx context.Context, i *Indexer, link string) ([]IndexedTorrent magnetAudio = append(magnetAudio, audio...) } - peer, seed, err := goscrape.GetLeechsAndSeeds(ctx, i.redis, infoHash, trackers) + peer, seed, err := goscrape.GetLeechsAndSeeds(ctx, i.redis, i.metrics, infoHash, trackers) if err != nil { fmt.Println(err) } @@ -365,8 +373,10 @@ func getDocument(ctx context.Context, i *Indexer, link string) (*goquery.Documen // try to get from redis first docCache, err := i.redis.Get(ctx, link) if err == nil { + i.metrics.CacheHits.WithLabelValues("document_body").Inc() return goquery.NewDocumentFromReader(ioutil.NopCloser(bytes.NewReader(docCache))) } + defer i.metrics.CacheMisses.WithLabelValues("document_body").Inc() resp, err := http.Get(link) if err != nil { diff --git a/api/index.go b/api/index.go index 554d196..35ccfbf 100644 --- a/api/index.go +++ b/api/index.go @@ -6,11 +6,13 @@ import ( "time" "github.com/felipemarinho97/torrent-indexer/cache" + "github.com/felipemarinho97/torrent-indexer/monitoring" "github.com/felipemarinho97/torrent-indexer/schema" ) type Indexer struct { - redis *cache.Redis + redis *cache.Redis + metrics *monitoring.Metrics } type IndexerMeta struct { @@ -39,9 +41,10 @@ type IndexedTorrent struct { SeedCount int `json:"seed_count"` } -func NewIndexers(redis *cache.Redis) *Indexer { +func NewIndexers(redis *cache.Redis, metrics *monitoring.Metrics) *Indexer { return &Indexer{ - redis: redis, + redis: redis, + metrics: metrics, } } diff --git a/go.mod b/go.mod index 48a4c82..dcdbe58 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,23 @@ module github.com/felipemarinho97/torrent-indexer -go 1.18 +go 1.21 -require github.com/redis/go-redis/v9 v9.2.0 +require github.com/redis/go-redis/v9 v9.5.1 require ( - github.com/andybalholm/cascadia v1.3.1 // indirect + github.com/andybalholm/cascadia v1.3.2 // indirect + github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - golang.org/x/net v0.7.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.50.0 // indirect + github.com/prometheus/procfs v0.13.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect ) -require github.com/PuerkitoBio/goquery v1.8.1 +require ( + github.com/PuerkitoBio/goquery v1.9.1 + github.com/prometheus/client_golang v1.19.0 +) diff --git a/go.sum b/go.sum index e650659..3d8faca 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,9 @@ -github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= -github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= -github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= -github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI= +github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY= +github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= +github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +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/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -10,36 +12,55 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/redis/go-redis/v9 v9.2.0 h1:zwMdX0A4eVzse46YN18QhuDiM4uf3JmkOB4VZrdt5uI= -github.com/redis/go-redis/v9 v9.2.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ= +github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ= +github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= +github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= +github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= +github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/main.go b/main.go index 3ee9575..94e32c8 100644 --- a/main.go +++ b/main.go @@ -5,17 +5,33 @@ import ( handler "github.com/felipemarinho97/torrent-indexer/api" "github.com/felipemarinho97/torrent-indexer/cache" + "github.com/felipemarinho97/torrent-indexer/monitoring" + "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { redis := cache.NewRedis() - indexers := handler.NewIndexers(redis) + metrics := monitoring.NewMetrics() + metrics.Register() + indexers := handler.NewIndexers(redis, metrics) - http.HandleFunc("/", handler.HandlerIndex) - http.HandleFunc("/indexers/comando_torrents", indexers.HandlerComandoIndexer) - http.HandleFunc("/indexers/bludv", indexers.HandlerBluDVIndexer) + indexerMux := http.NewServeMux() + metricsMux := http.NewServeMux() - err := http.ListenAndServe(":7006", nil) + indexerMux.HandleFunc("/", handler.HandlerIndex) + indexerMux.HandleFunc("/indexers/comando_torrents", indexers.HandlerComandoIndexer) + indexerMux.HandleFunc("/indexers/bludv", indexers.HandlerBluDVIndexer) + + metricsMux.Handle("/metrics", promhttp.Handler()) + + go func() { + err := http.ListenAndServe(":8081", metricsMux) + if err != nil { + panic(err) + } + }() + + err := http.ListenAndServe(":7006", indexerMux) if err != nil { panic(err) } diff --git a/monitoring/prometheus.go b/monitoring/prometheus.go new file mode 100644 index 0000000..74bd5fe --- /dev/null +++ b/monitoring/prometheus.go @@ -0,0 +1,47 @@ +package monitoring + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +type Metrics struct { + IndexerDuration *prometheus.HistogramVec + IndexerErrors *prometheus.CounterVec + IndexerRequests *prometheus.CounterVec + CacheHits *prometheus.CounterVec + CacheMisses *prometheus.CounterVec +} + +func NewMetrics() *Metrics { + return &Metrics{ + IndexerDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "indexer_duration_seconds", + Help: "Duration of indexer requests", + Buckets: []float64{0.1, 0.5, 1, 2, 5, 10, 20, 30}, + }, []string{"indexer"}), + IndexerErrors: prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "indexer_errors_total", + Help: "Number of indexer errors", + }, []string{"indexer"}), + IndexerRequests: prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "indexer_requests_total", + Help: "Number of indexer requests", + }, []string{"indexer"}), + CacheHits: prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "cache_hits_total", + Help: "Number of cache hits", + }, []string{"cache"}), + CacheMisses: prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "cache_misses_total", + Help: "Number of cache misses", + }, []string{"cache"}), + } +} + +func (m *Metrics) Register() { + prometheus.MustRegister(m.IndexerDuration) + prometheus.MustRegister(m.IndexerErrors) + prometheus.MustRegister(m.IndexerRequests) + prometheus.MustRegister(m.CacheHits) + prometheus.MustRegister(m.CacheMisses) +} diff --git a/scrape/info.go b/scrape/info.go index 87f2092..2d179b4 100644 --- a/scrape/info.go +++ b/scrape/info.go @@ -7,6 +7,7 @@ import ( "time" "github.com/felipemarinho97/torrent-indexer/cache" + "github.com/felipemarinho97/torrent-indexer/monitoring" ) type peers struct { @@ -44,11 +45,13 @@ func setPeersToCache(ctx context.Context, r *cache.Redis, infoHash string, peer, return nil } -func GetLeechsAndSeeds(ctx context.Context, r *cache.Redis, infoHash string, trackers []string) (int, int, error) { +func GetLeechsAndSeeds(ctx context.Context, r *cache.Redis, m *monitoring.Metrics, infoHash string, trackers []string) (int, int, error) { leech, seed, err := getPeersFromCache(ctx, r, infoHash) if err != nil { + m.CacheHits.WithLabelValues("peers").Inc() fmt.Println("unable to get peers from cache for infohash:", infoHash) } else { + m.CacheMisses.WithLabelValues("peers").Inc() fmt.Println("get from cache> leech:", leech, "seed:", seed) return leech, seed, nil }