solarwinds_apm 5.1.9 → 6.0.0.preV1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +0 -1
  3. data/ext/oboe_metal/extconf.rb +19 -23
  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/oboe_debug.h +1 -0
  10. data/lib/oboe_metal.rb +116 -80
  11. data/lib/rails/generators/solarwinds_apm/install_generator.rb +1 -2
  12. data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +44 -260
  13. data/lib/solarwinds_apm/api/current_trace_info.rb +148 -0
  14. data/lib/solarwinds_apm/api/tracing.rb +30 -0
  15. data/lib/solarwinds_apm/api/transaction_name.rb +57 -0
  16. data/lib/solarwinds_apm/api.rb +8 -15
  17. data/lib/solarwinds_apm/base.rb +4 -131
  18. data/lib/solarwinds_apm/config.rb +128 -175
  19. data/lib/solarwinds_apm/constants.rb +32 -0
  20. data/lib/solarwinds_apm/logger.rb +1 -1
  21. data/lib/solarwinds_apm/noop/context.rb +2 -5
  22. data/lib/solarwinds_apm/noop/metadata.rb +1 -2
  23. data/lib/solarwinds_apm/noop/profiling.rb +3 -7
  24. data/lib/solarwinds_apm/oboe_init_options.rb +71 -33
  25. data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +204 -0
  26. data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +163 -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 +330 -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 +33 -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 +31 -26
  52. metadata +76 -121
  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/ruby.rb +0 -35
  135. data/lib/solarwinds_apm/sdk/current_trace_info.rb +0 -123
  136. data/lib/solarwinds_apm/sdk/custom_metrics.rb +0 -94
  137. data/lib/solarwinds_apm/sdk/logging.rb +0 -37
  138. data/lib/solarwinds_apm/sdk/trace_context_headers.rb +0 -69
  139. data/lib/solarwinds_apm/sdk/tracing.rb +0 -432
  140. data/lib/solarwinds_apm/support/profiling.rb +0 -25
  141. data/lib/solarwinds_apm/support/trace_context.rb +0 -53
  142. data/lib/solarwinds_apm/support/trace_state.rb +0 -69
  143. data/lib/solarwinds_apm/support/trace_string.rb +0 -89
  144. data/lib/solarwinds_apm/support/transaction_metrics.rb +0 -67
  145. data/lib/solarwinds_apm/test.rb +0 -165
  146. data/lib/solarwinds_apm/util.rb +0 -426
  147. data/log/.keep +0 -0
  148. data/log/postgresql/.keep +0 -0
  149. data/solarwinds_apm.gemspec +0 -55
  150. data/yardoc_frontpage.md +0 -24
