solarwinds_apm 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +5 -0
  3. data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +16 -0
  4. data/.github/workflows/build_and_release_gem.yml +112 -0
  5. data/.github/workflows/build_for_packagecloud.yml +70 -0
  6. data/.github/workflows/docker-images.yml +47 -0
  7. data/.github/workflows/run_cpluplus_tests.yml +73 -0
  8. data/.github/workflows/run_tests.yml +155 -0
  9. data/.github/workflows/scripts/test_install.rb +23 -0
  10. data/.github/workflows/swig/swig-v4.0.2.tar.gz +0 -0
  11. data/.github/workflows/test_on_4_linux.yml +161 -0
  12. data/.gitignore +39 -0
  13. data/.rubocop.yml +29 -0
  14. data/.yardopts +7 -0
  15. data/CHANGELOG.md +769 -0
  16. data/CONFIG.md +31 -0
  17. data/Gemfile +14 -0
  18. data/LICENSE +202 -0
  19. data/README.md +383 -0
  20. data/bin/solarwinds_apm_config +15 -0
  21. data/examples/prepend.rb +13 -0
  22. data/examples/sdk_examples.rb +158 -0
  23. data/ext/oboe_metal/README.md +69 -0
  24. data/ext/oboe_metal/extconf.rb +141 -0
  25. data/ext/oboe_metal/extconf_local.rb +75 -0
  26. data/ext/oboe_metal/lib/.keep +0 -0
  27. data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.0.0.0.sha256 +1 -0
  28. data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.0.0.0.sha256 +1 -0
  29. data/ext/oboe_metal/noop/noop.c +8 -0
  30. data/ext/oboe_metal/src/README.md +6 -0
  31. data/ext/oboe_metal/src/VERSION +2 -0
  32. data/ext/oboe_metal/src/bson/bson.h +220 -0
  33. data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
  34. data/ext/oboe_metal/src/frames.cc +247 -0
  35. data/ext/oboe_metal/src/frames.h +40 -0
  36. data/ext/oboe_metal/src/init_solarwinds_apm.cc +21 -0
  37. data/ext/oboe_metal/src/logging.cc +95 -0
  38. data/ext/oboe_metal/src/logging.h +35 -0
  39. data/ext/oboe_metal/src/oboe.h +1169 -0
  40. data/ext/oboe_metal/src/oboe_api.cpp +658 -0
  41. data/ext/oboe_metal/src/oboe_api.hpp +433 -0
  42. data/ext/oboe_metal/src/oboe_debug.h +59 -0
  43. data/ext/oboe_metal/src/oboe_swig_wrap.cc +7562 -0
  44. data/ext/oboe_metal/src/profiling.cc +435 -0
  45. data/ext/oboe_metal/src/profiling.h +78 -0
  46. data/ext/oboe_metal/test/CMakeLists.txt +53 -0
  47. data/ext/oboe_metal/test/FindGMock.cmake +43 -0
  48. data/ext/oboe_metal/test/README.md +56 -0
  49. data/ext/oboe_metal/test/frames_test.cc +164 -0
  50. data/ext/oboe_metal/test/profiling_test.cc +93 -0
  51. data/ext/oboe_metal/test/ruby_inc_dir.rb +8 -0
  52. data/ext/oboe_metal/test/ruby_prefix.rb +8 -0
  53. data/ext/oboe_metal/test/ruby_test_helper.rb +67 -0
  54. data/ext/oboe_metal/test/test.h +11 -0
  55. data/ext/oboe_metal/test/test_main.cc +32 -0
  56. data/init.rb +4 -0
  57. data/lib/oboe.rb +7 -0
  58. data/lib/oboe_metal.rb +172 -0
  59. data/lib/rails/generators/solarwinds_apm/install_generator.rb +47 -0
  60. data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +424 -0
  61. data/lib/solarwinds_apm/api/layerinit.rb +41 -0
  62. data/lib/solarwinds_apm/api/logging.rb +356 -0
  63. data/lib/solarwinds_apm/api/memcache.rb +37 -0
  64. data/lib/solarwinds_apm/api/metrics.rb +63 -0
  65. data/lib/solarwinds_apm/api/util.rb +98 -0
  66. data/lib/solarwinds_apm/api.rb +21 -0
  67. data/lib/solarwinds_apm/base.rb +160 -0
  68. data/lib/solarwinds_apm/config.rb +301 -0
  69. data/lib/solarwinds_apm/frameworks/grape.rb +96 -0
  70. data/lib/solarwinds_apm/frameworks/padrino.rb +78 -0
  71. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller.rb +100 -0
  72. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller5.rb +50 -0
  73. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
  74. data/lib/solarwinds_apm/frameworks/rails/inst/action_view.rb +88 -0
  75. data/lib/solarwinds_apm/frameworks/rails/inst/active_record.rb +26 -0
  76. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +29 -0
  77. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +22 -0
  78. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +103 -0
  79. data/lib/solarwinds_apm/frameworks/rails/inst/logger_formatters.rb +14 -0
  80. data/lib/solarwinds_apm/frameworks/rails.rb +100 -0
  81. data/lib/solarwinds_apm/frameworks/sinatra.rb +96 -0
  82. data/lib/solarwinds_apm/inst/bunny-client.rb +157 -0
  83. data/lib/solarwinds_apm/inst/bunny-consumer.rb +102 -0
  84. data/lib/solarwinds_apm/inst/curb.rb +288 -0
  85. data/lib/solarwinds_apm/inst/dalli.rb +89 -0
  86. data/lib/solarwinds_apm/inst/delayed_job.rb +100 -0
  87. data/lib/solarwinds_apm/inst/excon.rb +113 -0
  88. data/lib/solarwinds_apm/inst/faraday.rb +96 -0
  89. data/lib/solarwinds_apm/inst/graphql.rb +206 -0
  90. data/lib/solarwinds_apm/inst/grpc_client.rb +147 -0
  91. data/lib/solarwinds_apm/inst/grpc_server.rb +119 -0
  92. data/lib/solarwinds_apm/inst/httpclient.rb +181 -0
  93. data/lib/solarwinds_apm/inst/logger_formatter.rb +46 -0
  94. data/lib/solarwinds_apm/inst/logging_log_event.rb +24 -0
  95. data/lib/solarwinds_apm/inst/lumberjack_formatter.rb +9 -0
  96. data/lib/solarwinds_apm/inst/memcached.rb +86 -0
  97. data/lib/solarwinds_apm/inst/mongo.rb +246 -0
  98. data/lib/solarwinds_apm/inst/mongo2.rb +225 -0
  99. data/lib/solarwinds_apm/inst/moped.rb +466 -0
  100. data/lib/solarwinds_apm/inst/net_http.rb +60 -0
  101. data/lib/solarwinds_apm/inst/rack.rb +217 -0
  102. data/lib/solarwinds_apm/inst/rack_cache.rb +35 -0
  103. data/lib/solarwinds_apm/inst/redis.rb +273 -0
  104. data/lib/solarwinds_apm/inst/resque.rb +129 -0
  105. data/lib/solarwinds_apm/inst/rest-client.rb +43 -0
  106. data/lib/solarwinds_apm/inst/sequel.rb +241 -0
  107. data/lib/solarwinds_apm/inst/sidekiq-client.rb +63 -0
  108. data/lib/solarwinds_apm/inst/sidekiq-worker.rb +64 -0
  109. data/lib/solarwinds_apm/inst/typhoeus.rb +90 -0
  110. data/lib/solarwinds_apm/instrumentation.rb +22 -0
  111. data/lib/solarwinds_apm/loading.rb +65 -0
  112. data/lib/solarwinds_apm/logger.rb +14 -0
  113. data/lib/solarwinds_apm/noop/README.md +9 -0
  114. data/lib/solarwinds_apm/noop/context.rb +26 -0
  115. data/lib/solarwinds_apm/noop/metadata.rb +25 -0
  116. data/lib/solarwinds_apm/noop/profiling.rb +21 -0
  117. data/lib/solarwinds_apm/oboe_init_options.rb +191 -0
  118. data/lib/solarwinds_apm/ruby.rb +35 -0
  119. data/lib/solarwinds_apm/sdk/current_trace_info.rb +123 -0
  120. data/lib/solarwinds_apm/sdk/custom_metrics.rb +94 -0
  121. data/lib/solarwinds_apm/sdk/logging.rb +37 -0
  122. data/lib/solarwinds_apm/sdk/trace_context_headers.rb +69 -0
  123. data/lib/solarwinds_apm/sdk/tracing.rb +432 -0
  124. data/lib/solarwinds_apm/support/profiling.rb +22 -0
  125. data/lib/solarwinds_apm/support/trace_context.rb +53 -0
  126. data/lib/solarwinds_apm/support/trace_state.rb +69 -0
  127. data/lib/solarwinds_apm/support/trace_string.rb +89 -0
  128. data/lib/solarwinds_apm/support/transaction_metrics.rb +67 -0
  129. data/lib/solarwinds_apm/support/transaction_settings.rb +233 -0
  130. data/lib/solarwinds_apm/support/x_trace_options.rb +113 -0
  131. data/lib/solarwinds_apm/support.rb +12 -0
  132. data/lib/solarwinds_apm/support_report.rb +113 -0
  133. data/lib/solarwinds_apm/test.rb +165 -0
  134. data/lib/solarwinds_apm/thread_local.rb +26 -0
  135. data/lib/solarwinds_apm/util.rb +334 -0
  136. data/lib/solarwinds_apm/version.rb +17 -0
  137. data/lib/solarwinds_apm.rb +72 -0
  138. data/log/.keep +0 -0
  139. data/log/postgresql/.keep +0 -0
  140. data/solarwinds_apm.gemspec +52 -0
  141. data/yardoc_frontpage.md +24 -0
  142. metadata +228 -0
