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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 943cc01f8f95ed1cabef20b1c88b50e462b7be871c0a962a46c225e85902e355
4
- data.tar.gz: f61664c5d936089b368e22c3c71d32f58f05081a35b478814a5b4f041b799133
3
+ metadata.gz: ea16fc5869d09516a29301382b4d59d9e3f33018226516beb208aa1c826a77f3
4
+ data.tar.gz: 6b73ffb1e1319585c2be4c7398181cf9beae32a4ea17914501df6ae1a5df2a66
5
5
  SHA512:
6
- metadata.gz: 7262178e8689bed7b675cc0fd60c494978ce73f826eac9566fc063054d33043c8475350e0780acadfd81c23a626c30f3ab24e1b5ca2e3191b83898b81e473f82
7
- data.tar.gz: e41f14091f0e5abde8930f421394a36bdaf12e617d5376c4126f86cb700c16de740f6fdb632bd60ddc0f3dd15ba655e6c75d532b43b3bfe297b8f04ff03bfc43
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
- # AllStak Ruby SDK
1
+ # allstak
2
2
 
3
- Official Ruby SDK for [AllStak](https://allstak.dev) error tracking,
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
- ```ruby
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
- ## 60-second setup
11
+ Or add it to your Gemfile:
18
12
 
19
13
  ```ruby
20
- require "allstak"
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
- That is the whole setup. Every request, every unhandled exception, every
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
- AllStak.configure { |c| ... } # once at bootstrap
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.log.info("request started", metadata: {...}) # structured logs
65
- AllStak.tracing # Tracing module
66
- AllStak.http # HTTP monitor
67
- AllStak.database # DB query monitor
68
- AllStak.cron.job("daily-report") { run_job } # cron heartbeats
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.flush # drain buffers
71
- AllStak.shutdown # drain + close
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 |c|
86
- c.api_key = ENV["ALLSTAK_API_KEY"]
87
- c.environment = Rails.env
88
- c.release = "myapp@#{ENV['GIT_SHA'] || 'dev'}"
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
- ## Sinatra
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
- ```ruby
98
- require "sinatra/base"
99
- require "allstak"
54
+ ## Rack (non-Rails)
100
55
 
101
- AllStak.configure { |c| c.api_key = ENV["ALLSTAK_API_KEY"] }
56
+ For plain Rack apps (Sinatra, Roda, etc.), add the middleware yourself:
102
57
 
103
- class MyApp < Sinatra::Base
104
- use AllStak::Integrations::Rack::Middleware
105
- # ...
106
- end
58
+ ```ruby
59
+ use AllStak::Integrations::Rack::Middleware
107
60
  ```
108
61
 
109
- ## What gets captured automatically
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
- The ActiveRecord subscriber and Net::HTTP patch are installed automatically
121
- by `AllStak.configure` — no extra setup needed.
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
- ## ActiveRecord
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
- # Nothing to do — this just works:
133
- User.where(email: "alice@example.com").first
134
- # → captured as: SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?
74
+ AllStak.tracing.in_span("checkout.authorize") do
75
+ authorize_payment
76
+ end
135
77
  ```
136
78
 
137
- ## Outbound HTTP (Net::HTTP)
79
+ ## Breadcrumbs
138
80
 
139
- ```ruby
140
- # Also nothing to do:
141
- Net::HTTP.get(URI("https://api.example.com/v1/data"))
142
- # captured as outbound HTTP telemetry with method, host, path, status, duration
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
- The SDK patches `Net::HTTP#request` at `configure` time. Since every
146
- convenience method (`get`, `post`, `post_form`, etc.) funnels through
147
- `#request`, there is no duplication. Calls to your AllStak ingest host are
148
- skipped to avoid recursive instrumentation.
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
- ## Manual capture cheat sheet
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
- # Errors
154
- AllStak.capture_exception(exc, metadata: { order_id: "ORD-123" })
155
- AllStak.capture_error("StripeTimeout", "Stripe /v1/charges timed out after 30s", level: "error")
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
- # Cron monitoring slug auto-created on first ping
170
- AllStak.cron.job("daily-report") do
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
- # User context (for events that should show who was affected)
175
- AllStak.set_user(id: "u-1", email: "alice@example.com")
176
- AllStak.clear_user
103
+ ## Structured logs (optional)
177
104
 
178
- # Graceful flush
179
- AllStak.flush
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
- ## Dashboard mapping
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
- | Your code | Dashboard page |
185
- | ----------------------------------------- | --------------------- |
186
- | `AllStak.capture_exception` / middleware | **Errors**, **Incidents** |
187
- | `AllStak.log.*` | **Logs** |
188
- | Rack middleware (inbound) | **Requests** |
189
- | `Net::HTTP` (outbound, auto) | **Requests** (outbound) |
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 | Default | Notes |
197
- | ------------------------- | ------------------------- | ----- |
198
- | `api_key` | `ENV["ALLSTAK_API_KEY"]` | Your `ask_live_...` key. |
199
- | `host` | `https://api.allstak.sa` | Override with your AllStak ingest host. |
200
- | `environment` | `nil` | e.g. `"production"` |
201
- | `release` | `nil` | e.g. `"myapp@1.2.3"` |
202
- | `service_name` | `"ruby-service"` | Shown on spans and logs. |
203
- | `flush_interval_ms` | `2000` | Background flush interval. |
204
- | `buffer_size` | `500` | Max buffered items per feature. |
205
- | `debug` | `false` | Verbose SDK logging. |
206
- | `connect_timeout` | `3` | Transport connect timeout (seconds). |
207
- | `read_timeout` | `3` | Transport read timeout (seconds). |
208
- | `max_retries` | `5` | Retry 5xx with exponential backoff. |
209
- | `capture_unhandled_exceptions` | `true` | Auto-capture from middleware. |
210
- | `capture_http_requests` | `true` | Auto-capture inbound HTTP. |
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
- ## Troubleshooting
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
- | Symptom | Fix |
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
- ## Full Sinatra + ActiveRecord example
152
+ The SDK redacts common sensitive headers and fields. Avoid putting secrets in custom metadata.
245
153
 
246
- ```ruby
247
- require "sinatra/base"
248
- require "active_record"
249
- require "allstak"
154
+ ## Troubleshooting
250
155
 
251
- AllStak.configure do |c|
252
- c.api_key = ENV["ALLSTAK_API_KEY"]
253
- c.environment = "production"
254
- c.release = "taskflow@1.4.2"
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
- ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: "app.db")
259
-
260
- class Task < ActiveRecord::Base; end
261
-
262
- class TaskFlow < Sinatra::Base
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.dev"]
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: Rack/Rails middleware, ActiveRecord instrumentation, outbound HTTP capture, distributed tracing, cron monitoring, and structured logs."
11
- spec.homepage = "https://allstak.dev"
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/allstak-io/allstak-ruby"
17
- spec.metadata["changelog_uri"] = "https://github.com/allstak-io/allstak-ruby/blob/main/CHANGELOG.md"
18
- spec.metadata["bug_tracker_uri"] = "https://github.com/allstak-io/allstak-ruby/issues"
19
- spec.metadata["documentation_uri"] = "https://allstak.dev/docs/sdks/ruby"
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
- spec.add_development_dependency "rspec", "~> 3.12"
37
- spec.add_development_dependency "webmock", "~> 3.19"
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