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,160 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ # Constants from liboboe
5
+ SW_APM_TRACE_DISABLED = 0
6
+ SW_APM_TRACE_ENABLED = 1
7
+
8
+ SAMPLE_RATE_MASK = 0b0000111111111111111111111111
9
+ SAMPLE_SOURCE_MASK = 0b1111000000000000000000000000
10
+
11
+ # w3c trace context related global constants
12
+ # see: https://www.w3.org/TR/trace-context/#tracestate-limits
13
+ SW_APM_TRACESTATE_ID = 'sw'.freeze
14
+ SW_APM_MAX_TRACESTATE_BYTES = 512
15
+ SW_APM_MAX_TRACESTATE_MEMBER_BYTES = 128
16
+
17
+ SW_APM_STR_LAYER = 'Layer'.freeze
18
+ SW_APM_STR_LABEL = 'Label'.freeze
19
+
20
+ ##
21
+ # This module is the base module for SolarWindsAPM reporting.
22
+ #
23
+ module SolarWindsAPMBase
24
+ extend SolarWindsAPM::ThreadLocal
25
+
26
+ attr_accessor :reporter
27
+ attr_accessor :loaded
28
+
29
+ thread_local :sample_source
30
+ thread_local :sample_rate
31
+ thread_local :layer
32
+ thread_local :layer_op
33
+
34
+ # trace context is used to store incoming w3c trace information
35
+ thread_local :trace_context
36
+
37
+ # transaction_name is used for custom transaction naming
38
+ # It needs to be globally accessible, but is only set by the request processors of the different frameworks
39
+ # and read by rack
40
+ thread_local :transaction_name
41
+
42
+ # Semaphore used during the test suite to test
43
+ # global config options.
44
+ thread_local :config_lock
45
+
46
+ ##
47
+ # tracing_layer?
48
+ #
49
+ # Queries the thread local variable about the current
50
+ # layer being traced. This is used in cases of recursive
51
+ # operation tracing or one instrumented operation calling another.
52
+ #
53
+ def tracing_layer?(layer)
54
+ SolarWindsAPM.layer == layer.to_sym
55
+ end
56
+
57
+ ##
58
+ # tracing_layer_op?
59
+ #
60
+ # Queries the thread local variable about the current
61
+ # operation being traced. This is used in cases of recursive
62
+ # operation tracing or one instrumented operation calling another.
63
+ #
64
+ # In such cases, we only want to trace the outermost operation.
65
+ #
66
+ def tracing_layer_op?(operation)
67
+ unless SolarWindsAPM.layer_op.nil? || SolarWindsAPM.layer_op.is_a?(Array)
68
+ SolarWindsAPM.logger.error('[SolarWindsAPM/logging] INTERNAL: layer_op should be nil or an array, please report to technicalsupport@solarwinds.com')
69
+ return false
70
+ end
71
+
72
+ return false if SolarWindsAPM.layer_op.nil? || SolarWindsAPM.layer_op.empty? || !operation.respond_to?(:to_sym)
73
+ SolarWindsAPM.layer_op.last == operation.to_sym
74
+ end
75
+
76
+ # TODO review use of these boolean statements
77
+ # ____ they should now be handled by TransactionSettings,
78
+ # ____ because there can be exceptions to :enabled and :disabled
79
+
80
+ ##
81
+ # Returns true if the tracing_mode is set to :enabled.
82
+ # False otherwise
83
+ #
84
+ def tracing_enabled?
85
+ SolarWindsAPM::Config[:tracing_mode] &&
86
+ [:enabled, :always].include?(SolarWindsAPM::Config[:tracing_mode].to_sym)
87
+ end
88
+
89
+ ##
90
+ # Returns true if the tracing_mode is set to :disabled.
91
+ # False otherwise
92
+ #
93
+ def tracing_disabled?
94
+ SolarWindsAPM::Config[:tracing_mode] &&
95
+ [:disabled, :never].include?(SolarWindsAPM::Config[:tracing_mode].to_sym)
96
+ end
97
+
98
+ ##
99
+ # Returns true if we are currently tracing a request
100
+ # False otherwise
101
+ #
102
+ def tracing?
103
+ return false if !SolarWindsAPM.loaded # || SolarWindsAPM.tracing_disabled?
104
+ SolarWindsAPM::Context.isSampled
105
+ end
106
+
107
+ def heroku?
108
+ ENV.key?('SW_APM_URL')
109
+ end
110
+
111
+ ##
112
+ # Determines if we are running under a forking webserver
113
+ #
114
+ def forking_webserver?
115
+ if (defined?(::Unicorn) && ($PROGRAM_NAME =~ /unicorn/i)) ||
116
+ (defined?(::Puma) && ($PROGRAM_NAME =~ /puma/i))
117
+ true
118
+ else
119
+ false
120
+ end
121
+ end
122
+
123
+ ##
124
+ # Indicates whether a supported framework is in use
125
+ # or not
126
+ #
127
+ def framework?
128
+ defined?(::Rails) || defined?(::Sinatra) || defined?(::Padrino) || defined?(::Grape)
129
+ end
130
+
131
+ ##
132
+ # These methods should be implemented by the descendants
133
+ # currently only Oboe_metal
134
+ #
135
+ def sample?(_opts = {})
136
+ fail 'sample? should be implemented by metal layer.'
137
+ end
138
+
139
+ def log(_layer, _label, _options = {})
140
+ fail 'log should be implemented by metal layer.'
141
+ end
142
+
143
+ def set_tracing_mode(_mode)
144
+ fail 'set_tracing_mode should be implemented by metal layer.'
145
+ end
146
+
147
+ def set_sample_rate(_rate)
148
+ fail 'set_sample_rate should be implemented by metal layer.'
149
+ end
150
+ end
151
+
152
+ module SolarWindsAPM
153
+ extend SolarWindsAPMBase
154
+ end
155
+
156
+ # Setup an alias so we don't bug users
157
+ # about single letter capitalization
158
+ SolarwindsAPM = SolarWindsAPM
159
+ SolarWindsApm = SolarWindsAPM
160
+ SolarwindsApm = SolarWindsAPM
@@ -0,0 +1,301 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ require_relative 'support/transaction_settings'
5
+
6
+ module SolarWindsAPM
7
+ ##
8
+ # This module exposes a nested configuration hash that can be used to
9
+ # configure and/or modify the functionality of the solarwinds_apm gem.
10
+ #
11
+ # Use SolarWindsAPM::Config.show to view the entire nested hash.
12
+ #
13
+ module Config
14
+ @@config = {}
15
+
16
+ @@instrumentation = [:action_controller, :action_controller_api, :action_view,
17
+ :active_record, :bunnyclient, :bunnyconsumer, :curb,
18
+ :dalli, :delayed_jobclient, :delayed_jobworker,
19
+ :excon, :faraday, :graphql, :grpc_client, :grpc_server, :grape,
20
+ :httpclient, :nethttp, :memcached, :mongo, :moped, :padrino, :rack, :redis,
21
+ :resqueclient, :resqueworker, :rest_client,
22
+ :sequel, :sidekiqclient, :sidekiqworker, :sinatra, :typhoeus]
23
+
24
+ # ignore configs for instrumentations we don't have anymore
25
+ # can't remove because the config may still be present in configs created
26
+ # with previous gem versions
27
+ @@ignore = [:em_http_request]
28
+
29
+ # Subgrouping of instrumentation
30
+ @@http_clients = [:curb, :excon,
31
+ :faraday, :httpclient, :nethttp, :rest_client, :typhoeus]
32
+
33
+ ##
34
+ # load_config_file
35
+ #
36
+ # There are 3 possible locations for the config file:
37
+ # Rails default, ENV['SW_APM_CONFIG_RUBY'], or the gem's default
38
+ #
39
+ # Hierarchie:
40
+ # 1 - Rails default: config/initializers/solarwinds_apm.rb
41
+ # (also loaded by Rails, but we can't reliably determine if Rails is running)
42
+ # 2 - ENV['SW_APM_CONFIG_RUBY']
43
+ # 3 - Gem default: <startup_dir>/solarwinds_apm_config.rb
44
+ #
45
+ def self.load_config_file
46
+ config_files = []
47
+
48
+ # Check for the rails config file
49
+ config_file = File.join(Dir.pwd, 'config/initializers/solarwinds_apm.rb')
50
+ config_files << config_file if File.exist?(config_file)
51
+
52
+ # Check for file set by env variable
53
+ if ENV.key?('SW_APM_CONFIG_RUBY')
54
+ if File.exist?(ENV['SW_APM_CONFIG_RUBY']) && !File.directory?(ENV['SW_APM_CONFIG_RUBY'])
55
+ config_files << ENV['SW_APM_CONFIG_RUBY']
56
+ elsif File.exist?(File.join(ENV['SW_APM_CONFIG_RUBY'], 'solarwinds_apm_config.rb'))
57
+ config_files << File.join(ENV['SW_APM_CONFIG_RUBY'], 'solarwinds_apm_config.rb')
58
+ else
59
+ SolarWindsAPM.logger.warn "[solarwinds_apm/config] Could not find the configuration file set by the SW_APM_CONFIG_RUBY environment variable: #{ENV['SW_APM_CONFIG_RUBY']}"
60
+ end
61
+ end
62
+
63
+ # Check for default config file
64
+ config_file = File.join(Dir.pwd, 'solarwinds_apm_config.rb')
65
+ config_files << config_file if File.exist?(config_file)
66
+
67
+ unless config_files.empty? # we use the defaults from the template if there are no config files
68
+ if config_files.size > 1
69
+ SolarWindsAPM.logger.warn [
70
+ '[solarwinds_apm/config] Multiple configuration files configured, using the first one listed: ',
71
+ config_files.join(', ')
72
+ ].join(' ')
73
+ end
74
+ load(config_files[0])
75
+ end
76
+
77
+ # sets SolarWindsAPM::Config[:debug_level], SolarWindsAPM.logger.level
78
+ set_log_level
79
+
80
+ # the verbose setting is only relevant for ruby, ENV['SW_APM_GEM_VERBOSE'] overrides
81
+ if ENV.key?('SW_APM_GEM_VERBOSE')
82
+ SolarWindsAPM::Config[:verbose] = ENV['SW_APM_GEM_VERBOSE'].downcase == 'true'
83
+ end
84
+ end
85
+
86
+ def self.set_log_level
87
+ unless (-1..6).include?(SolarWindsAPM::Config[:debug_level])
88
+ SolarWindsAPM::Config[:debug_level] = 3
89
+ end
90
+
91
+ # let's find and use the equivalent debug level for ruby
92
+ debug_level = (ENV['SW_APM_DEBUG_LEVEL'] || SolarWindsAPM::Config[:debug_level] || 3).to_i
93
+ if debug_level < 0
94
+ # there should be no logging if SW_APM_DEBUG_LEVEL == -1
95
+ # In Ruby level 5 is UNKNOWN and it can log, but level 6 is quiet
96
+ SolarWindsAPM.logger.level = 6
97
+ else
98
+ SolarWindsAPM.logger.level = [4 - debug_level, 0].max
99
+ end
100
+ SolarWindsAPM::Config[:debug_level] = debug_level
101
+ end
102
+
103
+ ##
104
+ # print_config
105
+ #
106
+ # print configurations one per line
107
+ # to create an output similar to the content of the config file
108
+ #
109
+ def self.print_config
110
+ SolarWindsAPM.logger.warn "# General configurations"
111
+ non_instrumentation = @@config.keys - @@instrumentation
112
+ non_instrumentation.each do |config|
113
+ SolarWindsAPM.logger.warn "SolarWindsAPM::Config[:#{config}] = #{@@config[config]}"
114
+ end
115
+
116
+ SolarWindsAPM.logger.warn "\n# Instrumentation specific configurations"
117
+ SolarWindsAPM.logger.warn "# Enabled/Disabled Instrumentation"
118
+ @@instrumentation.each do |config|
119
+ SolarWindsAPM.logger.warn "SolarWindsAPM::Config[:#{config}][:enabled] = #{@@config[config][:enabled]}"
120
+ end
121
+
122
+ SolarWindsAPM.logger.warn "\n# Enabled/Disabled Backtrace Collection"
123
+ @@instrumentation.each do |config|
124
+ SolarWindsAPM.logger.warn "SolarWindsAPM::Config[:#{config}][:collect_backtraces] = #{@@config[config][:collect_backtraces]}"
125
+ end
126
+
127
+ SolarWindsAPM.logger.warn "\n# Logging of outgoing HTTP query args"
128
+ @@instrumentation.each do |config|
129
+ SolarWindsAPM.logger.warn "SolarWindsAPM::Config[:#{config}][:log_args] = #{@@config[config][:log_args] || false}"
130
+ end
131
+
132
+ SolarWindsAPM.logger.warn "\n# Bunny Controller and Action"
133
+ SolarWindsAPM.logger.warn "SolarWindsAPM::Config[:bunnyconsumer][:controller] = #{@@config[:bunnyconsumer][:controller].inspect}"
134
+ SolarWindsAPM.logger.warn "SolarWindsAPM::Config[:bunnyconsumer][:action] = #{@@config[:bunnyconsumer][:action].inspect}"
135
+ nil
136
+ end
137
+
138
+ ##
139
+ # initialize
140
+ #
141
+ # Initializer method to set everything up with a default configuration.
142
+ # The defaults are read from the template configuration file.
143
+ #
144
+ # rubocop:disable Metrics/AbcSize
145
+ def self.initialize(_data = {})
146
+ (@@instrumentation+@@ignore).each { |k| @@config[k] = {} }
147
+ @@config[:transaction_name] = {}
148
+
149
+ # Always load the template, it has all the keys and defaults defined,
150
+ # no guarantee of completeness in the user's config file
151
+ load(File.join(File.dirname(File.dirname(__FILE__)),
152
+ 'rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb'))
153
+ end
154
+ # rubocop:enable Metrics/AbcSize
155
+
156
+ def self.update!(data)
157
+ data.each do |key, value|
158
+ self[key] = value
159
+ end
160
+ end
161
+
162
+ def self.merge!(data)
163
+ update!(data)
164
+ end
165
+
166
+ def self.[](key)
167
+ if key == :resque
168
+ SolarWindsAPM.logger.warn '[solarwinds_apm/warn] :resque config is deprecated. It is now split into :resqueclient and :resqueworker.'
169
+ SolarWindsAPM.logger.warn "[solarwinds_apm/warn] Called from #{Kernel.caller[0]}"
170
+ end
171
+
172
+ @@config[key.to_sym]
173
+ end
174
+
175
+ ##
176
+ # []=
177
+ #
178
+ # Config variable assignment method. Here we validate and store the
179
+ # assigned value(s) and trigger any secondary action needed.
180
+ #
181
+ # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
182
+ def self.[]=(key, value)
183
+ key = key.to_sym
184
+ @@config[key] = value
185
+
186
+ if key == :sampling_rate
187
+ SolarWindsAPM.logger.warn '[solarwinds_apm/config] sampling_rate is not a supported setting for SolarWindsAPM::Config. ' \
188
+ 'Please use :sample_rate.'
189
+
190
+ elsif key == :sample_rate
191
+ unless value.is_a?(Integer) || value.is_a?(Float)
192
+ SolarWindsAPM.logger.warn "[solarwinds_apm/config] :sample_rate must be a number between 0 and 1000000 (1m) " \
193
+ "(provided: #{value}), corrected to 0"
194
+ value = 0
195
+ end
196
+
197
+ # Validate :sample_rate value
198
+ unless value.between?(0, 1e6)
199
+ value_1 = value
200
+ value = value_1 < 0 ? 0 : 1_000_000
201
+ SolarWindsAPM.logger.warn "[solarwinds_apm/config] :sample_rate must be between 0 and 1000000 (1m) " \
202
+ "(provided: #{value_1}), corrected to #{value}"
203
+ end
204
+
205
+ # Assure value is an integer
206
+ @@config[key.to_sym] = value.to_i
207
+ SolarWindsAPM.set_sample_rate(value) if SolarWindsAPM.loaded
208
+
209
+ elsif key == :action_blacklist
210
+ SolarWindsAPM.logger.warn "[solarwinds_apm/config] :action_blacklist has been deprecated and no longer functions."
211
+
212
+ elsif key == :blacklist
213
+ SolarWindsAPM.logger.warn "[solarwinds_apm/config] :blacklist has been deprecated and no longer functions."
214
+
215
+ elsif key == :dnt_regexp
216
+ if value.nil? || value == ''
217
+ @@config[:dnt_compiled] = nil
218
+ else
219
+ @@config[:dnt_compiled] =
220
+ Regexp.new(SolarWindsAPM::Config[:dnt_regexp], SolarWindsAPM::Config[:dnt_opts] || nil)
221
+ end
222
+
223
+ elsif key == :dnt_opts
224
+ if SolarWindsAPM::Config[:dnt_regexp] && SolarWindsAPM::Config[:dnt_regexp] != ''
225
+ @@config[:dnt_compiled] =
226
+ Regexp.new(SolarWindsAPM::Config[:dnt_regexp], SolarWindsAPM::Config[:dnt_opts] || nil)
227
+ end
228
+
229
+ elsif key == :profiling_interval
230
+ if value.is_a?(Integer) && value > 0
231
+ value = [100, value].min
232
+ else
233
+ value = 10
234
+ end
235
+ @@config[:profiling_interval] = value
236
+ # CProfiler may not be loaded yet, the profiler will send the value
237
+ # after it is loaded
238
+ SolarWindsAPM::CProfiler.set_interval(value) if defined? SolarWindsAPM::CProfiler
239
+
240
+ elsif key == :transaction_settings
241
+ if value.is_a?(Hash)
242
+ SolarWindsAPM::TransactionSettings.compile_url_settings(value[:url])
243
+ else
244
+ SolarWindsAPM::TransactionSettings.reset_url_regexps
245
+ end
246
+
247
+ elsif key == :resque
248
+ SolarWindsAPM.logger.warn "[solarwinds_apm/config] :resque config is deprecated. It is now split into :resqueclient and :resqueworker."
249
+ SolarWindsAPM.logger.warn "[solarwinds_apm/config] Called from #{Kernel.caller[0]}"
250
+
251
+ elsif key == :include_url_query_params # DEPRECATED
252
+ # Obey the global flag and update all of the per instrumentation
253
+ # <tt>:log_args</tt> values.
254
+ @@config[:rack][:log_args] = value
255
+
256
+ elsif key == :include_remote_url_params # DEPRECATED
257
+ # Obey the global flag and update all of the per instrumentation
258
+ # <tt>:log_args</tt> values.
259
+ @@http_clients.each do |i|
260
+ @@config[i][:log_args] = value
261
+ end
262
+
263
+ elsif key == :tracing_mode
264
+ # CAN'T DO `set_tracing_mode` ANYMORE, ALL TRACING COMMUNICATION TO OBOE
265
+ # IS NOW HANDLED BY TransactionSettings
266
+ # SolarWindsAPM.set_tracing_mode(value.to_sym) if SolarWindsAPM.loaded
267
+
268
+ # Make sure that the mode is stored as a symbol
269
+ @@config[key.to_sym] = value.to_sym
270
+
271
+ elsif key == :trigger_tracing_mode
272
+ # Make sure that the mode is stored as a symbol
273
+ @@config[key.to_sym] = value.to_sym
274
+ end
275
+ end
276
+ # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
277
+
278
+ def self.method_missing(sym, *args)
279
+ class_var_name = "@@#{sym}"
280
+
281
+ if sym.to_s =~ /(.+)=$/
282
+ self[$1] = args.first
283
+ else
284
+ # Try part of the @@config hash first
285
+ if @@config.key?(sym)
286
+ self[sym]
287
+
288
+ # Then try as a class variable
289
+ elsif self.class_variable_defined?(class_var_name.to_sym)
290
+ self.class_eval(class_var_name)
291
+
292
+ # Congrats - You've won a brand new nil...
293
+ else
294
+ nil
295
+ end
296
+ end
297
+ end
298
+ end
299
+ end
300
+
301
+ SolarWindsAPM::Config.initialize
@@ -0,0 +1,96 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+ #
4
+ class GrapeError < StandardError; end
5
+
6
+ module SolarWindsAPM
7
+ module Grape
8
+ module API
9
+ def self.extended(klass)
10
+ SolarWindsAPM::Util.class_method_alias(klass, :inherited, ::Grape::API)
11
+ end
12
+
13
+ def inherited_with_sw_apm(subclass)
14
+ inherited_without_sw_apm(subclass)
15
+
16
+ subclass.use SolarWindsAPM::Rack
17
+ end
18
+ end
19
+
20
+ module Endpoint
21
+ def self.included(klass)
22
+ SolarWindsAPM::Util.method_alias(klass, :run, ::Grape::Endpoint)
23
+ end
24
+
25
+ def run_with_sw_apm(*args)
26
+ # Report Controller/Action and Transaction as best possible
27
+ report_kvs = {}
28
+
29
+ report_kvs[:Controller] = options[:for].to_s
30
+ report_kvs[:Action] =
31
+ if route&.pattern
32
+ route.options ? "#{route.options[:method]}#{route.pattern.origin}" : route.pattern.origin
33
+ else
34
+ args.empty? ? env['PATH_INFO'] : args[0]['PATH_INFO']
35
+ end
36
+ report_kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:grape][:collect_backtraces]
37
+
38
+ env['solarwinds_apm.controller'] = report_kvs[:Controller]
39
+ env['solarwinds_apm.action'] = report_kvs[:Action]
40
+
41
+ SolarWindsAPM::API.log_entry('grape', report_kvs)
42
+
43
+ run_without_sw_apm(*args)
44
+ ensure
45
+ SolarWindsAPM::API.log_exit('grape')
46
+ end
47
+ end
48
+
49
+ module Middleware
50
+ module Error
51
+ def self.included(klass)
52
+ SolarWindsAPM::Util.method_alias(klass, :error_response, ::Grape::Middleware::Error)
53
+ end
54
+
55
+ def error_response_with_sw_apm(error = {})
56
+ response = error_response_without_sw_apm(error)
57
+ status, headers, _body = response.finish
58
+
59
+ tracestring = SolarWindsAPM::Context.toString
60
+
61
+ if SolarWindsAPM.tracing?
62
+ # Since Grape uses throw/catch and not Exceptions, we have to create an exception here
63
+ exception = GrapeError.new(error[:message] ? error[:message] : "No message given.")
64
+ exception.set_backtrace(SolarWindsAPM::API.backtrace)
65
+
66
+ SolarWindsAPM::API.log_exception('rack', exception)
67
+
68
+ # Since calls to error() are handled similar to abort in Grape. We
69
+ # manually log the rack exit here since the original code won't
70
+ # be returned to
71
+ tracestring = SolarWindsAPM::API.log_end('rack', :Status => status)
72
+ end
73
+
74
+ if headers && headers.is_a?(Hash) && SolarWindsAPM::TraceString.valid?(tracestring)
75
+ # TODO this will change, w3c outgoing headers have not been standardized yet
76
+ headers['X-Trace'] = tracestring
77
+ end
78
+
79
+ response
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ if SolarWindsAPM::Config[:grape][:enabled] && defined?(Grape)
87
+ require 'solarwinds_apm/inst/rack'
88
+
89
+ SolarWindsAPM.logger.info "[solarwinds_apm/loading] Instrumenting Grape" if SolarWindsAPM::Config[:verbose]
90
+
91
+ SolarWindsAPM::Inst.load_instrumentation
92
+
93
+ SolarWindsAPM::Util.send_extend(Grape::API, SolarWindsAPM::Grape::API)
94
+ SolarWindsAPM::Util.send_include(Grape::Endpoint, SolarWindsAPM::Grape::Endpoint)
95
+ SolarWindsAPM::Util.send_include(Grape::Middleware::Error, SolarWindsAPM::Grape::Middleware::Error)
96
+ end
@@ -0,0 +1,78 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module SolarWindsAPM
5
+ module PadrinoInst
6
+ module Routing
7
+ def self.included(klass)
8
+ SolarWindsAPM::Util.method_alias(klass, :dispatch!, ::Padrino::Routing)
9
+ end
10
+
11
+ def dispatch_with_sw_apm
12
+
13
+ SolarWindsAPM::API.log_entry('padrino', {})
14
+ report_kvs = {}
15
+
16
+ result = dispatch_without_sw_apm
17
+
18
+ # Report Controller/Action and Transaction as best possible
19
+ controller = (request.controller && !request.controller.empty?) ? request.controller : nil
20
+ report_kvs[:Controller] = controller || self.class
21
+ report_kvs[:Action] = request.route_obj ? request.route_obj.path : request.action
22
+ env['solarwinds_apm.controller'] = report_kvs[:Controller]
23
+ env['solarwinds_apm.action'] = report_kvs[:Action]
24
+
25
+ result
26
+ rescue => e
27
+ SolarWindsAPM::API.log_exception('padrino', e)
28
+ raise e
29
+ ensure
30
+ report_kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:padrino][:collect_backtraces]
31
+ SolarWindsAPM::API.log_exit('padrino', report_kvs)
32
+ end
33
+ end
34
+
35
+ module Rendering
36
+ def self.included(klass)
37
+ SolarWindsAPM::Util.method_alias(klass, :render, ::Padrino::Rendering)
38
+ end
39
+
40
+ # TODO add test coverage, currently there are no tests for this
41
+ # ____ I'm not sure this gets ever called, Padrino uses Sinatra's render method
42
+ def render_with_sw_apm(engine, data = nil, options = {}, locals = {}, &block)
43
+ if SolarWindsAPM.tracing?
44
+ report_kvs = {}
45
+
46
+ report_kvs[:engine] = engine
47
+ report_kvs[:template] = data
48
+
49
+ SolarWindsAPM::SDK.trace(:padrino_render, kvs: report_kvs, protect_op: :padrino_render) do
50
+ report_kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:padrino][:collect_backtraces]
51
+ render_without_sw_apm(engine, data, options, locals, &block)
52
+ end
53
+ else
54
+ render_without_sw_apm(engine, data, options, locals, &block)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ if defined?(Padrino) && SolarWindsAPM::Config[:padrino][:enabled]
62
+ # This instrumentation is a superset of the Sinatra instrumentation similar
63
+ # to how Padrino is a superset of Sinatra itself.
64
+ SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting Padrino' if SolarWindsAPM::Config[:verbose]
65
+
66
+ Padrino.after_load do
67
+ SolarWindsAPM.logger = Padrino.logger if Padrino.respond_to?(:logger)
68
+ SolarWindsAPM::Inst.load_instrumentation
69
+
70
+ SolarWindsAPM::Util.send_include(Padrino::Routing::InstanceMethods, SolarWindsAPM::PadrinoInst::Routing)
71
+ if defined?(Padrino::Rendering)
72
+ SolarWindsAPM::Util.send_include(Padrino::Rendering::InstanceMethods, SolarWindsAPM::PadrinoInst::Rendering)
73
+ end
74
+
75
+ # Report __Init after fork when in Heroku
76
+ SolarWindsAPM::API.report_init unless SolarWindsAPM.heroku?
77
+ end
78
+ end