instana 2.9.0 → 2.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52139723659935eb43ab59f60e5c0f7305d860d5457c9545d395f9e4576fc604
4
- data.tar.gz: 223dcdbe22af35ceed817411b850114f9cf3e7eb5fce3b3fc1b4f5ffa29c00e1
3
+ metadata.gz: 651f82ecb4bf26f63225bb880fff4028fca72f641eeac492b3bbb85d4c0be0d2
4
+ data.tar.gz: b311f0c13d53cf688247b580f018a57a62569eb0a1fa5c25d8fac1a8939ffdf0
5
5
  SHA512:
6
- metadata.gz: 0743a4a453182c90abb623189d4a6e5105cd03e3823b3b6cf9ee5269c80f14652d21bfc5481c914457a79a8a52633c718cfd1581d219122327996cb67c2b222f
7
- data.tar.gz: 767f9eb2f714e83e045f27086061ef11ab3511574b8636c87740595f871a0051ccc977341e0e523f174d4f4d5a4575fbcd3d8b87ed1ab26b347198fbdb3d4d94
6
+ metadata.gz: 29c82de02af9e09ebaa84da9429d70867f1f8eb3c397836d50a78eaddecf8339a6b20e809bbdadc22dbd0f5f8672e766d96aff1c0802f9a08edefd7bccb0a1ae
7
+ data.tar.gz: e099c624a54ce5871010efad8ad469d8aebf0672cc7011a49bd26c9b448fcc6883eb58122cc9424f69757432b418b1a38fe332d78b1718c65ba60cae8958754e
data/README.md CHANGED
@@ -51,6 +51,179 @@ Although the gem has no configuration required for out of the box metrics and tr
51
51
 
52
52
  This Ruby gem provides a simple API for tracing and also supports [OpenTracing](http://opentracing.io/). See the [Ruby Tracing SDK](https://www.ibm.com/docs/en/instana-observability/current?topic=ruby-tracing-sdk) and [OpenTracing](https://www.ibm.com/docs/en/instana-observability/current?topic=ruby-opentracing) pages for details.
53
53
 
54
+ ## OTLP Export
55
+
56
+ The `instana` gem supports exporting traces via the **OpenTelemetry Protocol (OTLP)**
57
+ directly to the Instana agent's OTLP receiver, bypassing the native agent trace format.
58
+ OTLP export is **opt-in** — the gem behaves identically to previous versions when
59
+ `INSTANA_TRACING_OTLP_ENABLED` is not set.
60
+
61
+ ### Overview
62
+
63
+ When OTLP export is enabled:
64
+ - Spans are converted from Instana's internal format to OTLP protobuf
65
+ - The converted spans are sent via HTTP/protobuf to the agent's OTLP port (`4318`)
66
+ - The endpoint is auto-derived from the same host where the Instana agent was discovered
67
+ - The native agent reporting path (`/com.instana.plugin.ruby/traces`) is replaced by OTLP
68
+
69
+ ### Quick Start
70
+
71
+ The only required change is setting one environment variable before starting your application:
72
+
73
+ ```bash
74
+ INSTANA_TRACING_OTLP_ENABLED=true bundle exec rails server
75
+ ```
76
+
77
+ Expected log output (with `INSTANA_LOG_LEVEL=debug`):
78
+ ```
79
+ [Instana] Using OTLP Exporter to export result code: 0
80
+ ```
81
+
82
+ Result code `0` = `SUCCESS`. No other configuration is needed — the endpoint is
83
+ automatically set to `http://<agent-host>:4318/v1/traces`.
84
+
85
+ ### Gem Dependencies
86
+
87
+ `opentelemetry-exporter-otlp` is a **runtime dependency** of the `instana` gem and
88
+ is installed automatically. No additional gems are required.
89
+
90
+ > **Note:** gRPC (`OTEL_EXPORTER_OTLP_PROTOCOL=grpc`) is not supported.
91
+ > Only `http/protobuf` is supported and tested.
92
+
93
+ ### Environment Variables
94
+
95
+ | Variable | Type | Default | Description |
96
+ |---|---|---|---|
97
+ | `INSTANA_TRACING_OTLP_ENABLED` | Boolean | `false` | Enables OTLP export. Valid values: `true`, `1`, `yes` |
98
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | String (URL) | auto-derived from agent host | **Base** endpoint URL. `/v1/traces` is appended automatically. |
99
+ | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | String (URL) | `<OTEL_EXPORTER_OTLP_ENDPOINT>/v1/traces` | **Fully-qualified** trace endpoint. Used as-is; overrides `OTEL_EXPORTER_OTLP_ENDPOINT`. |
100
+ | `OTEL_EXPORTER_OTLP_PROTOCOL` | String | `http/protobuf` | Transport protocol. Only `http/protobuf` is supported. |
101
+ | `OTEL_EXPORTER_OTLP_HEADERS` | String | — | Comma-separated `key=value` auth headers. e.g. `api-key=secret,x-tenant-id=myorg` |
102
+ | `OTEL_EXPORTER_OTLP_TIMEOUT` | Integer (ms) | `10000` | Maximum time to wait for export completion. |
103
+ | `OTEL_EXPORTER_OTLP_COMPRESSION` | String | — | Compression algorithm. Valid value: `gzip`. |
104
+ | `OTEL_EXPORTER_OTLP_CERTIFICATE` | String (path) | — | Path to CA certificate file for TLS verification. |
105
+ | `OTEL_EXPORTER_OTLP_CLIENT_KEY` | String (path) | — | Path to client private key file for mutual TLS. |
106
+ | `OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE` | String (path) | — | Path to client certificate file for mutual TLS. |
107
+ | `OTEL_EXPORTER_OTLP_INSECURE` | Boolean | `false` | Disable TLS certificate verification. |
108
+ | `OTEL_SEMCONV_STABILITY_OPT_IN` | String | `stable` | Semantic convention stability level. Valid values: `stable`, `development`. |
109
+
110
+ > **Endpoint precedence:** `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` > `OTEL_EXPORTER_OTLP_ENDPOINT` > auto-derived from agent host
111
+
112
+ ### YAML Configuration
113
+
114
+ When `INSTANA_CONFIG_PATH` points to a YAML file, OTLP settings are placed under
115
+ the `tracing.otlp` key. YAML configuration takes precedence over environment variables.
116
+
117
+ **Minimal example** (`config/instana.yml`):
118
+ ```yaml
119
+ tracing:
120
+ otlp:
121
+ enabled: true
122
+ ```
123
+
124
+ **Full example:**
125
+ ```yaml
126
+ tracing:
127
+ otlp:
128
+ enabled: true
129
+ endpoint: "http://localhost:4318/v1/traces"
130
+ protocol: "http/protobuf"
131
+ timeout: 10000
132
+ compression: "gzip"
133
+ headers:
134
+ x-instana-key: "YOUR_API_KEY"
135
+ x-tenant-id: "YOUR_TENANT"
136
+ certificate: "/path/to/ca-cert.pem"
137
+ client_key: "/path/to/client-key.pem"
138
+ client_certificate: "/path/to/client-cert.pem"
139
+ insecure: false
140
+ semconv_stability: "stable"
141
+ ```
142
+
143
+ Start the application:
144
+ ```bash
145
+ INSTANA_CONFIG_PATH=config/instana.yml bundle exec rails server
146
+ ```
147
+
148
+ ### Configuration Precedence
149
+
150
+ Settings are resolved in this order (highest wins):
151
+
152
+ ```
153
+ INSTANA_CONFIG_PATH YAML → Environment variables → Agent config → Defaults
154
+ ```
155
+
156
+ This means a YAML file always wins over env vars. Agent-provided config (pushed
157
+ via the Instana agent's discovery payload) is applied after env vars but can be
158
+ overridden by YAML or env vars set at startup.
159
+
160
+ ### TLS / mTLS
161
+
162
+ **Server-side TLS** (verify the collector's certificate):
163
+ ```bash
164
+ INSTANA_TRACING_OTLP_ENABLED=true \
165
+ OTEL_EXPORTER_OTLP_ENDPOINT=https://your-collector:4318/v1/traces \
166
+ OTEL_EXPORTER_OTLP_CERTIFICATE=/path/to/ca-cert.pem \
167
+ bundle exec rails server
168
+ ```
169
+
170
+ **Mutual TLS** (client certificate authentication):
171
+ ```bash
172
+ INSTANA_TRACING_OTLP_ENABLED=true \
173
+ OTEL_EXPORTER_OTLP_ENDPOINT=https://your-collector:4318/v1/traces \
174
+ OTEL_EXPORTER_OTLP_CERTIFICATE=/path/to/ca-cert.pem \
175
+ OTEL_EXPORTER_OTLP_CLIENT_KEY=/path/to/client-key.pem \
176
+ OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE=/path/to/client-cert.pem \
177
+ bundle exec rails server
178
+ ```
179
+
180
+ ### Troubleshooting
181
+
182
+ **OTLP export is not happening — spans go via the native path**
183
+
184
+ OTLP is disabled by default. Check that `INSTANA_TRACING_OTLP_ENABLED=true` is set
185
+ in the process environment before the gem initialises (i.e. before `require 'instana'`
186
+ or Rails boot). Verify with:
187
+ ```bash
188
+ INSTANA_LOG_LEVEL=debug bundle exec rails server
189
+ # Look for: "Using OTLP Exporter" vs "Using Instana Native Exporter"
190
+ ```
191
+
192
+ **`Failed to initialize OTLP exporter` in the log**
193
+
194
+ The exporter could not be constructed. Common causes:
195
+ - `OTEL_EXPORTER_OTLP_CERTIFICATE` path does not exist or is not readable
196
+ - `OTEL_EXPORTER_OTLP_CLIENT_KEY` / `OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE` path invalid
197
+ - An unsupported protocol (e.g. `grpc`) was set — only `http/protobuf` is supported
198
+
199
+ The gem falls back to `@otlp_exporter = nil` and logs an `ERROR` line with the
200
+ exception message. Check that message for the root cause.
201
+
202
+ **`result code: 1` (FAILURE) — spans not arriving**
203
+
204
+ The exporter initialised but the export request failed. Common causes:
205
+ - The Instana agent is not running or OTLP is not enabled on the agent side
206
+ - `OTEL_EXPORTER_OTLP_ENDPOINT` points to a wrong host or port
207
+ - `OTEL_EXPORTER_OTLP_ENDPOINT` was set to a base URL like `http://host:4318`
208
+ without `/v1/traces` — this was a known bug fixed in v2.9.0 (the sensor now
209
+ appends the path automatically)
210
+ - A firewall is blocking port `4318`
211
+
212
+ Verify the agent OTLP port is reachable:
213
+ ```bash
214
+ curl -s -o /dev/null -w "%{http_code}" \
215
+ -X POST http://localhost:4318/v1/traces \
216
+ -H "Content-Type: application/x-protobuf"
217
+ # 200 or 415 = port open; "connection refused" = agent OTLP not enabled
218
+ ```
219
+
220
+ **`HTTP request path is empty` exception (pre-v2.9.0)**
221
+
222
+ If you are on a version earlier than 2.9.0 and pass a base URL to
223
+ `OTEL_EXPORTER_OTLP_ENDPOINT` (without `/v1/traces`), the `opentelemetry-exporter-otlp`
224
+ gem crashes with `HTTP request path is empty`. Upgrade to v2.9.0 or later, or use
225
+ `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` with the full path instead.
226
+
54
227
  ## Documentation
55
228
 
56
229
  You can find more documentation covering supported components and minimum versions in the Instana [documentation portal](https://www.ibm.com/docs/en/instana-observability/current?topic=technologies-monitoring-ruby).
@@ -186,11 +186,12 @@ module Instana
186
186
 
187
187
  endpoint = resolve_otlp_endpoint(config[:endpoint], config[:config_source])
188
188
  opts = { endpoint: endpoint, timeout: config[:timeout] / 1000.0 }
189
- opts[:compression] = config[:compression] if config[:compression]
190
- opts[:headers] = config[:headers] if config[:headers]&.any?
191
- opts[:certificate_file] = config[:certificate] if config[:certificate]
192
- opts[:client_certificate_file] = config[:client_certificate] if config[:client_certificate]
193
- opts[:client_key_file] = config[:client_key] if config[:client_key]
189
+ opts[:compression] = config[:compression] if config[:compression]
190
+ opts[:headers] = config[:headers] if config[:headers]&.any?
191
+ opts[:certificate_file] = config[:certificate] if config[:certificate]
192
+ opts[:client_certificate_file] = config[:client_certificate] if config[:client_certificate]
193
+ opts[:client_key_file] = config[:client_key] if config[:client_key]
194
+ opts[:ssl_verify_mode] = OpenSSL::SSL::VERIFY_NONE if config[:insecure]
194
195
 
195
196
  @otlp_exporter = OpenTelemetry::Exporter::OTLP::Exporter.new(**opts)
196
197
  rescue StandardError => e
@@ -198,21 +199,32 @@ module Instana
198
199
  @otlp_exporter = nil
199
200
  end
200
201
 
201
- # Derive the OTLP endpoint from the discovered agent host when no explicit
202
- # endpoint has been configured (config_source == 'default').
203
202
  OTLP_DEFAULT_PORT = 4318
204
203
  OTLP_TRACES_PATH = '/v1/traces'.freeze
205
204
 
205
+ # Returns a fully-qualified OTLP traces endpoint.
206
+ #
207
+ # When config_source is 'default', builds the endpoint from the discovered
208
+ # agent host so OTLP traffic goes to the same host as metrics/traces.
209
+ #
210
+ # For user-supplied endpoints, appends '/v1/traces' when the URL has no
211
+ # path, because OTEL_EXPORTER_OTLP_ENDPOINT is a base URL and the
212
+ # opentelemetry-exporter-otlp gem only auto-appends the path when no
213
+ # endpoint is given at all.
206
214
  def resolve_otlp_endpoint(endpoint, config_source)
207
- return endpoint unless config_source == 'default'
215
+ if config_source == 'default'
216
+ agent_host = @client&.host
217
+ return endpoint unless agent_host
208
218
 
209
- # Use the host that was discovered by HostAgentLookup (same host the
210
- # metrics/traces client is already talking to) and append the standard
211
- # OTLP HTTP port and traces path.
212
- agent_host = @client&.host
213
- return endpoint unless agent_host
219
+ return "http://#{agent_host}:#{OTLP_DEFAULT_PORT}#{OTLP_TRACES_PATH}"
220
+ end
214
221
 
215
- "http://#{agent_host}:#{OTLP_DEFAULT_PORT}#{OTLP_TRACES_PATH}"
222
+ # User-supplied: append the traces path if no path was given.
223
+ uri = URI.parse(endpoint)
224
+ uri.path = OTLP_TRACES_PATH if uri.path.empty? || uri.path == '/'
225
+ uri.to_s
226
+ rescue URI::InvalidURIError
227
+ endpoint
216
228
  end
217
229
  end
218
230
  end
data/lib/instana/base.rb CHANGED
@@ -6,6 +6,11 @@ require "instana/version"
6
6
  require "instana/util"
7
7
 
8
8
  module Instana
9
+ # Instana trace context header names
10
+ INSTANA_TRACE_ID_HEADER = 'X-Instana-T'.freeze
11
+ INSTANA_SPAN_ID_HEADER = 'X-Instana-S'.freeze
12
+ INSTANA_LEVEL_HEADER = 'X-Instana-L'.freeze
13
+
9
14
  class << self
10
15
  attr_accessor :agent
11
16
  attr_accessor :tracer
@@ -4,7 +4,7 @@
4
4
  require 'yaml'
5
5
 
6
6
  module Instana
7
- class Config
7
+ class Config # rubocop:disable Metrics/ClassLength
8
8
 
9
9
  LEGACY_TRACING_KEY = 'com.instana.tracing'.freeze
10
10
  TRACING_KEY = 'tracing'.freeze
@@ -63,6 +63,9 @@ module Instana
63
63
  certificate: nil,
64
64
  client_key: nil,
65
65
  client_certificate: nil,
66
+ protocol: 'http/protobuf',
67
+ semconv_stability: 'stable',
68
+ insecure: false,
66
69
  config_source: 'default'
67
70
  }
68
71
  read_otlp_config
@@ -80,6 +83,10 @@ module Instana
80
83
  # W3C Trace Context Support
81
84
  @config[:w3c_trace_correlation] = ENV.fetch('INSTANA_DISABLE_W3C_TRACE_CORRELATION', nil).nil?
82
85
 
86
+ # Server-Timing response header injection (default: injected)
87
+ # Priority: YAML > env var > agent config > default (false)
88
+ read_disable_server_timing_config
89
+
83
90
  # HTTP exit span 4xx error classification (opt-in, default: off)
84
91
  # Priority: env vars > agent config > default (false / [])
85
92
  read_http_exit_config
@@ -148,6 +155,8 @@ module Instana
148
155
  read_otlp_config_from_agent(tracing_config) if should_read_from_agent?(:otlp)
149
156
  # Read HTTP exit 4xx classification from agent if env vars did not already set it
150
157
  read_http_exit_classification_from_agent(tracing_config) unless env_http_exit_classification_set?
158
+ # Read Server-Timing disable flag from agent if not already set from YAML or env
159
+ read_disable_server_timing_from_agent(tracing_config) if @config[:disable_server_timing_source] == 'default'
151
160
  # Read span filtering configuration from agent
152
161
  ::Instana.span_filtering_config&.read_config_from_agent(discovery)
153
162
  rescue => e
@@ -257,11 +266,57 @@ module Instana
257
266
  @config[:otlp][:certificate] = otlp_config['certificate'] if otlp_config['certificate']
258
267
  @config[:otlp][:client_key] = otlp_config['client_key'] if otlp_config['client_key']
259
268
  @config[:otlp][:client_certificate] = otlp_config['client_certificate'] if otlp_config['client_certificate']
269
+ @config[:otlp][:protocol] = otlp_config['protocol'] if otlp_config['protocol']
270
+ @config[:otlp][:semconv_stability] = otlp_config['semconv_stability'] if otlp_config['semconv_stability']
271
+ @config[:otlp][:insecure] = truthy?(otlp_config['insecure']) unless otlp_config['insecure'].nil?
260
272
  @config[:otlp][:config_source] = 'agent'
261
273
  end
262
274
 
263
275
  private
264
276
 
277
+ # Read disable-server-timing from YAML (tracing.global) or INSTANA_TRACING_DISABLE_SERVER_TIMING.
278
+ # Agent value is applied later via read_config_from_agent if neither is set.
279
+ def read_disable_server_timing_config
280
+ @config[:disable_server_timing] = false
281
+ @config[:disable_server_timing_source] = 'default'
282
+
283
+ yaml_value = parse_disable_server_timing_from_yaml
284
+ unless yaml_value.nil?
285
+ @config[:disable_server_timing] = truthy?(yaml_value)
286
+ @config[:disable_server_timing_source] = 'yaml'
287
+ return
288
+ end
289
+
290
+ env_value = ENV.fetch('INSTANA_TRACING_DISABLE_SERVER_TIMING', nil)
291
+ return if env_value.nil? || env_value.strip.empty?
292
+
293
+ @config[:disable_server_timing] = truthy?(env_value.strip)
294
+ @config[:disable_server_timing_source] = 'env'
295
+ end
296
+
297
+ # @return [Object, nil] raw YAML value of tracing.global.disable-server-timing, or nil if not set
298
+ def parse_disable_server_timing_from_yaml
299
+ config_path = ENV.fetch('INSTANA_CONFIG_PATH', nil)
300
+ return nil unless config_path && File.exist?(config_path)
301
+
302
+ yaml_content = YAML.safe_load(File.read(config_path))
303
+ tracing_config = yaml_content[TRACING_KEY] || yaml_content[LEGACY_TRACING_KEY]
304
+ global_config = tracing_config && tracing_config['global']
305
+ global_config.is_a?(Hash) ? global_config['disable-server-timing'] : nil
306
+ rescue => e
307
+ ::Instana.logger.warn("Failed to load disable-server-timing configuration from YAML: #{e.message}")
308
+ nil
309
+ end
310
+
311
+ # @param tracing_config [Hash] the tracing sub-hash from the discovery payload
312
+ def read_disable_server_timing_from_agent(tracing_config)
313
+ global_config = tracing_config['global']
314
+ return unless global_config.is_a?(Hash) && !global_config['disable-server-timing'].nil?
315
+
316
+ @config[:disable_server_timing] = truthy?(global_config['disable-server-timing'])
317
+ @config[:disable_server_timing_source] = 'agent'
318
+ end
319
+
265
320
  # Read HTTP exit 4xx error classification config (wrapper — same pattern as read_span_stack_config / read_otlp_config)
266
321
  def read_http_exit_config
267
322
  @config[:http_exit_classify_all_4xx_as_errors] = false
@@ -381,6 +436,9 @@ module Instana
381
436
  result[:certificate] = otlp_yaml['certificate'] if otlp_yaml['certificate']
382
437
  result[:client_key] = otlp_yaml['client_key'] if otlp_yaml['client_key']
383
438
  result[:client_certificate] = otlp_yaml['client_certificate'] if otlp_yaml['client_certificate']
439
+ result[:protocol] = otlp_yaml['protocol'] if otlp_yaml['protocol']
440
+ result[:semconv_stability] = otlp_yaml['semconv_stability'] if otlp_yaml['semconv_stability']
441
+ result[:insecure] = truthy?(otlp_yaml['insecure']) unless otlp_yaml['insecure'].nil?
384
442
  result.empty? ? nil : result
385
443
  end
386
444
 
@@ -399,6 +457,9 @@ module Instana
399
457
  result[:certificate] = raw[:certificate] if raw[:certificate]
400
458
  result[:client_key] = raw[:client_key] if raw[:client_key]
401
459
  result[:client_certificate] = raw[:client_cert] if raw[:client_cert]
460
+ result[:protocol] = raw[:protocol] if raw[:protocol]
461
+ result[:semconv_stability] = raw[:semconv_stability] if raw[:semconv_stability]
462
+ result[:insecure] = truthy?(raw[:insecure_raw]) unless raw[:insecure_raw].nil?
402
463
  result
403
464
  end
404
465
 
@@ -413,7 +474,10 @@ module Instana
413
474
  headers_raw: ENV.fetch('OTEL_EXPORTER_OTLP_TRACES_HEADERS', nil) || ENV.fetch('OTEL_EXPORTER_OTLP_HEADERS', nil),
414
475
  certificate: ENV.fetch('OTEL_EXPORTER_OTLP_CERTIFICATE', nil),
415
476
  client_key: ENV.fetch('OTEL_EXPORTER_OTLP_CLIENT_KEY', nil),
416
- client_cert: ENV.fetch('OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE', nil)
477
+ client_cert: ENV.fetch('OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE', nil),
478
+ protocol: ENV.fetch('OTEL_EXPORTER_OTLP_PROTOCOL', nil),
479
+ semconv_stability: ENV.fetch('OTEL_SEMCONV_STABILITY_OPT_IN', nil),
480
+ insecure_raw: ENV.fetch('OTEL_EXPORTER_OTLP_INSECURE', nil)
417
481
  }
418
482
  end
419
483
 
@@ -6,6 +6,9 @@ require_relative 'base_converter'
6
6
  require 'opentelemetry/semconv/incubating/messaging'
7
7
  require 'opentelemetry/semconv/db'
8
8
  require 'opentelemetry/semconv/incubating/db'
9
+ require 'opentelemetry/semconv/incubating/rpc'
10
+ require 'opentelemetry/semconv/incubating/cloud'
11
+ require 'opentelemetry/semconv/incubating/faas'
9
12
 
10
13
  module Instana
11
14
  module Exporter
@@ -128,10 +131,18 @@ module Instana
128
131
  def convert_s3_attributes(attributes, s3_data)
129
132
  return unless s3_data
130
133
 
131
- add_attribute(attributes, 'aws.service', 's3')
134
+ add_attribute(attributes, Instana::Exporter::Otlp::RPC_SYSTEM_ATTR, 'aws-api')
135
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::RPC::RPC_METHOD, s3_rpc_method(s3_data[:op]))
136
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::CLOUD::CLOUD_PROVIDER, 'aws')
137
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::CLOUD::CLOUD_REGION, s3_data[:region])
132
138
  add_attribute(attributes, 'aws.s3.bucket', s3_data[:bucket])