@@ -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,330 @@
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 {"[#{self.class}/#{__method__}] should_sample? parameters \n
41
+ trace_id: #{trace_id.unpack1('H*')}\n
42
+ parent_context: #{parent_context}\n
43
+ parent_context.inspect: #{parent_context.inspect}\n
44
+ links: #{links}\n
45
+ name: #{name}\n
46
+ kind: #{kind}\n
47
+ attributes: #{attributes}"}
48
+
49
+ # if the upstream has tracestate: sw=.... then it should capture it
50
+
51
+ parent_span_context = ::OpenTelemetry::Trace.current_span(parent_context).context
52
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] should_sample? parent_span_context: #{parent_span_context.inspect}"}
53
+
54
+ xtraceoptions = SolarWindsAPM::XTraceOptions.new(parent_context)
55
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] xtraceoptions: #{xtraceoptions.inspect}"}
56
+
57
+ liboboe_decision = calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
58
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] liboboe_decision: #{liboboe_decision.inspect}"}
59
+
60
+ otel_decision = otel_decision_from_liboboe(liboboe_decision)
61
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] otel_decision: #{otel_decision.inspect}"}
62
+
63
+ new_trace_state = calculate_trace_state(liboboe_decision,parent_span_context,xtraceoptions)
64
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_trace_state: #{new_trace_state.inspect}"}
65
+
66
+ new_attributes = calculate_attributes(attributes,liboboe_decision,new_trace_state,parent_span_context,xtraceoptions)
67
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_attributes: #{new_attributes.inspect}"}
68
+
69
+ sampling_result = ::OpenTelemetry::SDK::Trace::Samplers::Result.new(decision: otel_decision, attributes: new_attributes, tracestate: new_trace_state)
70
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] sampling_result: #{sampling_result.inspect}"}
71
+
72
+ sampling_result
73
+ end
74
+
75
+ protected
76
+
77
+ attr_reader :decision
78
+
79
+ private
80
+
81
+ # return Hash
82
+ def calculate_liboboe_decision(parent_span_context, xtraceoptions, name, kind, attributes)
83
+
84
+ tracestring = nil
85
+ if parent_span_context.valid? && parent_span_context.remote?
86
+ tracestring = Transformer.traceparent_from_context(parent_span_context)
87
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_liboboe_decision parent_span_context.remote? #{parent_span_context.remote?} with #{tracestring}"}
88
+ end
89
+
90
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] name: #{name}, kind: #{kind}, attributes: #{attributes.inspect}"}
91
+
92
+ url = attributes.nil?? '' : attributes['http.host'] || attributes['http.url'] || attributes['net.peer.name'] # otel-ruby contrib use different key to store url info
93
+ transaction_naming_key = "#{url}-#{name}-#{kind}"
94
+
95
+ tracing_mode = SolarWindsAPM::TransactionCache.get(transaction_naming_key)
96
+
97
+ if tracing_mode.nil?
98
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] transaction cache NOT found: #{transaction_naming_key}."}
99
+ trans_settings = SolarWindsAPM::TransactionSettings.new(url: url, name: name, kind: kind)
100
+ tracing_mode = trans_settings.calculate_trace_mode == 1 ? SWO_TRACING_ENABLED : SWO_TRACING_DISABLED
101
+ SolarWindsAPM::TransactionCache.set(transaction_naming_key, tracing_mode)
102
+ else
103
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] transaction cache found: #{transaction_naming_key}."}
104
+ end
105
+
106
+ sw_member_value = parent_span_context.tracestate[SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY]
107
+
108
+ # need to create the config class
109
+ trigger_trace_mode = OboeTracingMode.get_oboe_trigger_trace_mode(@config["trigger_trace"])
110
+ sample_rate = UNSET
111
+
112
+ options = nil
113
+ trigger_trace = 0
114
+ signature = nil
115
+ timestamp = nil
116
+ if xtraceoptions
117
+ options = xtraceoptions.options
118
+ trigger_trace = xtraceoptions.intify_trigger_trace
119
+ signature = xtraceoptions.signature
120
+ timestamp = xtraceoptions.timestamp
121
+ end
122
+
123
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] decision parameters \n
124
+ tracestring: #{tracestring}\n
125
+ sw_member_value: #{sw_member_value}\n
126
+ tracing_mode: #{tracing_mode}\n
127
+ sample_rate: #{sample_rate}\n
128
+ trigger_trace: #{trigger_trace}\n
129
+ trigger_trace_mode: #{trigger_trace_mode}\n
130
+ options: #{options}\n
131
+ signature: #{signature}\n
132
+ timestamp: #{timestamp}"}
133
+
134
+ args = [tracestring,sw_member_value,tracing_mode,sample_rate,trigger_trace,trigger_trace_mode,options,signature,timestamp]
135
+ do_metrics, do_sample, rate, source, bucket_rate, \
136
+ bucket_cap, decision_type, auth, status_msg, auth_msg, status = SolarWindsAPM::Context.getDecisions(*args)
137
+
138
+ decision = {}
139
+ decision["do_metrics"] = do_metrics > 0
140
+ decision["do_sample"] = do_sample > 0
141
+ decision["rate"] = rate
142
+ decision["source"] = source
143
+ decision["bucket_rate"] = bucket_rate
144
+ decision["bucket_cap"] = bucket_cap
145
+ decision["decision_type"] = decision_type
146
+ decision["auth"] = auth
147
+ decision["status_msg"] = status_msg
148
+ decision["auth_msg"] = auth_msg
149
+ decision["status"] = status
150
+ decision
151
+ end
152
+
153
+ def otel_decision_from_liboboe(liboboe_decision)
154
+
155
+ decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::DROP
156
+ if liboboe_decision["do_sample"]
157
+ decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_AND_SAMPLE # even if not do_metrics
158
+ elsif liboboe_decision["do_metrics"]
159
+ decision = ::OpenTelemetry::SDK::Trace::Samplers::Decision::RECORD_ONLY
160
+ end
161
+ SolarWindsAPM.logger.debug {"OTel decision created: #{decision}"}
162
+ decision
163
+ end
164
+
165
+ def create_xtraceoptions_response_value(decision, parent_span_context, xtraceoptions)
166
+ 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}"}
167
+
168
+ response = []
169
+ w3c_sanitized = SolarWindsAPM::Constants::INTL_SWO_EQUALS_W3C_SANITIZED
170
+ response << [XTRACEOPTIONS_RESP_AUTH, decision['auth_msg']].join(w3c_sanitized) if xtraceoptions.signature && decision['auth_msg']
171
+ if !decision["auth"] || decision["auth"] < 1
172
+ trigger_msg = ""
173
+ tracestring = nil
174
+ if xtraceoptions.trigger_trace
175
+ # If a traceparent header was provided then oboe does not generate the message
176
+ tracestring = Transformer.traceparent_from_context(parent_span_context) if parent_span_context.valid? && parent_span_context.remote?
177
+ trigger_msg = tracestring && decision['decision_type'] == 0 ? XTRACEOPTIONS_RESP_TRIGGER_IGNORED : decision['status_msg']
178
+ else
179
+ trigger_msg = XTRACEOPTIONS_RESP_TRIGGER_NOT_REQUESTED
180
+ end
181
+
182
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] create_xtraceoptions_response_value parent_span_context: #{parent_span_context}; tracestring: #{tracestring}; trigger_msg: #{trigger_msg}"}
183
+
184
+ # e.g. response << trigger-trace####ok
185
+ response << [XTRACEOPTIONS_RESP_TRIGGER_TRACE, trigger_msg].join(w3c_sanitized)
186
+
187
+ end
188
+
189
+ # so far the x-trace-options are only used for liboboe calculate decision for x-trace feature
190
+ # probably not need for remaining services since liboboe decision only calculate once
191
+ unless xtraceoptions.ignored.empty?
192
+ ignored_response = [XTRACEOPTIONS_RESP_IGNORED, xtraceoptions.ignored.join(SolarWindsAPM::Constants::INTL_SWO_COMMA_W3C_SANITIZED)]
193
+ response << ignored_response.join(w3c_sanitized)
194
+ # e.g. response << ignored####invalidkeys,invalidkeys,invalidkeys
195
+ end
196
+
197
+ response.join(';') # e.g. trigger-trace####ok;ignored####invalidkeys,invalidkeys,invalidkeys
198
+ end
199
+
200
+ def create_new_trace_state(parent_span_context, decision)
201
+ decision = sw_from_span_and_decision(parent_span_context, decision)
202
+ trace_state = ::OpenTelemetry::Trace::Tracestate.from_hash({SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY => decision}) # e.g. sw=3e222c863a04123a-01
203
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Created new trace_state: #{trace_state.inspect}"}
204
+ trace_state
205
+ end
206
+
207
+ #
208
+ # calculate_trace_state
209
+ # This function merely just add sw=value and xtrace_options_response=value into the old/new tracestate
210
+ # The return value tracestate will be used in propagating to next services
211
+ #
212
+ def calculate_trace_state(decision, parent_span_context, xtraceoptions)
213
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_trace_state parent_span_context: #{parent_span_context.inspect}"}
214
+ if !parent_span_context.valid?
215
+
216
+ trace_state = create_new_trace_state(parent_span_context, decision)
217
+ else
218
+
219
+ parent_trace_state = parent_span_context.tracestate
220
+ if parent_trace_state.nil?
221
+ trace_state = create_new_trace_state(parent_span_context, decision)
222
+ else
223
+ trace_state = parent_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_from_span_and_decision(parent_span_context, decision))
224
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Updated trace_state with span_id and sw trace_flags: #{trace_state.inspect}"}
225
+ end
226
+ end
227
+
228
+ # for setting up the xtrace_options_response
229
+ if xtraceoptions&.options
230
+ trace_state = trace_state.set_value(
231
+ XTraceOptions.sw_xtraceoptions_response_key.to_s,
232
+ create_xtraceoptions_response_value(decision,parent_span_context,xtraceoptions))
233
+ end
234
+
235
+ trace_state
236
+ end
237
+
238
+ #
239
+ # SW_TRACESTATE_CAPTURE_KEY = "sw.w3c.tracestate"
240
+ # sw_xtraceoptions_response_key = "xtrace_options_response"
241
+ # trace_state is the new trace_state from existing span information
242
+ # parent_span_context.trace_state is from its parent
243
+ #
244
+ def add_tracestate_capture_to_attributes_dict(attributes_dict, decision, trace_state, parent_span_context)
245
+
246
+ tracestate_capture = attributes_dict[SW_TRACESTATE_CAPTURE_KEY]
247
+ 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}" }
248
+
249
+ # tracestate_capture seems always nil because attributes_dict never have the SW_TRACESTATE_CAPTURE_KEY (sw.w3c.tracestate)
250
+ # since tracestate_capture is always nil, so the sw always have new value for sw=key
251
+ if tracestate_capture.nil?
252
+ trace_state_no_response = trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
253
+
254
+ else
255
+ # Must retain all potential tracestate pairs for attributes
256
+ attr_trace_state = ::OpenTelemetry::Trace::Tracestate.from_string(tracestate_capture)
257
+
258
+ # This step generated the new sw=key for tracestate based on root parent_span_id
259
+ new_attr_trace_state = attr_trace_state.set_value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY, sw_from_span_and_decision(parent_span_context,decision))
260
+
261
+ trace_state_no_response = new_attr_trace_state.delete(XTraceOptions.sw_xtraceoptions_response_key)
262
+ end
263
+
264
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] trace_state_no_response #{trace_state_no_response.inspect}"}
265
+
266
+ # other approach
267
+ trace_state_no_response = parent_span_context.tracestate.delete(XTraceOptions.sw_xtraceoptions_response_key)
268
+ no_sw_count = trace_state_no_response.to_h.reject { |k, _v| k == "sw" }.count
269
+ attributes_dict[SW_TRACESTATE_CAPTURE_KEY] = Transformer.trace_state_header(trace_state_no_response) if no_sw_count > 0
270
+
271
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] attributes_dict #{attributes_dict.inspect}"}
272
+ attributes_dict
273
+ end
274
+
275
+ ##
276
+ # 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)
277
+ # calculate_attributes use new_trace_state that is derived from current span information and old tracestate from parent_span_context.tracestate
278
+ # the sw.w3c.tracestate should perserve the old tracestate value for debugging purpose
279
+ ##
280
+ def calculate_attributes(attributes, decision, trace_state, parent_span_context, xtraceoptions)
281
+ 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}"}
282
+
283
+ otel_decision = otel_decision_from_liboboe(decision)
284
+ return nil if Transformer.sampled?(otel_decision) == false
285
+
286
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Trace decision is_sampled - setting attributes #{otel_decision.inspect}"}
287
+
288
+ new_attributes = {}
289
+ # Copy existing MappingProxyType KV into new_attributes for modification.
290
+ attributes&.each {|k,v| new_attributes[k] = v}
291
+
292
+ # Always (root or is_remote) set _INTERNAL_SW_KEYS if injected
293
+ new_attributes[INTERNAL_SW_KEYS] = xtraceoptions.sw_keys if xtraceoptions.sw_keys
294
+
295
+ # Always (root or is_remote) set custom KVs if extracted from x-trace-options
296
+ xtraceoptions.custom_kvs&.each {|k,v| new_attributes[k] = v}
297
+
298
+ # Always (root or is_remote) set service entry internal KVs
299
+ new_attributes[INTERNAL_BUCKET_CAPACITY] = decision["bucket_cap"].to_s
300
+ new_attributes[INTERNAL_BUCKET_RATE] = decision["bucket_rate"].to_s
301
+ new_attributes[INTERNAL_SAMPLE_RATE] = decision["rate"]
302
+ new_attributes[INTERNAL_SAMPLE_SOURCE] = decision["source"]
303
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] Set attributes with service entry internal KVs: #{new_attributes}"}
304
+
305
+ # set sw.tracestate_parent_id if its tracestate contains "sw"
306
+ sw_value = parent_span_context.tracestate.value(SolarWindsAPM::Constants::INTL_SWO_TRACESTATE_KEY)
307
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] calculate_attributes sw_value: #{sw_value.inspect} parent_span_context.tracestate #{parent_span_context.tracestate.inspect}"}
308
+ new_attributes[SW_TRACESTATE_ROOT_KEY]= Transformer.span_id_from_sw(sw_value) if sw_value && parent_span_context.remote?
309
+
310
+ # If unsigned or signed TT (root or is_remote), set TriggeredTrace
311
+ new_attributes[SolarWindsAPM::Constants::INTERNAL_TRIGGERED_TRACE] = true if xtraceoptions.trigger_trace
312
+
313
+ # Trace's root span has no valid traceparent nor tracestate so we can't calculate remaining attributes
314
+ if !parent_span_context.valid? || trace_state.nil?
315
+ SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] No valid traceparent or no tracestate - returning attributes: #{new_attributes}"}
316
+ return new_attributes.freeze || nil
317
+ end
318
+
319
+ new_attributes = add_tracestate_capture_to_attributes_dict(new_attributes,decision,trace_state,parent_span_context)
320
+ # 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"}
321
+ new_attributes.freeze
322
+ end
323
+
324
+ def sw_from_span_and_decision(parent_span_context, decision)
325
+ trace_flag = Transformer.trace_flags_from_boolean(decision["do_sample"])
326
+ Transformer.sw_from_span_and_decision(parent_span_context.hex_span_id, trace_flag)
327
+ end
328
+ end
329
+ end
330
+ 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 {"[#{self.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 {"[#{self.name}/#{__method__}] config: #{config} = #{value}"}
65
+ end
66
+ @@config_map.each do |config, value|
67
+ SolarWindsAPM.logger.warn {"[#{self.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 {"[#{self.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 {"[#{self.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 {"[#{self.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 {"[#{self.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 {"[#{self.name}/#{__method__}] No configuration given for in-code configuration. Agent disabled."}
155
+ return
156
+ end
157
+
158
+ initialize
159
+ end
160
+ end
161
+ end
@@ -6,7 +6,6 @@ require 'logger'
6
6
  module SolarWindsAPM
7
7
  module Logger
8
8
  module Formatter
9
-
10
9
  def call(severity, time, progname, msg)
11
10
  return super if SolarWindsAPM::Config[:log_traceId] == :never
12
11
 
@@ -19,7 +18,7 @@ module SolarWindsAPM
19
18
  def insert_trace_id(msg)
20
19
  return msg if msg =~ /trace_id=/
21
20
 
22
- current_trace = SolarWindsAPM::SDK.current_trace_info
21
+ current_trace = SolarWindsAPM::API.current_trace_info
23
22
  if current_trace.do_log
24
23
  case msg
25
24
  when ::String
@@ -35,12 +34,12 @@ module SolarWindsAPM
35
34
 
36
35
  def insert_before_empty_lines(msg, for_log)
37
36
  stripped = msg.rstrip
38
- "#{stripped} #{for_log}#{msg[stripped.length..-1]}"
37
+ "#{stripped} #{for_log}#{msg[stripped.length..]}"
39
38
  end
40
39
  end
41
40
  end
42
41
  end
43
42
 
44
- if SolarWindsAPM.loaded
45
- Logger::Formatter.send(:prepend, SolarWindsAPM::Logger::Formatter)
46
- end
43
+ # To use the trace context in log, ::Logger::Formatter.new must be defined
44
+ # e.g. config.log_formatter = ::Logger::Formatter.new
45
+ Logger::Formatter.prepend(SolarWindsAPM::Logger::Formatter) if SolarWindsAPM.loaded
@@ -8,17 +8,14 @@ module SolarWindsAPM
8
8
  module LogEvent
9
9
  include SolarWindsAPM::Logger::Formatter # provides #insert_trace_id
10
10
 
11
- def initialize(logger, level, data, caller_tracing )
12
- return super if SolarWindsAPM::Config[:log_traceId] == :never
11
+ def initialize(logger, level, data, caller_tracing)
12
+ super if SolarWindsAPM::Config[:log_traceId] == :never
13
13
 
14
14
  data = insert_trace_id(data)
15
15
  super
16
16
  end
17
-
18
17
  end
19
18
  end
20
19
  end
21
20
 
22
- if SolarWindsAPM.loaded && defined?(Logging::LogEvent)
23
- Logging::LogEvent.send(:prepend, SolarWindsAPM::Logging::LogEvent)
24
- end
21
+ Logging::LogEvent.prepend(SolarWindsAPM::Logging::LogEvent) if SolarWindsAPM.loaded && defined?(Logging::LogEvent)
@@ -3,7 +3,4 @@
3
3
 
4
4
  require_relative 'logger_formatter'
5
5
 
6
- if SolarWindsAPM.loaded && defined?(Lumberjack::Formatter)
7
- Lumberjack::Formatter.send(:prepend, SolarWindsAPM::Logger::Formatter)
8
- end
9
-
6
+ Lumberjack::Formatter.prepend(SolarWindsAPM::Logger::Formatter) if SolarWindsAPM.loaded && defined?(Lumberjack::Formatter)
@@ -0,0 +1,27 @@
1
+ module SolarWindsAPM
2
+ module OpenTelemetry
3
+ # OboeTracingMode
4
+ # Used in solarwinds_sampler
5
+ class OboeTracingMode
6
+ OBOE_SETTINGS_UNSET = -1
7
+ OBOE_TRACE_DISABLED = 0
8
+ OBOE_TRACE_ENABLED = 1
9
+ OBOE_TRIGGER_DISABLED = 0
10
+ OBOE_TRIGGER_ENABLED = 1
11
+
12
+ def self.get_oboe_trace_mode(tracing_mode)
13
+ mode = OBOE_SETTINGS_UNSET
14
+ mode = OBOE_TRACE_ENABLED if tracing_mode == 'enabled'
15
+ mode = OBOE_TRACE_DISABLED if tracing_mode == 'disabled'
16
+ mode
17
+ end
18
+
19
+ def self.get_oboe_trigger_trace_mode(trigger_trace_mode)
20
+ mode = OBOE_SETTINGS_UNSET
21
+ mode = OBOE_TRIGGER_ENABLED if trigger_trace_mode == 'enabled'
22
+ mode = OBOE_TRIGGER_DISABLED if trigger_trace_mode == 'disabled'
23
+ mode
24
+ end
25
+ end
26
+ end
27
+ end