mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-11 05:31:46 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The server emits OpenTelemetry spans so operators can trace agent work > - Each span needs a service version that identifies the code that produced it > - The current service version comes from a static environment value and can become stale after a rebuild > - This pull request records the built commit and resolves the service version from the build stamp, runtime Git, the environment, or an unknown fallback > - The benefit is trace data that identifies the correct built commit during development and deployment ## Linked Issues or Issue Description **What happened?** The server used a static `OTEL_SERVICE_VERSION` value for every OpenTelemetry span. Rebuilds could produce traces with an old commit value. **Expected behavior** The server should report the built commit when a build stamp exists. It should use runtime Git, the environment value, or `unknown` as fallback. **Steps to reproduce** 1. Set `OTEL_SERVICE_VERSION` to an old commit value. 2. Build the server at a different commit. 3. Start the server and inspect the OpenTelemetry service version. 4. Confirm that the built commit takes precedence over the old environment value. ## What Changed - Add a build script that writes the short Git commit to `dist/build-info.json`. - Resolve `service.version` from the build stamp, runtime Git, the environment, or `unknown`. - Log the resolved service version once during server startup. - Add tests for the resolution order and safe behavior without Git. - Document the resolution order in `doc/observability.md`. ## Verification - `pnpm --filter @paperclipai/server build` - `npx vitest run server/src/__tests__/service-version.test.ts` - `pnpm --filter @paperclipai/server typecheck` - Confirm that the build stamp contains the short commit. - Confirm that the stamp wins over the environment value. - Confirm that a build without Git exits successfully without a stamp. ## Risks The server now prefers the built commit over `OTEL_SERVICE_VERSION`. A build without Git uses the existing environment value or `unknown`. The change needs no schema migration and has a single-commit rollback path. ## Model Used OpenAI Codex, GPT-5, tool use and code execution. The runtime does not expose the context window size or reasoning mode. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
102 lines
4.0 KiB
Markdown
102 lines
4.0 KiB
Markdown
# Observability
|
|
|
|
Paperclip ships with **opt-in** OpenTelemetry auto-instrumentation for the
|
|
server process. When activated it produces **traces only** — no metrics and no
|
|
logs are exported by this integration. The OTel packages are *optional peer
|
|
dependencies*: they are not in the default lockfile and are loaded dynamically
|
|
only when an operator turns the feature on.
|
|
|
|
When `OTEL_EXPORTER_OTLP_ENDPOINT` is unset, none of the `@opentelemetry/*`
|
|
packages are imported and there is zero runtime overhead.
|
|
|
|
## Enabling tracing
|
|
|
|
### 1. Install the OTel peer dependencies
|
|
|
|
Install the SDK, the auto-instrumentations bundle, the resources/semconv
|
|
helpers, and **one** exporter matching your chosen OTLP protocol.
|
|
|
|
Common to every protocol:
|
|
|
|
```bash
|
|
pnpm add \
|
|
@opentelemetry/sdk-node \
|
|
@opentelemetry/auto-instrumentations-node \
|
|
@opentelemetry/resources \
|
|
@opentelemetry/semantic-conventions
|
|
```
|
|
|
|
Then add the exporter for the protocol you intend to use:
|
|
|
|
| `OTEL_EXPORTER_OTLP_PROTOCOL` | Exporter package |
|
|
| ----------------------------- | --------------------------------------------- |
|
|
| `grpc` (default if unset) | `@opentelemetry/exporter-trace-otlp-grpc` |
|
|
| `http/protobuf` | `@opentelemetry/exporter-trace-otlp-proto` |
|
|
| `http/json` | `@opentelemetry/exporter-trace-otlp-http` |
|
|
|
|
For example, for the default gRPC path:
|
|
|
|
```bash
|
|
pnpm add @opentelemetry/exporter-trace-otlp-grpc
|
|
```
|
|
|
|
### 2. Set the environment
|
|
|
|
Minimal setup:
|
|
|
|
```bash
|
|
# Required — turns the feature on. Point at your collector.
|
|
# For grpc this is the gRPC target (typically port 4317). For the HTTP
|
|
# protocols give the collector's BASE URL (typically port 4318) — the
|
|
# exporter appends /v1/traces itself.
|
|
export OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4317"
|
|
|
|
# Optional — protocol. Defaults to grpc when unset.
|
|
# Valid values: grpc | http/protobuf | http/json
|
|
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
|
|
|
|
# Optional — service identity attached to every span.
|
|
export OTEL_SERVICE_NAME="paperclip"
|
|
export OTEL_SERVICE_VERSION="2026.5.0"
|
|
```
|
|
|
|
### `service.version` resolution order
|
|
|
|
The `service.version` span attribute reports the commit the running server was
|
|
built from. The server resolves it in this order and uses the first source that
|
|
returns a value:
|
|
|
|
1. **The build stamp.** The server `build` script writes the commit SHA into
|
|
`dist/build-info.json`. The stamp wins so the reported version tracks the
|
|
true built commit and cannot go stale across rebuilds. The build script
|
|
reads the commit from `git rev-parse --short HEAD` first. A Docker image
|
|
build excludes `.git`, so the build script reads the `PAPERCLIP_BUILD_COMMIT`
|
|
environment variable instead. Pass the built commit in that variable so the
|
|
image stamp records the true commit.
|
|
2. **A runtime `git rev-parse --short HEAD`.** This covers `tsx src/index.ts`
|
|
dev mode, where the server runs from the source checkout and writes no
|
|
stamp. A failure here is not fatal.
|
|
3. **The `OTEL_SERVICE_VERSION` environment variable.** This is the fallback
|
|
for a build with no stamp and no reachable git — for example a tarball
|
|
build. `OTEL_SERVICE_VERSION` is a Paperclip-specific variable, not an
|
|
OpenTelemetry SDK variable, so Paperclip controls this precedence.
|
|
4. **`"unknown"`** when no source returns a value.
|
|
|
|
The server logs the resolved `service.version` once at startup, so an operator
|
|
can confirm the value.
|
|
|
|
If `OTEL_EXPORTER_OTLP_PROTOCOL` is set to an unrecognized value, Paperclip
|
|
logs a single warning and falls back to gRPC.
|
|
|
|
If `OTEL_EXPORTER_OTLP_ENDPOINT` is set but the OTel packages are not
|
|
installed, the server logs a single diagnostic line on boot and continues
|
|
without tracing — your server stays up.
|
|
|
|
## Scope
|
|
|
|
This integration emits **traces only**. Metrics and log exporters are out of
|
|
scope and intentionally not configured here. Auto-instrumentations for
|
|
`fs`, `dns`, and `net` are disabled by default because they are too chatty
|
|
for this workload; everything else from
|
|
`@opentelemetry/auto-instrumentations-node` is on (HTTP, Express, PG, etc.).
|