133
139
  add_attribute(attributes, 'aws.s3.key', s3_data[:key])
134
- add_attribute(attributes, 'aws.s3.operation', s3_data[:op])
140
+ end
141
+
142
+ def s3_rpc_method(operation)
143
+ return unless operation
144
+
145
+ "S3.#{operation}"
135
146
  end
136
147
 
137
148
  def convert_lambda_attributes(attributes, lambda_data)
@@ -140,7 +151,7 @@ module Instana
140
151
  add_attribute(attributes, 'aws.service', 'lambda')
141
152
  add_attribute(attributes, 'aws.lambda.function_name', lambda_data[:function])
142
153
  add_attribute(attributes, 'aws.lambda.invocation_type', lambda_data[:type])
143
- add_attribute(attributes, 'faas.invoked_name', lambda_data[:function])
154
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::FAAS::FAAS_INVOKED_NAME, lambda_data[:function])
144
155
  end
145
156
  end
146
157
  end
@@ -41,6 +41,8 @@ module Instana
41
41
  convert_job_attributes(attributes, span[:'resque-client'] || span[:data]&.[](:'resque-client'), 'resque', 'publish')
42
42
  elsif span_type == 'resque-worker'
43
43
  convert_job_attributes(attributes, span[:'resque-worker'] || span[:data]&.[](:'resque-worker'), 'resque', 'process')
44
+ elsif span_type == 'activejob'
45
+ convert_activejob_attributes(attributes, span[:data]&.[](:activejob))
44
46
  end
45
47
 
46
48
  attributes
@@ -53,13 +55,22 @@ module Instana
53
55
 
54
56
  add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_SYSTEM, system)
55
57
  add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_DESTINATION_NAME, data[:queue] || data['queue'])
56
- add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_OPERATION, operation)
58
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_OPERATION_NAME, operation)
57
59
  add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_MESSAGE_ID, data[:job_id] || data['job_id'])
58
60
  add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_CONSUMER_GROUP_NAME, data[:job] || data['job'])
59
61
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_ADDRESS, extract_host(data[:'redis-url'] || data['redis-url']))
60
62
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_PORT, extract_port(data[:'redis-url'] || data['redis-url']))
61
63
  end
62
64
 
65
+ def convert_activejob_attributes(attributes, data)
66
+ return unless data
67
+
68
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_SYSTEM, 'activejob')
69
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_DESTINATION_NAME, data[:queue] || data['queue'])
70
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_OPERATION_NAME, data[:action] || data['action'])
71
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::MESSAGING::MESSAGING_MESSAGE_ID, data[:job_id] || data['job_id'])
72
+ end
73
+
63
74
  def extract_host(connection)
