diff --git a/.changeset/shy-bulldogs-wink.md b/.changeset/shy-bulldogs-wink.md new file mode 100644 index 00000000000..a2ff2d21f15 --- /dev/null +++ b/.changeset/shy-bulldogs-wink.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#added Full Open Telemetry support, configurable via `Telemetry` diff --git a/.mockery.yaml b/.mockery.yaml index 161348efbf0..599aa1e65ee 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -559,7 +559,6 @@ packages: config: filename: evm_mock.go dir: "{{ .InterfaceDir }}/rpclibmocks" - github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/prices: config: dir: "{{ .InterfaceDir }}/" diff --git a/core/capabilities/ccip/ccip_integration_tests/ocr_node_helper.go b/core/capabilities/ccip/ccip_integration_tests/ocr_node_helper.go index d8bbd5eace2..aff02cd55d8 100644 --- a/core/capabilities/ccip/ccip_integration_tests/ocr_node_helper.go +++ b/core/capabilities/ccip/ccip_integration_tests/ocr_node_helper.go @@ -135,7 +135,7 @@ func setupNodeOCR3( } relayerFactory := chainlink.RelayerFactory{ Logger: lggr, - LoopRegistry: plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), cfg.Tracing()), + LoopRegistry: plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), cfg.Tracing(), cfg.Telemetry()), GRPCOpts: loop.GRPCOpts{}, CapabilitiesRegistry: coretypes.NewCapabilitiesRegistry(t), } @@ -155,7 +155,7 @@ func setupNodeOCR3( RestrictedHTTPClient: &http.Client{}, AuditLogger: audit.NoopLogger, MailMon: mailMon, - LoopRegistry: plugins.NewLoopRegistry(lggr, cfg.Tracing()), + LoopRegistry: plugins.NewLoopRegistry(lggr, cfg.Tracing(), cfg.Telemetry()), }) require.NoError(t, err) require.NoError(t, app.GetKeyStore().Unlock(ctx, "password")) diff --git a/core/cmd/shell.go b/core/cmd/shell.go index ba3d56d748f..c862b936140 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -23,14 +23,16 @@ import ( "github.com/Masterminds/semver/v3" "github.com/getsentry/sentry-go" "github.com/gin-gonic/gin" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" "github.com/urfave/cli" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.uber.org/multierr" "go.uber.org/zap/zapcore" "golang.org/x/sync/errgroup" - "github.com/jmoiron/sqlx" - + "github.com/smartcontractkit/chainlink-common/pkg/beholder" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" @@ -63,20 +65,59 @@ var ( grpcOpts loop.GRPCOpts ) -func initGlobals(cfgProm config.Prometheus, cfgTracing config.Tracing, logger logger.Logger) error { +func initGlobals(cfgProm config.Prometheus, cfgTracing config.Tracing, cfgTelemetry config.Telemetry, lggr logger.Logger) error { // Avoid double initializations, but does not prevent relay methods from being called multiple times. var err error initGlobalsOnce.Do(func() { - prometheus = ginprom.New(ginprom.Namespace("service"), ginprom.Token(cfgProm.AuthToken())) - grpcOpts = loop.NewGRPCOpts(nil) // default prometheus.Registerer - err = loop.SetupTracing(loop.TracingConfig{ - Enabled: cfgTracing.Enabled(), - CollectorTarget: cfgTracing.CollectorTarget(), - NodeAttributes: cfgTracing.Attributes(), - SamplingRatio: cfgTracing.SamplingRatio(), - TLSCertPath: cfgTracing.TLSCertPath(), - OnDialError: func(error) { logger.Errorw("Failed to dial", "err", err) }, - }) + err = func() error { + prometheus = ginprom.New(ginprom.Namespace("service"), ginprom.Token(cfgProm.AuthToken())) + grpcOpts = loop.NewGRPCOpts(nil) // default prometheus.Registerer + + otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) { + lggr.Errorw("Telemetry error", "err", err) + })) + + tracingCfg := loop.TracingConfig{ + Enabled: cfgTracing.Enabled(), + CollectorTarget: cfgTracing.CollectorTarget(), + NodeAttributes: cfgTracing.Attributes(), + SamplingRatio: cfgTracing.SamplingRatio(), + TLSCertPath: cfgTracing.TLSCertPath(), + OnDialError: func(error) { lggr.Errorw("Failed to dial", "err", err) }, + } + if !cfgTelemetry.Enabled() { + return loop.SetupTracing(tracingCfg) + } + + var attributes []attribute.KeyValue + if tracingCfg.Enabled { + attributes = tracingCfg.Attributes() + } + for k, v := range cfgTelemetry.ResourceAttributes() { + attributes = append(attributes, attribute.String(k, v)) + } + clientCfg := beholder.Config{ + InsecureConnection: cfgTelemetry.InsecureConnection(), + CACertFile: cfgTelemetry.CACertFile(), + OtelExporterGRPCEndpoint: cfgTelemetry.OtelExporterGRPCEndpoint(), + ResourceAttributes: attributes, + TraceSampleRatio: cfgTelemetry.TraceSampleRatio(), + } + if tracingCfg.Enabled { + clientCfg.TraceSpanExporter, err = tracingCfg.NewSpanExporter() + if err != nil { + return err + } + } + var beholderClient *beholder.Client + beholderClient, err = beholder.NewClient(clientCfg) + if err != nil { + return err + } + beholder.SetClient(beholderClient) + beholder.SetGlobalOtelProviders() + return nil + }() }) return err } @@ -139,7 +180,7 @@ type ChainlinkAppFactory struct{} // NewApplication returns a new instance of the node with the given config. func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.GeneralConfig, appLggr logger.Logger, db *sqlx.DB) (app chainlink.Application, err error) { - err = initGlobals(cfg.Prometheus(), cfg.Tracing(), appLggr) + err = initGlobals(cfg.Prometheus(), cfg.Tracing(), cfg.Telemetry(), appLggr) if err != nil { appLggr.Errorf("Failed to initialize globals: %v", err) } @@ -159,7 +200,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G keyStore := keystore.New(ds, utils.GetScryptParams(cfg), appLggr) mailMon := mailbox.NewMonitor(cfg.AppID().String(), appLggr.Named("Mailbox")) - loopRegistry := plugins.NewLoopRegistry(appLggr, cfg.Tracing()) + loopRegistry := plugins.NewLoopRegistry(appLggr, cfg.Tracing(), cfg.Telemetry()) mercuryPool := wsrpc.NewPool(appLggr, cache.Config{ LatestReportTTL: cfg.Mercury().Cache().LatestReportTTL(), diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index 604daf75683..fedd83dec8a 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -22,9 +22,8 @@ import ( gethCommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/fatih/color" - "github.com/lib/pq" - "github.com/kylelemons/godebug/diff" + "github.com/lib/pq" "github.com/pkg/errors" "github.com/urfave/cli" "go.uber.org/multierr" diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 783781723ea..6f4907a5a6f 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -46,7 +46,7 @@ import ( func genTestEVMRelayers(t *testing.T, opts legacyevm.ChainRelayExtenderConfig, ks evmrelayer.CSAETHKeystore) *chainlink.CoreRelayerChainInteroperators { f := chainlink.RelayerFactory{ Logger: opts.Logger, - LoopRegistry: plugins.NewLoopRegistry(opts.Logger, opts.AppConfig.Tracing()), + LoopRegistry: plugins.NewLoopRegistry(opts.Logger, opts.AppConfig.Tracing(), opts.AppConfig.Telemetry()), CapabilitiesRegistry: capabilities.NewRegistry(opts.Logger), } diff --git a/core/cmd/shell_test.go b/core/cmd/shell_test.go index 6ecdc4a34de..a93be2fb9ea 100644 --- a/core/cmd/shell_test.go +++ b/core/cmd/shell_test.go @@ -351,7 +351,7 @@ func TestNewUserCache(t *testing.T) { func TestSetupSolanaRelayer(t *testing.T) { lggr := logger.TestLogger(t) - reg := plugins.NewLoopRegistry(lggr, nil) + reg := plugins.NewLoopRegistry(lggr, nil, nil) ks := mocks.NewSolana(t) // config 3 chains but only enable 2 => should only be 2 relayer @@ -466,7 +466,7 @@ func TestSetupSolanaRelayer(t *testing.T) { func TestSetupStarkNetRelayer(t *testing.T) { lggr := logger.TestLogger(t) - reg := plugins.NewLoopRegistry(lggr, nil) + reg := plugins.NewLoopRegistry(lggr, nil, nil) ks := mocks.NewStarkNet(t) // config 3 chains but only enable 2 => should only be 2 relayer nEnabledChains := 2 diff --git a/core/config/app_config.go b/core/config/app_config.go index 112e242636f..4cb7f1f610c 100644 --- a/core/config/app_config.go +++ b/core/config/app_config.go @@ -56,6 +56,7 @@ type AppConfig interface { Threshold() Threshold WebServer() WebServer Tracing() Tracing + Telemetry() Telemetry } type DatabaseBackupMode string diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index 2f7ff847e4b..d29223a31e1 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -689,3 +689,25 @@ TransmitQueueMaxSize = 10_000 # Default # when sending a message to the mercury server, before aborting and considering # the transmission to be failed. TransmitTimeout = "5s" # Default + +# Telemetry holds OTEL settings. +# This data includes open telemetry metrics, traces, & logs. +# It does not currently include prometheus metrics or standard out logs, but may in the future. +[Telemetry] +# Enabled turns telemetry collection on or off. +Enabled = false # Default +# Endpoint of the OTEL Collector. +Endpoint = 'example.com/collector' # Example +# CACertFile is the file path of the TLS certificate used for secure communication with the OTEL Collector. +# Required unless InescureConnection is true. +CACertFile = 'cert-file' # Example +# InsecureConnection bypasses the TLS CACertFile requirement and uses an insecure connection instead. +# Only available in dev mode. +InsecureConnection = false # Default +# TraceSampleRatio is the rate at which to sample traces. Must be between 0 and 1. +TraceSampleRatio = 0.01 # Default + +# ResourceAttributes are global metadata to include with all telemetry. +[Telemetry.ResourceAttributes] +# foo is an example resource attribute +foo = "bar" # Example diff --git a/core/config/telemetry_config.go b/core/config/telemetry_config.go new file mode 100644 index 00000000000..5440e70b43b --- /dev/null +++ b/core/config/telemetry_config.go @@ -0,0 +1,10 @@ +package config + +type Telemetry interface { + Enabled() bool + InsecureConnection() bool + CACertFile() string + OtelExporterGRPCEndpoint() string + ResourceAttributes() map[string]string + TraceSampleRatio() float64 +} diff --git a/core/config/toml/types.go b/core/config/toml/types.go index f89ecab9c08..2eb66d10a9f 100644 --- a/core/config/toml/types.go +++ b/core/config/toml/types.go @@ -59,6 +59,7 @@ type Core struct { Tracing Tracing `toml:",omitempty"` Mercury Mercury `toml:",omitempty"` Capabilities Capabilities `toml:",omitempty"` + Telemetry Telemetry `toml:",omitempty"` } // SetFrom updates c with any non-nil values from f. (currently TOML field only!) @@ -95,11 +96,12 @@ func (c *Core) SetFrom(f *Core) { c.Sentry.setFrom(&f.Sentry) c.Insecure.setFrom(&f.Insecure) c.Tracing.setFrom(&f.Tracing) + c.Telemetry.setFrom(&f.Telemetry) } func (c *Core) ValidateConfig() (err error) { _, verr := parse.HomeDir(*c.RootDir) - if err != nil { + if verr != nil { err = multierr.Append(err, configutils.ErrInvalid{Name: "RootDir", Value: true, Msg: fmt.Sprintf("Failed to expand RootDir. Please use an explicit path: %s", verr)}) } @@ -107,6 +109,12 @@ func (c *Core) ValidateConfig() (err error) { err = multierr.Append(err, configutils.ErrInvalid{Name: "P2P.V2.Enabled", Value: false, Msg: "P2P required for OCR or OCR2. Please enable P2P or disable OCR/OCR2."}) } + if *c.Tracing.Enabled && *c.Telemetry.Enabled { + if c.Tracing.CollectorTarget == c.Telemetry.Endpoint { + err = multierr.Append(err, configutils.ErrInvalid{Name: "Tracing.CollectorTarget", Value: *c.Tracing.CollectorTarget, Msg: "Same as Telemetry.Endpoint. Must be different or disabled."}) + } + } + return err } @@ -1576,25 +1584,25 @@ type Tracing struct { func (t *Tracing) setFrom(f *Tracing) { if v := f.Enabled; v != nil { - t.Enabled = f.Enabled + t.Enabled = v } if v := f.CollectorTarget; v != nil { - t.CollectorTarget = f.CollectorTarget + t.CollectorTarget = v } if v := f.NodeID; v != nil { - t.NodeID = f.NodeID + t.NodeID = v } if v := f.Attributes; v != nil { - t.Attributes = f.Attributes + t.Attributes = v } if v := f.SamplingRatio; v != nil { - t.SamplingRatio = f.SamplingRatio + t.SamplingRatio = v } if v := f.Mode; v != nil { - t.Mode = f.Mode + t.Mode = v } if v := f.TLSCertPath; v != nil { - t.TLSCertPath = f.TLSCertPath + t.TLSCertPath = v } } @@ -1648,6 +1656,59 @@ func (t *Tracing) ValidateConfig() (err error) { return err } +type Telemetry struct { + Enabled *bool + CACertFile *string + Endpoint *string + InsecureConnection *bool + ResourceAttributes map[string]string `toml:",omitempty"` + TraceSampleRatio *float64 +} + +func (b *Telemetry) setFrom(f *Telemetry) { + if v := f.Enabled; v != nil { + b.Enabled = v + } + if v := f.CACertFile; v != nil { + b.CACertFile = v + } + if v := f.Endpoint; v != nil { + b.Endpoint = v + } + if v := f.InsecureConnection; v != nil { + b.InsecureConnection = v + } + if v := f.ResourceAttributes; v != nil { + b.ResourceAttributes = v + } + if v := f.TraceSampleRatio; v != nil { + b.TraceSampleRatio = v + } +} + +func (b *Telemetry) ValidateConfig() (err error) { + if b.Enabled == nil || !*b.Enabled { + return nil + } + if b.Endpoint == nil || *b.Endpoint == "" { + err = multierr.Append(err, configutils.ErrMissing{Name: "Endpoint", Msg: "must be set when Telemetry is enabled"}) + } + if b.InsecureConnection != nil && *b.InsecureConnection { + if build.IsProd() { + err = multierr.Append(err, configutils.ErrInvalid{Name: "InsecureConnection", Msg: "cannot be used in production builds"}) + } + } else { + if b.CACertFile == nil || *b.CACertFile == "" { + err = multierr.Append(err, configutils.ErrMissing{Name: "CACertFile", Msg: "must be set, unless InsecureConnection is used"}) + } + } + if ratio := b.TraceSampleRatio; ratio != nil && (*ratio < 0 || *ratio > 1) { + err = multierr.Append(err, configutils.ErrInvalid{Name: "TraceSampleRatio", Value: *ratio, Msg: "must be between 0 and 1"}) + } + + return err +} + var hostnameRegex = regexp.MustCompile(`^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*$`) // Validates uri is valid external or local URI diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 23469cbb80c..7d333d94018 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -379,7 +379,7 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn keyStore := keystore.NewInMemory(ds, utils.FastScryptParams, lggr) mailMon := mailbox.NewMonitor(cfg.AppID().String(), lggr.Named("Mailbox")) - loopRegistry := plugins.NewLoopRegistry(lggr, nil) + loopRegistry := plugins.NewLoopRegistry(lggr, nil, nil) mercuryPool := wsrpc.NewPool(lggr, cache.Config{ LatestReportTTL: cfg.Mercury().Cache().LatestReportTTL(), @@ -471,7 +471,7 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn RestrictedHTTPClient: c, UnrestrictedHTTPClient: c, SecretGenerator: MockSecretGenerator{}, - LoopRegistry: plugins.NewLoopRegistry(lggr, nil), + LoopRegistry: plugins.NewLoopRegistry(lggr, nil, nil), MercuryPool: mercuryPool, CapabilitiesRegistry: capabilitiesRegistry, CapabilitiesDispatcher: dispatcher, diff --git a/core/scripts/go.mod b/core/scripts/go.mod index d3888520a3f..8ff3659fc29 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -119,7 +119,7 @@ require ( github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gagliardetto/binary v0.7.7 // indirect github.com/gagliardetto/solana-go v1.8.4 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect @@ -149,7 +149,7 @@ require ( github.com/go-openapi/swag v0.22.4 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.15.5 // indirect + github.com/go-playground/validator/v10 v10.22.0 // indirect github.com/go-viper/mapstructure/v2 v2.1.0 // indirect github.com/go-webauthn/webauthn v0.9.4 // indirect github.com/go-webauthn/x v0.1.5 // indirect @@ -174,7 +174,7 @@ require ( github.com/grafana/pyroscope-go v1.1.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/graph-gophers/dataloader v5.0.0+incompatible // indirect - github.com/graph-gophers/graphql-go v1.3.0 // indirect + github.com/graph-gophers/graphql-go v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect @@ -220,7 +220,7 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 6dd991a9588..9e90c299603 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -374,8 +374,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY= github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= @@ -438,6 +438,7 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -462,8 +463,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= -github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= +github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -547,6 +548,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -602,8 +604,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKt github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EKYrXrXXUNJHOgbRt+U6jOug= github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= -github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc= +github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= @@ -813,8 +815,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a h1:dHCfT5W7gghzPtfsW488uPmEOm85wewI+ypUwibyTdU= github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -1151,7 +1153,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -1278,6 +1279,7 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIX go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= go.opentelemetry.io/contrib/propagators/b3 v1.24.0 h1:n4xwCdTx3pZqZs2CjS/CUZAs03y3dZcGhC/FepKtEUY= go.opentelemetry.io/contrib/propagators/b3 v1.24.0/go.mod h1:k5wRxKRU2uXx2F8uNJ4TaonuEO/V7/5xoz7kdsDACT8= +go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240823153156-2a54df7bffb9 h1:UiRNKd1OgqsLbFwE+wkAWTdiAxXtCBqKIHeBIse4FUA= @@ -1304,6 +1306,7 @@ go.opentelemetry.io/otel/sdk/log v0.4.0 h1:1mMI22L82zLqf6KtkjrRy5BbagOTWdJsqMY/H go.opentelemetry.io/otel/sdk/log v0.4.0/go.mod h1:AYJ9FVF0hNOgAVzUG/ybg/QttnXhUePWAupmCqtdESo= go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= +go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 244d5da13d7..3cc63154c9a 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -14,6 +14,9 @@ import ( "github.com/grafana/pyroscope-go" "github.com/jonboulle/clockwork" "github.com/pkg/errors" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "go.uber.org/multierr" "go.uber.org/zap/zapcore" @@ -271,7 +274,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { // we need to initialize in case we serve OCR2 LOOPs loopRegistry := opts.LoopRegistry if loopRegistry == nil { - loopRegistry = plugins.NewLoopRegistry(globalLogger, opts.Config.Tracing()) + loopRegistry = plugins.NewLoopRegistry(globalLogger, opts.Config.Tracing(), opts.Config.Telemetry()) } // If the audit logger is enabled @@ -649,6 +652,14 @@ func (app *ChainlinkApplication) Start(ctx context.Context) error { panic("application is already started") } + var span trace.Span + ctx, span = otel.Tracer("").Start(ctx, "Start", trace.WithAttributes( + attribute.String("app-id", app.ID().String()), + attribute.String("version", static.Version), + attribute.String("commit", static.Sha), + )) + defer span.End() + if app.FeedsService != nil { if err := app.FeedsService.Start(ctx); err != nil { app.logger.Errorf("[Feeds Service] Failed to start %v", err) diff --git a/core/services/chainlink/cfgtest/cfgtest.go b/core/services/chainlink/cfgtest/cfgtest.go index 3bf95452650..1dfba71d469 100644 --- a/core/services/chainlink/cfgtest/cfgtest.go +++ b/core/services/chainlink/cfgtest/cfgtest.go @@ -76,7 +76,7 @@ func assertValNotNil(t *testing.T, key string, val reflect.Value) error { t.Helper() k := val.Kind() switch k { //nolint:exhaustive - case reflect.Ptr, reflect.Map: + case reflect.Ptr: if val.IsNil() { return fmt.Errorf("%s: nil", key) } @@ -94,6 +94,9 @@ func assertValNotNil(t *testing.T, key string, val reflect.Value) error { } return assertFieldsNotNil(t, key, val) case reflect.Map: + if val.IsNil() { + return nil // not actually a problem + } return assertValuesNotNil(t, key, val) case reflect.Slice: if val.IsNil() { diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 79c92f82145..dd0dc87b59a 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -524,5 +524,8 @@ func (g *generalConfig) Threshold() coreconfig.Threshold { func (g *generalConfig) Tracing() coreconfig.Tracing { return &tracingConfig{s: g.c.Tracing} } +func (g *generalConfig) Telemetry() coreconfig.Telemetry { + return &telemetryConfig{s: g.c.Telemetry} +} var zeroSha256Hash = models.Sha256Hash{} diff --git a/core/services/chainlink/config_telemetry.go b/core/services/chainlink/config_telemetry.go new file mode 100644 index 00000000000..790f2a19953 --- /dev/null +++ b/core/services/chainlink/config_telemetry.go @@ -0,0 +1,43 @@ +package chainlink + +import ( + "github.com/smartcontractkit/chainlink/v2/core/config/toml" +) + +type telemetryConfig struct { + s toml.Telemetry +} + +func (b *telemetryConfig) Enabled() bool { return *b.s.Enabled } + +func (b *telemetryConfig) InsecureConnection() bool { + if b.s.InsecureConnection == nil { + return false + } + return *b.s.InsecureConnection +} + +func (b *telemetryConfig) CACertFile() string { + if b.s.CACertFile == nil { + return "" + } + return *b.s.CACertFile +} + +func (b *telemetryConfig) OtelExporterGRPCEndpoint() string { + if b.s.Endpoint == nil { + return "" + } + return *b.s.Endpoint +} + +func (b *telemetryConfig) ResourceAttributes() map[string]string { + return b.s.ResourceAttributes +} + +func (b *telemetryConfig) TraceSampleRatio() float64 { + if b.s.TraceSampleRatio == nil { + return 0.0 + } + return *b.s.TraceSampleRatio +} diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 71dc763ad6a..1018778b32f 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -513,6 +513,14 @@ func TestConfig_Marshal(t *testing.T) { Environment: ptr("dev"), Release: ptr("v1.2.3"), } + full.Telemetry = toml.Telemetry{ + Enabled: ptr(true), + CACertFile: ptr("cert-file"), + Endpoint: ptr("example.com/collector"), + InsecureConnection: ptr(true), + ResourceAttributes: map[string]string{"Baz": "test", "Foo": "bar"}, + TraceSampleRatio: ptr(0.01), + } full.EVM = []*evmcfg.EVMConfig{ { ChainID: ubig.NewI(1), diff --git a/core/services/chainlink/mocks/general_config.go b/core/services/chainlink/mocks/general_config.go index f4594a43225..63a846c6edb 100644 --- a/core/services/chainlink/mocks/general_config.go +++ b/core/services/chainlink/mocks/general_config.go @@ -1771,6 +1771,53 @@ func (_c *GeneralConfig_StarknetConfigs_Call) RunAndReturn(run func() chainlinkc return _c } +// Telemetry provides a mock function with given fields: +func (_m *GeneralConfig) Telemetry() config.Telemetry { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Telemetry") + } + + var r0 config.Telemetry + if rf, ok := ret.Get(0).(func() config.Telemetry); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(config.Telemetry) + } + } + + return r0 +} + +// GeneralConfig_Telemetry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Telemetry' +type GeneralConfig_Telemetry_Call struct { + *mock.Call +} + +// Telemetry is a helper method to define mock.On call +func (_e *GeneralConfig_Expecter) Telemetry() *GeneralConfig_Telemetry_Call { + return &GeneralConfig_Telemetry_Call{Call: _e.mock.On("Telemetry")} +} + +func (_c *GeneralConfig_Telemetry_Call) Run(run func()) *GeneralConfig_Telemetry_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *GeneralConfig_Telemetry_Call) Return(_a0 config.Telemetry) *GeneralConfig_Telemetry_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *GeneralConfig_Telemetry_Call) RunAndReturn(run func() config.Telemetry) *GeneralConfig_Telemetry_Call { + _c.Call.Return(run) + return _c +} + // TelemetryIngress provides a mock function with given fields: func (_m *GeneralConfig) TelemetryIngress() config.TelemetryIngress { ret := _m.Called() diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index 5aaf6e16dd4..e83c2881c93 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -176,7 +176,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { factory := chainlink.RelayerFactory{ Logger: lggr, - LoopRegistry: plugins.NewLoopRegistry(lggr, nil), + LoopRegistry: plugins.NewLoopRegistry(lggr, nil, nil), GRPCOpts: loop.GRPCOpts{}, CapabilitiesRegistry: capabilities.NewRegistry(lggr), } diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index aff50754280..f78c98896d0 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -279,3 +279,10 @@ AuthTimestampToleranceSec = 0 [[Capabilities.GatewayConnector.Gateways]] ID = '' URL = '' + +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 0474a5a1e31..e000ff82d40 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -290,6 +290,17 @@ AuthTimestampToleranceSec = 10 ID = 'example_gateway' URL = 'wss://localhost:8081/node' +[Telemetry] +Enabled = true +CACertFile = 'cert-file' +Endpoint = 'example.com/collector' +InsecureConnection = true +TraceSampleRatio = 0.01 + +[Telemetry.ResourceAttributes] +Baz = 'test' +Foo = 'bar' + [[EVM]] ChainID = '1' Enabled = false diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 5472be09bfb..fc11ed10b1d 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -280,6 +280,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go index 676ae79e357..cecf99353c3 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go @@ -458,7 +458,7 @@ func setupNodeCCIP( }, CSAETHKeystore: simEthKeyStore, } - loopRegistry := plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), config.Tracing()) + loopRegistry := plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), config.Tracing(), config.Telemetry()) relayerFactory := chainlink.RelayerFactory{ Logger: lggr, LoopRegistry: loopRegistry, @@ -488,7 +488,7 @@ func setupNodeCCIP( RestrictedHTTPClient: &http.Client{}, AuditLogger: audit.NoopLogger, MailMon: mailMon, - LoopRegistry: plugins.NewLoopRegistry(lggr, config.Tracing()), + LoopRegistry: plugins.NewLoopRegistry(lggr, config.Tracing(), config.Telemetry()), }) require.NoError(t, err) require.NoError(t, app.GetKeyStore().Unlock(ctx, "password")) diff --git a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go index 2569aa53243..bff08e86385 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go @@ -455,7 +455,7 @@ func setupNodeCCIP( }, CSAETHKeystore: simEthKeyStore, } - loopRegistry := plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), config.Tracing()) + loopRegistry := plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), config.Tracing(), config.Telemetry()) relayerFactory := chainlink.RelayerFactory{ Logger: lggr, LoopRegistry: loopRegistry, @@ -485,7 +485,7 @@ func setupNodeCCIP( RestrictedHTTPClient: &http.Client{}, AuditLogger: audit.NoopLogger, MailMon: mailMon, - LoopRegistry: plugins.NewLoopRegistry(lggr, config.Tracing()), + LoopRegistry: plugins.NewLoopRegistry(lggr, config.Tracing(), config.Telemetry()), }) ctx := testutils.Context(t) require.NoError(t, err) diff --git a/core/web/loop_registry_internal_test.go b/core/web/loop_registry_internal_test.go index 48cd75a5cff..a02fa20802a 100644 --- a/core/web/loop_registry_internal_test.go +++ b/core/web/loop_registry_internal_test.go @@ -38,7 +38,7 @@ func TestLoopRegistryServer_CantWriteToResponse(t *testing.T) { l, o := logger.TestLoggerObserved(t, zap.ErrorLevel) s := &LoopRegistryServer{ exposedPromPort: 1, - registry: plugins.NewLoopRegistry(l, nil), + registry: plugins.NewLoopRegistry(l, nil, nil), logger: l.(logger.SugaredLogger), jsonMarshalFn: json.Marshal, } @@ -53,7 +53,7 @@ func TestLoopRegistryServer_CantMarshal(t *testing.T) { l, o := logger.TestLoggerObserved(t, zap.ErrorLevel) s := &LoopRegistryServer{ exposedPromPort: 1, - registry: plugins.NewLoopRegistry(l, nil), + registry: plugins.NewLoopRegistry(l, nil, nil), logger: l.(logger.SugaredLogger), jsonMarshalFn: func(any) ([]byte, error) { return []byte(""), errors.New("can't unmarshal") diff --git a/core/web/resolver/testdata/config-empty-effective.toml b/core/web/resolver/testdata/config-empty-effective.toml index aff50754280..f78c98896d0 100644 --- a/core/web/resolver/testdata/config-empty-effective.toml +++ b/core/web/resolver/testdata/config-empty-effective.toml @@ -279,3 +279,10 @@ AuthTimestampToleranceSec = 0 [[Capabilities.GatewayConnector.Gateways]] ID = '' URL = '' + +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index c01ab09b666..a13a6b9db59 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -290,6 +290,17 @@ AuthTimestampToleranceSec = 10 ID = 'example_gateway' URL = 'wss://localhost:8081/node' +[Telemetry] +Enabled = true +CACertFile = 'cert-file' +Endpoint = 'example.com/collector' +InsecureConnection = true +TraceSampleRatio = 0.01 + +[Telemetry.ResourceAttributes] +Baz = 'test' +Foo = 'bar' + [[EVM]] ChainID = '1' Enabled = false diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index e631642e051..a3853dd2b44 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -280,6 +280,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 3b42bc37562..2f339dfda4c 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1897,6 +1897,64 @@ TransmitTimeout controls how long the transmitter will wait for a response when sending a message to the mercury server, before aborting and considering the transmission to be failed. +## Telemetry +```toml +[Telemetry] +Enabled = false # Default +Endpoint = 'example.com/collector' # Example +CACertFile = 'cert-file' # Example +InsecureConnection = false # Default +TraceSampleRatio = 0.01 # Default +``` +Telemetry holds OTEL settings. +This data includes open telemetry metrics, traces, & logs. +It does not currently include prometheus metrics or standard out logs, but may in the future. + +### Enabled +```toml +Enabled = false # Default +``` +Enabled turns telemetry collection on or off. + +### Endpoint +```toml +Endpoint = 'example.com/collector' # Example +``` +Endpoint of the OTEL Collector. + +### CACertFile +```toml +CACertFile = 'cert-file' # Example +``` +CACertFile is the file path of the TLS certificate used for secure communication with the OTEL Collector. +Required unless InescureConnection is true. + +### InsecureConnection +```toml +InsecureConnection = false # Default +``` +InsecureConnection bypasses the TLS CACertFile requirement and uses an insecure connection instead. +Only available in dev mode. + +### TraceSampleRatio +```toml +TraceSampleRatio = 0.01 # Default +``` +TraceSampleRatio is the rate at which to sample traces. Must be between 0 and 1. + +## Telemetry.ResourceAttributes +```toml +[Telemetry.ResourceAttributes] +foo = "bar" # Example +``` +ResourceAttributes are global metadata to include with all telemetry. + +### foo +```toml +foo = "bar" # Example +``` +foo is an example resource attribute + ## EVM EVM defaults depend on ChainID: diff --git a/go.mod b/go.mod index 71c1adc029f..3351bf5bdab 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/gorilla/websocket v1.5.1 github.com/grafana/pyroscope-go v1.1.1 github.com/graph-gophers/dataloader v5.0.0+incompatible - github.com/graph-gophers/graphql-go v1.3.0 + github.com/graph-gophers/graphql-go v1.5.0 github.com/hashicorp/consul/sdk v0.16.0 github.com/hashicorp/go-envparse v0.1.0 github.com/hashicorp/go-plugin v1.6.2-0.20240829161738-06afb6d7ae99 @@ -100,6 +100,7 @@ require ( go.dedis.ch/kyber/v3 v3.1.0 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.49.0 go.opentelemetry.io/otel v1.28.0 + go.opentelemetry.io/otel/trace v1.28.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.27.0 @@ -190,7 +191,7 @@ require ( github.com/dvsekhvalnov/jose2go v1.7.0 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gagliardetto/binary v0.7.7 // indirect github.com/gagliardetto/treeout v0.1.4 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect @@ -207,7 +208,7 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.15.5 // indirect + github.com/go-playground/validator/v10 v10.22.0 // indirect github.com/go-webauthn/x v0.1.5 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect @@ -260,7 +261,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -343,7 +344,6 @@ require ( go.opentelemetry.io/otel/sdk v1.28.0 // indirect go.opentelemetry.io/otel/sdk/log v0.4.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/ratelimit v0.3.0 // indirect golang.org/x/arch v0.8.0 // indirect diff --git a/go.sum b/go.sum index bfc16c5adad..6375ccebe27 100644 --- a/go.sum +++ b/go.sum @@ -352,8 +352,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gagliardetto/binary v0.7.7 h1:QZpT38+sgoPg+TIQjH94sLbl/vX+nlIRA37pEyOsjfY= github.com/gagliardetto/binary v0.7.7/go.mod h1:mUuay5LL8wFVnIlecHakSZMvcdqfs+CsotR5n77kyjM= github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= @@ -410,6 +410,7 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -426,8 +427,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= -github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= +github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -511,6 +512,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -567,8 +569,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKt github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EKYrXrXXUNJHOgbRt+U6jOug= github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= -github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc= +github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= @@ -778,8 +780,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a h1:dHCfT5W7gghzPtfsW488uPmEOm85wewI+ypUwibyTdU= github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -1113,7 +1115,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -1236,6 +1237,7 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIX go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= go.opentelemetry.io/contrib/propagators/b3 v1.24.0 h1:n4xwCdTx3pZqZs2CjS/CUZAs03y3dZcGhC/FepKtEUY= go.opentelemetry.io/contrib/propagators/b3 v1.24.0/go.mod h1:k5wRxKRU2uXx2F8uNJ4TaonuEO/V7/5xoz7kdsDACT8= +go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240823153156-2a54df7bffb9 h1:UiRNKd1OgqsLbFwE+wkAWTdiAxXtCBqKIHeBIse4FUA= @@ -1262,6 +1264,7 @@ go.opentelemetry.io/otel/sdk/log v0.4.0 h1:1mMI22L82zLqf6KtkjrRy5BbagOTWdJsqMY/H go.opentelemetry.io/otel/sdk/log v0.4.0/go.mod h1:AYJ9FVF0hNOgAVzUG/ybg/QttnXhUePWAupmCqtdESo= go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= +go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= diff --git a/integration-tests/deployment/memory/node.go b/integration-tests/deployment/memory/node.go index 2ca84ac6c5b..6512788baf7 100644 --- a/integration-tests/deployment/memory/node.go +++ b/integration-tests/deployment/memory/node.go @@ -167,7 +167,7 @@ func NewNode( // Build relayer factory with EVM. relayerFactory := chainlink.RelayerFactory{ Logger: lggr, - LoopRegistry: plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), cfg.Tracing()), + LoopRegistry: plugins.NewLoopRegistry(lggr.Named("LoopRegistry"), cfg.Tracing(), cfg.Telemetry()), GRPCOpts: loop.GRPCOpts{}, CapabilitiesRegistry: coretypes.NewCapabilitiesRegistry(t), } @@ -187,7 +187,7 @@ func NewNode( RestrictedHTTPClient: &http.Client{}, AuditLogger: audit.NoopLogger, MailMon: mailMon, - LoopRegistry: plugins.NewLoopRegistry(lggr, cfg.Tracing()), + LoopRegistry: plugins.NewLoopRegistry(lggr, cfg.Tracing(), cfg.Telemetry()), }) require.NoError(t, err) t.Cleanup(func() { diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 21b260d21b8..f1c1b369126 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -838,8 +838,8 @@ github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EKYrXrXXUNJHOgbRt+U6jOug= github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= -github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc= +github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index a9d6785672b..a172b5e6e68 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -826,8 +826,8 @@ github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EKYrXrXXUNJHOgbRt+U6jOug= github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= -github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc= +github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= diff --git a/plugins/loop_registry.go b/plugins/loop_registry.go index b796ddf87ee..51c6310ffa7 100644 --- a/plugins/loop_registry.go +++ b/plugins/loop_registry.go @@ -27,15 +27,17 @@ type LoopRegistry struct { mu sync.Mutex registry map[string]*RegisteredLoop - lggr logger.Logger - cfgTracing config.Tracing + lggr logger.Logger + cfgTracing config.Tracing + cfgTelemetry config.Telemetry } -func NewLoopRegistry(lggr logger.Logger, tracingConfig config.Tracing) *LoopRegistry { +func NewLoopRegistry(lggr logger.Logger, tracing config.Tracing, telemetry config.Telemetry) *LoopRegistry { return &LoopRegistry{ - registry: map[string]*RegisteredLoop{}, - lggr: logger.Named(lggr, "LoopRegistry"), - cfgTracing: tracingConfig, + registry: map[string]*RegisteredLoop{}, + lggr: logger.Named(lggr, "LoopRegistry"), + cfgTracing: tracing, + cfgTelemetry: telemetry, } } @@ -65,6 +67,15 @@ func (m *LoopRegistry) Register(id string) (*RegisteredLoop, error) { envCfg.TracingAttributes = m.cfgTracing.Attributes() } + if m.cfgTelemetry != nil { + envCfg.TelemetryEnabled = m.cfgTelemetry.Enabled() + envCfg.TelemetryEndpoint = m.cfgTelemetry.OtelExporterGRPCEndpoint() + envCfg.TelemetryInsecureConnection = m.cfgTelemetry.InsecureConnection() + envCfg.TelemetryCACertFile = m.cfgTelemetry.CACertFile() + envCfg.TelemetryAttributes = m.cfgTelemetry.ResourceAttributes() + envCfg.TelemetryTraceSampleRatio = m.cfgTelemetry.TraceSampleRatio() + } + m.registry[id] = &RegisteredLoop{Name: id, EnvCfg: envCfg} m.lggr.Debugf("Registered loopp %q with config %v, port %d", id, envCfg, envCfg.PrometheusPort) return m.registry[id], nil diff --git a/plugins/loop_registry_test.go b/plugins/loop_registry_test.go index b307469e09b..84b6b0cefc9 100644 --- a/plugins/loop_registry_test.go +++ b/plugins/loop_registry_test.go @@ -5,12 +5,13 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestPluginPortManager(t *testing.T) { // register one - m := NewLoopRegistry(logger.TestLogger(t), nil) + m := NewLoopRegistry(logger.TestLogger(t), nil, nil) pFoo, err := m.Register("foo") require.NoError(t, err) require.Equal(t, "foo", pFoo.Name) @@ -26,37 +27,63 @@ func TestPluginPortManager(t *testing.T) { require.Equal(t, pFoo.EnvCfg.PrometheusPort+1, pBar.EnvCfg.PrometheusPort) } -// Mock tracing config -type MockCfgTracing struct{} +type mockCfgTracing struct{} -func (m *MockCfgTracing) Attributes() map[string]string { +func (m *mockCfgTracing) Attributes() map[string]string { return map[string]string{"attribute": "value"} } -func (m *MockCfgTracing) Enabled() bool { return true } -func (m *MockCfgTracing) NodeID() string { return "" } -func (m *MockCfgTracing) CollectorTarget() string { return "http://localhost:9000" } -func (m *MockCfgTracing) SamplingRatio() float64 { return 0.1 } -func (m *MockCfgTracing) TLSCertPath() string { return "/path/to/cert.pem" } -func (m *MockCfgTracing) Mode() string { return "tls" } +func (m *mockCfgTracing) Enabled() bool { return true } +func (m *mockCfgTracing) NodeID() string { return "" } +func (m *mockCfgTracing) CollectorTarget() string { return "http://localhost:9000" } +func (m *mockCfgTracing) SamplingRatio() float64 { return 0.1 } +func (m *mockCfgTracing) TLSCertPath() string { return "/path/to/cert.pem" } +func (m *mockCfgTracing) Mode() string { return "tls" } + +type mockCfgTelemetry struct{} + +func (m mockCfgTelemetry) Enabled() bool { return true } + +func (m mockCfgTelemetry) InsecureConnection() bool { return true } + +func (m mockCfgTelemetry) CACertFile() string { return "path/to/cert.pem" } + +func (m mockCfgTelemetry) OtelExporterGRPCEndpoint() string { return "http://localhost:9001" } + +func (m mockCfgTelemetry) ResourceAttributes() map[string]string { + return map[string]string{"foo": "bar"} +} + +func (m mockCfgTelemetry) TraceSampleRatio() float64 { return 0.42 } func TestLoopRegistry_Register(t *testing.T) { - mockCfgTracing := &MockCfgTracing{} + mockCfgTracing := &mockCfgTracing{} + mockCfgTelemetry := &mockCfgTelemetry{} registry := make(map[string]*RegisteredLoop) // Create a LoopRegistry instance with mockCfgTracing loopRegistry := &LoopRegistry{ - lggr: logger.TestLogger(t), - registry: registry, - cfgTracing: mockCfgTracing, + lggr: logger.TestLogger(t), + registry: registry, + cfgTracing: mockCfgTracing, + cfgTelemetry: mockCfgTelemetry, } // Test case 1: Register new loop registeredLoop, err := loopRegistry.Register("testID") require.Nil(t, err) require.Equal(t, "testID", registeredLoop.Name) - require.True(t, registeredLoop.EnvCfg.TracingEnabled) - require.Equal(t, "http://localhost:9000", registeredLoop.EnvCfg.TracingCollectorTarget) - require.Equal(t, map[string]string{"attribute": "value"}, registeredLoop.EnvCfg.TracingAttributes) - require.Equal(t, 0.1, registeredLoop.EnvCfg.TracingSamplingRatio) - require.Equal(t, "/path/to/cert.pem", registeredLoop.EnvCfg.TracingTLSCertPath) + + envCfg := registeredLoop.EnvCfg + require.True(t, envCfg.TracingEnabled) + require.Equal(t, "http://localhost:9000", envCfg.TracingCollectorTarget) + require.Equal(t, map[string]string{"attribute": "value"}, envCfg.TracingAttributes) + require.Equal(t, 0.1, envCfg.TracingSamplingRatio) + require.Equal(t, "/path/to/cert.pem", envCfg.TracingTLSCertPath) + + require.True(t, envCfg.TelemetryEnabled) + require.True(t, envCfg.TelemetryInsecureConnection) + require.Equal(t, "path/to/cert.pem", envCfg.TelemetryCACertFile) + require.Equal(t, "http://localhost:9001", envCfg.TelemetryEndpoint) + require.Equal(t, loop.OtelAttributes{"foo": "bar"}, envCfg.TelemetryAttributes) + require.Equal(t, 0.42, envCfg.TelemetryTraceSampleRatio) } diff --git a/testdata/scripts/node/validate/default.txtar b/testdata/scripts/node/validate/default.txtar index 1177b0d97e3..9dcec7a45c5 100644 --- a/testdata/scripts/node/validate/default.txtar +++ b/testdata/scripts/node/validate/default.txtar @@ -292,6 +292,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + Invalid configuration: invalid secrets: 2 errors: - Database.URL: empty: must be provided and non-empty - Password.Keystore: empty: must be provided and non-empty diff --git a/testdata/scripts/node/validate/defaults-override.txtar b/testdata/scripts/node/validate/defaults-override.txtar index 07bdea1ce16..3feb595be02 100644 --- a/testdata/scripts/node/validate/defaults-override.txtar +++ b/testdata/scripts/node/validate/defaults-override.txtar @@ -353,6 +353,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 6555292405e..cd0e2f7fa1a 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -336,6 +336,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 93769eb6c6b..07ed2c398a3 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -336,6 +336,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index eaf38bf2a53..3bb1179218f 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -336,6 +336,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/invalid-ocr-p2p.txtar b/testdata/scripts/node/validate/invalid-ocr-p2p.txtar index a434f75048a..5c9d6765723 100644 --- a/testdata/scripts/node/validate/invalid-ocr-p2p.txtar +++ b/testdata/scripts/node/validate/invalid-ocr-p2p.txtar @@ -321,6 +321,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + Invalid configuration: invalid configuration: P2P.V2.Enabled: invalid value (false): P2P required for OCR or OCR2. Please enable P2P or disable OCR/OCR2. -- err.txt -- diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 4838ddc61c2..e210c893823 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -326,6 +326,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index f7349ed533c..a11fd5f8926 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -333,6 +333,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index f900bc96263..071da2d2681 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -315,6 +315,13 @@ AuthTimestampToleranceSec = 0 ID = '' URL = '' +[Telemetry] +Enabled = false +CACertFile = '' +Endpoint = '' +InsecureConnection = false +TraceSampleRatio = 0.01 + # Configuration warning: Tracing.TLSCertPath: invalid value (something): must be empty when Tracing.Mode is 'unencrypted' Valid configuration.