allstak 0.1.1
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 +7 -0
- data/CHANGELOG.md +71 -0
- data/LICENSE +21 -0
- data/README.md +292 -0
- data/allstak.gemspec +40 -0
- data/lib/allstak/client.rb +98 -0
- data/lib/allstak/config.rb +36 -0
- data/lib/allstak/integrations/active_record.rb +78 -0
- data/lib/allstak/integrations/net_http.rb +87 -0
- data/lib/allstak/integrations/rack.rb +136 -0
- data/lib/allstak/models/user_context.rb +43 -0
- data/lib/allstak/modules/cron.rb +54 -0
- data/lib/allstak/modules/database.rb +89 -0
- data/lib/allstak/modules/errors.rb +111 -0
- data/lib/allstak/modules/http_monitor.rb +79 -0
- data/lib/allstak/modules/logs.rb +79 -0
- data/lib/allstak/modules/tracing.rb +170 -0
- data/lib/allstak/transport/flush_buffer.rb +91 -0
- data/lib/allstak/transport/http_transport.rb +97 -0
- data/lib/allstak/version.rb +3 -0
- data/lib/allstak.rb +151 -0
- metadata +128 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
require "securerandom"
|
|
2
|
+
|
|
3
|
+
module AllStak
|
|
4
|
+
module Modules
|
|
5
|
+
# Distributed tracing — spans with parent-child hierarchy via Thread-local state.
|
|
6
|
+
class Tracing
|
|
7
|
+
PATH = "/ingest/v1/spans".freeze
|
|
8
|
+
VALID_STATUSES = %w[ok error timeout].freeze
|
|
9
|
+
|
|
10
|
+
def initialize(transport, config, logger)
|
|
11
|
+
@transport = transport
|
|
12
|
+
@config = config
|
|
13
|
+
@logger = logger
|
|
14
|
+
@buffer = Transport::FlushBuffer.new(
|
|
15
|
+
name: "tracing",
|
|
16
|
+
max_size: config.buffer_size,
|
|
17
|
+
interval_ms: config.flush_interval_ms,
|
|
18
|
+
flush_proc: method(:flush_batch),
|
|
19
|
+
logger: logger
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def current_trace_id
|
|
24
|
+
Thread.current[:allstak_trace_id] ||= SecureRandom.hex(16)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def set_trace_id(trace_id)
|
|
28
|
+
Thread.current[:allstak_trace_id] = trace_id
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def current_span_id
|
|
32
|
+
stack = Thread.current[:allstak_span_stack]
|
|
33
|
+
stack&.last
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reset_trace
|
|
37
|
+
Thread.current[:allstak_trace_id] = nil
|
|
38
|
+
Thread.current[:allstak_span_stack] = nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def start_span(operation, description: "", tags: nil)
|
|
42
|
+
trace_id = current_trace_id
|
|
43
|
+
span_id = SecureRandom.hex(8)
|
|
44
|
+
parent = current_span_id || ""
|
|
45
|
+
Thread.current[:allstak_span_stack] ||= []
|
|
46
|
+
Thread.current[:allstak_span_stack] << span_id
|
|
47
|
+
|
|
48
|
+
Span.new(
|
|
49
|
+
trace_id: trace_id,
|
|
50
|
+
span_id: span_id,
|
|
51
|
+
parent_span_id: parent,
|
|
52
|
+
operation: operation,
|
|
53
|
+
description: description,
|
|
54
|
+
service: @config.service_name,
|
|
55
|
+
environment: @config.environment || "",
|
|
56
|
+
release: (@config.respond_to?(:release) ? @config.release : nil) || "",
|
|
57
|
+
tags: tags || {},
|
|
58
|
+
start_time_millis: (Time.now.to_f * 1000).to_i,
|
|
59
|
+
on_finish: method(:on_span_finish)
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Block-form helper: automatically finishes the span on return,
|
|
64
|
+
# on raise, or on non-local flow (e.g. Sinatra's `throw :halt`).
|
|
65
|
+
def in_span(operation, description: "", tags: nil)
|
|
66
|
+
span = start_span(operation, description: description, tags: tags)
|
|
67
|
+
status = "ok"
|
|
68
|
+
begin
|
|
69
|
+
return yield(span)
|
|
70
|
+
rescue => e
|
|
71
|
+
status = "error"
|
|
72
|
+
raise
|
|
73
|
+
ensure
|
|
74
|
+
span.finish(status) unless span.finished?
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def flush
|
|
79
|
+
@buffer.flush
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def shutdown
|
|
83
|
+
@buffer.shutdown
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def on_span_finish(span)
|
|
89
|
+
stack = Thread.current[:allstak_span_stack]
|
|
90
|
+
stack&.delete(span.span_id)
|
|
91
|
+
@buffer.push(span.to_h)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def flush_batch(items)
|
|
95
|
+
begin
|
|
96
|
+
@transport.post(PATH, { spans: items })
|
|
97
|
+
rescue Transport::AllStakAuthError
|
|
98
|
+
return
|
|
99
|
+
rescue Transport::AllStakTransportError => e
|
|
100
|
+
@logger.debug("[AllStak] span transport error: #{e.message}")
|
|
101
|
+
rescue => e
|
|
102
|
+
@logger.debug("[AllStak] span unexpected error: #{e.message}")
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class Span
|
|
108
|
+
attr_reader :trace_id, :span_id
|
|
109
|
+
|
|
110
|
+
def initialize(trace_id:, span_id:, parent_span_id:, operation:, description:,
|
|
111
|
+
service:, environment:, tags:, start_time_millis:, on_finish:, release: "")
|
|
112
|
+
@trace_id = trace_id
|
|
113
|
+
@span_id = span_id
|
|
114
|
+
@parent_span_id = parent_span_id
|
|
115
|
+
@operation = operation
|
|
116
|
+
@description = description
|
|
117
|
+
@service = service
|
|
118
|
+
@environment = environment
|
|
119
|
+
@release = release
|
|
120
|
+
@tags = tags.dup
|
|
121
|
+
@start_time_millis = start_time_millis
|
|
122
|
+
@end_time_millis = nil
|
|
123
|
+
@status = "ok"
|
|
124
|
+
@finished = false
|
|
125
|
+
@on_finish = on_finish
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def set_tag(key, value)
|
|
129
|
+
@tags[key.to_s] = value.to_s
|
|
130
|
+
self
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def set_description(description)
|
|
134
|
+
@description = description
|
|
135
|
+
self
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def finished?
|
|
139
|
+
@finished
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def finish(status = "ok")
|
|
143
|
+
return if @finished
|
|
144
|
+
@finished = true
|
|
145
|
+
@status = Tracing::VALID_STATUSES.include?(status) ? status : "ok"
|
|
146
|
+
@end_time_millis = (Time.now.to_f * 1000).to_i
|
|
147
|
+
@on_finish.call(self)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def to_h
|
|
151
|
+
end_ms = @end_time_millis || (Time.now.to_f * 1000).to_i
|
|
152
|
+
{
|
|
153
|
+
traceId: @trace_id,
|
|
154
|
+
spanId: @span_id,
|
|
155
|
+
parentSpanId: @parent_span_id,
|
|
156
|
+
operation: @operation,
|
|
157
|
+
description: @description,
|
|
158
|
+
status: @status,
|
|
159
|
+
durationMs: end_ms - @start_time_millis,
|
|
160
|
+
startTimeMillis: @start_time_millis,
|
|
161
|
+
endTimeMillis: end_ms,
|
|
162
|
+
service: @service,
|
|
163
|
+
environment: @environment,
|
|
164
|
+
release: @release,
|
|
165
|
+
tags: @tags
|
|
166
|
+
}
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require "monitor"
|
|
2
|
+
|
|
3
|
+
module AllStak
|
|
4
|
+
module Transport
|
|
5
|
+
# Bounded ring buffer with a background flush thread.
|
|
6
|
+
#
|
|
7
|
+
# * Max size: `maxsize` (default 500)
|
|
8
|
+
# * Eviction: oldest item dropped when full
|
|
9
|
+
# * Flush triggers: interval timer, >= 80% capacity, explicit flush, shutdown
|
|
10
|
+
# * Single-flight: only one flush runs at a time
|
|
11
|
+
class FlushBuffer
|
|
12
|
+
include MonitorMixin
|
|
13
|
+
|
|
14
|
+
def initialize(name:, max_size:, interval_ms:, flush_proc:, logger:)
|
|
15
|
+
super()
|
|
16
|
+
@name = name
|
|
17
|
+
@max_size = max_size
|
|
18
|
+
@interval = interval_ms / 1000.0
|
|
19
|
+
@flush_proc = flush_proc
|
|
20
|
+
@logger = logger
|
|
21
|
+
@queue = []
|
|
22
|
+
@stopped = false
|
|
23
|
+
@overflow_warned = false
|
|
24
|
+
@flushing_mutex = Mutex.new
|
|
25
|
+
start_timer
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def push(item)
|
|
29
|
+
synchronize do
|
|
30
|
+
if @queue.length >= @max_size
|
|
31
|
+
@queue.shift
|
|
32
|
+
unless @overflow_warned
|
|
33
|
+
@overflow_warned = true
|
|
34
|
+
@logger.warn("[AllStak] Buffer #{@name} full (#{@max_size}); oldest events dropped")
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
@overflow_warned = false
|
|
38
|
+
end
|
|
39
|
+
@queue << item
|
|
40
|
+
end
|
|
41
|
+
flush if count >= (@max_size * 0.8)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def count
|
|
45
|
+
synchronize { @queue.length }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def flush
|
|
49
|
+
@flushing_mutex.synchronize do
|
|
50
|
+
drained = synchronize do
|
|
51
|
+
next [] if @queue.empty?
|
|
52
|
+
current = @queue
|
|
53
|
+
@queue = []
|
|
54
|
+
current
|
|
55
|
+
end
|
|
56
|
+
return if drained.empty?
|
|
57
|
+
begin
|
|
58
|
+
@flush_proc.call(drained)
|
|
59
|
+
rescue => e
|
|
60
|
+
@logger.debug("[AllStak] flush error in #{@name}: #{e.class}: #{e.message}")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def shutdown
|
|
66
|
+
@stopped = true
|
|
67
|
+
@timer_thread&.wakeup rescue nil
|
|
68
|
+
@timer_thread&.join(2)
|
|
69
|
+
flush
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def start_timer
|
|
75
|
+
@timer_thread = Thread.new do
|
|
76
|
+
Thread.current.name = "allstak-flush-#{@name}" if Thread.current.respond_to?(:name=)
|
|
77
|
+
until @stopped
|
|
78
|
+
sleep @interval
|
|
79
|
+
break if @stopped
|
|
80
|
+
begin
|
|
81
|
+
flush
|
|
82
|
+
rescue => e
|
|
83
|
+
@logger.debug("[AllStak] timer flush error: #{e.message}")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
@timer_thread.abort_on_exception = false
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
require "uri"
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module AllStak
|
|
6
|
+
module Transport
|
|
7
|
+
class AllStakAuthError < StandardError; end
|
|
8
|
+
class AllStakTransportError < StandardError; end
|
|
9
|
+
|
|
10
|
+
# HTTP transport with retry/backoff and 401-disable.
|
|
11
|
+
#
|
|
12
|
+
# Contract:
|
|
13
|
+
# connect timeout = 3s · read timeout = 3s
|
|
14
|
+
# backoff = 1s → 2s → 4s → 8s (+ jitter 0-500ms)
|
|
15
|
+
# max attempts = 5
|
|
16
|
+
# 401 → disable SDK
|
|
17
|
+
# 4xx (400/403/404/422) → no retry
|
|
18
|
+
# 5xx / network → retry
|
|
19
|
+
class HttpTransport
|
|
20
|
+
NON_RETRYABLE_STATUSES = [400, 401, 403, 404, 422].freeze
|
|
21
|
+
BACKOFF_DELAYS = [1.0, 2.0, 4.0, 8.0].freeze
|
|
22
|
+
|
|
23
|
+
attr_reader :disabled
|
|
24
|
+
|
|
25
|
+
def initialize(config, logger)
|
|
26
|
+
@config = config
|
|
27
|
+
@logger = logger
|
|
28
|
+
@base_url = config.host
|
|
29
|
+
@api_key = config.api_key
|
|
30
|
+
@disabled = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def disabled?
|
|
34
|
+
@disabled
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def post(path, payload)
|
|
38
|
+
raise AllStakAuthError, "SDK disabled" if @disabled
|
|
39
|
+
|
|
40
|
+
uri = URI.parse("#{@base_url}#{path}")
|
|
41
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
42
|
+
http.use_ssl = (uri.scheme == "https")
|
|
43
|
+
http.open_timeout = @config.connect_timeout
|
|
44
|
+
http.read_timeout = @config.read_timeout
|
|
45
|
+
|
|
46
|
+
last_exc = nil
|
|
47
|
+
last_status = 0
|
|
48
|
+
max_attempts = [[@config.max_retries.to_i, 1].max, 5].min
|
|
49
|
+
|
|
50
|
+
(1..max_attempts).each do |attempt|
|
|
51
|
+
begin
|
|
52
|
+
req = Net::HTTP::Post.new(uri.request_uri, {
|
|
53
|
+
"Content-Type" => "application/json",
|
|
54
|
+
"X-AllStak-Key" => @api_key,
|
|
55
|
+
"User-Agent" => "allstak-ruby/#{AllStak::VERSION}"
|
|
56
|
+
})
|
|
57
|
+
req.body = payload.is_a?(String) ? payload : JSON.generate(payload)
|
|
58
|
+
@logger.debug("[AllStak] POST #{path} attempt=#{attempt}") if @config.debug
|
|
59
|
+
|
|
60
|
+
resp = http.request(req)
|
|
61
|
+
last_status = resp.code.to_i
|
|
62
|
+
body = resp.body.to_s
|
|
63
|
+
|
|
64
|
+
if last_status == 401
|
|
65
|
+
@disabled = true
|
|
66
|
+
@logger.warn("[AllStak] SDK disabled: invalid API key (401). No further events will be sent.")
|
|
67
|
+
raise AllStakAuthError, "Invalid API key"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
return [last_status, body] if NON_RETRYABLE_STATUSES.include?(last_status)
|
|
71
|
+
return [last_status, body] if last_status < 400
|
|
72
|
+
|
|
73
|
+
# 5xx → retry
|
|
74
|
+
rescue AllStakAuthError
|
|
75
|
+
raise
|
|
76
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, Errno::ECONNRESET,
|
|
77
|
+
SocketError, EOFError, IOError => e
|
|
78
|
+
last_exc = e
|
|
79
|
+
@logger.debug("[AllStak] transport error attempt=#{attempt}: #{e.class}: #{e.message}") if @config.debug
|
|
80
|
+
rescue => e
|
|
81
|
+
last_exc = e
|
|
82
|
+
@logger.debug("[AllStak] unexpected transport error attempt=#{attempt}: #{e.class}: #{e.message}") if @config.debug
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if attempt < max_attempts
|
|
86
|
+
delay = BACKOFF_DELAYS[[attempt - 1, BACKOFF_DELAYS.length - 1].min]
|
|
87
|
+
delay += rand * 0.5
|
|
88
|
+
sleep(delay)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
raise AllStakTransportError,
|
|
93
|
+
"All #{max_attempts} attempts failed for POST #{path}. last_status=#{last_status} last_error=#{last_exc&.message}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/allstak.rb
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
require "logger"
|
|
2
|
+
require "time"
|
|
3
|
+
|
|
4
|
+
require_relative "allstak/version"
|
|
5
|
+
require_relative "allstak/config"
|
|
6
|
+
require_relative "allstak/transport/http_transport"
|
|
7
|
+
require_relative "allstak/transport/flush_buffer"
|
|
8
|
+
require_relative "allstak/models/user_context"
|
|
9
|
+
require_relative "allstak/modules/errors"
|
|
10
|
+
require_relative "allstak/modules/logs"
|
|
11
|
+
require_relative "allstak/modules/http_monitor"
|
|
12
|
+
require_relative "allstak/modules/tracing"
|
|
13
|
+
require_relative "allstak/modules/database"
|
|
14
|
+
require_relative "allstak/modules/cron"
|
|
15
|
+
require_relative "allstak/client"
|
|
16
|
+
require_relative "allstak/integrations/rack"
|
|
17
|
+
require_relative "allstak/integrations/active_record"
|
|
18
|
+
require_relative "allstak/integrations/net_http"
|
|
19
|
+
|
|
20
|
+
# Official AllStak Ruby SDK.
|
|
21
|
+
#
|
|
22
|
+
# Quick start:
|
|
23
|
+
#
|
|
24
|
+
# require "allstak"
|
|
25
|
+
#
|
|
26
|
+
# AllStak.configure do |c|
|
|
27
|
+
# c.api_key = ENV["ALLSTAK_API_KEY"]
|
|
28
|
+
# c.environment = "production"
|
|
29
|
+
# c.release = "myapp@1.2.3"
|
|
30
|
+
# c.service_name = "myapp-api"
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# # Rack / Rails: add the middleware
|
|
34
|
+
# use AllStak::Integrations::Rack::Middleware
|
|
35
|
+
#
|
|
36
|
+
# # Manual:
|
|
37
|
+
# AllStak.capture_exception(exc)
|
|
38
|
+
# AllStak.log.info("hello", metadata: { foo: "bar" })
|
|
39
|
+
# AllStak.cron.job("daily-report") { generate_report }
|
|
40
|
+
module AllStak
|
|
41
|
+
@mutex = Mutex.new
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
attr_reader :logger
|
|
45
|
+
|
|
46
|
+
def configure
|
|
47
|
+
@mutex.synchronize do
|
|
48
|
+
@config ||= Config.new
|
|
49
|
+
yield @config if block_given?
|
|
50
|
+
@logger = Logger.new($stderr).tap do |l|
|
|
51
|
+
l.level = @config.debug ? Logger::DEBUG : Logger::WARN
|
|
52
|
+
l.progname = "allstak"
|
|
53
|
+
end
|
|
54
|
+
if @config.valid?
|
|
55
|
+
@client = Client.new(@config, @logger)
|
|
56
|
+
# Auto-wire integrations that are safe to install
|
|
57
|
+
AllStak::Integrations::ActiveRecordIntegration::Subscriber.install!
|
|
58
|
+
AllStak::Integrations::NetHTTP.install!
|
|
59
|
+
else
|
|
60
|
+
@logger.warn("[AllStak] api_key not set — SDK not started")
|
|
61
|
+
@client = nil
|
|
62
|
+
end
|
|
63
|
+
@client
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def initialized?
|
|
68
|
+
!@client.nil?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def client
|
|
72
|
+
@client or raise "AllStak not configured. Call AllStak.configure { |c| ... } first."
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def capture_exception(exc, **kw)
|
|
76
|
+
@client&.capture_exception(exc, **kw)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def capture_error(exception_class, message, **kw)
|
|
80
|
+
@client&.capture_error(exception_class, message, **kw)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Cross-SDK parity with JS captureMessage / Python capture_message /
|
|
84
|
+
# Java captureMessage. Emits a string as an error-group entry at the
|
|
85
|
+
# given level. Safe no-op if the SDK is not configured.
|
|
86
|
+
def capture_message(message, level: "info", **kw)
|
|
87
|
+
@client&.capture_message(message, level: level, **kw)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def set_user(**kw)
|
|
91
|
+
@client&.set_user(**kw)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def clear_user
|
|
95
|
+
@client&.clear_user
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Attach a tag that sticks to every future event.
|
|
99
|
+
# Cross-SDK parity with JS setTag / Python set_tag.
|
|
100
|
+
def set_tag(key, value)
|
|
101
|
+
@client&.set_tag(key, value)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def set_tags(pairs)
|
|
105
|
+
@client&.set_tags(pairs)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Attach a custom context entry to every future event.
|
|
109
|
+
# Cross-SDK parity with JS/Python setContext.
|
|
110
|
+
def set_context(key, value)
|
|
111
|
+
@client&.set_context(key, value)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def log
|
|
115
|
+
@client&.logs
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def tracing
|
|
119
|
+
@client&.tracing
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def http
|
|
123
|
+
@client&.http
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def database
|
|
127
|
+
@client&.database
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def cron
|
|
131
|
+
@client&.cron
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def flush
|
|
135
|
+
@client&.flush
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def shutdown
|
|
139
|
+
@client&.shutdown
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Test helper.
|
|
143
|
+
def reset!
|
|
144
|
+
@mutex.synchronize do
|
|
145
|
+
@client&.shutdown rescue nil
|
|
146
|
+
@client = nil
|
|
147
|
+
@config = nil
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: allstak
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- AllStak
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-04-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rspec
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.12'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.12'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: webmock
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.19'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.19'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rack
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '3.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: activerecord
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '8.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '8.0'
|
|
69
|
+
description: 'Production-ready Ruby SDK for AllStak observability: Rack/Rails middleware,
|
|
70
|
+
ActiveRecord instrumentation, outbound HTTP capture, distributed tracing, cron monitoring,
|
|
71
|
+
and structured logs.'
|
|
72
|
+
email:
|
|
73
|
+
- sdk@allstak.dev
|
|
74
|
+
executables: []
|
|
75
|
+
extensions: []
|
|
76
|
+
extra_rdoc_files: []
|
|
77
|
+
files:
|
|
78
|
+
- CHANGELOG.md
|
|
79
|
+
- LICENSE
|
|
80
|
+
- README.md
|
|
81
|
+
- allstak.gemspec
|
|
82
|
+
- lib/allstak.rb
|
|
83
|
+
- lib/allstak/client.rb
|
|
84
|
+
- lib/allstak/config.rb
|
|
85
|
+
- lib/allstak/integrations/active_record.rb
|
|
86
|
+
- lib/allstak/integrations/net_http.rb
|
|
87
|
+
- lib/allstak/integrations/rack.rb
|
|
88
|
+
- lib/allstak/models/user_context.rb
|
|
89
|
+
- lib/allstak/modules/cron.rb
|
|
90
|
+
- lib/allstak/modules/database.rb
|
|
91
|
+
- lib/allstak/modules/errors.rb
|
|
92
|
+
- lib/allstak/modules/http_monitor.rb
|
|
93
|
+
- lib/allstak/modules/logs.rb
|
|
94
|
+
- lib/allstak/modules/tracing.rb
|
|
95
|
+
- lib/allstak/transport/flush_buffer.rb
|
|
96
|
+
- lib/allstak/transport/http_transport.rb
|
|
97
|
+
- lib/allstak/version.rb
|
|
98
|
+
homepage: https://allstak.dev
|
|
99
|
+
licenses:
|
|
100
|
+
- MIT
|
|
101
|
+
metadata:
|
|
102
|
+
homepage_uri: https://allstak.dev
|
|
103
|
+
source_code_uri: https://github.com/allstak-io/allstak-ruby
|
|
104
|
+
changelog_uri: https://github.com/allstak-io/allstak-ruby/blob/main/CHANGELOG.md
|
|
105
|
+
bug_tracker_uri: https://github.com/allstak-io/allstak-ruby/issues
|
|
106
|
+
documentation_uri: https://allstak.dev/docs/sdks/ruby
|
|
107
|
+
rubygems_mfa_required: 'true'
|
|
108
|
+
post_install_message:
|
|
109
|
+
rdoc_options: []
|
|
110
|
+
require_paths:
|
|
111
|
+
- lib
|
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: 3.0.0
|
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
|
+
requirements:
|
|
119
|
+
- - ">="
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '0'
|
|
122
|
+
requirements: []
|
|
123
|
+
rubygems_version: 3.4.19
|
|
124
|
+
signing_key:
|
|
125
|
+
specification_version: 4
|
|
126
|
+
summary: Official AllStak Ruby SDK — error tracking, logs, HTTP + ActiveRecord monitoring,
|
|
127
|
+
tracing, and cron monitoring
|
|
128
|
+
test_files: []
|