{"id":21847539,"url":"https://github.com/scribd/go-sdk","last_synced_at":"2025-07-23T15:40:00.026Z","repository":{"id":37805851,"uuid":"284690540","full_name":"scribd/go-sdk","owner":"scribd","description":"Go SDK","archived":false,"fork":false,"pushed_at":"2025-04-28T18:21:55.000Z","size":827,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-04-28T19:31:10.637Z","etag":null,"topics":["go","managed-by-terraform","sdk","service-foundations"],"latest_commit_sha":null,"homepage":"https://github.com/scribd/go-sdk","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scribd.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-08-03T12:11:25.000Z","updated_at":"2025-03-04T17:20:59.000Z","dependencies_parsed_at":"2023-02-14T00:45:41.082Z","dependency_job_id":"9a7fdf33-5d22-4415-8079-eecb544151bb","html_url":"https://github.com/scribd/go-sdk","commit_stats":null,"previous_names":[],"tags_count":67,"template":false,"template_full_name":null,"purl":"pkg:github/scribd/go-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scribd%2Fgo-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scribd%2Fgo-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scribd%2Fgo-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scribd%2Fgo-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scribd","download_url":"https://codeload.github.com/scribd/go-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scribd%2Fgo-sdk/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266706626,"owners_count":23971904,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","managed-by-terraform","sdk","service-foundations"],"created_at":"2024-11-27T23:18:30.573Z","updated_at":"2025-07-23T15:40:00.001Z","avatar_url":"https://github.com/scribd.png","language":"Go","readme":"# Go SDK\n\nSDK, the Go version.\n\n## Table Of Contents\n\n- [Prerequisites](#prerequisites)\n- [SDK functionality](#sdk-functionality)\n    - [Application Configuration](#application-configuration)\n        - [Predefined application-agnostic configurations](#predefined-application-agnostic-configurations)\n        - [Custom application-specific configurations](#custom-application-specific-configurations)\n        - [Complex values representation](#complex-values-representation)\n        - [Environment-awareness](#environment-awareness)\n        - [Using application configuration in tests](#using-application-configuration-in-tests)\n    - [Logger](#logger)\n        - [Initialization and default configuration](#initialization-and-default-configuration)\n        - [Environment-awareness](#environment-awareness-1)\n        - [Log levels](#log-levels)\n        - [Structured logging](#structured-logging)\n    - [Logging \u0026 tracing middleware](#logging---tracing-middleware)\n        - [HTTP server middleware](#http-server-middleware)\n        - [gRPC server interceptors](#grpc-server-interceptors)\n        - [Formatting and handlers](#formatting-and-handlers)\n        - [Sentry error reporting](#sentry-error-reporting)\n    - [Database Connection](#database-connection)\n    - [Server](#server)\n        - [CORS settings](#cors-settings)\n        - [CORS middleware](#cors-middleware)\n    - [ORM Integration](#orm-integration)\n        - [Usage of ORM](#usage-of-orm)\n    - [PubSub](#pubsub)\n        - [Kafka specific configuration](#kafka-specific-configuration)\n        - [SQS specific configuration](#sqs-specific-configuration)\n    - [Cache](#cache)\n        - [Redis](#redis-specific-configuration)\n    - [Experimentation](#experimentation)\n        - [Initialization of Statsig](#initialization-of-statsig)\n        - [Usage of Statsig](#usage-of-statsig)\n    - [AWS configuration](#aws-configuration)\n        - [AWS Common configuration](#aws-common-configuration)\n        - [AWS Service configuration](#aws-service-configuration)\n- [APM \u0026 Instrumentation](#apm---instrumentation)\n    - [Request ID middleware](#request-id-middleware)\n        - [HTTP server Request ID middleware](#http-server-request-id-middleware)\n        - [gRPC server Request ID interceptors](#grpc-server-request-id-interceptors)\n    - [HTTP Router Instrumentation](#http-router-instrumentation)\n    - [gRPC server and client interceptors](#grpc-server-and-client-interceptors)\n    - [Database instrumentation \u0026 ORM logging](#database-instrumentation---orm-logging)\n        - [HTTP server middleware example](#http-server-middleware-example)\n        - [gRPC server interceptors example](#grpc-server-interceptors-example)\n    - [AWS Clients instrumentation](#aws-clients-instrumentation)\n    - [PubSub instrumentation and logging](#pubsub-instrumentation-and-logging)\n      - [Kafka](#kafka)\n    - [Cache instrumentation and logging](#cache-instrumentation-and-logging)\n      - [Redis](#redis)\n    - [Profiling](#profiling)\n    - [Custom Metrics](#custom-metrics)\n- [Using the `go-sdk` in isolation](#using-the--go-sdk--in-isolation)\n- [Developing the SDK](#developing-the-sdk)\n    - [Building the docker environment](#building-the-docker-environment)\n    - [Running tests within the docker environment](#running-tests-within-the-docker-environment)\n    - [Entering the docker environment](#entering-the-docker-environment)\n    - [Using a development build of `go-sdk`](#using-a-development-build-of--go-sdk-)\n    - [Commit messages](#commit-messages)\n- [Release](#release)\n- [Maintainers](#maintainers)\n\n## Prerequisites\n\n* [Go](https://golang.org) (version `1.24.0`).\n* [Docker](https://www.docker.com/) (version `19.03.2`).\n\n## SDK functionality\n\nThe SDK provides the following out-of-the-box functionality:\n\n* Application Configuration \u0026 Environment-awareness\n* Logger\n\n### Application Configuration\n\nThe SDK provides the framework for managing your application's configuration.\nAs it uses the excellent [Viper](https://github.com/spf13/viper) under the\nhood, `go-sdk` knows how to read configuration files (**YAML only**) and `ENV`\nvariables. `go-sdk` facilitates usage of predefined static configurations and\ncustom dynamic configurations. Lastly, the `go-sdk` is environment-aware. This\nmeans that it supports environment scoped configuration in the configuration\nfiles out-of-the-box.\n\n:warning: Environment variables will not be picked up by Viper if the keys are\nnot present in the respective YAML file. This is due to Viper loading the\nconfiguration by reading a YAML file **first**, and binding the respective ENV\nvariables on the fly.\n\n#### Predefined application-agnostic configurations\n\nThe predefined configurations have an associated type and expect their\nrespective configuration to be present in a file corresponding to their name.\n\nThe list of predefined top-level configurations:\n* `Server`, containing the application server configurations, expects a\n  `config/server.yml` configuration file.\n* `Logger`, containing the application logger configuration, expects a\n  `config/logger.yml` configuration file.\n* `Database`, containing the application database configuration, expects a\n  `config/database.yml` configuration file.\n* `Cache`, containing the application Cache configuration, expects a\n  `config/cache.yml` configuration file.\n* `PubSub`, containing the application pubsub configuration, expects a\n  `config/pubsub.yml` configuration file.\n* `AWS`, containing the application AWS configuration, expects a\n  `config/aws.yml` configuration file.\n\nFor example, to get the host and the port at which the HTTP server listens on\nin an application:\n\n```go\nhost     := sdk.Config.Server.Host\nhttpPort := sdk.Config.Server.HTTPPort\n```\n\n#### Custom application-specific configurations\n\nAdditionally, the `go-sdk` allows developers to add custom configuration data\nin a `go-chassis`-powered application, through the `config/settings.yml` file.\nAs these configurations are custom and their type cannot (read: shouldn't) be\ninferred by the SDK, it exposes a familiar Viper-like interface for interaction\nwith them.\n\nAll of the application-specific configurations can be accessed through the\n`sdk.Config.App` object. For example, given a `config/settings.yml` with the\nfollowing contents:\n\n```yaml\n# config/settings.yml\ncommon: \u0026common\n  name: \"my-awesome-app\"\n\ndevelopment:\n  \u003c\u003c: *common\n\nstaging:\n  \u003c\u003c: *common\n\nproduction:\n  \u003c\u003c: *common\n```\n\nTo get the application name from the configuration one would use the following\nstatement:\n\n```go\napplicationName := sdk.Config.App.GetString(\"name\")\n```\n\nThe configuration variables can be overridden by a corresponding environment\nvariable; these variables must have the following format:\n\n```\nAPP_SETTINGS_NAME=my-really-awesome-app\n^^^ ^^^^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^\n|   |        |    + ----------- variable_value\n|   |        + ---------------- variable_name\n|   + ------------------------- config_file\n+ ----------------------------- prefix\n```\n\nThe environment variable has the precedence over the configuration file.\n\n#### Complex values representation\n\nSince `yaml` data type support is much richer than environment variables we have to take extra care if we want to\noverride `yaml` complex data types such as `list` and `dictionary`.\n\nTo represent the `list` we can use space-separated values:\n\n```bash\nAPP_SETTINGS_NAME=\"value1 value2\"\n```\n\nAnd then, on the application side we have to convert it to a string slice manually:\n\n```go\n// if the value is the part of `settings.yml`\nstringSlice := sdk.Config.App.StringSlice(\"name\")\n\n// or calling the Viper's API directly\nstringSlice := viper.GetStringSlice(\"app.settings.name\")\n```\n\nTo represent the `dictionary` we can use a JSON:\n\n```bash\nAPP_SETTINGS_NAME=\"{\\\"key\\\":\\\"value\\\"}\"\n```\n\nAnd then, on the application side we have to convert it to a string map manually:\n\n```go\n// if the value is the part of `settings.yml`\nstringMap := sdk.Config.App.StringMapString(\"name\")\n\n// or calling the Viper's API directly\nstringMap := viper.GetStringMapString(\"app.settings.name\")\n```\n\n#### Environment-awareness\n\nApplication configurations vary between environments, therefore the `go-sdk` is\nenvironment-aware. This means that any application that uses the `go-sdk`\nshould have an `APP_ENV` environment variable set. Applications built using the\n`go-chassis` have this out of the box. In absence of the `APP_ENV` variable,\nit's defaulted to `development`.\n\nThis means that any configuration files, placed in the `config` directory,\nshould contain environment namespacing. For example:\n\n```yaml\n# config/logger.yml\ncommon: \u0026common\n  console_enabled: true\n  console_json_format: true\n  console_level: \"info\"\n  file_enabled: false\n  file_json_format: true\n  file_level: \"trace\"\n\ndevelopment:\n  \u003c\u003c: *common\n  console_level: \"debug\"\n  file_enabled: true\n  file_level: \"debug\"\n\nstaging:\n  \u003c\u003c: *common\n\nproduction:\n  \u003c\u003c: *common\n```\n\nWhen run in a particular environment, by setting `APP_ENV`, it will load the\nrespective section from the YAML file. For example, when the application is\nloaded in `development` environment, `go-sdk` will automatically load the\nvalues from the `development` section. This convention is applied to all\nconfigurations supported by `go-sdk`.\n\n#### Using application configuration in tests\n\nWhen an application is using the SDK to load configurations, that includes\nloading application configurations in test environment as well. This means that\ndue to its [environment-aware](#environment-awareness) nature, by default the\nSDK will load the configuration from the YAML files in the `test` namespace.\n\nThis provides two ways to configure your application for testing:\n1. By adding a `test` namespace to the respective YAML file and adding the test\n   configuration there, or\n2. Using the provided structs and their setters/getters from the test files\n   themselves.\n\nAdding a `test` namespace to any configuration file, looks like:\n\n```yaml\n# config/logger.yml\ncommon: \u0026common\n  console_enabled: true\n  console_json_format: true\n  console_level: \"info\"\n  file_enabled: false\n  file_json_format: true\n  file_level: \"trace\"\n\ndevelopment:\n  \u003c\u003c: *common\n\ntest:\n  \u003c\u003c: *common\n  console_level: \"debug\"\n  file_enabled: true\n  file_level: \"debug\"\n\nstaging:\n  \u003c\u003c: *common\n\nproduction:\n  \u003c\u003c: *common\n```\n\nGiven the configuration above, the SDK will load out-of-the-box the `test`\nconfiguration and apply it to the logger.\n\nIn cases where we want to modify the configuration of an application in a test\nfile, we can simply use the constructor that `go-sdk` provides:\n\n```go\npackage main\n\nimport (\n    \"testing\"\n\n    sdkconfig \"github.com/scribd/go-sdk/pkg/configuration\"\n}\n\nvar (\n    // Config is SDK-powered application configuration.\n    Config *sdkconfig.Config\n)\n\nfunc SomeTest(t *testing.T) {\n    if Config, err = sdkconfig.NewConfig(); err != nil {\n        log.Fatalf(\"Failed to load SDK config: %s\", err.Error())\n    }\n\n    // Change application settings\n    Config.App.Set(\"key\", \"value\")\n\n    // Continue with testing...\n    setting := Config.App.GetString(\"key\") // returns \"value\"\n}\n```\n\n### Logger\n\n`go-sdk` ships with a logger, configured with sane defaults out-of-the-box.\nUnder the hood, it uses the popular\n[logrus](https://github.com/sirupsen/logrus) as a logger, in combination with\n[lumberjack](https://github.com/natefinch/lumberjack) for log rolling. It\nsupports log levels, formatting (plain or JSON), structured logging and\nstoring the output to a file and/or `STDOUT`.\n\n#### Initialization and default configuration\n\n`go-sdk` comes with a configuration built-in for the logger. That means that\ninitializing a new logger is as easy as:\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\n\tsdklogger \"github.com/scribd/go-sdk/pkg/logger\"\n)\n\nvar (\n\t// Logger is SDK-powered application logger.\n\tLogger sdklogger.Logger\n\terr    error\n)\n\nfunc main() {\n\tif loggerConfig, err := sdklogger.NewConfig(); err != nil {\n\t\tlog.Fatalf(\"Failed to initialize SDK logger configuration: %s\", err.Error())\n\t}\n\n\tif Logger, err = sdklogger.NewBuilder(loggerConfig).Build(); err != nil {\n\t\tlog.Fatalf(\"Failed to load SDK logger: %s\", err.Error())\n\t}\n}\n```\n\nThe logger initialized with the default configuration will use the `log/`\ndirectory in the root of the project to save the log files, with the name of\nthe current application environment and a `.log` extension.\n\n#### Environment-awareness\n\nMuch like with the application configuration, the logger follows the convention\nof loading a YAML file placed in the `config/logger.yml` file. This means that\nany project which imports the `logger` package from `go-sdk` can place the\nlogger configuration in their `config/logger.yml` file and the `go-sdk` will\nload that configuration when initializing the logger.\n\nSince the logger is also environment-aware, it will assume the presence of the\n`APP_ENV` environment variable and use it to set the name of the log file to\nthe environment name. For example, an application running with\n`APP_ENV=development` will have its log entries in `log/development.log` by\ndefault.\n\nAlso, it expects the respective `logger.yml` file to be environment namespaced.\nFor example:\n\n```yaml\n# config/logger.yml\ncommon: \u0026common\n  console_enabled: true\n  console_json_format: false\n  console_level: \"debug\"\n  file_enabled: true\n  file_json_format: false\n  file_level: \"debug\"\n\ndevelopment:\n  \u003c\u003c: *common\n  file_enabled: false\n\ntest:\n  \u003c\u003c: *common\n  console_enabled: false\n\nstaging:\n  \u003c\u003c: *common\n  console_json_format: true\n  console_level: \"debug\"\n  file_enabled: false\n\nproduction:\n  \u003c\u003c: *common\n  console_json_format: true\n  console_level: \"info\"\n  file_enabled: false\n```\n\nGiven the configuration above, the logger package will load out-of-the-box the\nconfiguration for the respective environment and apply it to the logger\ninstance.\n\n#### Log levels\n\nThe SDK's logger follows best practices when it comes to logging levels. It\nexposes multiple log levels, in order from lowest to highest:\n\n* Trace, invoked with `logger.Tracef`\n* Debug, invoked with `logger.Debugf`\n* Info, invoked with `logger.Infof`\n* Warn, invoked with `logger.Warnf`\n* Error, invoked with `logger.Errorf`\n* Fatal, invoked with `logger.Fatalf`\n* Panic, invoked with `logger.Panicf`\n\nEach of these log levels will produce log entries, while two of them have\nadditional functionality:\n\n* `logger.Fatalf` will add a log entry and exit the program with error code 1\n  (i.e. `exit(1)`)\n* `logger.Panicf` will add a log entry and invoke `panic` with all of the\n  arguments passed to the `Panicf` call\n\n#### Structured logging\n\nLoggers created using the `go-sdk` logger package, define a list of hardcoded\nfields that every log entry will be consisted of. This is done by design, with\nthe goal of a uniform log line structure across all Go services that use the\n`go-sdk`.\n\nAdding more field is made possible by the `logger.WithFields` function\nand by the `Builder` API:\n\n```go\nfields := map[string]string{ \"role\": \"server\" }\n\nif Logger, err = sdklogger.NewBuilder(loggerConfig).SetFields(fields).Build(); err != nil {\n\tlog.Fatalf(\"Failed to load SDK logger: %s\", err.Error())\n}\n```\n\nWhile adding more fields is easy to do, removing the three default\nfields from the log lines is, by design, very hard to do and highly\ndiscouraged.\n\nThe list of fields are:\n\n* `level`, indicating the log level of the log line\n* `message`, representing the actual log message\n* `timestamp`, the date \u0026 time of the log entry in ISO 8601 UTC format\n\n### Logging \u0026 tracing middleware\n\n`go-sdk` ships with a `Logger` middleware. When used, it tries to retrieve the `RequestID`, `TraceID` and `SpanID`\nfrom the incoming context. Then, middleware assigns those values to the log entries for further correlation.\n\n#### HTTP server middleware\n\n```go\nfunc main() {\n\tloggingMiddleware := sdkmiddleware.NewLoggingMiddleware(sdk.Logger)\n\n\thttpServer := server.\n\t\tNewHTTPServer(host, httpPort, applicationEnv, applicationName).\n\t\tMountMiddleware(loggingMiddleware.Handler).\n\t\tMountRoutes(routes)\n}\n```\n\n#### gRPC server interceptors\n\n```go\nfunc main() {\n    rpcServer, err := server.NewGrpcServer(\n        host,\n        grpcPort,\n        []grpc.ServerOption{\n            grpc.ChainUnaryInterceptor(\n                sdkinterceptors.TracingUnaryServerInterceptor(applicationName),\n                sdkinterceptors.RequestIDUnaryServerInterceptor(),\n                sdkinterceptors.LoggerUnaryServerInterceptor(logger),\n            ),\n            grpc.ChainStreamInterceptor(\n                sdkinterceptors.TracingStreamServerInterceptor(applicationName),\n                sdkinterceptors.RequestIDStreamServerInterceptor(),\n                sdkinterceptors.LoggerStreamServerInterceptor(logger),\n            ),\n        }...)\n}\n```\n\n#### Formatting and handlers\n\nThe logger ships with two different formats: a plaintext and JSON format. This\nmeans that the log entries can have a simple plaintext format, or a JSON\nformat. These options are configurable using the `ConsoleJSONFormat` and\n`FileJSONFormat` attributes of the logger `Config`.\n\nAn example of the plain text format:\n\n```\ntimestamp=\"2019-10-23T15:28:54Z\" level=info message=\"GET  HTTP/1.1 200\"\n```\n\nAn example of the JSON format:\n\n```json\n{\"level\":\"info\",\"message\":\"GET  HTTP/1.1 200\",\"timestamp\":\"2019-10-23T15:29:26Z\"}\n```\n\nThe logger handles the log entries and can store them in a file or send them to\n`STDOUT`. These options are configurable using the `ConsoleEnabled` and\n`FileEnabled` attributes of the logger `Config`.\n\n#### Sentry error reporting\n\nThe logger can be further instrumented to report error messages to\n[Sentry](https://docs.sentry.io).\n\nThe following instructions assume that a project has been\n[setup in Sentry](https://docs.sentry.io/error-reporting/quickstart/?platform=go)\nand that the corresponding DSN, or Data Source Name, is available.\n\nThe respective configuration file is `sentry.yml` and it should include\nthe following content:\n\n```yaml\n# config/sentry.yml\ncommon: \u0026common\n  dsn: \"\"\n\ndevelopment:\n  \u003c\u003c: *common\n\nstaging:\n  \u003c\u003c: *common\n\nproduction:\n  \u003c\u003c: *common\n  dsn: \"https://\u003ckey\u003e@sentry.io/\u003cproject\u003e\"\n```\n\nThe tracking can be enabled from the `Logger Builder` with the `SetTracking`\nfunction. To trigger tracking, use `logger` with `WithError(err)` as follows:\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"errors\"\n\n\tsdklogger   \"github.com/scribd/go-sdk/pkg/logger\"\n\tsdktracking \"github.com/scribd/go-sdk/pkg/tracking\"\n)\n\nvar (\n\t// Logger is SDK-powered application logger.\n\tLogger sdklogger.Logger\n\terr    error\n)\n\nfunc main() {\n\tif loggerConfig, err := sdklogger.NewConfig(); err != nil {\n\t\tlog.Fatalf(\"Failed to initialize SDK logger configuration: %s\", err.Error())\n\t}\n\n\tif trackingConfig, err := sdktracking.NewConfig(); err != nil {\n\t\tlog.Fatalf(\"Failed to initialize SDK tracking configuration: %s\", err.Error())\n\t}\n\n\tif Logger, err = sdklogger.NewBuilder(loggerConfig).SetTracking(trackingConfig).Build(); err != nil {\n\t\tlog.Fatalf(\"Failed to load SDK logger: %s\", err.Error())\n\t}\n\n\terr = errors.New(\"new error\")\n\tlogger.WithError(err).Errorf(\"trying sentry error functionality\")\n```\n\nA logger build with a valid tracking configuration will automatically\nreport to Sentry any errors emitted from the following log levels:\n\n- `Error`;\n- `Fatal`;\n- `Panic`;\n\nThe following environment variables are automatically used in the configuration\nof the Sentry client to enrich the error data:\n\n```\nenvironment: APP_ENV\nrelease: APP_VERSION\nserverName: APP_SERVER_NAME\n```\n\nMore about the\n[\"environment\" configuration](https://docs.sentry.io/enriching-error-data/environments/?platform=go)\nthe\n[\"server name\" configuration](https://docs.sentry.io/error-reporting/configuration/?platform=go#server-name)\nand the\n[\"release\" configuration](https://docs.sentry.io/workflow/releases/?platform=go)\ncan be found in the Sentry documentation.\n\n### Database Connection\n\n`go-sdk` ships with a default setup for a database connection, built on top of\nthe built-in [database\nconfiguration](#predefined-application-agnostic-configurations). The\nconfiguration that is read from the `config/database.yml` file is then used to\ncreate connection details, which are then used to compose a [data source\nname](https://en.wikipedia.org/wiki/Data_source_name) (DSN), for example:\n\n```\nusername:password@tcp(192.168.1.1:8080)/dbname?timeout=10s\u0026charset=utf8\u0026parseTime=True\u0026loc=Local\n```\n\nAt the moment, the database connection established using the `go-sdk` can be\nonly to a MySQL database. This is subject to change as the `go-sdk` evolves and\nbecomes more feature-complete.\n\nThe database connection can also be configured through a YAML file. `go-sdk`\nexpects this file to be placed at the `config/database.yml` path, within the\nroot of the project.\n\nEach of these connection details can be overriden by an `ENV` variable.\n\n| Setting  | Description                      | YAML variable | Environment variable (ENV) | Default     |\n| -------- | -------------------------------- | ------------- | -------------------------- | ----------- |\n| Host     | The database host                | `host`        | `APP_DATABASE_HOST`        | `localhost` |\n| Port     | The database port                | `port`        | `APP_DATABASE_PORT`        | `3306`      |\n| Database | The database name                | `database`    | `APP_DATABASE_DATABASE`    |             |\n| Username | App user name                    | `username`    | `APP_DATABASE_USERNAME`    |             |\n| Password | App user password                | `password`    | `APP_DATABASE_PASSWORD`    |             |\n| Pool     | Connection pool size             | `pool`        | `APP_DATABASE_POOL`        | `5`         |\n| Timeout  | Connection timeout (in seconds)  | `timeout`     | `APP_DATABASE_TIMEOUT`     | `1s`        |\n\nAn example `database.yml`:\n\n```yaml\ncommon: \u0026common\n  host: db\n  port: 3306\n  username: username\n  password: password\n  pool: 5\n  timeout: 1s\n\ndevelopment:\n  \u003c\u003c: *common\n  database: application_development\n\ntest:\n  \u003c\u003c: *common\n  database: application_test\n\nproduction:\n  \u003c\u003c: *common\n```\n\n### Server\n\n`go-sdk` provides a convenient way to create a basic Server configuration.\n\n| Setting     | Description                      | YAML variable    | Environment variable (ENV) |\n| --------    | -------------------------------- | -------------    | -------------------------- |\n| Host        | Server host                      | `host`           | `APP_SERVER_HOST`          |\n| HTTPPort    | HTTP port                        | `http_port`      | `APP_SERVER_HTTP_PORT`     |\n| GRPCPort    | gRPC port                        | `grpc_port`      | `APP_SERVER_GRPC_PORT`     |\n| HTTPTimeout | HTTP related timeouts            | `http_timeout`   |                            |\n| CORS        | CORS settings                    | `cors`           |                            |\n\nAn example `server.yml`:\n\n```yaml\ncommon: \u0026common\n  http_port: 8080\n  http_timeout:\n    write: 2s\n    read: 1s\n    idle: 90s\n  cors:\n    enabled: true\n    settings:\n      - path: \"*\"\n        allowed_origins: [\"*\"]\n        allowed_methods: [\"GET\"]\n        allowed_headers: [\"Allowed-Header\"]\n        exposed_headers: [\"Exposed-Header\"]\n        allow_credentials: true\n        max_age: 600\n```\n\n#### CORS settings\n\nCORS stands for [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/). `go-sdk` provides a basic\noptional configuration for the CORS settings that are passed to the HTTP middleware.\n\n| Setting  | Description                      | YAML variable | Environment variable (ENV) | Default |\n| -------- | -------------------------------- | ------------- | -------------------------- | ------- |\n| Enabled  | Whether CORS enabled or not      | `enabled`     | `APP_SERVER_CORS_ENABLED`  | false   |\n| Settings | List of CORS Settings            | `settings`    |                            |         |\n\n**PLEASE NOTE:** there is no way to specify `Settings` via environment variables as it is presented as\na nested structure. To configure the CORS, use the `server.yaml` file\n\nFor the full list of the CORS settings please refer to the inline documentation of the [server package](https://github.com/scribd/go-sdk/blob/main/pkg/server/config.go)\nAlso, consider looking into the documentation of the [cors library](https://github.com/rs/cors#parameters) which\ncurrently lays under the hood of the [CORS middleware](https://github.com/scribd/go-sdk/tree/main/pkg/middleware/cors.go).\n\n#### CORS middleware\n\nCORS [middleware](https://github.com/scribd/go-sdk/tree/main/pkg/middleware/cors.go) is a tiny wrapper around the [cors library](https://github.com/rs/cors#parameters).\nIt aims to provide an extensive way to configure CORS and at the same time not bind services to a particular\nimplementation.\n\nBelow is an example of the CORS middleware initialization:\n\n```go\npackage main\n\nimport (\n\t\"github.com/scribd/go-sdk/pkg/server\"\n\t\"log\"\n)\n\nfunc main() {\n\tconfig, err := server.NewConfig()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tcorsMiddleware := middleware.NewCorsMiddleware(config.Cors.Settings[0])\n\n\t// possible Server implementation\n\thttpServer := server.\n\t\tNewHTTPServer(host, httpPort, applicationEnv, applicationName).\n\t\tMountMiddleware(corsMiddleware.Handler).\n\t\tMountRoutes(routes)\n}\n```\n\n### ORM Integration\n\n`go-sdk` comes with an integration with the popular\n[gorm](https://gorm.io/gorm) as an object-relational mapper (ORM).\nUsing the configuration details, namely the [data source\nname](https://en.wikipedia.org/wiki/Data_source_name) (DSN) as their product,\ngorm is able to open a connection and give the `go-sdk` users a preconfigured\nready-to-use database connection with an ORM attached. This can be done as\nfollows:\n\n```go\npackage main\n\nimport (\n\tsdkdb \"github.com/scribd/go-sdk/pkg/database\"\n)\n\nfunc main() {\n\t// Loads the database configuration.\n\tdbConfig, err := sdkdb.NewConfig()\n\n\t// Establishes a gorm database connection using the connection details.\n\tdbConn, err := sdkdb.NewConnection(dbConfig)\n}\n```\n\nThe connection details are handled internally by the gorm integration, in other\nwords the `NewConnection` function, so they remain opaque for the user.\n\n#### Usage of ORM\n\nInvoking the constructor for a database connection, `go-sdk` returns a\n[Gorm-powered](https://gorm.io/gorm) database connection. It can be\nused right away to query the database:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\tsdkdb \"github.com/scribd/go-sdk/pkg/database\"\n\t\"gorm.io/gorm\"\n)\n\ntype User struct {\n\tgorm.Model\n\n\tName string `gorm:\"type:varchar(255)\"`\n\tAge  uint   `gorm:\"type:int\"`\n}\n\nfunc main() {\n\tdbConfig, err := sdkdb.NewConfig()\n\tdbConn, err := sdkdb.NewConnection(dbConfig)\n\n\tuser := User{Name: name, Age: age}\n\n\terrs := dbConn.Create(\u0026user).GetErrors()\n\tif errs != nil {\n\t\tfmt.Println(errs)\n\t}\n\tfmt.Println(user)\n}\n```\n\nTo learn more about Gorm, you can start with its [official\ndocumentation](https://gorm.io/docs/).\n\n### PubSub\n\n`go-sdk` provides a convenient way to create a basic [PubSub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) configuration.\n\nTop-level configuration is split into sections specific for the vendor. Let's look into the sample `pubsub.yml` file:\n\n```yaml\ncommon: \u0026common\n  kafka:\n    # Set by APP_PUBSUB_KAFKA_BROKER_URLS env variable\n    broker_urls:\n      - \"localhost:9092\"\n    # Set by APP_PUBSUB_KAFKA_CLIENT_ID env variable\n    client_id: \"test-app\"\n    # Set by APP_PUBSUB_KAFKA_CERT_PEM env variable\n    cert_pem: \"pem string\"\n    # Set by APP_PUBSUB_KAFKA_CERT_PEM_KEY env variable\n    cert_pem_key: \"pem key\"\n    security_protocol: \"ssl\"\n    ssl_verification_enabled: true\n    publisher:\n      # Set by APP_PUBSUB_KAFKA_PUBLISHER_MAX_ATTEMPTS env variable\n      max_attempts: 3\n      write_timeout: \"10s\"\n      topic: \"test-topic\"\n    subscriber:\n      topic: \"test-topic\"\n      group_id: \"\"\n  sqs:\n    subscriber:\n      # Set by APP_PUBSUB_SQS_SUBSCRIBER_ENABLED env variable\n      enabled: true\n      # Set by APP_PUBSUB_SQS_SUBSCRIBER_QUEUE_URL env variable\n      queue_url: \"https://sqs.us-east-2.amazonaws.com/123456789012/test-queue\"\n      # Set by APP_PUBSUB_SQS_SUBSCRIBER_MAX_MESSAGES env variable\n      max_messages: 10\n      # Set by APP_PUBSUB_SQS_SUBSCRIBER_WORKERS env variable\n      workers: 1\n      # Set by APP_PUBSUB_SQS_SUBSCRIBER_WAIT_TIME env variable\n      wait_time: \"10s\"\n    publisher:\n      # Set by APP_PUBSUB_SQS_PUBLISHER_ENABLED env variable\n      enabled: true\n      # Set by APP_PUBSUB_SQS_PUBLISHER_QUEUE_URL env variable\n      queue_url: \"https://sqs.us-east-2.amazonaws.com/123456789012/test-queue\"\n```\n\n#### Kafka specific configuration\n\nKafka top-level configuration contains generic options that fit both publisher and subscriber:\n\n| Setting                  | Description                                                       | YAML variable              | Environment variable (ENV)                  | Type         | Possible Values                                       |\n|--------------------------|-------------------------------------------------------------------|----------------------------|---------------------------------------------|--------------|-------------------------------------------------------|\n| Broker URLs              | Broker URLs to connect to                                         | `broker_urls`              | `APP_PUBSUB_KAFKA_BROKER_URLS`              | list(string) | localhost:9093                                        |\n| Client ID                | Client identifier                                                 | `client_id`                | `APP_PUBSUB_KAFKA_CLIENT_ID`                | string       | go-chassis-app                                        |\n| Cert PEM                 | Client's public key string (PEM format) used for authentication   | `cert_pem`                 | `APP_PUBSUB_KAFKA_CERT_PEM`                 | string       | long PEM string (deprecated)                          |\n| Cert PEM Key             | Client's private key string (PEM format) used for authentication  | `cert_pem_key`             | `APP_PUBSUB_KAFKA_CERT_PEM_KEY`             | string       | long PEM key string (deprecated)                      |\n| Security Protocol        | Protocol used to communicate with brokers                         | `security_protocol`        | `APP_PUBSUB_KAFKA_SECURITY_PROTOCOL`        | string       | plaintext, ssl, sasl_plaintext, sasl_ssl (deprecated) |\n| SSL verification enabled | Enable OpenSSL's builtin broker (server) certificate verification | `ssl_verification_enabled` | `APP_PUBSUB_KAFKA_SSL_VERIFICATION_ENABLED` | bool         | true, false (deprecated)                              |\n| Enable Metrics           | Enable publishing metrics for Kafka client broker connection      | `metrics_enabled`          | `APP_PUBSUB_KAFKA_METRICS_ENABLED`          | bool         | true, false                                           |\n\nTo break it down further `Publisher` and `Subscriber` have their own set of configuration options:\n\n**Publisher**:\n\n| Setting        | Description                                                                                                                     | YAML variable     | Environment variable (ENV)                   | Type   | Possible Values |\n|----------------|---------------------------------------------------------------------------------------------------------------------------------|-------------------|----------------------------------------------|--------|-----------------|\n| Max attempts   | Maximum amount of times to retry sending a failing Message                                                                      | `max_attempts`    | `APP_PUBSUB_KAFKA_PUBLISHER_MAX_ATTEMPTS`    | int    | 3               |\n| Write timeout  | Local message timeout. This value is only enforced locally and limits the time a produced message waits for successful delivery | `write_timeout`   | `APP_PUBSUB_KAFKA_PUBLISHER_WRITE_TIMEOUT`   | string | 10s             |\n| Topic          | Topic name                                                                                                                      | `topic`           | `APP_PUBSUB_KAFKA_PUBLISHER_TOPIC`           | string | topic           |\n| Enable Metrics | Enable publishing metrics for Kafka producer                                                                                    | `metrics_enabled` | `APP_PUBSUB_KAFKA_PUBLISHER_METRICS_ENABLED` | bool   | true, false     |\n\n**Subscriber**:\n\n| Setting           | Description                                                                                                                                                                                               | YAML variable         | Environment variable (ENV)                        | Type    | Possible Values |\n|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------|---------------------------------------------------|---------|-----------------|\n| Topic             | Topic name                                                                                                                                                                                                | `topic`               | `APP_PUBSUB_KAFKA_SUBSCRIBER_TOPIC`               | string  | topic           |\n| Group ID          | Client group id string. All clients sharing the same group id belong to the same group                                                                                                                    | `group_id`            | `APP_PUBSUB_KAFKA_SUBSCRIBER_GROUP_ID`            | string  | service-name    |\n| Enable Metrics    | Enable publishing metrics for Kafka consumer                                                                                                                                                              | `metrics_enabled`     | `APP_PUBSUB_KAFKA_SUBSCRIBER_METRICS_ENABLED`     | bool    | true, false     |\n| Enable AutoCommit | Enable AutoCommit option for Kafka consumer (default `true`)                                                                                                                                              | `auto_commit.enabled` | `APP_PUBSUB_KAFKA_SUBSCRIBER_AUTO_COMMIT_ENABLED` | bool    | true, false     |\n| Number of workers | Number of workers processing incoming messages                                                                                                                                                            | `workers`             | `APP_PUBSUB_KAFKA_SUBSCRIBER_WORKERS`             | number  | 1,2...          |\n| Block rebalance   | Indicates if the subscriber will block rebalance event when polling. For more information refer to the [franz-go documentation](https://pkg.go.dev/github.com/twmb/franz-go/pkg/kgo#BlockRebalanceOnPoll) | `block_rebalance`     | `APP_PUBSUB_KAFKA_SUBSCRIBER_BLOCK_REBALANCE`     | bool    | true, false     |\n| Max records       | Number of messages to retrieve from Kafka topic per polling iteration                                                                                                                                     | `max_records`         | `APP_PUBSUB_KAFKA_SUBSCRIBER_MAX_RECORDS`         | number  | 1,2...          |\n\n**IMPORTANT NOTES**:\n1. `APP_PUBSUB_KAFKA_SUBSCRIBER_WORKERS` is a setting for subscriber to increase the throughput. If the number is more than 1, messages will be processed concurrently in a separate goroutine. If the order of messages is important, this setting should be set to 1.\n2. `APP_PUBSUB_KAFKA_SUBSCRIBER_BLOCK_REBALANCE` is a setting for subscriber to block rebalance event when polling. If the setting is set to `true`, the subscriber will block the rebalance event when polling. This option helps to reduce the amount of duplicate messages. But it is important to be sure that the message processing is fast enough to avoid blocking the rebalance event for a long time. For more information refer to the [franz-go documentation](https://pkg.go.dev/github.com/twmb/franz-go/pkg/kgo#BlockRebalanceOnPoll).\n\nTo authenticate the requests to Kafka, Go SDK provides a configuration set for TLS and [SASL](https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer)\n\n**TLS**:\n\n| Setting               | Description                                                      | YAML variable          | Environment variable (ENV)                  | Type   | Possible Values                  |\n|-----------------------|------------------------------------------------------------------|------------------------|---------------------------------------------|--------|----------------------------------|\n| Enabled               | Whether TLS authentication is enabled or not                     | `enabled`              | `APP_PUBSUB_KAFKA_TLS_ENABLED`              | bool   | true, false                      |\n| Root certificate      | Ca Root CA certificate                                           | `ca`                   | `APP_PUBSUB_KAFKA_TLS_CA`                   | string | Root CA                          |\n| Cert PEM              | Client's public key string (PEM format) used for authentication  | `cert_pem`             | `APP_PUBSUB_KAFKA_TLS_CERT_PEM`             | string | long PEM string (deprecated)     |\n| Cert PEM Key          | Client's private key string (PEM format) used for authentication | `cert_pem_key`         | `APP_PUBSUB_KAFKA_TLS_CERT_PEM_KEY`         | string | long PEM key string (deprecated) |\n| Passphrase            | Passphrase is used in case the private key needs to be decrypted | `passphrase`           | `APP_PUBSUB_KAFKA_TLS_PASSPHRASE`           | string | pass phrase                      |\n| Skip TLS verification | Turn on / off TLS verification                                   | `insecure_skip_verify` | `APP_PUBSUB_KAFKA_TLS_INSECURE_SKIP_VERIFY` | bool   | true, false                      |\n\n**SASL**:\n\n| Setting                | Description                                                                                                                                                                                             | YAML variable   | Environment variable (ENV)                        | Type   | Possible Values    |\n|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|---------------------------------------------------|--------|--------------------|\n| Enabled                | Whether SASL authentication is enabled or not                                                                                                                                                           | `enabled`       | `APP_PUBSUB_KAFKA_SASL_ENABLED`                   | bool   | true, false        |\n| Mechanism              | Mechanism is a string representation of the SASL mechanism. Only \"plain\" and \"aws_msk_iam\" are supported                                                                                                | `mechanism`     | `APP_PUBSUB_KAFKA_SASL_MECHANISM`                 | string | plain, aws_msk_iam |\n| Username               | The username to authenticate Kafka requests                                                                                                                                                             | `username`      | `APP_PUBSUB_KAFKA_SASL_USERNAME`                  | string | username           |\n| Password               | The password to authenticate Kafka requests                                                                                                                                                             | `paswword`      | `APP_PUBSUB_KAFKA_SASL_PASSWORD`                  | string | password           |\n| AWS MSK IAM access key | AWS MSK IAM access key to authenticate AWS MSK requests                                                                                                                                                 | `access_key`    | `APP_PUBSUB_KAFKA_SASL_AWS_MSK_IAM_ACCESS_KEY`    | string | access key         |\n| AWS MSK IAM secret key | AWS MSK IAM secret key to authenticate AWS MSK requests                                                                                                                                                 | `secret_key`    | `APP_PUBSUB_KAFKA_SASL_AWS_MSK_IAM_SECRET_KEY`    | string | secret key         |\n| AWS STS Session Token  | SessionToken is used to authenticate AWS MSK requests via AWS STS service.\u003cbr/\u003eFor more information please check the [documentation](https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html). | `session_token` | `APP_PUBSUB_KAFKA_SASL_AWS_MSK_IAM_SESSION_TOKEN` | string | token              |\n| User agent             | The client's user agent string                                                                                                                                                                          | `user_agent`    | `APP_PUBSUB_KAFKA_SASL_AWS_MSK_IAM_USER_AGENT`    | string | user agent         |\n| Assumable role         | This role will be used to establish connection to AWS MSK ignoring the static credentials                                                                                                               | `role`          | `APP_PUBSUB_KAFKA_SASL_AWS_MSK_IAM_ROLE`          | string | AWS ARN string     |\n| Session name           | Will be passed to AWS STS when assuming the role                                                                                                                                                        | `session_name`  | `APP_PUBSUB_KAFKA_SASL_AWS_MSK_IAM_SESSION_NAME`  | string | application        |\n\n#### SQS specific configuration\n\nUnder the hood, the SQS integration relies on the provided SQS client. To learn more about the AWS services initialization, check the [AWS service configuration](#aws-service-configuration).\n\n**Subscriber**:\n\n| Setting           | Description                                                                                     | YAML variable         | Environment variable (ENV)                        | Type    | Possible Values |\n|-------------------|-------------------------------------------------------------------------------------------------|-----------------------|---------------------------------------------------|---------|-----------------|\n| Queue URL         | URL of the SQS queue                                                                            | `queue_url`           | `APP_PUBSUB_SQS_SUBSCRIBER_QUEUE_URL`             | string  | queue URL       |\n| Max messages      | Maximum number of messages to retrieve from the queue per polling iteration                     | `max_messages`        | `APP_PUBSUB_SQS_SUBSCRIBER_MAX_MESSAGES`          | number  | 1,2...          |\n| Number of workers | Number of workers processing incoming messages                                                  | `workers`             | `APP_PUBSUB_SQS_SUBSCRIBER_WORKERS`               | number  | 1,2...          |\n| Wait time         | The maximum amount of time in time.Duration to wait for messages to be available for retrieval. | `wait_time`           | `APP_PUBSUB_SQS_SUBSCRIBER_WAIT_TIME`             | string  | 10s             |\n\n**Publisher**:\n\n| Setting   | Description           | YAML variable         | Environment variable (ENV)                        | Type    | Possible Values |\n|-----------|-----------------------|-----------------------|---------------------------------------------------|---------|-----------------|\n| Queue URL | URL of the SQS queue  | `queue_url`           | `APP_PUBSUB_SQS_PUBLISHER_QUEUE_URL`              | string  | queue URL       |\n\n### Cache\n\n`go-sdk` provides a convenient way to create an application Cache configuration.\n\nTop-level configuration is split into sections specific for the vendor. Let's look into the sample `cache.yml` file:\n\n```yaml\ncommon: \u0026common\n  store: redis\n  redis:\n    url: \"\"\n    # Set by APP_CACHE_REDIS_ADDRS env variable\n    addrs:\n      - \"localhost:6379\"\n    # Set by APP_CACHE_REDIS_USERNAME env variable\n    username: \"username\"\n    # Set by APP_CACHE_REDIS_PASSWORD env variable\n    password: \"password\"\n```\n\n#### Redis specific configuration\n\nRedis top-level configuration is based on the official [go-redis UniversalOptions](https://pkg.go.dev/github.com/redis/go-redis/v9#UniversalOptions) library options. The following table describes the configuration options:\n\n| Setting               | Description                                                                                                                         | YAML variable             | Environment variable (ENV)                | Type          | Possible Values                    |\n|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------|---------------------------|-------------------------------------------|---------------|------------------------------------|\n| URL                   | URL into Redis ClusterOptions that can be used to connect to Redis                                                                  | `url`                     | `APP_CACHE_REDIS_URL`                     | string        | redis://:password@localhost:6379/0 |\n| Addrs                 | Either a single address or a seed list of host:port addresses of cluster/sentinel nodes.                                            | `addrs`                   | `APP_CACHE_REDIS_ADDRS`                   | list(string)  | localhost:6379                     |\n| ClientName            | ClientName will execute the `CLIENT SETNAME ClientName` command for each conn.                                                      | `client_name`             | `APP_CACHE_REDIS_CLIENT_NAME`             | string        | client-name                        |\n| DB                    | Database to be selected after connecting to the server. Only single-node and failover clients.                                      | `db`                      | `APP_CACHE_REDIS_DB`                      | int           | 0                                  |\n| Protocol              | Protocol 2 or 3. Use the version to negotiate RESP version with redis-server.                                                       | `protocol`                | `APP_CACHE_REDIS_PROTOCOL`                | int           | 2, 3                               |\n| Username              | Username for Redis authentication.                                                                                                  | `username`                | `APP_CACHE_REDIS_USERNAME`                | string        | username                           |\n| Password              | Password for Redis authentication.                                                                                                  | `password`                | `APP_CACHE_REDIS_PASSWORD`                | string        | password                           |\n| SentinelUsername      | SentinelUsername is the username for Sentinel authentication.                                                                       | `sentinel_username`       | `APP_CACHE_REDIS_SENTINEL_USERNAME`       | string        | sentinel-username                  |\n| SentinelPassword      | SentinelPassword is the password for Sentinel authentication.                                                                       | `sentinel_password`       | `APP_CACHE_REDIS_SENTINEL_PASSWORD`       | string        | sentinel-password                  |\n| MaxRetries            | Maximum number of retries before giving up.                                                                                         | `max_retries`             | `APP_CACHE_REDIS_MAX_RETRIES`             | int           | 0                                  |\n| MinRetryBackoff       | Minimum backoff between each retry.                                                                                                 | `min_retry_backoff`       | `APP_CACHE_REDIS_MIN_RETRY_BACKOFF`       | time.Duration | 8ms                                |\n| MaxRetryBackoff       | Maximum backoff between each retry.                                                                                                 | `max_retry_backoff`       | `APP_CACHE_REDIS_MAX_RETRY_BACKOFF`       | time.Duration | 512ms                              |\n| DialTimeout           | The timeout for establishing a connection.                                                                                          | `dial_timeout`            | `APP_CACHE_REDIS_DIAL_TIMEOUT`            | time.Duration | 5s                                 |\n| ReadTimeout           | The timeout for socket reads.                                                                                                       | `read_timeout`            | `APP_CACHE_REDIS_READ_TIMEOUT`            | time.Duration | 3s                                 |\n| WriteTimeout          | The timeout for socket writes.                                                                                                      | `write_timeout`           | `APP_CACHE_REDIS_WRITE_TIMEOUT`           | time.Duration | 3s                                 |\n| ContextTimeoutEnabled | Controls whether the client respects context timeouts and deadlines.                                                                | `context_timeout_enabled` | `APP_CACHE_REDIS_CONTEXT_TIMEOUT_ENABLED` | bool          | true, false                        |\n| PoolSize              | Base number of socket connections.                                                                                                  | `pool_size`               | `APP_CACHE_REDIS_POOL_SIZE`               | int           | 10                                 |\n| PoolTimeout           | Amount of time client waits for connection if all connections are busy before returning an error.                                   | `pool_timeout`            | `APP_CACHE_REDIS_POOL_TIMEOUT`            | time.Duration | 4s                                 |\n| MaxIdleConns          | Maximum number of idle connections.                                                                                                 | `max_idle_conns`          | `APP_CACHE_REDIS_MAX_IDLE_CONNS`          | int           | 10                                 |\n| MinIdleConns          | Minimum number of idle connections.                                                                                                 | `min_idle_conns`          | `APP_CACHE_REDIS_MIN_IDLE_CONNS`          | int           | 5                                  |\n| MaxActiveConns        | Maximum number of connections allocated by the pool at a given time.                                                                | `max_active_conns`        | `APP_CACHE_REDIS_MAX_ACTIVE_CONNS`        | int           | 100                                |\n| ConnMaxIdleTime       | Maximum amount of time a connection may be idle. Should be less than server's timeout.                                              | `conn_max_idle_time`      | `APP_CACHE_REDIS_CONN_MAX_IDLE_TIME`      | time.Duration | 5m                                 |\n| ConnMaxLifetime       | Maximum amount of time a connection may be reused.                                                                                  | `conn_max_lifetime`       | `APP_CACHE_REDIS_CONN_MAX_LIFETIME`       | time.Duration | 5m                                 |\n| MaxRedirects          | The maximum number of retries before giving up. Command is retried on network errors and MOVED/ASK redirects. Only cluster clients. | `max_redirects`           | `APP_CACHE_REDIS_MAX_REDIRECTS`           | int           | 8                                  |\n| ReadOnly              | Enable read-only commands on slave nodes. Only cluster clients.                                                                     | `read_only`               | `APP_CACHE_REDIS_READ_ONLY`               | bool          | true, false                        |\n| RouteByLatency        | Route read-only commands to the closest master or slave node. Only cluster clients.                                                 | `route_by_latency`        | `APP_CACHE_REDIS_ROUTE_BY_LATENCY`        | bool          | true, false                        |\n| RouteRandomly         | Route read-only commands to a random master or slave node. Only cluster clients.                                                    | `route_randomly`          | `APP_CACHE_REDIS_ROUTE_RANDOMLY`          | bool          | true, false                        |\n| MasterName            | Name of the master to use for Sentinel failover.                                                                                    | `master_name`             | `APP_CACHE_REDIS_MASTER_NAME`             | string        | master-name                        |\n| DisableIndentity      | Disable set-lib on connect.                                                                                                         | `disable_indentity`       | `APP_CACHE_REDIS_DISABLE_INDENTITY`       | bool          | true, false                        |\n| IdentitySuffix        | Add suffix to client name.                                                                                                          | `identity_suffix`         | `APP_CACHE_REDIS_IDENTITY_SUFFIX`         | string        | suffix                             |\n\nTo secure the requests to Redis, Go SDK provides a configuration set for TLS:\n\n**TLS**:\n\n| Setting               | Description                                                      | YAML variable          | Environment variable (ENV)                 | Type   | Possible Values     |\n|-----------------------|------------------------------------------------------------------|------------------------|--------------------------------------------|--------|---------------------|\n| Enabled               | Whether TLS is enabled or not                                    | `enabled`              | `APP_CACHE_REDIS_TLS_ENABLED`              | bool   | true, false         |\n| Root certificate      | Ca Root CA certificate                                           | `ca`                   | `APP_CACHE_REDIS_TLS_CA`                   | string | Root CA             |\n| Cert PEM              | Client's public key string (PEM format) used for authentication  | `cert_pem`             | `APP_CACHE_REDIS_TLS_CERT_PEM`             | string | long PEM string     |\n| Cert PEM Key          | Client's private key string (PEM format) used for authentication | `cert_pem_key`         | `APP_CACHE_REDIS_TLS_CERT_PEM_KEY`         | string | long PEM key string |\n| Passphrase            | Passphrase is used in case the private key needs to be decrypted | `passphrase`           | `APP_CACHE_REDIS_TLS_PASSPHRASE`           | string | pass phrase         |\n| Skip TLS verification | Turn on / off TLS verification                                   | `insecure_skip_verify` | `APP_CACHE_REDIS_TLS_INSECURE_SKIP_VERIFY` | bool   | true, false         |\n\n### Experimentation\n\n`go-sdk` comes with an integration with [Statsig](https://statsig.com) to\nleverage its experimentation and feature flag functionality.\n\n#### Initialization of Statsig\n\nThe respective configuration file is `statsig.yml` and it should include\nthe following content:\n\n```yaml\n# config/statsig.yml\ncommon: \u0026common\n  secret_key: \"\"\n  local_mode: false\n  config_sync_interval: 10s\n  id_list_sync_interval: 60s\n\ndevelopment:\n  \u003c\u003c: *common\n\ntest:\n  \u003c\u003c: *common\n\nstaging:\n  \u003c\u003c: *common\n\nproduction:\n  \u003c\u003c: *common\n```\n\nFor more details on the configuration check the\n[Statsig Go Server SDK](https://docs.statsig.com/server/golangSDK).\n\nUsing the configuration details, the statsig client can be initialized as\nfollows:\n\n```go\npackage main\n\nimport (\n\tsdkstatsig \"github.com/scribd/go-sdk/pkg/statsig\"\n)\n\nfunc main() {\n\t// Loads the statsig configuration.\n\tstatsigConfig, err := sdkstatsig.NewConfig()\n\n\t// Initialize statsig connection using the configuration.\n\tsdkstatsig.Initialize(statsigConfig)\n}\n```\n\n#### Usage of Statsig\n\nAfter initializing the statsig client, one can start using experiments or\nfeature flags using the following code:\n\n```go\nimport (\n  sdkstatsig \"github.com/scribd/go-sdk/pkg/statsig\"\n  statsig \"github.com/statsig-io/go-sdk\"\n)\n\nfunc main() {\n  u := statsig.User{}\n\n  experiment := sdkstatsig.GetExperiment(u, \"experiment_name\")\n  featureFlag := sdkstatsig.GetFeatureFlag(u, \"feature_flag_name\")\n}\n```\n\n### AWS configuration\n\n`go-sdk` provides a convenient way to create an AWS configuration.\n\nConfiguration contains the common configuration for AWS configuration and specific configuration for services.\n\nService configuration is split by the service name to facilitate the configuration of multiple services per service type.\n\n```yaml\ncommon: \u0026common\n  config:\n    region: \"us-east-2\"\n  s3:\n    default:\n      # APP_AWS_S3_DEFAULT_REGION\n      region: \"us-east-1\"\n      credentials:\n        assume_role:\n          # APP_AWS_S3_DEFAULT_CREDENTIALS_ASSUME_ROLE_ARN\n          arn: \"\"\n    example:\n      # APP_AWS_S3_EXAMPLE_REGION\n      region: \"us-west-2\"\n      credentials:\n        static:\n          # APP_AWS_S3_EXAMPLE_CREDENTIALS_STATIC_ACCESS_KEY_ID\n          access_key_id: \"\"\n          # APP_AWS_S3_EXAMPLE_CREDENTIALS_STATIC_SECRET_ACCESS_KEY\n          secret_access_key: \"\"\n          # APP_AWS_S3_EXAMPLE_CREDENTIALS_STATIC_SESSION_TOKEN\n          session_token: \"\"\n```\n\n#### AWS common configuration\n\nCommon configuration contains the following options:\n\n| Setting                    | Description                                            | YAML variable    | Environment variable (ENV)           | Type   | Possible Values |\n|----------------------------|--------------------------------------------------------|------------------|--------------------------------------|--------|-----------------|\n| Region                     | AWS region to use                                      | `region`         | `APP_AWS_REGION`                     | string | us-west-2       |\n| HTTP client max idle conns | Maximum number of idle connections in the HTTP client. | `max_idle_conns` | `APP_AWS_HTTP_CLIENT_MAX_IDLE_CONNS` | int    | 100             |\n\n#### AWS Service configuration\n\nCurrently, `go-sdk` supports the following AWS service types: `s3`, `sagemakerruntime`, `sfn` and `sqs`.\n\nTo facilitate the configuration of multiple services per service type, the configuration is split by the service name.\nTo override the settings specified in the YAML via the environment variables, the following naming convention is used:\n\n    ```\n    APP_AWS_\u003cSERVICE_TYPE\u003e_\u003cSERVICE_NAME\u003e_*\n    ```\n\nWhere `SERVICE_TYPE` is the service type (s3, sqs etc.) and `SERVICE_NAME` is the service name (default, example etc.).\n\nIt is possible to specify region and HTTP client settings for each service, which will override the common configuration.\n\nFor each service, it is possible to specify credentials. Currently, `go-sdk` supports two types of credentials: `assume_role` and `static`.\n\n`assume_role` credentials are used to assume a role to get credentials using AWS STS service. The following options are available:\n\n| Setting | Description                                          | YAML variable | Environment variable (ENV)                       | Type   | Possible Values                          |\n|---------|------------------------------------------------------|---------------|--------------------------------------------------|--------|------------------------------------------|\n| ARN     | The Amazon Resource Name (ARN) of the role to assume | `arn`         | `APP_AWS_S3_DEFAULT_CREDENTIALS_ASSUME_ROLE_ARN` | string | arn:aws:iam::123456789012:role/role-name |\n\n`static` credentials are used to specify the access key ID, secret access key and session token. The following options are available:\n\n| Setting           | Description           | YAML variable       | Environment variable (ENV)                                | Type   | Possible Values   |\n|-------------------|-----------------------|---------------------|-----------------------------------------------------------|--------|-------------------|\n| Access Key ID     | The access key ID     | `access_key_id`     | `APP_AWS_S3_EXAMPLE_CREDENTIALS_STATIC_ACCESS_KEY_ID`     | string | access-key-id     |\n| Secret Access Key | The secret access key | `secret_access_key` | `APP_AWS_S3_EXAMPLE_CREDENTIALS_STATIC_SECRET_ACCESS_KEY` | string | secret-access-key |\n| Session Token     | The session token     | `session_token`     | `APP_AWS_S3_EXAMPLE_CREDENTIALS_STATIC_SESSION_TOKEN`     | string | session-token     |\n\n## APM \u0026 Instrumentation\n\nThe `go-sdk` provides an easy way to add application performance monitoring\n(APM) \u0026 instrumentation to a service. It provides DataDog APM using the\n[`dd-trace-go`](https://github.com/DataDog/dd-trace-go) library. `dd-trace-go`\nprovides gRPC server \u0026 client interceptors, HTTP router instrumentation, database connection \u0026 ORM instrumentation\nand AWS clients instrumentation. All of the traces and data are opaquely sent\nto DataDog.\n\n### Request ID middleware\n\nFor easier identification of requests and their tracing within components of a\nsingle service, and across-services, `go-sdk` has a `RequestID` middleware.\n\n#### HTTP server Request ID middleware\n\nAs an HTTP middleware, it checks every incoming request for a `X-Request-Id` HTTP header\nand sets the value of the header as a field in the request `Context`. In case\nthere's no `RequestID` present, it will generate a UUID and assign it in the\nrequest `Context`.\n\nExample usage of the middleware:\n\n```go\nfunc main() {\n\trequestIDMiddleware := sdkmiddleware.NewRequestIDMiddleware()\n\n\thttpServer := server.\n\t\tNewHTTPServer(host, httpPort, applicationEnv, applicationName).\n\t\tMountMiddleware(requestIDMiddleware.Handler).\n\t\tMountRoutes(routes)\n}\n```\n\n#### gRPC server Request ID interceptors\n\nFor the gRPC server, sdk provides unary and stream interceptors. It checks every incoming request\nfor a `x-request-id` [grpc metadata](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) header and sets the value of the header as a field in the request `Context`.\nIn case there's no `RequestID` present, it will generate a UUID and assign it in the request `Context`.\n\nExample usage of interceptors:\n\n```go\nfunc main() {\n    grpcServer, err := server.NewGrpcServer(\n        host,\n        grpcPort,\n        []grpc.ServerOption{\n            grpc.ChainUnaryInterceptor(\n                sdkinterceptors.TracingUnaryServerInterceptor(applicationName),\n                sdkinterceptors.RequestIDUnaryServerInterceptor(),\n                sdkinterceptors.LoggerUnaryServerInterceptor(logger),\n            ),\n            grpc.ChainStreamInterceptor(\n                sdkinterceptors.TracingStreamServerInterceptor(applicationName),\n                sdkinterceptors.RequestIDStreamServerInterceptor(),\n                sdkinterceptors.LoggerStreamServerInterceptor(logger),\n            ),\n        }...)\n}\n```\n\n### HTTP Router Instrumentation\n\n`go-sdk` ships with HTTP router instrumentation, based on DataDog's\n`dd-trace-go` library. It spawns a new instrumented router where each of the\nrequests will create traces that will be sent opaquely to the DataDog agent.\n\nExample usage of the instrumentation:\n\n```go\n// Example taken from go-chassis\nfunc NewHTTPServer(host, port, applicationEnv, applicationName string) *HTTPServer {\n\trouter := sdk.Tracer.Router(applicationName)\n\tsrv := \u0026http.Server{\n\t\tAddr:    fmt.Sprintf(\"%s:%s\", host, port),\n\t\tHandler: router,\n\t}\n\treturn \u0026HTTPServer{\n\t\tsrv:            srv,\n\t\tapplicationEnv: applicationEnv,\n\t}\n}\n```\n\n### gRPC server and client interceptors\n\n`go-sdk` ships with gRPC instrumentation, based on DataDog's\n`dd-trace-go` library. It creates new gRPC interceptors for the server \u0026 client. Thus,\nrequests will create traces that will be sent opaquely to the DataDog agent.\n\nExample usage of the instrumentation:\n\n```go\n// gRPC client example\n\nsi := grpctrace.StreamClientInterceptor(grpctrace.WithServiceName(\"client-application\"))\nui := grpctrace.UnaryClientInterceptor(grpctrace.WithServiceName(\"client-application\"))\n\nconn, err := grpc.Dial(\"\u003cgRPC host\u003e\", grpc.WithStreamInterceptor(si), grpc.WithUnaryInterceptor(ui))\nif err != nil {\n    log.Fatalf(\"Failed to init gRPC connection: %s\", err)\n}\ndefer conn.Close()\n```\n\n```go\n// gRPC server example\n\ngrpcServer, err := server.NewGrpcServer(\n    host,\n    grpcPort,\n    []grpc.ServerOption{\n        grpc.ChainUnaryInterceptor(\n            sdkinterceptors.TracingUnaryServerInterceptor(applicationName),\n            sdkinterceptors.LoggerUnaryServerInterceptor(logger),\n            sdkinterceptors.MetricsUnaryServerInterceptor(metrics),\n            sdkinterceptors.DatabaseUnaryServerInterceptor(database),\n            kitgrpc.Interceptor,\n        ),\n        grpc.ChainStreamInterceptor(\n            sdkinterceptors.TracingStreamServerInterceptor(applicationName),\n            sdkinterceptors.LoggerStreamServerInterceptor(logger),\n            sdkinterceptors.MetricsStreamServerInterceptor(metrics),\n            sdkinterceptors.DatabaseStreamServerInterceptor(database),\n        ),\n    }...)\nif err != nil {\n    logger.Fatalf(\"Failed to create gRPC Server: %s\", err)\n}\n```\n\n### Database instrumentation \u0026 ORM logging\n\n`go-sdk` ships with two database-related middlewares: `Database` \u0026\n`DatabaseLogging` for both HTTP and gRPC servers.\n\nThe `Database` middleware which instruments the\n[Gorm-powered](https://gorm.io/gorm) database connection. It utilizes\nGorm-specific callbacks that report spans and traces to Datadog. The\ninstrumented Gorm database connection is injected in the request `Context` and\nit is always scoped within the request.\n\nThe `DatabaseLogging` middleware checks for a logger injected in the request\n`context`. If found, the logger is passed to the Gorm database connection,\nwhich in turn uses the logger to produce database query logs. A nice\nside-effect of this approach is that, if the logger is tagged with a\n`request_id`, there's a logs correlation between the HTTP requests and the\ndatabase queries. Also, if the logger is tagged with `trace_id` we can easily\ncorrelate logs with traces and see corresponding database queries. Keep in mind \nthat ORM logging happens at the same level as the base logger. The additional\ndatabase fields ('duration', 'affected_rows' and 'sql') are available\nonly when using the `trace` levels.\n\n#### HTTP server middleware example\n\n```go\nfunc main() {\n\tdatabaseMiddleware := sdkmiddleware.NewDatabaseMiddleware(sdk.Database)\n\tdatabaseLoggingMiddleware := sdkmiddleware.NewDatabaseLoggingMiddleware()\n\n\thttpServer := server.\n\t\tNewHTTPServer(host, httpPort, applicationEnv, applicationName).\n\t\tMountMiddleware(databaseMiddleware.Handler).\n\t\tMountMiddleware(databaseLoggingMiddleware.Handler).\n\t\tMountRoutes(routes)\n}\n```\n\n#### gRPC server interceptors example\n\n```go\nfunc main() {\n    grpcServer, err := server.NewGrpcServer(\n        host,\n        grpcPort,\n        []grpc.ServerOption{\n            grpc.ChainUnaryInterceptor(\n                sdkinterceptors.TracingUnaryServerInterceptor(applicationName),\n                sdkinterceptors.LoggerUnaryServerInterceptor(logger),\n                sdkinterceptors.MetricsUnaryServerInterceptor(metrics),\n                sdkinterceptors.DatabaseUnaryServerInterceptor(database),\n                sdkinterceptors.DatabaseLoggingUnaryServerInterceptor(),\n                kitgrpc.Interceptor,\n            ),\n            grpc.ChainStreamInterceptor(\n                sdkinterceptors.TracingStreamServerInterceptor(applicationName),\n                sdkinterceptors.LoggerStreamServerInterceptor(logger),\n                sdkinterceptors.MetricsStreamServerInterceptor(metrics),\n                sdkinterceptors.DatabaseStreamServerInterceptor(database),\n                sdkinterceptors.DatabaseLoggingStreamServerInterceptor(),\n            ),\n        }...)\n}\n```\n\n### AWS clients instrumentation\n\n`go-sdk` instruments the AWS clients by wrapping it with a DataDog trace and\ntagging it with the service name. In addition, this registers AWS as a separate\nservice in DataDog.\n\nExample usage of the instrumentation:\n\n```go\nimport (\n    \"context\"\n    \"log\"\n\n    sdkaws \"github.com/scribd/go-sdk/pkg/aws\"\n    sdkconfig \"github.com/scribd/go-sdk/pkg/configuration\"\n    instrumentation \"github.com/scribd/go-sdk/pkg/instrumentation\"\n)\n\nfunc main() {\n    config, err := sdkconfig.NewConfig()\n    if err != nil {\n        log.Fatalf(\"Failed to load SDK config: %s\", err)\n    }\n\n    awsBuilder := sdkaws.NewBuilder(config.AWS)\n\n    cfg, err := awsBuilder.LoadConfig(context.Background())\n    if err != nil {\n        log.Fatalf(\"error: %v\", err)\n    }\n    instrumentation.InstrumentAWSClient(cfg, instrumentation.Settings{\n        AppName: applicationName,\n    })\n\n    // Create an S3 service client with the service name \"default\"\n    s3client := awsBuilder.NewS3Service(cfg, \"default\")\n}\n```\n\nTo correlate the traces that are registered with DataDog with the corresponding\nrequests in other DataDog services, the\n[`aws-sdk-go`](https://aws.amazon.com/sdk-for-go) provides functions with the\n`WithContext` suffix. These functions expect the request `Context` as the first\narugment of the function, which allows the tracing chain to be continued inside\nthe SDK call stack. To learn more about these functions you can start by\nreading about them on [the AWS developer\nblog](https://aws.amazon.com/blogs/developer/v2-aws-sdk-for-go-adds-context-to-api-operations).\n\n### PubSub instrumentation and logging\n\n#### Kafka\n\n`go-sdk` provides a flexible way to instrument the Kafka client with logging, tracing and metrics capabilities.\n\nThe [franz-go](https://github.com/twmb/franz-go/) Kafka client library is wrapped by the `go-sdk` and traces calls to Kafka.\n\nFor logging, `go-sdk` provides a [franz-go compatible](https://pkg.go.dev/github.com/twmb/franz-go/pkg/kgo#WithLogger) logger which is basically a plugin\naround the SDK's `logger.Logger` interface. This logger will print `franz-go` log stubs depending on a configured log level.\n\n```go\nimport (\n    \"github.com/twmb/franz-go/pkg/kgo\"\n\n    sdkkafkalogger \"github.com/scribd/go-sdk/pkg/logger/kafka\"\n)\n\nfunc main() {\n    kafkaClientOpts := []kgo.Opt{\n        kgo.WithLogger(\n            sdkkafkalogger.NewKafkaLogger(\n                logger,\n                sdkkafkalogger.WithLevel(sdklogger.Level(config.Logger.ConsoleLevel)),\n            ),\n        ),\n    }\n}\n```\n\nFor metrics, `go-sdk` provides a `franz-go` plugin to publish metrics to DataDog via DogStatsd. It is implemented as a\n[hook](https://pkg.go.dev/github.com/twmb/franz-go/pkg/kgo#WithHooks). Metrics are split into three categories: `broker`, `producer` and `consumer`.\n\n**Broker metrics**\n\nBroker metrics represent a set of metrics around the broker connection. If enabled, the following metrics will be published:\n\n```\ncount metrics\n#{service_name}.kafka_client.broker.read_errors_total{node=\"#{node}\"}\n#{service_name}.kafka_client.broker.read_bytes_total{node=\"#{node}\"}\n#{service_name}.kafka_client.broker.write_errors_total{node=\"#{node}\"}\n#{service_name}.kafka_client.broker.write_bytes_total{node=\"#{node}\"}\n#{service_name}.kafka_client.broker.disconnects_total{node=\"#{node}\"}\n#{service_name}.kafka_client.broker.connect_errors_total{node=\"#{node}\"}\n#{service_name}.kafka_client.broker.connects_total{node=\"#{node}\"}\n\ntime metrics\n#{service_name}.kafka_client.broker.write_latency{node=\"#{node}\"}\n#{service_name}.kafka_client.broker.write_wait_latency{node=\"#{node}\"}\n#{service_name}.kafka_client.broker.read_latency{node=\"#{node}\"}\n#{service_name}.kafka_client.broker.read_wait_latency{node=\"#{node}\"}\n\nhistogram metrics\n#{service_name}.kafka_client.broker.throttle_latency{node=\"#{node}\"}\n```\n\n**Producer metrics**\n\nProducer metrics represent a set of metrics around the producer. If enabled, the following metrics will be published:\n\n```\ncount metrics\n#{service_name}.kafka_client.producer.records_error\n#{service_name}.kafka_client.producer.records_unbuffered\n#{service_name}.kafka_client.producer.records_buffered\n#{service_name}.kafka_client.producer.produce_bytes_uncompressed_total{node=\"#{node}\",topic=\"#{topic}\",partition=\"#{partition}\"}\n#{service_name}.kafka_client.producer.produce_bytes_compressed_total{node=\"#{node}\",topic=\"#{topic}\",partition=\"#{partition}\"}\n```\n\n**Consumer metrics**\n\nConsumer metrics represent a set of metrics around the consumer. If enabled, the following metrics will be published:\n\n```\ncount metrics\n#{service_name}.kafka_client.consumer.records_unbuffered\n#{service_name}.kafka_client.consumer.records_buffered\n#{service_name}.kafka_client.consumer.fetch_bytes_uncompressed_total{node=\"#{node}\",topic=\"#{topic}\",partition=\"#{partition}\"}\n#{service_name}.kafka_client.consumer.fetch_bytes_compressed_total{node=\"#{node}\",topic=\"#{topic}\",partition=\"#{partition}\"}\n```\n\nBy default, all metrics will be published without sampling (rate `1.0`). The client can specify the sampling rate per\nmetric type and/or per metric name:\n\n```go\nimport (\n    \"github.com/twmb/franz-go/pkg/kgo\"\n\n    sdkkafkamertics \"github.com/scribd/go-sdk/pkg/metrics/kafka\"\n)\n\nfunc main() {\n    kafkaClientOpts := []kgo.Opt{\n        // no sampling rate specified\n        kgo.WithHooks(sdkkafkamertics.NewBrokerMetrics(metrics)),\n        // sampling rate of 0.5 will be applied for all producer metrics\n        kgo.WithHooks(sdkkafkamertics.NewProducerMetrics(metrics, sdkkafkamertics.WithSampleRate(0.5))),\n        // sampling rate of 0.3 will be applied to consumer metric name provided only,\n        // other metrics will be published with default sample rate\n        kgo.WithHooks(sdkkafkamertics.NewConsumerMetrics(\n            metrics,\n            sdkkafkamertics.WithSampleRatesPerMetric(map[string]float64{\n                \"kafka_client.consumer.records_buffered\": 0.3,\n            }),\n        )),\n    }\n}\n```\n\n### Cache instrumentation and logging\n\n#### Redis\n\n`go-sdk` provides a flexible way to instrument the Redis client with logging and tracing capabilities.\n\nThe [go-redis](https://github.com/redis/go-redis) Redis client library is wrapped by the `go-sdk` using [dd-trace-go](https://github.com/DataDog/dd-trace-go/tree/fda4cc5e15b744b10d378a62e026dc61d35f364a/contrib/redis/go-redis.v9) library and traces calls to Redis.\n\nFor logging, `go-sdk` sets the [go-redis](https://pkg.go.dev/github.com/redis/go-redis/v9#SetLogger) logger to the SDK logger. This logger will print `go-redis` log stubs as error log entries.\n\n```go\nimport (\n    sdklogger \"github.com/scribd/go-sdk/pkg/logger\"\n\n    instrumentation \"github.com/scribd/go-sdk/pkg/instrumentation\"\n\n    sdkredis \"github.com/scribd/go-sdk/pkg/cache/redis\"\n)\n\nfunc main() {\n    config, err := sdkconfig.NewConfig()\n    if err != nil {\n        log.Fatalf(\"Failed to load SDK config: %s\", err)\n    }\n\n    logger, err := sdklogger.NewBuilder(config.Logger).SetTracking(config.Tracking).Build()\n    if err != nil {\n        log.Fatalf(\"Failed to load SDK logger: %s\", err)\n    }\n\n    redisClient, err := sdkredis.New(\u0026config.Cache.Redis)\n    if err != nil {\n        logger.WithError(err).Fatalf(\"Failed to create Redis client: %s\", err)\n    }\n\n    // instrument redis client\n    instrumentation.InstrumentRedis(redisClient, applicationName)\n\n    // set logger for redis client\n    sdklogger.SetRedisLogger(logger)\n}\n```\n\n### Profiling\n\nYou can send `pprof` samples to DataDog by enabling the profiler.\nUnder the hood the DataDog profiler will continuously take heap, CPU and mutex profiles, [every 1 minute by default](https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/profiler#pkg-constants).\nThe [default CPU profile duration is 15 seconds](https://godoc.org/gopkg.in/DataDog/dd-trace-go.v1/profiler#pkg-constants). Keep in mind that the profiler introduces overhead when it is being executed.\nThe default DataDog configuration, which go-sdk uses by default, is following [good practices](https://groups.google.com/g/golang-nuts/c/e6lB8ENbIw8?pli=1).\n\n```go\nfunc main() {\n    // Build profiler.\n    sdkProfiler := instrumentation.NewProfiler(\n\tconfig.Instrumentation,\n\tprofiler.WithService(appName),\n\tprofiler.WithVersion(version),\n    )\n    if err := sdkProfiler.Start(); err != nil {\n        log.Fatalf(\"Failed to start profiler: %s\", err)\n    }\n    defer sdkProfiler.Stop()\n}\n```\n\n### Custom Metrics\n\n`go-sdk` provides a way to send custom metrics to Datadog.\n\nThe `metrics.Configuration` configuration can be used to define the set of\ntags to attach to every metric emitted by the client.\n\nThis client is configured by default with:\n\n- `service:$APP_NAME`\n- `env:$ENVIRONMENT`\n\nDatadog tags documentation is available [here][ddtags].\n\nSee the [metric submission documentation][submit-metric] on how to\nsubmit custom metrics.\n\nMetric names must only contain ASCII alphanumerics, underscores, and\nperiods. The client will not replace nor check for invalid characters.\n\nSome options are suppported when submitting metrics, like applying a\n[sample rate][rate] to your metrics or tagging your metrics with your\n[custom tags][custom-tags]. Find all the available functions to report\nmetrics in the Datadog Go [client GoDoc documentation][client-go].\n\nExample usage of the custom metrics:\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/scribd/go-sdk/pkg/metrics\"\n)\n\nfunc main() {\n\tapplicationEnv := \"development\"\n\tapplicationName := \"go-sdk-example\"\n\n\tmetricsConfig := \u0026metrics.Config{\n\t\tEnvironment: applicationEnv,\n\t\tApp:         applicationName,\n\t}\n\tclient, err := metrics.NewBuilder(metricsConfig).Build()\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not initialize Metrics client: %s\", err)\n\t}\n\n\t_ = client.Incr(\"example.increment\", []string{\"\"}, 1)\n\t_ = client.Decr(\"example.decrement\", []string{\"\"}, 1)\n\t_ = client.Count(\"example.count\", 2, []string{\"\"}, 1)\n}\n```\n\n[ddtags]: \u003chttps://docs.datadoghq.com/getting_started/tagging/\u003e\n[client-go]: \u003chttps://godoc.org/github.com/DataDog/datadog-go/statsd#Client\u003e\n[custom-tags]: \u003chttps://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=go#metric-tagging\u003e\n[rate]: \u003chttps://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=go#metric-submission-options\u003e\n[submit-metric]: \u003chttps://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=go\u003e\n\n## Using the `go-sdk` in isolation\n\nThe `go-sdk` is a standalone Go module. This means that it can be imported and\nused in virtually any Go project. Still, there are four conventions that the\n`go-sdk` enforces which **must be present** in the host application:\n1. The presence of the `APP_ENV` environment variable, used for\n   [environment-awareness](#environment-awareness),\n2. Support of a single file format (YAML) for storing configurations,\n3. Support of only a single path to store the configuration, `config/` in the\n   application root, and\n4. The presence of the `APP_ROOT` environment variable, set to the absolute\n   path of the application that it's used in. This is used to locate the\n   `config` directory on disk and load the enclosed YAML files.\n\nA good way to approach initialization of the `go-sdk` in your application can be\nseen in the `go-chassis` itself:\n\n```go\n// internal/pkg/sdk/sdk.go\npackage sdk\n\nimport (\n\t\"log\"\n\n\tsdkconfig \"github.com/scribd/go-sdk/pkg/configuration\"\n\tsdklogger \"github.com/scribd/go-sdk/pkg/logger\"\n)\n\nvar (\n\t// Config is SDK-powered application configuration.\n\tConfig *sdkconfig.Config\n\t// Logger is SDK-powered application logger.\n\tLogger sdklogger.Logger\n\terr    error\n)\n\nfunc init() {\n\tif Config, err = sdkconfig.NewConfig(); err != nil {\n\t\tlog.Fatalf(\"Failed to load SDK config: %s\", err.Error())\n\t}\n\n\tif Logger, err = sdklogger.NewBuilder(loggerConfig).Build(); err != nil {\n\t\tlog.Fatalf(\"Failed to load SDK logger: %s\", err.Error())\n\t}\n}\n```\n\n**Please note** that while using the `go-sdk` in isolation is possible, it is\n**highly recommended** to use it in combination with the `go-chassis` for the\nbest development, debugging and maintenance experience.\n\n## Developing the SDK\n\n* The SDK provides a [Docker\n  Compose](https://docs.docker.com/compose/) development environment to\n  run, develop and test a service (version `1.24.1`).\n* See [`docker-compose.yml`](./docker-compose.yml) for the service definitions.\n\n### Building the docker environment\n\n```sh\n$ docker-compose build [--no-cache|--pull|--parallel]\n```\n\nRefer to the\n[Compose CLI reference](https://docs.docker.com/compose/reference/build/)\nfor the full list of option and the synopsis of the command.\n\n### Running tests within the docker environment\n\nCompose provides a way to create and destroy isolated testing environments:\n\n```sh\n$ docker-compose run --rm sdk mage test:run\n```\n\n### Entering the docker environment\n\nYou can enter the docker environment to build, run and debug your service:\n\n```\n$ docker-compose run --rm sdk /bin/bash\nroot@1f31fa8e5c49:/sdk# go version\ngo version go1.24.0 linux/amd64\n```\n\nRefer to the\n[Compose CLI reference](https://docs.docker.com/compose/reference/run/)\nfor the full list of option and the synopsis of the command.\n\n### Using a development build of `go-sdk`\n\nWhen developing a project that uses `go-sdk` as a dependency, you might need\nto introduce changes to the `go-sdk` codebase.\n\nIn this case, perform the following steps:\n\n1. Create a branch in the `go-sdk` repository with the changes you want,\n   and push the branch to the repository remote:\n\n   ```sh\n   git push -u origin \u003cusername/branch-name\u003e\n   ```\n\n2. Add or change the following line in the `go.mod` file of the project\n   that uses `go-sdk` as a dependency:\n\n   ```go\n   replace github.com/scribd/go-sdk =\u003e github.com/scribd/go-sdk.git \u003cusername/branch-name\u003e\n   ```\n\n3. From the project root, fetch the new branch by running:\n\n   ```sh\n   go mod tidy\n   ```\n\n4. Note that running `go mod tidy` will tie `go-sdk` to the specific git commit.\n   So after running it, the `replace` statement will look like this:\n\n   ```go\n   replace github.com/scribd/go-sdk =\u003e github.com/scribd/go-sdk.git \u003cpseudo-version\u003e\n   ```\n\n   Therefore, you will need to repeat steps 1, 2 and 3 each time you add new\n   changes to your branch in `go-sdk`.\n\n5. After you are done with the required changes, create a merge request to the\n   `go-sdk` repository. After the merge request is merged and a release is done,\n   you need to, once again, alter the `replace` statement in your `go.mod` file:\n\n   ```go\n   replace github.com/scribd/go-sdk =\u003e github.com/scribd/go-sdk.git \u003ctag-name\u003e\n   ```\n\n### Commit messages\n\nPlease follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) and [Angular commit message guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines) for writing commit messages.\n\n## Release\n\nThis project is using [semantic-release](https://semantic-release.gitbook.io/semantic-release/)\nand [conventional-commits](https://www.conventionalcommits.org/en/v1.0.0/),\nwith the [`angular` preset](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).\n\nReleases are done from the `origin/main` branch using a manual step at the end of the CI/CD pipeline.\n\nIn order to create a new release:\n\n1. Merge / push changes to `origin/main`\n2. Open the `origin/main` [GitHub Action Release pipeline](https://github.com/scribd/go-sdk/actions/workflows/release.yml)\n3. Press ▶️ on the \"Run workflow\" step\n\nA version bump will happen automatically and the type of version bump\n(patch, minor, major) depends on the commits introduced since the last release.\n\nThe `semantic-release` configuration is in [`.releaserc.yml`](https://github.com/scribd/go-sdk/blob/main/.releaserc.yml).\n\n## Maintainers\n\nMade with ❤️ by the Core Services team.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscribd%2Fgo-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscribd%2Fgo-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscribd%2Fgo-sdk/lists"}