diff --git a/.rubocop.yml b/.rubocop.yml index d23a385..bf12bb0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -47,3 +47,4 @@ Naming/VariableNumber: Metrics/BlockNesting: Exclude: - 'lib/solarwinds_apm.rb' + - 'lib/solarwinds_apm/support.rb' diff --git a/CONFIGURATION.md b/CONFIGURATION.md index c56820d..4787c61 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -139,23 +139,6 @@ export SW_APM_CONFIG_RUBY=config/file/location.rb The configuration file should be Ruby code that sets key/values in the hash exposed by `SolarWindsAPM::Config`. The bundled [Rails generator template file](https://github.com/solarwinds/apm-ruby/blob/main/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb) serves as an example of the supported values, see also the [Reference](#reference) section. -### Transaction Filtering - -Specific transactions can be disabled from tracing (suppressing both spans and metrics) using the `:transaction_settings` configuration. An example that filters out static assets and a message consumer: -```ruby -SolarWindsAPM::Config[:transaction_settings] = [ - { - regexp: '\.(css|js|png)$', - opts: Regexp::IGNORECASE, - tracing: :disabled - }, - { - regexp: 'CONSUMER:mytopic process', - tracing: :disabled - } -] -``` - ## Reference Environment Variable | Config File Key | Description | Default @@ -169,10 +152,56 @@ Environment Variable | Config File Key | Description | Default `SW_APM_LOG_FILEPATH` | N/A | Configure the log file path for the C extension, e.g. `export SW_APM_LOG_FILEPATH=/path/file_path.log`. If set, messages from the C extension are written to the specified file instead of stderr. | None `SW_APM_PROXY` | `:http_proxy` | Configure an HTTP proxy through which the library connects to the collector. | None `SW_APM_SERVICE_KEY` | `:service_key` | API token and service name in the form of _token:service name_, **required**. | -`SW_APM_TAG_SQL` | `:tag_sql` | Enable/disable injecting trace context into supported SQL statements. Set to boolean true or (or string `true` in env var) to enable. | false +`SW_APM_TAG_SQL` | `:tag_sql` | Enable/disable injecting trace context into supported SQL statements. Set to boolean true or (or string `true` in env var) to enable, see [Tag Query with Trace Context](#tag-query-with-trace-context) for details.| false `SW_APM_TRIGGER_TRACING_MODE` | `:trigger_tracing_mode` | Enable/disable trigger tracing for the service. Setting to `disabled` may impact DEM visibility into the service. | `enabled` `SW_APM_TRUSTEDPATH` | N/A | The library uses the host system's default trusted CA certificates to verify the TLS connection to the collector. To override the default, define the trusted certificate path configuration option with an absolute path to a specific trusted certificate file in PEM format. | None N/A | `:log_args` | Enable/disable the collection of URL query parameters, set to boolean false to disable. | true N/A | `:log_traceId` | Configure the insertion of trace context into application logs, setting `:traced` would include the available context fields such as trace_id, span_id into log messages. | `:never` N/A | `:tracing_mode` | Enable/disable the tracing mode for this service, setting `:disabled` would suppress all trace spans and metrics. | `:enabled` -N/A | `:transaction_settings` | Configure tracing mode per transaction, aka transaction filtering. | None +N/A | `:transaction_settings` | Configure tracing mode per transaction, aka transaction filtering. See [Transaction Filtering](#transaction-filtering) for details.| None + +### Transaction Filtering + +Specific transactions can be disabled from tracing (suppressing both spans and metrics) using the `:transaction_settings` configuration. An example that filters out static assets and a message consumer: +```ruby +SolarWindsAPM::Config[:transaction_settings] = [ + { + regexp: '\.(css|js|png)$', + opts: Regexp::IGNORECASE, + tracing: :disabled + }, + { + regexp: 'CONSUMER:mytopic process', + tracing: :disabled + } +] +``` + +### Tag Query with Trace Context + +You can set the environment variable `SW_APM_TAG_SQL` or configuration file option `:tag_sql` to true to enable appending the current trace context into a database query as a SQL comment. For example: +``` +# query without tag sql +SELECT * FROM SAMPLE_TABLE WHERE user_id = 1; + +# query with tag sql +SELECT * FROM SAMPLE_TABLE WHERE user_id = 1; /* traceparent=7435a9fe510ae4533414d425dadf4e18-49e60702469db05f-01 */ +``` + +For Rails < 7 and non-Rails applications, we use a port of [Marginalia](https://github.com/basecamp/marginalia) that patches ActiveRecord to inject the comment. + +For Rails >= 7, Marginalia is [integrated into ActiveRecord](https://api.rubyonrails.org/classes/ActiveRecord/QueryLogs.html) so enabling this feature should be done through Rails [configuration](https://guides.rubyonrails.org/v7.0/configuring.html#config-active-record-query-log-tags-enabled). For example: +```ruby +class Application < Rails::Application + config.active_record.query_log_tags_enabled = true + config.active_record.query_log_tags = [ + { + traceparent: -> { + SolarWindsAPM::SWOMarginalia::Comment.traceparent + } + } + ] +end +``` + +Note that with Rails >= 7.1 the comment format can be specified via the `config.active_record.query_log_tags_format` option. SolarWinds Observability functionality depends on the default `:sqlcommenter` format, it is not recommended to change this value. diff --git a/Rakefile b/Rakefile index 9f1ed2f..51e4bee 100755 --- a/Rakefile +++ b/Rakefile @@ -69,7 +69,7 @@ end desc 'Start ubuntu docker container for testing and debugging.' task docker_dev: [:docker_down] do - cmd = "docker-compose run --service-ports \ + cmd = "docker compose run --service-ports \ --name ruby_sw_apm_ubuntu_development ruby_sw_apm_ubuntu_development" Dir.chdir('test') do sh cmd do |ok, res| @@ -81,7 +81,7 @@ end desc 'Stop all containers that were started for testing and debugging' task :docker_down do Dir.chdir('test') do - sh 'docker-compose down -v --remove-orphans' + sh 'docker compose down -v --remove-orphans' end end diff --git a/lib/solarwinds_apm/support.rb b/lib/solarwinds_apm/support.rb index 54ea935..c27d3e6 100644 --- a/lib/solarwinds_apm/support.rb +++ b/lib/solarwinds_apm/support.rb @@ -23,20 +23,9 @@ if defined?(Rails) if Rails.version < '7' require_relative 'support/swomarginalia/railtie' - elsif Rails.version >= '7' - # User has to define in their config/environments: - # config.active_record.query_log_tags = [ - # { - # tracecontext: -> { - # SolarWindsAPM::SWOMarginalia::Comment.traceparent - # } - # } - # ] - SolarWindsAPM.logger.info do - 'In Rails 7, tag tracecontext on a query by including SolarWindsAPM::SWOMarginalia::Comment.traceparent as function in config.active_record.query_log_tags.' - end - SolarWindsAPM.logger.info { 'For more information, please check https://api.rubyonrails.org/classes/ActiveRecord/QueryLogs.html' } + else require_relative 'support/swomarginalia/comment' + require_relative 'support/swomarginalia/formatter' if Rails.version <= '7.1' end elsif defined?(ActiveRecord) require_relative 'support/swomarginalia/load_swomarginalia' diff --git a/lib/solarwinds_apm/support/swomarginalia/formatter.rb b/lib/solarwinds_apm/support/swomarginalia/formatter.rb new file mode 100644 index 0000000..bc8a31b --- /dev/null +++ b/lib/solarwinds_apm/support/swomarginalia/formatter.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module SolarWindsAPM + module SWOMarginalia + module Formatter + def tag_content + comment = super + comment.tr!(':', '=') + comment + end + end + end +end + +# Prepend the Formatter module to the singleton class of ActiveRecord::QueryLogs +if defined?(ActiveRecord::QueryLogs) + class << ActiveRecord::QueryLogs + prepend SolarWindsAPM::SWOMarginalia::Formatter + end +end diff --git a/solarwinds_apm.gemspec b/solarwinds_apm.gemspec index 9aeaeb7..4557ea0 100644 --- a/solarwinds_apm.gemspec +++ b/solarwinds_apm.gemspec @@ -51,7 +51,7 @@ Gem::Specification.new do |s| # this still gives a warning, would have to be pinned to a minor version # but that is not necessary and may restrict other gems - s.add_runtime_dependency('json', '~> 2.0') + s.add_dependency('json', '~> 2.0') s.required_ruby_version = '>= 2.7.0' s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }