aspecto-opentelemetry 0.1.1 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +71 -37
- data/lib/aspecto/opentelemetry/config/remote_config.rb +76 -0
- data/lib/aspecto/opentelemetry/configurator.rb +2 -1
- data/lib/aspecto/opentelemetry/sampler/condition.rb +5 -6
- data/lib/aspecto/opentelemetry/sampler/message_process_sampler.rb +56 -0
- data/lib/aspecto/opentelemetry/sampler/operator.rb +36 -0
- data/lib/aspecto/opentelemetry/sampler/rules_sampler.rb +9 -5
- data/lib/aspecto/opentelemetry/sampler/sampling_rule.rb +4 -3
- data/lib/aspecto/opentelemetry/version.rb +1 -1
- data/lib/aspecto/opentelemetry.rb +23 -15
- metadata +61 -18
- data/lib/aspecto/opentelemetry/remote_config.rb +0 -22
- data/lib/aspecto/opentelemetry/sampler/utils.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42bf7169cad40cd8e0fc53c8ef85c766f783330e06ed185c5bb14fcf48ce4d4d
|
4
|
+
data.tar.gz: aa44a60faa0b08052bbfb60458e53697fe6c11c22f0b64fe7285a4f4cec19635
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81009b5b65381793e19eee34fb04c579a910952a4ad965b6b7879f69d15ac42477ec274380895a38a49b6fd31a991090a7ac7ab6ca9b1bbc27da524ceae23971
|
7
|
+
data.tar.gz: 2a1ac520558ef20b6fa09e7cad8c310b0507336ed7f1ab715bfe42ae8ab2df5dd8670e7132810b009e7289298d4572d16370cf45b8199d9d318b8ea47917448d
|
data/README.md
CHANGED
@@ -1,76 +1,110 @@
|
|
1
1
|
# Aspecto::OpenTelemetry
|
2
2
|
|
3
|
-
Aspecto's SDK for ruby.
|
3
|
+
Aspecto's SDK for ruby.
|
4
|
+
This gem is a distribution of OpenTelemetry pre-configured to use all available instrumentations and export trace data to Aspecto.
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
7
|
-
|
8
|
+
Install the gem using:
|
8
9
|
|
9
|
-
```
|
10
|
-
gem
|
10
|
+
```zsh
|
11
|
+
$ gem install aspecto-opentelemetry
|
11
12
|
```
|
12
13
|
|
13
|
-
|
14
|
+
Or, if you use [bundler](https://bundler.io), include aspecto-opentelemetry in your Gemfile.
|
14
15
|
|
15
|
-
|
16
|
+
## Usage
|
16
17
|
|
17
|
-
|
18
|
+
### Rails Applications
|
18
19
|
|
19
|
-
|
20
|
+
#### Configuration in Code
|
20
21
|
|
21
|
-
|
22
|
+
Add this code to a new file `aspecto.rb` under `config/initializers/`:
|
23
|
+
|
24
|
+
```rb
|
25
|
+
# config/initializers/aspecto.rb
|
26
|
+
require 'aspecto/opentelemetry'
|
27
|
+
|
28
|
+
Aspecto::OpenTelemetry::configure do |c|
|
29
|
+
c.service_name = '<YOUR_SERVICE_NAME>'
|
30
|
+
c.aspecto_auth = '<YOUR_ASPECTO_TOKEN>'
|
31
|
+
# c.sampling_ratio = 1.0 # [optional]: defaults to 1.0, use aspecto app to configure remotely
|
32
|
+
end
|
33
|
+
```
|
22
34
|
|
23
|
-
|
35
|
+
#### Configuration via Environment Variables
|
24
36
|
|
25
|
-
|
37
|
+
In your Gemfile:
|
26
38
|
|
27
39
|
```
|
28
40
|
gem 'aspecto-opentelemetry', require: 'aspecto/auto_instrument'
|
29
41
|
```
|
30
42
|
|
31
|
-
|
43
|
+
And set environment variables:
|
44
|
+
|
45
|
+
```
|
46
|
+
OTEL_SERVICE_NAME=<YOUR_SERVICE_NAME>
|
47
|
+
ASPECTO_AUTH=<YOUR_ASPECTO_TOKEN>
|
48
|
+
# ASPECTO_SAMPLING_RATIO=1.0 # [optional]: defaults to 1.0, use aspecto app to configure remotely
|
49
|
+
```
|
50
|
+
|
51
|
+
### Ruby Applications
|
32
52
|
|
33
|
-
|
53
|
+
#### Configuration in Code
|
34
54
|
|
35
|
-
|
55
|
+
Add this code after your require other gems:
|
36
56
|
|
37
57
|
```rb
|
38
58
|
require 'aspecto/opentelemetry'
|
39
59
|
|
40
|
-
Aspecto::OpenTelemetry::configure
|
60
|
+
Aspecto::OpenTelemetry::configure do |c|
|
61
|
+
c.service_name = '<YOUR_SERVICE_NAME>'
|
62
|
+
c.aspecto_auth = '<YOUR_ASPECTO_TOKEN>'
|
63
|
+
# c.env = '<CURRENT_ENVIRONMENT>' # [optional]: automatically detected for rails and sinatra
|
64
|
+
# c.sampling_ratio = 1.0 # [optional]: defaults to 1.0, use aspecto app to configure remotely
|
65
|
+
end
|
41
66
|
```
|
42
67
|
|
43
|
-
|
44
|
-
For rails - add this code to a file `aspecto.rb` under `config/initializers/`.
|
68
|
+
#### Configuration via Environment Variables
|
45
69
|
|
46
|
-
|
70
|
+
Add this require statement after your require other gems:
|
47
71
|
|
48
|
-
|
49
|
-
|
72
|
+
```rb
|
73
|
+
require 'aspecto/auto_instrument'
|
74
|
+
```
|
50
75
|
|
51
|
-
|
76
|
+
And set environment variables:
|
52
77
|
|
53
|
-
```
|
54
|
-
|
78
|
+
```
|
79
|
+
OTEL_SERVICE_NAME=<YOUR_SERVICE_NAME>
|
80
|
+
ASPECTO_AUTH=<YOUR_ASPECTO_TOKEN>
|
81
|
+
# ASPECTO_ENV=<CURRENT_ENVIRONMENT> # [optional]: automatically detected for rails and sinatra
|
82
|
+
# ASPECTO_SAMPLING_RATIO=1.0 # [optional]: defaults to 1.0, use aspecto app to configure remotely
|
83
|
+
```
|
55
84
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
85
|
+
### Shutdown
|
86
|
+
|
87
|
+
Call this function when your application shuts down
|
88
|
+
|
89
|
+
```rb
|
90
|
+
Aspecto::OpenTelemetry::shutdown
|
62
91
|
```
|
63
92
|
|
64
|
-
|
93
|
+
## Configuration
|
94
|
+
|
95
|
+
You can set configuration via environment variables or via code. Values set in code takes precedence.
|
96
|
+
The only required config options are [`aspecto_auth`](https://app.aspecto.io/app/integration/api-key) and `service_name`.
|
65
97
|
|
66
|
-
|
67
|
-
| -------------- | ---------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
68
|
-
| `aspecto_auth` | `ASPECTO_AUTH` | UUID string | - | Aspecto's [API key for authentication](https://app.aspecto.io/app/integration/api-key) |
|
69
|
-
| `service_name` | `OTEL_SERVICE_NAME` | string | - | name of the service which is sending telemetry |
|
70
|
-
| `env` | `ASPECTO_ENV` | string | extracted from rails or sinatra if used | deployment environment: `production` / `staging` / `development`, etc. |
|
71
|
-
| `log_level` | `OTEL_LOG_LEVEL` | string | `ERROR` | `ERROR` / `WARN` / `INFO`, etc. |
|
72
|
-
| `sampling_ratio` | `ASPECTO_SAMPLING_RATIO` | float | 1.0 | How many of the traces starting in this service should be sampled. set to number in range [0.0, 1.0] where 0.0 is no sampling, and 1.0 is sample all |
|
98
|
+
### Configuration Options
|
73
99
|
|
100
|
+
| Option Name | Environment Variable | Type | Default | Description |
|
101
|
+
| ------------------------------------ | ------------------------------------ | ----------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
102
|
+
| `aspecto_auth` | `ASPECTO_AUTH` | UUID string | - | Aspecto's [API key for authentication](https://app.aspecto.io/app/integration/api-key) |
|
103
|
+
| `service_name` | `OTEL_SERVICE_NAME` | string | - | name of the service which is sending telemetry |
|
104
|
+
| `env` | `ASPECTO_ENV` | string | extracted from rails or sinatra if used | deployment environment: `production` / `staging` / `development`, etc. |
|
105
|
+
| `log_level` | `OTEL_LOG_LEVEL` | string | `ERROR` | `ERROR` / `WARN` / `INFO`, etc. |
|
106
|
+
| `sampling_ratio` | `ASPECTO_SAMPLING_RATIO` | float | 1.0 | How many of the traces starting in this service should be sampled. set to number in range [0.0, 1.0] where 0.0 is no sampling, and 1.0 is sample all |
|
107
|
+
| `otel_exporter_otlp_traces_endpoint` | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | URL | "https://otelcol.aspecto.io/v1/trace" | Url |
|
74
108
|
|
75
109
|
## Contributing
|
76
110
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "faraday_middleware"
|
5
|
+
require "rufus/scheduler"
|
6
|
+
|
7
|
+
require_relative "../sampler/rules_sampler"
|
8
|
+
require_relative "../sampler/message_process_sampler"
|
9
|
+
|
10
|
+
module Aspecto
|
11
|
+
# Aspecto's OpenTelemetry distribution
|
12
|
+
module OpenTelemetry
|
13
|
+
module Config
|
14
|
+
# Handle fetching of remote configuration from aspecto
|
15
|
+
class RemoteConfig
|
16
|
+
def initialize(aspecto_auth, service_name, env, fallback_sampler)
|
17
|
+
@service_name = service_name
|
18
|
+
@env = env
|
19
|
+
@fallback_sampler = fallback_sampler
|
20
|
+
|
21
|
+
aspecto_config_url = ENV.fetch("ASPECTO_CONFIG_HOST", "https://config.aspecto.io")
|
22
|
+
@http_client = Faraday.new "#{aspecto_config_url}/config/#{aspecto_auth}" do |f|
|
23
|
+
f.response :json # decode response bodies as JSON
|
24
|
+
end
|
25
|
+
@http_client.options.timeout = 10
|
26
|
+
|
27
|
+
@scheduler = Rufus::Scheduler.new
|
28
|
+
@remote_config_poll_frequency = ENV.fetch("ASPECTO_REMOTE_CONFIG_POLL_FREQUENCY", "30s")
|
29
|
+
@scheduler.interval @remote_config_poll_frequency, first: :now do
|
30
|
+
update_config
|
31
|
+
end
|
32
|
+
|
33
|
+
::OpenTelemetry.logger.info "[Aspecto] SDK client initialized"
|
34
|
+
end
|
35
|
+
|
36
|
+
def shutdown
|
37
|
+
@scheduler.shutdown
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def update_config # rubocop:disable Metrics/AbcSize
|
43
|
+
::OpenTelemetry::Common::Utilities.untraced do
|
44
|
+
response = @http_client.get do |req|
|
45
|
+
req.headers["If-None-Match"] = @latest_config_etag unless @latest_config_etag.nil?
|
46
|
+
end
|
47
|
+
@latest_config_etag = response.headers["etag"]
|
48
|
+
return if response.status == 304
|
49
|
+
|
50
|
+
if response.status >= 400
|
51
|
+
::OpenTelemetry.logger.error("[Aspecto] error when trying to get remote config. will try again in #{@remote_config_poll_frequency}")
|
52
|
+
return
|
53
|
+
end
|
54
|
+
handle_new_config response.body if response.status < 300
|
55
|
+
end
|
56
|
+
rescue StandardError => e
|
57
|
+
::OpenTelemetry.logger.error "[Aspecto] updating remote config failed. using previous remote config"
|
58
|
+
::OpenTelemetry.logger.debug e
|
59
|
+
end
|
60
|
+
|
61
|
+
def handle_new_config(config)
|
62
|
+
::OpenTelemetry.logger.info("[Aspecto] successfully received remote configuration")
|
63
|
+
update_sampler config["samplingRules"]
|
64
|
+
end
|
65
|
+
|
66
|
+
def update_sampler(sampler_config)
|
67
|
+
rules_sampler = Sampler::RulesSampler.new sampler_config, @fallback_sampler, @service_name, @env
|
68
|
+
service_sampler = ::OpenTelemetry::SDK::Trace::Samplers.parent_based(root: rules_sampler)
|
69
|
+
message_process_sampler = Sampler::MessageProcessSampler.new rules_sampler, service_sampler
|
70
|
+
# updating the sampler should be thread safe as it's an atomic setter on a global value
|
71
|
+
::OpenTelemetry.tracer_provider.sampler = message_process_sampler
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -13,6 +13,7 @@ module Aspecto
|
|
13
13
|
self.log_level = ENV.fetch("OTEL_LOG_LEVEL", Logger::ERROR)
|
14
14
|
self.env = ENV["ASPECTO_ENV"] if ENV["ASPECTO_ENV"]
|
15
15
|
self.sampling_ratio = Float(ENV.fetch("ASPECTO_SAMPLING_RATIO", 1.0))
|
16
|
+
self.otel_exporter_otlp_traces_endpoint = ENV.fetch("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "https://otelcol.aspecto.io/v1/trace")
|
16
17
|
end
|
17
18
|
|
18
19
|
def service_name=(service_name)
|
@@ -23,7 +24,7 @@ module Aspecto
|
|
23
24
|
@override_resource_attributes[::OpenTelemetry::SemanticConventions::Resource::DEPLOYMENT_ENVIRONMENT] = env
|
24
25
|
end
|
25
26
|
|
26
|
-
attr_accessor :sampling_ratio, :log_level
|
27
|
+
attr_accessor :sampling_ratio, :log_level, :otel_exporter_otlp_traces_endpoint
|
27
28
|
attr_reader :aspecto_auth
|
28
29
|
|
29
30
|
def aspecto_auth=(aspecto_auth)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "./
|
3
|
+
require_relative "./operator"
|
4
4
|
|
5
5
|
module Aspecto
|
6
6
|
# Aspecto OpenTelemetry Distro
|
@@ -9,18 +9,17 @@ module Aspecto
|
|
9
9
|
# Aspecto's sampling rule condition
|
10
10
|
class Condition
|
11
11
|
def initialize(condition_config)
|
12
|
-
@
|
13
|
-
@value = condition_config["value"]
|
12
|
+
@operator = Operator.new condition_config["comparison"], condition_config["value"]
|
14
13
|
@from = condition_config["from"]&.to_sym
|
15
14
|
@key = condition_config["key"]
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
17
|
+
def satisfies?(attributes, span_name)
|
19
18
|
case @from
|
20
19
|
when :attribute
|
21
|
-
|
20
|
+
@operator.satisfies? attributes&.[](@key)&.to_s
|
22
21
|
when :operation
|
23
|
-
|
22
|
+
@operator.satisfies? span_name
|
24
23
|
else
|
25
24
|
# Other "from" are not implemented for now
|
26
25
|
false
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "opentelemetry/semantic_conventions"
|
4
|
+
require "opentelemetry-api"
|
5
|
+
|
6
|
+
module Aspecto
|
7
|
+
# Aspecto OpenTelemetry Distro
|
8
|
+
module OpenTelemetry
|
9
|
+
module Sampler
|
10
|
+
# OpenTelemetry sampler which implements the remote rules logic with fallback to service sampler
|
11
|
+
class MessageProcessSampler
|
12
|
+
def initialize(rules_sampler, service_sampler)
|
13
|
+
@rules_sampler = rules_sampler
|
14
|
+
@service_sampler = service_sampler
|
15
|
+
end
|
16
|
+
|
17
|
+
def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:)
|
18
|
+
if attributes && attributes[::OpenTelemetry::SemanticConventions::Trace::MESSAGING_OPERATION] == "process"
|
19
|
+
sampling_decision = should_sample_processing(parent_context, links, name, attributes)
|
20
|
+
return sampling_decision if sampling_decision
|
21
|
+
end
|
22
|
+
@service_sampler.should_sample?(trace_id: trace_id, parent_context: parent_context, links: links, name: name, kind: kind, attributes: attributes)
|
23
|
+
end
|
24
|
+
|
25
|
+
def description
|
26
|
+
"MessageProcessSampler"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def should_sample_processing(parent_context, links, name, attributes)
|
32
|
+
return if links.length.zero?
|
33
|
+
|
34
|
+
decision = link_decision(links[0])
|
35
|
+
|
36
|
+
# sidekiq receive side is a single "process" span which is the entry for the trace
|
37
|
+
is_root = ::OpenTelemetry::Trace.current_span(parent_context) == ::OpenTelemetry::Trace::Span::INVALID
|
38
|
+
if is_root # rubocop:disable Style/GuardClause
|
39
|
+
matched_rule = @rules_sampler.matching_rule(name: name, attributes: attributes)
|
40
|
+
rule_context = matched_rule&.context
|
41
|
+
return ::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: decision, tracestate: ::OpenTelemetry::Trace.current_span(parent_context).context.tracestate) if rule_context && rule_context["messaging.sampling.inherit_from_publisher"]
|
42
|
+
end
|
43
|
+
|
44
|
+
# if not root, then we need to check if the entry span set "messaging.sampling.inherit_from_publisher" on the context.
|
45
|
+
# since we currently only support sidekiq, it is not needed.
|
46
|
+
# but in the future, it can be implemented with context.attach when the rule is applied, and context.get here
|
47
|
+
end
|
48
|
+
|
49
|
+
def link_decision(link)
|
50
|
+
publisher_sampled = link.span_context.trace_flags.sampled?
|
51
|
+
publisher_sampled ? ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE : ::OpenTelemetry::SDK::Trace::Samplers::Decision::DROP
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aspecto
|
4
|
+
# Aspecto OpenTelemetry Distro
|
5
|
+
module OpenTelemetry
|
6
|
+
# Sampling logic for aspecto otel distribution
|
7
|
+
module Sampler
|
8
|
+
# A single operator used to evaluate sampling rules
|
9
|
+
class Operator
|
10
|
+
def initialize(operator, expected) # rubocop:disable Metrics/AbcSize
|
11
|
+
@expected = expected&.downcase
|
12
|
+
@expected = Regexp.new(@expected) if operator == "matches"
|
13
|
+
|
14
|
+
operator_to_proc = {
|
15
|
+
"eq" => proc { |actual| @expected == actual },
|
16
|
+
"ne" => proc { |actual| @expected != actual },
|
17
|
+
"starts_with" => proc { |actual| actual&.start_with?(expected) },
|
18
|
+
"ends_with" => proc { |actual| actual&.end_with?(expected) },
|
19
|
+
"contains" => proc { |actual| actual&.include?(expected) },
|
20
|
+
"not_contains" => proc { |actual| !actual&.include?(expected) },
|
21
|
+
"matches" => proc { |actual| @expected.match(actual) },
|
22
|
+
"defined" => proc { |actual| !actual.nil? },
|
23
|
+
"undefined" => proc { |actual| actual.nil? },
|
24
|
+
"any" => proc { true }
|
25
|
+
}
|
26
|
+
|
27
|
+
@evaluate = operator_to_proc.fetch(operator, proc { false })
|
28
|
+
end
|
29
|
+
|
30
|
+
def satisfies?(actual)
|
31
|
+
@evaluate.call actual&.downcase
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "./sampling_rule"
|
4
|
-
require_relative "./
|
4
|
+
require_relative "./operator"
|
5
5
|
|
6
6
|
module Aspecto
|
7
7
|
# Aspecto OpenTelemetry Distro
|
@@ -11,21 +11,25 @@ module Aspecto
|
|
11
11
|
class RulesSampler
|
12
12
|
def initialize(config, fallback_sampler, service_name, env)
|
13
13
|
@rules = config
|
14
|
-
.select { |rule|
|
15
|
-
.select { |rule|
|
14
|
+
.select { |rule| Operator.new(rule["packageName"]["comparison"], rule["packageName"]["value"]).satisfies?(service_name) }
|
15
|
+
.select { |rule| Operator.new(rule["env"]["comparison"], rule["env"]["value"]).satisfies?(env) }
|
16
16
|
.map { |rule_config| SamplingRule.new rule_config }
|
17
17
|
@fallback_sampler = fallback_sampler
|
18
18
|
end
|
19
19
|
|
20
20
|
def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:)
|
21
|
-
|
22
|
-
delegate_sampler =
|
21
|
+
rule = matching_rule(name: name, attributes: attributes)
|
22
|
+
delegate_sampler = rule ? rule.sampler : @fallback_sampler
|
23
23
|
delegate_sampler.should_sample?(trace_id: trace_id, parent_context: parent_context, links: links, name: name, kind: kind, attributes: attributes)
|
24
24
|
end
|
25
25
|
|
26
26
|
def description
|
27
27
|
"RulesSampler {#{rules.length} rules}"
|
28
28
|
end
|
29
|
+
|
30
|
+
def matching_rule(name:, attributes:)
|
31
|
+
@rules.find { |rule| rule.satisfies?(attributes, name) }
|
32
|
+
end
|
29
33
|
end
|
30
34
|
end
|
31
35
|
end
|
@@ -13,12 +13,13 @@ module Aspecto
|
|
13
13
|
@id = rule_config["_id"]
|
14
14
|
@sampler = ::OpenTelemetry::SDK::Trace::Samplers.trace_id_ratio_based(rule_config["samplingRate"])
|
15
15
|
@conditions = rule_config["conditions"].map { |condition_config| Condition.new condition_config }
|
16
|
+
@context = rule_config["context"]
|
16
17
|
end
|
17
18
|
|
18
|
-
attr_reader :sampler
|
19
|
+
attr_reader :sampler, :context
|
19
20
|
|
20
|
-
def
|
21
|
-
@conditions.all? { |condition| condition.
|
21
|
+
def satisfies?(attributes, span_name)
|
22
|
+
@conditions.all? { |condition| condition.satisfies?(attributes, span_name) }
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
@@ -4,8 +4,7 @@ require_relative "opentelemetry/version"
|
|
4
4
|
require_relative "opentelemetry/configurator"
|
5
5
|
require_relative "opentelemetry/resource/detectors/aspecto"
|
6
6
|
require_relative "opentelemetry/resource/detectors/deployment"
|
7
|
-
require_relative "opentelemetry/remote_config"
|
8
|
-
require_relative "opentelemetry/sampler/rules_sampler"
|
7
|
+
require_relative "opentelemetry/config/remote_config"
|
9
8
|
|
10
9
|
require "opentelemetry/sdk"
|
11
10
|
require "opentelemetry/exporter/otlp"
|
@@ -31,36 +30,45 @@ module Aspecto
|
|
31
30
|
c.resource = Aspecto::OpenTelemetry::Resource::Detectors::Deployment.detect
|
32
31
|
c.resource = configurator.config_override_resource # must be last
|
33
32
|
c.use_all "OpenTelemetry::Instrumentation::ActionPack" => { enable_recognize_route: true },
|
34
|
-
"OpenTelemetry::Instrumentation::AwsSdk" => {
|
33
|
+
"OpenTelemetry::Instrumentation::AwsSdk" => {
|
34
|
+
suppress_internal_instrumentation: true,
|
35
|
+
inject_messaging_context: true,
|
36
|
+
extract_messaging_context: true
|
37
|
+
}
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
)
|
42
|
-
)
|
39
|
+
otlp_exporter = ::OpenTelemetry::Exporter::OTLP::Exporter.new(endpoint: configurator.otel_exporter_otlp_traces_endpoint, headers: {
|
40
|
+
"Authorization" => configurator.aspecto_auth
|
41
|
+
})
|
42
|
+
span_processor = ::OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new otlp_exporter
|
43
|
+
c.add_span_processor span_processor
|
43
44
|
|
44
45
|
at_exit do
|
45
|
-
|
46
|
+
# at_exit might be call when service terminates
|
47
|
+
# but can also be the initiator or the application like with sinatra:
|
48
|
+
# https://github.com/sinatra/sinatra/blob/cd503e6c590cd48c2c9bb7869522494bfc62cb14/lib/sinatra/main.rb#L25
|
49
|
+
span_processor.force_flush timeout: 2
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
49
53
|
fallback_sampler = ::OpenTelemetry::SDK::Trace::Samplers.trace_id_ratio_based(configurator.sampling_ratio)
|
50
|
-
remote_config = fetch_config configurator.aspecto_auth
|
51
54
|
# TODO: how to properly extract the data from resource?
|
52
55
|
_, service_name = ::OpenTelemetry.tracer_provider.resource.attribute_enumerator.detect { |elem| elem[0] == ::OpenTelemetry::SemanticConventions::Resource::SERVICE_NAME }
|
53
56
|
_, env = ::OpenTelemetry.tracer_provider.resource.attribute_enumerator.detect { |elem| elem[0] == ::OpenTelemetry::SemanticConventions::Resource::DEPLOYMENT_ENVIRONMENT }
|
54
|
-
|
55
|
-
::OpenTelemetry.tracer_provider.sampler = ::OpenTelemetry::SDK::Trace::Samplers.parent_based(root: rules_sampler)
|
57
|
+
@remote_config_service = Config::RemoteConfig.new configurator.aspecto_auth, service_name, env, fallback_sampler
|
56
58
|
rescue StandardError => e
|
57
59
|
warn "Failed to initialize Aspecto tracing."
|
58
60
|
warn e
|
59
61
|
end
|
60
62
|
|
63
|
+
def shutdown
|
64
|
+
::OpenTelemetry.logger.info("Aspecto is shuting down. tracing is now stopped")
|
65
|
+
::OpenTelemetry.tracer_provider.shutdown
|
66
|
+
@remote_config_service&.shutdown
|
67
|
+
end
|
68
|
+
|
61
69
|
def validate_configurator_options(configurator)
|
62
70
|
# aspecto_auth
|
63
|
-
unless configurator.aspecto_auth.instance_of?(String)
|
71
|
+
unless configurator.aspecto_auth.instance_of?(String) && !configurator.aspecto_auth.empty?
|
64
72
|
raise "
|
65
73
|
Unable to retrieve Aspecto token.
|
66
74
|
In order for the Aspecto service to work, it requires an auth token.
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aspecto-opentelemetry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aspecto
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aspecto-opentelemetry-instrumentation-aws_sdk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.8
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.8
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: faraday
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,61 +53,61 @@ dependencies:
|
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '1.2'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: opentelemetry-exporter-otlp
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - '='
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
61
|
+
version: 0.21.0
|
48
62
|
type: :runtime
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - '='
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
68
|
+
version: 0.21.0
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name: opentelemetry-
|
70
|
+
name: opentelemetry-instrumentation-all
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - '='
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
75
|
+
version: 0.22.0
|
62
76
|
type: :runtime
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - '='
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
82
|
+
version: 0.22.0
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name: opentelemetry-
|
84
|
+
name: opentelemetry-sdk
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - '='
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
89
|
+
version: 1.0.2
|
76
90
|
type: :runtime
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
94
|
- - '='
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
96
|
+
version: 1.0.2
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
98
|
+
name: rufus-scheduler
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
|
-
- -
|
101
|
+
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
103
|
+
version: '3.8'
|
90
104
|
type: :runtime
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
|
-
- -
|
108
|
+
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
110
|
+
version: '3.8'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: minitest
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +122,20 @@ dependencies:
|
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: '5.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: opentelemetry-common
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.19.3
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.19.3
|
111
139
|
- !ruby/object:Gem::Dependency
|
112
140
|
name: rake
|
113
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +150,20 @@ dependencies:
|
|
122
150
|
- - "~>"
|
123
151
|
- !ruby/object:Gem::Version
|
124
152
|
version: '13.0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rspec-mocks
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
125
167
|
- !ruby/object:Gem::Dependency
|
126
168
|
name: rubocop
|
127
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -151,14 +193,15 @@ files:
|
|
151
193
|
- Rakefile
|
152
194
|
- lib/aspecto/auto_instrument.rb
|
153
195
|
- lib/aspecto/opentelemetry.rb
|
196
|
+
- lib/aspecto/opentelemetry/config/remote_config.rb
|
154
197
|
- lib/aspecto/opentelemetry/configurator.rb
|
155
|
-
- lib/aspecto/opentelemetry/remote_config.rb
|
156
198
|
- lib/aspecto/opentelemetry/resource/detectors/aspecto.rb
|
157
199
|
- lib/aspecto/opentelemetry/resource/detectors/deployment.rb
|
158
200
|
- lib/aspecto/opentelemetry/sampler/condition.rb
|
201
|
+
- lib/aspecto/opentelemetry/sampler/message_process_sampler.rb
|
202
|
+
- lib/aspecto/opentelemetry/sampler/operator.rb
|
159
203
|
- lib/aspecto/opentelemetry/sampler/rules_sampler.rb
|
160
204
|
- lib/aspecto/opentelemetry/sampler/sampling_rule.rb
|
161
|
-
- lib/aspecto/opentelemetry/sampler/utils.rb
|
162
205
|
- lib/aspecto/opentelemetry/version.rb
|
163
206
|
homepage: https://docs.aspecto.io/
|
164
207
|
licenses:
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "faraday"
|
4
|
-
require "faraday_middleware"
|
5
|
-
|
6
|
-
module Aspecto
|
7
|
-
# Aspecto's OpenTelemetry distribution
|
8
|
-
module OpenTelemetry
|
9
|
-
def self.fetch_config(aspecto_auth)
|
10
|
-
aspecto_config_url = ENV["ASPECTO_CONFIG_HOST"] || "https://config.aspecto.io"
|
11
|
-
|
12
|
-
conn = Faraday.new "#{aspecto_config_url}/config/#{aspecto_auth}" do |f|
|
13
|
-
f.response :json # decode response bodies as JSON
|
14
|
-
end
|
15
|
-
|
16
|
-
::OpenTelemetry::Common::Utilities.untraced do
|
17
|
-
response = conn.get
|
18
|
-
response.body
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Aspecto
|
4
|
-
# Aspecto OpenTelemetry Distro
|
5
|
-
module OpenTelemetry
|
6
|
-
# Sampling logic for aspecto otel distribution
|
7
|
-
module Sampler
|
8
|
-
extend self
|
9
|
-
|
10
|
-
def meets_operator?(operator, expected, actual) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
11
|
-
expected_lower = expected&.downcase
|
12
|
-
actual_lower = actual&.downcase
|
13
|
-
case operator
|
14
|
-
when :eq
|
15
|
-
expected_lower == actual_lower
|
16
|
-
when :ne
|
17
|
-
expected_lower != actual_lower
|
18
|
-
when :starts_with
|
19
|
-
actual_lower.start_with?(expected_lower)
|
20
|
-
when :ends_with
|
21
|
-
actual_lower.end_with?(expected_lower)
|
22
|
-
when :contains
|
23
|
-
actual_lower.include?(expected_lower)
|
24
|
-
when :not_contains
|
25
|
-
!actual_lower.include?(expected_lower)
|
26
|
-
when :matches
|
27
|
-
Regexp.new(expected_lower).match(actual_lower)
|
28
|
-
when :defined
|
29
|
-
!actual.nil?
|
30
|
-
when :undefined
|
31
|
-
actual.nil?
|
32
|
-
when :any
|
33
|
-
true
|
34
|
-
else
|
35
|
-
false
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|