64
75
  return nil unless connection
65
76
 
@@ -5,10 +5,22 @@
5
5
  require_relative 'resource'
6
6
  require 'opentelemetry/trace'
7
7
  require 'forwardable'
8
+ require 'opentelemetry/semconv/incubating/rpc'
8
9
 
9
10
  module Instana
10
11
  module Exporter
11
12
  module Otlp
13
+ # Compatibility shim: RPC_SYSTEM_NAME was added in a newer release of
14
+ # opentelemetry-semantic_conventions. Fall back to the deprecated
15
+ # RPC_SYSTEM constant ('rpc.system') on older installs (e.g. Ruby 3.2
16
+ # lockfiles that resolve an earlier gem version).
17
+ RPC_SYSTEM_ATTR =
18
+ if OpenTelemetry::SemConv::Incubating::RPC.const_defined?(:RPC_SYSTEM_NAME)
19
+ OpenTelemetry::SemConv::Incubating::RPC::RPC_SYSTEM_NAME
20
+ else
21
+ OpenTelemetry::SemConv::Incubating::RPC::RPC_SYSTEM
22
+ end
23
+
12
24
  # Base class for all OTLP span converters
13
25
  #
14
26
  # Provides common interface and shared functionality for converting Instana spans
@@ -312,12 +324,12 @@ module Instana
312
324
  timestamp = calculate_end_timestamp
