aspecto-opentelemetry 0.1.5 → 0.1.8

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: 42bf7169cad40cd8e0fc53c8ef85c766f783330e06ed185c5bb14fcf48ce4d4d
4
- data.tar.gz: aa44a60faa0b08052bbfb60458e53697fe6c11c22f0b64fe7285a4f4cec19635
3
+ metadata.gz: c6dc0881d253f1a91182a09ae64a3464bc935b63744ea68654011bb317b1278b
4
+ data.tar.gz: b42327874bf790cfa4d4fbcea31f84a4bd9a8e31cf94362329f25c195202ec4f
5
5
  SHA512:
6
- metadata.gz: 81009b5b65381793e19eee34fb04c579a910952a4ad965b6b7879f69d15ac42477ec274380895a38a49b6fd31a991090a7ac7ab6ca9b1bbc27da524ceae23971
7
- data.tar.gz: 2a1ac520558ef20b6fa09e7cad8c310b0507336ed7f1ab715bfe42ae8ab2df5dd8670e7132810b009e7289298d4572d16370cf45b8199d9d318b8ea47917448d
6
+ metadata.gz: da5bc77255107ff1236861ab1ffcf61a98e27af86ad9a12787fa03f6d8d6ef3f32a7bb1de1032a717f0f51c647f59942924fd162e507c039d785f6b0272ea905
7
+ data.tar.gz: 905b157c77e02bb808ca5c4e5a2a644156b79be3fd8d39b2f8ebbc2a32c4cb7651d6ef4c6dec5b041d37ef574a05085a1d03dbc21f165ca5b32a217c613f6482
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Aspecto::OpenTelemetry
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/aspecto-opentelemetry.svg)](https://badge.fury.io/rb/aspecto-opentelemetry)
4
+
3
5
  Aspecto's SDK for ruby.
4
6
  This gem is a distribution of OpenTelemetry pre-configured to use all available instrumentations and export trace data to Aspecto.
5
7
 
@@ -97,14 +99,18 @@ The only required config options are [`aspecto_auth`](https://app.aspecto.io/app
97
99
 
98
100
  ### Configuration Options
99
101
 
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 |
102
+ | Option Name | Environment Variable | Type | Default | Description |
103
+ | --- | --- | --- | --- | --- |
104
+ | `aspecto_auth` | `ASPECTO_AUTH` | UUID string | - | Aspecto's [API key](https://app.aspecto.io/app/integration/api-key) for authentication |
105
+ | `service_name` | `OTEL_SERVICE_NAME` | string | - | Name of the service which is sending telemetry|
106
+ | `env` | `ASPECTO_ENV` | string | Extracted from Rails or Sinatra if used | Deployment environment: `production` / `staging` / `development`, etc. |
107
+ | `log_level` | `OTEL_LOG_LEVEL` | string | `ERROR` | `ERROR` / `WARN` / `INFO`, etc. |
108
+ | `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 |
109
+ | `otel_exporter_otlp_traces_endpoint` | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | URL | `https://otelcol.aspecto.io/v1/trace` | Url |
110
+ | `require_config_for_traces` | `ASPECTO_REQUIRE_CONFIG_FOR_TRACES` | boolean | `false` | When `true`, the SDK will not trace anything until remote sampling configuration arrives (few hundreds ms). Can be used to enforce sampling configuration is always applied, with the cost of losing traces generated during service startup. |
111
+ | `extract_b3_context` | `ASPECTO_EXTRACT_B3_CONTEXT` | boolean | `false` | Set to `true` when the service receives requests from another instrumented component that propagate context via B3 protocol multi or single header. For example: Envoy Proxy, Ambassador and Istio |
112
+ | `inject_b3_context_single_header` | `ASPECTO_INJECT_B3_CONTEXT_SINGLE_HEADER` | boolean | `false` | Set to `true` when the service send traffic to another instrumented component that propagate context via B3 **single header** protocol |
113
+ | `inject_b3_context_multi_header` | `ASPECTO_INJECT_B3_CONTEXT_MULTI_HEADER` | boolean | `false` | Set to `true` when the service send traffic to another instrumented component that propagate context via B3 **multi header** protocol. For example: Envoy Proxy, Istio |
108
114
 
109
115
  ## Contributing
110
116
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "faraday"
4
- require "faraday_middleware"
5
3
  require "rufus/scheduler"
4
+ require "net/http"
5
+ require "json"
6
6
 
7
7
  require_relative "../sampler/rules_sampler"
8
8
  require_relative "../sampler/message_process_sampler"
@@ -17,12 +17,9 @@ module Aspecto
17
17
  @service_name = service_name
18
18
  @env = env
19
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
20
+ aspecto_config_host = ENV.fetch("ASPECTO_CONFIG_HOST", "https://config.aspecto.io")
21
+ @aspecto_config_url = URI("#{aspecto_config_host}/config/#{aspecto_auth}")
22
+ init_http_client
26
23
 
27
24
  @scheduler = Rufus::Scheduler.new
28
25
  @remote_config_poll_frequency = ENV.fetch("ASPECTO_REMOTE_CONFIG_POLL_FREQUENCY", "30s")
@@ -39,19 +36,36 @@ module Aspecto
39
36
 
40
37
  private
41
38
 
39
+ def init_http_client
40
+ write_timeout_supported = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6")
41
+
42
+ @http_client = Net::HTTP.new(@aspecto_config_url.host, @aspecto_config_url.port)
43
+ @http_client.read_timeout = 10
44
+ @http_client.open_timeout = 10
45
+ @http_client.write_timeout = 10 if write_timeout_supported
46
+ @http_client.max_retries = 0
47
+
48
+ # use uri.scheme == 'https' instead
49
+ @http_client.use_ssl = true
50
+ @http_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
51
+ end
52
+
42
53
  def update_config # rubocop:disable Metrics/AbcSize
43
54
  ::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
55
+ request = Net::HTTP::Get.new(@aspecto_config_url.path)
56
+ request["If-None-Match"] = @latest_config_etag unless @latest_config_etag.nil?
57
+ response = @http_client.request(request)
58
+ response_code = response.code.to_i
49
59
 
50
- if response.status >= 400
60
+ return if response_code == 304
61
+
62
+ if response_code >= 400
51
63
  ::OpenTelemetry.logger.error("[Aspecto] error when trying to get remote config. will try again in #{@remote_config_poll_frequency}")
52
64
  return
53
65
  end
54
- handle_new_config response.body if response.status < 300
66
+
67
+ @latest_config_etag = response["etag"]
68
+ handle_new_config JSON.parse(response.body) if response_code < 300
55
69
  end
56
70
  rescue StandardError => e
57
71
  ::OpenTelemetry.logger.error "[Aspecto] updating remote config failed. using previous remote config"
@@ -4,7 +4,9 @@ module Aspecto
4
4
  module OpenTelemetry
5
5
  # Aspecto OpenTelemetry Distro Configurator
6
6
  class Configurator
7
- def initialize
7
+ TRUTHY_VALUES = %w[1 T t true TRUE True].freeze
8
+
9
+ def initialize # rubocop:disable Metrics/AbcSize
8
10
  # initialize config options from environment variables.
9
11
  # they can later be overwritten with configurator attribute setters
10
12
  # that have precedence over env
@@ -14,6 +16,12 @@ module Aspecto
14
16
  self.env = ENV["ASPECTO_ENV"] if ENV["ASPECTO_ENV"]
15
17
  self.sampling_ratio = Float(ENV.fetch("ASPECTO_SAMPLING_RATIO", 1.0))
16
18
  self.otel_exporter_otlp_traces_endpoint = ENV.fetch("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "https://otelcol.aspecto.io/v1/trace")
19
+ self.require_config_for_traces = self.class.bool_env_variable "ASPECTO_REQUIRE_CONFIG_FOR_TRACES", false
20
+
21
+ # b3 propagattor
22
+ self.extract_b3_context = self.class.bool_env_variable "ASPECTO_EXTRACT_B3_CONTEXT", false
23
+ self.inject_b3_context_single_header = self.class.bool_env_variable "ASPECTO_INJECT_B3_CONTEXT_SINGLE_HEADER", false
24
+ self.inject_b3_context_multi_header = self.class.bool_env_variable "ASPECTO_INJECT_B3_CONTEXT_MULTI_HEADER", false
17
25
  end
18
26
 
19
27
  def service_name=(service_name)
@@ -24,7 +32,7 @@ module Aspecto
24
32
  @override_resource_attributes[::OpenTelemetry::SemanticConventions::Resource::DEPLOYMENT_ENVIRONMENT] = env
25
33
  end
26
34
 
27
- attr_accessor :sampling_ratio, :log_level, :otel_exporter_otlp_traces_endpoint
35
+ attr_accessor :sampling_ratio, :log_level, :otel_exporter_otlp_traces_endpoint, :require_config_for_traces, :extract_b3_context, :inject_b3_context_single_header, :inject_b3_context_multi_header
28
36
  attr_reader :aspecto_auth
29
37
 
30
38
  def aspecto_auth=(aspecto_auth)
@@ -35,6 +43,13 @@ module Aspecto
35
43
  def config_override_resource
36
44
  ::OpenTelemetry::SDK::Resources::Resource.create(@override_resource_attributes)
37
45
  end
46
+
47
+ def self.bool_env_variable(env_variable_name, default_value)
48
+ env_value = ENV[env_variable_name]
49
+ return default_value if env_value.nil?
50
+
51
+ TRUTHY_VALUES.include?(env_value.strip)
52
+ end
38
53
  end
39
54
  end
40
55
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "opentelemetry"
4
+ require "opentelemetry-propagator-b3"
5
+
6
+ module Aspecto
7
+ module OpenTelemetry
8
+ module Propagator
9
+ # Aspecto OpenTelemetry Propagator Configuration
10
+ module Aspecto
11
+ extend self
12
+
13
+ W3C_PROPAGATOR = ::OpenTelemetry::Trace::Propagation::TraceContext.text_map_propagator
14
+ B3_SINGLE_PROPAGATOR = ::OpenTelemetry::Propagator::B3::Single.text_map_propagator
15
+ B3_MULTI_PROPAGATOR = ::OpenTelemetry::Propagator::B3::Multi.text_map_propagator
16
+
17
+ def from_configurator(configurator)
18
+ injectors = [W3C_PROPAGATOR]
19
+ injectors.push(B3_SINGLE_PROPAGATOR) if configurator.inject_b3_context_single_header
20
+ injectors.push(B3_MULTI_PROPAGATOR) if configurator.inject_b3_context_multi_header
21
+
22
+ extractors = [W3C_PROPAGATOR]
23
+ extractors.push(B3_SINGLE_PROPAGATOR, B3_MULTI_PROPAGATOR) if configurator.extract_b3_context
24
+
25
+ ::OpenTelemetry::Context::Propagation::CompositeTextMapPropagator.compose(injectors: injectors, extractors: extractors)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -29,7 +29,7 @@ module Aspecto
29
29
  private
30
30
 
31
31
  def should_sample_processing(parent_context, links, name, attributes)
32
- return if links.length.zero?
32
+ return if !links || links.length.zero?
33
33
 
34
34
  decision = link_decision(links[0])
35
35
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Aspecto
4
4
  module OpenTelemetry
5
- VERSION = "0.1.5"
5
+ VERSION = "0.1.8"
6
6
  end
7
7
  end
@@ -2,14 +2,15 @@
2
2
 
3
3
  require_relative "opentelemetry/version"
4
4
  require_relative "opentelemetry/configurator"
5
+ require_relative "opentelemetry/propagator/aspecto"
5
6
  require_relative "opentelemetry/resource/detectors/aspecto"
6
- require_relative "opentelemetry/resource/detectors/deployment"
7
7
  require_relative "opentelemetry/config/remote_config"
8
8
 
9
9
  require "opentelemetry/sdk"
10
10
  require "opentelemetry/exporter/otlp"
11
11
  require "opentelemetry/instrumentation/all"
12
12
  require "opentelemetry-instrumentation-aws_sdk"
13
+ require "opentelemetry-resource-detector-deployment"
13
14
 
14
15
  module Aspecto
15
16
  # Aspecto OpenTelemetry Distro
@@ -27,7 +28,7 @@ module Aspecto
27
28
  ::OpenTelemetry::SDK.configure do |c|
28
29
  c.logger = Logger.new($stdout, level: configurator.log_level)
29
30
  c.resource = Aspecto::OpenTelemetry::Resource::Detectors::Aspecto.detect
30
- c.resource = Aspecto::OpenTelemetry::Resource::Detectors::Deployment.detect
31
+ c.resource = ::OpenTelemetry::Resource::Detector::Deployment.detect
31
32
  c.resource = configurator.config_override_resource # must be last
32
33
  c.use_all "OpenTelemetry::Instrumentation::ActionPack" => { enable_recognize_route: true },
33
34
  "OpenTelemetry::Instrumentation::AwsSdk" => {
@@ -50,6 +51,15 @@ module Aspecto
50
51
  end
51
52
  end
52
53
 
54
+ # Propagation
55
+ ::OpenTelemetry.propagation = ::Aspecto::OpenTelemetry::Propagator::Aspecto.from_configurator configurator
56
+
57
+ # Sampling
58
+ if configurator.require_config_for_traces
59
+ ::OpenTelemetry.logger.info "[Aspecto] Require config for traces. Applying ALWAYS_OFF sampler"
60
+ ::OpenTelemetry.tracer_provider.sampler = ::OpenTelemetry::SDK::Trace::Samplers::ALWAYS_OFF
61
+ end
62
+
53
63
  fallback_sampler = ::OpenTelemetry::SDK::Trace::Samplers.trace_id_ratio_based(configurator.sampling_ratio)
54
64
  # TODO: how to properly extract the data from resource?
55
65
  _, service_name = ::OpenTelemetry.tracer_provider.resource.attribute_enumerator.detect { |elem| elem[0] == ::OpenTelemetry::SemanticConventions::Resource::SERVICE_NAME }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspecto-opentelemetry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aspecto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-21 00:00:00.000000000 Z
11
+ date: 2022-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aspecto-opentelemetry-instrumentation-aws_sdk
@@ -25,61 +25,61 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.1.8
27
27
  - !ruby/object:Gem::Dependency
28
- name: faraday
28
+ name: opentelemetry-exporter-otlp
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: '1.8'
33
+ version: 0.21.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: '1.8'
40
+ version: 0.21.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: faraday_middleware
42
+ name: opentelemetry-instrumentation-all
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: '1.2'
47
+ version: 0.22.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: '1.2'
54
+ version: 0.22.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: opentelemetry-exporter-otlp
56
+ name: opentelemetry-propagator-b3
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 0.21.0
61
+ version: 0.19.2
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - '='
67
67
  - !ruby/object:Gem::Version
68
- version: 0.21.0
68
+ version: 0.19.2
69
69
  - !ruby/object:Gem::Dependency
70
- name: opentelemetry-instrumentation-all
70
+ name: opentelemetry-resource-detector-deployment
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - '='
74
74
  - !ruby/object:Gem::Version
75
- version: 0.22.0
75
+ version: 0.0.1
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
- version: 0.22.0
82
+ version: 0.0.1
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: opentelemetry-sdk
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -195,8 +195,8 @@ files:
195
195
  - lib/aspecto/opentelemetry.rb
196
196
  - lib/aspecto/opentelemetry/config/remote_config.rb
197
197
  - lib/aspecto/opentelemetry/configurator.rb
198
+ - lib/aspecto/opentelemetry/propagator/aspecto.rb
198
199
  - lib/aspecto/opentelemetry/resource/detectors/aspecto.rb
199
- - lib/aspecto/opentelemetry/resource/detectors/deployment.rb
200
200
  - lib/aspecto/opentelemetry/sampler/condition.rb
201
201
  - lib/aspecto/opentelemetry/sampler/message_process_sampler.rb
202
202
  - lib/aspecto/opentelemetry/sampler/operator.rb
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aspecto
4
- module OpenTelemetry
5
- module Resource
6
- module Detectors
7
- # Deployment contains detect class method for determining deployment resource attributes
8
- module Deployment
9
- extend self
10
-
11
- def detect
12
- resource_attributes = {}
13
- deployment_environment = rails_env || sinatra_env || rack_env
14
- resource_attributes[::OpenTelemetry::SemanticConventions::Resource::DEPLOYMENT_ENVIRONMENT] = deployment_environment if deployment_environment
15
- ::OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
16
- end
17
-
18
- private
19
-
20
- def rails_env
21
- # rails extract env like this:
22
- # https://github.com/rails/rails/blob/5647a9c1ced68d20338552d47a3b755e10a271c4/railties/lib/rails.rb#L74
23
- # ActiveSupport::EnvironmentInquirer.new(ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development")
24
- ::Rails.env.to_s if defined?(::Rails.env)
25
- end
26
-
27
- def rack_env
28
- ENV["RACK_ENV"]
29
- end
30
-
31
- def sinatra_env
32
- # https://github.com/sinatra/sinatra/blob/e69b6b9dee7165d3a583fc8a6af10ceee1ea687d/lib/sinatra/base.rb#L1801
33
- # cases:
34
- #
35
- # 1. if sinatra is "require"d before the detector, then we return the value from the library
36
- # this case will return the default "development" fallback if not env variable is set which is good.
37
- #
38
- # 2. if sinatra is "require"d after the detector, then:
39
- # 2.1 if user is setting environment via 'APP_ENV' or 'RACK_ENV' then those value will be picked up and reported
40
- # 2.2 else, the sinatra environment will fallback to "development", but detector will return nil for it.
41
- # this issue is not covered, as when detector initialize the immutable resource, it has no way
42
- # of knowing if "sinatra" will be required later or not.
43
- (::Sinatra::Base.environment.to_s if defined?(::Sinatra::Base.environment)) || ENV["APP_ENV"]
44
- end
45
- end
46
- end
47
- end
48
- end
49
- end