Skip to content

feat: migrate observability to Axiom#906

Merged
AchoArnold merged 11 commits into
mainfrom
feat/axiom-observability-migration
May 29, 2026
Merged

feat: migrate observability to Axiom#906
AchoArnold merged 11 commits into
mainfrom
feat/axiom-observability-migration

Conversation

@AchoArnold
Copy link
Copy Markdown
Member

Summary

Migrate production observability from GCP Cloud Logging + Uptrace to Axiom for all three signals: logs, traces, and metrics.

Changes

  • Logging: Replace \zerodriver\ JSON output with Axiom's zerolog adapter as the sole production log writer. Logs ship directly to Axiom's ingest API.
  • Traces: Replace Uptrace with standard OTLP HTTP exporter pointed at \�pi.axiom.co.
  • Metrics: Replace Google Cloud Monitoring with OTLP HTTP metric exporter pointed at \�pi.axiom.co.
  • Log-trace correlation: Use standard \ race_id/\span_id\ fields instead of GCP-specific format.

Environment Variables

Variable Purpose
\AXIOM_TOKEN\ Axiom API token
\AXIOM_DATASET\ Target dataset for logs, traces, and metrics

Rollback

All old provider code is kept as dead functions:

  1. Logging: Change \logDriver()\ to call \jsonLogger()\ instead of \�xiomLogger()\
  2. Traces/Metrics: Change \InitializeTraceProvider()\ to call \initializeUptraceProvider()\

Testing

  • \go build ./...\ passes
  • Pre-commit hooks pass (go-fumpt, go-lint, go-imports, go-mod-tidy)
  • Pre-existing \go vet\ warnings unchanged (not introduced by this PR)

AchoArnold and others added 6 commits May 26, 2026 10:18
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace GCP Cloud Logging JSON output with Axiom's zerolog writer.
Logs are shipped directly to Axiom's ingest API in production.
Local dev still uses console logger. jsonLogger() kept for rollback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace GCP-specific TraceContext format with standard OTel field names
so Axiom can correlate logs with traces.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace Uptrace with standard OTLP HTTP exporters pointed at
api.axiom.co for traces and metrics. Old providers kept as
dead functions for rollback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add AXIOM_TOKEN and AXIOM_DATASET configuration. Mark UPTRACE_DSN
as deprecated but keep for rollback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@what-the-diff
Copy link
Copy Markdown

what-the-diff Bot commented May 29, 2026

PR Summary

  • Update Axiom Observability Configuration
    The team made adjustments to configuration for Axiom's observability tools. This involves newly added environment variables.

  • New Axiom Go SDK Dependency
    The project now includes a new software kit for Go from Axiom (version v0.32.0).

  • Update OpenTelemetry Dependencies
    We updated tracing and metrics libraries (OpenTelemetry) to a newer version.

  • Deprecated UPTRACE_DSN & Replaced Tracing Provider
    Some outdated parts of the code were removed and replaced to utilize Axiom instead of Uptrace for performance monitoring.

  • New Function for Axiom Trace Provider Initialization
    A new function was added to set up Axiom's tracing and metrics functions.

  • Improved Logging Mechanism
    The system's logging functionality has been enhanced with a new axiomLogger function.

  • Adjustment to Event Decoration
    Some changes were made to how the system adds context data for logged events.


  • Comprehensive Migration Document Added
    The project now includes guidance for a major software upgrade process transitioning from Nuxt 2 and Vuetify 2 to Nuxt 4 and Vuetify 4.

  • Detailed Target Versions for Migration
    The new guide provides details about the target software versions involved in the migration.

  • Major Architectural Decisions Described
    Important technical decisions made about software design and structure are now detailed, like switching to Composition API, using Pinia for state management, and static site generation.

  • New Directory Structure Outlined
    A new organization structure for the application files was introduced.

  • Specified Key Migration Patterns
    There's more detailed guidance on how to transition various components and systems to the new software version.

  • Listed Breaking Changes in Vuetify 4
    The document includes detailed information on upcoming changes in Vuetify 4 that could affect the project.

  • Provided Migration Order
    The guide includes an order for handling the migration steps, starting from configuration up to complete verification.

  • Outlined Verification Strategy
    A verification strategy has been set to ensure all components and pages function correctly under the new guidelines.

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 29, 2026

Not up to standards ⛔

🔴 Issues 50 minor

Alerts:
⚠ 50 issues (≤ 0 issues of at least minor severity)

Results:
50 new issues

Category Results
CodeStyle 50 minor

View in Codacy

🟢 Metrics 19 complexity · 4 duplication

Metric Results
Complexity 19
Duplication 4

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 29, 2026

Greptile Summary

This PR migrates the production observability stack from GCP Cloud Logging + Uptrace to Axiom, covering all three signals (logs via the axiom-go zerolog adapter, traces and metrics via OTLP HTTP exporters pointed at api.axiom.co), and updates log-trace correlation fields from GCP-specific format to standard trace_id/span_id strings.

  • Logging: logDriver() now routes non-local environments to axiomLogger(), which uses the axiom-go zerolog adapter; the old jsonLogger() (GCP severity mapping) is kept as dead code for rollback.
  • Traces & Metrics: New initializeAxiomTraceProvider() creates OTLP HTTP exporters with Authorization: Bearer AXIOM_TOKEN and X-Axiom-Dataset headers, replacing both initializeUptraceProvider and initializeGoogleTraceProvider; all OTel packages bumped from v1.43.0 → v1.44.0.

Confidence Score: 3/5

The observability wiring is correct in structure, but the error handling around missing credentials has two gaps that could leave production running with no traces/metrics and no logs in Axiom, with no startup-time indication of the problem.

When AXIOM_TOKEN or AXIOM_DATASET is absent, the logger silently falls back to stderr without printing why, and the OTLP exporters are constructed successfully but will silently reject every export batch with a 401. Both gaps mean a misconfigured deployment looks healthy at startup while losing all observability data. The fixes are small but should land before this reaches production.

api/pkg/di/container.go — both axiomLogger and initializeAxiomTraceProvider need attention; the other files are straightforward.

Important Files Changed

Filename Overview
api/pkg/di/container.go Adds initializeAxiomTraceProvider for OTLP traces+metrics and axiomLogger for log ingestion; silent error swallowing in the logger fallback and missing env-var guards on the trace exporter are the main concerns.
api/pkg/telemetry/zerolog_logger.go Replaces GCP-specific TraceContext() call with standard trace_id/span_id/trace_sampled string fields; straightforward and correct.
api/go.mod Adds axiom-go v0.32.0 and promotes OTLP HTTP exporters to direct dependencies at v1.44.0; stdouttrace/stdoutmetric stay at v1.43.0 as indirect deps (minor inconsistency, no functional impact).
api/.env.docker Documents the two new AXIOM_TOKEN / AXIOM_DATASET env vars and marks UPTRACE_DSN as deprecated; straightforward config update.
docs/superpowers/specs/2026-05-26-nuxt4-vuetify4-migration-design.md New design specification document for a future Nuxt 4 / Vuetify 4 frontend migration; unrelated to the observability change in this PR.

Sequence Diagram

sequenceDiagram
    participant App as API Service
    participant Logger as axiomLogger
    participant OTLP as initializeAxiomTraceProvider
    participant AxiomLogs as Axiom Logs Ingest
    participant AxiomOTLP as Axiom OTLP (api.axiom.co)

    App->>Logger: logDriver() [non-local]
    Logger->>AxiomLogs: axiomzerolog.New(SetDataset)
    alt Axiom not configured
        AxiomLogs-->>Logger: error
        Logger-->>App: stderr fallback (silent)
    else configured
        AxiomLogs-->>Logger: writer
        Logger-->>App: zerolog to Axiom writer
    end

    App->>OTLP: InitializeTraceProvider()
    OTLP->>AxiomOTLP: otlptracehttp.New(endpoint, auth headers)
    OTLP->>AxiomOTLP: otlpmetrichttp.New(endpoint, auth headers)
    AxiomOTLP-->>OTLP: exporters created (lazy connect)
    OTLP-->>App: shutdown func

    App->>AxiomLogs: log entries via zerolog JSON
    App->>AxiomOTLP: spans via BatchSpanProcessor to /v1/traces
    App->>AxiomOTLP: metrics via PeriodicReader to /v1/metrics
Loading

Reviews (1): Last reviewed commit: "chore: add Axiom env vars to .env.docker" | Re-trigger Greptile

Comment thread api/pkg/di/container.go
Comment on lines +1971 to +1978
axiomWriter, err := axiomzerolog.New(
axiomzerolog.SetDataset(os.Getenv("AXIOM_DATASET")),
)
if err != nil {
// Fall back to stderr JSON if Axiom is not configured
zl := zerolog.New(os.Stderr).With().Timestamp().CallerWithSkipFrameCount(skipFrameCount).Logger()
return &zerodriver.Logger{Logger: &zl}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Silent fallback without any output makes misconfiguration invisible in production. When axiomzerolog.New() fails (e.g. AXIOM_TOKEN or AXIOM_DATASET is unset), the function returns a stderr logger but writes nothing to explain the failure. An operator checking why logs aren't appearing in Axiom has no signal to act on — the app simply logs to stderr as if that were intentional. Writing the error to stderr before falling back gives operators a clear indication that Axiom ingestion is not active.

Suggested change
axiomWriter, err := axiomzerolog.New(
axiomzerolog.SetDataset(os.Getenv("AXIOM_DATASET")),
)
if err != nil {
// Fall back to stderr JSON if Axiom is not configured
zl := zerolog.New(os.Stderr).With().Timestamp().CallerWithSkipFrameCount(skipFrameCount).Logger()
return &zerodriver.Logger{Logger: &zl}
}
axiomWriter, err := axiomzerolog.New(
axiomzerolog.SetDataset(os.Getenv("AXIOM_DATASET")),
)
if err != nil {
// Fall back to stderr JSON if Axiom is not configured
_, _ = fmt.Fprintf(os.Stderr, "axiom logger init failed, falling back to stderr: %v\n", err)
zl := zerolog.New(os.Stderr).With().Timestamp().CallerWithSkipFrameCount(skipFrameCount).Logger()
return &zerodriver.Logger{Logger: &zl}
}

Comment thread api/pkg/di/container.go
Comment on lines +1847 to +1853
func (container *Container) initializeAxiomTraceProvider(version string, namespace string) func() {
container.logger.Debug("initializing axiom trace provider")

headers := map[string]string{
"Authorization": "Bearer " + os.Getenv("AXIOM_TOKEN"),
"X-Axiom-Dataset": os.Getenv("AXIOM_DATASET"),
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing guard for empty AXIOM_TOKEN before constructing the OTLP exporters. otlptracehttp.New() and otlpmetrichttp.New() initialize lazily — they succeed even when AXIOM_TOKEN is empty, producing an Authorization: Bearer header. All subsequent export calls will receive HTTP 401 responses from Axiom and be silently dropped (the OTel batch processor discards failed batches). Unlike the logger which at least falls back visibly to stderr, there is no equivalent guard here, so traces and metrics will be lost with no startup-time indication.

Suggested change
func (container *Container) initializeAxiomTraceProvider(version string, namespace string) func() {
container.logger.Debug("initializing axiom trace provider")
headers := map[string]string{
"Authorization": "Bearer " + os.Getenv("AXIOM_TOKEN"),
"X-Axiom-Dataset": os.Getenv("AXIOM_DATASET"),
}
func (container *Container) initializeAxiomTraceProvider(version string, namespace string) func() {
container.logger.Debug("initializing axiom trace provider")
if os.Getenv("AXIOM_TOKEN") == "" {
container.logger.Fatal(stacktrace.NewError("AXIOM_TOKEN is required for the Axiom trace/metric provider"))
}
if os.Getenv("AXIOM_DATASET") == "" {
container.logger.Fatal(stacktrace.NewError("AXIOM_DATASET is required for the Axiom trace/metric provider"))
}
headers := map[string]string{
"Authorization": "Bearer " + os.Getenv("AXIOM_TOKEN"),
"X-Axiom-Dataset": os.Getenv("AXIOM_DATASET"),
}

AchoArnold and others added 5 commits May 29, 2026 11:59
Use regional edge endpoint for improved data locality on both
OTLP exporters (traces/metrics) and the zerolog log adapter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Logs use the default Axiom API endpoint. Edge endpoint is only
for OTLP trace and metric exporters.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Traces and logs go to AXIOM_TRACES_DATASET (events), metrics go
to AXIOM_METRICS_DATASET (metrics). Replaces single AXIOM_DATASET.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ATASET_METRICS

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AchoArnold AchoArnold merged commit 03ac74b into main May 29, 2026
5 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant