aspecto-opentelemetry 0.1.1 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd25c9da7c25d1c4c7e7a389189e7f0369dc7b97120425eda7cf49d9fb344b42
4
- data.tar.gz: '075783f49804d1e01c16b6b80d1c37ed3b0d1de4c05b6ed4f2cd19007c28ccfc'
3
+ metadata.gz: 42bf7169cad40cd8e0fc53c8ef85c766f783330e06ed185c5bb14fcf48ce4d4d
4
+ data.tar.gz: aa44a60faa0b08052bbfb60458e53697fe6c11c22f0b64fe7285a4f4cec19635
5
5
  SHA512:
6
- metadata.gz: eb1b4410ae3dfa99e61592663cc1f4dff627908632f784fc39938b72a698a9f220e2ce88feb636402ae7a1c791b3395ad7dd5a376daa240ee0398f62961a25a2
7
- data.tar.gz: 67606263a5e1dc5ecbf8375bd511fd97e9728dd8fd6fa259d746641abb8a488b8e88d81a0b72416a3aee8184c94f30f61e2600bda267cfba6d8a16d814b1d82a
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. This gem is a distribution of OpenTelemetry configured to use all available instrumentations and export trace data to Aspecto.
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
- Add this line to your application's Gemfile:
8
+ Install the gem using:
8
9
 
9
- ```ruby
10
- gem 'aspecto-opentelemetry'
10
+ ```zsh
11
+ $ gem install aspecto-opentelemetry
11
12
  ```
12
13
 
13
- And then execute:
14
+ Or, if you use [bundler](https://bundler.io), include aspecto-opentelemetry in your Gemfile.
14
15
 
15
- $ bundle install
16
+ ## Usage
16
17
 
17
- Or install it yourself as:
18
+ ### Rails Applications
18
19
 
19
- $ gem install aspecto-opentelemetry
20
+ #### Configuration in Code
20
21
 
21
- ## Usage
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
- ### Auto Instrumentation
35
+ #### Configuration via Environment Variables
24
36
 
25
- Does not require code changes. Just modify you `Gemfile` to add `require` like this:
37
+ In your Gemfile:
26
38
 
27
39
  ```
28
40
  gem 'aspecto-opentelemetry', require: 'aspecto/auto_instrument'
29
41
  ```
30
42
 
31
- When using auto-instrumentation, you need to pass relevant configuration options via environment variables.
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
- ### Manual Instrumentation
53
+ #### Configuration in Code
34
54
 
35
- Allow you to configure the SDK via code:
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
- Add this code at the beginning of your application.
44
- For rails - add this code to a file `aspecto.rb` under `config/initializers/`.
68
+ #### Configuration via Environment Variables
45
69
 
46
- ## Configuration
70
+ Add this require statement after your require other gems:
47
71
 
48
- You can set configuration it 2 ways: via environment variables or via code.
49
- The only required config options are `aspecto_auth` and `service_name`.
72
+ ```rb
73
+ require 'aspecto/auto_instrument'
74
+ ```
50
75
 
51
- ### Configuration in Code
76
+ And set environment variables:
52
77
 
53
- ```rb
54
- require 'aspecto/opentelemetry'
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
- Aspecto::OpenTelemetry::configure do |c|
57
- c.service_name = 'MY_SERIVCE'
58
- c.aspecto_auth = 'YOUR_TOKEN_HERE'
59
- c.env = 'MY_COOL_ENV'
60
- c.sampling_ratio = 0.1
61
- end
85
+ ### Shutdown
86
+
87
+ Call this function when your application shuts down
88
+
89
+ ```rb
90
+ Aspecto::OpenTelemetry::shutdown
62
91
  ```
63
92
 
64
- ### Configuration Options
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
- | Option Name | Environment Variable | Type | Default | Description |
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 "./utils"
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
- @comparison = condition_config["comparison"]&.to_sym
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 satisfied?(attributes, span_name)
17
+ def satisfies?(attributes, span_name)
19
18
  case @from
20
19
  when :attribute
21
- Sampler.meets_operator?(@comparison, @value, attributes[@key]&.to_s)
20
+ @operator.satisfies? attributes&.[](@key)&.to_s
22
21
  when :operation
23
- Sampler.meets_operator?(@comparison, @value, span_name)
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 "./utils"
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| Sampler.meets_operator?(rule["packageName"]["comparison"].to_sym, rule["packageName"]["value"], service_name) }
15
- .select { |rule| Sampler.meets_operator?(rule["env"]["comparison"].to_sym, rule["env"]["value"], env) }
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
- matching_rule = @rules.find { |rule| rule.satisfied?(attributes, name) }
22
- delegate_sampler = matching_rule ? matching_rule.sampler : @fallback_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 satisfied?(attributes, span_name)
21
- @conditions.all? { |condition| condition.satisfied?(attributes, span_name) }
21
+ def satisfies?(attributes, span_name)
22
+ @conditions.all? { |condition| condition.satisfies?(attributes, span_name) }
22
23
  end
23
24
  end
24
25
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Aspecto
4
4
  module OpenTelemetry
5
- VERSION = "0.1.1"
5
+ VERSION = "0.1.5"
6
6
  end
7
7
  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" => { suppress_internal_instrumentation: true }
33
+ "OpenTelemetry::Instrumentation::AwsSdk" => {
34
+ suppress_internal_instrumentation: true,
35
+ inject_messaging_context: true,
36
+ extract_messaging_context: true
37
+ }
35
38
 
36
- c.add_span_processor(
37
- ::OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
38
- ::OpenTelemetry::Exporter::OTLP::Exporter.new(endpoint: "https://otelcol.aspecto.io/v1/trace", headers: {
39
- "Authorization" => configurator.aspecto_auth
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
- ::OpenTelemetry.tracer_provider.shutdown
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
- rules_sampler = Sampler::RulesSampler.new remote_config["samplingRules"], fallback_sampler, service_name, env
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.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-23 00:00:00.000000000 Z
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: aspecto-opentelemetry-instrumentation-aws_sdk
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.1.4
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.1.4
68
+ version: 0.21.0
55
69
  - !ruby/object:Gem::Dependency
56
- name: opentelemetry-exporter-otlp
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.20.6
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.20.6
82
+ version: 0.22.0
69
83
  - !ruby/object:Gem::Dependency
70
- name: opentelemetry-instrumentation-all
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.21.3
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.21.3
96
+ version: 1.0.2
83
97
  - !ruby/object:Gem::Dependency
84
- name: opentelemetry-sdk
98
+ name: rufus-scheduler
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
- - - '='
101
+ - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: '1.0'
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: '1.0'
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