From 36ba78d6308c015b98d089a8a8c0bcbab44e2c67 Mon Sep 17 00:00:00 2001 From: boreq Date: Thu, 7 Dec 2023 12:40:12 +0100 Subject: [PATCH] Add uploading pyroscope metrics --- README.md | 27 ++++++++++++++++++++ cmd/event-service/di/wire.go | 4 +++ cmd/event-service/di/wire_gen.go | 2 +- cmd/event-service/main.go | 41 +++++++++++++++++++++++++++++++ go.mod | 2 ++ go.sum | 4 +++ service/adapters/config/config.go | 8 ++++++ service/config/config.go | 37 ++++++++++++++++++++++++++++ 8 files changed, 124 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9242e22..5348260 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,33 @@ set to `PRODUCTION`. Path to your Google Cloud credentials JSON file. Required when `EVENTS_ENVIRONMENT` is set to `PRODUCTION`. +### `EVENTS_PYROSCOPE_APPLICATION_NAME` + +Pyroscope application name. + +Optional, defaults to `events.nos.social`. Relevant only if Pyroscope server +address is set. + +### `EVENTS_PYROSCOPE_SERVER_ADDRESS` + +Pyroscope server address. If this variable is set to an empty string then +uploading metrics to a Pyroscope server is disabled. This should be a full +address e.g. `https://example.com`. + +Optional, defaults to an empty string disabling pushing Pyroscope metrics. + +### `PYROSCOPE_BASIC_AUTH_USER` + +User component of basic auth of the Pyroscope server. + +Optional, disabled by default. Relevant only if Pyroscope server address is set. + +### `PYROSCOPE_BASIC_AUTH_PASSWORD` + +Password component of basic auth of the Pyroscope server. + +Optional, disabled by default. Relevant only if Pyroscope server address is set. + ## Metrics See configuration for the address of our metrics endpoint. Many out-of-the-box diff --git a/cmd/event-service/di/wire.go b/cmd/event-service/di/wire.go index 9136f23..79cb5f0 100644 --- a/cmd/event-service/di/wire.go +++ b/cmd/event-service/di/wire.go @@ -103,6 +103,10 @@ func newTestAdaptersConfig(tb testing.TB) (config.Config, error) { fixtures.SomeString(), nil, fixtures.SomeFile(tb), + fixtures.SomeString(), + fixtures.SomeString(), + fixtures.SomeString(), + fixtures.SomeString(), ) } diff --git a/cmd/event-service/di/wire_gen.go b/cmd/event-service/di/wire_gen.go index 4e5f611..6cf0805 100644 --- a/cmd/event-service/di/wire_gen.go +++ b/cmd/event-service/di/wire_gen.go @@ -254,7 +254,7 @@ type TestApplication struct { } func newTestAdaptersConfig(tb testing.TB) (config.Config, error) { - return config.NewConfig(fixtures.SomeString(), config.EnvironmentDevelopment, logging.LevelDebug, fixtures.SomeString(), nil, fixtures.SomeFile(tb)) + return config.NewConfig(fixtures.SomeString(), config.EnvironmentDevelopment, logging.LevelDebug, fixtures.SomeString(), nil, fixtures.SomeFile(tb), fixtures.SomeString(), fixtures.SomeString(), fixtures.SomeString(), fixtures.SomeString()) } type buildTransactionSqliteAdaptersDependencies struct { diff --git a/cmd/event-service/main.go b/cmd/event-service/main.go index 2e69eb8..0c70be2 100644 --- a/cmd/event-service/main.go +++ b/cmd/event-service/main.go @@ -4,10 +4,13 @@ import ( "context" "fmt" "os" + "runtime" "github.com/boreq/errors" + "github.com/grafana/pyroscope-go" "github.com/planetary-social/nos-event-service/cmd/event-service/di" configadapters "github.com/planetary-social/nos-event-service/service/adapters/config" + "github.com/planetary-social/nos-event-service/service/config" ) func main() { @@ -25,6 +28,10 @@ func run() error { return errors.Wrap(err, "error creating a config") } + if err := setupPyroscope(conf); err != nil { + return errors.Wrap(err, "error setting up pyroscope") + } + service, cleanup, err := di.BuildService(ctx, conf) if err != nil { return errors.Wrap(err, "error building a service") @@ -37,3 +44,37 @@ func run() error { return service.Run(ctx) } + +func setupPyroscope(conf config.Config) error { + if conf.PyroscopeServerAddress() == "" { + return nil + } + + runtime.SetMutexProfileFraction(5) + runtime.SetBlockProfileRate(5) + + _, err := pyroscope.Start(pyroscope.Config{ + ApplicationName: conf.PyroscopeApplicationName(), + ServerAddress: conf.PyroscopeServerAddress(), + Logger: nil, + BasicAuthUser: conf.PyroscopeBasicAuthUser(), + BasicAuthPassword: conf.PyroscopeBasicAuthPassword(), + ProfileTypes: []pyroscope.ProfileType{ + pyroscope.ProfileCPU, + pyroscope.ProfileAllocObjects, + pyroscope.ProfileAllocSpace, + pyroscope.ProfileInuseObjects, + pyroscope.ProfileInuseSpace, + pyroscope.ProfileGoroutines, + pyroscope.ProfileMutexCount, + pyroscope.ProfileMutexDuration, + pyroscope.ProfileBlockCount, + pyroscope.ProfileBlockDuration, + }, + }) + if err != nil { + return errors.Wrap(err, "error starting profiling") + } + + return nil +} diff --git a/go.mod b/go.mod index 0c00b95..158b0e5 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/google/wire v0.5.0 github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.0 + github.com/grafana/pyroscope-go v1.0.4 github.com/hashicorp/go-multierror v1.1.1 github.com/mattn/go-sqlite3 v1.14.18 github.com/nbd-wtf/go-nostr v0.25.4 @@ -51,6 +52,7 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.8.0 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.4 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kr/text v0.2.0 // indirect diff --git a/go.sum b/go.sum index 5a6bcc0..33f0804 100644 --- a/go.sum +++ b/go.sum @@ -152,6 +152,10 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/pyroscope-go v1.0.4 h1:oyQX0BOkL+iARXzHuCdIF5TQ7/sRSel1YFViMHC7Bm0= +github.com/grafana/pyroscope-go v1.0.4/go.mod h1:0d7ftwSMBV/Awm7CCiYmHQEG8Y44Ma3YSjt+nWcWztY= +github.com/grafana/pyroscope-go/godeltaprof v0.1.4 h1:mDsJ3ngul7UfrHibGQpV66PbZ3q1T8glz/tK3bQKKEk= +github.com/grafana/pyroscope-go/godeltaprof v0.1.4/go.mod h1:1HSPtjU8vLG0jE9JrTdzjgFqdJ/VgN7fvxBNq3luJko= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= diff --git a/service/adapters/config/config.go b/service/adapters/config/config.go index 3a59d47..109b931 100644 --- a/service/adapters/config/config.go +++ b/service/adapters/config/config.go @@ -20,6 +20,10 @@ const ( envGooglePubsubProjectID = "GOOGLE_PUBSUB_PROJECT_ID" envGooglePubsubCredentialsJSONPath = "GOOGLE_PUBSUB_CREDENTIALS_JSON_PATH" envDatabasePath = "DATABASE_PATH" + envPyroscopeApplicationName = "PYROSCOPE_APPLICATION_NAME" + envPyroscopeServerAddress = "PYROSCOPE_SERVER_ADDRESS" + envPyroscopeBasicAuthUser = "PYROSCOPE_BASIC_AUTH_USER" + envPyroscopeBasicAuthPassword = "PYROSCOPE_BASIC_AUTH_PASSWORD" ) type EnvironmentConfigLoader struct { @@ -62,6 +66,10 @@ func (c *EnvironmentConfigLoader) Load() (config.Config, error) { c.getenv(envGooglePubsubProjectID), googlePubSubCredentialsJSON, c.getenv(envDatabasePath), + c.getenv(envPyroscopeApplicationName), + c.getenv(envPyroscopeServerAddress), + c.getenv(envPyroscopeBasicAuthUser), + c.getenv(envPyroscopeBasicAuthPassword), ) } diff --git a/service/config/config.go b/service/config/config.go index 221f15a..8ef6444 100644 --- a/service/config/config.go +++ b/service/config/config.go @@ -26,6 +26,11 @@ type Config struct { googlePubSubCredentialsJSON []byte databasePath string + + pyroscopeApplicationName string + pyroscopeServerAddress string + pyroscopeBasicAuthUser string + pyroscopeBasicAuthPassword string } func NewConfig( @@ -35,6 +40,10 @@ func NewConfig( googlePubSubProjectID string, googlePubSubCredentialsJSON []byte, databasePath string, + pyroscopeApplicationName string, + pyroscopeServerAddress string, + pyroscopeBasicAuthUser string, + pyroscopeBasicAuthPassword string, ) (Config, error) { c := Config{ listenAddress: listenAddress, @@ -43,6 +52,10 @@ func NewConfig( googlePubSubProjectID: googlePubSubProjectID, googlePubSubCredentialsJSON: googlePubSubCredentialsJSON, databasePath: databasePath, + pyroscopeApplicationName: pyroscopeApplicationName, + pyroscopeServerAddress: pyroscopeServerAddress, + pyroscopeBasicAuthUser: pyroscopeBasicAuthUser, + pyroscopeBasicAuthPassword: pyroscopeBasicAuthPassword, } c.setDefaults() @@ -77,10 +90,30 @@ func (c *Config) DatabasePath() string { return c.databasePath } +func (c *Config) PyroscopeApplicationName() string { + return c.pyroscopeApplicationName +} + +func (c *Config) PyroscopeServerAddress() string { + return c.pyroscopeServerAddress +} + +func (c *Config) PyroscopeBasicAuthUser() string { + return c.pyroscopeBasicAuthUser +} + +func (c *Config) PyroscopeBasicAuthPassword() string { + return c.pyroscopeBasicAuthPassword +} + func (c *Config) setDefaults() { if c.listenAddress == "" { c.listenAddress = ":8008" } + + if c.pyroscopeApplicationName == "" { + c.pyroscopeApplicationName = "events.nos.social" + } } func (c *Config) validate() error { @@ -109,5 +142,9 @@ func (c *Config) validate() error { return errors.New("missing database path") } + if c.pyroscopeApplicationName == "" { + return errors.New("missing pyroscope application name") + } + return nil }