@@ -0,0 +1,157 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module SolarWindsAPM
5
+ module Inst
6
+ module BunnyExchange
7
+ include SolarWindsAPM::SDK::TraceContextHeaders
8
+
9
+ def self.included(klass)
10
+ SolarWindsAPM::Util.method_alias(klass, :delete, ::Bunny::Exchange)
11
+ end
12
+
13
+ def delete_with_sw_apm(opts = {})
14
+ # If we're not tracing, just do a fast return.
15
+ return delete_without_sw_apm(opts) if !SolarWindsAPM.tracing?
16
+
17
+ begin
18
+ kvs = {}
19
+ kvs[:Spec] = :pushq
20
+ kvs[:Flavor] = :rabbitmq
21
+ kvs[:Op] = :delete
22
+ kvs[:ExchangeType] = @type
23
+ kvs[:RemoteHost] = channel.connection.host
24
+ kvs[:RemotePort] = channel.connection.port.to_i
25
+ kvs[:VirtualHost] = channel.connection.vhost
26
+
27
+ if @name.is_a?(String) && !@name.empty?
28
+ kvs[:ExchangeName] = @name
29
+ else
30
+ kvs[:ExchangeName] = :default
31
+ end
32
+
33
+ SolarWindsAPM::API.log_entry(:'rabbitmq-client')
34
+ delete_without_sw_apm(opts)
35
+ rescue => e
36
+ SolarWindsAPM::API.log_exception(:'rabbitmq-client', e)
37
+ raise e
38
+ ensure
39
+ kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:bunnyclient][:collect_backtraces]
40
+ SolarWindsAPM::API.log_exit(:'rabbitmq-client', kvs)
41
+ end
42
+ end
43
+ end
44
+
45
+ module BunnyChannel
46
+ include SolarWindsAPM::SDK::TraceContextHeaders
47
+
48
+ def self.included(klass)
49
+ SolarWindsAPM::Util.method_alias(klass, :basic_publish, ::Bunny::Channel)
50
+ SolarWindsAPM::Util.method_alias(klass, :queue, ::Bunny::Channel)
51
+ SolarWindsAPM::Util.method_alias(klass, :wait_for_confirms, ::Bunny::Channel)
52
+ end
53
+
54
+ def collect_channel_kvs
55
+ kvs = {}
56
+ kvs[:Spec] = :pushq
57
+ kvs[:Flavor] = :rabbitmq
58
+ kvs[:RemoteHost] = @connection.host
59
+ kvs[:RemotePort] = @connection.port.to_i
60
+ kvs[:VirtualHost] = @connection.vhost
61
+ kvs
62
+ rescue => e
63
+ SolarWindsAPM.logger.debug "[solarwinds_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if SolarWindsAPM::Config[:verbose]
64
+ ensure
65
+ return kvs
66
+ end
67
+
68
+ def basic_publish_with_sw_apm(payload, exchange, routing_key, opts = {})
69
+ # If we're not tracing, just do a fast return.
70
+ return basic_publish_without_sw_apm(payload, exchange, routing_key, opts) if !SolarWindsAPM.tracing?
71
+
72
+ begin
73
+ kvs = collect_channel_kvs
74
+
75
+ if exchange.respond_to?(:name)
76
+ kvs[:ExchangeName] = exchange.name
77
+ elsif exchange.respond_to?(:empty?) && !exchange.empty?
78
+ kvs[:ExchangeName] = exchange
79
+ else
80
+ kvs[:ExchangeName] = :default
81
+ end
82
+
83
+ kvs[:Queue] = opts[:queue] if opts.key?(:queue)
84
+ kvs[:RoutingKey] = routing_key if routing_key
85
+ kvs[:Op] = :publish
86
+
87
+ SolarWindsAPM::API.log_entry(:'rabbitmq-client')
88
+ # Pass the tracing context as a header
89
+ opts[:headers] ||= {}
90
+ opts[:headers][:SourceTrace] = SolarWindsAPM::Context.toString
91
+ add_tracecontext_headers(opts[:headers])
92
+
93
+ basic_publish_without_sw_apm(payload, exchange, routing_key, opts)
94
+ rescue => e
95
+ SolarWindsAPM::API.log_exception(:'rabbitmq-client', e)
96
+ raise e
97
+ ensure
98
+ kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:bunnyclient][:collect_backtraces]
99
+ SolarWindsAPM::API.log_exit(:'rabbitmq-client', kvs)
100
+ end
101
+ end
102
+
103
+ def queue_with_sw_apm(name = AMQ::Protocol::EMPTY_STRING, opts = {})
104
+ # If we're not tracing, just do a fast return.
105
+ return queue_without_sw_apm(name, opts) if !SolarWindsAPM.tracing?
106
+
107
+ begin
108
+ kvs = collect_channel_kvs
109
+ kvs[:Op] = :queue
110
+
111
+ SolarWindsAPM::API.log_entry(:'rabbitmq-client')
112
+ opts[:headers] ||= {}
113
+ add_tracecontext_headers(opts[:headers])
114
+
115
+ result = queue_without_sw_apm(name, opts)
116
+ kvs[:Queue] = result.name
117
+ result
118
+ rescue => e
119
+ SolarWindsAPM::API.log_exception(:'rabbitmq-client', e)
120
+ raise e
121
+ ensure
122
+ kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:bunnyclient][:collect_backtraces]
123
+ SolarWindsAPM::API.log_exit(:'rabbitmq-client', kvs)
124
+ end
125
+ end
126
+
127
+ def wait_for_confirms_with_sw_apm
128
+ # If we're not tracing, just do a fast return.
129
+ return wait_for_confirms_without_sw_apm if !SolarWindsAPM.tracing?
130
+
131
+ begin
132
+ kvs = collect_channel_kvs
133
+ kvs[:Op] = :wait_for_confirms
134
+
135
+ SolarWindsAPM::API.log_entry(:'rabbitmq-client')
136
+ # can't continue trace for wait on consumer, because we can't send opts for wait
137
+ # Seems ok, since this is waiting client side
138
+ # and not actually spending time on the consumer
139
+
140
+ wait_for_confirms_without_sw_apm
141
+ rescue => e
142
+ SolarWindsAPM::API.log_exception(:'rabbitmq-client', e)
143
+ raise e
144
+ ensure
145
+ kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:bunnyclient][:collect_backtraces]
146
+ SolarWindsAPM::API.log_exit(:'rabbitmq-client', kvs)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ if defined?(Bunny) && SolarWindsAPM::Config[:bunnyclient][:enabled]
154
+ SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting bunny client' if SolarWindsAPM::Config[:verbose]
155
+ SolarWindsAPM::Util.send_include(Bunny::Exchange, SolarWindsAPM::Inst::BunnyExchange)
156
+ SolarWindsAPM::Util.send_include(Bunny::Channel, SolarWindsAPM::Inst::BunnyChannel)
157
+ end
@@ -0,0 +1,102 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module SolarWindsAPM
5
+ module Inst
6
+ module BunnyConsumer
7
+ include SolarWindsAPM::SDK::TraceContextHeaders
8
+
9
+ def self.included(klass)
10
+ SolarWindsAPM::Util.method_alias(klass, :call, ::Bunny::Consumer)
11
+ end
12
+
13
+ def collect_consumer_kvs(args)
14
+ kvs = {}
15
+ kvs[:Spec] = :job
16
+ kvs[:Flavor] = :rabbitmq
17
+ kvs[:RemoteHost] = @channel.connection.host
18
+ kvs[:RemotePort] = @channel.connection.port.to_i
19
+ kvs[:VirtualHost] = @channel.connection.vhost
20
+
21
+ mp = args[1]
22
+ kvs[:RoutingKey] = args[0].routing_key if args[0].routing_key
23
+ kvs[:MsgID] = args[1].message_id if mp.message_id
24
+ kvs[:AppID] = args[1].app_id if mp.app_id
25
+ kvs[:Priority] = args[1].priority if mp.priority
26
+
27
+ if @queue.respond_to?(:name)
28
+ kvs[:Queue] = @queue.name
29
+ else
30
+ kvs[:Queue] = @queue
31
+ end
32
+
33
+ # Report configurable Controller/Action KVs
34
+ # See SolarWindsAPM::Config[:bunnyconsumer] in lib/solarwinds_apm/config.rb
35
+ # Used for dashboard trace filtering
36
+ controller_key = SolarWindsAPM::Config[:bunnyconsumer][:controller]
37
+ if mp.respond_to?(controller_key)
38
+ value = mp.method(controller_key).call
39
+ kvs[:Controller] = value if value
40
+ end
41
+
42
+ action_key = SolarWindsAPM::Config[:bunnyconsumer][:action]
43
+ if mp.respond_to?(action_key)
44
+ value = mp.method(action_key).call
45
+ kvs[:Action] = value if value
46
+ end
47
+
48
+ if kvs[:Queue]
49
+ kvs[:URL] = "/bunny/#{kvs[:Queue]}"
50
+ else
51
+ kvs[:URL] = "/bunny/consumer"
52
+ end
53
+
54
+ if SolarWindsAPM::Config[:bunnyconsumer][:log_args] && @arguments
55
+ kvs[:Args] = @arguments.to_s
56
+ end
57
+
58
+ kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:bunnyconsumer][:collect_backtraces]
59
+
60
+ kvs
61
+ rescue => e
62
+ SolarWindsAPM.logger.debug "[solarwinds_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if SolarWindsAPM::Config[:verbose]
63
+ ensure
64
+ return kvs
65
+ end
66
+
67
+ def call_with_sw_apm(*args)
68
+ report_kvs = collect_consumer_kvs(args)
69
+
70
+ # TODO all of this (to the next empty line) can be removed
71
+ # once all of our agents (actually only Node) use w3c-header for RabbitMQ
72
+ # w3c headers are read and added by the logging code
73
+ # If SourceTrace was passed:
74
+ # - capture it, report it
75
+ # - and add it as traceparent and tracestate header for use by start_trace
76
+ headers = args[1][:headers]
77
+ if headers && headers['SourceTrace']
78
+ report_kvs[:SourceTrace] = headers['SourceTrace']
79
+ # Remove SourceTrace
80
+ headers.delete('SourceTrace')
81
+ unless headers['traceparent'] && headers['tracestate']
82
+ add_tracecontext_headers(headers)
83
+ end
84
+ end
85
+
86
+ # the context either gets propagated via w3c headers
87
+ # or a new one should be started
88
+ # lets clear any context that may exist
89
+ # TODO revert with NH-11132
90
+ SolarWindsAPM::Context.clear
91
+ SolarWindsAPM::SDK.start_trace(:'rabbitmq-consumer', kvs: report_kvs, headers: headers) do
92
+ call_without_sw_apm(*args)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ if SolarWindsAPM::Config[:bunnyconsumer][:enabled] && defined?(Bunny)
100
+ SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting bunny consumer' if SolarWindsAPM::Config[:verbose]
101
+ SolarWindsAPM::Util.send_include(Bunny::Consumer, SolarWindsAPM::Inst::BunnyConsumer)
102
+ end
@@ -0,0 +1,288 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module SolarWindsAPM
5
+ module Inst
6
+
7
+ # Curb instrumentation wraps instance and class methods in two classes:
8
+ # Curl::Easy and Curl::Multi. This CurlUtility module is used as a common module
9
+ # to be shared among both modules.
10
+ module CurlUtility
11
+ include SolarWindsAPM::SDK::TraceContextHeaders
12
+
13
+ private
14
+ ##
15
+ # sw_apm_collect
16
+ #
17
+ # Used as a central area to retrieve and return values
18
+ # that we're interesting in reporting to SolarWindsAPM
19
+ #
20
+ def sw_apm_collect(verb = nil)
21
+ kvs = {}
22
+
23
+ kvs[:Spec] = 'rsc'
24
+ kvs[:IsService] = 1
25
+
26
+ # Conditionally log query args
27
+ if SolarWindsAPM::Config[:curb][:log_args]
28
+ kvs[:RemoteURL] = url
29
+ else
30
+ kvs[:RemoteURL] = url.split('?').first
31
+ end
32
+
33
+ kvs[:HTTPMethod] = verb if verb
34
+
35
+ kvs
36
+ rescue => e
37
+ SolarWindsAPM.logger.debug "[solarwinds_apm/debug] Error capturing curb KVs: #{e.message}"
38
+ if SolarWindsAPM::Config[:verbose]
39
+ SolarWindsAPM.logger.debug "[solarwinds_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
40
+ SolarWindsAPM.logger.debug e.backtrace.join('\n')
41
+ end
42
+ ensure
43
+ return kvs
44
+ end
45
+
46
+ ##
47
+ # trace_curb_method
48
+ #
49
+ # An agnostic method that will profile any Curl::Easy method (and optional args and block)
50
+ # that you throw at it.
51
+ #
52
+ def trace_curb_method(kvs, method, args, &block)
53
+ # If we're not tracing, just do a fast return.
54
+ unless SolarWindsAPM.tracing?
55
+ add_tracecontext_headers(self.headers)
56
+ return self.send(method, args, &block)
57
+ end
58
+
59
+ begin
60
+ kvs.merge! sw_apm_collect
61
+
62
+ SolarWindsAPM::API.log_entry(:curb, kvs)
63
+ kvs.clear
64
+
65
+ # The core curb call
66
+ add_tracecontext_headers(self.headers)
67
+ response = self.send(method, *args, &block)
68
+
69
+ kvs[:HTTPStatus] = response_code
70
+
71
+ # If we get a redirect, report the location header
72
+ if ((300..308).to_a.include? response_code) && headers.key?("Location")
73
+ kvs[:Location] = headers["Location"]
74
+ end
75
+
76
+ response
77
+ rescue => e
78
+ SolarWindsAPM::API.log_exception(:curb, e)
79
+ raise e
80
+ ensure
81
+ kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:curb][:collect_backtraces]
82
+ SolarWindsAPM::API.log_exit(:curb, kvs)
83
+ end
84
+ end
85
+
86
+ end # CurlUtility
87
+
88
+ # Instrumentation specific to ::Curl::Easy
89
+ module CurlEasy
90
+ # Common methods
91
+ include SolarWindsAPM::Inst::CurlUtility
92
+
93
+ def self.included(klass)
94
+ SolarWindsAPM::Util.method_alias(klass, :http, ::Curl::Easy)
95
+ SolarWindsAPM::Util.method_alias(klass, :perform, ::Curl::Easy)
96
+ SolarWindsAPM::Util.method_alias(klass, :http_put, ::Curl::Easy)
97
+ SolarWindsAPM::Util.method_alias(klass, :http_post, ::Curl::Easy)
98
+ end
99
+
100
+ ##
101
+ # http_post_with_sw_apm
102
+ #
103
+ # ::Curl::Easy.new.http_post wrapper
104
+ #
105
+ def http_post_with_sw_apm(*args, &block)
106
+ # If we're not tracing, just do a fast return.
107
+ if !SolarWindsAPM.tracing? || SolarWindsAPM.tracing_layer?(:curb)
108
+ add_tracecontext_headers(self.headers)
109
+ return http_post_without_sw_apm(*args)
110
+ end
111
+
112
+ kvs = {}
113
+ kvs[:HTTPMethod] = :POST
114
+
115
+ trace_curb_method(kvs, :http_post_without_sw_apm, args, &block)
116
+ end
117
+
118
+ ##
119
+ # http_put_with_sw_apm
120
+ #
121
+ # ::Curl::Easy.new.http_put wrapper
122
+ #
123
+ def http_put_with_sw_apm(*args, &block)
124
+ # If we're not tracing, just do a fast return.
125
+ if !SolarWindsAPM.tracing? || SolarWindsAPM.tracing_layer?(:curb)
126
+ add_tracecontext_headers(self.headers)
127
+ return http_put_without_sw_apm(data)
128
+ end
129
+
130
+ kvs = {}
131
+ kvs[:HTTPMethod] = :PUT
132
+
133
+ trace_curb_method(kvs, :http_put_without_sw_apm, args, &block)
134
+ end
135
+
136
+ ##
137
+ # perform_with_sw_apm
138
+ #
139
+ # ::Curl::Easy.new.perform wrapper
140
+ #
141
+ def perform_with_sw_apm(&block)
142
+ # If we're not tracing, just do a fast return.
143
+ # excluding curb layer: because the curb C code for easy.http calls perform,
144
+ # we have to make sure we don't log again
145
+ if !SolarWindsAPM.tracing? || SolarWindsAPM.tracing_layer?(:curb)
146
+ add_tracecontext_headers(self.headers)
147
+ return perform_without_sw_apm(&block)
148
+ end
149
+
150
+ kvs = {}
151
+ # This perform gets called from two places, ::Curl::Easy.new.perform
152
+ # and Curl::Easy.new.http_head. In the case of http_head we detect the
153
+ # HTTP verb via get info.
154
+ if self.getinfo(self.sym2curl(:nobody))
155
+ kvs[:HTTPMethod] = :HEAD
156
+ else
157
+ kvs[:HTTPMethod] = :GET
158
+ end
159
+
160
+ trace_curb_method(kvs, :perform_without_sw_apm, nil, &block)
161
+ end
162
+
163
+ ##
164
+ # http_with_sw_apm
165
+ #
166
+ # ::Curl::Easy.new.http wrapper
167
+ #
168
+ def http_with_sw_apm(verb, &block)
169
+ unless SolarWindsAPM.tracing?
170
+ add_tracecontext_headers(self.headers)
171
+ # If we're not tracing, just do a fast return.
172
+ return http_without_sw_apm(verb)
173
+ end
174
+
175
+ kvs = {}
176
+ kvs[:HTTPMethod] = verb
177
+
178
+ trace_curb_method(kvs, :http_without_sw_apm, [verb], &block)
179
+ end
180
+ end
181
+
182
+ ##
183
+ # CurlMultiCM
184
+ #
185
+ # This module contains the class method wrappers for the CurlMulti class.
186
+ # This module should be _extended_ by CurlMulti.
187
+ #
188
+ module CurlMultiCM
189
+ include SolarWindsAPM::Inst::CurlUtility
190
+
191
+ def self.extended(klass)
192
+ SolarWindsAPM::Util.class_method_alias(klass, :http, ::Curl::Multi)
193
+ end
194
+
195
+ ##
196
+ # http_with_sw_apm
197
+ #
198
+ # ::Curl::Multi.new.http wrapper
199
+ #
200
+ def http_with_sw_apm(urls_with_config, multi_options={}, &block)
201
+ # If we're not tracing, just do a fast return.
202
+ unless SolarWindsAPM.tracing?
203
+ urls_with_config.each do |conf|
204
+ conf[:headers] ||= {}
205
+ add_tracecontext_headers(conf[:headers])
206
+ end
207
+ return http_without_sw_apm(urls_with_config, multi_options, &block)
208
+ end
209
+
210
+ begin
211
+ kvs = {}
212
+ kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:curb][:collect_backtraces]
213
+
214
+ SolarWindsAPM::API.log_entry(:curb_multi, kvs)
215
+ context = SolarWindsAPM::Context.toString
216
+
217
+ traces = []
218
+ urls_with_config.each do |conf|
219
+ conf[:headers] ||= {}
220
+ add_tracecontext_headers(conf[:headers])
221
+ end
222
+ # The core curb call
223
+ http_without_sw_apm(urls_with_config, multi_options)
224
+ rescue => e
225
+ SolarWindsAPM::API.log_exception(:curb_multi, e)
226
+ raise e
227
+ ensure
228
+ SolarWindsAPM::API.log_exit(:curb_multi)
229
+ end
230
+ end
231
+ end
232
+
233
+ ##
234
+ # CurlMultiIM
235
+ #
236
+ # This module contains the instance method wrappers for the CurlMulti class.
237
+ # This module should be _included_ into CurlMulti.
238
+ #
239
+ module CurlMultiIM
240
+ include SolarWindsAPM::Inst::CurlUtility
241
+
242
+ def self.included(klass)
243
+ SolarWindsAPM::Util.method_alias(klass, :perform, ::Curl::Multi)
244
+ end
245
+
246
+ ##
247
+ # perform_with_sw_apm
248
+ #
249
+ # ::Curl::Multi.new.perform wrapper
250
+ #
251
+ # the reason we instrument this method is because it can be called directly,
252
+ # therefore we exclude calls that already have a curb layer assigned
253
+ # Be aware: this method is also called from the c-implementation
254
+ #
255
+ def perform_with_sw_apm(&block)
256
+ self.requests.each do |request|
257
+ request = request[1] if request.is_a?(Array)
258
+ add_tracecontext_headers(request.headers)
259
+ end
260
+ # If we're not tracing or we're already tracing curb, just do a fast return.
261
+ if !SolarWindsAPM.tracing? || [:curb, :curb_multi].include?(SolarWindsAPM.layer)
262
+ return perform_without_sw_apm(&block)
263
+ end
264
+
265
+ begin
266
+ kvs = {}
267
+ kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:curb][:collect_backtraces]
268
+
269
+ SolarWindsAPM::API.log_entry(:curb_multi, kvs)
270
+
271
+ perform_without_sw_apm(&block)
272
+ rescue => e
273
+ SolarWindsAPM::API.log_exception(:curb_multi, e)
274
+ raise e
275
+ ensure
276
+ SolarWindsAPM::API.log_exit(:curb_multi)
277
+ end
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ if SolarWindsAPM::Config[:curb][:enabled] && defined?(::Curl)
284
+ SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting curb' if SolarWindsAPM::Config[:verbose]
285
+ SolarWindsAPM::Util.send_include(::Curl::Easy, SolarWindsAPM::Inst::CurlEasy)
286
+ SolarWindsAPM::Util.send_extend(::Curl::Multi, SolarWindsAPM::Inst::CurlMultiCM)
287
+ SolarWindsAPM::Util.send_include(::Curl::Multi, SolarWindsAPM::Inst::CurlMultiIM)
288
+ end
@@ -0,0 +1,89 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module SolarWindsAPM
5
+ module Inst
6
+ module Dalli
7
+ include SolarWindsAPM::API::Memcache
8
+
9
+ def self.included(cls)
10
+ cls.class_eval do
11
+ SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting memcache (dalli)' if SolarWindsAPM::Config[:verbose]
12
+ if ::Dalli::Client.private_method_defined? :perform
13
+ alias perform_without_sw_apm perform
14
+ alias perform perform_with_sw_apm
15
+ else
16
+ SolarWindsAPM.logger.warn '[solarwinds_apm/loading] Couldn\'t properly instrument Memcache (Dalli). Partial traces may occur.'
17
+ end
18
+
19
+ if ::Dalli::Client.method_defined? :get_multi
20
+ alias get_multi_without_sw_apm get_multi
21
+ alias get_multi get_multi_with_sw_apm
22
+ end
23
+ end
24
+ end
25
+
26
+ def perform_with_sw_apm(*all_args, &blk)
27
+ op, key, *args = *all_args
28
+
29
+ report_kvs = {}
30
+ report_kvs[:KVOp] = op
31
+ report_kvs[:KVKey] = key
32
+
33
+ servers = @servers || @normalized_servers # name change since Dall 3.2.0
34
+ if servers.is_a?(Array) && !servers.empty?
35
+ report_kvs[:RemoteHost] = servers.join(", ")
36
+ end
37
+
38
+ if SolarWindsAPM.tracing? && !SolarWindsAPM.tracing_layer_op?(:get_multi)
39
+ SolarWindsAPM::SDK.trace(:memcache, kvs: report_kvs) do
40
+ result = perform_without_sw_apm(*all_args, &blk)
41
+
42
+ # Clear the hash for a potential info event
43
+ report_kvs.clear
44
+ report_kvs[:KVHit] = memcache_hit?(result) if op == :get && key.class == String
45
+ report_kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:dalli][:collect_backtraces]
46
+
47
+ result
48
+ end
49
+ else
50
+ perform_without_sw_apm(*all_args, &blk)
51
+ end
52
+ end
53
+
54
+ def get_multi_with_sw_apm(*keys)
55
+ return get_multi_without_sw_apm(*keys) unless SolarWindsAPM.tracing?
56
+
57
+ info_kvs = {}
58
+
59
+ begin
60
+ info_kvs[:KVKeyCount] = keys.flatten.length
61
+ info_kvs[:KVKeyCount] = (info_kvs[:KVKeyCount] - 1) if keys.last.is_a?(Hash) || keys.last.nil?
62
+
63
+ servers = @servers || @normalized_servers # name change since Dalli 3.2.1
64
+ if servers.is_a?(Array) && !servers.empty?
65
+ info_kvs[:RemoteHost] = servers.join(", ")
66
+ end
67
+ rescue => e
68
+ SolarWindsAPM.logger.debug "[solarwinds_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if SolarWindsAPM::Config[:verbose]
69
+ end
70
+
71
+ info_kvs[:KVOp] = :get_multi
72
+ info_kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:dalli][:collect_backtraces]
73
+ SolarWindsAPM::SDK.trace(:memcache, kvs: info_kvs, protect_op: :get_multi) do
74
+ values = get_multi_without_sw_apm(*keys)
75
+
76
+ info_kvs[:KVHitCount] = values.length
77
+
78
+ values
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ if defined?(Dalli) && SolarWindsAPM::Config[:dalli][:enabled]
86
+ ::Dalli::Client.module_eval do
87
+ include SolarWindsAPM::Inst::Dalli
88
+ end
89
+ end