solarwinds_apm 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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