From 378e03c22081e1653e8e3fb7b5ef186b98f5a4f3 Mon Sep 17 00:00:00 2001 From: Ankur Shrivastava Date: Mon, 24 Apr 2023 17:49:43 +0800 Subject: [PATCH] updating setup steps --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++- config/README.md | 36 +++-------------------- config/config.go | 28 ++---------------- core.go | 23 +++++++++------ initializers.go | 64 ++++++++++++++++++++++++++--------------- 5 files changed, 135 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index e9da394..c230631 100755 --- a/README.md +++ b/README.md @@ -40,6 +40,14 @@ The core module is the base module for cold brew and provides the base implement ## Index +- [func ConfigureInterceptors(DoNotLogGRPCReflection bool, traceHeaderName string)](<#func-configureinterceptors>) +- [func SetupEnvironment(env string)](<#func-setupenvironment>) +- [func SetupHystrixPrometheus()](<#func-setuphystrixprometheus>) +- [func SetupLogger(logLevel string, jsonlogs bool) error](<#func-setuplogger>) +- [func SetupNROpenTelemetry(serviceName, license, version string, ratio float64) error](<#func-setupnropentelemetry>) +- [func SetupNewRelic(serviceName, apiKey string, tracing bool) error](<#func-setupnewrelic>) +- [func SetupReleaseName(rel string)](<#func-setupreleasename>) +- [func SetupSentry(dsn string)](<#func-setupsentry>) - [type CB](<#type-cb>) - [func New(c config.Config) CB](<#func-new>) - [type CBGracefulStopper](<#type-cbgracefulstopper>) @@ -47,6 +55,70 @@ The core module is the base module for cold brew and provides the base implement - [type CBStopper](<#type-cbstopper>) +## func [ConfigureInterceptors]() + +```go +func ConfigureInterceptors(DoNotLogGRPCReflection bool, traceHeaderName string) +``` + +ConfigureInterceptors configures the interceptors package with the provided DoNotLogGRPCReflection is a boolean that indicates whether to log the grpc.reflection.v1alpha.ServerReflection service calls in logs traceHeaderName is the name of the header to use for tracing \(e.g. X\-Trace\-Id\) \- if empty, defaults to X\-Trace\-Id + +## func [SetupEnvironment]() + +```go +func SetupEnvironment(env string) +``` + +SetupEnvironment sets the environment This is used to identify the environment in Sentry and New Relic env is the environment to set for the service \(e.g. prod, staging, dev\) + +## func [SetupHystrixPrometheus]() + +```go +func SetupHystrixPrometheus() +``` + +SetupHystrixPrometheus sets up the hystrix metrics This is a workaround for hystrix\-go not supporting the prometheus registry + +## func [SetupLogger]() + +```go +func SetupLogger(logLevel string, jsonlogs bool) error +``` + +SetupLogger sets up the logger It uses the coldbrew logger to log messages to stdout logLevel is the log level to set for the logger jsonlogs is a boolean to enable or disable json logs + +## func [SetupNROpenTelemetry]() + +```go +func SetupNROpenTelemetry(serviceName, license, version string, ratio float64) error +``` + +setupOpenTelemetry sets up the OpenTelemetry tracing It uses the New Relic OTLP exporter to send traces to New Relic One APM and Insights serviceName is the name of the service license is the New Relic license key version is the version of the service ratio is the sampling ratio to use for traces + +## func [SetupNewRelic]() + +```go +func SetupNewRelic(serviceName, apiKey string, tracing bool) error +``` + +SetupNewRelic sets up the New Relic tracing and monitoring agent for the service It uses the New Relic Go Agent to send traces to New Relic One APM and Insights serviceName is the name of the service apiKey is the New Relic license key tracing is a boolean to enable or disable tracing + +## func [SetupReleaseName]() + +```go +func SetupReleaseName(rel string) +``` + +SetupReleaseName sets the release name This is used to identify the release in Sentry rel is the release name to set for the service \(e.g. v1.0.0\) + +## func [SetupSentry]() + +```go +func SetupSentry(dsn string) +``` + +SetupSentry sets up the Sentry notifier It uses the Sentry HTTP Transport to send errors to Sentry server dsn is the Sentry DSN to use for sending errors + ## type [CB]() CB is the interface that wraps coldbrew methods. @@ -67,7 +139,7 @@ type CB interface { } ``` -### func [New]() +### func [New]() ```go func New(c config.Config) CB diff --git a/config/README.md b/config/README.md index f908c2f..3b39466 100755 --- a/config/README.md +++ b/config/README.md @@ -13,39 +13,9 @@ import "github.com/go-coldbrew/core/config" - [type Config](<#type-config>) -## type [Config]() +## type [Config]() -### Config is the configuration for the Coldbrew server -It is populated from environment variables and has sensible defaults for all fields so that you can just use it as is without any configuration -The following environment variables are supported and can be used to override the defaults for the fields - -``` -LISTEN_HOST - Host to listen on, defaults to -GRPC_PORT - GRPC Port, defaults to 9090 -HTTP_PORT - HTTP Port, defaults to 9091 -APP_NAME - Name of the Application -ENVIRONMENT - Environment e.g. Production / Integration / Development -LOG_LEVEL - LogLevel to print, default to info -JSON_LOGS - Should logs be emitted in json format, defaults to true -DISABLE_SWAGGER - Should we disable swagger at /swagger/, defaults to false -DISABLE_DEBUG - Should we disable go debug at /debug/, defaults to false -DISABLE_PROMETHEUS - Should we disable prometheus at /metrics, defaults to false -ENABLE_PROMETHEUS_GRPC_HISTOGRAM - Enables grpc request histograms in prometheus reporting -NEW_RELIC_LICENSE_KEY - The License key for NewRelic metrics reporting -NEW_RELIC_DISTRIBUTED_TRACING - Enable NewRelic Distributed Tracing -NEW_RELIC_OPENTELEMETRY - Enable new relic opentelemetry -NEW_RELIC_OPENTELEMETRY_SAMPLE - Sampling ratio for NR opentelemetry -SENTRY_DSN - DSN for reporting errors to sentry -RELEASE_NAME - Name of this release -DISABLE_GRPC_REFLECTION - When set disable the GRPC reflecttion server which can be useful for tools like grpccurl, default false -TRACE_HEADER_NAME - Trace header, when this HTTP header is present CB will add the value to log/trace contexts -HTTP_HEADER_PREFIX - When we match this HTTP header prefix, we forward append the values to grpc metadata -DO_NOT_LOG_GRPC_REFLECTION - Should we log calls to GRPC reflection API, defaults to true -DISABLE_SIGNAL_HANDLER - Should we disable signal handler, defaults to false and CB handles all SIG_INT/SIG_TERM -SHUTDOWN_DURATION_IN_SECONDS - Duration for which CB will wait for calls to complete before shutting down the server -GRPC_GRACEFUL_DURATION_IN_SECONDS - Duration for which CB will wait for healthcheck fail to be propagated before initiating server shutdown once shutdown is initiated all new calls will fail -USE_JSON_BUILTIN_MARSHALLER - UseJSONBuiltinMarshaller switches marshaler for application/json to encoding/json -``` +Config is the configuration for the Coldbrew server It is populated from environment variables and has sensible defaults for all fields so that you can just use it as is without any configuration The following environment variables are supported and can be used to override the defaults for the fields ```go type Config struct { @@ -79,6 +49,8 @@ type Config struct { NewRelicOpentelemetry bool `envconfig:"NEW_RELIC_OPENTELEMETRY" default:"true"` // Sampling ratio for NR opentelemetry NewRelicOpentelemetrySample float64 `envconfig:"NEW_RELIC_OPENTELEMETRY_SAMPLE" default:"0.2"` + // The name of the application in NewRelic + NewRelicAppname string `envconfig:"NEW_RELIC_APPNAME" default:""` // DSN for reporting errors to sentry SentryDSN string `envconfig:"SENTRY_DSN" default:""` // Name of this release diff --git a/config/config.go b/config/config.go index 17b9733..994f321 100644 --- a/config/config.go +++ b/config/config.go @@ -3,32 +3,6 @@ package config // Config is the configuration for the Coldbrew server // It is populated from environment variables and has sensible defaults for all fields so that you can just use it as is without any configuration // The following environment variables are supported and can be used to override the defaults for the fields -// -// LISTEN_HOST - Host to listen on, defaults to -// GRPC_PORT - GRPC Port, defaults to 9090 -// HTTP_PORT - HTTP Port, defaults to 9091 -// APP_NAME - Name of the Application -// ENVIRONMENT - Environment e.g. Production / Integration / Development -// LOG_LEVEL - LogLevel to print, default to info -// JSON_LOGS - Should logs be emitted in json format, defaults to true -// DISABLE_SWAGGER - Should we disable swagger at /swagger/, defaults to false -// DISABLE_DEBUG - Should we disable go debug at /debug/, defaults to false -// DISABLE_PROMETHEUS - Should we disable prometheus at /metrics, defaults to false -// ENABLE_PROMETHEUS_GRPC_HISTOGRAM - Enables grpc request histograms in prometheus reporting -// NEW_RELIC_LICENSE_KEY - The License key for NewRelic metrics reporting -// NEW_RELIC_DISTRIBUTED_TRACING - Enable NewRelic Distributed Tracing -// NEW_RELIC_OPENTELEMETRY - Enable new relic opentelemetry -// NEW_RELIC_OPENTELEMETRY_SAMPLE - Sampling ratio for NR opentelemetry -// SENTRY_DSN - DSN for reporting errors to sentry -// RELEASE_NAME - Name of this release -// DISABLE_GRPC_REFLECTION - When set disable the GRPC reflecttion server which can be useful for tools like grpccurl, default false -// TRACE_HEADER_NAME - Trace header, when this HTTP header is present CB will add the value to log/trace contexts -// HTTP_HEADER_PREFIX - When we match this HTTP header prefix, we forward append the values to grpc metadata -// DO_NOT_LOG_GRPC_REFLECTION - Should we log calls to GRPC reflection API, defaults to true -// DISABLE_SIGNAL_HANDLER - Should we disable signal handler, defaults to false and CB handles all SIG_INT/SIG_TERM -// SHUTDOWN_DURATION_IN_SECONDS - Duration for which CB will wait for calls to complete before shutting down the server -// GRPC_GRACEFUL_DURATION_IN_SECONDS - Duration for which CB will wait for healthcheck fail to be propagated before initiating server shutdown once shutdown is initiated all new calls will fail -// USE_JSON_BUILTIN_MARSHALLER - UseJSONBuiltinMarshaller switches marshaler for application/json to encoding/json type Config struct { // Host to listen on ListenHost string `envconfig:"LISTEN_HOST" default:"0.0.0.0"` @@ -60,6 +34,8 @@ type Config struct { NewRelicOpentelemetry bool `envconfig:"NEW_RELIC_OPENTELEMETRY" default:"true"` // Sampling ratio for NR opentelemetry NewRelicOpentelemetrySample float64 `envconfig:"NEW_RELIC_OPENTELEMETRY_SAMPLE" default:"0.2"` + // The name of the application in NewRelic + NewRelicAppname string `envconfig:"NEW_RELIC_APPNAME" default:""` // DSN for reporting errors to sentry SentryDSN string `envconfig:"SENTRY_DSN" default:""` // Name of this release diff --git a/core.go b/core.go index b2d4c24..ef3f639 100644 --- a/core.go +++ b/core.go @@ -55,18 +55,23 @@ func (c *cb) SetOpenAPIHandler(handler http.Handler) { c.openAPIHandler = handler } +// processConfig processes the config and sets up the logger, newrelic, sentry, environment, release name, jaeger, hystrix prometheus and signal handler func (c *cb) processConfig() { - setupLogger(c.config.LogLevel, c.config.JSONLogs) - setupNewRelic(c.config.AppName, c.config.NewRelicLicenseKey, c.config.NewRelicDistributedTracing) - setupSentry(c.config.SentryDSN) - setupEnvironment(c.config.Environment) - setupReleaseName(c.config.ReleaseName) + SetupLogger(c.config.LogLevel, c.config.JSONLogs) + nrName := c.config.AppName + if nrName == "" { + nrName = c.config.AppName + } + SetupNewRelic(nrName, c.config.NewRelicLicenseKey, c.config.NewRelicDistributedTracing) + SetupSentry(c.config.SentryDSN) + SetupEnvironment(c.config.Environment) + SetupReleaseName(c.config.ReleaseName) cls := setupJaeger(c.config.AppName) if cls != nil { c.closers = append(c.closers, cls) } - setupHystrix() - configureInterceptors(c.config.DoNotLogGRPCReflection, c.config.TraceHeaderName) + SetupHystrixPrometheus() + ConfigureInterceptors(c.config.DoNotLogGRPCReflection, c.config.TraceHeaderName) if !c.config.DisableSignalHandler { dur := time.Second * 10 if c.config.ShutdownDurationInSeconds > 0 { @@ -78,13 +83,15 @@ func (c *cb) processConfig() { grpc_prometheus.EnableHandlingTimeHistogram() } if c.config.NewRelicOpentelemetry { - setupNROpenTelemetry(c.config.AppName, c.config.NewRelicLicenseKey, c.config.ReleaseName, c.config.NewRelicOpentelemetrySample) + SetupNROpenTelemetry(nrName, c.config.NewRelicLicenseKey, c.config.ReleaseName, c.config.NewRelicOpentelemetrySample) } } // https://grpc-ecosystem.github.io/grpc-gateway/docs/operations/tracing/#opentracing-support var grpcGatewayTag = opentracing.Tag{Key: string(ext.Component), Value: "grpc-gateway"} +// tracingWrapper is a middleware that creates a new span for each incoming request. +// It also adds the span to the context so it can be used by other middlewares or handlers to add additional tags. func tracingWrapper(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { parentSpanContext, err := opentracing.GlobalTracer().Extract( diff --git a/initializers.go b/initializers.go index 450e3ff..d4a8242 100644 --- a/initializers.go +++ b/initializers.go @@ -32,12 +32,15 @@ import ( semconv "go.opentelemetry.io/otel/semconv/v1.12.0" ) -// setupNewRelic sets up the New Relic tracing and monitoring agent for the service +// SetupNewRelic sets up the New Relic tracing and monitoring agent for the service // It uses the New Relic Go Agent to send traces to New Relic One APM and Insights -func setupNewRelic(serviceName, apiKey string, tracing bool) { +// serviceName is the name of the service +// apiKey is the New Relic license key +// tracing is a boolean to enable or disable tracing +func SetupNewRelic(serviceName, apiKey string, tracing bool) error { if strings.TrimSpace(apiKey) == "" { log.Info(context.Background(), "Not initializing NewRelic because token is empty") - return + return nil } app, err := newrelic.NewApplication( @@ -48,43 +51,51 @@ func setupNewRelic(serviceName, apiKey string, tracing bool) { ) if err != nil { log.Error(context.Background(), "msg", "NewRelic could not be initialized", "err", err) - return + return err } nrutil.SetNewRelicApp(app) log.Info(context.Background(), "NewRelic initialized for "+serviceName) + return nil } -// setupLogger sets up the logger +// SetupLogger sets up the logger // It uses the coldbrew logger to log messages to stdout -func setupLogger(logLevel string, jsonlogs bool) { +// logLevel is the log level to set for the logger +// jsonlogs is a boolean to enable or disable json logs +func SetupLogger(logLevel string, jsonlogs bool) error { log.SetLogger(log.NewLogger(gokit.NewLogger(loggers.WithJSONLogs(jsonlogs)))) ll, err := loggers.ParseLevel(logLevel) if err != nil { log.Error(context.Background(), "err", "could not set log level", "level", logLevel) - } else { - log.SetLevel(ll) + return err } + log.SetLevel(ll) + return nil } -// setupSentry sets up the Sentry notifier +// SetupSentry sets up the Sentry notifier // It uses the Sentry HTTP Transport to send errors to Sentry server -func setupSentry(dsn string) { +// dsn is the Sentry DSN to use for sending errors +func SetupSentry(dsn string) { if dsn != "" { notifier.InitSentry(dsn) } } -// setupEnvironment sets the environment -func setupEnvironment(env string) { +// SetupEnvironment sets the environment +// This is used to identify the environment in Sentry and New Relic +// env is the environment to set for the service (e.g. prod, staging, dev) +func SetupEnvironment(env string) { if env != "" { notifier.SetEnvironment(env) } } -// setupReleaseName sets the release name +// SetupReleaseName sets the release name // This is used to identify the release in Sentry -func setupReleaseName(rel string) { +// rel is the release name to set for the service (e.g. v1.0.0) +func SetupReleaseName(rel string) { if rel != "" { notifier.SetRelease(rel) } @@ -117,10 +128,14 @@ func setupJaeger(serviceName string) io.Closer { // setupOpenTelemetry sets up the OpenTelemetry tracing // It uses the New Relic OTLP exporter to send traces to New Relic One APM and Insights -func setupNROpenTelemetry(serviceName, license, version string, ratio float64) { +// serviceName is the name of the service +// license is the New Relic license key +// version is the version of the service +// ratio is the sampling ratio to use for traces +func SetupNROpenTelemetry(serviceName, license, version string, ratio float64) error { if serviceName == "" || license == "" { log.Info(context.Background(), "msg", "not initializing NR opentelemetry tracing") - return + return nil } var headers = map[string]string{ "api-key": license, @@ -135,7 +150,7 @@ func setupNROpenTelemetry(serviceName, license, version string, ratio float64) { otlpExporter, err := otlptrace.New(context.Background(), otlptracegrpc.NewClient(clientOpts...)) if err != nil { log.Error(context.Background(), "msg", "creating OTLP trace exporter", "err", err) - return + return err } d := resource.Default() @@ -148,13 +163,13 @@ func setupNROpenTelemetry(serviceName, license, version string, ratio float64) { ) if err != nil { log.Error(context.Background(), "msg", "creating OTLP resource", "err", err) - return + return err } r, err := resource.Merge(d, res) if err != nil { log.Error(context.Background(), "msg", "merging OTLP resource", "err", err) - return + return err } tracerProvider := sdktrace.NewTracerProvider( @@ -169,17 +184,20 @@ func setupNROpenTelemetry(serviceName, license, version string, ratio float64) { otel.SetTracerProvider(wrapperTracerProvider) opentracing.SetGlobalTracer(bridgeTracer) log.Info(context.Background(), "msg", "Initialized NR opentelemetry tracing") + return nil } -// setupHystrix sets up the hystrix metrics +// SetupHystrixPrometheus sets up the hystrix metrics // This is a workaround for hystrix-go not supporting the prometheus registry -func setupHystrix() { +func SetupHystrixPrometheus() { promC := hystrixprometheus.NewPrometheusCollector("hystrix", nil, prometheus.DefBuckets) metricCollector.Registry.Register(promC.Collector) } -// configureInterceptors configures the interceptors package with the provided -func configureInterceptors(DoNotLogGRPCReflection bool, traceHeaderName string) { +// ConfigureInterceptors configures the interceptors package with the provided +// DoNotLogGRPCReflection is a boolean that indicates whether to log the grpc.reflection.v1alpha.ServerReflection service calls in logs +// traceHeaderName is the name of the header to use for tracing (e.g. X-Trace-Id) - if empty, defaults to X-Trace-Id +func ConfigureInterceptors(DoNotLogGRPCReflection bool, traceHeaderName string) { if DoNotLogGRPCReflection { interceptors.FilterMethods = append(interceptors.FilterMethods, "grpc.reflection.v1alpha.ServerReflection") }