313
325
 
314
326
  if stacktrace
315
- attrs = { 'exception.type' => span_name }
327
+ attrs = {}
316
328
  attrs['exception.message'] = error_msg if error_msg
317
329
  attrs['exception.stacktrace'] = stacktrace
318
330
  [Event.new('exception', attrs, timestamp)]
319
331
  else
320
- [Event.new('error', { 'error.type' => span_name }, timestamp)]
332
+ [Event.new('error', {}, timestamp)]
321
333
  end
322
334
  end
323
335
 
@@ -82,32 +82,51 @@ module Instana
82
82
  parts.empty? ? 'mongodb' : parts.join('.')
83
83
  end
84
84
 
85
+ # Normalize an ActiveRecord/Sequel adapter name to OTel db.system.name.
86
+ # Only adapters that are actively instrumented and tested are listed here.
87
+ DB_SYSTEM_NORMALIZATION = {
88
+ 'mysql2' => 'mysql',
89
+ 'sqlite3' => 'sqlite',
90
+ 'postgresql' => 'postgresql',
91
+ 'mysql' => 'mysql',
92
+ 'sqlite' => 'sqlite'
93
+ }.freeze
94
+
95
+ def normalize_db_system(adapter)
96
+ return unless adapter
97
+
98
+ DB_SYSTEM_NORMALIZATION.fetch(adapter.to_s.downcase, adapter.to_s.downcase)
99
+ end
100
+
85
101
  def convert_activerecord_attributes(attributes, ar_data)
