Observe it
Set the standard OpenTelemetry variables and tablewalk sends traces, metrics and logs over OTLP/HTTP. Leave them unset and nothing is loaded.
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 npx tablewalk --app ./my-appThree setups, each only environment variables
Section titled “Three setups, each only environment variables”OpenTelemetry Collector (or anything that takes OTLP/HTTP):
export OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318export OTEL_SERVICE_NAME=loansexport OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=productionDatadog, through the Datadog Agent’s OTLP intake (enable
otlp_config.receiver.protocols.http on the Agent):
export OTEL_EXPORTER_OTLP_ENDPOINT=http://datadog-agent:4318export OTEL_SERVICE_NAME=loansexport OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=production,service.version=1.4.0Dynatrace, straight to the environment’s OTLP API with an access token
(scopes openTelemetryTrace.ingest, metrics.ingest, logs.ingest):
export OTEL_EXPORTER_OTLP_ENDPOINT=https://{your-environment-id}.live.dynatrace.com/api/v2/otlpexport OTEL_EXPORTER_OTLP_HEADERS="Authorization=Api-Token%20${DT_API_TOKEN}"export OTEL_SERVICE_NAME=loansA host running OneAgent can take the same export at its local OTLP endpoint instead. dd-trace and OneAgent auto-instrumentation keep working beside it: tablewalk’s server is not bundled, so the modules they hook are the ones it runs.
Also read: OTEL_EXPORTER_OTLP_PROTOCOL (http/protobuf, the default, or
http/json; gRPC is not spoken, so point at the HTTP port), the per-signal
OTEL_EXPORTER_OTLP_{TRACES,LOGS,METRICS}_ENDPOINT and _HEADERS,
OTEL_{TRACES,LOGS,METRICS}_EXPORTER=none, OTEL_TRACES_SAMPLER and its
_ARG, OTEL_EXPORTER_OTLP_COMPRESSION=gzip, OTEL_METRIC_EXPORT_INTERVAL and
OTEL_SDK_DISABLED=true.
What is sent
Section titled “What is sent”| Signal | What |
|---|---|
| Traces | One per request, GET /api/query, named by route template; a span per statement (SELECT loan: its kind and table), command (id, version, outcome, refusal code), job attempt, event delivery, outbound call and email; the gate’s decision as an event. W3C traceparent is read on requests and MCP calls (_meta.traceparent) and sent on webhooks and HTTP sources. |
| Metrics | http.server.request.duration, db.client.operation.duration, tablewalk.command.executions and .duration, tablewalk.job.attempts, tablewalk.outbound.calls. |
| Logs | Each refusal, with the trace and span it happened in. |
Nothing private is in any of them: no request or response body, row value, SQL text or parameter, email, token or tenant data. A test runs a command round trip seeded with secrets into a local OTLP receiver and fails if one arrives.
GET /healthz answers while the process runs; GET /readyz answers 503 while
it starts, while a row policy restarts after a save, and from the moment it
begins to shut down.
The dev observer
Section titled “The dev observer”npx tablewalk --app ./my-app --devhttp://localhost:4111/_tablewalk/observe/ (under the --base-path, when the
server has one) shows every request as a
waterfall: its SQL with parameters, rows, transaction and EXPLAIN; N+1 and
slow statements; outbound calls with their bodies; commands with input,
output and the refusal’s cause; the log lines written inside each span; all
logs, live, and what went straight to stderr; the page’s errors, layout
shifts and web vitals (LCP, CLS, INP); and why a role can or cannot read a
column. A refusal on the page carries an Open in observer link; a request
the browser gave up on is shown as aborted, not as an error. A tenant App
serves it the same way.
It runs on a loopback listener only, keeps the last few hundred requests in
memory, and masks credentials and hidden columns. A start without --dev
never loads it, and the address answers 404.
npx tablewalk observe prints its address; npx tablewalk observe --mcp
gives a coding agent the same as tools, and check, which answers what
tablewalk check --json prints and needs no running server: recent_errors, list_traces,
get_trace, search_logs, slow_queries, n_plus_one, explain_statement,
explain_access, page_vitals and tail.