solarwinds_apm 5.1.9 → 6.0.0.preV2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +28 -55
  3. data/ext/oboe_metal/extconf.rb +37 -41
  4. data/ext/oboe_metal/lib/liboboe-1.0-aarch64.so.sha256 +1 -1
  5. data/ext/oboe_metal/lib/liboboe-1.0-alpine-aarch64.so.sha256 +1 -1
  6. data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.sha256 +1 -1
  7. data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.sha256 +1 -1
  8. data/ext/oboe_metal/src/VERSION +1 -1
  9. data/ext/oboe_metal/src/init_solarwinds_apm.cc +0 -6
  10. data/ext/oboe_metal/src/oboe_debug.h +1 -0
  11. data/lib/oboe_metal.rb +116 -80
  12. data/lib/rails/generators/solarwinds_apm/install_generator.rb +1 -5
  13. data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +42 -267
  14. data/lib/solarwinds_apm/api/current_trace_info.rb +148 -0
  15. data/lib/solarwinds_apm/api/tracing.rb +30 -0
  16. data/lib/solarwinds_apm/api/transaction_name.rb +58 -0
  17. data/lib/solarwinds_apm/api.rb +8 -15
  18. data/lib/solarwinds_apm/base.rb +4 -131
  19. data/lib/solarwinds_apm/config.rb +101 -174
  20. data/lib/solarwinds_apm/constants.rb +32 -0
  21. data/lib/solarwinds_apm/logger.rb +1 -1
  22. data/lib/solarwinds_apm/noop/context.rb +2 -5
  23. data/lib/solarwinds_apm/noop/metadata.rb +1 -2
  24. data/lib/solarwinds_apm/oboe_init_options.rb +74 -38
  25. data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +204 -0
  26. data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +166 -0
  27. data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +92 -0
  28. data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +72 -0
  29. data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +335 -0
  30. data/lib/solarwinds_apm/opentelemetry.rb +8 -0
  31. data/lib/solarwinds_apm/otel_config.rb +161 -0
  32. data/lib/solarwinds_apm/{inst → support}/logger_formatter.rb +5 -6
  33. data/lib/solarwinds_apm/{inst → support}/logging_log_event.rb +3 -6
  34. data/lib/solarwinds_apm/{inst → support}/lumberjack_formatter.rb +1 -4
  35. data/lib/solarwinds_apm/support/oboe_tracing_mode.rb +27 -0
  36. data/lib/solarwinds_apm/support/swomarginalia/LICENSE +20 -0
  37. data/lib/solarwinds_apm/support/swomarginalia/README.md +41 -0
  38. data/lib/solarwinds_apm/support/swomarginalia/comment.rb +205 -0
  39. data/lib/solarwinds_apm/support/swomarginalia/load_swomarginalia.rb +48 -0
  40. data/lib/solarwinds_apm/support/swomarginalia/railtie.rb +22 -0
  41. data/lib/solarwinds_apm/support/swomarginalia/swomarginalia.rb +86 -0
  42. data/lib/solarwinds_apm/support/transaction_cache.rb +24 -0
  43. data/lib/solarwinds_apm/support/transaction_settings.rb +26 -209
  44. data/lib/solarwinds_apm/support/transformer.rb +56 -0
  45. data/lib/solarwinds_apm/support/txn_name_manager.rb +25 -0
  46. data/lib/solarwinds_apm/support/x_trace_options.rb +42 -26
  47. data/lib/solarwinds_apm/support.rb +37 -10
  48. data/lib/solarwinds_apm/support_report.rb +10 -32
  49. data/lib/solarwinds_apm/thread_local.rb +1 -1
  50. data/lib/solarwinds_apm/version.rb +4 -4
  51. data/lib/solarwinds_apm.rb +29 -25
  52. metadata +63 -123
  53. data/.dockerignore +0 -5
  54. data/.gitignore +0 -58
  55. data/.rubocop.yml +0 -29
  56. data/.whitesource +0 -22
  57. data/.yardopts +0 -7
  58. data/CHANGELOG-appoptics.md +0 -766
  59. data/CHANGELOG.md +0 -82
  60. data/CONFIG.md +0 -31
  61. data/Gemfile +0 -15
  62. data/README.md +0 -385
  63. data/bin/solarwinds_apm_config +0 -15
  64. data/examples/prepend.rb +0 -13
  65. data/examples/sdk_examples.rb +0 -158
  66. data/ext/oboe_metal/README.md +0 -69
  67. data/ext/oboe_metal/extconf_local.rb +0 -75
  68. data/ext/oboe_metal/lib/.keep +0 -0
  69. data/ext/oboe_metal/noop/noop.c +0 -8
  70. data/ext/oboe_metal/src/README.md +0 -6
  71. data/ext/oboe_metal/src/frames.cc +0 -247
  72. data/ext/oboe_metal/src/frames.h +0 -40
  73. data/ext/oboe_metal/src/logging.cc +0 -97
  74. data/ext/oboe_metal/src/logging.h +0 -34
  75. data/ext/oboe_metal/src/profiling.cc +0 -435
  76. data/ext/oboe_metal/src/profiling.h +0 -78
  77. data/ext/oboe_metal/test/CMakeLists.txt +0 -53
  78. data/ext/oboe_metal/test/FindGMock.cmake +0 -43
  79. data/ext/oboe_metal/test/README.md +0 -56
  80. data/ext/oboe_metal/test/frames_test.cc +0 -164
  81. data/ext/oboe_metal/test/profiling_test.cc +0 -93
  82. data/ext/oboe_metal/test/ruby_inc_dir.rb +0 -8
  83. data/ext/oboe_metal/test/ruby_prefix.rb +0 -8
  84. data/ext/oboe_metal/test/ruby_test_helper.rb +0 -67
  85. data/ext/oboe_metal/test/test.h +0 -11
  86. data/ext/oboe_metal/test/test_main.cc +0 -32
  87. data/init.rb +0 -4
  88. data/lib/solarwinds_apm/api/layerinit.rb +0 -41
  89. data/lib/solarwinds_apm/api/logging.rb +0 -356
  90. data/lib/solarwinds_apm/api/memcache.rb +0 -37
  91. data/lib/solarwinds_apm/api/metrics.rb +0 -63
  92. data/lib/solarwinds_apm/api/util.rb +0 -98
  93. data/lib/solarwinds_apm/frameworks/grape.rb +0 -96
  94. data/lib/solarwinds_apm/frameworks/padrino.rb +0 -78
  95. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller.rb +0 -100
  96. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller5.rb +0 -50
  97. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller_api.rb +0 -50
  98. data/lib/solarwinds_apm/frameworks/rails/inst/action_view.rb +0 -88
  99. data/lib/solarwinds_apm/frameworks/rails/inst/active_record.rb +0 -26
  100. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +0 -29
  101. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +0 -22
  102. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +0 -103
  103. data/lib/solarwinds_apm/frameworks/rails/inst/logger_formatters.rb +0 -14
  104. data/lib/solarwinds_apm/frameworks/rails.rb +0 -100
  105. data/lib/solarwinds_apm/frameworks/sinatra.rb +0 -96
  106. data/lib/solarwinds_apm/inst/bunny-client.rb +0 -157
  107. data/lib/solarwinds_apm/inst/bunny-consumer.rb +0 -102
  108. data/lib/solarwinds_apm/inst/curb.rb +0 -289
  109. data/lib/solarwinds_apm/inst/dalli.rb +0 -89
  110. data/lib/solarwinds_apm/inst/delayed_job.rb +0 -100
  111. data/lib/solarwinds_apm/inst/excon.rb +0 -113
  112. data/lib/solarwinds_apm/inst/faraday.rb +0 -96
  113. data/lib/solarwinds_apm/inst/graphql.rb +0 -206
  114. data/lib/solarwinds_apm/inst/grpc_client.rb +0 -147
  115. data/lib/solarwinds_apm/inst/grpc_server.rb +0 -119
  116. data/lib/solarwinds_apm/inst/httpclient.rb +0 -182
  117. data/lib/solarwinds_apm/inst/memcached.rb +0 -86
  118. data/lib/solarwinds_apm/inst/mongo.rb +0 -246
  119. data/lib/solarwinds_apm/inst/mongo2.rb +0 -225
  120. data/lib/solarwinds_apm/inst/moped.rb +0 -466
  121. data/lib/solarwinds_apm/inst/net_http.rb +0 -60
  122. data/lib/solarwinds_apm/inst/rack.rb +0 -223
  123. data/lib/solarwinds_apm/inst/rack_cache.rb +0 -35
  124. data/lib/solarwinds_apm/inst/redis.rb +0 -280
  125. data/lib/solarwinds_apm/inst/redis_v4.rb +0 -273
  126. data/lib/solarwinds_apm/inst/resque.rb +0 -129
  127. data/lib/solarwinds_apm/inst/rest-client.rb +0 -43
  128. data/lib/solarwinds_apm/inst/sequel.rb +0 -241
  129. data/lib/solarwinds_apm/inst/sidekiq-client.rb +0 -63
  130. data/lib/solarwinds_apm/inst/sidekiq-worker.rb +0 -64
  131. data/lib/solarwinds_apm/inst/typhoeus.rb +0 -90
  132. data/lib/solarwinds_apm/instrumentation.rb +0 -22
  133. data/lib/solarwinds_apm/loading.rb +0 -65
  134. data/lib/solarwinds_apm/noop/profiling.rb +0 -21
  135. data/lib/solarwinds_apm/ruby.rb +0 -35
  136. data/lib/solarwinds_apm/sdk/current_trace_info.rb +0 -123
  137. data/lib/solarwinds_apm/sdk/custom_metrics.rb +0 -94
  138. data/lib/solarwinds_apm/sdk/logging.rb +0 -37
  139. data/lib/solarwinds_apm/sdk/trace_context_headers.rb +0 -69
  140. data/lib/solarwinds_apm/sdk/tracing.rb +0 -432
  141. data/lib/solarwinds_apm/support/profiling.rb +0 -25
  142. data/lib/solarwinds_apm/support/trace_context.rb +0 -53
  143. data/lib/solarwinds_apm/support/trace_state.rb +0 -69
  144. data/lib/solarwinds_apm/support/trace_string.rb +0 -89
  145. data/lib/solarwinds_apm/support/transaction_metrics.rb +0 -67
  146. data/lib/solarwinds_apm/test.rb +0 -165
  147. data/lib/solarwinds_apm/util.rb +0 -426
  148. data/log/.keep +0 -0
  149. data/log/postgresql/.keep +0 -0
  150. data/solarwinds_apm.gemspec +0 -55
  151. data/yardoc_frontpage.md +0 -24
