diff --git a/go/lib/sciond/BUILD.bazel b/go/lib/sciond/BUILD.bazel index 853c762a49..82ea1a59ab 100644 --- a/go/lib/sciond/BUILD.bazel +++ b/go/lib/sciond/BUILD.bazel @@ -16,6 +16,7 @@ go_library( "//go/lib/hostinfo:go_default_library", "//go/lib/infra/disp:go_default_library", "//go/lib/log:go_default_library", + "//go/lib/sciond/internal/metrics:go_default_library", "//go/lib/serrors:go_default_library", "//go/lib/sock/reliable:go_default_library", "//go/lib/util:go_default_library", diff --git a/go/lib/sciond/internal/metrics/BUILD.bazel b/go/lib/sciond/internal/metrics/BUILD.bazel new file mode 100644 index 0000000000..cd22fc473b --- /dev/null +++ b/go/lib/sciond/internal/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["metrics.go"], + importpath = "github.com/scionproto/scion/go/lib/sciond/internal/metrics", + visibility = ["//go/lib/sciond:__subpackages__"], + deps = [ + "//go/lib/prom:go_default_library", + "@com_github_prometheus_client_golang//prometheus:go_default_library", + ], +) diff --git a/go/lib/sciond/internal/metrics/metrics.go b/go/lib/sciond/internal/metrics/metrics.go new file mode 100644 index 0000000000..4466fc478a --- /dev/null +++ b/go/lib/sciond/internal/metrics/metrics.go @@ -0,0 +1,110 @@ +// Copyright 2019 ETH Zurich +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + + "github.com/scionproto/scion/go/lib/prom" +) + +const ( + // Namespace is the metrics namespace for the SCIOND client API. + Namespace = "lib_sciond" + + subsystemConn = "conn" + subsystemPath = "path" + subsystemASInfo = "as_info" + subsystemIFInfo = "if_info" + subsystemSVCInfo = "service_info" + subsystemRevocation = "revocation" +) + +// Result values +const ( + OkSuccess = prom.Success + ErrTimeout = prom.ErrTimeout + ErrNotClassified = prom.ErrNotClassified +) + +var resultLabel = []string{prom.LabelResult} + +// Metric accessors. +var ( + // PathRequests contains metrics for path requests. + PathRequests = newPathRequest() + // Revocations contains metrics for revocations. + Revocations = newRevocation() + // ASInfos contains metrics for AS info requests. + ASInfos = newASInfoRequest() + // IFInfos contains metrics for IF info requests. + IFInfos = newIFInfo() + // SVCInfos contains metrics for SVC info requests. + SVCInfos = newSVCInfo() + // Conns contains metrics for connections to SCIOND. + Conns = newConn() +) + +// Request is the generic metric for requests. +type Request struct { + count *prometheus.CounterVec +} + +// Inc increases the metric count. The result parameter is used to label the increment. +func (r Request) Inc(result string) { + r.count.WithLabelValues(result).Inc() +} + +func newConn() Request { + return Request{ + count: prom.NewCounterVec(Namespace, subsystemConn, "connections_total", + "The amount of SCIOND connection attempts.", resultLabel), + } +} + +func newPathRequest() Request { + return Request{ + count: prom.NewCounterVec(Namespace, subsystemPath, "requests_total", + "The amount of Path requests sent.", resultLabel), + } +} + +func newRevocation() Request { + return Request{ + count: prom.NewCounterVec(Namespace, subsystemRevocation, "requests_total", + "The amount of Revocation requests sent.", resultLabel), + } +} + +func newASInfoRequest() Request { + return Request{ + count: prom.NewCounterVec(Namespace, subsystemASInfo, "requests_total", + "The amount of AS info requests sent.", resultLabel), + } +} + +func newSVCInfo() Request { + return Request{ + count: prom.NewCounterVec(Namespace, subsystemSVCInfo, "requests_total", + "The amount of SVC info requests sent.", resultLabel), + } +} + +func newIFInfo() Request { + return Request{ + count: prom.NewCounterVec(Namespace, subsystemIFInfo, "requests_total", + "The amount of IF info requests sent.", resultLabel), + } +} diff --git a/go/lib/sciond/sciond.go b/go/lib/sciond/sciond.go index bb715659ac..596ee06a59 100644 --- a/go/lib/sciond/sciond.go +++ b/go/lib/sciond/sciond.go @@ -38,6 +38,7 @@ import ( "github.com/scionproto/scion/go/lib/ctrl/path_mgmt" "github.com/scionproto/scion/go/lib/infra/disp" "github.com/scionproto/scion/go/lib/log" + "github.com/scionproto/scion/go/lib/sciond/internal/metrics" "github.com/scionproto/scion/go/lib/serrors" "github.com/scionproto/scion/go/lib/sock/reliable" "github.com/scionproto/scion/go/proto" @@ -177,12 +178,14 @@ func (c *conn) ctxAwareConnect(ctx context.Context) (*disp.Dispatcher, error) { select { case rValue := <-barrier: + metrics.Conns.Inc(errorToPrometheusLabel(rValue.err)) return rValue.dispatcher, rValue.err case <-ctx.Done(): // In the situation where ConnectTimeout doesn't finish and ctx is Done // via a cancellation function, this may (1) permanently leak a // goroutine, if ctx doesn't have a deadline, or (2) for a long amount // of time, if the deadline is very far into the future. + metrics.Conns.Inc(errorToPrometheusLabel(ctx.Err())) return nil, ctx.Err() } } @@ -192,6 +195,7 @@ func (c *conn) Paths(ctx context.Context, dst, src addr.IA, max uint16, roundTripper, err := c.ctxAwareConnect(ctx) if err != nil { + metrics.PathRequests.Inc(errorToPrometheusLabel(err)) return nil, serrors.Wrap(ErrUnableToConnect, err) } defer roundTripper.Close(ctx) @@ -210,14 +214,17 @@ func (c *conn) Paths(ctx context.Context, dst, src addr.IA, max uint16, nil, ) if err != nil { + metrics.PathRequests.Inc(errorToPrometheusLabel(err)) return nil, serrors.WrapStr("[sciond-API] Failed to get Paths", err) } + metrics.PathRequests.Inc(metrics.OkSuccess) return reply.(*Pld).PathReply, nil } func (c *conn) ASInfo(ctx context.Context, ia addr.IA) (*ASInfoReply, error) { roundTripper, err := c.ctxAwareConnect(ctx) if err != nil { + metrics.ASInfos.Inc(errorToPrometheusLabel(err)) return nil, serrors.Wrap(ErrUnableToConnect, err) } defer roundTripper.Close(ctx) @@ -233,14 +240,17 @@ func (c *conn) ASInfo(ctx context.Context, ia addr.IA) (*ASInfoReply, error) { nil, ) if err != nil { + metrics.ASInfos.Inc(errorToPrometheusLabel(err)) return nil, serrors.WrapStr("[sciond-API] Failed to get ASInfo", err) } + metrics.ASInfos.Inc(metrics.OkSuccess) return pld.(*Pld).AsInfoReply, nil } func (c *conn) IFInfo(ctx context.Context, ifs []common.IFIDType) (*IFInfoReply, error) { roundTripper, err := c.ctxAwareConnect(ctx) if err != nil { + metrics.IFInfos.Inc(errorToPrometheusLabel(err)) return nil, serrors.Wrap(ErrUnableToConnect, err) } defer roundTripper.Close(ctx) @@ -256,8 +266,10 @@ func (c *conn) IFInfo(ctx context.Context, ifs []common.IFIDType) (*IFInfoReply, nil, ) if err != nil { + metrics.IFInfos.Inc(errorToPrometheusLabel(err)) return nil, serrors.WrapStr("[sciond-API] Failed to get IFInfo", err) } + metrics.IFInfos.Inc(metrics.OkSuccess) return pld.(*Pld).IfInfoReply, nil } @@ -266,6 +278,7 @@ func (c *conn) SVCInfo(ctx context.Context, roundTripper, err := c.ctxAwareConnect(ctx) if err != nil { + metrics.SVCInfos.Inc(errorToPrometheusLabel(err)) return nil, serrors.Wrap(ErrUnableToConnect, err) } defer roundTripper.Close(ctx) @@ -281,8 +294,10 @@ func (c *conn) SVCInfo(ctx context.Context, nil, ) if err != nil { + metrics.SVCInfos.Inc(errorToPrometheusLabel(err)) return nil, serrors.WrapStr("[sciond-API] Failed to get SVCInfo", err) } + metrics.SVCInfos.Inc(metrics.OkSuccess) return pld.(*Pld).ServiceInfoReply, nil } @@ -300,6 +315,7 @@ func (c *conn) RevNotification(ctx context.Context, roundTripper, err := c.ctxAwareConnect(ctx) if err != nil { + metrics.Revocations.Inc(errorToPrometheusLabel(err)) return nil, serrors.Wrap(ErrUnableToConnect, err) } defer roundTripper.Close(ctx) @@ -315,8 +331,10 @@ func (c *conn) RevNotification(ctx context.Context, nil, ) if err != nil { + metrics.Revocations.Inc(errorToPrometheusLabel(err)) return nil, serrors.WrapStr("[sciond-API] Failed to send RevNotification", err) } + metrics.Revocations.Inc(metrics.OkSuccess) return reply.(*Pld).RevReply, nil } @@ -348,3 +366,14 @@ func GetDefaultSCIONDPath(ia *addr.IA) string { } return fmt.Sprintf("/run/shm/sciond/sd%s.sock", ia.FileFmt(false)) } + +func errorToPrometheusLabel(err error) string { + switch { + case err == nil: + return metrics.OkSuccess + case serrors.IsTimeout(err): + return metrics.ErrTimeout + default: + return metrics.ErrNotClassified + } +}