aspecto-opentelemetry 0.1.6 → 0.1.9

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: c8d67f2909e02b637a0342f01aaab45d1cbb0b5fae5cc27271b76113fe7af183
4
- data.tar.gz: 1615052d3f62bb92ac80f91e3bc5a4f5f035ed9f361cefd4e7f459b6b3849d77
3
+ metadata.gz: 005e783e20aa54623dca0a11daae40ef3e3d8a6d9b83a13b6fd44f11a0a7f00b
4
+ data.tar.gz: 9edaffde8afa0cbfb8f3547aec3fd4ab3fc236395e70e6ecb1e60b83f5d563b1
5
5
  SHA512:
6
- metadata.gz: 7133ce0b64d98585b535fac5791f77b84850ed98c561ec820ea5ba9ebd282b04249810eae0af36cb34f7d52dcd0eb54c020432d03a0d0d967884362d49401f94
7
- data.tar.gz: 8686004ce06bb37f4b4e92623b708d7905e1b9dfdf26220853f0887bfb19293123e58b3a633b251f92f6c91bce788ee5b4bb5879b37ce36176a163488b2c3c36
6
+ metadata.gz: a220142b3826b3cf0d95e73c559e0ac5ce0e29828f98883fd6e7f8e7a9c4a486bfe6620a49f73cbc938d23c3b0c48f8782147a69779256719520532112ef1b6d
7
+ data.tar.gz: fd9e2c8b3513d25599411f2718ff1f3aa79b75425fd9d81f75f32e1ceb7b69c4228dfc46abc1f0dc64a7f020c2916ed47f1bc29d11539cabf87e91945ce38851
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,16 +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 |
108
- | `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.
109
- |
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 |
110
114
 
111
115
  ## Contributing
112
116
 
@@ -16,6 +16,8 @@ module Aspecto
16
16
  def initialize(aspecto_auth, service_name, env, fallback_sampler)
17
17
  @service_name = service_name
18
18
  @env = env
19
+ @error_reported = false
20
+ @got_remote_config = false
19
21
  @fallback_sampler = fallback_sampler
20
22
  aspecto_config_host = ENV.fetch("ASPECTO_CONFIG_HOST", "https://config.aspecto.io")
21
23
  @aspecto_config_url = URI("#{aspecto_config_host}/config/#{aspecto_auth}")
@@ -27,7 +29,10 @@ module Aspecto
27
29
  update_config
28
30
  end
29
31
 
30
- ::OpenTelemetry.logger.info "[Aspecto] SDK client initialized"
32
+ ::OpenTelemetry.logger.info "[Aspecto] initialized remote config polling"
33
+ rescue StandardError => e
34
+ ::OpenTelemetry.logger.error "[Aspecto] failed to initialize remote config polling"
35
+ ::OpenTelemetry.logger.error e
31
36
  end
32
37
 
33
38
  def shutdown
@@ -50,7 +55,7 @@ module Aspecto
50
55
  @http_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
51
56
  end
52
57
 
53
- def update_config # rubocop:disable Metrics/AbcSize
58
+ def update_config
54
59
  ::OpenTelemetry::Common::Utilities.untraced do
55
60
  request = Net::HTTP::Get.new(@aspecto_config_url.path)
56
61
  request["If-None-Match"] = @latest_config_etag unless @latest_config_etag.nil?
@@ -60,7 +65,7 @@ module Aspecto
60
65
  return if response_code == 304
61
66
 
62
67
  if response_code >= 400
63
- ::OpenTelemetry.logger.error("[Aspecto] error when trying to get remote config. will try again in #{@remote_config_poll_frequency}")
68
+ log_config_error "failed to get remote config with http response #{response_code}."
64
69
  return
65
70
  end
66
71
 
@@ -68,13 +73,14 @@ module Aspecto
68
73
  handle_new_config JSON.parse(response.body) if response_code < 300
69
74
  end
70
75
  rescue StandardError => e
71
- ::OpenTelemetry.logger.error "[Aspecto] updating remote config failed. using previous remote config"
72
- ::OpenTelemetry.logger.debug e
76
+ log_config_error "updating remote config failed with exception.", e
73
77
  end
74
78
 
75
79
  def handle_new_config(config)
76
- ::OpenTelemetry.logger.info("[Aspecto] successfully received remote configuration")
77
80
  update_sampler config["samplingRules"]
81
+ @error_reported = false
82
+ @got_remote_config = true
83
+ ::OpenTelemetry.logger.info("[Aspecto] successfully updated remote configuration")
78
84
  end
79
85
 
80
86
  def update_sampler(sampler_config)
@@ -84,6 +90,16 @@ module Aspecto
84
90
  # updating the sampler should be thread safe as it's an atomic setter on a global value
85
91
  ::OpenTelemetry.tracer_provider.sampler = message_process_sampler
86
92
  end
93
+
94
+ def log_config_error(msg, err = nil)
95
+ return if @error_reported # report every issue only once
96
+
97
+ retry_msg = "will try again until success every #{@remote_config_poll_frequency}."
98
+ previous_config_msg = @got_remote_config ? "using previous remote config" : "no previous config is avialable"
99
+ ::OpenTelemetry.logger.info("[Aspecto]: #{msg} #{retry_msg} #{previous_config_msg}")
100
+ ::OpenTelemetry.logger.info(err) if err
101
+ @error_reported = true
102
+ end
87
103
  end
88
104
  end
89
105
  end
@@ -6,7 +6,7 @@ module Aspecto
6
6
  class Configurator
7
7
  TRUTHY_VALUES = %w[1 T t true TRUE True].freeze
8
8
 
9
- def initialize
9
+ def initialize # rubocop:disable Metrics/AbcSize
10
10
  # initialize config options from environment variables.
11
11
  # they can later be overwritten with configurator attribute setters
12
12
  # that have precedence over env
@@ -16,7 +16,12 @@ module Aspecto
16
16
  self.env = ENV["ASPECTO_ENV"] if ENV["ASPECTO_ENV"]
17
17
  self.sampling_ratio = Float(ENV.fetch("ASPECTO_SAMPLING_RATIO", 1.0))
18
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 = TRUTHY_VALUES.include?(ENV["ASPECTO_REQUIRE_CONFIG_FOR_TRACES"]&.strip)
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
20
25
  end
21
26
 
22
27
  def service_name=(service_name)
@@ -27,7 +32,7 @@ module Aspecto
27
32
  @override_resource_attributes[::OpenTelemetry::SemanticConventions::Resource::DEPLOYMENT_ENVIRONMENT] = env
28
33
  end
29
34
 
30
- attr_accessor :sampling_ratio, :log_level, :otel_exporter_otlp_traces_endpoint, :require_config_for_traces
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
31
36
  attr_reader :aspecto_auth
32
37
 
33
38
  def aspecto_auth=(aspecto_auth)
@@ -38,6 +43,13 @@ module Aspecto
38
43
  def config_override_resource
39
44
  ::OpenTelemetry::SDK::Resources::Resource.create(@override_resource_attributes)
40
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
41
53
  end
42
54
  end
43
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.6"
5
+ VERSION = "0.1.9"
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,10 @@ 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
53
58
  if configurator.require_config_for_traces
54
59
  ::OpenTelemetry.logger.info "[Aspecto] Require config for traces. Applying ALWAYS_OFF sampler"
55
60
  ::OpenTelemetry.tracer_provider.sampler = ::OpenTelemetry::SDK::Trace::Samplers::ALWAYS_OFF
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.6
4
+ version: 0.1.9
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-23 00:00:00.000000000 Z
11
+ date: 2022-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aspecto-opentelemetry-instrumentation-aws_sdk
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - '='
53
53
  - !ruby/object:Gem::Version
54
54
  version: 0.22.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: opentelemetry-propagator-b3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.19.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.19.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: opentelemetry-resource-detector-deployment
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 0.0.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 0.0.1
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: opentelemetry-sdk
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -167,8 +195,8 @@ files:
167
195
  - lib/aspecto/opentelemetry.rb
168
196
  - lib/aspecto/opentelemetry/config/remote_config.rb
169
197
  - lib/aspecto/opentelemetry/configurator.rb
198
+ - lib/aspecto/opentelemetry/propagator/aspecto.rb
170
199
  - lib/aspecto/opentelemetry/resource/detectors/aspecto.rb
171
- - lib/aspecto/opentelemetry/resource/detectors/deployment.rb
172
200
  - lib/aspecto/opentelemetry/sampler/condition.rb
173
201
  - lib/aspecto/opentelemetry/sampler/message_process_sampler.rb
174
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