@@ -0,0 +1,92 @@
1
+ module SolarWindsAPM
2
+ module OpenTelemetry
3
+ module SolarWindsPropagator
4
+ # TextMapPropagator
5
+ class TextMapPropagator
6
+ TRACESTATE_HEADER_NAME = "tracestate".freeze
7
+ XTRACEOPTIONS_HEADER_NAME = "x-trace-options".freeze
8
+ XTRACEOPTIONS_SIGNATURE_HEADER_NAME = "x-trace-options-signature".freeze
9
+ INTL_SWO_X_OPTIONS_KEY = "sw_xtraceoptions".freeze
10
+ INTL_SWO_SIGNATURE_KEY = "sw_signature".freeze
11
+
12
+ private_constant \
13
+ :TRACESTATE_HEADER_NAME, :XTRACEOPTIONS_HEADER_NAME,
14
+ :XTRACEOPTIONS_SIGNATURE_HEADER_NAME, :INTL_SWO_X_OPTIONS_KEY, :INTL_SWO_SIGNATURE_KEY
15
+
16
+ # Extract trace context from the supplied carrier.
17
+ #
18
+ # @param [Carrier] carrier The carrier to get the header from
19
+ # @param [optional Context] context Context to be updated with the trace context
20
+ # extracted from the carrier. Defaults to +Context.current+.
21
+ # @param [optional Getter] getter If the optional getter is provided, it
22
+ # will be used to read the header from the carrier, otherwise the default
23
+ # text map getter will be used.
24
+ #
25
+ # @return [Context] context updated with extracted baggage, or the original context
26
+ # if extraction fails
27
+ def extract(carrier, context: ::OpenTelemetry::Context.current, getter: ::OpenTelemetry::Context::Propagation.text_map_getter)
28
+
29
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] context(before): #{context.inspect} #{context.nil?}"}
30
+
31
+ context = ::OpenTelemetry::Context.new({}) if context.nil?
32
+
33
+ xtraceoptions_header = getter.get(carrier, XTRACEOPTIONS_HEADER_NAME)
34
+ context = context.set_value(INTL_SWO_X_OPTIONS_KEY, xtraceoptions_header) if xtraceoptions_header
35
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] xtraceoptions_header: #{xtraceoptions_header}"}
36
+
37
+ signature_header = getter.get(carrier, XTRACEOPTIONS_SIGNATURE_HEADER_NAME)
38
+ context = context.set_value(INTL_SWO_SIGNATURE_KEY, signature_header) if signature_header
39
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] signature_header: #{signature_header}; propagator extract context: #{context.inspect}"}
40
+
41
+ context
42
+ end
43
+
44
+ # Inject trace context into the supplied carrier.
45
+ #
46
+ # @param [Carrier] carrier The mutable carrier to inject trace context into
47
+ # @param [Context] context The context to read trace context from
48
+ # @param [optional Setter] setter If the optional setter is provided, it
49
+ # will be used to write context into the carrier, otherwise the default
50
+ # text map setter will be used.
51
+ def inject(carrier, context: ::OpenTelemetry::Context.current, setter: ::OpenTelemetry::Context::Propagation.text_map_setter)
52
+
53
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] inject context: #{context.inspect}"}
54
+
55
+ cspan = ::OpenTelemetry::Trace.current_span(context)
56
+ span_context = cspan&.context
57
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] cspan #{cspan.inspect}; span_context #{span_context.inspect}"}
58
+ return unless span_context&.valid?
59
+
60
+ sw_value = Transformer.sw_from_context(span_context) # sw_value is a string
61
+ trace_state_header = carrier[TRACESTATE_HEADER_NAME].nil?? nil : carrier[TRACESTATE_HEADER_NAME]
62
+
63
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] sw_value: #{sw_value}; trace_state_header: #{trace_state_header}"}
64
+
65
+ # Prepare carrier with carrier's or new tracestate
66
+ trace_state = nil
67
+ if trace_state_header.nil?
68
+ # Only create new trace state if valid span_id
69
+ return if span_context.span_id == ::OpenTelemetry::Trace::INVALID_SPAN_ID
70
+
71
+ trace_state = ::OpenTelemetry::Trace::Tracestate.create({SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY => sw_value})
72
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] creating new trace state: #{trace_state.inspect}"}
73
+ else
74
+ trace_state_from_string = ::OpenTelemetry::Trace::Tracestate.from_string(trace_state_header)
75
+ trace_state = trace_state_from_string.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_value)
76
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Updating/Adding trace state for injection #{trace_state.inspect}"}
77
+ end
78
+
79
+ setter.set(carrier, TRACESTATE_HEADER_NAME, Transformer.trace_state_header(trace_state))
80
+ end
81
+
82
+ # Returns the predefined propagation fields. If your carrier is reused, you
83
+ # should delete the fields returned by this method before calling +inject+.
84
+ #
85
+ # @return [Array<String>] a list of fields that will be used by this propagator.
86
+ def fields
87
+ TRACESTATE_HEADER_NAME
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,72 @@
1
+ module SolarWindsAPM
2
+ module OpenTelemetry
3
+ module SolarWindsResponsePropagator
4
+ # ResponsePropagator
5
+ class TextMapPropagator
6
+ HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers".freeze
7
+ XTRACE_HEADER_NAME = "x-trace".freeze
8
+ XTRACEOPTIONS_RESPONSE_HEADER_NAME = "x-trace-options-response".freeze
9
+ INTL_SWO_EQUALS = "=".freeze
10
+
11
+ private_constant \
12
+ :HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, :XTRACE_HEADER_NAME,
13
+ :XTRACEOPTIONS_RESPONSE_HEADER_NAME
14
+
15
+ def extract(carrier, context: ::OpenTelemetry::Context.current, getter: ::OpenTelemetry::Context::Propagation.text_map_getter) # rubocop:disable Lint/UnusedMethodArgument
16
+ context
17
+ end
18
+
19
+ # Inject trace context into the supplied carrier.
20
+ #
21
+ # @param [Carrier] carrier The mutable carrier to inject trace context into
22
+ # @param [Context] context The context to read trace context from
23
+ # @param [optional Setter] setter If the optional setter is provided, it
24
+ # will be used to write context into the carrier, otherwise the default
25
+ # text map setter will be used.
26
+ def inject(carrier, context: ::OpenTelemetry::Context.current, setter: ::OpenTelemetry::Context::Propagation.text_map_setter)
27
+
28
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] response propagator context: #{context.inspect}"}
29
+ span_context = ::OpenTelemetry::Trace.current_span(context).context
30
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] response propagator span_context: #{span_context.inspect}"}
31
+ return unless span_context.valid?
32
+
33
+ x_trace = Transformer.traceparent_from_context(span_context)
34
+ setter.set(carrier, XTRACE_HEADER_NAME, x_trace)
35
+ exposed_headers = [XTRACE_HEADER_NAME]
36
+
37
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] response propagator span_context.tracestate: #{span_context.tracestate.inspect}"}
38
+ xtraceoptions_response = recover_response_from_tracestate(span_context.tracestate)
39
+
40
+ unless xtraceoptions_response.empty?
41
+ exposed_headers << XTRACEOPTIONS_RESPONSE_HEADER_NAME
42
+ setter.set(carrier, XTRACEOPTIONS_RESPONSE_HEADER_NAME, xtraceoptions_response)
43
+ end
44
+
45
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] response propagator exposed_headers: #{exposed_headers.inspect}"}
46
+ setter.set(carrier, HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, exposed_headers.join(","))
47
+
48
+ end
49
+
50
+ # Returns the predefined propagation fields. If your carrier is reused, you
51
+ # should delete the fields returned by this method before calling +inject+.
52
+ #
53
+ # @return [Array<String>] a list of fields that will be used by this propagator.
54
+ def fields
55
+ TRACESTATE_HEADER_NAME
56
+ end
57
+
58
+ private
59
+
60
+ # sw_xtraceoptions_response_key -> xtrace_options_response
61
+ def recover_response_from_tracestate(tracestate)
62
+ sanitized = tracestate.value(XTraceOptions.sw_xtraceoptions_response_key)
63
+ sanitized = "" if sanitized.nil?
64
+ sanitized = sanitized.gsub(SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED, SolarWindsAPM::Constants::INTL_SWO_EQUALS)
65
+ sanitized = sanitized.gsub(SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED, SolarWindsAPM::Constants::INTL_SWO_COMMA)
66
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] recover_response_from_tracestate sanitized: #{sanitized.inspect}"}
67
+ sanitized
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,335 @@
1
+ module SolarWindsAPM
2
+ module OpenTelemetry
3
+ # SolarWindsSampler
4
+ class SolarWindsSampler
5
+ INTERNAL_BUCKET_CAPACITY = "BucketCapacity".freeze
6
+ INTERNAL_BUCKET_RATE = "BucketRate".freeze
7
+ INTERNAL_SAMPLE_RATE = "SampleRate".freeze
8
+ INTERNAL_SAMPLE_SOURCE = "SampleSource".freeze
9
+ INTERNAL_SW_KEYS = "SWKeys".freeze
10
+ LIBOBOE_CONTINUED = -1
11
+ SW_TRACESTATE_CAPTURE_KEY = "sw.w3c.tracestate".freeze
12
+ SW_TRACESTATE_ROOT_KEY = "sw.tracestate_parent_id".freeze
13
+ UNSET = -1
14
+ SWO_TRACING_ENABLED = 1
15
+ SWO_TRACING_DISABLED = 0
16
+ SWO_TRACING_UNSET = -1
17
+ XTRACEOPTIONS_RESP_AUTH = "auth".freeze
18
+ XTRACEOPTIONS_RESP_IGNORED = "ignored".freeze
19
+ XTRACEOPTIONS_RESP_TRIGGER_IGNORED = "ignored".freeze
20
+ XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED = "not-requested".freeze
21
+ XTRACEOPTIONS_RESP_TRIGGER_TRACE = "trigger-trace".freeze
22
+
23
+ attr_reader :description
24
+
25
+ def initialize(config={})
26
+ @config = config
27
+ end
28
+
29
+ def ==(other)
30
+ @decision == other.decision && @description == other.description
31
+ end
32
+
33
+ # @api private
34
+ #
35
+ # See {Samplers}.
36
+ # trace_id
37
+ # parent_context: OpenTelemetry::Context
38
+ def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:)
39
+
40
+ SolarWindsAPM.logger.debug do
41
+ "[#{self.class}/#{__method__}] should_sample? parameters \n
42
+ trace_id: #{trace_id.unpack1('H*')}\n
43
+ parent_context: #{parent_context}\n
44
+ parent_context.inspect: #{parent_context.inspect}\n
45
+ links: #{links}\n
46
+ name: #{name}\n
47
+ kind: #{kind}\n
48
+ attributes: #{attributes}"
49
+ end
50
+
51
+ # if the upstream has tracestate: sw=.... then it should capture it
52
+
53
+ parent_span_context = ::OpenTelemetry::Trace.current_span(parent_context).context
54
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] should_sample? parent_span_context: #{parent_span_context.inspect}"}
55
+
56
+ xtraceoptions = SolarWindsAPM::XTraceOptions.new(parent_context)
57
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] xtraceoptions: #{xtraceoptions.inspect}"}
58
+
59
+ liboboe_decision = calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
60
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] liboboe_decision: #{liboboe_decision.inspect}"}
61
+
62
+ otel_decision = otel_decision_from_liboboe(liboboe_decision)
63
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] otel_decision: #{otel_decision.inspect}"}
64
+
65
+ new_trace_state = calculate_trace_state(liboboe_decision,parent_span_context,xtraceoptions)
66
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_trace_state: #{new_trace_state.inspect}"}
67
+
68
+ new_attributes = calculate_attributes(attributes,liboboe_decision,new_trace_state,parent_span_context,xtraceoptions)
69
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_attributes: #{new_attributes.inspect}"}
70
+
71
+ sampling_result = ::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: otel_decision, attributes: new_attributes, tracestate: new_trace_state)
72
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] sampling_result: #{sampling_result.inspect}"}
73
+
74
+ sampling_result
75
+ end
76
+
77
+ protected
78
+
79
+ attr_reader :decision
80
+
81
+ private
82
+
83
+ # return Hash
84
+ def calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
85
+
86
+ tracestring = nil
87
+ if parent_span_context.valid? && parent_span_context.remote?
88
+ tracestring = Transformer.traceparent_from_context(parent_span_context)
89
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_liboboe_decision parent_span_context.remote? #{parent_span_context.remote?} with #{tracestring}"}
90
+ end
91
+
92
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] name: #{name}, kind: #{kind}, attributes: #{attributes.inspect}"}
93
+
94
+ # otel-ruby contrib use different key to store url info, currently it's using http.target for path
95
+ url_path = attributes.nil?? '' : attributes['http.target']
96
+ transaction_naming_key = "#{url_path}-#{name}-#{kind}"
97
+
98
+ tracing_mode = SolarWindsAPM::TransactionCache.get(transaction_naming_key)
99
+
100
+ if tracing_mode.nil?
101
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] transaction cache NOT found: #{transaction_naming_key}."}
102
+ trans_settings = SolarWindsAPM::TransactionSettings.new(url_path: url_path, name: name, kind: kind)
103
+ tracing_mode = trans_settings.calculate_trace_mode == 1 ? SWO_TRACING_ENABLED : SWO_TRACING_DISABLED
104
+ SolarWindsAPM::TransactionCache.set(transaction_naming_key, tracing_mode)
105
+ else
106
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] transaction cache found: #{transaction_naming_key}."}
107
+ end
108
+
109
+ sw_member_value = parent_span_context.tracestate[SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY]
110
+
111
+ # need to create the config class
112
+ trigger_trace_mode = OboeTracingMode.get_oboe_trigger_trace_mode(@config["trigger_trace"])
113
+ sample_rate = UNSET
114
+
115
+ options = nil
116
+ trigger_trace = 0
117
+ signature = nil
118
+ timestamp = nil
119
+ if xtraceoptions
120
+ options = xtraceoptions.options
121
+ trigger_trace = xtraceoptions.intify_trigger_trace
122
+ signature = xtraceoptions.signature
123
+ timestamp = xtraceoptions.timestamp
124
+ end
125
+
126
+ SolarWindsAPM.logger.debug do
127
+ "[#{self.class}/#{__method__}] decision parameters \n
128
+ tracestring: #{tracestring}\n
129
+ sw_member_value: #{sw_member_value}\n
130
+ tracing_mode: #{tracing_mode}\n
131
+ sample_rate: #{sample_rate}\n
132
+ trigger_trace: #{trigger_trace}\n
133
+ trigger_trace_mode: #{trigger_trace_mode}\n
134
+ options: #{options}\n
135
+ signature: #{signature}\n
136
+ timestamp: #{timestamp}"
137
+ end
138
+
139
+ args = [tracestring,sw_member_value,tracing_mode,sample_rate,trigger_trace,trigger_trace_mode,options,signature,timestamp]
140
+ do_metrics, do_sample, rate, source, bucket_rate, \
141
+ bucket_cap, decision_type, auth, status_msg, auth_msg, status = SolarWindsAPM::Context.getDecisions(*args)
142
+
143
+ decision = {}
144
+ decision["do_metrics"] = do_metrics > 0
145
+ decision["do_sample"] = do_sample > 0
146
+ decision["rate"] = rate
147
+ decision["source"] = source
148
+ decision["bucket_rate"] = bucket_rate
149
+ decision["bucket_cap"] = bucket_cap
150
+ decision["decision_type"] = decision_type
151
+ decision["auth"] = auth
152
+ decision["status_msg"] = status_msg
153
+ decision["auth_msg"] = auth_msg
154
+ decision["status"] = status
155
+ decision
156
+ end
157
+
158
+ def otel_decision_from_liboboe(liboboe_decision)
159
+
160
+ decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::DROP
161
+ if liboboe_decision["do_sample"]
162
+ decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE # even if not do_metrics
163
+ elsif liboboe_decision["do_metrics"]
164
+ decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_ONLY
165
+ end
166
+ SolarWindsAPM.logger.debug {"OTel decision created: #{decision}"}
167
+ decision
168
+ end
169
+
170
+ def create_xtraceoptions_response_value(decision, parent_span_context, xtraceoptions)
171
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] create_xtraceoptions_response_value decision[auth]: #{decision['auth']}; decision[auth_msg]: #{decision['auth_msg']}; xtraceoptions.trigger_trace: #{xtraceoptions.trigger_trace}"}
172
+
173
+ response = []
174
+ w3c_sanitized = SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED
175
+ response << [XTRACEOPTIONS_RESP_AUTH, decision['auth_msg']].join(w3c_sanitized) if xtraceoptions.signature && decision['auth_msg']
176
+ if !decision["auth"] || decision["auth"] < 1
177
+ trigger_msg = ""
178
+ tracestring = nil
179
+ if xtraceoptions.trigger_trace
180
+ # If a traceparent header was provided then oboe does not generate the message
181
+ tracestring = Transformer.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
182
+ trigger_msg = tracestring && decision['decision_type'] == 0 ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : decision['status_msg']
183
+ else
184
+ trigger_msg = XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED
185
+ end
186
+
187
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] create_xtraceoptions_response_value parent_span_context: #{parent_span_context}; tracestring: #{tracestring}; trigger_msg: #{trigger_msg}"}
188
+
189
+ # e.g. response << trigger-trace####ok
190
+ response << [XTRACEOPTIONS_RESP_TRIGGER_TRACE, trigger_msg].join(w3c_sanitized)
191
+
192
+ end
193
+
194
+ # so far the x-trace-options are only used for liboboe calculate decision for x-trace feature
195
+ # probably not need for remaining services since liboboe decision only calculate once
196
+ unless xtraceoptions.ignored.empty?
197
+ ignored_response = [XTRACEOPTIONS_RESP_IGNORED, xtraceoptions.ignored.join(SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED)]
198
+ response << ignored_response.join(w3c_sanitized)
199
+ # e.g. response << ignored####invalidkeys,invalidkeys,invalidkeys
200
+ end
201
+
202
+ response.join(';') # e.g. trigger-trace####ok;ignored####invalidkeys,invalidkeys,invalidkeys
203
+ end
204
+
205
+ def create_new_trace_state(parent_span_context, decision)
206
+ decision = sw_from_span_and_decision(parent_span_context, decision)
207
+ trace_state = ::OpenTelemetry::Trace::Tracestate.from_hash({SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY => decision}) # e.g. sw=3e222c863a04123a-01
208
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Created new trace_state: #{trace_state.inspect}"}
209
+ trace_state
210
+ end
211
+
212
+ #
213
+ # calculate_trace_state
214
+ # This function merely just add sw=value and xtrace_options_response=value into the old/new tracestate
215
+ # The return value tracestate will be used in propagating to next services
216
+ #
217
+ def calculate_trace_state(decision, parent_span_context, xtraceoptions)
218
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_trace_state parent_span_context: #{parent_span_context.inspect}"}
219
+ if !parent_span_context.valid?
220
+
221
+ trace_state = create_new_trace_state(parent_span_context, decision)
222
+ else
223
+
224
+ parent_trace_state = parent_span_context.tracestate
225
+ if parent_trace_state.nil?
226
+ trace_state = create_new_trace_state(parent_span_context, decision)
227
+ else
228
+ trace_state = parent_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_from_span_and_decision(parent_span_context, decision))
229
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Updated trace_state with span_id and sw trace_flags: #{trace_state.inspect}"}
230
+ end
231
+ end
232
+
233
+ # for setting up the xtrace_options_response
234
+ if xtraceoptions&.options
235
+ trace_state = trace_state.set_value(
236
+ XTraceOptions.sw_xtraceoptions_response_key.to_s,
237
+ create_xtraceoptions_response_value(decision,parent_span_context,xtraceoptions))
238
+ end
239
+
240
+ trace_state
241
+ end
242
+
243
+ #
244
+ # SW_TRACESTATE_CAPTURE_KEY = "sw.w3c.tracestate"
245
+ # sw_xtraceoptions_response_key = "xtrace_options_response"
246
+ # trace_state is the new trace_state from existing span information
247
+ # parent_span_context.trace_state is from its parent
248
+ #
249
+ def add_tracestate_capture_to_attributes_dict(attributes_dict, decision, trace_state, parent_span_context)
250
+
251
+ tracestate_capture = attributes_dict[SW_TRACESTATE_CAPTURE_KEY]
252
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] tracestate_capture #{tracestate_capture.inspect}; attributes_dict #{attributes_dict.inspect}; trace_state #{trace_state.inspect}; parent_span_context: #{parent_span_context.inspect}" }
253
+
254
+ # tracestate_capture seems always nil because attributes_dict never have the SW_TRACESTATE_CAPTURE_KEY (sw.w3c.tracestate)
255
+ # since tracestate_capture is always nil, so the sw always have new value for sw=key
256
+ if tracestate_capture.nil?
257
+ trace_state_no_response = trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
258
+
259
+ else
260
+ # Must retain all potential tracestate pairs for attributes
261
+ attr_trace_state = ::OpenTelemetry::Trace::Tracestate.from_string(tracestate_capture)
262
+
263
+ # This step generated the new sw=key for tracestate based on root parent_span_id
264
+ new_attr_trace_state = attr_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_from_span_and_decision(parent_span_context,decision))
265
+
266
+ trace_state_no_response = new_attr_trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
267
+ end
268
+
269
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] trace_state_no_response #{trace_state_no_response.inspect}"}
270
+
271
+ # other approach
272
+ trace_state_no_response = parent_span_context.tracestate.delete(XTraceOptions.sw_xtraceoptions_response_key)
273
+ no_sw_count = trace_state_no_response.to_h.reject { |k, _v| k == "sw" }.count
274
+ attributes_dict[SW_TRACESTATE_CAPTURE_KEY] = Transformer.trace_state_header(trace_state_no_response) if no_sw_count > 0
275
+
276
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] attributes_dict #{attributes_dict.inspect}"}
277
+ attributes_dict
278
+ end
279
+
280
+ ##
281
+ # calculate_attributes is used for getting the otel Result class in last step of sampler should_sample? e.g. result = Result.new(decision: otel_decision, attributes: new_attributes, tracestate: new_trace_state)
282
+ # calculate_attributes use new_trace_state that is derived from current span information and old tracestate from parent_span_context.tracestate
283
+ # the sw.w3c.tracestate should perserve the old tracestate value for debugging purpose
284
+ ##
285
+ def calculate_attributes(attributes, decision, trace_state, parent_span_context, xtraceoptions)
286
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Received attributes: #{attributes.inspect}; decision:#{decision.inspect}; trace_state:#{trace_state.inspect}; parent_span_context:#{parent_span_context.inspect}; xtraceoptions:#{xtraceoptions.inspect}"}
287
+
288
+ otel_decision = otel_decision_from_liboboe(decision)
289
+ return nil if Transformer.sampled?(otel_decision) == false
290
+
291
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Trace decision is_sampled - setting attributes #{otel_decision.inspect}"}
292
+
293
+ new_attributes = {}
294
+ # Copy existing MappingProxyType KV into new_attributes for modification.
295
+ attributes&.each {|k,v| new_attributes[k] = v}
296
+
297
+ # Always (root or is_remote) set _INTERNAL_SW_KEYS if injected
298
+ new_attributes[INTERNAL_SW_KEYS] = xtraceoptions.sw_keys if xtraceoptions.sw_keys
299
+
300
+ # Always (root or is_remote) set custom KVs if extracted from x-trace-options
301
+ xtraceoptions.custom_kvs&.each {|k,v| new_attributes[k] = v}
302
+
303
+ # Always (root or is_remote) set service entry internal KVs
304
+ new_attributes[INTERNAL_BUCKET_CAPACITY] = decision["bucket_cap"].to_s
305
+ new_attributes[INTERNAL_BUCKET_RATE] = decision["bucket_rate"].to_s
306
+ new_attributes[INTERNAL_SAMPLE_RATE] = decision["rate"]
307
+ new_attributes[INTERNAL_SAMPLE_SOURCE] = decision["source"]
308
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Set attributes with service entry internal KVs: #{new_attributes}"}
309
+
310
+ # set sw.tracestate_parent_id if its tracestate contains "sw"
311
+ sw_value = parent_span_context.tracestate.value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY)
312
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_attributes sw_value: #{sw_value.inspect} parent_span_context.tracestate #{parent_span_context.tracestate.inspect}"}
313
+ new_attributes[SW_TRACESTATE_ROOT_KEY]= Transformer.span_id_from_sw(sw_value) if sw_value && parent_span_context.remote?
314
+
315
+ # If unsigned or signed TT (root or is_remote), set TriggeredTrace
316
+ new_attributes[SolarWindsAPM::Constants::INTERNAL_TRIGGERED_TRACE] = true if xtraceoptions.trigger_trace
317
+
318
+ # Trace's root span has no valid traceparent nor tracestate so we can't calculate remaining attributes
319
+ if !parent_span_context.valid? || trace_state.nil?
320
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] No valid traceparent or no tracestate - returning attributes: #{new_attributes}"}
321
+ return new_attributes.freeze || nil
322
+ end
323
+
324
+ new_attributes = add_tracestate_capture_to_attributes_dict(new_attributes,decision,trace_state,parent_span_context)
325
+ # e.g. {"SWKeys"=>"check-id:check-1013,website-id:booking-demo", "BucketCapacity"=>"6.0", "BucketRate"=>"0.1", "SampleRate"=>-1, "SampleSource"=>-1, "http.method"=>"GET", "http.host"=>"0.0.0.0:8002", "http.scheme"=>"http", "http.target"=>"/call_second_rails/", "http.user_agent"=>"curl/7.81.0", "sw.w3c.tracestate"=>"sw=aaaa1111bbbb2222-01"}
326
+ new_attributes.freeze
327
+ end
328
+
329
+ def sw_from_span_and_decision(parent_span_context, decision)
330
+ trace_flag = Transformer.trace_flags_from_boolean(decision["do_sample"])
331
+ Transformer.sw_from_span_and_decision(parent_span_context.hex_span_id, trace_flag)
332
+ end
333
+ end
334
+ end
335
+ end
@@ -0,0 +1,8 @@
1
+ require 'opentelemetry/sdk'
2
+ require 'opentelemetry/instrumentation/all'
3
+
4
+ require_relative './opentelemetry/solarwinds_propagator'
5
+ require_relative './opentelemetry/solarwinds_processor'
6
+ require_relative './opentelemetry/solarwinds_sampler'
7
+ require_relative './opentelemetry/solarwinds_exporter'
8
+ require_relative './opentelemetry/solarwinds_response_propagator'
@@ -0,0 +1,161 @@
1
+ module SolarWindsAPM
2
+ # OTelConfig module
3
+ # For configure otel component: configurable: propagator, exporter
4
+ # non-config: sampler, processor, response_propagator
5
+ # Level of this configuration: SolarWindsOTel::Config -> OboeOption -> SolarWindsOTel::OTelConfig
6
+ module OTelConfig
7
+ @@config = {}
8
+ @@config_map = {}
9
+
10
+ @@agent_enabled = true
11
+
12
+ def self.disable_agent
13
+ return unless @@agent_enabled # only show the msg once
14
+
15
+ @@agent_enabled = false
16
+ SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] Agent disabled. No Trace exported."}
17
+ end
18
+
19
+ def self.validate_service_key
20
+ return unless (ENV['SW_APM_REPORTER'] || 'ssl') == 'ssl'
21
+
22
+ disable_agent unless ENV['SW_APM_SERVICE_KEY'] || SolarWindsAPM::Config[:service_key]
23
+ end
24
+
25
+ def self.resolve_sampler
26
+
27
+ resolve_sampler_config
28
+ @@config[:sampler] =
29
+ ::OpenTelemetry::SDK::Trace::Samplers.parent_based(
30
+ root: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(@@config[:sampler_config]),
31
+ remote_parent_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(@@config[:sampler_config]),
32
+ remote_parent_not_sampled: SolarWindsAPM::OpenTelemetry::SolarWindsSampler.new(@@config[:sampler_config]))
33
+ end
34
+
35
+ def self.resolve_sampler_config
36
+ sampler_config = {}
37
+ sampler_config["trigger_trace"] = "enabled"
38
+ sampler_config["trigger_trace"] = nil if ENV["SW_APM_TRIGGER_TRACING_MODE"] == 'disabled'
39
+ @@config[:sampler_config] = sampler_config
40
+ end
41
+
42
+ #
43
+ # append/add solarwinds_response_propagator into rack instrumentation
44
+ #
45
+ def self.resolve_for_response_propagator
46
+ response_propagator = SolarWindsAPM::OpenTelemetry::SolarWindsResponsePropagator::TextMapPropagator.new
47
+ if @@config_map["OpenTelemetry::Instrumentation::Rack"]
48
+ if @@config_map["OpenTelemetry::Instrumentation::Rack"][:response_propagators].instance_of?(Array)
49
+ @@config_map["OpenTelemetry::Instrumentation::Rack"][:response_propagators].append(response_propagator)
50
+ else
51
+ @@config_map["OpenTelemetry::Instrumentation::Rack"][:response_propagators] = [response_propagator]
52
+ end
53
+ else
54
+ @@config_map["OpenTelemetry::Instrumentation::Rack"] = {response_propagators: [response_propagator]}
55
+ end
56
+ end
57
+
58
+ def self.[](key)
59
+ @@config[key.to_sym]
60
+ end
61
+
62
+ def self.print_config
63
+ @@config.each do |config, value|
64
+ SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] config: #{config} = #{value}"}
65
+ end
66
+ @@config_map.each do |config, value|
67
+ SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] config_map: #{config} = #{value}"}
68
+ end
69
+ end
70
+
71
+ def self.resolve_solarwinds_processor
72
+ txn_manager = SolarWindsAPM::OpenTelemetry::TxnNameManager.new
73
+ exporter = SolarWindsAPM::OpenTelemetry::SolarWindsExporter.new(txn_manager: txn_manager)
74
+ @@config[:span_processor] = SolarWindsAPM::OpenTelemetry::SolarWindsProcessor.new(exporter, txn_manager)
75
+ end
76
+
77
+ def self.resolve_solarwinds_propagator
78
+ @@config[:propagators] = SolarWindsAPM::OpenTelemetry::SolarWindsPropagator::TextMapPropagator.new
79
+ end
80
+
81
+ def self.validate_propagator(propagators)
82
+ if propagators.nil?
83
+ disable_agent
84
+ return
85
+ end
86
+
87
+ SolarWindsAPM.logger.debug {"[#{name}/#{__method__}] propagators: #{propagators.map(&:class)}"}
88
+ unless ([::OpenTelemetry::Trace::Propagation::TraceContext::TextMapPropagator, ::OpenTelemetry::Baggage::Propagation::TextMapPropagator] - propagators.map(&:class)).empty? # rubocop:disable Style/GuardClause
89
+ SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] Missing tracecontext propagator."}
90
+ disable_agent
91
+ end
92
+ end
93
+
94
+ def self.initialize
95
+ unless defined?(::OpenTelemetry::SDK::Configurator)
96
+ SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] missing OpenTelemetry::SDK::Configurator; opentelemetry seems not loaded."}
97
+ disable_agent
98
+ return
99
+ end
100
+
101
+ validate_service_key
102
+
103
+ return unless @@agent_enabled
104
+
105
+ resolve_sampler
106
+
107
+ resolve_solarwinds_propagator
108
+ resolve_solarwinds_processor
109
+ resolve_for_response_propagator
110
+
111
+ print_config if SolarWindsAPM.logger.level.zero?
112
+
113
+ ENV['OTEL_TRACES_EXPORTER'] = 'none' if ENV['OTEL_TRACES_EXPORTER'].nil?
114
+ ::OpenTelemetry::SDK.configure do |c|
115
+ c.use_all(@@config_map)
116
+ end
117
+
118
+ validate_propagator(::OpenTelemetry.propagation.instance_variable_get(:@propagators))
119
+
120
+ return unless @@agent_enabled
121
+
122
+ # append our propagators
123
+ ::OpenTelemetry.propagation.instance_variable_get(:@propagators).append(@@config[:propagators])
124
+
125
+ # append our processors (with our exporter)
126
+ ::OpenTelemetry.tracer_provider.add_span_processor(@@config[:span_processor])
127
+
128
+ # configure sampler afterwards
129
+ ::OpenTelemetry.tracer_provider.sampler = @@config[:sampler]
130
+ nil
131
+ end
132
+
133
+ #
134
+ # Allow initialize after set new value to SolarWindsAPM::Config[:key]=value
135
+ #
136
+ # Usage:
137
+ #
138
+ # Default using the use_all to load all instrumentation
139
+ # But with specific instrumentation disabled, use {:enabled: false} in config
140
+ # SolarWindsAPM::OTelConfig.initialize_with_config do |config|
141
+ # config["OpenTelemetry::Instrumentation::Rack"] = {"a" => "b"}
142
+ # config["OpenTelemetry::Instrumentation::Dalli"] = {:enabled: false}
143
+ # end
144
+ #
145
+ def self.initialize_with_config
146
+ unless block_given?
147
+ SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] Block not given while doing in-code configuration. Agent disabled."}
148
+ return
149
+ end
150
+
151
+ yield @@config_map
152
+
153
+ if @@config_map.empty?
154
+ SolarWindsAPM.logger.warn {"[#{name}/#{__method__}] No configuration given for in-code configuration. Agent disabled."}
155
+ return
156
+ end
157
+
158
+ initialize
159
+ end
160
+ end
161
+ end