Compare commits

..

1 Commits

Author SHA1 Message Date
Pandey
a321ef8de8 refactor(instrumentation): flatten code source into code.filepath, code.function, code.lineno (#10667)
Some checks are pending
Release Drafter / update_release_draft (push) Waiting to run
build-staging / prepare (push) Waiting to run
build-staging / js-build (push) Blocked by required conditions
build-staging / go-build (push) Blocked by required conditions
build-staging / staging (push) Blocked by required conditions
Replace the nested `slog.Source` group with flat OTel semantic convention
keys by adding a Source log handler wrapper that extracts source info
from record.PC. Remove AddSource from the JSON handler since the wrapper
handles it directly.
2026-03-22 17:16:23 +00:00
15 changed files with 88 additions and 216 deletions

View File

@@ -39,13 +39,6 @@ instrumentation:
host: "0.0.0.0"
port: 9090
##################### PProf #####################
pprof:
# Whether to enable the pprof server.
enabled: true
# The address on which the pprof server listens.
address: 0.0.0.0:6060
##################### Web #####################
web:
# Whether to enable the web frontend

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/http"
_ "net/http/pprof" // http profiler
"slices"
"go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux"
@@ -312,6 +313,15 @@ func (s *Server) Start(ctx context.Context) error {
s.unavailableChannel <- healthcheck.Unavailable
}()
go func() {
slog.Info("Starting pprof server", "addr", baseconst.DebugHttpPort)
err = http.ListenAndServe(baseconst.DebugHttpPort, nil)
if err != nil {
slog.Error("Could not start pprof server", errors.Attr(err))
}
}()
go func() {
slog.Info("Starting OpAmp Websocket server", "addr", baseconst.OpAmpWsEndpoint)
err := s.opampServer.Start(baseconst.OpAmpWsEndpoint)

View File

@@ -13,13 +13,7 @@ type zapToSlogConverter struct{}
func NewLogger(config Config) *slog.Logger {
logger := slog.New(
loghandler.New(
slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: config.Logs.Level, AddSource: true, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// This is more in line with OpenTelemetry semantic conventions
if a.Key == slog.SourceKey {
a.Key = "code"
return a
}
slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: config.Logs.Level, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
a.Key = "timestamp"
return a
@@ -27,6 +21,7 @@ func NewLogger(config Config) *slog.Logger {
return a
}}),
loghandler.NewSource(),
loghandler.NewCorrelation(),
loghandler.NewFiltering(),
loghandler.NewException(),

View File

@@ -0,0 +1,28 @@
package loghandler
import (
"context"
"log/slog"
"runtime"
)
type source struct{}
func NewSource() *source {
return &source{}
}
func (h *source) Wrap(next LogHandler) LogHandler {
return LogHandlerFunc(func(ctx context.Context, record slog.Record) error {
if record.PC != 0 {
frame, _ := runtime.CallersFrames([]uintptr{record.PC}).Next()
record.AddAttrs(
slog.String("code.filepath", frame.File),
slog.String("code.function", frame.Function),
slog.Int("code.lineno", frame.Line),
)
}
return next.Handle(ctx, record)
})
}

View File

@@ -0,0 +1,37 @@
package loghandler
import (
"bytes"
"context"
"encoding/json"
"log/slog"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSource(t *testing.T) {
src := NewSource()
buf := bytes.NewBuffer(nil)
logger := slog.New(&handler{base: slog.NewJSONHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}), wrappers: []Wrapper{src}})
logger.InfoContext(context.Background(), "test")
m := make(map[string]any)
err := json.Unmarshal(buf.Bytes(), &m)
require.NoError(t, err)
assert.Contains(t, m, "code.filepath")
assert.Contains(t, m, "code.function")
assert.Contains(t, m, "code.lineno")
assert.Contains(t, m["code.filepath"], "source_test.go")
assert.Contains(t, m["code.function"], "TestSource")
assert.NotZero(t, m["code.lineno"])
// Ensure the nested "source" key is not present.
assert.NotContains(t, m, "source")
assert.NotContains(t, m, "code")
}

View File

@@ -1,32 +0,0 @@
package pprof
import "github.com/SigNoz/signoz/pkg/factory"
// Config holds the configuration for the pprof server.
type Config struct {
Enabled bool `mapstructure:"enabled"`
Address string `mapstructure:"address"`
}
func NewConfigFactory() factory.ConfigFactory {
return factory.NewConfigFactory(factory.MustNewName("pprof"), newConfig)
}
func newConfig() factory.Config {
return Config{
Enabled: true,
Address: "0.0.0.0:6060",
}
}
func (c Config) Validate() error {
return nil
}
func (c Config) Provider() string {
if c.Enabled {
return "http"
}
return "noop"
}

View File

@@ -1,42 +0,0 @@
package pprof
import (
"context"
"testing"
"github.com/SigNoz/signoz/pkg/config"
"github.com/SigNoz/signoz/pkg/config/envprovider"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewWithEnvProvider(t *testing.T) {
t.Setenv("SIGNOZ_PPROF_ENABLED", "false")
t.Setenv("SIGNOZ_PPROF_ADDRESS", "127.0.0.1:6061")
conf, err := config.New(
context.Background(),
config.ResolverConfig{
Uris: []string{"env:"},
ProviderFactories: []config.ProviderFactory{
envprovider.NewFactory(),
},
},
[]factory.ConfigFactory{
NewConfigFactory(),
},
)
require.NoError(t, err)
actual := Config{}
err = conf.Unmarshal("pprof", &actual)
require.NoError(t, err)
expected := Config{
Enabled: false,
Address: "127.0.0.1:6061",
}
assert.Equal(t, expected, actual)
}

View File

@@ -1,58 +0,0 @@
package httppprof
import (
"context"
"log/slog"
"net/http"
httppprof "net/http/pprof"
runtimepprof "runtime/pprof"
"github.com/SigNoz/signoz/pkg/factory"
httpserver "github.com/SigNoz/signoz/pkg/http/server"
"github.com/SigNoz/signoz/pkg/pprof"
)
type provider struct {
server *httpserver.Server
}
func NewFactory() factory.ProviderFactory[pprof.PProf, pprof.Config] {
return factory.NewProviderFactory(factory.MustNewName("http"), New)
}
func New(_ context.Context, settings factory.ProviderSettings, config pprof.Config) (pprof.PProf, error) {
server, err := httpserver.New(
settings.Logger.With(slog.String("pkg", "github.com/SigNoz/signoz/pkg/pprof/httppprof")),
httpserver.Config{Address: config.Address},
newHandler(),
)
if err != nil {
return nil, err
}
return &provider{server: server}, nil
}
func (provider *provider) Start(ctx context.Context) error {
return provider.server.Start(ctx)
}
func (provider *provider) Stop(ctx context.Context) error {
return provider.server.Stop(ctx)
}
func newHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/debug/pprof/", httppprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", httppprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", httppprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", httppprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", httppprof.Trace)
// Register the runtime profiles in the same order returned by runtime/pprof.Profiles().
for _, profile := range runtimepprof.Profiles() {
mux.Handle("/debug/pprof/"+profile.Name(), httppprof.Handler(profile.Name()))
}
return mux
}

View File

@@ -1,35 +0,0 @@
package nooppprof
import (
"context"
"sync"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/pprof"
)
type provider struct {
stopC chan struct{}
stopOnce sync.Once
}
func NewFactory() factory.ProviderFactory[pprof.PProf, pprof.Config] {
return factory.NewProviderFactory(factory.MustNewName("noop"), New)
}
func New(_ context.Context, _ factory.ProviderSettings, _ pprof.Config) (pprof.PProf, error) {
return &provider{stopC: make(chan struct{})}, nil
}
func (provider *provider) Start(context.Context) error {
<-provider.stopC
return nil
}
func (provider *provider) Stop(context.Context) error {
provider.stopOnce.Do(func() {
close(provider.stopC)
})
return nil
}

View File

@@ -1,8 +0,0 @@
package pprof
import "github.com/SigNoz/signoz/pkg/factory"
// PProf is the interface that wraps the pprof service lifecycle.
type PProf interface {
factory.Service
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/http"
_ "net/http/pprof" // http profiler
"slices"
"github.com/SigNoz/signoz/pkg/cache/memorycache"
@@ -293,6 +294,15 @@ func (s *Server) Start(ctx context.Context) error {
s.unavailableChannel <- healthcheck.Unavailable
}()
go func() {
slog.Info("Starting pprof server", "addr", constants.DebugHttpPort)
err = http.ListenAndServe(constants.DebugHttpPort, nil)
if err != nil {
slog.Error("Could not start pprof server", errors.Attr(err))
}
}()
go func() {
slog.Info("Starting OpAmp Websocket server", "addr", constants.OpAmpWsEndpoint)
err := s.opampServer.Start(constants.OpAmpWsEndpoint)

View File

@@ -14,6 +14,7 @@ import (
const (
HTTPHostPort = "0.0.0.0:8080" // Address to serve http (query service)
PrivateHostPort = "0.0.0.0:8085" // Address to server internal services like alert manager
DebugHttpPort = "0.0.0.0:6060" // Address to serve http (pprof)
OpAmpWsEndpoint = "0.0.0.0:4320" // address for opamp websocket
)

View File

@@ -23,7 +23,6 @@ import (
"github.com/SigNoz/signoz/pkg/instrumentation"
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/pprof"
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/ruler"
@@ -51,9 +50,6 @@ type Config struct {
// Instrumentation config
Instrumentation instrumentation.Config `mapstructure:"instrumentation"`
// PProf config
PProf pprof.Config `mapstructure:"pprof"`
// Analytics config
Analytics analytics.Config `mapstructure:"analytics"`
@@ -126,7 +122,6 @@ func NewConfig(ctx context.Context, logger *slog.Logger, resolverConfig config.R
global.NewConfigFactory(),
version.NewConfigFactory(),
instrumentation.NewConfigFactory(),
pprof.NewConfigFactory(),
analytics.NewConfigFactory(),
web.NewConfigFactory(),
cache.NewConfigFactory(),

View File

@@ -34,9 +34,6 @@ import (
"github.com/SigNoz/signoz/pkg/modules/session/implsession"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/modules/user/impluser"
"github.com/SigNoz/signoz/pkg/pprof"
"github.com/SigNoz/signoz/pkg/pprof/httppprof"
"github.com/SigNoz/signoz/pkg/pprof/nooppprof"
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/prometheus/clickhouseprometheus"
"github.com/SigNoz/signoz/pkg/querier"
@@ -92,13 +89,6 @@ func NewWebProviderFactories() factory.NamedMap[factory.ProviderFactory[web.Web,
)
}
func NewPProfProviderFactories() factory.NamedMap[factory.ProviderFactory[pprof.PProf, pprof.Config]] {
return factory.MustNewNamedMap(
httppprof.NewFactory(),
nooppprof.NewFactory(),
)
}
func NewSQLStoreProviderFactories() factory.NamedMap[factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config]] {
return factory.MustNewNamedMap(
sqlitesqlstore.NewFactory(sqlstorehook.NewLoggingFactory(), sqlstorehook.NewInstrumentationFactory()),

View File

@@ -107,17 +107,6 @@ func New(
// Get the provider settings from instrumentation
providerSettings := instrumentation.ToProviderSettings()
pprofService, err := factory.NewProviderFromNamedMap(
ctx,
providerSettings,
config.PProf,
NewPProfProviderFactories(),
config.PProf.Provider(),
)
if err != nil {
return nil, err
}
// Initialize analytics just after instrumentation, as providers might require it
analytics, err := factory.NewProviderFromNamedMap(
ctx,
@@ -459,7 +448,6 @@ func New(
registry, err := factory.NewRegistry(
instrumentation.Logger(),
factory.NewNamedService(factory.MustNewName("instrumentation"), instrumentation),
factory.NewNamedService(factory.MustNewName("pprof"), pprofService),
factory.NewNamedService(factory.MustNewName("analytics"), analytics),
factory.NewNamedService(factory.MustNewName("alertmanager"), alertmanager),
factory.NewNamedService(factory.MustNewName("licensing"), licensing),