|
| 1 | +# AGENTS.md — codebase map for AI agents |
| 2 | + |
| 3 | +Read this first. It is the orientation map for `ubgo/otelkit` so a fresh agent (or human) knows what every part does, how the pieces connect, and where to make a change — without reading every file. |
| 4 | + |
| 5 | +## What this repo is |
| 6 | + |
| 7 | +`otelkit` is an **OpenTelemetry bootstrap for Go**: one constructor (`Init`) that stands up the trace/metric/log SDK pipeline (providers + exporters + resource + propagators + ordered shutdown) and points it at a backend. It is a *bootstrap, not an SDK* — it wires `go.opentelemetry.io/otel`, never reimplements it. It does **not** write log lines (that's a logger's job) and does **not** do auto-instrumentation. See `README.md` for the user-facing pitch and `docs/architecture.md` for the design. |
| 8 | + |
| 9 | +## Modules (multi-module repo) |
| 10 | + |
| 11 | +| Path | Module | Role | |
| 12 | +|---|---|---| |
| 13 | +| `.` | `github.com/ubgo/otelkit` | Core: everything except gRPC. Deps: stdlib + `otel/*` + `otelconf` + `autoexport`. | |
| 14 | +| `contrib/otelkit-grpc/` | `…/contrib/otelkit-grpc` | OTLP/gRPC exporters. Self-registers via `otelkit.RegisterGRPC` on blank import. Keeps `google.golang.org/grpc` out of the core. | |
| 15 | +| `examples/` | `…/examples` | 8 runnable programs. Replaces the two above to `../`. Must compile in CI. | |
| 16 | + |
| 17 | +`go.work` is a **local-dev convenience only and is gitignored** — CI builds each module standalone (each has its own `go.mod`, the examples/contrib use `replace` to the local core). Minimum Go: **1.25** (pulled up by `otelconf`). |
| 18 | + |
| 19 | +## Core files — what each owns |
| 20 | + |
| 21 | +| File | Responsibility | |
| 22 | +|---|---| |
| 23 | +| `doc.go` | Package overview godoc. | |
| 24 | +| `types.go` | The enums: `Signal`, `Transport`, `Sampler`, `Temporality`, `TLSMode` + their OTEL-spec mappings (ports 4317/4318, `/v1/<signal>` suffixes, protocol/sampler value strings). | |
| 25 | +| `errors.go` | Exported sentinel errors (`ErrMissingEndpoint`, `ErrGRPCNotLinked`, …). | |
| 26 | +| `config.go` | `Config` + `SignalConfig` + defaults, **and** `resolveEndpoint`/`GRPCTarget` — the pure port+path resolver (the footgun-killer). | |
| 27 | +| `env.go` | The `OTEL_*` environment overlay (`applyEnv`) + parse helpers. The only file that reads `os.Getenv`. | |
| 28 | +| `options.go` | The public `With*` functional options + the `Option` type. | |
| 29 | +| `resource.go` | Builds the OTEL `Resource` (detectors, `unknown_service` fallback, `deployment.environment.name`). | |
| 30 | +| `exporters.go` | Per-signal OTLP/HTTP + stdout exporter construction; the gRPC factory seam + `RegisterGRPC`. | |
| 31 | +| `providers.go` | Builds the Tracer/Meter/Logger providers (+ sampler, no-op providers for disabled signals). | |
| 32 | +| `otelkit.go` | **The entry point**: `Init`, the `*Telemetry` handle, accessors, `SetGlobal`, `ForceFlush`, `SelfTest`, the propagator builder, and the dry-run printer. | |
| 33 | +| `shutdown.go` | `Shutdown` (ordered logs→metrics→traces, `errors.Join`) + `RunOnSignal`. | |
| 34 | +| `errhandler.go` | Installs the global OTEL error handler (loud-by-default). | |
| 35 | +| `probe.go` | `ProbeEndpoint` — the connectivity diagnostic. | |
| 36 | +| `presets.go` | Vendor presets (`PresetHyperDX`, …) + `Preset`/`WithPreset`. | |
| 37 | +| `declarative.go` | `OTEL_CONFIG_FILE` delegation to `otelconf`. | |
| 38 | + |
| 39 | +Every file starts with a header comment describing its role; every exported symbol has godoc. |
| 40 | + |
| 41 | +## The three flows to understand |
| 42 | + |
| 43 | +1. **Config resolution** (`otelkit.go:Init` → `options.go` → `env.go` / `declarative.go`): start from `defaultConfig()`, apply preset+options, then either delegate to a config file (`OTEL_CONFIG_FILE`) or overlay `OTEL_*` env. Precedence: **defaults < preset < options < env**; a config file wins outright. |
| 44 | +2. **The Init pipeline** (`otelkit.go:Init`): resolve config → `buildResource` → `buildTracerProvider`/`buildMeterProvider`/`buildLoggerProvider` (each builds an exporter via `exporters.go`) → install error handler → assemble `*Telemetry` → optional self-test. `OTEL_SDK_DISABLED` short-circuits to a no-op handle. |
| 45 | +3. **Endpoint/path resolution** (`config.go:resolveEndpoint`): the single place that turns a host/URL + transport into the correct dial target — gRPC `host:port` (4317), HTTP with the `/v1/<signal>` append rule (and double-append guard), per-signal URLs verbatim. This is the most-tested logic; if you touch OTLP endpoints, this is the file. |
| 46 | + |
| 47 | +## Conventions (also in CONTRIBUTING.md) |
| 48 | + |
| 49 | +- **100% line coverage, `-race`, enforced in CI.** `task cover` fails under 100%. Cover error branches with small test seams (e.g. `buildResourceFn`, `newSDKFn`) rather than leaving them uncovered. |
| 50 | +- **Lean core deps.** Anything heavy (notably gRPC) goes in a `contrib/` module behind the factory seam. |
| 51 | +- **No `init()` magic; pure constructors.** Globals are opt-in via `SetGlobal`. |
| 52 | +- **Loud, not silent.** New failure modes surface as an error or logged warning. |
| 53 | +- **Comments explain *why*, not *what*** — the names carry the *what*. |
| 54 | + |
| 55 | +## Running things |
| 56 | + |
| 57 | +```sh |
| 58 | +task cover # core: race + 100% gate |
| 59 | +cd contrib/otelkit-grpc && go test -race ./... |
| 60 | +cd examples && go build ./... |
| 61 | +``` |
| 62 | + |
| 63 | +## Where to look for X |
| 64 | + |
| 65 | +- "How do I configure it?" → `docs/configuration.md` (env vars) + `options.go`. |
| 66 | +- "Add a vendor" → `presets.go` + `docs/presets.md`; assert the exact endpoint/auth/temporality in `presets_test.go`. |
| 67 | +- "Why isn't data arriving?" → `probe.go`, `errhandler.go`, the dry-run in `otelkit.go`, and `docs/diagnostics.md`. |
| 68 | +- "Add gRPC behavior" → `contrib/otelkit-grpc/grpc.go`. |
| 69 | +- "The design rationale" → `docs/adr/` (Architecture Decision Records). |
0 commit comments