86
102
  return unless ar_data
87
103
 
88
- add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_SYSTEM_NAME, ar_data[:adapter])
104
+ add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_SYSTEM_NAME, normalize_db_system(ar_data[:adapter]))
89
105
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_NAMESPACE, ar_data[:db])
90
106
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_QUERY_TEXT, ar_data[:sql])
91
107
  add_attribute(attributes, OpenTelemetry::SemConv::Incubating::DB::DB_USER, ar_data[:username])
92
108
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_ADDRESS, ar_data[:host])
109
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::DB::DB_COLLECTION_NAME, ar_data[:table])
93
110
  end
94
111
 
95
112
  def convert_sequel_attributes(attributes, seq_data)
96
113
  return unless seq_data
97
114
 
98
- add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_SYSTEM_NAME, seq_data[:adapter])
115
+ add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_SYSTEM_NAME, normalize_db_system(seq_data[:adapter]))
99
116
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_NAMESPACE, seq_data[:db])
100
117
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_QUERY_TEXT, seq_data[:sql])
101
118
  add_attribute(attributes, OpenTelemetry::SemConv::Incubating::DB::DB_USER, seq_data[:username])
102
119
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_ADDRESS, seq_data[:host])
120
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::DB::DB_COLLECTION_NAME, seq_data[:table])
103
121
  end
