justanalytics 0.1.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 +7 -0
- data/README.md +103 -0
- data/lib/justanalytics/client.rb +374 -0
- data/lib/justanalytics/configuration.rb +71 -0
- data/lib/justanalytics/context.rb +122 -0
- data/lib/justanalytics/integrations/net_http.rb +125 -0
- data/lib/justanalytics/integrations/rails.rb +124 -0
- data/lib/justanalytics/integrations/sidekiq.rb +98 -0
- data/lib/justanalytics/logger.rb +150 -0
- data/lib/justanalytics/span.rb +170 -0
- data/lib/justanalytics/trace_context.rb +85 -0
- data/lib/justanalytics/transport.rb +204 -0
- data/lib/justanalytics/version.rb +6 -0
- data/lib/justanalytics.rb +208 -0
- metadata +117 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module JustAnalytics
|
|
6
|
+
# W3C Trace Context traceparent header parsing and serialization.
|
|
7
|
+
#
|
|
8
|
+
# Format: "00-{traceId}-{spanId}-{flags}"
|
|
9
|
+
# - version: "00" (only supported version)
|
|
10
|
+
# - traceId: 32-char lowercase hex (128-bit, must not be all zeros)
|
|
11
|
+
# - spanId: 16-char lowercase hex (64-bit, must not be all zeros)
|
|
12
|
+
# - flags: 2-char hex ("01" = sampled, "00" = not sampled)
|
|
13
|
+
#
|
|
14
|
+
# @see https://www.w3.org/TR/trace-context/
|
|
15
|
+
module TraceContext
|
|
16
|
+
TRACE_ID_REGEX = /\A[0-9a-f]{32}\z/
|
|
17
|
+
SPAN_ID_REGEX = /\A[0-9a-f]{16}\z/
|
|
18
|
+
FLAGS_REGEX = /\A[0-9a-f]{2}\z/
|
|
19
|
+
ZERO_TRACE_ID = "0" * 32
|
|
20
|
+
ZERO_SPAN_ID = "0" * 16
|
|
21
|
+
|
|
22
|
+
# Parsed traceparent data.
|
|
23
|
+
TraceparentData = Struct.new(:version, :trace_id, :parent_span_id, :trace_flags, keyword_init: true)
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
# Generate a 32-character lowercase hex trace ID (128-bit).
|
|
27
|
+
#
|
|
28
|
+
# @return [String] 32-character hex string
|
|
29
|
+
def generate_trace_id
|
|
30
|
+
SecureRandom.hex(16)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Generate a 16-character lowercase hex span ID (64-bit).
|
|
34
|
+
#
|
|
35
|
+
# @return [String] 16-character hex string
|
|
36
|
+
def generate_span_id
|
|
37
|
+
SecureRandom.hex(8)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Parse a W3C traceparent header string.
|
|
41
|
+
#
|
|
42
|
+
# @param header [String] the traceparent header value
|
|
43
|
+
# @return [TraceparentData, nil] parsed data or nil if invalid
|
|
44
|
+
#
|
|
45
|
+
# @example
|
|
46
|
+
# data = TraceContext.parse_traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
|
|
47
|
+
# data.trace_id #=> "4bf92f3577b34da6a3ce929d0e0e4736"
|
|
48
|
+
def parse_traceparent(header)
|
|
49
|
+
return nil unless header.is_a?(String)
|
|
50
|
+
|
|
51
|
+
parts = header.split("-")
|
|
52
|
+
return nil unless parts.length == 4
|
|
53
|
+
|
|
54
|
+
version, trace_id, parent_span_id, trace_flags = parts
|
|
55
|
+
|
|
56
|
+
return nil unless version == "00"
|
|
57
|
+
return nil unless TRACE_ID_REGEX.match?(trace_id) && trace_id != ZERO_TRACE_ID
|
|
58
|
+
return nil unless SPAN_ID_REGEX.match?(parent_span_id) && parent_span_id != ZERO_SPAN_ID
|
|
59
|
+
return nil unless FLAGS_REGEX.match?(trace_flags)
|
|
60
|
+
|
|
61
|
+
TraceparentData.new(
|
|
62
|
+
version: version,
|
|
63
|
+
trace_id: trace_id,
|
|
64
|
+
parent_span_id: parent_span_id,
|
|
65
|
+
trace_flags: trace_flags
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Serialize a W3C traceparent header string.
|
|
70
|
+
#
|
|
71
|
+
# @param trace_id [String] 32-character hex trace ID
|
|
72
|
+
# @param span_id [String] 16-character hex span ID
|
|
73
|
+
# @param sampled [Boolean] whether the trace is sampled (default: true)
|
|
74
|
+
# @return [String] formatted traceparent header
|
|
75
|
+
#
|
|
76
|
+
# @example
|
|
77
|
+
# TraceContext.serialize_traceparent("4bf92f...", "00f067...", true)
|
|
78
|
+
# #=> "00-4bf92f...-00f067...-01"
|
|
79
|
+
def serialize_traceparent(trace_id, span_id, sampled: true)
|
|
80
|
+
flags = sampled ? "01" : "00"
|
|
81
|
+
"00-#{trace_id}-#{span_id}-#{flags}"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module JustAnalytics
|
|
8
|
+
# Threaded batch transport for sending data to JustAnalytics ingestion endpoints.
|
|
9
|
+
#
|
|
10
|
+
# Collects spans, errors, logs, and metrics in separate thread-safe queues.
|
|
11
|
+
# A background thread periodically flushes each queue to its respective endpoint:
|
|
12
|
+
# - Spans: +POST /api/ingest/spans+ (batched array)
|
|
13
|
+
# - Errors: +POST /api/ingest/errors+ (batched array)
|
|
14
|
+
# - Logs: +POST /api/ingest/logs+ (batched array)
|
|
15
|
+
# - Metrics: +POST /api/ingest/metrics+ (batched array)
|
|
16
|
+
#
|
|
17
|
+
# Uses Ruby's +Thread::Queue+ for thread-safe buffering and +at_exit+ for
|
|
18
|
+
# graceful shutdown.
|
|
19
|
+
class Transport
|
|
20
|
+
# @return [Integer] number of spans pending flush
|
|
21
|
+
attr_reader :pending_span_count
|
|
22
|
+
|
|
23
|
+
# @param server_url [String] base URL of JustAnalytics server
|
|
24
|
+
# @param api_key [String] API key for Authorization header
|
|
25
|
+
# @param site_id [String] site ID for X-Site-ID header
|
|
26
|
+
# @param flush_interval [Float] flush interval in seconds
|
|
27
|
+
# @param max_batch_size [Integer] max items per batch
|
|
28
|
+
# @param debug [Boolean] enable debug logging
|
|
29
|
+
def initialize(server_url:, api_key:, site_id:, flush_interval: 2.0, max_batch_size: 100, debug: false)
|
|
30
|
+
@server_url = server_url
|
|
31
|
+
@api_key = api_key
|
|
32
|
+
@site_id = site_id
|
|
33
|
+
@flush_interval = flush_interval
|
|
34
|
+
@max_batch_size = max_batch_size
|
|
35
|
+
@debug = debug
|
|
36
|
+
|
|
37
|
+
@span_queue = Thread::Queue.new
|
|
38
|
+
@error_queue = Thread::Queue.new
|
|
39
|
+
@log_queue = Thread::Queue.new
|
|
40
|
+
@metric_queue = Thread::Queue.new
|
|
41
|
+
|
|
42
|
+
@pending_span_count = 0
|
|
43
|
+
@mutex = Mutex.new
|
|
44
|
+
@running = false
|
|
45
|
+
@worker_thread = nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Start the background flush worker thread.
|
|
49
|
+
#
|
|
50
|
+
# @return [void]
|
|
51
|
+
def start
|
|
52
|
+
return if @running
|
|
53
|
+
|
|
54
|
+
@running = true
|
|
55
|
+
@worker_thread = Thread.new { flush_loop }
|
|
56
|
+
@worker_thread.abort_on_exception = false
|
|
57
|
+
|
|
58
|
+
# Register at_exit hook for graceful shutdown
|
|
59
|
+
at_exit { shutdown }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Stop the background worker and flush remaining data.
|
|
63
|
+
#
|
|
64
|
+
# @return [void]
|
|
65
|
+
def shutdown
|
|
66
|
+
return unless @running
|
|
67
|
+
|
|
68
|
+
@running = false
|
|
69
|
+
@worker_thread&.wakeup rescue nil
|
|
70
|
+
@worker_thread&.join(5)
|
|
71
|
+
flush_all
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Enqueue a span payload for batched sending.
|
|
75
|
+
#
|
|
76
|
+
# @param span_hash [Hash] serialized span data
|
|
77
|
+
# @return [void]
|
|
78
|
+
def enqueue_span(span_hash)
|
|
79
|
+
@span_queue << span_hash
|
|
80
|
+
@mutex.synchronize { @pending_span_count += 1 }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Enqueue an error payload for batched sending.
|
|
84
|
+
#
|
|
85
|
+
# @param error_hash [Hash] serialized error data
|
|
86
|
+
# @return [void]
|
|
87
|
+
def enqueue_error(error_hash)
|
|
88
|
+
@error_queue << error_hash
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Enqueue a log entry for batched sending.
|
|
92
|
+
#
|
|
93
|
+
# @param log_hash [Hash] serialized log entry data
|
|
94
|
+
# @return [void]
|
|
95
|
+
def enqueue_log(log_hash)
|
|
96
|
+
@log_queue << log_hash
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Enqueue a metric data point for batched sending.
|
|
100
|
+
#
|
|
101
|
+
# @param metric_hash [Hash] serialized metric data
|
|
102
|
+
# @return [void]
|
|
103
|
+
def enqueue_metric(metric_hash)
|
|
104
|
+
@metric_queue << metric_hash
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Flush all pending data to the server immediately.
|
|
108
|
+
#
|
|
109
|
+
# @return [void]
|
|
110
|
+
def flush
|
|
111
|
+
flush_all
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
# Background loop that periodically flushes all queues.
|
|
117
|
+
def flush_loop
|
|
118
|
+
while @running
|
|
119
|
+
sleep(@flush_interval)
|
|
120
|
+
flush_all
|
|
121
|
+
end
|
|
122
|
+
rescue => e
|
|
123
|
+
debug_log("Flush loop error: #{e.message}")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Drain all queues and send batched requests.
|
|
127
|
+
def flush_all
|
|
128
|
+
flush_queue(@span_queue, "/api/ingest/spans", "spans") do |batch|
|
|
129
|
+
@mutex.synchronize { @pending_span_count -= batch.size }
|
|
130
|
+
{ "spans" => batch }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
flush_queue(@error_queue, "/api/ingest/errors", "errors") do |batch|
|
|
134
|
+
{ "errors" => batch }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
flush_queue(@log_queue, "/api/ingest/logs", "logs") do |batch|
|
|
138
|
+
{ "logs" => batch }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
flush_queue(@metric_queue, "/api/ingest/metrics", "metrics") do |batch|
|
|
142
|
+
{ "metrics" => batch }
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Drain items from a queue and send in batches.
|
|
147
|
+
#
|
|
148
|
+
# @param queue [Thread::Queue] the queue to drain
|
|
149
|
+
# @param path [String] API endpoint path
|
|
150
|
+
# @param label [String] human-readable label for debug logs
|
|
151
|
+
# @yield [Array<Hash>] batch of items to wrap in request body
|
|
152
|
+
def flush_queue(queue, path, label)
|
|
153
|
+
return if queue.empty?
|
|
154
|
+
|
|
155
|
+
batch = []
|
|
156
|
+
batch << queue.pop(true) until queue.empty? || batch.size >= @max_batch_size rescue nil
|
|
157
|
+
|
|
158
|
+
return if batch.empty?
|
|
159
|
+
|
|
160
|
+
body = yield(batch)
|
|
161
|
+
send_request(path, body, label, batch.size)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Send an HTTP POST request to the given endpoint.
|
|
165
|
+
#
|
|
166
|
+
# @param path [String] API path (e.g. "/api/ingest/spans")
|
|
167
|
+
# @param body [Hash] request body to JSON-encode
|
|
168
|
+
# @param label [String] label for debug messages
|
|
169
|
+
# @param count [Integer] number of items in the batch
|
|
170
|
+
def send_request(path, body, label, count)
|
|
171
|
+
uri = URI.join(@server_url, path)
|
|
172
|
+
json_body = JSON.generate(body)
|
|
173
|
+
|
|
174
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
175
|
+
http.use_ssl = (uri.scheme == "https")
|
|
176
|
+
http.open_timeout = 5
|
|
177
|
+
http.read_timeout = 10
|
|
178
|
+
|
|
179
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
180
|
+
request["Content-Type"] = "application/json"
|
|
181
|
+
request["Authorization"] = "Bearer #{@api_key}"
|
|
182
|
+
request["X-Site-ID"] = @site_id
|
|
183
|
+
request["User-Agent"] = "justanalytics-ruby/#{JustAnalytics::VERSION}"
|
|
184
|
+
request.body = json_body
|
|
185
|
+
|
|
186
|
+
response = http.request(request)
|
|
187
|
+
|
|
188
|
+
case response.code.to_i
|
|
189
|
+
when 200..299
|
|
190
|
+
debug_log("Flushed #{count} #{label} successfully")
|
|
191
|
+
when 429
|
|
192
|
+
debug_log("Rate limited (429). Dropped #{count} #{label}.")
|
|
193
|
+
else
|
|
194
|
+
debug_log("Flush failed with HTTP #{response.code}. Dropped #{count} #{label}. Response: #{response.body}")
|
|
195
|
+
end
|
|
196
|
+
rescue => e
|
|
197
|
+
debug_log("Network error flushing #{label}: #{e.message}. Dropped #{count} #{label}.")
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def debug_log(message)
|
|
201
|
+
$stderr.puts "[JustAnalytics] #{message}" if @debug
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# JustAnalytics Ruby SDK
|
|
4
|
+
#
|
|
5
|
+
# End-to-end observability for Ruby applications. Provides distributed tracing,
|
|
6
|
+
# error tracking, structured logging, and infrastructure metrics reporting
|
|
7
|
+
# to the JustAnalytics platform.
|
|
8
|
+
#
|
|
9
|
+
# @example Quick start
|
|
10
|
+
# require "justanalytics"
|
|
11
|
+
#
|
|
12
|
+
# JustAnalytics.init(
|
|
13
|
+
# site_id: "site_abc123",
|
|
14
|
+
# api_key: "ja_sk_your_key_here",
|
|
15
|
+
# service_name: "rails-api",
|
|
16
|
+
# environment: "production"
|
|
17
|
+
# )
|
|
18
|
+
#
|
|
19
|
+
# JustAnalytics.start_span("process-order") do |span|
|
|
20
|
+
# span.set_attribute("order.id", "12345")
|
|
21
|
+
# # ... your code ...
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# @see https://justanalytics.app/docs/ruby-sdk
|
|
25
|
+
module JustAnalytics
|
|
26
|
+
class Error < StandardError; end
|
|
27
|
+
|
|
28
|
+
autoload :VERSION, "justanalytics/version"
|
|
29
|
+
autoload :Configuration, "justanalytics/configuration"
|
|
30
|
+
autoload :Context, "justanalytics/context"
|
|
31
|
+
autoload :TraceContext, "justanalytics/trace_context"
|
|
32
|
+
autoload :Span, "justanalytics/span"
|
|
33
|
+
autoload :Transport, "justanalytics/transport"
|
|
34
|
+
autoload :Logger, "justanalytics/logger"
|
|
35
|
+
autoload :Client, "justanalytics/client"
|
|
36
|
+
|
|
37
|
+
# Integrations
|
|
38
|
+
autoload :RailsMiddleware, "justanalytics/integrations/rails"
|
|
39
|
+
autoload :Railtie, "justanalytics/integrations/rails"
|
|
40
|
+
autoload :SidekiqServerMiddleware, "justanalytics/integrations/sidekiq"
|
|
41
|
+
autoload :SidekiqClientMiddleware, "justanalytics/integrations/sidekiq"
|
|
42
|
+
autoload :NetHttpPatch, "justanalytics/integrations/net_http"
|
|
43
|
+
|
|
44
|
+
class << self
|
|
45
|
+
# Get the singleton client instance.
|
|
46
|
+
#
|
|
47
|
+
# @return [Client]
|
|
48
|
+
def client
|
|
49
|
+
@client ||= Client.new
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Initialize the JustAnalytics SDK.
|
|
53
|
+
#
|
|
54
|
+
# Must be called before any other SDK method. Can only be called once;
|
|
55
|
+
# subsequent calls log a warning and are ignored.
|
|
56
|
+
#
|
|
57
|
+
# @param site_id [String] site ID from JustAnalytics dashboard (required)
|
|
58
|
+
# @param api_key [String] API key (required)
|
|
59
|
+
# @param service_name [String] service name (required)
|
|
60
|
+
# @param environment [String, nil] deployment environment
|
|
61
|
+
# @param release [String, nil] release/version string
|
|
62
|
+
# @param server_url [String, nil] JA server URL override
|
|
63
|
+
# @param debug [Boolean] enable debug logging (default: false)
|
|
64
|
+
# @param flush_interval [Float] flush interval in seconds (default: 2.0)
|
|
65
|
+
# @param max_batch_size [Integer] max batch size (default: 100)
|
|
66
|
+
# @param enabled [Boolean] enable/disable SDK (default: true)
|
|
67
|
+
# @return [void]
|
|
68
|
+
#
|
|
69
|
+
# @example
|
|
70
|
+
# JustAnalytics.init(
|
|
71
|
+
# site_id: "site_abc123",
|
|
72
|
+
# api_key: "ja_sk_your_key_here",
|
|
73
|
+
# service_name: "rails-api",
|
|
74
|
+
# environment: "production",
|
|
75
|
+
# release: "1.2.3"
|
|
76
|
+
# )
|
|
77
|
+
def init(site_id:, api_key:, service_name:, **options)
|
|
78
|
+
client.init(site_id: site_id, api_key: api_key, service_name: service_name, **options)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Whether the SDK has been initialized.
|
|
82
|
+
#
|
|
83
|
+
# @return [Boolean]
|
|
84
|
+
def initialized?
|
|
85
|
+
client.initialized?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Create and execute a span.
|
|
89
|
+
#
|
|
90
|
+
# @param name [String] operation name
|
|
91
|
+
# @param op [String] span kind (default: "internal")
|
|
92
|
+
# @param attributes [Hash] initial span attributes
|
|
93
|
+
# @yield [Span] the created span
|
|
94
|
+
# @return the return value of the block
|
|
95
|
+
#
|
|
96
|
+
# @example
|
|
97
|
+
# result = JustAnalytics.start_span("fetch-user", op: "db") do |span|
|
|
98
|
+
# span.set_attribute("user.id", user_id)
|
|
99
|
+
# User.find(user_id)
|
|
100
|
+
# end
|
|
101
|
+
def start_span(name, op: "internal", attributes: {}, &block)
|
|
102
|
+
client.start_span(name, op: op, attributes: attributes, &block)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Capture an exception and send it to JustAnalytics.
|
|
106
|
+
#
|
|
107
|
+
# @param error [Exception, String] the error to capture
|
|
108
|
+
# @param tags [Hash] additional tags
|
|
109
|
+
# @param extra [Hash] arbitrary extra data
|
|
110
|
+
# @param user [Hash, nil] user context override
|
|
111
|
+
# @return [String] unique event ID
|
|
112
|
+
#
|
|
113
|
+
# @example
|
|
114
|
+
# begin
|
|
115
|
+
# risky_operation
|
|
116
|
+
# rescue => e
|
|
117
|
+
# JustAnalytics.capture_exception(e, tags: { module: "payments" })
|
|
118
|
+
# end
|
|
119
|
+
def capture_exception(error, **options)
|
|
120
|
+
client.capture_exception(error, **options)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Capture a message and send it to JustAnalytics.
|
|
124
|
+
#
|
|
125
|
+
# @param message [String] the message
|
|
126
|
+
# @param level [String] severity level (default: "info")
|
|
127
|
+
# @param tags [Hash] additional tags
|
|
128
|
+
# @return [String] unique event ID
|
|
129
|
+
#
|
|
130
|
+
# @example
|
|
131
|
+
# JustAnalytics.capture_message("Rate limit exceeded", level: "warning")
|
|
132
|
+
def capture_message(message, **options)
|
|
133
|
+
client.capture_message(message, **options)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Set user context for the current thread scope.
|
|
137
|
+
#
|
|
138
|
+
# @param id [String, nil] user ID
|
|
139
|
+
# @param email [String, nil] user email
|
|
140
|
+
# @param username [String, nil] username
|
|
141
|
+
# @return [void]
|
|
142
|
+
#
|
|
143
|
+
# @example
|
|
144
|
+
# JustAnalytics.set_user(id: "user-123", email: "alice@example.com")
|
|
145
|
+
def set_user(id: nil, email: nil, username: nil)
|
|
146
|
+
client.set_user(id: id, email: email, username: username)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Set a tag on the current thread scope.
|
|
150
|
+
#
|
|
151
|
+
# @param key [String] tag key
|
|
152
|
+
# @param value [String] tag value
|
|
153
|
+
# @return [void]
|
|
154
|
+
#
|
|
155
|
+
# @example
|
|
156
|
+
# JustAnalytics.set_tag("feature", "checkout")
|
|
157
|
+
def set_tag(key, value)
|
|
158
|
+
client.set_tag(key, value)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Get the structured logger instance.
|
|
162
|
+
#
|
|
163
|
+
# @return [Logger] the logger (no-op logger if SDK not initialized)
|
|
164
|
+
def logger
|
|
165
|
+
client.logger || Logger.new(
|
|
166
|
+
service_name: "unknown",
|
|
167
|
+
transport: nil,
|
|
168
|
+
enabled: false
|
|
169
|
+
)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Record a custom infrastructure metric.
|
|
173
|
+
#
|
|
174
|
+
# @param metric_name [String] metric name (dot notation)
|
|
175
|
+
# @param value [Numeric] metric value
|
|
176
|
+
# @param tags [Hash] optional tags
|
|
177
|
+
# @return [void]
|
|
178
|
+
def record_metric(metric_name, value, tags: {})
|
|
179
|
+
client.record_metric(metric_name, value, tags: tags)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Flush all pending data to the server.
|
|
183
|
+
#
|
|
184
|
+
# @return [void]
|
|
185
|
+
def flush
|
|
186
|
+
client.flush
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Shut down the SDK.
|
|
190
|
+
#
|
|
191
|
+
# @return [void]
|
|
192
|
+
def close
|
|
193
|
+
client.close
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Reset the singleton client (primarily for testing).
|
|
197
|
+
#
|
|
198
|
+
# @return [void]
|
|
199
|
+
# @api private
|
|
200
|
+
def reset!
|
|
201
|
+
client.close if client.initialized?
|
|
202
|
+
@client = nil
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Auto-load Railtie if Rails is present
|
|
208
|
+
require "justanalytics/integrations/rails" if defined?(::Rails::Railtie)
|
metadata
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: justanalytics
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Velocity Digital Labs
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-16 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: '2.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '13.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '13.0'
|
|
69
|
+
description: Distributed tracing, error tracking, structured logging, and infrastructure
|
|
70
|
+
metrics for Ruby applications. Ships data to the JustAnalytics platform via batched
|
|
71
|
+
HTTP transport with automatic trace context propagation.
|
|
72
|
+
email:
|
|
73
|
+
- support@velocitydigitallabs.com
|
|
74
|
+
executables: []
|
|
75
|
+
extensions: []
|
|
76
|
+
extra_rdoc_files: []
|
|
77
|
+
files:
|
|
78
|
+
- README.md
|
|
79
|
+
- lib/justanalytics.rb
|
|
80
|
+
- lib/justanalytics/client.rb
|
|
81
|
+
- lib/justanalytics/configuration.rb
|
|
82
|
+
- lib/justanalytics/context.rb
|
|
83
|
+
- lib/justanalytics/integrations/net_http.rb
|
|
84
|
+
- lib/justanalytics/integrations/rails.rb
|
|
85
|
+
- lib/justanalytics/integrations/sidekiq.rb
|
|
86
|
+
- lib/justanalytics/logger.rb
|
|
87
|
+
- lib/justanalytics/span.rb
|
|
88
|
+
- lib/justanalytics/trace_context.rb
|
|
89
|
+
- lib/justanalytics/transport.rb
|
|
90
|
+
- lib/justanalytics/version.rb
|
|
91
|
+
homepage: https://justanalytics.app
|
|
92
|
+
licenses:
|
|
93
|
+
- MIT
|
|
94
|
+
metadata:
|
|
95
|
+
homepage_uri: https://justanalytics.app
|
|
96
|
+
source_code_uri: https://github.com/velocitydigitallabs/justanalytics-ruby
|
|
97
|
+
changelog_uri: https://github.com/velocitydigitallabs/justanalytics-ruby/blob/main/CHANGELOG.md
|
|
98
|
+
post_install_message:
|
|
99
|
+
rdoc_options: []
|
|
100
|
+
require_paths:
|
|
101
|
+
- lib
|
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
|
+
requirements:
|
|
104
|
+
- - ">="
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: 2.6.0
|
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '0'
|
|
112
|
+
requirements: []
|
|
113
|
+
rubygems_version: 3.0.3.1
|
|
114
|
+
signing_key:
|
|
115
|
+
specification_version: 4
|
|
116
|
+
summary: JustAnalytics Ruby SDK - End-to-end observability for Ruby applications
|
|
117
|
+
test_files: []
|