hyperprobe-agent 1.2.27.pre.3-java
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/LICENSE +13 -0
- data/README.md +386 -0
- data/lib/hyperprobe/agent.rb +537 -0
- data/lib/hyperprobe/core/broker.rb +183 -0
- data/lib/hyperprobe/core/evaluator.rb +418 -0
- data/lib/hyperprobe/core/lexical_scope.rb +222 -0
- data/lib/hyperprobe/core/logger.rb +198 -0
- data/lib/hyperprobe/core/monitoring_engine.rb +857 -0
- data/lib/hyperprobe/core/quota.rb +111 -0
- data/lib/hyperprobe/core/safe_ast_validator.rb +170 -0
- data/lib/hyperprobe/core/safety.rb +256 -0
- data/lib/hyperprobe/core/serializer.rb +437 -0
- data/lib/hyperprobe/core/trace_extractor.rb +158 -0
- data/lib/hyperprobe/core/transports/java_grpc.rb +57 -0
- data/lib/hyperprobe/jars/hyperprobe-grpc.jar +0 -0
- data/lib/hyperprobe/lambda.rb +92 -0
- data/lib/hyperprobe/protos/agent_descriptor.rb +7 -0
- data/lib/hyperprobe/protos/agent_pb.rb +33 -0
- data/lib/hyperprobe/protos/agent_services_pb.rb +31 -0
- data/lib/hyperprobe/protos/java_messages.rb +199 -0
- data/lib/hyperprobe/protos.rb +14 -0
- data/lib/hyperprobe/railtie.rb +22 -0
- data/lib/hyperprobe/version.rb +5 -0
- data/lib/hyperprobe-agent.rb +3 -0
- data/lib/hyperprobe.rb +282 -0
- data/transport/README.md +98 -0
- data/transport/THIRD_PARTY_NOTICES.md +57 -0
- data/transport/pom.xml +78 -0
- data/transport/src/main/java/co/hyperprobe/transport/GrpcTransport.java +118 -0
- data/transport/src/main/java/co/hyperprobe/transport/ProtoCodec.java +43 -0
- metadata +117 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 22d2884311f7185515158c9deb20d5e0586212ac776858419509f801aa1cddff
|
|
4
|
+
data.tar.gz: 133d5beee8aee59156a920843a2fbf5347c8dc4958315e99eb7e1d122ee15fdc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e36e52c7502683657feb108b0b00aadb161cfed5d8d9157c45fe9b2df2b1f0199e9201df0b3b01fa687e85c1b4e43593ae4917bf1741d18a049ceaf0e841d642
|
|
7
|
+
data.tar.gz: 880c5c03b040ac486885f7fe3393b6ad49532fb5057815fec1c40b03fd997f046bd266bcc1d3260ea36eaa0633e61926270b2a3bbcbaf0df4053ecff46861837
|
data/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Hyperprobe Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hyperprobe. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software and its associated documentation are proprietary to Hyperprobe.
|
|
6
|
+
No permission is granted to use, copy, modify, distribute, sublicense, or
|
|
7
|
+
create derivative works except as expressly authorized under a written
|
|
8
|
+
commercial license agreement with Hyperprobe.
|
|
9
|
+
|
|
10
|
+
A valid commercial license from Hyperprobe is required to operate this
|
|
11
|
+
software.
|
|
12
|
+
|
|
13
|
+
For licensing inquiries, visit https://hyperprobe.co/.
|
data/README.md
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# HyperProbe Ruby Agent
|
|
2
|
+
|
|
3
|
+
Live snapshots, dynamic logs, counters, metrics, and durations for Ruby applications, without debugger breakpoints that suspend execution. Probe evaluation still runs on application threads and has overhead; quotas, capture budgets, and the safety monitor limit instrumentation rather than guarantee zero impact.
|
|
4
|
+
|
|
5
|
+
## Runtime Compatibility
|
|
6
|
+
|
|
7
|
+
| Runtime | Supported baseline | Transport and requirements |
|
|
8
|
+
| :--- | :--- | :--- |
|
|
9
|
+
| CRuby / MRI | Ruby 3.0+ (tested 4.0.6) | Native `grpc` gem; zero launch flags needed |
|
|
10
|
+
| JRuby | JRuby 9.3+ on Java 11+ | Bundled grpc-java transport and private protobuf codec; launch profile below |
|
|
11
|
+
|
|
12
|
+
RubyGems selects the `ruby` or `java` artifact. The Java artifact requires Ruby
|
|
13
|
+
>= 2.6 and has no `google-protobuf`, native `grpc`, or `ffi` runtime dependencies.
|
|
14
|
+
It bundles grpc-java and patched `protobuf-java` 3.25.9 core in a shaded JAR.
|
|
15
|
+
An SDK-private Ruby adapter uses Java `DynamicMessage` with a generated descriptor
|
|
16
|
+
of the existing SDK protocol schema; it neither provides nor modifies the host's
|
|
17
|
+
`Google::Protobuf` module. Java `com.google.protobuf` names are relocated under
|
|
18
|
+
`co.hyperprobe.internal.google.protobuf`. MRI remains Ruby >= 3.0 with its existing
|
|
19
|
+
`google-protobuf` (>= 3.25, < 5) and `grpc` (>= 1.50, < 2) dependencies.
|
|
20
|
+
|
|
21
|
+
The Java artifact does not use `java.net.http.HttpClient` or `jar-dependencies`.
|
|
22
|
+
Loading or connecting with the installed Java gem does not download dependencies;
|
|
23
|
+
Maven and a JDK are needed only to build it from source. Normal gem installation
|
|
24
|
+
still needs access to the selected artifacts and application dependencies.
|
|
25
|
+
Clean packaged Bundler specs passed on JRuby 9.3, 9.4, and 10.1. Choose a JDK
|
|
26
|
+
supported by your JRuby version too.
|
|
27
|
+
|
|
28
|
+
JRuby must retain line events and correct bindings after JIT compilation.
|
|
29
|
+
`--debug` alone is insufficient; use this complete process-start profile:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
jruby --debug \
|
|
33
|
+
-J-Djruby.ir.passes=AddCallProtocolInstructions,AddMissingInitsPass \
|
|
34
|
+
-J-Djruby.ir.jit.passes=AddCallProtocolInstructions,AddMissingInitsPass app.rb
|
|
35
|
+
# Or set before launching the application:
|
|
36
|
+
JRUBY_OPTS="--debug -J-Djruby.ir.passes=AddCallProtocolInstructions,AddMissingInitsPass -J-Djruby.ir.jit.passes=AddCallProtocolInstructions,AddMissingInitsPass" jruby app.rb
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For containers using the JRuby launcher, put the same complete profile in
|
|
40
|
+
`JRUBY_OPTS` before process startup. Use the JVM properties shown here rather
|
|
41
|
+
than generic `-Xir...` options, which older JRuby 9.3 direct `Main` rejects.
|
|
42
|
+
Direct `java -jar` / `org.jruby.Main` does not honor `JRUBY_OPTS`; put all JVM
|
|
43
|
+
properties before `-jar`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
java -Djruby.debug.fullTrace=true \
|
|
47
|
+
-Djruby.ir.passes=AddCallProtocolInstructions,AddMissingInitsPass \
|
|
48
|
+
-Djruby.ir.jit.passes=AddCallProtocolInstructions,AddMissingInitsPass \
|
|
49
|
+
-jar /path/to/jruby-complete.jar app.rb
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
JRuby 9.3 on Java 17 also requires standard runtime I/O compatibility opens:
|
|
53
|
+
`-J--add-opens=java.base/sun.nio.ch=ALL-UNNAMED` and
|
|
54
|
+
`-J--add-opens=java.base/java.io=ALL-UNNAMED` with the JRuby launcher. For direct
|
|
55
|
+
Java, use `--add-opens=java.base/sun.nio.ch=ALL-UNNAMED` and
|
|
56
|
+
`--add-opens=java.base/java.io=ALL-UNNAMED` before `-jar`. These are explicit
|
|
57
|
+
JRuby/JDK runtime requirements, not settings silently changed by the SDK.
|
|
58
|
+
|
|
59
|
+
`HyperProbe.start` checks the actual effective
|
|
60
|
+
`Java::OrgJruby::RubyInstanceConfig.FULL_TRACE_ENABLED` Java field (dot access),
|
|
61
|
+
plus `Options::IR_COMPILER_PASSES.load` and `Options::IR_JIT_PASSES.load` under
|
|
62
|
+
`Java::OrgJrubyUtilCli`. Both pass lists must equal the lists above. Environment
|
|
63
|
+
text or a system-property string alone is not proof that tracing is enabled.
|
|
64
|
+
Unsafe profiles disable SDK startup instead of changing process configuration.
|
|
65
|
+
|
|
66
|
+
This profile keeps JIT enabled without method exclusions, but changes
|
|
67
|
+
process-wide optimization passes. Benchmark your own application
|
|
68
|
+
before deployment; see the [transport README](transport/README.md).
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
Add to your application's `Gemfile`, then run `bundle install`:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
gem 'hyperprobe-agent'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Quickstart
|
|
79
|
+
|
|
80
|
+
Create an application initializer, for example `hyperprobe.rb`:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
require 'hyperprobe'
|
|
84
|
+
|
|
85
|
+
HyperProbe.start(
|
|
86
|
+
service_id: '<service-uuid-from-dashboard>',
|
|
87
|
+
environment: ENV['HYPERPROBE_ENVIRONMENT'] || 'production',
|
|
88
|
+
broker_url: 'https://logger.app.hyperprobe.co',
|
|
89
|
+
commit_sha: ENV['GIT_COMMIT']
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Require it early in your application entrypoint:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
require_relative 'hyperprobe'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use the service ID, environment, broker URL, and commit SHA that identify your actual deployment. The SHA must match the deployed source; missing, blank, or `unknown` commit values prevent startup. For the repository's local demo, retain its existing service and broker URL rather than replacing them with these placeholders or another endpoint.
|
|
100
|
+
|
|
101
|
+
In ordinary (non-Lambda) mode, the first probe sync starts asynchronously on a background thread without waiting for the periodic interval. `HyperProbe.start` does not wait for broker connectivity or probe installation; early requests can run before probes arrive. Agent-owned load, startup, sync, capture, and shutdown failures are isolated from application code, not exposed as public failure exceptions. Host signals and explicit process termination (`SignalException` and `SystemExit`) retain their normal behavior; the agent does not cancel application shutdown. Startup returns an agent on success or `nil` when disabled, deferred, or unsuccessful; some failures produce diagnostics, and others are silent. A successful return is not proof of connectivity or telemetry delivery.
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
Explicit options take precedence over environment variables. The server can update capture/redaction configuration, and per-probe capture options can override global capture limits within hard caps.
|
|
106
|
+
|
|
107
|
+
| Variable | Meaning | Default |
|
|
108
|
+
| :--- | :--- | :--- |
|
|
109
|
+
| `HYPERPROBE_SERVICE_ID` | Service UUID | Required |
|
|
110
|
+
| `HYPERPROBE_ENVIRONMENT` | Deployment environment | Required |
|
|
111
|
+
| `HYPERPROBE_BROKER_URL` | Broker gRPC endpoint | Required |
|
|
112
|
+
| `GIT_COMMIT` / `HYPERPROBE_COMMIT_SHA` | Deployment commit SHA, in that precedence order | Required |
|
|
113
|
+
| `HYPERPROBE_DISABLED` | Set to `YES` to disable startup | Unset |
|
|
114
|
+
| `HYPERPROBE_DISABLE_SAFE_EVALUATION` | Set to `true` to allow unrestricted Ruby execution in probe expressions (opt-in) | `false` |
|
|
115
|
+
| `HYPERPROBE_SYNC_INTERVAL_MS` | Periodic probe sync interval | `60000` |
|
|
116
|
+
| `HYPERPROBE_FLUSH_INTERVAL_MS` | Telemetry batch flush interval | `1000` |
|
|
117
|
+
| `HYPERPROBE_RPC_TIMEOUT_SEC` | Broker RPC deadline | `10` |
|
|
118
|
+
| `HYPERPROBE_MAX_QUEUE_SIZE` | Maximum queued telemetry events | `100` |
|
|
119
|
+
| `HYPERPROBE_HITS_PER_SEC` | Probe evaluation quota | `10` |
|
|
120
|
+
| `HYPERPROBE_BANDWIDTH_KB_PER_SEC` | Telemetry bandwidth quota | `1024` |
|
|
121
|
+
| `HYPERPROBE_MAX_LAG_MS` | Thread-lag threshold (MRI only; ignored on JRuby) | `50` |
|
|
122
|
+
| `HYPERPROBE_PAUSE_BUDGET_MS` | Probe-handler time budget per second (MRI only; ignored on JRuby) | `15` |
|
|
123
|
+
| `HYPERPROBE_COOLDOWN_SEC` | Safety cooldown duration | `10` |
|
|
124
|
+
| `HYPERPROBE_MAX_OBJECT_DEPTH` | Object recursion depth | `3` |
|
|
125
|
+
| `HYPERPROBE_MAX_ARRAY_LENGTH` | Items captured per sequence | `3` |
|
|
126
|
+
| `HYPERPROBE_MAX_OBJECT_PROPERTIES` | Properties captured per object | `50` |
|
|
127
|
+
| `HYPERPROBE_MAX_STRING_LENGTH` | Snapshot string byte limit | `1024` |
|
|
128
|
+
| `HYPERPROBE_STACK_FRAME_DEPTH` | Number of frames, including the hit frame | `3` |
|
|
129
|
+
| `HYPERPROBE_REDACT_KEYS` | Comma-separated restricted patterns | `password,secret,token,authorization,cookie,key,signature` |
|
|
130
|
+
| `HYPERPROBE_REDACT_VALUES` | Comma-separated restricted patterns | Empty |
|
|
131
|
+
|
|
132
|
+
Use `capture_closures: true` as a start option (or the corresponding server/probe setting) to opt into closure capture. There is no `HYPERPROBE_CAPTURE_CLOSURES` environment option. Fork deferral uses the Ruby keyword `defer_start: true`.
|
|
133
|
+
|
|
134
|
+
Use HTTPS for TLS. Raw `host:port` and `http://host:port` use plaintext gRPC. Do not add a trailing slash or query parameters. JRuby uses the JVM trust store and hostname verification; private CAs should be configured with standard JVM trust-store options, not disabled certificate verification.
|
|
135
|
+
|
|
136
|
+
### Runtime Safety
|
|
137
|
+
|
|
138
|
+
MRI retains timing-based safety: repeated safety-thread lag or a cumulative
|
|
139
|
+
probe-handler time budget breach can suspend instrumentation.
|
|
140
|
+
|
|
141
|
+
JRuby uses JVM heap safety, matching the Java SDK's memory thresholds. Heap is
|
|
142
|
+
sampled every 5 seconds, starting 5 seconds after monitor startup. Available
|
|
143
|
+
headroom is `(freeMemory + maxMemory - totalMemory) / maxMemory`, including
|
|
144
|
+
uncommitted heap capacity, not free system RAM. Below 15% is YELLOW (warning);
|
|
145
|
+
below 5% is RED (suspend probes). At exactly 5% health is YELLOW, and at exactly
|
|
146
|
+
15% it is GREEN. These heap thresholds are fixed.
|
|
147
|
+
|
|
148
|
+
On JRuby, probe-handler time budget breaches and safety-thread lag do not emit
|
|
149
|
+
logs, change heap health, or suspend probes. Timing measurements remain internal;
|
|
150
|
+
the lag and pause-budget options have no effect on JRuby safety. Handler elapsed
|
|
151
|
+
time is not a JVM-wide pause or a complete measure of instrumentation overhead.
|
|
152
|
+
|
|
153
|
+
Both runtimes resume suspended probes only after the configured cooldown has
|
|
154
|
+
elapsed and health is GREEN. Hit/bandwidth quotas, capture limits, redaction,
|
|
155
|
+
bounded queues, and exception isolation remain enabled. Ephemeral Lambda mode
|
|
156
|
+
continues to bypass the background safety monitor.
|
|
157
|
+
|
|
158
|
+
JRuby's memory-only enforcement does not bound capture latency, prevent every
|
|
159
|
+
out-of-memory failure, or undo the process-wide tracing profile. Benchmark
|
|
160
|
+
application overhead before production use.
|
|
161
|
+
|
|
162
|
+
## Expressions
|
|
163
|
+
|
|
164
|
+
By default, conditions, watches, log placeholders, metrics, and duration correlation expressions use a small, bounded interpreter rather than Ruby `eval` or `Binding#eval`. Safe mode validates the complete expression, including branches that would be short-circuited.
|
|
165
|
+
|
|
166
|
+
Supported syntax in safe mode:
|
|
167
|
+
|
|
168
|
+
- Local variable names; `nil`, `true`, `false`, numeric, ordinary quoted string, and simple symbol literals; array/hash literals and parentheses.
|
|
169
|
+
- Basic arithmetic `+ - * / %`, unary `+ -`, primitive comparisons `== != < <= > >=`, boolean `! not && || and or`, and the ternary `condition ? a : b`.
|
|
170
|
+
- String concatenation and ordered string comparisons on exact built-in strings. Numeric arithmetic requires exact built-in integers/floats; equality requires primitives, not arbitrary objects or containers.
|
|
171
|
+
- `.length` / `.size` (optionally with empty parentheses) on exact built-in `String`, `Array`, or `Hash` values only.
|
|
172
|
+
- A single integer subscript on exact built-in arrays/strings; primitive-key subscripts on small exact built-in hashes. Hash lookup scans without calling default procs or application `hash`/`eql?` hooks; identity hashes and unsupported keys are rejected.
|
|
173
|
+
|
|
174
|
+
Examples: `total > 100 && items.length > 0`, `items[0]`, `payload[:status]`, `count + 1`. Logs accept `${count}` and `#{count}` placeholders using this same grammar, not Ruby string interpolation execution.
|
|
175
|
+
|
|
176
|
+
In safe mode, arbitrary method calls and getters such as `user.name`, `record.save`, `object.to_s`, and `java_object.getName()` are forbidden. So are constants, `self`, instance/global/class variable access, assignments, blocks, loops, backticks, regex literals, ranges, exponentiation, interpolated string literals, and general function calls. An object's presence in a watch does not authorize calling methods on it: snapshot serialization handles it separately under the rules below.
|
|
177
|
+
|
|
178
|
+
Expressions are limited to 256 source bytes, 128 compiler nodes, nesting depth 32, and 512 interpreter steps. Evaluated strings are limited to 4096 bytes and integers to 1024 bits. Hash lookups and log-rendered collections are limited to 128 items; MRI also rejects hash lookups whose storage exceeds 131072 bytes. Log templates/output have a 16384-byte bound and at most 32 placeholders, with rendered values limited to 4096 bytes. Unsupported or over-budget work becomes an evaluation/capture error or is skipped at the agent boundary; it does not gain access to general Ruby execution.
|
|
179
|
+
|
|
180
|
+
### Unrestricted Evaluation (Opt-In)
|
|
181
|
+
|
|
182
|
+
On MRI and JRuby, enable method calls and ordinary Ruby execution with:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
export HYPERPROBE_DISABLE_SAFE_EVALUATION=true
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Alternatively, add `disable_safe_evaluation: true` (or `disableSafeEvaluation: true`)
|
|
189
|
+
to your existing `HyperProbe.start` options. Only boolean `true` or a trimmed,
|
|
190
|
+
case-insensitive `"true"` string opts in. Explicit options take precedence over
|
|
191
|
+
the environment, including `false`; the agent resolves this setting at startup.
|
|
192
|
+
Individual probes and broker capture settings cannot enable it.
|
|
193
|
+
|
|
194
|
+
This bypasses AST validation and the restricted interpreter for conditions,
|
|
195
|
+
watches, log placeholders, metrics, and duration correlations. Expressions run
|
|
196
|
+
through `Binding#eval` in the probe-hit frame. A warning is emitted once at startup
|
|
197
|
+
when safe evaluation is disabled.
|
|
198
|
+
|
|
199
|
+
**Only enable this for trusted probes.** Expressions can modify application
|
|
200
|
+
state, perform I/O, block, or terminate the process. There is no sandbox or
|
|
201
|
+
execution timeout. Source/template size limits, output formatting and metric
|
|
202
|
+
type checks, redaction, capture limits, and quotas remain, but they do not bound
|
|
203
|
+
or undo the work performed by arbitrary Ruby code. Log placeholders still use
|
|
204
|
+
the existing `${...}` / `#{...}` template parser and bounded value formatter.
|
|
205
|
+
|
|
206
|
+
## Snapshot Scope
|
|
207
|
+
|
|
208
|
+
On MRI, snapshots capture locals from the probe-hit frame and available Ruby caller frames, including blocks, up to `stack_frame_depth` (default 3, maximum 16). The MRI gem includes `debug_inspector` to obtain frame locations and bindings from the same native stack snapshot. Caller frames without an available binding retain empty local scopes. JRuby captures locals only from the probe-hit frame; its caller scopes remain empty.
|
|
209
|
+
|
|
210
|
+
MRI skips caller inspection when the visible stack exceeds 128 frames. Caller local-table extraction also rejects large compiled code trees before expanding them (32 instruction sequences and 16 KiB of aggregate native ISeq storage). Rejected callers retain empty scopes, including when closure capture is enabled. These are conservative admission checks, not hard native allocation or latency guarantees. Metadata caches use weak keys so reloaded code is not retained.
|
|
211
|
+
|
|
212
|
+
Closure variables are excluded by default. The probe-hit frame uses installation-time lexical analysis of the source file. The source must match the loaded code. Missing/unparseable/oversized source, ambiguous lines, or unsupported parser output make scope ownership unknown; in that case locals are omitted by default instead of capturing everything visible in the binding. Analysis is bounded to 512 KiB source, 20000 AST nodes/tokens, and depth 128. MRI caller frames use their compiled local-variable tables instead, avoiding source reads during capture. Only local-name metadata is cached, never live bindings.
|
|
213
|
+
|
|
214
|
+
With explicit `capture_closures: true`, visible binding variables not proven local are included in a separately marked `closure` scope. This includes unknown-scope variables; it is an intentional privacy opt-in, not a claim of perfect ownership inference. At most 128 visible binding names are considered. Watches evaluate against the hit binding independently of automatic closure exclusion, so an explicit watch can name a closure variable even when automatic closure capture is off.
|
|
215
|
+
|
|
216
|
+
## Serialization And Redaction
|
|
217
|
+
|
|
218
|
+
Serialization binds captured core implementations directly rather than dispatching application overrides. Ruby object instance variables can be captured using trusted core ivar enumeration and reads, without invoking getters, `inspect`, or custom `to_s`/`to_json`. Exceptions and unsupported built-in subclasses remain opaque summaries. Collection support includes exact built-in Ruby arrays, hashes, and sets, plus supported structs and `OpenStruct` storage.
|
|
219
|
+
|
|
220
|
+
On JRuby, only exact Java `ArrayList`, `LinkedList`, `LinkedHashMap`, and `LinkedHashSet` classes are traversed. Arbitrary Java objects, POJOs, exceptions, collection subclasses, `HashMap`, and `HashSet` are opaque. No POJO getters, Throwable accessors, `equals`, or `toString` are invoked for capture.
|
|
221
|
+
|
|
222
|
+
Snapshot limits are clamped to depth 16, 128 sequence items/properties, and 4096 string bytes. A shared capture budget across watches and captured scopes allows 512 nodes and 65536 bytes of conservative accounting, reserving 4096 bytes for exhaustion markers. Strings include allowance for JSON escaping in that accounting. Integers over 256 bits are summarized; cycles/repeated references produce reference markers. Limits produce summaries, truncation markers, or `[Capture budget exhausted]`, not an unlimited traversal.
|
|
223
|
+
|
|
224
|
+
The byte budget is not a promise that an entire serialized telemetry event is at most 64 KiB. Final JSON normalization has separate bounded headroom for wrappers/markers (depth 32, 1024 items/properties, 8192 string bytes, 2048 nodes, and 131072 accounting bytes); event metadata and generated markers also add overhead. Native Ruby ivar-name enumeration and binding-local enumeration materialize full name lists before bounded field reads. These operations are not hard wall-clock or allocation bounds. MRI hashes with storage over 131072 bytes are summarized to avoid sparse scans.
|
|
225
|
+
|
|
226
|
+
### Restricted Redaction Patterns
|
|
227
|
+
|
|
228
|
+
Key and value matching is case-insensitive and uses ordinary Ruby regex search semantics, not exact whole-string matching unless anchored. Patterns are deliberately restricted to avoid pathological regex execution:
|
|
229
|
+
|
|
230
|
+
- Each key/value pattern list permits at most 16 patterns, each at most 256 bytes before whitespace trimming.
|
|
231
|
+
- Unescaped literal characters are ASCII letters/digits, space, underscore, colon, `@`, comma, slash, and hyphen.
|
|
232
|
+
- Literal punctuation `. ^ $ | ? * + ( ) [ ] { } \ -` can be backslash-escaped.
|
|
233
|
+
- Nonempty literal alternatives can be separated by `|`. One optional `^` at the start and one optional `$` at the end are allowed. These retain Ruby line-anchor and alternation precedence semantics; they are not automatically grouped or converted to whole-string anchors.
|
|
234
|
+
- No repetition, groups, lookaround, character classes, backreferences, or escapes such as `\d`, `\s`, `\A`, and `\z` are allowed. An unescaped dot is not allowed.
|
|
235
|
+
|
|
236
|
+
Examples of accepted patterns: `password`, `token|secret`, `^authorization$`, `api\.key`. `.*secret.*`, `(token)+`, and `[0-9]+` are rejected. Environment variables split on commas; use an option array to include a literal comma in a pattern. Option lists replace defaults rather than extending them.
|
|
237
|
+
|
|
238
|
+
Invalid syntax, an oversized list/pattern, or invalid serializer pattern input fails closed: that entire key or value matcher becomes match-all, rather than dropping just the invalid pattern. Empty lists disable that matcher; whitespace-only patterns are ignored, disabling the matcher if none remain. Key matches replace the associated value with `[REDACTED Key]`; value matches replace matching string portions with `[REDACTED Value]`. Truncated or opaque keys are treated as sensitive when key redaction is enabled. With value redaction enabled, a truncated snapshot string is redacted rather than exposing an unchecked prefix. Dynamic logs apply value redaction to the evaluated text before display truncation and optional stdout emission; key redaction alone does not redact log text.
|
|
239
|
+
|
|
240
|
+
Redaction is not secret discovery: configure value patterns for sensitive string content and review what probes capture. Numeric values are not converted into strings for value-pattern matching.
|
|
241
|
+
|
|
242
|
+
Custom `set_trace_id` callbacks and installed APM integrations are trusted application integrations, not sandboxed probe expressions. They must be nonblocking and side-effect-free. The SDK cannot safely preempt arbitrary application/native code or guarantee recovery from process-wide resource exhaustion.
|
|
243
|
+
|
|
244
|
+
## Puma / Unicorn Forking
|
|
245
|
+
|
|
246
|
+
For MRI preloaded multi-worker servers, defer startup in the parent **before any active agent/client has been created**:
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
# Preloaded application initializer
|
|
250
|
+
require 'hyperprobe'
|
|
251
|
+
|
|
252
|
+
HyperProbe.start(
|
|
253
|
+
service_id: ENV['HYPERPROBE_SERVICE_ID'],
|
|
254
|
+
environment: ENV['HYPERPROBE_ENVIRONMENT'],
|
|
255
|
+
broker_url: ENV['HYPERPROBE_BROKER_URL'],
|
|
256
|
+
commit_sha: ENV['GIT_COMMIT'],
|
|
257
|
+
defer_start: true
|
|
258
|
+
)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Deferral validates and stores configuration, returns `nil`, and creates no agent, broker client, or background loops in the parent. Start a fresh child-owned agent in each worker:
|
|
262
|
+
|
|
263
|
+
```ruby
|
|
264
|
+
# config/puma.rb
|
|
265
|
+
preload_app!
|
|
266
|
+
on_worker_boot do
|
|
267
|
+
HyperProbe.after_fork
|
|
268
|
+
end
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
For Unicorn, use its worker hook instead:
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
# config/unicorn.rb
|
|
275
|
+
preload_app true
|
|
276
|
+
after_fork do |_server, _worker|
|
|
277
|
+
HyperProbe.after_fork
|
|
278
|
+
end
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
If an active native gRPC client was inherited, `after_fork` disables the inherited agent in the child without calling, closing, or replacing that client. That child cannot restart HyperProbe, even via another `start` call. Restart the worker deployment with deferred initialization; adding `after_fork` alone to an already active preloaded agent is not a recovery mechanism. The parent is unaffected. JRuby uses threaded workers rather than this MRI fork path and does not need `after_fork`.
|
|
282
|
+
|
|
283
|
+
## AWS Lambda
|
|
284
|
+
|
|
285
|
+
For a file named `app.rb`, configure the Lambda handler as `app.lambda_handler`:
|
|
286
|
+
|
|
287
|
+
```ruby
|
|
288
|
+
require 'hyperprobe'
|
|
289
|
+
|
|
290
|
+
HYPERPROBE_HANDLER = HyperProbe.wrap_lambda(
|
|
291
|
+
service_id: ENV['HYPERPROBE_SERVICE_ID'],
|
|
292
|
+
environment: ENV['HYPERPROBE_ENVIRONMENT'],
|
|
293
|
+
broker_url: ENV['HYPERPROBE_BROKER_URL'],
|
|
294
|
+
commit_sha: ENV['GIT_COMMIT']
|
|
295
|
+
) do |event:, context:|
|
|
296
|
+
{ statusCode: 200, body: 'OK' }
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def lambda_handler(event:, context:)
|
|
300
|
+
HYPERPROBE_HANDLER.call(event: event, context: context)
|
|
301
|
+
end
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Ephemeral Lambda mode syncs before the handler and attempts a flush afterward, subject to remaining execution time. Options `lambda_sync_timeout_ms` (default `2000`) and `flush_timeout_ms` (default `1500`) bound these RPCs. Instrumentation failure must not replace the handler's result or mask its exception; application exceptions still propagate normally. Local emulators (`IS_OFFLINE=true` or `AWS_SAM_LOCAL=true`) use ordinary background mode.
|
|
305
|
+
|
|
306
|
+
## Development And Release
|
|
307
|
+
|
|
308
|
+
From `agents/ruby-sdk`, with MRI 3.0+ and Bundler available:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
bundle install
|
|
312
|
+
bundle exec rake transport:build
|
|
313
|
+
bundle exec rspec
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
The transport build requires Maven and JDK 11+ (`mvn`, `java`, and `javac`) and can download build dependencies. Build it before the full suite because packaging/JRuby interoperability tests need the JAR. To build only the MRI gem without Maven/Java, use `bundle exec rake build:ruby`; `bundle exec rake build` produces both platforms.
|
|
317
|
+
|
|
318
|
+
Prepare JRuby's own gem environment with `rspec` (no Google Ruby protobuf gem is
|
|
319
|
+
needed), then run:
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
HYPERPROBE_MRI_RUBY="$(command -v ruby)" jruby --debug \
|
|
323
|
+
-J-Djruby.ir.passes=AddCallProtocolInstructions,AddMissingInitsPass \
|
|
324
|
+
-J-Djruby.ir.jit.passes=AddCallProtocolInstructions,AddMissingInitsPass -S rspec
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
That MRI executable must have `grpc`, `google-protobuf`, and `rspec` installed for
|
|
328
|
+
the local interop server. Resolve development dependencies for the chosen runtime
|
|
329
|
+
rather than reusing a frozen MRI lockfile on JRuby; the legacy demo bundle for
|
|
330
|
+
Ruby < 3.2 uses Bundler 2.4.22. The release script supplies the
|
|
331
|
+
complete required JRuby profile and runs JRuby tests without `bundle exec`, so
|
|
332
|
+
RSpec must be discoverable by that interpreter, not only installed into an
|
|
333
|
+
MRI-specific Bundler path. Set `HYPERPROBE_JRUBY=/path/to/jruby` to select it.
|
|
334
|
+
|
|
335
|
+
Regenerate Ruby protobuf/gRPC stubs on MRI with the development bundle enabled:
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
bundle exec rake proto:generate
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
`grpc-tools` is a development/test Gemfile dependency only, not an installed SDK runtime dependency. Generated Ruby stubs and the Java adapter's schema descriptor are tracked; inspect their diff after generation. Maven output, bundled build JARs, gem artifacts, and Ruby LSP caches are ignored.
|
|
342
|
+
|
|
343
|
+
The SDK release validator builds both artifacts, runs MRI and JRuby tests, and validates package platform/dependency/JAR metadata:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
bash publish_rubygems.sh --dry-run
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
This does not publish, but does build, may fetch Maven dependencies, and may update `Gemfile.lock`. `--version VERSION` (or the `VERSION` environment variable) also **writes `lib/hyperprobe/version.rb`**, including in a dry run; the standalone publisher does not restore these files. Publishing with `--yes` uploads both `pkg/hyperprobe-agent-VERSION.gem` and `pkg/hyperprobe-agent-VERSION-java.gem` at the same version. A failure on the second upload can leave the first artifact published; inspect both platforms before retrying. `--skip-tests` bypasses both runtime suites and is not a substitute for release validation.
|
|
350
|
+
|
|
351
|
+
The root `build.sh` requires a clean worktree, checks both Ruby runtimes plus Maven/JDK tools before changing versions, and installs the MRI bundle before invoking the SDK validator. Bundler install output is suppressed to avoid leaking authenticated source URLs. On successful root builds, it restores only Ruby's tracked `lib/hyperprobe/version.rb` and `Gemfile.lock` when the Ruby version stage was entered. Failed builds retain the existing rollback behavior and can leave temporary versions/lockfile changes for inspection; unrelated rollback logic is unchanged. Do not use root `build.sh` for a Ruby-only check: it also builds other SDKs, Docker, and the extension, and `SHOULD_PUBLISH=yes` performs remote writes.
|
|
352
|
+
|
|
353
|
+
Offline source/syntax checks that do not run the root build or publisher:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
bash -n ../../build.sh
|
|
357
|
+
bash -n publish_rubygems.sh
|
|
358
|
+
ruby -c Gemfile
|
|
359
|
+
ruby -c Rakefile
|
|
360
|
+
ruby -rrspec/core -e 'exit RSpec::Core::Runner.run(ARGV)' spec/release_integration_spec.rb
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The focused release checks require RSpec already installed, but not a built transport or a running demo/broker. Keep release credentials out of command arguments and logs; use the publisher's documented credential environment/file mechanisms.
|
|
364
|
+
|
|
365
|
+
## Local Demo Verification
|
|
366
|
+
|
|
367
|
+
Keep the existing service ID and broker URL in `agents/ruby-demo-app/start.sh`. Start the backend from the repository root with `pnpm run dev`. From `agents/ruby-demo-app`, install and run one runtime at a time:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
bundle install
|
|
371
|
+
bash start.sh
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
For JRuby, use the `--jruby` flag:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
bash start.sh --jruby
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
To test a checkout event that triggers live probes, send a request:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
curl -X POST http://localhost:4003/checkout \
|
|
384
|
+
-H "Content-Type: application/json" \
|
|
385
|
+
-d '{"userId":"test-user","items":[{"price":10,"quantity":1},{"price":200,"quantity":1}]}'
|
|
386
|
+
```
|