104
122
 
105
123
  def convert_redis_attributes(attributes, redis_data)
106
124
  return unless redis_data
107
125
 
108
126
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_SYSTEM_NAME, 'redis')
127
+ add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_OPERATION_NAME, redis_data[:command])
109
128
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_QUERY_TEXT, redis_data[:command])
110
- add_attribute(attributes, 'db.redis.database_index', redis_data[:db])
129
+ add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_NAMESPACE, redis_data[:db]&.to_s)
111
130
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_ADDRESS, extract_host(redis_data[:connection]))
112
131
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_PORT, extract_port(redis_data[:connection]))
113
132
  end
@@ -119,7 +138,7 @@ module Instana
119
138
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_OPERATION_NAME, mc_data[:command])
120
139
  add_attribute(attributes, 'db.memcached.key', mc_data[:key])
121
140
  add_attribute(attributes, 'db.memcached.keys', mc_data[:keys])
122
- add_attribute(attributes, 'db.memcached.namespace', mc_data[:namespace])
141
+ add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_NAMESPACE, mc_data[:namespace])
123
142
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_ADDRESS, extract_host(mc_data[:server]))
124
143
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_PORT, extract_port(mc_data[:server]))
125
144
  end
@@ -131,6 +150,7 @@ module Instana
131
150
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_NAMESPACE, mongo_data[:namespace])
132
151
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_OPERATION_NAME, mongo_data[:command])
133
152
  add_attribute(attributes, OpenTelemetry::SemConv::DB::DB_QUERY_TEXT, mongo_data[:json])
153
+ add_attribute(attributes, OpenTelemetry::SemConv::Incubating::DB::DB_COLLECTION_NAME, mongo_data[:collection])
134
154
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_ADDRESS, mongo_data.dig(:peer, :hostname))
135
155
  add_attribute(attributes, OpenTelemetry::SemConv::SERVER::SERVER_PORT, mongo_data.dig(:peer, :port))
136
156
  end
@@ -7,6 +7,7 @@ require 'opentelemetry/semconv/http'
7
7
  require 'opentelemetry/semconv/url'
8
8
  require 'opentelemetry/semconv/server'
9
9
  require 'opentelemetry/semconv/user_agent'
10
+ require 'opentelemetry/semconv/network'
10
11
 
11
12
  module Instana
12
13
  module Exporter
@@ -120,8 +121,8 @@ module Instana
120
121
  name = parts[0].downcase
121
122
  version = parts[1]
122
123
 
123
- add_attribute(attributes, 'network.protocol.name', name)
124
- add_attribute(attributes, 'network.protocol.version', version)
124
+ add_attribute(attributes, OpenTelemetry::SemConv::NETWORK::NETWORK_PROTOCOL_NAME, name)
125
+ add_attribute(attributes, OpenTelemetry::SemConv::NETWORK::NETWORK_PROTOCOL_VERSION, version)
125
126
  end
126
127
  end
127
128
  end