refactor(config): debrand env — O11Y prefix, DATASTORE store name (no SIGNOZ/CLICKHOUSE in env)

Canonical flat operator key O11Y_DATASTORE_DSN aliases into telemetrystore.datastore.dsn; long structured form kept as internal fallback. Store stays ClickHouse wire-protocol (driver/provider/tables unchanged).
This commit is contained in:
2026-07-02 16:50:28 -07:00
parent 6edd138e30
commit 3888a93de3
15 changed files with 180 additions and 93 deletions
+4 -4
View File
@@ -13,7 +13,7 @@
# fetch needs no private git auth — only GOPRIVATE + GOSUMDB=off.
#
# The browser SPA is served at the edge by hanzoai/static (house-native static
# plugin), not bundled here, so the server runs headless (SIGNOZ_WEB_ENABLED=false).
# plugin), not bundled here, so the server runs headless (O11Y_WEB_ENABLED=false).
# The frontend/ tree is a separate concern — its pnpm-lock.yaml is mid-migration
# and not build-ready; bundle it back once that is resolved.
@@ -76,9 +76,9 @@ COPY --from=backend /out/o11y /usr/local/bin/o11y
COPY templates/ /root/templates/
# The browser SPA is served by hanzoai/static at the edge; run headless. When a
# deployment bundles/mounts web assets, set SIGNOZ_WEB_ENABLED=true and
# SIGNOZ_WEB_DIRECTORY to their path.
ENV SIGNOZ_WEB_ENABLED=false
# deployment bundles/mounts web assets, set O11Y_WEB_ENABLED=true and
# O11Y_WEB_DIRECTORY to their path.
ENV O11Y_WEB_ENABLED=false
# Public query-service HTTP + query API (constants.HTTPHostPort = 0.0.0.0:8080).
EXPOSE 8080
+65
View File
@@ -37,6 +37,37 @@ SigNoz eras); it dissolved when the whole `pkg/ cmd/` tree was re-synced to one
consistent upstream version. Keep it that way: bump by re-syncing to a newer SigNoz
`main`, not by piecemeal-porting individual packages.
The real server binary is `./cmd/community` (NOT `./cmd/server`, which does not
exist). Build check: `GOPRIVATE='github.com/hanzoai/*' GOSUMDB=off go build ./cmd/community`.
## Container image (`ghcr.io/hanzoai/o11y`)
Root `Dockerfile` + `.github/workflows/docker.yaml` build a standalone community
server image on push to `main` and `v*` tags → `ghcr.io/hanzoai/o11y:<sha>` (+ `:main`).
This replaces the unrelated **Langfuse** image that previously squatted the tags.
- Builds ONLY `./cmd/community`. It does NOT import `github.com/hanzoai/cloud`, so
go.mod's `replace github.com/hanzoai/cloud => ../cloud` is inert for the image and
no cloud sibling is checked out. (Cloud lives only in root `mount.go`, compiled by
the `go build ./...` CI job — so `ci.yaml` still needs the sibling; the container
does not.) A bare `go mod download` WOULD fail (cloud→../cloud); build the one pkg.
- All external deps in its graph are PUBLIC hanzoai/* forks (signoz-otel-collector,
govaluate, clickhouse-go-mock, expr) → no private git auth, just
`GOPRIVATE=github.com/hanzoai/* GOSUMDB=off`. GHCR push uses `GH_PAT || GITHUB_TOKEN`
(the package is linked to hanzoai/o11y, so GITHUB_TOKEN+`packages: write` suffices).
- Runs headless: `O11Y_WEB_ENABLED=false`. `routerweb` os.Stat()s its web dir at
boot and fatals if missing; the SPA is served by hanzoai/static at the edge. The
`frontend/` tree is NOT bundled — its `pnpm-lock.yaml` is STALE vs `package.json`
(mid rolldown-vite/oxlint migration), fails `--frozen-lockfile`. TODO: regen lockfile.
- Listens on `0.0.0.0:8080` (constants.HTTPHostPort); sqlstore default = sqlite at
`/var/lib/signoz/signoz.db`; needs an external ClickHouse for telemetry.
Boot fix: `pkg/instrumentation/sdk.go` hard-pinned `semconv/v1.40.0.SchemaURL`,
which contrib `NewSDK` merges against `resource.Default()` (schema 1.41.0, from the
re-synced otel/sdk) — OTEL rejects differing non-empty schema URLs, so `signoz.New`
crashed at boot with "conflicting Schema URL". Now sourced from the detected resource
(`resource.SchemaURL()`), version-agnostic. Verified: boots, runs migrations, serves :8080.
## Hanzo layer to re-apply on the green base
The re-sync reverted these Hanzo-original packages to SigNoz canonical to kill the
@@ -46,3 +77,37 @@ OpenFGA is the current default and must be replaced), `pkg/zapreceiver` +
`pkg/zapmetricreceiver` (ZAP-native OTLP receivers), `pkg/billing` + `pkg/types/billingtypes`.
Preserved through the sync: module path, `mount.go` (the `cloud.Register`/zip mount
adapter), `NOTICE`, `LICENSE`.
## Config env naming (debranded — no SIGNOZ/CLICKHOUSE)
Operator-facing env vars and config keys are Hanzo-branded, never SigNoz/ClickHouse.
Naming-only: the store is still ClickHouse wire-protocol under the hood — the
`clickhouse-go` driver, `db.system=clickhouse` semconv, and the `clickhouse`
telemetrystore provider registration all stay (implementation, not surface).
- **Env prefix `O11Y_`** (was `SIGNOZ_`): set once at `pkg/config/envprovider/provider.go`
(`prefix`). The koanf env provider derives every structured key from it. Single `_`
is the `::` path delimiter, double `__` is a literal `_`.
- **Store key segment `datastore`** (was `clickhouse`): the `mapstructure` tag on
`telemetrystore.Config.Clickhouse` is `datastore` (the Go field/type keep the
`Clickhouse` name — internal). YAML key `telemetrystore.datastore`. Provider
selector value stays `clickhouse`.
- **Canonical DSN key `O11Y_DATASTORE_DSN`** (flat — THE operator knob): wired as an
override alias in `pkg/signoz/config.go` (`mergeAndEnsureBackwardCompatibility`),
mapping into `telemetrystore.datastore.dsn`. Value → Hanzo Datastore
(`tcp://datastore.hanzo.svc:9000/?database=o11y`, set at deploy time). It takes
precedence over the structured `O11Y_TELEMETRYSTORE_DATASTORE_DSN`, which stays as
an internal fallback (don't document/set the long form). Keep operator knobs flat and
short — no `O11Y_A_B_C_D` compounds where a flat alias reads better.
- **Legacy override aliases** in `pkg/signoz/config.go` (`mergeAndEnsureBackwardCompatibility`)
debranded too: `O11Y_LOCAL_DB_PATH`, `DatastoreUrl`, `O11Y_SAAS_SEGMENT_KEY`,
`O11Y_JWT_SECRET`; and headless web via `O11Y_WEB_ENABLED` / `O11Y_WEB_DIRECTORY`.
- **Deliberately NOT renamed** (implementation / not app-config surface): the `clickhouse`
provider value + `MustNewName("clickhouse")` registration, `clickhouse-go` driver + mock,
SQL/table/DB names (`signoz_traces` etc.) and query-template vars
(`SIGNOZ_START_TIME`/`SIGNOZ_END_TIME`), the ClickHouse **server** container in `deploy/`
(service/volume names, its own `CLICKHOUSE_SKIP_USER_SETUP` env), the `SIGNOZ_E2E_*`
Playwright test-harness vars (separate `tests/e2e` subsystem). The
`O11Y_OTEL_COLLECTOR_DATASTORE_*` keys in `deploy/` are consumed by the
`hanzoai/signoz-otel-collector` fork — renamed here for consistency; that fork must
accept the `_DATASTORE_` segment (coordinated cross-repo rename).
+4 -3
View File
@@ -138,12 +138,13 @@ telemetrystore:
dial_timeout: 5s
# Specifies the telemetrystore provider to use.
provider: clickhouse
clickhouse:
# The DSN for Hanzo Datastore.
datastore:
# The DSN for Hanzo Datastore. Canonical env override: O11Y_DATASTORE_DSN
# (e.g. tcp://datastore.hanzo.svc:9000/?database=o11y).
dsn: tcp://datastore:9000
# The cluster name for Hanzo Datastore.
cluster: cluster
# The query settings for clickhouse.
# The query settings for the datastore.
settings:
max_execution_time: 0
max_execution_time_leaf: 0
+7 -7
View File
@@ -198,7 +198,7 @@ services:
- ./clickhouse-setup/data/observe/:/var/lib/observe/
environment:
- O11Y_ALERTMANAGER_PROVIDER=observe
- O11Y_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_SQLSTORE_SQLITE_PATH=/var/lib/ghcr.io/hanzoai/o11y.db
- O11Y_TOKENIZER_JWT_SECRET=secret
healthcheck:
@@ -229,9 +229,9 @@ services:
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}}
- LOW_CARDINAL_EXCEPTION_GROUPING=false
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_DATASTORE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_DATASTORE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_TIMEOUT=10m
ports:
# - "1777:1777" # pprof extension
@@ -243,9 +243,9 @@ services:
!!merge <<: *db-depend
image: ghcr.io/hanzoai/otel-collector:v0.144.2
environment:
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_DATASTORE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_DATASTORE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_TIMEOUT=10m
entrypoint:
- /bin/sh
+7 -7
View File
@@ -124,7 +124,7 @@ services:
- sqlite:/var/lib/observe/
environment:
- O11Y_ALERTMANAGER_PROVIDER=observe
- O11Y_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_SQLSTORE_SQLITE_PATH=/var/lib/ghcr.io/hanzoai/o11y.db
- O11Y_TOKENIZER_JWT_SECRET=secret
healthcheck:
@@ -155,9 +155,9 @@ services:
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}}
- LOW_CARDINAL_EXCEPTION_GROUPING=false
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_DATASTORE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_DATASTORE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_TIMEOUT=10m
ports:
# - "1777:1777" # pprof extension
@@ -169,9 +169,9 @@ services:
!!merge <<: *db-depend
image: ghcr.io/hanzoai/otel-collector:v0.144.2
environment:
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_DATASTORE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_DATASTORE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_TIMEOUT=10m
entrypoint:
- /bin/sh
+1 -1
View File
@@ -1 +1 @@
COMPOSE_PROJECT_NAME=signoz
COMPOSE_PROJECT_NAME=o11y
+7 -7
View File
@@ -189,7 +189,7 @@ services:
- sqlite:/var/lib/observe/
environment:
- O11Y_ALERTMANAGER_PROVIDER=observe
- O11Y_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_SQLSTORE_SQLITE_PATH=/var/lib/ghcr.io/hanzoai/o11y.db
- O11Y_TOKENIZER_JWT_SECRET=secret
healthcheck:
@@ -219,9 +219,9 @@ services:
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name=observe-host,os.type=linux
- LOW_CARDINAL_EXCEPTION_GROUPING=false
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_DATASTORE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_DATASTORE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_TIMEOUT=10m
ports:
# - "1777:1777" # pprof extension
@@ -232,9 +232,9 @@ services:
image: ghcr.io/hanzoai/otel-collector:${OTELCOL_TAG:-v0.144.2}
container_name: o11y-telemetrystore-migrator
environment:
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_DATASTORE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_DATASTORE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_TIMEOUT=10m
entrypoint:
- /bin/sh
+7 -7
View File
@@ -117,7 +117,7 @@ services:
- sqlite:/var/lib/observe/
environment:
- O11Y_ALERTMANAGER_PROVIDER=observe
- O11Y_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_SQLSTORE_SQLITE_PATH=/var/lib/ghcr.io/hanzoai/o11y.db
- O11Y_TOKENIZER_JWT_SECRET=secret
healthcheck:
@@ -147,9 +147,9 @@ services:
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name=observe-host,os.type=linux
- LOW_CARDINAL_EXCEPTION_GROUPING=false
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_DATASTORE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_DATASTORE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_TIMEOUT=10m
ports:
# - "1777:1777" # pprof extension
@@ -160,9 +160,9 @@ services:
image: ghcr.io/hanzoai/otel-collector:${OTELCOL_TAG:-v0.144.2}
container_name: o11y-telemetrystore-migrator
environment:
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_DATASTORE_DSN=tcp://clickhouse:9000
- O11Y_OTEL_COLLECTOR_DATASTORE_CLUSTER=cluster
- O11Y_OTEL_COLLECTOR_DATASTORE_REPLICATION=true
- O11Y_OTEL_COLLECTOR_TIMEOUT=10m
entrypoint:
- /bin/sh
+1 -1
View File
@@ -9,7 +9,7 @@ import (
)
const (
prefix string = "SIGNOZ_"
prefix string = "O11Y_"
scheme string = "env"
)
+17 -17
View File
@@ -11,8 +11,8 @@ import (
"github.com/stretchr/testify/require"
)
// clearSignozEnv unsets all existing SIGNOZ_* env vars for the duration of the test.
func clearSignozEnv(t *testing.T) {
// clearEnv unsets all existing O11Y_* env vars for the duration of the test.
func clearEnv(t *testing.T) {
t.Helper()
for _, kv := range os.Environ() {
if strings.HasPrefix(kv, prefix) {
@@ -25,12 +25,12 @@ func clearSignozEnv(t *testing.T) {
}
func TestGetWithStrings(t *testing.T) {
clearSignozEnv(t)
t.Setenv("SIGNOZ_K1_K2", "string")
t.Setenv("SIGNOZ_K3__K4", "string")
t.Setenv("SIGNOZ_K5__K6_K7__K8", "string")
t.Setenv("SIGNOZ_K9___K10", "string")
t.Setenv("SIGNOZ_K11____K12", "string")
clearEnv(t)
t.Setenv("O11Y_K1_K2", "string")
t.Setenv("O11Y_K3__K4", "string")
t.Setenv("O11Y_K5__K6_K7__K8", "string")
t.Setenv("O11Y_K9___K10", "string")
t.Setenv("O11Y_K11____K12", "string")
expected := map[string]any{
"k1::k2": "string",
"k3_k4": "string",
@@ -47,7 +47,7 @@ func TestGetWithStrings(t *testing.T) {
}
func TestGetWithNoPrefix(t *testing.T) {
clearSignozEnv(t)
clearEnv(t)
t.Setenv("K1_K2", "string")
t.Setenv("K3_K4", "string")
expected := map[string]any{}
@@ -60,11 +60,11 @@ func TestGetWithNoPrefix(t *testing.T) {
}
func TestGetWithGoTypes(t *testing.T) {
clearSignozEnv(t)
t.Setenv("SIGNOZ_BOOL", "true")
t.Setenv("SIGNOZ_STRING", "string")
t.Setenv("SIGNOZ_INT", "1")
t.Setenv("SIGNOZ_SLICE", "[1,2]")
clearEnv(t)
t.Setenv("O11Y_BOOL", "true")
t.Setenv("O11Y_STRING", "string")
t.Setenv("O11Y_INT", "1")
t.Setenv("O11Y_SLICE", "[1,2]")
expected := map[string]any{
"bool": "true",
"int": "1",
@@ -80,9 +80,9 @@ func TestGetWithGoTypes(t *testing.T) {
}
func TestGetWithGoTypesWithUnmarshal(t *testing.T) {
t.Setenv("SIGNOZ_BOOL", "true")
t.Setenv("SIGNOZ_STRING", "string")
t.Setenv("SIGNOZ_INT", "1")
t.Setenv("O11Y_BOOL", "true")
t.Setenv("O11Y_STRING", "string")
t.Setenv("O11Y_INT", "1")
type test struct {
Bool bool `mapstructure:"bool"`
+34 -26
View File
@@ -221,13 +221,13 @@ func validateConfig(config Config) error {
}
func mergeAndEnsureBackwardCompatibility(ctx context.Context, logger *slog.Logger, config *Config) {
if os.Getenv("SIGNOZ_LOCAL_DB_PATH") != "" {
logger.WarnContext(ctx, "[Deprecated] env SIGNOZ_LOCAL_DB_PATH is deprecated and scheduled for removal. Please use SIGNOZ_SQLSTORE_SQLITE_PATH instead.")
config.SQLStore.Sqlite.Path = os.Getenv("SIGNOZ_LOCAL_DB_PATH")
if os.Getenv("O11Y_LOCAL_DB_PATH") != "" {
logger.WarnContext(ctx, "[Deprecated] env O11Y_LOCAL_DB_PATH is deprecated and scheduled for removal. Please use O11Y_SQLSTORE_SQLITE_PATH instead.")
config.SQLStore.Sqlite.Path = os.Getenv("O11Y_LOCAL_DB_PATH")
}
if os.Getenv("CONTEXT_TIMEOUT") != "" {
logger.WarnContext(ctx, "[Deprecated] env CONTEXT_TIMEOUT is deprecated and scheduled for removal. Please use SIGNOZ_APISERVER_TIMEOUT_DEFAULT instead.")
logger.WarnContext(ctx, "[Deprecated] env CONTEXT_TIMEOUT is deprecated and scheduled for removal. Please use O11Y_APISERVER_TIMEOUT_DEFAULT instead.")
contextTimeoutDuration, err := time.ParseDuration(os.Getenv("CONTEXT_TIMEOUT") + "s")
if err == nil {
config.APIServer.Timeout.Default = contextTimeoutDuration
@@ -237,7 +237,7 @@ func mergeAndEnsureBackwardCompatibility(ctx context.Context, logger *slog.Logge
}
if os.Getenv("CONTEXT_TIMEOUT_MAX_ALLOWED") != "" {
logger.WarnContext(ctx, "[Deprecated] env CONTEXT_TIMEOUT_MAX_ALLOWED is deprecated and scheduled for removal. Please use SIGNOZ_APISERVER_TIMEOUT_MAX instead.")
logger.WarnContext(ctx, "[Deprecated] env CONTEXT_TIMEOUT_MAX_ALLOWED is deprecated and scheduled for removal. Please use O11Y_APISERVER_TIMEOUT_MAX instead.")
contextTimeoutDuration, err := time.ParseDuration(os.Getenv("CONTEXT_TIMEOUT_MAX_ALLOWED") + "s")
if err == nil {
@@ -248,27 +248,35 @@ func mergeAndEnsureBackwardCompatibility(ctx context.Context, logger *slog.Logge
}
if os.Getenv("STORAGE") != "" {
logger.WarnContext(ctx, "[Deprecated] env STORAGE is deprecated and scheduled for removal. Please use SIGNOZ_TELEMETRYSTORE_PROVIDER instead.")
logger.WarnContext(ctx, "[Deprecated] env STORAGE is deprecated and scheduled for removal. Please use O11Y_TELEMETRYSTORE_PROVIDER instead.")
config.TelemetryStore.Provider = os.Getenv("STORAGE")
}
if os.Getenv("ClickHouseUrl") != "" {
logger.WarnContext(ctx, "[Deprecated] env ClickHouseUrl is deprecated and scheduled for removal. Please use SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN instead.")
config.TelemetryStore.Clickhouse.DSN = os.Getenv("ClickHouseUrl")
if os.Getenv("DatastoreUrl") != "" {
logger.WarnContext(ctx, "[Deprecated] env DatastoreUrl is deprecated and scheduled for removal. Please use O11Y_DATASTORE_DSN instead.")
config.TelemetryStore.Clickhouse.DSN = os.Getenv("DatastoreUrl")
}
// O11Y_DATASTORE_DSN is the canonical, flat operator-facing key for the Hanzo
// Datastore connection. It takes precedence over the structured
// O11Y_TELEMETRYSTORE_DATASTORE_DSN (telemetrystore.datastore.dsn), which stays
// as an internal fallback.
if dsn := os.Getenv("O11Y_DATASTORE_DSN"); dsn != "" {
config.TelemetryStore.Clickhouse.DSN = dsn
}
if os.Getenv("INVITE_EMAIL_TEMPLATE") != "" {
logger.WarnContext(ctx, "[Deprecated] env INVITE_EMAIL_TEMPLATE is deprecated and scheduled for removal. Please use SIGNOZ_EMAILING_TEMPLATES_DIRECTORY instead.")
logger.WarnContext(ctx, "[Deprecated] env INVITE_EMAIL_TEMPLATE is deprecated and scheduled for removal. Please use O11Y_EMAILING_TEMPLATES_DIRECTORY instead.")
config.Emailing.Templates.Directory = path.Dir(os.Getenv("INVITE_EMAIL_TEMPLATE"))
}
if os.Getenv("SMTP_ENABLED") != "" {
logger.WarnContext(ctx, "[Deprecated] env SMTP_ENABLED is deprecated and scheduled for removal. Please use SIGNOZ_EMAILING_ENABLED instead.")
logger.WarnContext(ctx, "[Deprecated] env SMTP_ENABLED is deprecated and scheduled for removal. Please use O11Y_EMAILING_ENABLED instead.")
config.Emailing.Enabled = os.Getenv("SMTP_ENABLED") == "true"
}
if os.Getenv("SMTP_HOST") != "" {
logger.WarnContext(ctx, "[Deprecated] env SMTP_HOST is deprecated and scheduled for removal. Please use SIGNOZ_EMAILING_ADDRESS instead.")
logger.WarnContext(ctx, "[Deprecated] env SMTP_HOST is deprecated and scheduled for removal. Please use O11Y_EMAILING_ADDRESS instead.")
if os.Getenv("SMTP_PORT") != "" {
config.Emailing.SMTP.Address = os.Getenv("SMTP_HOST") + ":" + os.Getenv("SMTP_PORT")
} else {
@@ -277,49 +285,49 @@ func mergeAndEnsureBackwardCompatibility(ctx context.Context, logger *slog.Logge
}
if os.Getenv("SMTP_PORT") != "" {
logger.WarnContext(ctx, "[Deprecated] env SMTP_PORT is deprecated and scheduled for removal. Please use SIGNOZ_EMAILING_ADDRESS instead.")
logger.WarnContext(ctx, "[Deprecated] env SMTP_PORT is deprecated and scheduled for removal. Please use O11Y_EMAILING_ADDRESS instead.")
}
if os.Getenv("SMTP_USERNAME") != "" {
logger.WarnContext(ctx, "[Deprecated] env SMTP_USERNAME is deprecated and scheduled for removal. Please use SIGNOZ_EMAILING_AUTH_USERNAME instead.")
logger.WarnContext(ctx, "[Deprecated] env SMTP_USERNAME is deprecated and scheduled for removal. Please use O11Y_EMAILING_AUTH_USERNAME instead.")
config.Emailing.SMTP.Auth.Username = os.Getenv("SMTP_USERNAME")
}
if os.Getenv("SMTP_PASSWORD") != "" {
logger.WarnContext(ctx, "[Deprecated] env SMTP_PASSWORD is deprecated and scheduled for removal. Please use SIGNOZ_EMAILING_AUTH_PASSWORD instead.")
logger.WarnContext(ctx, "[Deprecated] env SMTP_PASSWORD is deprecated and scheduled for removal. Please use O11Y_EMAILING_AUTH_PASSWORD instead.")
config.Emailing.SMTP.Auth.Password = os.Getenv("SMTP_PASSWORD")
}
if os.Getenv("SMTP_FROM") != "" {
logger.WarnContext(ctx, "[Deprecated] env SMTP_FROM is deprecated and scheduled for removal. Please use SIGNOZ_EMAILING_FROM instead.")
logger.WarnContext(ctx, "[Deprecated] env SMTP_FROM is deprecated and scheduled for removal. Please use O11Y_EMAILING_FROM instead.")
config.Emailing.SMTP.From = os.Getenv("SMTP_FROM")
}
if os.Getenv("SIGNOZ_SAAS_SEGMENT_KEY") != "" {
logger.WarnContext(ctx, "[Deprecated] env SIGNOZ_SAAS_SEGMENT_KEY is deprecated and scheduled for removal. Please use SIGNOZ_ANALYTICS_SEGMENT_KEY instead.")
config.Analytics.Segment.Key = os.Getenv("SIGNOZ_SAAS_SEGMENT_KEY")
if os.Getenv("O11Y_SAAS_SEGMENT_KEY") != "" {
logger.WarnContext(ctx, "[Deprecated] env O11Y_SAAS_SEGMENT_KEY is deprecated and scheduled for removal. Please use O11Y_ANALYTICS_SEGMENT_KEY instead.")
config.Analytics.Segment.Key = os.Getenv("O11Y_SAAS_SEGMENT_KEY")
}
if os.Getenv("TELEMETRY_ENABLED") != "" {
logger.WarnContext(ctx, "[Deprecated] env TELEMETRY_ENABLED is deprecated and scheduled for removal. Please use SIGNOZ_ANALYTICS_ENABLED instead.")
logger.WarnContext(ctx, "[Deprecated] env TELEMETRY_ENABLED is deprecated and scheduled for removal. Please use O11Y_ANALYTICS_ENABLED instead.")
config.Analytics.Enabled = os.Getenv("TELEMETRY_ENABLED") == "true"
}
if os.Getenv("USE_SPAN_METRICS") != "" {
logger.WarnContext(ctx, "[Deprecated] env USE_SPAN_METRICS is deprecated and scheduled for removal. Please use SIGNOZ_FLAGGER_CONFIG_BOOLEAN_USE__SPAN__METRICS instead.")
logger.WarnContext(ctx, "[Deprecated] env USE_SPAN_METRICS is deprecated and scheduled for removal. Please use O11Y_FLAGGER_CONFIG_BOOLEAN_USE__SPAN__METRICS instead.")
if config.Flagger.Config.Boolean == nil {
config.Flagger.Config.Boolean = make(map[string]bool)
}
config.Flagger.Config.Boolean[flagger.FeatureUseSpanMetrics.String()] = os.Getenv("USE_SPAN_METRICS") == "true"
}
if os.Getenv("SIGNOZ_JWT_SECRET") != "" {
logger.WarnContext(ctx, "[Deprecated] env SIGNOZ_JWT_SECRET is deprecated and scheduled for removal. Please use SIGNOZ_TOKENIZER_JWT_SECRET instead.")
config.Tokenizer.JWT.Secret = os.Getenv("SIGNOZ_JWT_SECRET")
if os.Getenv("O11Y_JWT_SECRET") != "" {
logger.WarnContext(ctx, "[Deprecated] env O11Y_JWT_SECRET is deprecated and scheduled for removal. Please use O11Y_TOKENIZER_JWT_SECRET instead.")
config.Tokenizer.JWT.Secret = os.Getenv("O11Y_JWT_SECRET")
}
if os.Getenv("KAFKA_SPAN_EVAL") != "" {
logger.WarnContext(ctx, "[Deprecated] env KAFKA_SPAN_EVAL is deprecated and scheduled for removal. Please use SIGNOZ_FLAGGER_CONFIG_BOOLEAN_KAFKA__SPAN__EVAL instead.")
logger.WarnContext(ctx, "[Deprecated] env KAFKA_SPAN_EVAL is deprecated and scheduled for removal. Please use O11Y_FLAGGER_CONFIG_BOOLEAN_KAFKA__SPAN__EVAL instead.")
if config.Flagger.Config.Boolean == nil {
config.Flagger.Config.Boolean = make(map[string]bool)
}
@@ -327,7 +335,7 @@ func mergeAndEnsureBackwardCompatibility(ctx context.Context, logger *slog.Logge
}
if os.Getenv("RULES_EVAL_DELAY") != "" {
logger.WarnContext(ctx, "[Deprecated] env RULES_EVAL_DELAY is deprecated and scheduled for removal. Please use SIGNOZ_RULER_EVAL__DELAY instead.")
logger.WarnContext(ctx, "[Deprecated] env RULES_EVAL_DELAY is deprecated and scheduled for removal. Please use O11Y_RULER_EVAL__DELAY instead.")
if d, err := time.ParseDuration(os.Getenv("RULES_EVAL_DELAY")); err == nil {
config.Ruler.EvalDelay = d
} else {
+13
View File
@@ -7,6 +7,7 @@ import (
"github.com/hanzoai/o11y/pkg/config/configtest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// This is a test to ensure that all fields of config implement the factory.Config interface and are valid with
@@ -16,3 +17,15 @@ func TestValidateConfig(t *testing.T) {
_, err := NewConfig(context.Background(), logger, configtest.NewResolverConfig())
assert.NoError(t, err)
}
// O11Y_DATASTORE_DSN is the canonical, flat operator-facing key: it must map into
// the telemetrystore datastore DSN via mergeAndEnsureBackwardCompatibility.
func TestDatastoreDSNCanonicalAlias(t *testing.T) {
const dsn = "tcp://datastore.hanzo.svc:9000/?database=o11y"
t.Setenv("O11Y_DATASTORE_DSN", dsn)
logger := slog.New(slog.DiscardHandler)
config, err := NewConfig(context.Background(), logger, configtest.NewResolverConfig())
require.NoError(t, err)
assert.Equal(t, dsn, config.TelemetryStore.Clickhouse.DSN)
}
+2 -2
View File
@@ -13,8 +13,8 @@ type Config struct {
// Connection is the connection configuration
Connection ConnectionConfig `mapstructure:",squash"`
// Clickhouse is the clickhouse configuration
Clickhouse ClickhouseConfig `mapstructure:"clickhouse"`
// Clickhouse is the datastore (ClickHouse-protocol) configuration
Clickhouse ClickhouseConfig `mapstructure:"datastore"`
}
type ConnectionConfig struct {
+10 -10
View File
@@ -13,11 +13,11 @@ import (
)
func TestNewWithEnvProvider(t *testing.T) {
t.Setenv("SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN", "tcp://localhost:9000")
t.Setenv("SIGNOZ_TELEMETRYSTORE_MAX__IDLE__CONNS", "60")
t.Setenv("SIGNOZ_TELEMETRYSTORE_MAX__OPEN__CONNS", "150")
t.Setenv("SIGNOZ_TELEMETRYSTORE_DIAL__TIMEOUT", "5s")
t.Setenv("SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DEBUG", "true")
t.Setenv("O11Y_TELEMETRYSTORE_DATASTORE_DSN", "tcp://localhost:9000")
t.Setenv("O11Y_TELEMETRYSTORE_MAX__IDLE__CONNS", "60")
t.Setenv("O11Y_TELEMETRYSTORE_MAX__OPEN__CONNS", "150")
t.Setenv("O11Y_TELEMETRYSTORE_DIAL__TIMEOUT", "5s")
t.Setenv("O11Y_TELEMETRYSTORE_DATASTORE_DEBUG", "true")
conf, err := config.New(
context.Background(),
@@ -50,11 +50,11 @@ func TestNewWithEnvProvider(t *testing.T) {
}
func TestNewWithEnvProviderWithQuerySettings(t *testing.T) {
t.Setenv("SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_SETTINGS_MAX__EXECUTION__TIME", "10")
t.Setenv("SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_SETTINGS_MAX__EXECUTION__TIME__LEAF", "10")
t.Setenv("SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_SETTINGS_TIMEOUT__BEFORE__CHECKING__EXECUTION__SPEED", "10")
t.Setenv("SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_SETTINGS_MAX__BYTES__TO__READ", "1000000")
t.Setenv("SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_SETTINGS_MAX__RESULT__ROWS", "10000")
t.Setenv("O11Y_TELEMETRYSTORE_DATASTORE_SETTINGS_MAX__EXECUTION__TIME", "10")
t.Setenv("O11Y_TELEMETRYSTORE_DATASTORE_SETTINGS_MAX__EXECUTION__TIME__LEAF", "10")
t.Setenv("O11Y_TELEMETRYSTORE_DATASTORE_SETTINGS_TIMEOUT__BEFORE__CHECKING__EXECUTION__SPEED", "10")
t.Setenv("O11Y_TELEMETRYSTORE_DATASTORE_SETTINGS_MAX__BYTES__TO__READ", "1000000")
t.Setenv("O11Y_TELEMETRYSTORE_DATASTORE_SETTINGS_MAX__RESULT__ROWS", "10000")
conf, err := config.New(
context.Background(),
+1 -1
View File
@@ -46,7 +46,7 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/hanzoai/o11y/pkg/tokenizer/jwttokenizer")
if config.JWT.Secret == "" {
settings.Logger().ErrorContext(ctx, "🚨 CRITICAL SECURITY ISSUE: No JWT secret key specified!", slog.String("error", "SIGNOZ_TOKENIZER_JWT_SECRET environment variable is not set. This has dire consequences for the security of the application. Without a JWT secret, user sessions are vulnerable to tampering and unauthorized access. Please set the SIGNOZ_TOKENIZER_JWT_SECRET environment variable immediately. For more information, please refer to https://github.com/hanzoai/o11y/issues/8400."))
settings.Logger().ErrorContext(ctx, "🚨 CRITICAL SECURITY ISSUE: No JWT secret key specified!", slog.String("error", "O11Y_TOKENIZER_JWT_SECRET environment variable is not set. This has dire consequences for the security of the application. Without a JWT secret, user sessions are vulnerable to tampering and unauthorized access. Please set the O11Y_TOKENIZER_JWT_SECRET environment variable immediately. For more information, please refer to https://github.com/hanzoai/o11y/issues/8400."))
}
lastObservedAtCache, err := ristretto.NewCache(&ristretto.Config[string, map[valuer.UUID]time.Time]{