From a5fe157e619193b54cfffbd291ffecd3b95d956d Mon Sep 17 00:00:00 2001 From: "Yoshiaki Ueda (bootjp)" Date: Sat, 30 Aug 2025 04:23:27 +0900 Subject: [PATCH] Register Prometheus counter --- adapter/grpc.go | 7 +++++++ adapter/metrics.go | 14 ++++++++++++++ adapter/redis.go | 5 +++++ go.mod | 6 ++++++ go.sum | 11 +++++++++++ main.go | 16 +++++++++++++++- 6 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 adapter/metrics.go diff --git a/adapter/grpc.go b/adapter/grpc.go index d056645..f25daa5 100644 --- a/adapter/grpc.go +++ b/adapter/grpc.go @@ -38,6 +38,7 @@ func NewGRPCServer(store store.ScanStore, coordinate *kv.Coordinate) *GRPCServer } func (r GRPCServer) RawGet(ctx context.Context, req *pb.RawGetRequest) (*pb.RawGetResponse, error) { + opCounter.WithLabelValues("raw_get", "grpc").Inc() v, err := r.store.Get(ctx, req.Key) if err != nil { switch { @@ -60,6 +61,7 @@ func (r GRPCServer) RawGet(ctx context.Context, req *pb.RawGetRequest) (*pb.RawG } func (r GRPCServer) RawPut(_ context.Context, req *pb.RawPutRequest) (*pb.RawPutResponse, error) { + opCounter.WithLabelValues("raw_put", "grpc").Inc() m, err := r.grpcTranscoder.RawPutToRequest(req) if err != nil { return nil, errors.WithStack(err) @@ -80,6 +82,7 @@ func (r GRPCServer) RawPut(_ context.Context, req *pb.RawPutRequest) (*pb.RawPut } func (r GRPCServer) RawDelete(ctx context.Context, req *pb.RawDeleteRequest) (*pb.RawDeleteResponse, error) { + opCounter.WithLabelValues("raw_delete", "grpc").Inc() m, err := r.grpcTranscoder.RawDeleteToRequest(req) if err != nil { return nil, errors.WithStack(err) @@ -112,6 +115,7 @@ func (r GRPCServer) Rollback(ctx context.Context, req *pb.RollbackRequest) (*pb. } func (r GRPCServer) Put(ctx context.Context, req *pb.PutRequest) (*pb.PutResponse, error) { + opCounter.WithLabelValues("put", "grpc").Inc() reqs, err := r.grpcTranscoder.TransactionalPutToRequests(req) if err != nil { return nil, errors.WithStack(err) @@ -133,6 +137,7 @@ func (r GRPCServer) Put(ctx context.Context, req *pb.PutRequest) (*pb.PutRespons } func (r GRPCServer) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { + opCounter.WithLabelValues("get", "grpc").Inc() h := murmur3.New64() if _, err := h.Write(req.Key); err != nil { return nil, errors.WithStack(err) @@ -153,6 +158,7 @@ func (r GRPCServer) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetRespons } func (r GRPCServer) Delete(ctx context.Context, req *pb.DeleteRequest) (*pb.DeleteResponse, error) { + opCounter.WithLabelValues("delete", "grpc").Inc() reqs, err := r.grpcTranscoder.TransactionalDeleteToRequests(req) if err != nil { return nil, errors.WithStack(err) @@ -174,6 +180,7 @@ func (r GRPCServer) Delete(ctx context.Context, req *pb.DeleteRequest) (*pb.Dele } func (r GRPCServer) Scan(ctx context.Context, req *pb.ScanRequest) (*pb.ScanResponse, error) { + opCounter.WithLabelValues("scan", "grpc").Inc() limit, err := internal.Uint64ToInt(req.Limit) if err != nil { return &pb.ScanResponse{ diff --git a/adapter/metrics.go b/adapter/metrics.go new file mode 100644 index 0000000..0fc812e --- /dev/null +++ b/adapter/metrics.go @@ -0,0 +1,14 @@ +package adapter + +import "github.com/prometheus/client_golang/prometheus" + +var ( + opCounter = prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "elastickv_operations_total", + Help: "Total number of KV operations", + }, []string{"op", "client"}) +) + +func init() { + prometheus.MustRegister(opCounter) +} diff --git a/adapter/redis.go b/adapter/redis.go index 481fd06..0b03aab 100644 --- a/adapter/redis.go +++ b/adapter/redis.go @@ -97,6 +97,7 @@ func (r *RedisServer) ping(conn redcon.Conn, _ redcon.Command) { } func (r *RedisServer) set(conn redcon.Conn, cmd redcon.Command) { + opCounter.WithLabelValues("set", "redis").Inc() res, err := r.redisTranscoder.SetToRequest(cmd.Args[1], cmd.Args[2]) if err != nil { conn.WriteError(err.Error()) @@ -113,6 +114,7 @@ func (r *RedisServer) set(conn redcon.Conn, cmd redcon.Command) { } func (r *RedisServer) get(conn redcon.Conn, cmd redcon.Command) { + opCounter.WithLabelValues("get", "redis").Inc() v, err := r.store.Get(context.Background(), cmd.Args[1]) if err != nil { conn.WriteNull() @@ -123,6 +125,7 @@ func (r *RedisServer) get(conn redcon.Conn, cmd redcon.Command) { } func (r *RedisServer) del(conn redcon.Conn, cmd redcon.Command) { + opCounter.WithLabelValues("del", "redis").Inc() res, err := r.redisTranscoder.DeleteToRequest(cmd.Args[1]) if err != nil { conn.WriteError(err.Error()) @@ -139,6 +142,7 @@ func (r *RedisServer) del(conn redcon.Conn, cmd redcon.Command) { } func (r *RedisServer) exists(conn redcon.Conn, cmd redcon.Command) { + opCounter.WithLabelValues("exists", "redis").Inc() ok, err := r.store.Exists(context.Background(), cmd.Args[1]) if err != nil { conn.WriteError(err.Error()) @@ -153,6 +157,7 @@ func (r *RedisServer) exists(conn redcon.Conn, cmd redcon.Command) { } func (r *RedisServer) keys(conn redcon.Conn, cmd redcon.Command) { + opCounter.WithLabelValues("keys", "redis").Inc() // If an asterisk (*) is not included, the match will be exact, // so check if the key exists. diff --git a/go.mod b/go.mod index 9daf3e0..a4d3401 100644 --- a/go.mod +++ b/go.mod @@ -15,10 +15,12 @@ require ( github.com/aws/aws-sdk-go-v2/service/dynamodb v1.50.0 github.com/cockroachdb/errors v1.12.0 github.com/emirpasic/gods v1.18.1 + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/raft v1.7.3 github.com/hashicorp/raft-boltdb/v2 v2.3.1 github.com/pkg/errors v0.9.1 + github.com/prometheus/client_golang v1.19.0 github.com/redis/go-redis/v9 v9.12.1 github.com/spaolacci/murmur3 v1.1.0 github.com/stretchr/testify v1.11.1 @@ -42,6 +44,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.1 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.38.1 // indirect github.com/aws/smithy-go v1.23.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect github.com/boltdb/bolt v1.3.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect @@ -63,6 +66,9 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/tidwall/btree v1.1.0 // indirect github.com/tidwall/match v1.1.1 // indirect diff --git a/go.sum b/go.sum index 2dcafc1..66aac53 100644 --- a/go.sum +++ b/go.sum @@ -55,6 +55,7 @@ github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= @@ -153,6 +154,8 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -248,21 +251,29 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +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.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg= github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= diff --git a/main.go b/main.go index f3ff849..7248320 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "net" + "net/http" "os" "path/filepath" @@ -17,8 +18,10 @@ import ( pb "github.com/bootjp/elastickv/proto" "github.com/bootjp/elastickv/store" "github.com/cockroachdb/errors" + "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/hashicorp/raft" boltdb "github.com/hashicorp/raft-boltdb/v2" + "github.com/prometheus/client_golang/prometheus/promhttp" "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -31,6 +34,7 @@ var ( raftId = flag.String("raft_id", "", "Node id used by Raft") raftDir = flag.String("raft_data_dir", "data/", "Raft data dir") raftBootstrap = flag.Bool("raft_bootstrap", false, "Whether to bootstrap the Raft cluster") + metricsAddr = flag.String("metrics_address", ":2112", "TCP host+port for Prometheus metrics") ) func main() { @@ -62,7 +66,10 @@ func main() { log.Fatalf("failed to start raft: %v", err) } - gs := grpc.NewServer() + gs := grpc.NewServer( + grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor), + grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), + ) trx := kv.NewTransaction(r) coordinate := kv.NewCoordinator(trx, r) pb.RegisterRawKVServer(gs, adapter.NewGRPCServer(s, coordinate)) @@ -73,12 +80,16 @@ func main() { leaderhealth.Setup(r, gs, []string{"RawKV", "Example"}) raftadmin.Register(gs, r) reflection.Register(gs) + grpc_prometheus.Register(gs) redisL, err := lc.Listen(ctx, "tcp", *redisAddr) if err != nil { log.Fatalf("failed to listen: %v", err) } + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.Handler()) + eg := errgroup.Group{} eg.Go(func() error { return errors.WithStack(gs.Serve(grpcSock)) @@ -86,6 +97,9 @@ func main() { eg.Go(func() error { return errors.WithStack(adapter.NewRedisServer(redisL, s, coordinate).Run()) }) + eg.Go(func() error { + return errors.WithStack(http.ListenAndServe(*metricsAddr, mux)) + }) err = eg.Wait() if err != nil {