Zanzana: Add server side metrics (#109923)

* Zanzana: Add metrics to server side

* Zanzana: Collect check duration

* add metrics for other methods
This commit is contained in:
Alexander Zobnin
2025-08-21 15:20:07 +02:00
committed by GitHub
parent 88507add9d
commit 2b254ed623
10 changed files with 70 additions and 16 deletions
+1 -1
View File
@@ -189,7 +189,7 @@ func (s *ModuleServer) Run() error {
})
m.RegisterModule(modules.ZanzanaServer, func() (services.Service, error) {
return authz.ProvideZanzanaService(s.cfg, s.features)
return authz.ProvideZanzanaService(s.cfg, s.features, s.registerer)
})
m.RegisterModule(modules.FrontendServer, func() (services.Service, error) {
+5 -3
View File
@@ -88,7 +88,7 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, tracer tracing.Tracer, features
return nil, fmt.Errorf("failed to start zanzana: %w", err)
}
srv, err := zanzana.NewServer(cfg.ZanzanaServer, openfga, logger, tracer)
srv, err := zanzana.NewServer(cfg.ZanzanaServer, openfga, logger, tracer, reg)
if err != nil {
return nil, fmt.Errorf("failed to start zanzana: %w", err)
}
@@ -127,11 +127,12 @@ type ZanzanaService interface {
var _ ZanzanaService = (*Zanzana)(nil)
// ProvideZanzanaService is used to register zanzana as a module so we can run it seperatly from grafana.
func ProvideZanzanaService(cfg *setting.Cfg, features featuremgmt.FeatureToggles) (*Zanzana, error) {
func ProvideZanzanaService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, reg prometheus.Registerer) (*Zanzana, error) {
s := &Zanzana{
cfg: cfg,
features: features,
logger: log.New("zanzana.server"),
reg: reg,
}
s.BasicService = services.NewBasicService(s.start, s.running, s.stopping).WithName("zanzana")
@@ -147,6 +148,7 @@ type Zanzana struct {
logger log.Logger
handle grpcserver.Provider
features featuremgmt.FeatureToggles
reg prometheus.Registerer
}
func (z *Zanzana) start(ctx context.Context) error {
@@ -172,7 +174,7 @@ func (z *Zanzana) start(ctx context.Context) error {
return fmt.Errorf("failed to start zanzana: %w", err)
}
zanzanaServer, err := zanzana.NewServer(z.cfg.ZanzanaServer, openfgaServer, z.logger, tracer)
zanzanaServer, err := zanzana.NewServer(z.cfg.ZanzanaServer, openfgaServer, z.logger, tracer, z.reg)
if err != nil {
return fmt.Errorf("failed to start zanzana: %w", err)
}
+3 -2
View File
@@ -5,6 +5,7 @@ import (
openfgaserver "github.com/openfga/openfga/pkg/server"
openfgastorage "github.com/openfga/openfga/pkg/storage"
"github.com/prometheus/client_golang/prometheus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
@@ -13,8 +14,8 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
func NewServer(cfg setting.ZanzanaServerSettings, openfga server.OpenFGAServer, logger log.Logger, tracer tracing.Tracer) (*server.Server, error) {
return server.NewServer(cfg, openfga, logger, tracer)
func NewServer(cfg setting.ZanzanaServerSettings, openfga server.OpenFGAServer, logger log.Logger, tracer tracing.Tracer, reg prometheus.Registerer) (*server.Server, error) {
return server.NewServer(cfg, openfga, logger, tracer, reg)
}
func NewHealthServer(target server.DiagnosticServer) *server.HealthServer {
@@ -0,0 +1,31 @@
package server
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
const (
metricsNamespace = "iam"
metricsSubSystem = "authz_zanzana_server"
)
type metrics struct {
// requestDurationSeconds is a summary for zanzana server request duration
requestDurationSeconds *prometheus.HistogramVec
}
func newZanzanaServerMetrics(reg prometheus.Registerer) *metrics {
return &metrics{
requestDurationSeconds: promauto.With(reg).NewHistogramVec(
prometheus.HistogramOpts{
Name: "request_duration_seconds",
Help: "Histogram for zanzana server request duration",
Namespace: metricsNamespace,
Subsystem: metricsSubSystem,
Buckets: prometheus.ExponentialBuckets(0.00001, 4, 10),
},
[]string{"method", "namespace"},
),
}
}
+6 -3
View File
@@ -9,6 +9,7 @@ import (
"github.com/fullstorydev/grpchan/inprocgrpc"
authzv1 "github.com/grafana/authlib/authz/proto/v1"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"github.com/prometheus/client_golang/prometheus"
"google.golang.org/protobuf/types/known/wrapperspb"
dashboardV2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
@@ -43,8 +44,9 @@ type Server struct {
storesMU *sync.Mutex
cache *localcache.CacheService
logger log.Logger
tracer tracing.Tracer
logger log.Logger
tracer tracing.Tracer
metrics *metrics
}
type storeInfo struct {
@@ -52,7 +54,7 @@ type storeInfo struct {
ModelID string
}
func NewServer(cfg setting.ZanzanaServerSettings, openfga OpenFGAServer, logger log.Logger, tracer tracing.Tracer) (*Server, error) {
func NewServer(cfg setting.ZanzanaServerSettings, openfga OpenFGAServer, logger log.Logger, tracer tracing.Tracer, reg prometheus.Registerer) (*Server, error) {
channel := &inprocgrpc.Channel{}
openfgav1.RegisterOpenFGAServiceServer(channel, openfga)
openFGAClient := openfgav1.NewOpenFGAServiceClient(channel)
@@ -66,6 +68,7 @@ func NewServer(cfg setting.ZanzanaServerSettings, openfga OpenFGAServer, logger
cache: localcache.New(cfg.CacheSettings.CheckQueryCacheTTL, cacheCleanInterval),
logger: logger,
tracer: tracer,
metrics: newZanzanaServerMetrics(reg),
}
return s, nil
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"time"
authzv1 "github.com/grafana/authlib/authz/proto/v1"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
@@ -16,9 +17,11 @@ import (
func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.CheckResponse, error) {
ctx, span := s.tracer.Start(ctx, "server.Check")
defer span.End()
span.SetAttributes(
attribute.String("namespace", r.GetNamespace()),
)
span.SetAttributes(attribute.String("namespace", r.GetNamespace()))
defer func(t time.Time) {
s.metrics.requestDurationSeconds.WithLabelValues("server.Check", r.GetNamespace()).Observe(time.Since(t).Seconds())
}(time.Now())
res, err := s.check(ctx, r)
if err != nil {
@@ -8,6 +8,7 @@ import (
"hash/fnv"
"io"
"strings"
"time"
authzv1 "github.com/grafana/authlib/authz/proto/v1"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
@@ -19,9 +20,11 @@ import (
func (s *Server) List(ctx context.Context, r *authzv1.ListRequest) (*authzv1.ListResponse, error) {
ctx, span := s.tracer.Start(ctx, "server.List")
defer span.End()
span.SetAttributes(
attribute.String("namespace", r.GetNamespace()),
)
span.SetAttributes(attribute.String("namespace", r.GetNamespace()))
defer func(t time.Time) {
s.metrics.requestDurationSeconds.WithLabelValues("server.List", r.GetNamespace()).Observe(time.Since(t).Seconds())
}(time.Now())
res, err := s.list(ctx, r)
if err != nil {
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"time"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
@@ -15,6 +16,10 @@ func (s *Server) Read(ctx context.Context, req *authzextv1.ReadRequest) (*authze
ctx, span := s.tracer.Start(ctx, "server.Read")
defer span.End()
defer func(t time.Time) {
s.metrics.requestDurationSeconds.WithLabelValues("server.Read", req.GetNamespace()).Observe(time.Since(t).Seconds())
}(time.Now())
res, err := s.read(ctx, req)
if err != nil {
s.logger.Error("failed to perform read request", "error", err, "namespace", req.GetNamespace())
@@ -5,6 +5,7 @@ import (
"testing"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
authnlib "github.com/grafana/authlib/authn"
@@ -92,7 +93,7 @@ func setup(t *testing.T, testDB db.DB, cfg *setting.Cfg) *Server {
openfga, err := NewOpenFGAServer(cfg.ZanzanaServer, store)
require.NoError(t, err)
srv, err := NewServer(cfg.ZanzanaServer, openfga, log.NewNopLogger(), tracing.NewNoopTracerService())
srv, err := NewServer(cfg.ZanzanaServer, openfga, log.NewNopLogger(), tracing.NewNoopTracerService(), prometheus.NewRegistry())
require.NoError(t, err)
storeInf, err := srv.getStoreInfo(context.Background(), namespace)
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"time"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
@@ -15,6 +16,10 @@ func (s *Server) Write(ctx context.Context, req *authzextv1.WriteRequest) (*auth
ctx, span := s.tracer.Start(ctx, "server.Write")
defer span.End()
defer func(t time.Time) {
s.metrics.requestDurationSeconds.WithLabelValues("server.Write", req.GetNamespace()).Observe(time.Since(t).Seconds())
}(time.Now())
res, err := s.write(ctx, req)
if err != nil {
s.logger.Error("failed to perform write request", "error", err, "namespace", req.GetNamespace())