|
| 1 | +# Ruby/solarwinds_apm |
| 2 | + |
| 3 | +In the `solarwinds_apm` repository: |
| 4 | + |
| 5 | +- Gem is an OpenTelemetry Ruby distribution (>= 6.0.0). |
| 6 | +- MRI Ruby 3.x required. Versions < 7.0.0 are Linux-only (no‑op elsewhere). |
| 7 | +- Add `gem 'solarwinds_apm'` at the END of the Gemfile. Do not reorder earlier app dependencies to accommodate it. |
| 8 | +- If not using `Bundler.require`, explicitly `require 'solarwinds_apm'` only after all gems needing instrumentation are loaded. |
| 9 | +- A service key is REQUIRED: set `SW_APM_SERVICE_KEY=<api_token:service_name>` (or configure `:service_key`). The service name (after the colon) becomes `OTEL_SERVICE_NAME`. |
| 10 | +- Do NOT manually call `OpenTelemetry::SDK.configure` to replace the global provider; that breaks distribution customizations. |
| 11 | +- The gem auto-initializes TracerProvider + MeterProvider. Replacing globals disables tracing/export. |
| 12 | +- Treat startup readiness: short‑lived scripts should call `SolarWindsAPM::API.solarwinds_ready?(wait_ms)` before generating spans. |
| 13 | +- Prefer vendor helpers (`SolarWindsAPM::API.in_span`, `add_tracer`) over duplicating tracer acquisition boilerplate. |
| 14 | +- Deprecated metric helpers (`increment_metric`, `summary_metric`) are no‑ops >= 7.0.0: use standard OTel Metrics API instead. |
| 15 | + |
| 16 | +## Development Environment |
| 17 | + |
| 18 | +- Work inside the Linux dev container (`bundle exec rake docker_dev`); host Ruby can also be used for running test or simple experiment with irb. |
| 19 | +- Use `rbenv` to select a Ruby already installed there; set globally (`rbenv global <version>`). |
| 20 | +- Run `bundle install` after selecting Ruby. |
| 21 | +- Build gem: `bundle exec rake build_gem` (outputs `builds/solarwinds_apm-<version>.gem`) or `gem build solarwinds_apm.gemspec` (outputs `solarwinds_apm-<version>.gem`). |
| 22 | +- Load gem interactively: |
| 23 | + ``` |
| 24 | + SW_APM_SERVICE_KEY=<api-token:service-name> irb -r solarwinds_apm |
| 25 | + ``` |
| 26 | + |
| 27 | +## Linting & Formatting |
| 28 | + |
| 29 | +- Run `bundle exec rake rubocop` before committing; fix issues recorded in `rubocop_result.txt`. |
| 30 | +- Do not silence cops without justification; prefer code changes over blanket disables. |
| 31 | +- Keep public API docs accurate when changing method signatures (update YARD/RubyDoc comments if present). |
| 32 | + |
| 33 | +## Manual Instrumentation Conventions |
| 34 | + |
| 35 | +- Acquire tracers via: `Tracer = OpenTelemetry.tracer_provider.tracer(ENV['OTEL_SERVICE_NAME'])`; do not hardcode service names. |
| 36 | +- Use `SolarWindsAPM::API.in_span('logical.operation', attributes: {...})` for convenience. |
| 37 | +- Method instrumentation: |
| 38 | + ``` |
| 39 | + include SolarWindsAPM::API::Tracer |
| 40 | + add_tracer :method_name, 'span.name', { attributes: { 'key' => 'value' }, kind: :consumer } |
| 41 | + ``` |
| 42 | +- Keep span names concise, lowercase with dots (`component.action`). |
| 43 | +- Prefer attribute keys matching semantic conventions where applicable. |
| 44 | +- Use `SolarWindsAPM::API.current_trace_info` for logging correlation IDs (avoid reimplementing W3C header parsing). |
| 45 | +- Only set a custom transaction name when the framework default is ambiguous; last assignment wins. |
| 46 | + |
| 47 | +## Metrics Conventions |
| 48 | + |
| 49 | +- Create instruments through the global meter: |
| 50 | + ``` |
| 51 | + Meter = OpenTelemetry.meter_provider.meter('solarwinds_apm.custom') |
| 52 | + counter = Meter.create_counter('operation.count', description: 'Count of X', unit: '{operation}') |
| 53 | + counter.add(1, attributes: { 'foo' => 'bar' }) |
| 54 | + ``` |
| 55 | +- Choose units and names consistent with OTel semantic conventions; use `{unit}` braces for ad‑hoc units. |
| 56 | + |
| 57 | +## Readiness & Startup |
| 58 | + |
| 59 | +- For batch/CLI tasks: |
| 60 | + ``` |
| 61 | + SolarWindsAPM::API.solarwinds_ready?(2000) || warn('Tracing not initialized in time') |
| 62 | + ``` |
| 63 | +- Avoid long blocking waits in web servers; readiness is mainly for short processes. |
| 64 | + |
| 65 | +## Tests |
| 66 | + |
| 67 | +- Full suite (default Ruby baseline): |
| 68 | + ``` |
| 69 | + bundle exec rake 'docker_tests[,,,APM_RUBY_TEST_KEY=<key>]' |
| 70 | + ``` |
| 71 | +- Alternate Ruby / platform example: |
| 72 | + ``` |
| 73 | + bundle exec rake 'docker_tests[3.2-alpine,,linux/amd64,APM_RUBY_TEST_KEY=<key>]' |
| 74 | + ``` |
| 75 | +- Interactive debugging: |
| 76 | + ``` |
| 77 | + bundle exec rake 'docker_tests[,false]' |
| 78 | + test/test_setup.sh |
| 79 | + APM_RUBY_TEST_KEY=<key> test/run_tests.sh |
| 80 | + ``` |
| 81 | +- Without container: |
| 82 | + ``` |
| 83 | + test/test_setup.sh |
| 84 | + ``` |
| 85 | +- Run a single file / example: |
| 86 | + ``` |
| 87 | + bundle exec ruby -I test test/opentelemetry/solarwinds_propagator_test.rb |
| 88 | + bundle exec ruby -I test test/opentelemetry/solarwinds_propagator_test.rb -n /trace_state_header/ |
| 89 | + ``` |
| 90 | +- Logs emitted to `log/` (bind mounted) — inspect when diagnosing failures. |
| 91 | + |
| 92 | +### Test Change Scope Guidelines |
| 93 | + |
| 94 | +- Pure doc / comment changes: no test run required (optional quick lint). |
| 95 | +- Instrumentation logic, exporter, propagator, or API changes: run baseline suite + at least one additional Ruby version. |
| 96 | +- Dependency version matrix adjustments: run all configured Ruby variants. |
| 97 | +- Failing tests before commit are not acceptable; do not mark flakey without issue reference. |
| 98 | + |
| 99 | +## Release / Build Checklist (Internal) |
| 100 | + |
| 101 | +1. Ensure rubocop passes. |
| 102 | +2. Ensure test suite green on primary supported Ruby versions. |
| 103 | +3. Build gem (`rake build_gem`) and verify `gem spec builds/solarwinds_apm-*.gem name version`. |
| 104 | +4. Smoke test with IRB + service key (span appears, no initialization warnings). |
| 105 | + |
| 106 | +## Common Pitfalls |
| 107 | + |
| 108 | +- Reinitializing OpenTelemetry -> lose customized processors/exporters. |
| 109 | +- Forgetting to append gem at end of Gemfile -> instrumentation may miss early‑loaded libraries. |
| 110 | +- Attempting to use deprecated custom metric helpers on >= 7.0.0 -> they are intentional no‑ops. |
| 111 | +- Not waiting in short‑lived scripts -> zero spans exported. |
| 112 | + |
| 113 | +## Quick API Reference |
| 114 | + |
| 115 | +| Task | Call | |
| 116 | +|------|------| |
| 117 | +| Create span | `SolarWindsAPM::API.in_span('name') { ... }` | |
| 118 | +| Instrument method | `add_tracer :m, 'span.m'` | |
| 119 | +| Current trace info | `SolarWindsAPM::API.current_trace_info` | |
| 120 | +| Ready check | `SolarWindsAPM::API.solarwinds_ready?(1500)` | |
| 121 | +| Custom transaction name | `SolarWindsAPM::API.set_transaction_name('custom')` | |
| 122 | +| Counter metric | `meter.create_counter('metric').add(1, attributes: {...})` | |
| 123 | + |
| 124 | +## Logging Correlation |
| 125 | + |
| 126 | +Use `trace = SolarWindsAPM::API.current_trace_info` then include `trace.trace_id` and `trace.span_id` in structured logs; do not attempt to read internal span context fields directly. |
| 127 | + |
| 128 | +## Performance Notes |
| 129 | + |
| 130 | +- Keep attribute key/value counts minimal in high-frequency spans. |
| 131 | +- Avoid excessive synchronous metric instrument creation; cache instruments globally. |
| 132 | +- Prefer method instrumentation (`add_tracer`) over wrapping public call sites repeatedly. |
| 133 | + |
| 134 | +## Do Not |
| 135 | + |
| 136 | +- Do not rename public module namespaces (`SolarWindsAPM::API`). |
| 137 | +- Do not vendor or fork OpenTelemetry SDK inside this repo. |
| 138 | +- Do not add hidden global state affecting sampler/exporter without documentation. |
| 139 | +- Do not bypass provided readiness API with ad‑hoc sleep loops. |
| 140 | + |
| 141 | +## When in Doubt |
| 142 | + |
| 143 | +- Follow existing file-local style. |
| 144 | +- Mirror OTel semantic conventions. |
| 145 | +- Keep spans short-lived and purposeful. |
0 commit comments