Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a way to add custom labels to the standard prometheus metrics? #85

Closed
3 tasks
a6dang opened this issue Jan 11, 2023 · 1 comment
Closed
3 tasks

Comments

@a6dang
Copy link

a6dang commented Jan 11, 2023

Issue Description

Hello. I'm new to Go. I was wondering if there is a way to add custom labels to the standard prometheus metrics that are collected by the echo middleware.

I have tried to do add middleware using the CurryWith(Labels) API on respective MetricVec classes.

I run into this error when the middleware gets run.

2 unknown label(s) found during currying

I've attached some sample code of what I'm trying to accomplish. My end goal is adding my two custom labels to all metrics being generated.

Appreciate the time.

Checklist

  • Dependencies installed
  • No typos
  • Searched existing issues and docs

Expected behaviour

Actual behaviour

Steps to reproduce

Working code to debug

var errorCount = &prometheus.Metric{
	ID:          "err_cnt",
	Name:        "error_count",
	Description: "Error count",
	Type:        "counter_vec",
	Args:        []string{"code", "method", "host", "url", "myLabel1", "myLabel2"},
}

type MyMetrics struct {
	errorCount      *prometheus.Metric // my own custom defined metric
	standardMetrics []*prometheus.Metric // populated with prometheus.MetricsList
}

func (metrics *MyMetrics) addLabels() {
	labels := ptheus.Labels{"myLabel1": "value1", "myLabel2": "value2"}
	errorCount.MetricCollector.(*ptheus.CounterVec).With(labels)

        // attempt to loop over standard metrics and add my custom labels
	for i := 0; i < len(metrics.standardMetrics); i++ {
		standardMetric := metrics.standardMetrics[i]
		if standardMetric.MetricCollector != nil {
			switch standardMetric.Type {
			case "histogram_vec":
				curriedMetric, err := standardMetric.MetricCollector.(*ptheus.GaugeVec).CurryWith(labels)
				if curriedMetric != nil {
				 	standardMetric.MetricCollector = curriedMetric
				} else if err != nil {
				 	fmt.Printf("Histogram Error %s with metric named %s\n", err.Error(), standardMetric.Name)
				}
			case "counter_vec":
				standardMetric.MetricCollector.(*ptheus.CounterVec).With(labels)
				curriedMetric, err := standardMetric.MetricCollector.(*ptheus.GaugeVec).CurryWith(labels)
				curriedMetric, err := standardMetric.MetricCollector.(*ptheus.CounterVec).CurryWith(labels)
				if curriedMetric != nil {
				 	standardMetric.MetricCollector = curriedMetric
				} else if err != nil {
				 	fmt.Printf("Counter Error %s\n", err.Error())
			        }
		.....
			fmt.Printf("Type of %s metric is %s\n", standardMetric.Name, standardMetric.Type)
		}
	}
}

// middleware function that I've attached to my echo instance
func (metrics *MyMetrics) customMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		metrics.addLabels()
		return next(c)
	}
}

func createMetricsServer(e *echo.Echo, port string) error {
	echoPrometheus := echo.New()
	echoPrometheus.HideBanner = true
	echoPrometheus.HidePort = true

	customMetricsList := []*prometheus.Metric{
		errorCount,
	}

	prom := prometheus.NewPrometheus("myService", urlSkipper, customMetricsList)

	myMetrics := &MyMetrics{
		errorCount:      errorCount,
		standardMetrics: prom.MetricsList,
	}

	// Middleware that replaces the values of uuids in pathnames for more consistent triaging and metrics
	prom.RequestCounterURLLabelMappingFunc = func(c echo.Context) string {
		url := c.Request().URL.Path
		for _, p := range c.ParamNames() {
			if p == "uuid" {
				url = strings.Replace(url, c.Param(p), ":uuid", 1)
			} else if p == "attachmentUuid" {
				url = strings.Replace(url, c.Param(p), ":attachmentUuid", 1)
			}
		}
		return url
	}

	e.Use(myMetrics.customMiddleware)

	e.Use(prom.HandlerFunc)
	prom.SetMetricsPath(echoPrometheus)

	// Starts a new goroutine to manage the metrics endpoint
	go func() { echoPrometheus.Logger.Fatal(echoPrometheus.Start(port)) }()

	log.Info().Msgf("Prometheus Metrics established started on localhost%s", port)
	return nil
}

Version/commit

@aldas
Copy link
Contributor

aldas commented May 23, 2023

closing, new middleware was added with #94

Custom labels can be added like that:

package main

import (
	"errors"
	"github.com/labstack/echo-contrib/echoprometheus"
	"github.com/labstack/echo/v4"
	"log"
	"net/http"
)

func main() {
	e := echo.New()

	// add middleware with custom labels
	e.Use(echoprometheus.NewMiddlewareWithConfig(echoprometheus.MiddlewareConfig{
		LabelFuncs: map[string]echoprometheus.LabelValueFunc{
			"scheme": func(c echo.Context, err error) string { // additional custom label
				return c.Scheme()
			},
			"url": func(c echo.Context, err error) string { // overrides default 'url' label value
				return "x_" + c.Request().URL.Path
			},
			"host": func(c echo.Context, err error) string { // overrides default 'host' label value
				return "y_" + c.Request().Host
			},
		},
	}))
	e.GET("/metrics", echoprometheus.NewHandler()) // register route where metrics can be scraped

	if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
		log.Fatal(err)
	}
}

@aldas aldas closed this as completed May 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants