allstak 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +167 -0
- data/README.md +110 -233
- data/allstak.gemspec +10 -9
- data/lib/allstak/client.rb +66 -3
- data/lib/allstak/config.rb +259 -3
- data/lib/allstak/global_handler.rb +100 -0
- data/lib/allstak/integrations/active_record.rb +18 -0
- data/lib/allstak/integrations/logger.rb +201 -0
- data/lib/allstak/integrations/net_http.rb +27 -1
- data/lib/allstak/integrations/rack.rb +71 -10
- data/lib/allstak/integrations/rails.rb +59 -0
- data/lib/allstak/integrations/sidekiq.rb +184 -0
- data/lib/allstak/modules/database.rb +4 -1
- data/lib/allstak/modules/errors.rb +164 -22
- data/lib/allstak/modules/http_monitor.rb +7 -2
- data/lib/allstak/modules/logs.rb +28 -3
- data/lib/allstak/modules/tracing.rb +33 -2
- data/lib/allstak/propagation.rb +48 -0
- data/lib/allstak/sampling.rb +38 -0
- data/lib/allstak/sanitizer.rb +322 -0
- data/lib/allstak/session_tracker.rb +216 -0
- data/lib/allstak/transport/event_spool.rb +227 -0
- data/lib/allstak/transport/http_transport.rb +168 -5
- data/lib/allstak/version.rb +1 -1
- data/lib/allstak.rb +90 -1
- metadata +24 -29
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea16fc5869d09516a29301382b4d59d9e3f33018226516beb208aa1c826a77f3
|
|
4
|
+
data.tar.gz: 6b73ffb1e1319585c2be4c7398181cf9beae32a4ea17914501df6ae1a5df2a66
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1a21a2da36cf561691221369b9cf25fdfc4063300fc3f32452652d6f0987044b9121d719a597db9d6b18d617583e0253e8ee59f08539c8f2ed3c1efe5ffd543a
|
|
7
|
+
data.tar.gz: 43e2c1d26dd502e77fe498e4850352235a63be28fa33ac5c2d61a4ee7171cafda55bc11285d781de96664cdb962becf4df387c83573ccdfa495de3e80fe05695
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,173 @@
|
|
|
3
3
|
All notable changes to the AllStak Ruby SDK.
|
|
4
4
|
This project follows [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## Unreleased
|
|
7
|
+
|
|
8
|
+
## [0.3.0] — 2026-05-30
|
|
9
|
+
|
|
10
|
+
### Added — Automatic breadcrumbs across all instrumentation
|
|
11
|
+
- Breadcrumbs are now collected automatically from every auto-instrumentation
|
|
12
|
+
layer, not just Sidekiq. The inbound Rack middleware (request method/path/
|
|
13
|
+
status/duration), the outbound `Net::HTTP` patch (method/host/path/status/
|
|
14
|
+
duration), and the ActiveRecord subscriber (truncated SQL/duration/status)
|
|
15
|
+
each emit a breadcrumb, and every `AllStak.log.*` call is bridged into a
|
|
16
|
+
`log` breadcrumb. The trail is attached to the next captured exception with
|
|
17
|
+
no per-call developer code.
|
|
18
|
+
- The breadcrumb ring buffer is now **per-thread** (50 entries), so one
|
|
19
|
+
request/job's trail never leaks into a concurrent request's captured
|
|
20
|
+
exception. Breadcrumb collection is drained on capture as before.
|
|
21
|
+
- New `config.enable_auto_breadcrumbs` (default `true`; env
|
|
22
|
+
`ALLSTAK_AUTO_BREADCRUMBS=0/false` to disable) gates only the automatic
|
|
23
|
+
layers. New top-level `AllStak.add_breadcrumb(...)` records a manual
|
|
24
|
+
breadcrumb and always works regardless of the toggle.
|
|
25
|
+
|
|
26
|
+
### Added — Optional structured-log adapter
|
|
27
|
+
- `AllStak::Integrations::Logger` — a `::Logger`-compatible sink that forwards
|
|
28
|
+
records to `/ingest/v1/logs` via `AllStak.log`. Compose it alongside your
|
|
29
|
+
existing logger (`AllStak::Integrations::Logger.attach_to_rails!` on Rails
|
|
30
|
+
7.1+ BroadcastLogger, or `AllStak::Integrations::Logger.broadcast(logger)`)
|
|
31
|
+
so existing log destinations are preserved and app logs ship automatically.
|
|
32
|
+
- ERROR PROMOTION: records at/above `ERROR` (configurable via
|
|
33
|
+
`error_promotion_level:`) are additionally captured as message error-group
|
|
34
|
+
entries so they surface in the Errors list. Opt out with
|
|
35
|
+
`error_promotion: false`. Opt-in by design and fully fail-open — the adapter
|
|
36
|
+
never raises into the host's logging path and is a no-op until the SDK is
|
|
37
|
+
configured. Defined on require but never auto-attached, so default logging
|
|
38
|
+
behavior is unchanged.
|
|
39
|
+
|
|
40
|
+
## [0.2.0] — 2026-05-29
|
|
41
|
+
|
|
42
|
+
### Added — Release-health session tracking
|
|
43
|
+
- `AllStak::SessionTracker` — server-mode single-session release-health
|
|
44
|
+
lifecycle that mirrors the AllStak SDKs. On boot it POSTs
|
|
45
|
+
`/ingest/v1/sessions/start` (off the hot path, on a daemon thread) with a
|
|
46
|
+
per-process `sessionId`, the resolved release, environment, and SDK identity;
|
|
47
|
+
on shutdown it POSTs `/ingest/v1/sessions/end` (synchronous, best-effort)
|
|
48
|
+
with `durationMs` + final status. Status model `ok | errored | crashed |
|
|
49
|
+
abnormal`: handled errors transition `ok -> errored`, an unhandled/fatal
|
|
50
|
+
exception sets `crashed` (terminal). Status transitions are in-memory only —
|
|
51
|
+
only `start`/`end` perform network I/O, so per-error latency is unaffected,
|
|
52
|
+
enabling crash-free session/user rates on the backend.
|
|
53
|
+
- Idempotent and re-entrancy safe (double `start`/`end` are no-ops). Sessions
|
|
54
|
+
are never sampled. Fully fail-open: a network error or read-only runtime must
|
|
55
|
+
never crash app boot or shutdown.
|
|
56
|
+
- Opt-out via `config.enable_auto_session_tracking = false`. Auto-disables under
|
|
57
|
+
a unit-test runtime so the suite never emits session traffic. The active
|
|
58
|
+
`sessionId` is attached to error/event payloads (and is allowlisted in the
|
|
59
|
+
sanitizer so it survives PII scrubbing).
|
|
60
|
+
|
|
61
|
+
### Added — Offline / persistent transport queue
|
|
62
|
+
- `AllStak::Transport::EventSpool` — a filesystem spool for un-sent telemetry
|
|
63
|
+
envelopes so events survive a process restart, network outage, or shutdown.
|
|
64
|
+
One JSON file per envelope (atomic temp-write + rename) so a partially written
|
|
65
|
+
file is discarded without losing the rest of the queue. Persisted bytes are
|
|
66
|
+
**already PII-scrubbed** by the caller before they hit disk.
|
|
67
|
+
- Bounded by COUNT (default 100), BYTES (default 4 MiB), and AGE (default 48h),
|
|
68
|
+
evicting OLDEST-first via an embedded millis+seq ordering. Stale entries past
|
|
69
|
+
max-age are dropped on drain. On the next SDK init the queue is replayed and
|
|
70
|
+
delivered entries are removed; unreadable/oversized entries are dropped.
|
|
71
|
+
- Default ON for server runtimes (`config.enable_offline_queue`), spool dir
|
|
72
|
+
`<tmpdir>/allstak-spool` (override via `config.offline_queue_dir`), with
|
|
73
|
+
configurable `offline_queue_max_entries` / `_max_bytes` / `_max_age_s`. Env
|
|
74
|
+
overrides: `ALLSTAK_OFFLINE_QUEUE`, `ALLSTAK_OFFLINE_QUEUE_DIR`. Fully
|
|
75
|
+
fail-open: a read-only / sandboxed / serverless FS degrades silently to
|
|
76
|
+
in-memory behavior and never blocks capture or init.
|
|
77
|
+
|
|
78
|
+
### Added — Value-pattern PII scrubbing + sendDefaultPii
|
|
79
|
+
- `AllStak::Sanitizer` gains a second scrubbing layer alongside the existing
|
|
80
|
+
key-name denylist: VALUE-PATTERN redaction that scans free-text string values
|
|
81
|
+
for PII regardless of key name (AllStak data-scrubbing). Tier A is
|
|
82
|
+
ALWAYS scrubbed — Luhn-validated credit-card numbers (13–19 digits, optional
|
|
83
|
+
space/hyphen separators) and hyphenated US SSNs. Tier B — email addresses and
|
|
84
|
+
IPv4/IPv6 — is scrubbed UNLESS `send_default_pii` is enabled.
|
|
85
|
+
- `config.send_default_pii` (default `false`, ; env
|
|
86
|
+
`ALLSTAK_SEND_DEFAULT_PII`) opts into shipping email/IP values. The
|
|
87
|
+
always-on financial/identity scrubbers are not affected by this flag.
|
|
88
|
+
- Structural exemptions prevent over-redaction: the explicit `user` object and
|
|
89
|
+
`stackTrace`/`frames` subtrees are key-name-redacted but never value-scrubbed,
|
|
90
|
+
and structured scalar keys (release, sdk/version, url/path/host, span/trace
|
|
91
|
+
ids, `sessionId`, timestamp) are skipped. Strings over 16 KiB are passed
|
|
92
|
+
through. Value scrubbing is fail-open: any error falls back to the
|
|
93
|
+
key-redacted (non-value-scrubbed) structure.
|
|
94
|
+
|
|
95
|
+
### Added — Global capture, sampling, and release auto-detection
|
|
96
|
+
- `AllStak::GlobalHandler` — global `at_exit` hook captures uncaught exceptions
|
|
97
|
+
(mechanism `at_exit`), feeding session crash status, before re-raising.
|
|
98
|
+
- `before_send` callback + client-side event/trace sampling hooks.
|
|
99
|
+
- Runtime release auto-detection from local git (`AllStak.register_runtime_release`
|
|
100
|
+
/ `config.detect_release`) so events are attributed to a release without
|
|
101
|
+
manual configuration; auto-disabled under a test runtime.
|
|
102
|
+
- Transport hardening: every wire payload is sanitized at the single
|
|
103
|
+
`HttpTransport#post` chokepoint, the `sdk.version` wire value is corrected,
|
|
104
|
+
and `Retry-After` is honored on 429/503.
|
|
105
|
+
|
|
106
|
+
### Added — Sidekiq integration
|
|
107
|
+
- `AllStak::Integrations::Sidekiq::Middleware` — Sidekiq **server** middleware
|
|
108
|
+
that starts a fresh trace per job, wraps execution in a `queue.process`
|
|
109
|
+
span + breadcrumb, and auto-captures job failures with worker class, jid,
|
|
110
|
+
queue, and (PII-sanitized via `Sanitizer`) args, re-raising so Sidekiq's
|
|
111
|
+
retry machinery still runs.
|
|
112
|
+
- `death_handler` registration captures jobs that exhaust their retries
|
|
113
|
+
(`mechanism=sidekiq.death`).
|
|
114
|
+
- Auto-registered by `AllStak.configure` via `Sidekiq.configure_server` when
|
|
115
|
+
Sidekiq is present; a graceful, idempotent no-op otherwise.
|
|
116
|
+
|
|
117
|
+
### Added — Rails Railtie
|
|
118
|
+
- `AllStak::Integrations::Rails::Railtie` — a real `Rails::Railtie` that
|
|
119
|
+
auto-inserts the Rack middleware into the app's middleware stack via an
|
|
120
|
+
initializer. Rails apps are now instrumented **without manual wiring**,
|
|
121
|
+
matching the prior README claim. Guarded (only loads when `Rails::Railtie`
|
|
122
|
+
is defined) and idempotent.
|
|
123
|
+
|
|
124
|
+
### Docs
|
|
125
|
+
- README + gemspec description corrected to reflect real auto-install behavior
|
|
126
|
+
(Railtie + Sidekiq server middleware) instead of the previously overstated
|
|
127
|
+
"Rails middleware" claim. Fixed stale `AllStak::Rack::Middleware` and
|
|
128
|
+
`AllStak.start_span` references.
|
|
129
|
+
|
|
130
|
+
### Tests / tooling
|
|
131
|
+
- New minitest coverage for the Sidekiq middleware (capture + context + span +
|
|
132
|
+
no-op without Sidekiq), the Railtie (middleware insertion + guarded without
|
|
133
|
+
Rails), and backfill for `Client`, `HttpTransport`, and `Modules::Errors`.
|
|
134
|
+
- Removed unused `rspec` / `webmock` dev-dependencies (the suite is minitest
|
|
135
|
+
only; transport tests stub `Net::HTTP` directly). CI workflow simplified
|
|
136
|
+
accordingly.
|
|
137
|
+
- Added minitest coverage for the new waves: `test_session_tracker`,
|
|
138
|
+
`test_session_error_wiring`, `test_event_spool`, `test_offline_queue`,
|
|
139
|
+
`test_send_default_pii`, plus value-pattern cases in `test_sanitizer` and
|
|
140
|
+
`test_global_handler` / `test_release_autodetect` / `test_retry_after`.
|
|
141
|
+
Full suite: 177 runs, 448 assertions, 0 failures.
|
|
142
|
+
|
|
143
|
+
### Repo hygiene
|
|
144
|
+
- Untracked stray build/vendor artifacts that the `.gitignore` already covers
|
|
145
|
+
but were committed before the ignore rules existed: the root `*.gem` packs
|
|
146
|
+
(`allstak-0.1.0.gem`, `allstak-0.1.1.gem`) and the committed
|
|
147
|
+
`vendor/bundle/` dependency cache. These are not part of the published gem
|
|
148
|
+
(`spec.files` ships only `lib/**/*.rb`, README, CHANGELOG, LICENSE, gemspec).
|
|
149
|
+
|
|
150
|
+
## 0.1.2 — 2026-05-18
|
|
151
|
+
|
|
152
|
+
### Added — Recursive payload sanitizer
|
|
153
|
+
- New `AllStak::Sanitizer` module with `scrub(payload, extra_denylist: nil)`.
|
|
154
|
+
25-term canonical denylist, recursive over `Hash` / `Array`, `[REDACTED]`
|
|
155
|
+
substitution, `object_id` Set cycle protection, pure (no caller mutation).
|
|
156
|
+
- Wired into `AllStak::Transport::HttpTransport#post` — every wire payload
|
|
157
|
+
is scrubbed before serialization. One chokepoint protects errors, logs,
|
|
158
|
+
http, db, traces.
|
|
159
|
+
- Fail-open: if the sanitizer raises on a pathological payload, the SDK
|
|
160
|
+
logs at Warning level and sends raw — telemetry is never blocked.
|
|
161
|
+
|
|
162
|
+
### Live canary E2E
|
|
163
|
+
- Verified end-to-end against the AllStak ingest API at `api.allstak.sa`.
|
|
164
|
+
- Ingested event confirmed `leak_pos = 0` across `metadata`, `stack_trace`,
|
|
165
|
+
`breadcrumbs`, `message`. Canary `should_not_leak_ruby` planted in
|
|
166
|
+
`password`, `authorization`, `cookie`, `Bearer`, `api_key`, request
|
|
167
|
+
headers / body, `credit_card`, `ssn`, and a 3-level-nested `token`.
|
|
168
|
+
All scrubbed.
|
|
169
|
+
|
|
170
|
+
### Tests
|
|
171
|
+
- `test/test_sanitizer.rb` — 12 runs, 51 assertions, 0 failures.
|
|
172
|
+
|
|
6
173
|
## 0.1.0 — 2026-04-11
|
|
7
174
|
|
|
8
175
|
First public release. Driven end-to-end through a real Sinatra + ActiveRecord
|
data/README.md
CHANGED
|
@@ -1,291 +1,168 @@
|
|
|
1
|
-
#
|
|
1
|
+
# allstak
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
structured logs, HTTP + ActiveRecord monitoring, distributed tracing, and cron
|
|
5
|
-
monitoring for Rack-based Ruby applications (Rails, Sinatra, Roda, Hanami).
|
|
3
|
+
AllStak SDK for Ruby, Rails, Rack, and Sidekiq. Captures exceptions, logs, inbound and outbound HTTP requests, ActiveRecord queries, Sidekiq job failures, spans, and cron heartbeats. Rails apps are auto-instrumented via a Railtie; Sidekiq via a server middleware.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
gem "allstak"
|
|
9
|
-
```
|
|
5
|
+
## Install
|
|
10
6
|
|
|
11
7
|
```bash
|
|
12
|
-
bundle install
|
|
13
|
-
# or
|
|
14
8
|
gem install allstak
|
|
15
9
|
```
|
|
16
10
|
|
|
17
|
-
|
|
11
|
+
Or add it to your Gemfile:
|
|
18
12
|
|
|
19
13
|
```ruby
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
AllStak.configure do |c|
|
|
23
|
-
c.api_key = ENV["ALLSTAK_API_KEY"]
|
|
24
|
-
c.environment = "production"
|
|
25
|
-
c.release = "myapp@1.2.3"
|
|
26
|
-
c.service_name = "myapp-api"
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Rack / Sinatra / Rails:
|
|
30
|
-
use AllStak::Integrations::Rack::Middleware
|
|
31
|
-
|
|
32
|
-
# Manual capture:
|
|
33
|
-
begin
|
|
34
|
-
risky!
|
|
35
|
-
rescue => e
|
|
36
|
-
AllStak.capture_exception(e)
|
|
37
|
-
end
|
|
14
|
+
gem "allstak"
|
|
38
15
|
```
|
|
39
16
|
|
|
40
|
-
|
|
41
|
-
ActiveRecord query, every outbound Net::HTTP call, and every trace are
|
|
42
|
-
captured automatically.
|
|
43
|
-
|
|
44
|
-
## Public API (cross-SDK consistent)
|
|
45
|
-
|
|
46
|
-
Every method below is a module-level method on `AllStak`, matching the
|
|
47
|
-
names used by the JS, Python, Java, Go, PHP, and .NET SDKs so docs carry
|
|
48
|
-
across languages.
|
|
17
|
+
## Setup
|
|
49
18
|
|
|
50
19
|
```ruby
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
AllStak.set_user(id: "42", email: "alice@example.com") # user context
|
|
54
|
-
AllStak.clear_user
|
|
55
|
-
|
|
56
|
-
AllStak.set_tag("service", "checkout") # sticky tag
|
|
57
|
-
AllStak.set_tags(region: "us-east-1", tier: "web") # bulk
|
|
58
|
-
AllStak.set_context("deployment", "canary") # sticky context
|
|
59
|
-
|
|
60
|
-
AllStak.capture_exception(exc) # preferred for errors
|
|
61
|
-
AllStak.capture_error("DomainError", "bad input") # without a throwable
|
|
62
|
-
AllStak.capture_message("hello", level: "info") # plain string event
|
|
20
|
+
require "allstak"
|
|
63
21
|
|
|
64
|
-
AllStak.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
22
|
+
AllStak.configure do |config|
|
|
23
|
+
config.api_key = ENV["ALLSTAK_API_KEY"]
|
|
24
|
+
config.environment = ENV.fetch("APP_ENV", "production")
|
|
25
|
+
config.release = ENV["ALLSTAK_RELEASE"]
|
|
26
|
+
config.service_name = "checkout-api"
|
|
27
|
+
end
|
|
69
28
|
|
|
70
|
-
AllStak.
|
|
71
|
-
AllStak.
|
|
29
|
+
AllStak.capture_exception(StandardError.new("checkout failed"))
|
|
30
|
+
AllStak.log.info("payment retry", metadata: { order_id: "ord_123" })
|
|
72
31
|
```
|
|
73
32
|
|
|
74
|
-
`AllStak.capture_message`, `AllStak.set_tag`, and `AllStak.set_context`
|
|
75
|
-
landed in 0.1.1 as cross-SDK parity additions. Older 0.1.0 code that only
|
|
76
|
-
used `capture_exception` / `capture_error` keeps working — no breaking
|
|
77
|
-
changes.
|
|
78
|
-
|
|
79
33
|
## Rails
|
|
80
34
|
|
|
35
|
+
For Rails apps there is **no manual wiring**. Requiring the gem loads a
|
|
36
|
+
`Railtie` that auto-inserts the AllStak Rack middleware into your application's
|
|
37
|
+
middleware stack during boot. Just configure the SDK during app initialization
|
|
38
|
+
(e.g. in `config/initializers/allstak.rb`):
|
|
39
|
+
|
|
81
40
|
```ruby
|
|
82
|
-
# config/initializers/allstak.rb
|
|
83
41
|
require "allstak"
|
|
84
42
|
|
|
85
|
-
AllStak.configure do |
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
c.service_name = "myapp-api"
|
|
43
|
+
AllStak.configure do |config|
|
|
44
|
+
config.api_key = ENV["ALLSTAK_API_KEY"]
|
|
45
|
+
config.environment = Rails.env
|
|
46
|
+
config.service_name = "checkout-api"
|
|
90
47
|
end
|
|
91
|
-
|
|
92
|
-
Rails.application.config.middleware.use AllStak::Integrations::Rack::Middleware
|
|
93
48
|
```
|
|
94
49
|
|
|
95
|
-
|
|
50
|
+
You then get inbound request telemetry, trace propagation, unhandled-exception
|
|
51
|
+
capture, ActiveRecord query instrumentation, and outbound `Net::HTTP` capture
|
|
52
|
+
automatically. The middleware insertion is idempotent.
|
|
96
53
|
|
|
97
|
-
|
|
98
|
-
require "sinatra/base"
|
|
99
|
-
require "allstak"
|
|
54
|
+
## Rack (non-Rails)
|
|
100
55
|
|
|
101
|
-
|
|
56
|
+
For plain Rack apps (Sinatra, Roda, etc.), add the middleware yourself:
|
|
102
57
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
# ...
|
|
106
|
-
end
|
|
58
|
+
```ruby
|
|
59
|
+
use AllStak::Integrations::Rack::Middleware
|
|
107
60
|
```
|
|
108
61
|
|
|
109
|
-
##
|
|
110
|
-
|
|
111
|
-
| What | How |
|
|
112
|
-
| ------------------------------------- | ------------------------------------------ |
|
|
113
|
-
| Unhandled exceptions | Rack middleware |
|
|
114
|
-
| Inbound HTTP requests | Rack middleware |
|
|
115
|
-
| Per-request trace ID | Rack middleware |
|
|
116
|
-
| User context (from env/session) | Rack middleware |
|
|
117
|
-
| ActiveRecord SQL queries | `sql.active_record` subscriber |
|
|
118
|
-
| Outbound HTTP via `Net::HTTP` | `Net::HTTP#request` patched |
|
|
62
|
+
## Sidekiq
|
|
119
63
|
|
|
120
|
-
|
|
121
|
-
|
|
64
|
+
When Sidekiq is present, `AllStak.configure` registers a Sidekiq **server
|
|
65
|
+
middleware** automatically — no manual setup. It wraps each job in a span and
|
|
66
|
+
breadcrumb, and captures job failures with worker class, jid, queue, and
|
|
67
|
+
(PII-sanitized) args as context, then re-raises so Sidekiq's retry machinery
|
|
68
|
+
still runs. Jobs that exhaust their retries are also captured via a death
|
|
69
|
+
handler. When Sidekiq is not installed, this is a graceful no-op.
|
|
122
70
|
|
|
123
|
-
##
|
|
124
|
-
|
|
125
|
-
`AllStak.configure` installs an `ActiveSupport::Notifications` subscriber on
|
|
126
|
-
`sql.active_record`, which gives you normalized SQL, duration, row counts,
|
|
127
|
-
status, and error messages for every query — whether it's from an Active Record
|
|
128
|
-
relation, a `find_by_sql`, or a raw `connection.execute`. No duplication,
|
|
129
|
-
because every AR query fires exactly one `sql.active_record` event.
|
|
71
|
+
## Spans
|
|
130
72
|
|
|
131
73
|
```ruby
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
74
|
+
AllStak.tracing.in_span("checkout.authorize") do
|
|
75
|
+
authorize_payment
|
|
76
|
+
end
|
|
135
77
|
```
|
|
136
78
|
|
|
137
|
-
##
|
|
79
|
+
## Breadcrumbs
|
|
138
80
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
```
|
|
81
|
+
Breadcrumbs are collected **automatically** — no per-call code. After
|
|
82
|
+
`AllStak.configure`, the SDK records a trail of recent events on a 50-entry
|
|
83
|
+
**per-thread** ring buffer and attaches it to the next captured exception on
|
|
84
|
+
that thread. Auto breadcrumbs come from:
|
|
144
85
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
86
|
+
- inbound Rack requests (method, path, status, duration),
|
|
87
|
+
- outbound `Net::HTTP` calls (method, host, path, status, duration),
|
|
88
|
+
- ActiveRecord queries (truncated SQL, duration, status),
|
|
89
|
+
- Sidekiq job processing,
|
|
90
|
+
- and every `AllStak.log.*` call.
|
|
149
91
|
|
|
150
|
-
|
|
92
|
+
Because the buffer is per-thread, one request's trail never bleeds into a
|
|
93
|
+
concurrent request's exception. You can also add your own:
|
|
151
94
|
|
|
152
95
|
```ruby
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
# Logs (buffered, flushed in background)
|
|
158
|
-
AllStak.log.info("Order placed", metadata: { id: "ORD-1" })
|
|
159
|
-
AllStak.log.warn("Slow query", metadata: { ms: 4500 })
|
|
160
|
-
AllStak.log.error("Payment failed", metadata: { gateway: "stripe" })
|
|
161
|
-
# valid levels: debug | info | warn | error | fatal
|
|
162
|
-
|
|
163
|
-
# Distributed tracing (block-form)
|
|
164
|
-
AllStak.tracing.in_span("db.query", description: "SELECT users") do |span|
|
|
165
|
-
span.set_tag("db.type", "postgresql")
|
|
166
|
-
rows = User.all.to_a
|
|
167
|
-
end
|
|
96
|
+
AllStak.add_breadcrumb(type: "auth", message: "password reset requested",
|
|
97
|
+
data: { "userId" => current_user.id })
|
|
98
|
+
```
|
|
168
99
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
generate_report
|
|
172
|
-
end
|
|
100
|
+
Disable automatic collection (manual `add_breadcrumb` still works) with
|
|
101
|
+
`enable_auto_breadcrumbs = false` or `ALLSTAK_AUTO_BREADCRUMBS=0`.
|
|
173
102
|
|
|
174
|
-
|
|
175
|
-
AllStak.set_user(id: "u-1", email: "alice@example.com")
|
|
176
|
-
AllStak.clear_user
|
|
103
|
+
## Structured logs (optional)
|
|
177
104
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
```
|
|
105
|
+
Ship your application logs to AllStak by composing the optional log adapter
|
|
106
|
+
with your existing logger — existing log destinations are preserved:
|
|
181
107
|
|
|
182
|
-
|
|
108
|
+
```ruby
|
|
109
|
+
# Rails 7.1+ (broadcast alongside the current logger):
|
|
110
|
+
AllStak::Integrations::Logger.attach_to_rails!
|
|
111
|
+
|
|
112
|
+
# Any Ruby logger:
|
|
113
|
+
logger = AllStak::Integrations::Logger.broadcast(logger)
|
|
114
|
+
```
|
|
183
115
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
| ActiveRecord queries (auto) | **Database** |
|
|
191
|
-
| `AllStak.tracing.in_span` | **Traces** |
|
|
192
|
-
| `AllStak.cron.job` / `cron.ping` | **Cron Jobs** |
|
|
116
|
+
Once attached, `logger.info / .warn / .error` ship to `/ingest/v1/logs`
|
|
117
|
+
automatically. Records at `ERROR`/`FATAL` are additionally promoted to error
|
|
118
|
+
entries so they surface in the Errors list. Disable promotion with
|
|
119
|
+
`AllStak::Integrations::Logger.new(error_promotion: false)`, or change the
|
|
120
|
+
threshold with `error_promotion_level:`. The adapter is opt-in and fail-open —
|
|
121
|
+
it never raises into your logging path.
|
|
193
122
|
|
|
194
123
|
## Configuration
|
|
195
124
|
|
|
196
|
-
| Option
|
|
197
|
-
|
|
|
198
|
-
| `api_key`
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
202
|
-
| `
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
| `capture_user_context` | `true` | Attach user claims to errors. |
|
|
212
|
-
| `capture_sql` | `true` | Auto-capture AR queries. |
|
|
213
|
-
|
|
214
|
-
Environment variables: `ALLSTAK_API_KEY`, `ALLSTAK_HOST`, `ALLSTAK_ENVIRONMENT`,
|
|
215
|
-
`ALLSTAK_RELEASE`, `ALLSTAK_SERVICE`, `ALLSTAK_DEBUG`.
|
|
216
|
-
|
|
217
|
-
## Production notes
|
|
218
|
-
|
|
219
|
-
- **Never crashes your app.** Every integration catches its own exceptions
|
|
220
|
-
and logs at debug level. The middleware re-raises so your framework's
|
|
221
|
-
exception handler still runs.
|
|
222
|
-
- **Retries.** 5xx and network errors retry with exponential backoff
|
|
223
|
-
(1s → 2s → 4s → 8s, +jitter, max 5 attempts). 4xx are not retried.
|
|
224
|
-
- **401 disables the SDK.** An invalid API key disables the SDK for the
|
|
225
|
-
rest of the process — no further events are sent, a warning is logged
|
|
226
|
-
once, and your app keeps running.
|
|
227
|
-
- **Flush on shutdown.** `at_exit` triggers a best-effort flush.
|
|
228
|
-
- **Thread-safe.** All public APIs are safe to call from any thread.
|
|
229
|
-
Trace context uses Ruby's thread-local storage.
|
|
230
|
-
- **Non-blocking.** Telemetry is buffered and flushed on background threads.
|
|
231
|
-
Your request pipeline is never blocked by SDK work.
|
|
125
|
+
| Option | Description |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `api_key` | Project API key. |
|
|
128
|
+
| `environment` | Deployment environment. |
|
|
129
|
+
| `release` | App version or commit SHA. |
|
|
130
|
+
| `service_name` | Logical service name. |
|
|
131
|
+
| `flush_interval_ms` | Background flush interval. |
|
|
132
|
+
| `buffer_size` | Max buffered events. |
|
|
133
|
+
| `enable_auto_breadcrumbs` | Collect breadcrumbs automatically from the Rack/Net::HTTP/ActiveRecord/Sidekiq instrumentation and the `AllStak.log.*` bridge (default `true`; env `ALLSTAK_AUTO_BREADCRUMBS=0` to disable). Manual `AllStak.add_breadcrumb` is unaffected. |
|
|
134
|
+
| `install_at_exit_handler` | Install a process-wide `at_exit` hook that captures the exception terminating the process as an unhandled event (default `true`). |
|
|
135
|
+
| `before_send` | Callable invoked with the event hash just before transport. Return a modified hash, or `nil` to drop the event. Fails open (sends the original) if it raises. |
|
|
136
|
+
| `sample_rate` | Float in `[0.0, 1.0]` head-sampling rate for error/message events (default `1.0` = keep all). |
|
|
137
|
+
| `traces_sample_rate` | Float in `[0.0, 1.0]` span sampling rate. `nil` (default) keeps every span and the `traceparent` sampled flag; when set, span creation is sampled and the propagated `traceparent` flag reflects the decision. |
|
|
138
|
+
|
|
139
|
+
For top-level uncaught exceptions outside Rack (workers, threads, rake tasks), the `at_exit` handler catches genuine unhandled terminations automatically. You can also report manually at a boundary:
|
|
232
140
|
|
|
233
|
-
|
|
141
|
+
```ruby
|
|
142
|
+
begin
|
|
143
|
+
run_worker
|
|
144
|
+
rescue => e
|
|
145
|
+
AllStak.capture_unhandled(e) # mechanism=at_exit, handled=false
|
|
146
|
+
raise
|
|
147
|
+
end
|
|
148
|
+
```
|
|
234
149
|
|
|
235
|
-
|
|
236
|
-
| ------------------------------------ | ------------------------------------------------ |
|
|
237
|
-
| No events in dashboard | Check `host` and `api_key`. Set `debug = true`. |
|
|
238
|
-
| 401 warning | Invalid API key. Create a new one in Settings. |
|
|
239
|
-
| Inbound requests missing | Make sure `use AllStak::Integrations::Rack::Middleware`. |
|
|
240
|
-
| DB queries missing | Make sure `AllStak.configure` runs BEFORE your first AR query. |
|
|
241
|
-
| Outbound HTTP missing | Same — `configure` must run before the first `Net::HTTP` call. |
|
|
242
|
-
| Cron monitor not appearing | Auto-created on first ping; check the slug matches. |
|
|
150
|
+
## Privacy
|
|
243
151
|
|
|
244
|
-
|
|
152
|
+
The SDK redacts common sensitive headers and fields. Avoid putting secrets in custom metadata.
|
|
245
153
|
|
|
246
|
-
|
|
247
|
-
require "sinatra/base"
|
|
248
|
-
require "active_record"
|
|
249
|
-
require "allstak"
|
|
154
|
+
## Troubleshooting
|
|
250
155
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
c.service_name = "taskflow-api"
|
|
256
|
-
end
|
|
156
|
+
- No events: confirm `ALLSTAK_API_KEY` is available before configuration.
|
|
157
|
+
- Missing request telemetry (Rails): confirm `require "allstak"` runs at boot so the Railtie can auto-insert the middleware.
|
|
158
|
+
- Missing request telemetry (plain Rack): confirm `use AllStak::Integrations::Rack::Middleware` is in your Rack stack.
|
|
159
|
+
- Short-lived job: call `AllStak.flush` before exit when possible.
|
|
257
160
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
use AllStak::Integrations::Rack::Middleware
|
|
264
|
-
|
|
265
|
-
get "/tasks" do
|
|
266
|
-
Task.all.to_json
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
post "/tasks/:id/notify" do
|
|
270
|
-
task = Task.find(params[:id])
|
|
271
|
-
AllStak.tracing.in_span("http.notify", description: "POST httpbin.org/post") do |span|
|
|
272
|
-
span.set_tag("task.id", task.id.to_s)
|
|
273
|
-
uri = URI("https://httpbin.org/post")
|
|
274
|
-
Net::HTTP.post(uri, { task_id: task.id }.to_json, "Content-Type" => "application/json")
|
|
275
|
-
end
|
|
276
|
-
{ ok: true }.to_json
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
error do
|
|
280
|
-
# Framework-level rescue. Sinatra handles the exception before Rack middleware
|
|
281
|
-
# sees it, so forward manually:
|
|
282
|
-
e = env["sinatra.error"]
|
|
283
|
-
AllStak.capture_exception(e) if e
|
|
284
|
-
status 500
|
|
285
|
-
{ error: e.class.name, message: e.message }.to_json
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
```
|
|
161
|
+
## Contributing and Support
|
|
162
|
+
|
|
163
|
+
- Report bugs with the GitHub bug report template: https://github.com/AllStak/allstak-ruby/issues/new/choose
|
|
164
|
+
- Open pull requests using the checklist in [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
165
|
+
- Report security vulnerabilities privately through [SECURITY.md](SECURITY.md).
|
|
289
166
|
|
|
290
167
|
## License
|
|
291
168
|
|
data/allstak.gemspec
CHANGED
|
@@ -4,19 +4,19 @@ Gem::Specification.new do |spec|
|
|
|
4
4
|
spec.name = "allstak"
|
|
5
5
|
spec.version = AllStak::VERSION
|
|
6
6
|
spec.authors = ["AllStak"]
|
|
7
|
-
spec.email = ["sdk@allstak.
|
|
7
|
+
spec.email = ["sdk@allstak.sa"]
|
|
8
8
|
|
|
9
9
|
spec.summary = "Official AllStak Ruby SDK — error tracking, logs, HTTP + ActiveRecord monitoring, tracing, and cron monitoring"
|
|
10
|
-
spec.description = "Production-ready Ruby SDK for AllStak observability:
|
|
11
|
-
spec.homepage = "https://allstak.
|
|
10
|
+
spec.description = "Production-ready Ruby SDK for AllStak observability: auto-installing Rails Railtie and Rack middleware, a Sidekiq server middleware, ActiveRecord instrumentation, outbound HTTP capture, distributed tracing, cron monitoring, and structured logs."
|
|
11
|
+
spec.homepage = "https://allstak.sa"
|
|
12
12
|
spec.license = "MIT"
|
|
13
13
|
spec.required_ruby_version = ">= 3.0.0"
|
|
14
14
|
|
|
15
15
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
16
|
-
spec.metadata["source_code_uri"] = "https://github.com/
|
|
17
|
-
spec.metadata["changelog_uri"] = "https://github.com/
|
|
18
|
-
spec.metadata["bug_tracker_uri"] = "https://github.com/
|
|
19
|
-
spec.metadata["documentation_uri"] = "https://allstak.
|
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/AllStak/allstak-ruby"
|
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/AllStak/allstak-ruby/blob/main/CHANGELOG.md"
|
|
18
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/AllStak/allstak-ruby/issues"
|
|
19
|
+
spec.metadata["documentation_uri"] = "https://allstak.sa/docs/sdks/ruby"
|
|
20
20
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
21
21
|
|
|
22
22
|
spec.files = Dir[
|
|
@@ -33,8 +33,9 @@ Gem::Specification.new do |spec|
|
|
|
33
33
|
# Framework integrations (Rack, Rails, ActiveRecord, Net::HTTP) are loaded
|
|
34
34
|
# lazily and only activate when the host app has them available.
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
# Tests use minitest only (Ruby's bundled stdlib test framework); the
|
|
37
|
+
# transport tests stub Net::HTTP directly, so no rspec/webmock is needed.
|
|
38
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
38
39
|
spec.add_development_dependency "rack", "~> 3.0"
|
|
39
40
|
spec.add_development_dependency "activerecord", "~> 8.0"
|
|
40
41
|
end
|