address review feedback

This commit is contained in:
jonny rhea 2025-12-29 17:21:29 -06:00 committed by Felix Lange
parent 34ced2ebe6
commit d9c9243251
9 changed files with 157 additions and 162 deletions

View file

@ -242,6 +242,15 @@ func makeFullNode(ctx *cli.Context) *node.Node {
// Start metrics export if enabled
utils.SetupMetrics(&cfg.Metrics)
// Setup OpenTelemetry tracing if enabled
otelService, err := utils.SetupOTEL(ctx)
if err != nil {
utils.Fatalf("failed to setup OpenTelemetry: %v", err)
}
if otelService != nil {
utils.RegisterOTELService(otelService, stack)
}
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
// Create gauge with geth system and build information

View file

@ -33,7 +33,6 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/internal/otel"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"go.uber.org/automaxprocs/maxprocs"
@ -218,10 +217,8 @@ var (
}
otelFlags = []cli.Flag{
utils.OTELTracingFlag,
utils.OTELEnabledFlag,
utils.OTELEndpointFlag,
utils.OTELProtocolFlag,
utils.OTELInsecureFlag,
utils.OTELServiceNameFlag,
}
)
@ -284,14 +281,10 @@ func init() {
if err := debug.Setup(ctx); err != nil {
return err
}
if err := otel.Setup(ctx); err != nil {
return err
}
flags.CheckEnvVars(ctx, app.Flags, "GETH")
return nil
}
app.After = func(ctx *cli.Context) error {
otel.Exit()
debug.Exit()
prompt.Stdin.Close() // Resets terminal mode.
return nil

View file

@ -32,8 +32,8 @@ require (
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/crypto v0.44.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.39.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

View file

@ -108,22 +108,22 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View file

@ -1043,30 +1043,15 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
Category: flags.MetricsCategory,
}
OTELTracingFlag = &cli.BoolFlag{
Name: "otel.tracing",
OTELEnabledFlag = &cli.BoolFlag{
Name: "otel.enabled",
Usage: "Enable OpenTelemetry tracing",
Category: flags.LoggingCategory,
}
OTELEndpointFlag = &cli.StringFlag{
Name: "otel.endpoint",
Usage: "OpenTelemetry collector endpoint (e.g., localhost:4317)",
Value: "localhost:4317",
Category: flags.LoggingCategory,
}
OTELProtocolFlag = &cli.StringFlag{
Name: "otel.protocol",
Usage: "OpenTelemetry protocol: grpc or http",
Value: "grpc",
Category: flags.LoggingCategory,
}
OTELInsecureFlag = &cli.BoolFlag{
Name: "otel.insecure",
Usage: "Use insecure connection to OpenTelemetry collector",
Value: false,
Usage: "Defines where OpenTelemetry traces are sent (e.g., http://localhost:4318)",
Category: flags.LoggingCategory,
}

131
cmd/utils/otel.go Normal file
View file

@ -0,0 +1,131 @@
// Copyright 2025 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package utils
import (
"context"
"fmt"
"net/url"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/urfave/cli/v2"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
// otelService wraps the OpenTelemetry TracerProvider to implement node.Lifecycle.
type otelService struct {
tracerProvider *sdktrace.TracerProvider
}
// Start implements node.Lifecycle.
func (o *otelService) Start() error {
return nil // Provider is already started during setup
}
// Stop implements node.Lifecycle
func (o *otelService) Stop() error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := o.tracerProvider.Shutdown(ctx); err != nil {
log.Error("Failed to stop otelService", "err", err)
return err
}
log.Info("otelService stopped")
return nil
}
// SetupOTEL initializes OpenTelemetry tracing based on CLI flags.
func SetupOTEL(ctx *cli.Context) (*otelService, error) {
if !ctx.Bool(OTELEnabledFlag.Name) {
return nil, nil
}
endpoint := ctx.String(OTELEndpointFlag.Name)
if endpoint == "" {
return nil, nil
}
serviceName := ctx.String(OTELServiceNameFlag.Name)
setupCtx := ctx.Context
if setupCtx == nil {
setupCtx = context.Background()
}
// Create exporter based on endpoint URL
u, err := url.Parse(endpoint)
if err != nil {
return nil, fmt.Errorf("invalid otel endpoint URL: %w", err)
}
var exporter sdktrace.SpanExporter
switch u.Scheme {
case "http", "https":
opts := []otlptracehttp.Option{
otlptracehttp.WithEndpoint(endpoint),
}
if u.Scheme == "http" {
opts = append(opts, otlptracehttp.WithInsecure())
}
exporter, err = otlptracehttp.New(setupCtx, opts...)
default:
return nil, fmt.Errorf("unsupported otel url scheme: %s", u.Scheme)
}
if err != nil {
return nil, fmt.Errorf("failed to create otel exporter: %w", err)
}
// Create resource with service information
res, err := resource.New(setupCtx,
resource.WithAttributes(
semconv.ServiceName(serviceName),
),
)
if err != nil {
return nil, fmt.Errorf("failed to create otel resource: %w", err)
}
// Create TracerProvider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
)
// Set as global provider
otel.SetTracerProvider(tp)
// Set global propagator for context propagation
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))
log.Info("OpenTelemetry tracing enabled", "endpoint", endpoint, "service", serviceName)
return &otelService{tracerProvider: tp}, nil
}
// RegisterOTELService registers the otelService with the node so its lifecycle is managed by the node.
func RegisterOTELService(otelService *otelService, stack *node.Node) {
stack.RegisterLifecycle(otelService)
}

