clicksend-opentelemetry 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/CHANGELOG.md +37 -0
- data/LICENSE.txt +22 -0
- data/README.md +119 -0
- data/lib/clicksend/opentelemetry/version.rb +7 -0
- data/lib/clicksend/opentelemetry.rb +186 -0
- metadata +82 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0af816c4b5ff18d37e86e352b4d0deee0a48113c66a8a6b0afcbf2a6a41b8753
|
|
4
|
+
data.tar.gz: 8ae1ea1e1ffb6c812e7e5ea3d922641ed45e3da6a841f7e4e02a9236a07a3185
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c53aa4d5940bef92d06089b2e2ce40c38178a88754667692af2baa541fca6e36694664dc1cb7fc8c77a432b7bd5ab0a8395f8df9170399238990f649ce8f0d06
|
|
7
|
+
data.tar.gz: 57faca30aa2d16c156e336c34dd54b525947bd8a0d6c24d57ac1b1a1ac9bc84e8bc35a45301efcf1b167644c624e53f0a986611dee3d4bf92e6b9a2ed468517c
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `clicksend-opentelemetry` are documented here. It is versioned and released
|
|
4
|
+
separately from [`clicksend`](https://github.com/prayantr/clicksend/blob/master/CHANGELOG.md). The format follows
|
|
5
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
6
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). While the version is 0.x, a minor
|
|
7
|
+
release may rename span attributes as OpenTelemetry's semantic conventions change.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
## [0.1.0] - 2026-10-06
|
|
12
|
+
|
|
13
|
+
The first release.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `Clicksend::OpenTelemetry::Instrumenter`, passed to `Clicksend::Client.new(instrumenter:)`. One
|
|
18
|
+
span of kind CLIENT per ClickSend API call, retries included, named `clicksend <operation>`
|
|
19
|
+
(`clicksend <METHOD>` when there is no operation). Attributes: `http.request.method`,
|
|
20
|
+
`server.address`, `server.port`, `url.path` (never the query string; `record_path: false` leaves
|
|
21
|
+
it out, and the path out of the exception event's message), `http.response.status_code`,
|
|
22
|
+
`error.type`, and `clicksend.operation`, `clicksend.idempotent`, `clicksend.attempts`,
|
|
23
|
+
`clicksend.ambiguous`, `clicksend.response_code`.
|
|
24
|
+
Each retry is a `clicksend.retry` span event. A failed call sets the span status to ERROR and
|
|
25
|
+
adds an `exception` event whose message is built only from the class, HTTP status, ClickSend's
|
|
26
|
+
`response_code` and the request line, never from the exception's own message.
|
|
27
|
+
- `Clicksend::OpenTelemetry::FanOut`, which sends the client's events to several instrumenters
|
|
28
|
+
(for example `ActiveSupport::Notifications` and the OpenTelemetry instrumenter) while the
|
|
29
|
+
request still runs once.
|
|
30
|
+
- The instrumenter never changes a call's outcome: its own failures go to
|
|
31
|
+
`OpenTelemetry.handle_error`, the request runs exactly once, and errors, including ambiguous
|
|
32
|
+
ones, pass through unchanged.
|
|
33
|
+
- Requires Ruby 3.3 or newer, `clicksend` 1.x (from 1.1) and `opentelemetry-api` 1.x. The SDK and
|
|
34
|
+
exporters are the application's choice.
|
|
35
|
+
|
|
36
|
+
[Unreleased]: https://github.com/prayantr/clicksend/compare/clicksend-opentelemetry-v0.1.0...HEAD
|
|
37
|
+
[0.1.0]: https://github.com/prayantr/clicksend/releases/tag/clicksend-opentelemetry-v0.1.0
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Prayantr (https://prayantr.com), Amit Solanki, Braj Pratap Singh
|
|
4
|
+
Copyright (c) 2026 Amit Solanki and contributors
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
|
14
|
+
all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# clicksend-opentelemetry
|
|
2
|
+
|
|
3
|
+
OpenTelemetry spans for the [clicksend](https://github.com/prayantr/clicksend) gem, built only on
|
|
4
|
+
its public `instrumenter:` hook. It depends on `opentelemetry-api`; your application chooses the
|
|
5
|
+
SDK and exporter.
|
|
6
|
+
|
|
7
|
+
**Optional.** `clicksend` doesn't depend on this gem or on OpenTelemetry, and works the same
|
|
8
|
+
without it. Add it only if you want ClickSend spans in your traces.
|
|
9
|
+
|
|
10
|
+
> It lives in the clicksend repository and is released separately from `clicksend`, on its own
|
|
11
|
+
> version line ([CHANGELOG](CHANGELOG.md)). While it is 0.x, a minor release may rename attributes
|
|
12
|
+
> as OpenTelemetry's semantic conventions change. Unofficial: not affiliated with ClickSend.
|
|
13
|
+
|
|
14
|
+
Requires Ruby 3.3 or newer, `clicksend` 1.x (1.1 or later) and `opentelemetry-api` 1.x.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
# Gemfile
|
|
20
|
+
gem "clicksend-opentelemetry", "~> 0.1"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
# config/initializers/clicksend.rb
|
|
27
|
+
require "clicksend/opentelemetry"
|
|
28
|
+
|
|
29
|
+
CLICKSEND = Clicksend::Client.new(instrumenter: Clicksend::OpenTelemetry::Instrumenter.new)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The instrumenter takes its tracer from the global provider when it is built. Before
|
|
33
|
+
`OpenTelemetry::SDK.configure` runs, that is the API's proxy, which forwards to the SDK once it is
|
|
34
|
+
configured, so the order of your initializers doesn't matter. Pass `tracer_provider:` to use
|
|
35
|
+
another provider.
|
|
36
|
+
|
|
37
|
+
To keep your `ActiveSupport::Notifications` subscribers as well, combine both with `FanOut`. Put
|
|
38
|
+
the OpenTelemetry instrumenter **last**: its span then covers only the request, and a subscriber
|
|
39
|
+
that raises after the request finished (which the client ignores) is not recorded as the span's
|
|
40
|
+
error.
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
CLICKSEND = Clicksend::Client.new(
|
|
44
|
+
instrumenter: Clicksend::OpenTelemetry::FanOut.new(ActiveSupport::Notifications, Clicksend::OpenTelemetry::Instrumenter.new)
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
- `base_url:` the client's `base_url`, if it isn't the default `https://rest.clicksend.com`. The
|
|
50
|
+
instrumentation payload has no host, so `server.address` and `server.port` come from here.
|
|
51
|
+
- `record_path: false` leaves out `url.path`, and the path in the exception event's message. Paths never contain a query string, but some contain
|
|
52
|
+
a message ID (`sms.receipt`, `sms.cancel`), and paths you pass to `client.request` are recorded as
|
|
53
|
+
you wrote them.
|
|
54
|
+
|
|
55
|
+
## What is recorded
|
|
56
|
+
|
|
57
|
+
One span per API call, retries included: kind CLIENT, named `clicksend <operation>` (for example
|
|
58
|
+
`clicksend sms.deliver`), or `clicksend <METHOD>` for `client.request` without `operation:`. Paths
|
|
59
|
+
are never used in span names, because they can contain IDs. `sms.search_history` reads history page
|
|
60
|
+
by page, so it produces one `clicksend sms.history` span per page.
|
|
61
|
+
|
|
62
|
+
| Attribute | From |
|
|
63
|
+
|---|---|
|
|
64
|
+
| `http.request.method`, `url.path`, `server.address`, `server.port` | the call |
|
|
65
|
+
| `http.response.status_code` | the last response, if there was one |
|
|
66
|
+
| `error.type`, span status ERROR, an `exception` event | a failed call. The event's message is built from the class, HTTP status, ClickSend's `response_code` and the request line only, never from the exception's own message |
|
|
67
|
+
| `clicksend.operation`, `clicksend.idempotent`, `clicksend.attempts`, `clicksend.ambiguous`, `clicksend.response_code` | the `request.clicksend` payload |
|
|
68
|
+
|
|
69
|
+
Each retry adds a `clicksend.retry` event (`clicksend.retry.attempt`, `clicksend.retry.delay` in
|
|
70
|
+
seconds, `error.type`, `http.response.status_code`).
|
|
71
|
+
|
|
72
|
+
`http.request.method`, `server.*`, `url.path`, `http.response.status_code` and `error.type` are
|
|
73
|
+
OpenTelemetry's stable HTTP attribute names. The span describes a logical call that may span
|
|
74
|
+
several HTTP attempts, so it doesn't claim to be an HTTP client span: it has no `url.full`, and the
|
|
75
|
+
attempt count is `clicksend.attempts`, not `http.request.resend_count` (which describes one
|
|
76
|
+
physical request).
|
|
77
|
+
|
|
78
|
+
Never recorded: phone numbers, message text, `custom_string`, request or response bodies, query
|
|
79
|
+
strings, headers and credentials. The specs check the exported spans for each of these.
|
|
80
|
+
|
|
81
|
+
A message ClickSend refuses inside an HTTP 200 (`Clicksend::MessageRejected`) is detected after the
|
|
82
|
+
HTTP call succeeded, so its span is not an error.
|
|
83
|
+
|
|
84
|
+
## HTTP-level instrumentation records the query string
|
|
85
|
+
|
|
86
|
+
With `opentelemetry-instrumentation-faraday`, each HTTP attempt becomes a child CLIENT span of this
|
|
87
|
+
gem's span (Net::HTTP's own span is then suppressed). With only
|
|
88
|
+
`opentelemetry-instrumentation-net_http`, each attempt is a `GET`/`POST` span plus a `connect`
|
|
89
|
+
span. **Those spans record the query string** (`url.full` or `url.query`), and
|
|
90
|
+
`sms.history(to:)` and `sms.search_history` put the recipient's phone number there
|
|
91
|
+
(`q=to:+61...`). Those instrumentations also send a `traceparent` header to ClickSend. This gem
|
|
92
|
+
can't change what they record. With `opentelemetry-instrumentation-net_http` 0.29.1 and
|
|
93
|
+
`-faraday` 0.33.0 (checked):
|
|
94
|
+
- `untraced_hosts: ["rest.clicksend.com"]` on the Net::HTTP instrumentation drops its spans for
|
|
95
|
+
ClickSend and keeps this gem's span;
|
|
96
|
+
- the Faraday instrumentation has no such option, so don't enable it in an application that
|
|
97
|
+
calls history by number. `OpenTelemetry::Common::Utilities.untraced { ... }` around a call
|
|
98
|
+
suppresses the Faraday span, but this gem's span too.
|
|
99
|
+
|
|
100
|
+
## Guarantees
|
|
101
|
+
|
|
102
|
+
The instrumenter never changes a call's outcome. Its own failures go to
|
|
103
|
+
`OpenTelemetry.handle_error`, the request runs exactly once, and errors, including ambiguous
|
|
104
|
+
ones, pass through unchanged. A send that times out is still a single-attempt, ambiguous
|
|
105
|
+
`Clicksend::TimeoutError` with tracing on. The specs show this with a failing tracer, a failing
|
|
106
|
+
span, a send timeout against `Clicksend::Testing::FakeAPI`, and a real local server.
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
The specs run against the `clicksend` in this repository (`path: "../.."` in the Gemfile), with
|
|
111
|
+
their own bundle:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
cd companions/clicksend-opentelemetry
|
|
115
|
+
bundle install
|
|
116
|
+
bundle exec rspec
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
When the core gem's version changes, run `bundle install` here too, so `Gemfile.lock` matches it.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
require "opentelemetry"
|
|
5
|
+
require "clicksend"
|
|
6
|
+
require_relative "opentelemetry/version"
|
|
7
|
+
|
|
8
|
+
module Clicksend
|
|
9
|
+
# OpenTelemetry tracing for Clicksend::Client, built only on the client's
|
|
10
|
+
# public instrumenter hook (see Clicksend::Instrumentation):
|
|
11
|
+
#
|
|
12
|
+
# require "clicksend/opentelemetry"
|
|
13
|
+
#
|
|
14
|
+
# CLICKSEND = Clicksend::Client.new(instrumenter: Clicksend::OpenTelemetry::Instrumenter.new)
|
|
15
|
+
#
|
|
16
|
+
# Each logical API call (retries included) becomes one span of kind CLIENT,
|
|
17
|
+
# named after its operation ("clicksend sms.deliver"); each retry is a
|
|
18
|
+
# "clicksend.retry" event on it. Spans never hold phone numbers, message
|
|
19
|
+
# text, bodies, query strings, headers or credentials: only what the
|
|
20
|
+
# request.clicksend payload holds, plus the server address.
|
|
21
|
+
module OpenTelemetry
|
|
22
|
+
# Spans for request.clicksend, span events for retry.clicksend.
|
|
23
|
+
#
|
|
24
|
+
# The instrumenter never changes a call's outcome. Its own failures are
|
|
25
|
+
# sent to ::OpenTelemetry.handle_error, the request block runs exactly once
|
|
26
|
+
# whatever happens, and the request's own result or exception is passed
|
|
27
|
+
# through untouched. (Clicksend::Connection also ignores instrumenter
|
|
28
|
+
# failures after the request has run; this class does not rely on that.)
|
|
29
|
+
class Instrumenter
|
|
30
|
+
SPAN_KEY = ::OpenTelemetry::Context.create_key("clicksend-span")
|
|
31
|
+
private_constant :SPAN_KEY
|
|
32
|
+
|
|
33
|
+
# @param tracer_provider [#tracer] defaults to the global provider (a
|
|
34
|
+
# proxy until the SDK is configured, so creating the client first is fine)
|
|
35
|
+
# @param base_url [String] the client's base_url, for server.address and
|
|
36
|
+
# server.port (the instrumentation payload does not carry the host)
|
|
37
|
+
# @param record_path [Boolean] whether to set url.path. Paths never hold
|
|
38
|
+
# query strings; some hold a message ID, and paths you pass to
|
|
39
|
+
# Client#request are reported as written.
|
|
40
|
+
def initialize(tracer_provider: ::OpenTelemetry.tracer_provider, base_url: Clicksend::Client::DEFAULT_BASE_URL, record_path: true)
|
|
41
|
+
@tracer = tracer_provider.tracer("clicksend-opentelemetry", VERSION)
|
|
42
|
+
uri = URI.parse(base_url)
|
|
43
|
+
@server = {"server.address" => uri.host, "server.port" => uri.port}.freeze
|
|
44
|
+
@record_path = record_path
|
|
45
|
+
freeze
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The Clicksend::Instrumentation interface.
|
|
49
|
+
def instrument(name, payload = {}, &block)
|
|
50
|
+
case name
|
|
51
|
+
when "request.clicksend" then block ? trace(payload, &block) : nil
|
|
52
|
+
when "retry.clicksend" then add_retry_event(payload, &block)
|
|
53
|
+
else block&.call(payload)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def inspect
|
|
58
|
+
"#<#{self.class.name}>"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def trace(payload)
|
|
64
|
+
span = safely { @tracer.start_span(span_name(payload), kind: :client, attributes: start_attributes(payload)) }
|
|
65
|
+
token = safely { attach(span) } if span
|
|
66
|
+
begin
|
|
67
|
+
result = yield payload
|
|
68
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
69
|
+
safely { record_failure(span, e) } if span
|
|
70
|
+
raise
|
|
71
|
+
ensure
|
|
72
|
+
safely { finish(span, token, payload) } if span
|
|
73
|
+
end
|
|
74
|
+
result
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Makes the span current, so HTTP-level spans (Faraday, Net::HTTP
|
|
78
|
+
# instrumentation) nest under it and retry events can find it.
|
|
79
|
+
def attach(span)
|
|
80
|
+
::OpenTelemetry::Context.attach(::OpenTelemetry::Trace.context_with_span(span).set_value(SPAN_KEY, span))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def finish(span, token, payload)
|
|
84
|
+
::OpenTelemetry::Context.detach(token) if token
|
|
85
|
+
attributes = {
|
|
86
|
+
"http.response.status_code" => payload[:http_status],
|
|
87
|
+
"clicksend.response_code" => payload[:response_code],
|
|
88
|
+
"clicksend.attempts" => payload[:attempts],
|
|
89
|
+
"clicksend.ambiguous" => payload[:ambiguous]
|
|
90
|
+
}.compact
|
|
91
|
+
span.add_attributes(attributes) unless attributes.empty?
|
|
92
|
+
ensure
|
|
93
|
+
span.finish
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# The exception's message is never recorded: an API error's message holds
|
|
97
|
+
# ClickSend's response_msg, and a foreign exception's may hold anything.
|
|
98
|
+
def record_failure(span, error)
|
|
99
|
+
type = error.class.name || "Exception"
|
|
100
|
+
span.set_attribute("error.type", type)
|
|
101
|
+
span.add_event("exception", attributes: {
|
|
102
|
+
"exception.type" => type,
|
|
103
|
+
"exception.message" => safe_message(error),
|
|
104
|
+
"exception.stacktrace" => Array(error.backtrace).join("\n")
|
|
105
|
+
})
|
|
106
|
+
span.status = ::OpenTelemetry::Trace::Status.error(type)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def add_retry_event(payload)
|
|
110
|
+
safely do
|
|
111
|
+
span = ::OpenTelemetry::Context.current.value(SPAN_KEY)
|
|
112
|
+
span&.add_event("clicksend.retry", attributes: {
|
|
113
|
+
"clicksend.retry.attempt" => payload[:attempt],
|
|
114
|
+
"clicksend.retry.delay" => payload[:delay]&.to_f,
|
|
115
|
+
"error.type" => payload[:error_class],
|
|
116
|
+
"http.response.status_code" => payload[:http_status]
|
|
117
|
+
}.compact)
|
|
118
|
+
end
|
|
119
|
+
yield payload if block_given?
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def span_name(payload)
|
|
123
|
+
payload[:operation] ? "clicksend #{payload[:operation]}" : "clicksend #{payload[:http_method].to_s.upcase}"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def start_attributes(payload)
|
|
127
|
+
attributes = @server.merge(
|
|
128
|
+
"http.request.method" => payload[:http_method].to_s.upcase,
|
|
129
|
+
"clicksend.operation" => payload[:operation],
|
|
130
|
+
"clicksend.idempotent" => payload[:idempotent]
|
|
131
|
+
)
|
|
132
|
+
attributes["url.path"] = payload[:path] if @record_path
|
|
133
|
+
attributes.compact
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Class, HTTP status, ClickSend's response_code and the request line:
|
|
137
|
+
# all already present in the request.clicksend payload. Without
|
|
138
|
+
# record_path the request line is the method alone.
|
|
139
|
+
def safe_message(error)
|
|
140
|
+
parts = [error.class.name]
|
|
141
|
+
parts << "HTTP #{error.http_status}" if error.respond_to?(:http_status) && error.http_status
|
|
142
|
+
parts << error.response_code if error.respond_to?(:response_code) && error.response_code.is_a?(String)
|
|
143
|
+
if error.is_a?(Clicksend::Error) && error.request
|
|
144
|
+
parts << (@record_path ? "(#{error.request})" : "(#{error.request.http_method.to_s.upcase})")
|
|
145
|
+
end
|
|
146
|
+
parts.join(" ")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def safely
|
|
150
|
+
yield
|
|
151
|
+
rescue => e
|
|
152
|
+
::OpenTelemetry.handle_error(exception: e, message: "clicksend-opentelemetry")
|
|
153
|
+
nil
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Sends every event to several instrumenters by nesting them, so the
|
|
158
|
+
# request still runs exactly once:
|
|
159
|
+
#
|
|
160
|
+
# Clicksend::OpenTelemetry::FanOut.new(ActiveSupport::Notifications, Clicksend::OpenTelemetry::Instrumenter.new)
|
|
161
|
+
#
|
|
162
|
+
# The first instrumenter is the outermost. Put the OpenTelemetry one last:
|
|
163
|
+
# its span then covers only the request, and an exception raised by an
|
|
164
|
+
# outer subscriber after the request finished (which the client ignores)
|
|
165
|
+
# is never recorded as the span's error.
|
|
166
|
+
class FanOut
|
|
167
|
+
def initialize(*instrumenters)
|
|
168
|
+
raise ArgumentError, "every instrumenter must respond to #instrument" unless instrumenters.all? { |i| i.respond_to?(:instrument) }
|
|
169
|
+
|
|
170
|
+
@instrumenters = instrumenters.freeze
|
|
171
|
+
freeze
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def instrument(name, payload = {}, &block)
|
|
175
|
+
chain = @instrumenters.reverse.reduce(block) do |inner, instrumenter|
|
|
176
|
+
proc { |yielded| instrumenter.instrument(name, yielded || payload, &inner) }
|
|
177
|
+
end
|
|
178
|
+
chain&.call(payload)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def inspect
|
|
182
|
+
"#<#{self.class.name} #{@instrumenters.map(&:inspect).join(", ")}>"
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: clicksend-opentelemetry
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Amit Solanki
|
|
8
|
+
- Braj Pratap Singh
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: clicksend
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: opentelemetry-api
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.1'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.1'
|
|
41
|
+
description: An instrumenter for Clicksend::Client that opens one CLIENT span per
|
|
42
|
+
ClickSend API call, with retries as span events. Never records phone numbers, message
|
|
43
|
+
text, bodies, query strings or credentials. Not affiliated with ClickSend.
|
|
44
|
+
email:
|
|
45
|
+
- amit@prayantr.com
|
|
46
|
+
executables: []
|
|
47
|
+
extensions: []
|
|
48
|
+
extra_rdoc_files: []
|
|
49
|
+
files:
|
|
50
|
+
- CHANGELOG.md
|
|
51
|
+
- LICENSE.txt
|
|
52
|
+
- README.md
|
|
53
|
+
- lib/clicksend/opentelemetry.rb
|
|
54
|
+
- lib/clicksend/opentelemetry/version.rb
|
|
55
|
+
homepage: https://github.com/prayantr/clicksend
|
|
56
|
+
licenses:
|
|
57
|
+
- MIT
|
|
58
|
+
metadata:
|
|
59
|
+
source_code_uri: https://github.com/prayantr/clicksend/tree/master/companions/clicksend-opentelemetry
|
|
60
|
+
changelog_uri: https://github.com/prayantr/clicksend/blob/master/companions/clicksend-opentelemetry/CHANGELOG.md
|
|
61
|
+
bug_tracker_uri: https://github.com/prayantr/clicksend/issues
|
|
62
|
+
documentation_uri: https://github.com/prayantr/clicksend/blob/master/companions/clicksend-opentelemetry/README.md
|
|
63
|
+
allowed_push_host: https://rubygems.org
|
|
64
|
+
rubygems_mfa_required: 'true'
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '3.3'
|
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubygems_version: 4.0.20
|
|
80
|
+
specification_version: 4
|
|
81
|
+
summary: OpenTelemetry spans for the clicksend gem's instrumentation hook.
|
|
82
|
+
test_files: []
|