Chore: Add html_handler_requests metric (#113991)
* Chore: Add html_handler_requests metric for tracking requests handled by index.go * make a member of HttpServer * make it a histogram instead * update test
This commit is contained in:
+30
-20
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics/metricutil"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
@@ -202,26 +203,27 @@ type HTTPServer struct {
|
||||
pluginsCDNService *pluginscdn.Service
|
||||
managedPluginsService managedplugins.Manager
|
||||
|
||||
userService user.Service
|
||||
tempUserService tempUser.Service
|
||||
loginAttemptService loginAttempt.Service
|
||||
orgService org.Service
|
||||
orgDeletionService org.DeletionService
|
||||
TeamService team.Service
|
||||
accesscontrolService accesscontrol.Service
|
||||
annotationsRepo annotations.Repository
|
||||
tagService tag.Service
|
||||
oauthTokenService oauthtoken.OAuthTokenService
|
||||
statsService stats.Service
|
||||
authnService authn.Service
|
||||
starApi *starApi.API
|
||||
promRegister prometheus.Registerer
|
||||
promGatherer prometheus.Gatherer
|
||||
clientConfigProvider grafanaapiserver.DirectRestConfigProvider
|
||||
namespacer request.NamespaceMapper
|
||||
anonService anonymous.Service
|
||||
userVerifier user.Verifier
|
||||
tlsCerts TLSCerts
|
||||
userService user.Service
|
||||
tempUserService tempUser.Service
|
||||
loginAttemptService loginAttempt.Service
|
||||
orgService org.Service
|
||||
orgDeletionService org.DeletionService
|
||||
TeamService team.Service
|
||||
accesscontrolService accesscontrol.Service
|
||||
annotationsRepo annotations.Repository
|
||||
tagService tag.Service
|
||||
oauthTokenService oauthtoken.OAuthTokenService
|
||||
statsService stats.Service
|
||||
authnService authn.Service
|
||||
starApi *starApi.API
|
||||
promRegister prometheus.Registerer
|
||||
promGatherer prometheus.Gatherer
|
||||
clientConfigProvider grafanaapiserver.DirectRestConfigProvider
|
||||
namespacer request.NamespaceMapper
|
||||
anonService anonymous.Service
|
||||
userVerifier user.Verifier
|
||||
tlsCerts TLSCerts
|
||||
htmlHandlerRequestsDuration *prometheus.HistogramVec
|
||||
}
|
||||
|
||||
type TLSCerts struct {
|
||||
@@ -375,7 +377,15 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
namespacer: request.GetNamespaceMapper(cfg),
|
||||
anonService: anonService,
|
||||
userVerifier: userVerifier,
|
||||
htmlHandlerRequestsDuration: metricutil.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: "grafana",
|
||||
Name: "html_handler_requests_duration_seconds",
|
||||
Help: "Duration of requests handled by the index.go HTML handler",
|
||||
}, []string{"handler"}),
|
||||
}
|
||||
|
||||
promRegister.MustRegister(hs.htmlHandlerRequestsDuration)
|
||||
|
||||
if hs.Listener != nil {
|
||||
hs.log.Debug("Using provided listener")
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/webassets"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics/metricutil"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
@@ -272,6 +274,11 @@ func (hs *HTTPServer) Index(c *contextmodel.ReqContext) {
|
||||
c, span := hs.injectSpan(c, "api.Index")
|
||||
defer span.End()
|
||||
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
metricutil.ObserveWithExemplar(c.Req.Context(), hs.htmlHandlerRequestsDuration.WithLabelValues("index"), time.Since(start).Seconds())
|
||||
}()
|
||||
|
||||
data, err := hs.setIndexViewData(c)
|
||||
if err != nil {
|
||||
c.Handle(hs.Cfg, http.StatusInternalServerError, "Failed to get settings", err)
|
||||
@@ -286,6 +293,11 @@ func (hs *HTTPServer) NotFoundHandler(c *contextmodel.ReqContext) {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
metricutil.ObserveWithExemplar(c.Req.Context(), hs.htmlHandlerRequestsDuration.WithLabelValues("not_found"), time.Since(start).Seconds())
|
||||
}()
|
||||
|
||||
data, err := hs.setIndexViewData(c)
|
||||
if err != nil {
|
||||
c.Handle(hs.Cfg, http.StatusInternalServerError, "Failed to get settings", err)
|
||||
|
||||
@@ -9,11 +9,13 @@ import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/errutil"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics/metricutil"
|
||||
"github.com/grafana/grafana/pkg/infra/network"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/middleware/cookies"
|
||||
@@ -101,6 +103,10 @@ func (hs *HTTPServer) LoginView(c *contextmodel.ReqContext) {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
metricutil.ObserveWithExemplar(c.Req.Context(), hs.htmlHandlerRequestsDuration.WithLabelValues("login"), time.Since(start).Seconds())
|
||||
}()
|
||||
viewData, err := setIndexViewData(hs, c)
|
||||
if err != nil {
|
||||
c.Handle(hs.Cfg, http.StatusInternalServerError, "Failed to get settings", err)
|
||||
|
||||
+68
-36
@@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -22,6 +23,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics/metricutil"
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
"github.com/grafana/grafana/pkg/models/usertoken"
|
||||
"github.com/grafana/grafana/pkg/services/auth/authtest"
|
||||
@@ -44,6 +46,18 @@ import (
|
||||
|
||||
const loginCookieName = "grafana_session"
|
||||
|
||||
// setupHTMLHandlerMetrics creates and registers the prometheus metrics needed for HTTPServer tests
|
||||
// that call methods using htmlHandlerRequestsDuration (LoginView, Index, NotFoundHandler)
|
||||
func setupHTMLHandlerMetrics() (prometheus.Registerer, *prometheus.HistogramVec) {
|
||||
promRegister := prometheus.NewRegistry()
|
||||
htmlHandlerRequestsDuration := metricutil.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: "grafana",
|
||||
Name: "html_handler_requests_duration_seconds",
|
||||
}, []string{"handler"})
|
||||
promRegister.MustRegister(htmlHandlerRequestsDuration)
|
||||
return promRegister, htmlHandlerRequestsDuration
|
||||
}
|
||||
|
||||
func fakeSetIndexViewData(t *testing.T) {
|
||||
origSetIndexViewData := setIndexViewData
|
||||
t.Cleanup(func() {
|
||||
@@ -109,13 +123,16 @@ func TestLoginErrorCookieAPIEndpoint(t *testing.T) {
|
||||
sc := setupScenarioContext(t, "/login")
|
||||
cfg := setting.NewCfg()
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
promRegister, htmlHandlerRequestsDuration := setupHTMLHandlerMetrics()
|
||||
hs := &HTTPServer{
|
||||
Cfg: cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
SocialService: &mockSocialService{},
|
||||
SecretsService: secretsService,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
Cfg: cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
SocialService: &mockSocialService{},
|
||||
SecretsService: secretsService,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
promRegister: promRegister,
|
||||
htmlHandlerRequestsDuration: htmlHandlerRequestsDuration,
|
||||
}
|
||||
|
||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
||||
@@ -156,13 +173,16 @@ func TestLoginViewRedirect(t *testing.T) {
|
||||
fakeViewIndex(t)
|
||||
sc := setupScenarioContext(t, "/login")
|
||||
cfg := setting.NewCfg()
|
||||
promRegister, htmlHandlerRequestsDuration := setupHTMLHandlerMetrics()
|
||||
hs := &HTTPServer{
|
||||
Cfg: cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
SocialService: &mockSocialService{},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
log: log.NewNopLogger(),
|
||||
Cfg: cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
SocialService: &mockSocialService{},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
log: log.NewNopLogger(),
|
||||
promRegister: promRegister,
|
||||
htmlHandlerRequestsDuration: htmlHandlerRequestsDuration,
|
||||
}
|
||||
hs.Cfg.CookieSecure = true
|
||||
|
||||
@@ -492,13 +512,16 @@ func TestLoginOAuthRedirect(t *testing.T) {
|
||||
},
|
||||
oAuthInfos: oAuthInfos,
|
||||
}
|
||||
promRegister, htmlHandlerRequestsDuration := setupHTMLHandlerMetrics()
|
||||
hs := &HTTPServer{
|
||||
authnService: &authntest.FakeService{},
|
||||
Cfg: cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
SocialService: mock,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
authnService: &authntest.FakeService{},
|
||||
Cfg: cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
SocialService: mock,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
promRegister: promRegister,
|
||||
htmlHandlerRequestsDuration: htmlHandlerRequestsDuration,
|
||||
}
|
||||
|
||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
||||
@@ -521,11 +544,14 @@ func TestLoginInternal(t *testing.T) {
|
||||
|
||||
fakeViewIndex(t)
|
||||
sc := setupScenarioContext(t, "/login")
|
||||
promRegister, htmlHandlerRequestsDuration := setupHTMLHandlerMetrics()
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
License: &licensing.OSSLicensingService{},
|
||||
log: log.New("test"),
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
Cfg: setting.NewCfg(),
|
||||
License: &licensing.OSSLicensingService{},
|
||||
log: log.New("test"),
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
promRegister: promRegister,
|
||||
htmlHandlerRequestsDuration: htmlHandlerRequestsDuration,
|
||||
}
|
||||
|
||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
||||
@@ -583,14 +609,17 @@ func TestAuthProxyLoginWithEnableLoginTokenAndEnabledOauthAutoLogin(t *testing.T
|
||||
sc := setupScenarioContext(t, "/login")
|
||||
sc.cfg.LoginCookieName = loginCookieName
|
||||
sc.cfg.OAuthAutoLogin = true
|
||||
promRegister, htmlHandlerRequestsDuration := setupHTMLHandlerMetrics()
|
||||
hs := &HTTPServer{
|
||||
Cfg: sc.cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: sc.cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
AuthTokenService: authtest.NewFakeUserAuthTokenService(),
|
||||
log: log.New("hello"),
|
||||
SocialService: mock,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
Cfg: sc.cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: sc.cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
AuthTokenService: authtest.NewFakeUserAuthTokenService(),
|
||||
log: log.New("hello"),
|
||||
SocialService: mock,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
promRegister: promRegister,
|
||||
htmlHandlerRequestsDuration: htmlHandlerRequestsDuration,
|
||||
}
|
||||
|
||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
||||
@@ -623,14 +652,17 @@ func setupAuthProxyLoginTest(t *testing.T, enableLoginToken bool) *scenarioConte
|
||||
|
||||
sc := setupScenarioContext(t, "/login")
|
||||
sc.cfg.LoginCookieName = loginCookieName
|
||||
promRegister, htmlHandlerRequestsDuration := setupHTMLHandlerMetrics()
|
||||
hs := &HTTPServer{
|
||||
Cfg: sc.cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: sc.cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
AuthTokenService: authtest.NewFakeUserAuthTokenService(),
|
||||
log: log.New("hello"),
|
||||
SocialService: &mockSocialService{},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
Cfg: sc.cfg,
|
||||
SettingsProvider: &setting.OSSImpl{Cfg: sc.cfg},
|
||||
License: &licensing.OSSLicensingService{},
|
||||
AuthTokenService: authtest.NewFakeUserAuthTokenService(),
|
||||
log: log.New("hello"),
|
||||
SocialService: &mockSocialService{},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
promRegister: promRegister,
|
||||
htmlHandlerRequestsDuration: htmlHandlerRequestsDuration,
|
||||
}
|
||||
|
||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
||||
|
||||
Reference in New Issue
Block a user