Skip to content
tarsier-devPublic

About

Static scanner for observability gaps

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

tarsier

Static scanner for observability gaps in application code: high-cardinality metric labels, unstructured logs, and Kafka produce/consume without trace context. Runs on a checkout. Does not need your telemetry backend.

Install

Prerequisites: Go 1.27+ and ast-grep 0.45.x on PATH.

# macOS
brew install ast-grep
# or: npm i -g @ast-grep/cli@0.45.1

go install github.com/Pieczasz/tarsier/cmd/tarsier@v0.2.0

Or from source:

git clone https://github.com/Pieczasz/tarsier.git
cd tarsier
make build
./bin/tarsier scan fixtures/_badshop

Quick start

tarsier scan path/to/repo
tarsier --output json scan path/to/repo
tarsier --output html scan path/to/repo > tarsier-report.html
tarsier scan --engine=all path/to/go/module   # pattern + Go deep tier

--engine pattern|go|all (default pattern). Medium-confidence findings never trip --fail-on.

CI gate without a policy file:

tarsier scan --fail-on warning .

Policy gate (opt-in blocking classes only - TAR-47):

# observability-policy.yaml
# version: 1
# block:
#   - unbounded-metric-labels
tarsier check --policy observability-policy.yaml .

Exit codes for check: 0 when no opted-in blocking findings; 1 when a blocked class matches (or flags/policy are invalid). Layer 3/4 rules cannot be listed under block (rejected at parse time). --baseline is honoured.

Adopt on an existing repo without failing the first build:

tarsier scan . --baseline-write tarsier-baseline.json
tarsier scan . --baseline tarsier-baseline.json --fail-on warning

Quiet one finding in source (reason required):

// tarsier:ignore metrics/high-cardinality-label tenant label is bounded here

Example workflow: examples/github-actions/scan.yml.

What it detects

Rule Languages Notes
metrics/high-cardinality-label Go, TS, Java, Rust, Python Literal unbounded label names at declaration
metrics/unbounded-label-value Go, TS, Java, Python err.Error() / uuid-shaped values at the call site
logs/unstructured-logging Go, TS log.Printf/console.* where slog/zap/pino/winston is imported
msgtrace/kafka-produce-no-inject Go, TS, Java Headerless produce (medium on TS/Java when inject is invisible)
msgtrace/kafka-consume-no-extract Go, Java Read without .Headers/Extract
httpctx/outbound-call-without-context Go http.Get/Post/Head/PostForm
otel/sdk-missing-resource-attrs Go, TS Provider/SDK without service.name
otel/sdk-missing-shutdown Go, TS Provider without Shutdown/forceFlush (medium)
otel/semconv-drift Go, TS Deprecated HTTP attribute names (medium; TAR-77 precision gate)
errors/swallowed-on-critical-path Go, Java _ = err / empty catch (medium)
traces/error-path-not-recorded-on-span Go return err after .Start( without RecordError/SetStatus (medium)
logs/missing-trace-correlation Go, TS slog/pino alongside OTel without a bridge (medium)
logs/slog-sprintf-message Go slog.*(fmt.Sprintf(...)) - prefer structured attrs (medium; TAR-77)

Precision process for high-confidence rules: docs/PRECISION.md.

Demo fixture

fixtures/_badshop is a deliberately broken multi-language app. Every interesting line is marked want: / notwant: / gap:.

make build
./bin/tarsier scan fixtures/_badshop
./bin/tarsier --output html scan fixtures/_badshop > /tmp/tarsier-badshop.html
./bin/tarsier scan --fail-on warning fixtures/_badshop; echo exit:$?

See DEMO.md for a five-minute walkthrough.

License

Apache-2.0. See LICENSE.

Library imports

Shared packages live at the module root (not under internal/) so product code can require github.com/Pieczasz/tarsier and import e.g. github.com/Pieczasz/tarsier/finding, .../engine/pattern, .../rules.

About

Static scanner for observability gaps

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages