opentrace 0.1.0 → 0.2.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/README.md +234 -43
- data/lib/opentrace/client.rb +23 -1
- data/lib/opentrace/version.rb +1 -1
- data/lib/opentrace.rb +14 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b4a10bb3e35d7526cb02f721c1040ad9f568fa6058d17d1e7aaf1bf31d42ecd6
|
|
4
|
+
data.tar.gz: 64f7700651c457151c9d9a961a8bba205cf88d0746265e6eebaa7f928e2c0f9b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fcd303032ee0ae96aeb6602ffdd3a60df2972e58cfff1c6499f5cc17ea57c89a0ffe6e5e092a9eb7c0ccb55f798afcfbce13f34ac789e70908d890a42da5699f
|
|
7
|
+
data.tar.gz: 54f2c8bfd37f9a36b9f32aa49b76a6846335ab0063e804fb35aa491892d18d4a560c410e9204b667ebd5e57cfe522114d677f8973b788a897084a6ce12e62bb1
|
data/README.md
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
# OpenTrace Ruby
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[](https://rubygems.org/gems/opentrace)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
A thin, safe Ruby client that forwards structured application logs to an [OpenTrace](https://github.com/adham90/opentrace-ruby) server over HTTP.
|
|
7
|
+
|
|
8
|
+
**This gem will never crash or slow down your application.** All network errors are swallowed silently. If the server is unreachable, logs are dropped -- your app continues running normally.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Zero-risk integration** -- all errors swallowed, never raises to host app
|
|
13
|
+
- **Async dispatch** -- logs are queued in-memory and sent via a background thread
|
|
14
|
+
- **Batch sending** -- groups logs into configurable batches for efficient network usage
|
|
15
|
+
- **Bounded queue** -- caps at 1,000 entries to prevent memory bloat
|
|
16
|
+
- **Smart truncation** -- oversized payloads are truncated instead of silently dropped
|
|
17
|
+
- **Rails integration** -- auto-instruments controllers, SQL queries, and ActiveJob via Railtie
|
|
18
|
+
- **Rack middleware** -- propagates `request_id` via thread-local storage
|
|
19
|
+
- **Logger wrapper** -- drop-in replacement that forwards to OpenTrace while keeping your original logger
|
|
20
|
+
- **Rails 7.1+ BroadcastLogger** -- native support via `broadcast_to`
|
|
21
|
+
- **TaggedLogging** -- preserves `ActiveSupport::TaggedLogging` tags in metadata
|
|
22
|
+
- **Context support** -- attach global metadata to every log via Hash or Proc
|
|
23
|
+
- **Level filtering** -- `min_level` config to control which severities are forwarded
|
|
24
|
+
- **Auto-enrichment** -- every log includes `hostname`, `pid`, and `git_sha` automatically
|
|
25
|
+
- **Exception helper** -- `OpenTrace.error` captures class, message, and cleaned backtrace
|
|
26
|
+
- **Runtime controls** -- enable/disable logging at runtime without restarting
|
|
6
27
|
|
|
7
28
|
## Installation
|
|
8
29
|
|
|
@@ -18,24 +39,77 @@ Then run:
|
|
|
18
39
|
bundle install
|
|
19
40
|
```
|
|
20
41
|
|
|
42
|
+
Or install directly:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
gem install opentrace
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
OpenTrace.configure do |c|
|
|
52
|
+
c.endpoint = "https://opentrace.example.com"
|
|
53
|
+
c.api_key = ENV["OPENTRACE_API_KEY"]
|
|
54
|
+
c.service = "my-app"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
OpenTrace.log("INFO", "User signed in", { user_id: 42 })
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
That's it. Logs are queued and sent asynchronously -- your code never blocks.
|
|
61
|
+
|
|
21
62
|
## Configuration
|
|
22
63
|
|
|
23
64
|
```ruby
|
|
24
65
|
OpenTrace.configure do |c|
|
|
25
|
-
|
|
26
|
-
c.
|
|
27
|
-
c.
|
|
28
|
-
c.
|
|
29
|
-
|
|
30
|
-
|
|
66
|
+
# Required
|
|
67
|
+
c.endpoint = "https://opentrace.example.com"
|
|
68
|
+
c.api_key = ENV["OPENTRACE_API_KEY"]
|
|
69
|
+
c.service = "billing-api"
|
|
70
|
+
|
|
71
|
+
# Optional
|
|
72
|
+
c.environment = "production" # default: nil
|
|
73
|
+
c.timeout = 1.0 # HTTP timeout in seconds (default: 1.0)
|
|
74
|
+
c.enabled = true # default: true
|
|
75
|
+
c.min_level = :info # minimum level to forward (default: :debug)
|
|
76
|
+
c.batch_size = 50 # logs per batch (default: 50)
|
|
77
|
+
c.flush_interval = 5.0 # seconds between flushes (default: 5.0)
|
|
78
|
+
|
|
79
|
+
# Global context -- attached to every log entry
|
|
80
|
+
c.context = { deploy_version: "v1.2.3" }
|
|
81
|
+
# Or use a Proc for dynamic context:
|
|
82
|
+
c.context = -> { { tenant_id: Current.tenant&.id } }
|
|
83
|
+
|
|
84
|
+
# Auto-populated (override if needed)
|
|
85
|
+
c.hostname = Socket.gethostname # auto-detected
|
|
86
|
+
c.pid = Process.pid # auto-detected
|
|
87
|
+
c.git_sha = ENV["REVISION"] # checks REVISION, GIT_SHA, HEROKU_SLUG_COMMIT
|
|
88
|
+
|
|
89
|
+
# SQL logging (Rails only)
|
|
90
|
+
c.sql_logging = true # default: true
|
|
91
|
+
c.sql_duration_threshold_ms = 100.0 # only log queries slower than this (default: 0.0 = all)
|
|
31
92
|
end
|
|
32
93
|
```
|
|
33
94
|
|
|
34
95
|
If any required field (`endpoint`, `api_key`, `service`) is missing or empty, the gem **disables itself automatically**. No errors, no logs sent.
|
|
35
96
|
|
|
97
|
+
### Level Filtering
|
|
98
|
+
|
|
99
|
+
Control which log levels are forwarded with `min_level`:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
OpenTrace.configure do |c|
|
|
103
|
+
# ...
|
|
104
|
+
c.min_level = :warn # only forward WARN, ERROR, and FATAL
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Available levels: `:debug`, `:info`, `:warn`, `:error`, `:fatal`
|
|
109
|
+
|
|
36
110
|
## Usage
|
|
37
111
|
|
|
38
|
-
### Direct
|
|
112
|
+
### Direct Logging
|
|
39
113
|
|
|
40
114
|
```ruby
|
|
41
115
|
OpenTrace.log("INFO", "User signed in", { user_id: 42, ip: "1.2.3.4" })
|
|
@@ -52,7 +126,24 @@ OpenTrace.log("ERROR", "Payment failed", {
|
|
|
52
126
|
|
|
53
127
|
Pass `trace_id` inside metadata and it will be promoted to a top-level field automatically.
|
|
54
128
|
|
|
55
|
-
###
|
|
129
|
+
### Exception Logging
|
|
130
|
+
|
|
131
|
+
Use `OpenTrace.error` to log exceptions with automatic class, message, and backtrace extraction:
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
begin
|
|
135
|
+
dangerous_operation
|
|
136
|
+
rescue => e
|
|
137
|
+
OpenTrace.error(e, { user_id: current_user.id, action: "checkout" })
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
This captures:
|
|
142
|
+
- `exception_class` -- the exception class name
|
|
143
|
+
- `exception_message` -- truncated to 500 characters
|
|
144
|
+
- `backtrace` -- cleaned (Rails backtrace cleaner or gem-filtered), limited to 15 frames
|
|
145
|
+
|
|
146
|
+
### Logger Wrapper
|
|
56
147
|
|
|
57
148
|
Wrap any Ruby `Logger` to forward all log output to OpenTrace while keeping the original logger working exactly as before:
|
|
58
149
|
|
|
@@ -66,13 +157,35 @@ logger.info("This goes to STDOUT and to OpenTrace")
|
|
|
66
157
|
logger.error("So does this")
|
|
67
158
|
```
|
|
68
159
|
|
|
69
|
-
|
|
160
|
+
Attach default metadata to every log from this logger:
|
|
70
161
|
|
|
71
162
|
```ruby
|
|
72
163
|
logger = OpenTrace::Logger.new(original_logger, metadata: { component: "worker" })
|
|
164
|
+
logger.info("Processing job")
|
|
165
|
+
# metadata: { component: "worker" }
|
|
73
166
|
```
|
|
74
167
|
|
|
75
|
-
###
|
|
168
|
+
### Global Context
|
|
169
|
+
|
|
170
|
+
Attach metadata to **every** log entry using `config.context`:
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
# Static context
|
|
174
|
+
OpenTrace.configure do |c|
|
|
175
|
+
# ...
|
|
176
|
+
c.context = { deploy_version: "v1.2.3", region: "us-east-1" }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Dynamic context (evaluated on each log call)
|
|
180
|
+
OpenTrace.configure do |c|
|
|
181
|
+
# ...
|
|
182
|
+
c.context = -> { { request_id: Thread.current[:request_id], tenant: Current.tenant&.slug } }
|
|
183
|
+
end
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Context has the lowest priority -- caller-provided metadata overrides context values.
|
|
187
|
+
|
|
188
|
+
## Rails Integration
|
|
76
189
|
|
|
77
190
|
In a Rails app, add an initializer:
|
|
78
191
|
|
|
@@ -86,29 +199,97 @@ OpenTrace.configure do |c|
|
|
|
86
199
|
end
|
|
87
200
|
```
|
|
88
201
|
|
|
89
|
-
The gem auto-detects Rails and
|
|
202
|
+
The gem auto-detects Rails and provides the following integrations automatically:
|
|
203
|
+
|
|
204
|
+
### Rack Middleware
|
|
205
|
+
|
|
206
|
+
Automatically inserted into the middleware stack. Captures `request_id` from `action_dispatch.request_id` or `HTTP_X_REQUEST_ID` and makes it available via `OpenTrace.current_request_id`. All logs within a request automatically include the `request_id`.
|
|
207
|
+
|
|
208
|
+
### Logger Wrapping
|
|
209
|
+
|
|
210
|
+
- **Rails 7.1+**: Uses `BroadcastLogger#broadcast_to` to register as a broadcast target (non-invasive)
|
|
211
|
+
- **Pre-7.1**: Wraps `Rails.logger` with `OpenTrace::Logger` which delegates to the original and forwards to OpenTrace
|
|
212
|
+
|
|
213
|
+
All your existing `Rails.logger.info(...)` calls automatically get forwarded to OpenTrace.
|
|
214
|
+
|
|
215
|
+
### Controller Subscriber
|
|
216
|
+
|
|
217
|
+
Subscribes to `process_action.action_controller` and captures:
|
|
218
|
+
|
|
219
|
+
| Field | Description |
|
|
220
|
+
|---|---|
|
|
221
|
+
| `request_id` | From ActionDispatch |
|
|
222
|
+
| `controller` | Controller class name |
|
|
223
|
+
| `action` | Action name |
|
|
224
|
+
| `method` | HTTP method (GET, POST, etc.) |
|
|
225
|
+
| `path` | Request path |
|
|
226
|
+
| `status` | HTTP response status code |
|
|
227
|
+
| `duration_ms` | Request duration in milliseconds |
|
|
228
|
+
| `user_id` | Auto-captured if controller responds to `current_user` |
|
|
229
|
+
| `params` | Filtered request parameters (respects `filter_parameters`) |
|
|
230
|
+
| `exception_class` | Exception class (if raised) |
|
|
231
|
+
| `exception_message` | Exception message (if raised) |
|
|
232
|
+
| `backtrace` | Cleaned backtrace (if exception raised) |
|
|
233
|
+
|
|
234
|
+
Log levels are set automatically:
|
|
235
|
+
- **ERROR** -- exceptions or 5xx status
|
|
236
|
+
- **WARN** -- 4xx status
|
|
237
|
+
- **INFO** -- everything else
|
|
238
|
+
|
|
239
|
+
### SQL Query Subscriber
|
|
240
|
+
|
|
241
|
+
Subscribes to `sql.active_record` and logs every query with:
|
|
242
|
+
|
|
243
|
+
| Field | Description |
|
|
244
|
+
|---|---|
|
|
245
|
+
| `sql_name` | Query name (e.g., "User Load") |
|
|
246
|
+
| `sql` | Query text (truncated to 1000 chars) |
|
|
247
|
+
| `sql_duration_ms` | Query duration in milliseconds |
|
|
248
|
+
| `sql_cached` | Whether the result was cached |
|
|
249
|
+
| `sql_table` | Extracted table name for filtering |
|
|
250
|
+
|
|
251
|
+
SCHEMA queries (migrations, structure dumps) are automatically skipped. Queries over 1 second are logged as `WARN`, all others as `DEBUG`.
|
|
252
|
+
|
|
253
|
+
Configure SQL logging:
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
OpenTrace.configure do |c|
|
|
257
|
+
# ...
|
|
258
|
+
c.sql_logging = true # enable/disable (default: true)
|
|
259
|
+
c.sql_duration_threshold_ms = 100.0 # only log slow queries (default: 0.0 = all)
|
|
260
|
+
end
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### ActiveJob Subscriber
|
|
264
|
+
|
|
265
|
+
Subscribes to `perform.active_job` and logs every job execution with:
|
|
90
266
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
267
|
+
| Field | Description |
|
|
268
|
+
|---|---|
|
|
269
|
+
| `job_class` | Job class name |
|
|
270
|
+
| `job_id` | Unique job ID |
|
|
271
|
+
| `queue_name` | Queue the job ran on |
|
|
272
|
+
| `executions` | Attempt number |
|
|
273
|
+
| `duration_ms` | Execution duration |
|
|
274
|
+
| `job_arguments` | Serialized arguments (truncated to 512 bytes) |
|
|
275
|
+
| `exception_class` | Exception class (if failed) |
|
|
276
|
+
| `exception_message` | Exception message (if failed) |
|
|
277
|
+
| `backtrace` | Cleaned backtrace (if failed) |
|
|
97
278
|
|
|
98
|
-
|
|
279
|
+
Failed jobs are logged as `ERROR`, successful jobs as `INFO`.
|
|
99
280
|
|
|
100
281
|
### TaggedLogging
|
|
101
282
|
|
|
102
283
|
If your wrapped logger uses `ActiveSupport::TaggedLogging`, tags are preserved and injected into the metadata:
|
|
103
284
|
|
|
104
285
|
```ruby
|
|
105
|
-
Rails.logger.tagged("RequestID-123") do
|
|
286
|
+
Rails.logger.tagged("RequestID-123", "UserID-42") do
|
|
106
287
|
Rails.logger.info("Processing request")
|
|
107
|
-
# metadata
|
|
288
|
+
# metadata: { tags: ["RequestID-123", "UserID-42"] }
|
|
108
289
|
end
|
|
109
290
|
```
|
|
110
291
|
|
|
111
|
-
## Runtime
|
|
292
|
+
## Runtime Controls
|
|
112
293
|
|
|
113
294
|
```ruby
|
|
114
295
|
OpenTrace.enabled? # check if logging is active
|
|
@@ -116,7 +297,7 @@ OpenTrace.disable! # turn off (logs are silently dropped)
|
|
|
116
297
|
OpenTrace.enable! # turn back on
|
|
117
298
|
```
|
|
118
299
|
|
|
119
|
-
## Graceful
|
|
300
|
+
## Graceful Shutdown
|
|
120
301
|
|
|
121
302
|
If your app needs a clean shutdown (e.g. a Sidekiq worker), drain the queue before exiting:
|
|
122
303
|
|
|
@@ -126,19 +307,24 @@ OpenTrace.shutdown(timeout: 5)
|
|
|
126
307
|
|
|
127
308
|
This gives the background thread up to 5 seconds to send any remaining queued logs.
|
|
128
309
|
|
|
129
|
-
## How
|
|
310
|
+
## How It Works
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
Your App --log()--> [In-Memory Queue] --background thread--> POST /api/logs --> OpenTrace Server
|
|
314
|
+
```
|
|
130
315
|
|
|
131
316
|
- Logs are serialized to JSON and pushed onto an in-memory queue
|
|
132
|
-
- A single background thread reads from the queue and sends
|
|
317
|
+
- A single background thread reads from the queue and sends batches via `POST /api/logs`
|
|
133
318
|
- The thread is started lazily on the first log call -- no threads are created at boot
|
|
134
319
|
- If the queue exceeds 1,000 items, new logs are dropped (oldest are preserved)
|
|
135
|
-
- Payloads
|
|
320
|
+
- Payloads exceeding 32 KB are intelligently truncated (backtrace, params, SQL removed first)
|
|
321
|
+
- If still too large after truncation, the payload is split and retried in smaller batches
|
|
136
322
|
- All network errors (timeouts, connection refused, DNS failures) are swallowed silently
|
|
137
323
|
- The HTTP timeout defaults to 1 second
|
|
138
324
|
|
|
139
|
-
## Log
|
|
325
|
+
## Log Payload Format
|
|
140
326
|
|
|
141
|
-
Each log is sent as a JSON object
|
|
327
|
+
Each log is sent as a JSON object to `POST /api/logs`:
|
|
142
328
|
|
|
143
329
|
```json
|
|
144
330
|
{
|
|
@@ -150,26 +336,31 @@ Each log is sent as a JSON object:
|
|
|
150
336
|
"message": "PG::UniqueViolation",
|
|
151
337
|
"metadata": {
|
|
152
338
|
"user_id": 42,
|
|
153
|
-
"request_id": "req-456"
|
|
339
|
+
"request_id": "req-456",
|
|
340
|
+
"hostname": "web-01",
|
|
341
|
+
"pid": 12345,
|
|
342
|
+
"git_sha": "a1b2c3d"
|
|
154
343
|
}
|
|
155
344
|
}
|
|
156
345
|
```
|
|
157
346
|
|
|
158
|
-
| Field
|
|
159
|
-
|
|
160
|
-
| `timestamp`
|
|
161
|
-
| `level`
|
|
162
|
-
| `message`
|
|
163
|
-
| `service`
|
|
164
|
-
| `environment` | string | no
|
|
165
|
-
| `trace_id`
|
|
166
|
-
| `metadata`
|
|
347
|
+
| Field | Type | Required |
|
|
348
|
+
|---|---|---|
|
|
349
|
+
| `timestamp` | string (ISO 8601) | yes |
|
|
350
|
+
| `level` | string | yes |
|
|
351
|
+
| `message` | string | yes |
|
|
352
|
+
| `service` | string | no |
|
|
353
|
+
| `environment` | string | no |
|
|
354
|
+
| `trace_id` | string | no |
|
|
355
|
+
| `metadata` | object | no |
|
|
356
|
+
|
|
357
|
+
The server accepts a single JSON object or an array of objects.
|
|
167
358
|
|
|
168
359
|
## Requirements
|
|
169
360
|
|
|
170
|
-
- Ruby 3.0
|
|
171
|
-
- Rails 6
|
|
361
|
+
- Ruby >= 3.0
|
|
362
|
+
- Rails >= 6 (optional, auto-detected)
|
|
172
363
|
|
|
173
364
|
## License
|
|
174
365
|
|
|
175
|
-
MIT
|
|
366
|
+
[MIT](LICENSE)
|
data/lib/opentrace/client.rb
CHANGED
|
@@ -15,11 +15,14 @@ module OpenTrace
|
|
|
15
15
|
@queue = Thread::Queue.new
|
|
16
16
|
@mutex = Mutex.new
|
|
17
17
|
@thread = nil
|
|
18
|
+
@pid = Process.pid
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def enqueue(payload)
|
|
21
22
|
return unless @config.enabled?
|
|
22
23
|
|
|
24
|
+
reset_after_fork! if forked?
|
|
25
|
+
|
|
23
26
|
# Drop newest if queue is full
|
|
24
27
|
return if @queue.size >= MAX_QUEUE_SIZE
|
|
25
28
|
|
|
@@ -34,15 +37,34 @@ module OpenTrace
|
|
|
34
37
|
|
|
35
38
|
private
|
|
36
39
|
|
|
40
|
+
def forked?
|
|
41
|
+
Process.pid != @pid
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def reset_after_fork!
|
|
45
|
+
# After fork, the old thread/queue/mutex from the parent are dead.
|
|
46
|
+
# Re-create everything cleanly in the child process.
|
|
47
|
+
@pid = Process.pid
|
|
48
|
+
@queue = Thread::Queue.new
|
|
49
|
+
@mutex = Mutex.new
|
|
50
|
+
@thread = nil
|
|
51
|
+
end
|
|
52
|
+
|
|
37
53
|
def ensure_thread_running
|
|
38
54
|
return if @thread&.alive?
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
# Use try_lock so we never block the calling thread.
|
|
57
|
+
# If another thread is already spawning the dispatch thread, skip —
|
|
58
|
+
# the next enqueue will see it alive.
|
|
59
|
+
return unless @mutex.try_lock
|
|
60
|
+
begin
|
|
41
61
|
return if @thread&.alive?
|
|
42
62
|
|
|
43
63
|
@thread = Thread.new { dispatch_loop }
|
|
44
64
|
@thread.abort_on_exception = false
|
|
45
65
|
@thread.report_on_exception = false
|
|
66
|
+
ensure
|
|
67
|
+
@mutex.unlock
|
|
46
68
|
end
|
|
47
69
|
end
|
|
48
70
|
|
data/lib/opentrace/version.rb
CHANGED
data/lib/opentrace.rb
CHANGED
|
@@ -90,11 +90,11 @@ module OpenTrace
|
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
def current_request_id
|
|
93
|
-
|
|
93
|
+
Fiber[:opentrace_request_id]
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
def current_request_id=(id)
|
|
97
|
-
|
|
97
|
+
Fiber[:opentrace_request_id] = id
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def shutdown(timeout: 5)
|
|
@@ -106,12 +106,23 @@ module OpenTrace
|
|
|
106
106
|
@config = nil
|
|
107
107
|
@client = nil
|
|
108
108
|
@static_context = nil
|
|
109
|
+
@at_exit_registered = nil
|
|
109
110
|
end
|
|
110
111
|
|
|
111
112
|
private
|
|
112
113
|
|
|
113
114
|
def client
|
|
114
|
-
@client ||=
|
|
115
|
+
@client ||= begin
|
|
116
|
+
c = Client.new(config)
|
|
117
|
+
register_at_exit_hook!
|
|
118
|
+
c
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def register_at_exit_hook!
|
|
123
|
+
return if @at_exit_registered
|
|
124
|
+
@at_exit_registered = true
|
|
125
|
+
at_exit { OpenTrace.shutdown(timeout: 2) }
|
|
115
126
|
end
|
|
116
127
|
|
|
117
128
|
def reset_client!
|