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,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