7
go.mod
View file

@ -61,9 +61,9 @@ require (
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/urfave/cli/v2 v2.27.5
go.opentelemetry.io/otel v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0
go.opentelemetry.io/otel/sdk v1.39.0
go.opentelemetry.io/otel/trace v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0
go.uber.org/automaxprocs v1.5.2
go.uber.org/goleak v1.3.0
golang.org/x/crypto v0.44.0
@ -79,14 +79,13 @@ require (
)
require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/otel/trace v1.39.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect

2
go.sum
View file

@ -384,8 +384,6 @@ go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=

View file

@ -1,120 +0,0 @@
// Copyright 2025 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package otel provides OpenTelemetry tracing configuration for geth.
package otel
import (
"context"
"fmt"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
var (
tracerProvider *sdktrace.TracerProvider
)
// Setup initializes OpenTelemetry tracing based on CLI flags.
func Setup(ctx *cli.Context) error {
if !ctx.Bool(utils.OTELTracingFlag.Name) {
return nil
}
endpoint := ctx.String(utils.OTELEndpointFlag.Name)
protocol := ctx.String(utils.OTELProtocolFlag.Name)
insecure := ctx.Bool(utils.OTELInsecureFlag.Name)
serviceName := ctx.String(utils.OTELServiceNameFlag.Name)
setupCtx := ctx.Context
if setupCtx == nil {
setupCtx = context.Background()
}
// Create resource with service information
res, err := resource.New(setupCtx,
resource.WithAttributes(
semconv.ServiceName(serviceName),
),
)
if err != nil {
return fmt.Errorf("failed to create otel resource: %w", err)
}
// Create exporter based on protocol
var exporter sdktrace.SpanExporter
switch protocol {
case "grpc":
opts := []otlptracegrpc.Option{
otlptracegrpc.WithEndpoint(endpoint),
}
if insecure {
opts = append(opts, otlptracegrpc.WithInsecure())
}
exporter, err = otlptracegrpc.New(setupCtx, opts...)
case "http":
opts := []otlptracehttp.Option{
otlptracehttp.WithEndpoint(endpoint),
}
if insecure {
opts = append(opts, otlptracehttp.WithInsecure())
}
exporter, err = otlptracehttp.New(setupCtx, opts...)
default:
return fmt.Errorf("unsupported otel protocol: %s (use 'grpc' or 'http')", protocol)
}
if err != nil {
return fmt.Errorf("failed to create otel exporter: %w", err)
}
// Create TracerProvider
tracerProvider = sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
)
// Set as global provider
otel.SetTracerProvider(tracerProvider)
// Set global propagator for context propagation
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))
log.Info("OpenTelemetry tracing enabled", "endpoint", endpoint, "protocol", protocol, "service", serviceName)
return nil
}
// Exit shuts down the OpenTelemetry TracerProvider, flushing any remaining spans.
func Exit() {
if tracerProvider != nil {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := tracerProvider.Shutdown(ctx); err != nil {
log.Error("Failed to shutdown OpenTelemetry tracer provider", "err", err)
}
}
}