diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7b88e893..d9954230 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,8 @@ * @solarwindscloud/apm-instrumentation + +# as of Sept 2023, remove after GH EMU move (see below) +* @solarwindscloud/eng-apm-instrumentation +* @solarwinds-cloud/eng-apm-instrumentation + +# placeholder for GH EMU where this will move to new org for public repos +* @solarwinds/eng-apm-instrumentation diff --git a/.github/workflows/build_and_release_gem.yml b/.github/workflows/build_and_release_gem.yml index dd0d9815..3f77e3a7 100644 --- a/.github/workflows/build_and_release_gem.yml +++ b/.github/workflows/build_and_release_gem.yml @@ -34,7 +34,7 @@ jobs: - name: Install swig 4.0.2 run: | - apt update && apt install -y --no-install-recommends bison swig + sudo apt-get update && sudo apt-get install -y --no-install-recommends bison swig - name: Download files from cloud.solarwinds.com and create swig wrapper env: diff --git a/CHANGELOG.md b/CHANGELOG.md index 134e2908..9c9e54a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,28 @@ https://github.com/solarwindscloud/solarwinds-apm-ruby/releases Dates in this file are in the format MM/DD/YYYY. +# solarwinds_apm 6.0.0.preV3 (08/08/2023) + +This release includes the following features: + +* Relaxed the opentelemetry-sdk version requirement to 1.2.0 for common version 0.19.6 + +# solarwinds_apm 6.0.0.preV3 (08/08/2023) + +This release includes the following features: + +* Updgraded liboboe version to 13.0.0 +* Rescue the sampler, processor and exporter without re-raise the error +* Marginlia for tracecontext in sql will work for activerecord > 7 with non-rails app (e.g. sinatra) +* Environmental variable name change: SOLARWINDS_APM_ENABLED -> SW_APM_ENABLED +* Updated lumberjack for tracecontext in logs +* Refactored test file structure based on folder `lib/` +* DB statement obfuscate for mysql2, pg and dalli will be default for opentelemetry-instrumentation +* Added otel.status and otel.description in span attributes +* Abandoned baggage to store root span; instea, using txn_manager +* Changed layer to span.kind:span.name +* Removed extensions from transaction_settings + # solarwinds_apm 6.0.0.preV2 (08/08/2023) This release includes the following features: diff --git a/CONFIG.md b/CONFIG.md deleted file mode 100644 index fe830a9d..00000000 --- a/CONFIG.md +++ /dev/null @@ -1,59 +0,0 @@ -# SolarWindsAPM Gem Configuration - -## Environment Variables - -The following environment variables are detected by the solarwinds_apm gem and affect how the gem functions. - -### General - -Name | Description | Default ----- | ----------- | ------- -`SW_APM_ENABLED` | Enables/Disables the SolarWinds APM Ruby library | `true` -`SW_APM_SERVICE_KEY` | API token + service name combination, mandatory for metrics and traces to show in the dashboard | -`SW_APM_COLLECTOR` | Collector endpoint the library connects and exports data to. It should be defined using the format host:port. | apm.collector.cloud.solarwinds.com:443 -`SW_APM_TRUSTEDPATH` | 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. -`SW_APM_DEBUG_LEVEL` | Level at which log messages will be written to log file (0-6) | 3 -`SW_APM_AUTO_CONFIGURE` | By default the library is configured to work out-of-the-box with all automatic instrumentation libraries enabled. Set this to false to custom initialize the library with configuration options for instrumentation, see [In-code Configuration](#in-code-configuration) for details. | `true` -`SW_APM_TAG_SQL` | Enables injecting trace context into supported SQL statements | `false` - - -## In-code Configuration - -SolarWindsAPM allows the in-code configuration for setting the options of otel instrumentations. More information about what option can be configured, please consult [opentelemetry-ruby-contrib](https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation) pages. - -Below example sets Dalli otel instrumentation to disable (e.g. `{:enabled: false}`); and sets Rack otel instrumentation to have header1 and header2 to allowed request headers. More information about Rack's [allowed_request_headers](https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/instrumentation/rack/lib/opentelemetry/instrumentation/rack/instrumentation.rb#L23). - -Note: in order to set the customized configuration, please set `SW_APM_AUTO_CONFIGURE=false` -```ruby -require 'solarwinds_apm' -SolarWindsAPM::OTelConfig.initialize_with_config do |config| - config["OpenTelemetry::Instrumentation::Dalli"] = {:enabled: false} - config["OpenTelemetry::Instrumentation::Rack"] = {:allowed_request_headers: ['header1', 'header2']} -end -``` - -## SolarWindsAPM config file - -`SolarWindsAPM::Config` is a nested hash used by the solarwinds_apm gem to store preferences and switches. - -See [this Rails generator template file](https://github.com/solarwindscloud/swotel-ruby/blob/main/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb) for documentation on all of the supported values. - -### Transaction Filtering (`[:transaction_settings]`) - -Sample configuration: -```ruby -SolarWindsAPM::Config[:transaction_settings] = [ - { - extensions: %w[long_job], - tracing: :disabled - }, - { - regexp: '^.*\/long_job\/.*$', - opts: Regexp::IGNORECASE, - tracing: :disabled - }, - { - regexp: /batch/, - } -] -``` \ No newline at end of file diff --git a/CONFIGURATION.md b/CONFIGURATION.md new file mode 100644 index 00000000..4e7b7256 --- /dev/null +++ b/CONFIGURATION.md @@ -0,0 +1,124 @@ +# Configuration + +By default all applicable instrumentations are enabled. The only required configuration is the service key, so a minimal example to get started is: +```bash +export SW_APM_SERVICE_KEY= +ruby application.rb +``` + +Configuration can be set several ways, with the following precedence: + +`in-code > environmental variable > configuration file > default` + +## In-code Configuration + +Many OpenTelemetry instrumenter configurations can be set within the `SolarWindsAPM::OTelConfig.initialize_with_config` block, which overrides the same options set via environment variable or configuration file. Please consult the individual [instrumenter](https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation) README pages for the options available. + +**Important**: this feature is only enabled if auto-config is disabled via `SW_APM_AUTO_CONFIGURE=false`. + +Below is an example that disables Dalli instrumentation and sets the Rack instrumentation to capture certain headers as Span attributes: +```ruby +# note auto-configure must be disabled, e.g. +# export SW_APM_AUTO_CONFIGURE=false + +require 'solarwinds_apm' + +SolarWindsAPM::OTelConfig.initialize_with_config do |config| + config["OpenTelemetry::Instrumentation::Dalli"] = {:enabled => false} + config["OpenTelemetry::Instrumentation::Rack"] = {:allowed_request_headers => ['header1', 'header2']} +end +``` + +## Environment Variables + +Settings specific to `solarwinds_apm` are prefixed by `SW_APM_` and described in the [Reference](#reference) section. Standard OpenTelemetry environment variables that impact this library's functionality are noted below. + +### Exporter + +The default `solarwinds` exporter which communicates with the SolarWinds Observability backend is always configured. Additional exporters can be configured via the `OTEL_TRACES_EXPORTER` environment variable. For example, console exporter is part of standard installation and can be enabled via: + +```bash +export OTEL_TRACES_EXPORTER=console +``` + +Other exporters must first be installed and required before loading `solarwinds_apm`. For example, if dependencies are loaded by `Bundler.require`, add the OTLP exporter to the Gemfile: +```ruby +# application dependencies, eg +# gem "rails", "~> 7.0.5", ">= 7.0.5.1" + +gem "opentelemetry-exporter-otlp" + +# end of Gemfile +gem 'solarwinds_apm', '>=6.0.0' +``` + +And set the environment variable: +```bash +export OTEL_TRACES_EXPORTER=otlp +``` + +### Service Name + +By default the service name portion of the service key is used, e.g. `my-service` if the service key is `SW_APM_SERVICE_KEY=api-token:my-service`. If the `OTEL_SERVICE_NAME` or `OTEL_RESOURCE_ATTRIBUTES` environment variable is used to specify a service name, it will take precedence over the default. + +```bash +export SW_APM_SERVICE_KEY=:foo +export OTEL_SERVICE_NAME=bar + +# service name for instrumented app will be "bar" +ruby application.rb +``` + +## Configuration File + +On startup, the library looks for the configuration file in the following locations under the application's current working directory: + +* `config/initializers/solarwinds_apm.rb` for Rails applications, which can be created by running the provided generator: + ```bash + bundle exec rails generate solarwinds_apm:install + ``` +* `solarwinds_apm_config.rb` for non-Rails applications + +The default location can be overridden via environment variable `SW_APM_CONFIG_RUBY`: +```bash +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/solarwindscloud/swotel-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 +-------------------- | --------------- | ----------- | ------- +`SW_APM_AUTO_CONFIGURE` | N/A | By default the library is configured to work out-of-the-box with all automatic instrumentation libraries enabled. Set this to false to custom initialize the library with configuration options for instrumentation, see [In-code Configuration](#in-code-configuration) for details. | true +`SW_APM_COLLECTOR` | N/A | Override the default collector endpoint to which the library connects and exports data. It should be defined using the format host:port. | apm.collector.cloud.solarwinds.com:443 +`SW_APM_CONFIG_RUBY` | N/A | Override the default location for the configuration file. This can be an absolute or relative filename, or the directory under which the `solarwinds_apm_config.rb` file would be looked for. | None +`SW_APM_DEBUG_LEVEL` | `:debug_level` | Set the library's logging level, valid values are 0 through 6 (least to most verbose). | 3 +`SW_APM_EC2_METADATA_TIMEOUT` | `:ec2_metadata_timeout` | Timeout for AWS IMDS metadata retrieval in milliseconds. | 1000 +`SW_APM_ENABLED` | N/A | Enable/disable the library, setting `false` is an alternative to uninstalling `solarwinds_apm` since it will prevent the library from loading. | true +`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 + service name combination, **required**. | +`SW_APM_TAG_SQL` | `:tag_sql` | Enable/disable injecting trace context into supported SQL statements. | false +`SW_APM_TRIGGER_TRACING_MODE` | `:trigger_tracing_mode` | | enable +`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 `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 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 98c1c3eb..8309c63d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -90,7 +90,7 @@ bundle install ### Building the Gem -The gem can be built, installed, and ran inside the development container: +The gem can be built, installed, and run inside the development container: ```bash # build the gem bundle exec rake build_gem @@ -102,6 +102,24 @@ gem install builds/solarwinds_apm-.gem SW_APM_SERVICE_KEY= irb -r solarwinds_apm ``` +#### Compiling the C Extension + +During install, the gem compiles a C extension called oboe which provides core functionality such as sampling and data transmission. When loading the gem from local source for development, the extension needs to be explicitly compiled: +```bash +bundle exec rake clean +bundle exec rake fetch +bundle exec rake compile + +# or use the short version that does it all +bundle exec rake cfc +``` + +Now loading the gem from local source should work: +```bash +SW_APM_SERVICE_KEY= bundle exec irb -r solarwinds_apm +``` + + ### Linting Use this Rake task to run rubocop inside the development container: diff --git a/README.md b/README.md index b0aa26a0..4c3c0b64 100644 --- a/README.md +++ b/README.md @@ -1,132 +1,107 @@ -# swotel-ruby -An [OpenTelemetry Ruby](https://opentelemetry.io/docs/instrumentation/ruby/) distribution for SolarWinds Observability. Provides automatic configuration, instrumentation, and APM data export for Ruby applications. +# solarwinds_apm +The `solarwinds_apm` gem starting from version 6.0.0 is an [OpenTelemetry Ruby](https://opentelemetry.io/docs/instrumentation/ruby/) distribution. It provides automatic instrumentation and custom SolarWinds Observability features for Ruby applications. ----- ## Requirements -All published artifacts support Ruby 2.7 or higher. A full list of system requirements is available at [SolarWinds Observability System Requirements](https://documentation.solarwinds.com/en/success_center/observability/content/configure/services/ruby/install.htm). +Only Linux is supported, the gem will go into no-op mode on other platforms. MRI Ruby version 3 or above is required. The [SolarWinds Observability documentation website](https://documentation.solarwinds.com/en/success_center/observability/content/configure/services/ruby/install.htm) has details on the supported platforms and system dependencies. See [CONTRIBUTING.md](CONTRIBUTING.md) for how to build for development. -## Getting Started - -The `solarwinds_apm` gem is hosted on RubyGems. To install, run `gem install solarwinds_apm` or add `gem 'solarwinds_apm'` at the end of your Gemfile if the application manages gems using Bundler. - -Ideally all application gems are required by `Bundler.require`, which guarantees loading in the order they appear in the Gemfile. If `Bundler.require` does not require all application gems, call `require 'solarwinds_apm'` after all gems that need instrumentation are loaded. - -Set the service key and ingestion endpoint. An easy way to do this is via environment variables available to your application process. An example: +## Installation and Setup +`solarwinds_apm` is [available on Rubygems](https://rubygems.org/gems/solarwinds_apm). Install with: ```bash -export SW_APM_SERVICE_KEY= -export SW_APM_COLLECTOR= +gem install solarwinds_apm -v '>=6.0.0' ``` -## Configuration - -`solarwinds_apm` is configured to work out-of-the-box for SolarWinds Observability with all automatic instrumentation libraries enabled. The service key is the only required configuration; all other configurations are optional. +Or add to **the end** of your application Gemfile and run `bundle install` if managing gems with Bundler: -Options to control the Ruby Library behavior can be set several ways, with the following precedence: - -`in-code configure > environmental variable > config file > default` - -### In-code Configuration - -Additional configuration can be set within the `SolarWindsAPM::OTelConfig.initialize_with_config` block, this will overwrite the same options set via environment variable or configuration file. - -An example that disables the Dalli instrumentation and sets the Rack instrumentation to capture certain headers as Span attributes. ```ruby -require 'solarwinds_apm' +# application dependencies, eg +# gem "rails", "~> 7.0.5", ">= 7.0.5.1" -SolarWindsAPM::OTelConfig.initialize_with_config do |config| - config["OpenTelemetry::Instrumentation::Dalli"] = {:enabled => false} - config["OpenTelemetry::Instrumentation::Rack"] = {:allowed_request_headers => ['header1', 'header2']} -end +# end of Gemfile +gem 'solarwinds_apm', '>=6.0.0' ``` -### Environmental Variable +Ideally all application gems are required by `Bundler.require`, which guarantees loading in the order they appear in the Gemfile. If `Bundler.require` does not require all application gems, call `require 'solarwinds_apm'` after all gems that need instrumentation are loaded. -More environmental variable can be found in [CONFIG.md](https://github.com/solarwindscloud/swotel-ruby/blob/main/CONFIG.md) +The only required configuration is the service key, which can be set in the `SW_APM_SERVICE_KEY` environment variable or in the configuration file as `:service_key`. See [CONFIGURATION.md](CONFIGURATION.md) for the complete reference. -#### OTEL_TRACES_EXPORTER +## Custom Instrumentation -Used to define the exporter +`solarwinds_apm` supports the standard OpenTelemetry API for tracing and includes a helper to ease its use in manual instrumentation. Additionally, a set of SolarWindsAPM APIs are provided for features specific to SolarWinds Observability. -Supported exporters: solarwinds +### Using the OpenTelemetry API -Example: -```bash -export OTEL_TRACES_EXPORTER=otlp -``` +This gem installs the dependencies needed to use the OTel API and initializes the globally-registered `TracerProvider`. So the "Setup" and "Acquiring a Tracer" sections of the [OTel Ruby Manual Instrumentation](https://opentelemetry.io/docs/instrumentation/ruby/manual/) should be skipped. Instead, your application code should acquire a `Tracer` from the global `TracerProvider` as follows. -#### OTEL_PROPAGATORS +The `Tracer` object is determined by the service name, which is the portion after the colon (`:`) set in the `SW_APM_SERVICE_KEY` or `:service_key` configuration. The service name is also automatically set into the `OTEL_SERVICE_NAME` environment variable which can be referenced as shown below. The `Tracer` object can then be used as described in the OTel Ruby documentation. -Used to define list of propagators +The example below shows how the standard OTel API to [create a span](https://opentelemetry.io/docs/instrumentation/ruby/manual/#creating-new-spans) and [get the current span](https://opentelemetry.io/docs/instrumentation/ruby/manual/#get-the-current-span) can be used in an application where `solarwinds_apm` has been loaded. See also the convenience [wrapper for in_span provided by the SolarWindsAPM API](#convenience-method-for-in_span): -Supported propagators can be found [here](https://github.com/open-telemetry/opentelemetry-ruby/blob/main/sdk/lib/opentelemetry/sdk/configurator.rb#L199-L208) - -```bash -export OTEL_PROPAGATORS=tracecontext,baggage -``` - -#### OTEL_SERVICE_NAME - -```bash -export OTEL_SERVICE_NAME=your_service_name -``` +```ruby +# acquire the tracer +MyAppTracer = ::OpenTelemetry.tracer_provider.tracer(ENV['OTEL_SERVICE_NAME']) -#### SW_APM_CONFIG_RUBY +# create a new span +MyAppTracer.in_span('new.span', attributes: {'key1' => 'value1', 'key2' => 'value2'}) do |span| + # do things +end -```bash -export SW_APM_CONFIG_RUBY=config/file/location.rb +# work with the current span +current_span = ::OpenTelemetry::Trace.current_span +# current_span.add_attributes +# current_span.add_event +# current_span.record_exception ``` -#### SW_APM_SERVICE_KEY - -```bash -export SW_APM_SERVICE_KEY=: -``` +Note that if `OpenTelemetry::SDK.configure` is used to set up a `TracerProvider`, it will not be configured with our distribution's customizations and manual instrumentation made with its `Tracer` object will not be reported to SolarWinds Observability. +### Using the SolarWindsAPM API -### Configuration Files +Several convenience and vendor-specific APIs are availabe to an application where `solarwinds_apm` has been loaded, below is a quick overview of the features provided. The full reference can be found at the [RubyDoc page for this gem](https://www.rubydoc.info/gems/solarwinds_apm). -The configuration file can be in one of the following locations +#### Convenience Method for in_span -For rails application: config/initializers/solarwinds_apm.rb +This method acquires the correct `Tracer` so a new span can be created in a single call, below is a simple Rails controller example: -For non-rails application: solarwinds_apm_config.rb +```ruby +class StaticController < ApplicationController + def home + SolarWindsAPM::API.in_span('custom_span') do |span| + # do things + end + end +end +``` -Sample configuration file can be found [here](https://github.com/solarwindscloud/swotel-ruby/blob/main/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb) +#### Get Curent Trace Context Information +The `current_trace_info` method returns a `TraceInfo` object containing string representations of the current trace context that can be used in logging or manual propagation of context. This is a convenience method that wraps the OTel API `::OpenTelemetry::Trace.current_span`. -Also can be defined with environmental variable `SW_APM_CONFIG_RUBY` +```ruby +trace = SolarWindsAPM::API.current_trace_info -```bash -export SW_APM_CONFIG_RUBY=config/file/location.rb +trace.tracestring # 00-7435a9fe510ae4533414d425dadf4e18-49e60702469db05f-01 +trace.trace_id # 7435a9fe510ae4533414d425dadf4e18 +trace.span_id # 49e60702469db05f +trace.trace_flags # 01 ``` -### Default configuration +#### Check if solarwinds_apm Is Ready -#### Propagators +On startup, this library initializes and maintains a connection to a SolarWinds Observability collector, and receives settings used for making tracing decisions. This process can take up to a few seconds depending on the connection. If the application receives requests before initialization has completed, these requests will not be traced. While this is not critical for long-running server processes, it might be a problem for short-running apps such as cron jobs or CLI apps. -Default propagators are tracecontext, baggage, solarwinds +A call to the `solarwinds_ready` method allows the application to block until initialization has completed and the library is ready for tracing. The method accepts an optional timeout parameter in milliseconds. -#### Exporter +```ruby +SolarWindsAPM::API.solarwinds_ready(wait_milliseconds=3000) +``` -Default exporter is solarwinds +#### Set a Custom Transaction Name -## Oboe -Oboe is the c-library that provides the methods to send data to -the collector. -When using the gem from source it needs to be installed once on a -new platform: -```bash -bundle exec rake clean -bundle exec rake fetch -bundle exec rake compile -``` -or use the short version that does it all -```bash -bundle exec rake cfc -``` -If the ruby version changes it needs to be re-compiled -(Don't worry about segfaults, some background job may have been running) +By default, transaction names are constructed based on attributes such as the requested route in the server framework, or the span name. If this name is not descriptive enough, you can override it with a custom one. If multiple transaction names are set on the same trace, the last transaction name is used. +```ruby +result = SolarWindsAPM::API.set_transaction_name('my-custom-trace-name') +``` \ No newline at end of file diff --git a/Rakefile b/Rakefile index 8b297ffd..b2715851 100755 --- a/Rakefile +++ b/Rakefile @@ -155,12 +155,15 @@ task :fetch_oboe_file, [:env] do |_t, args| end # remove all oboe* files, they may hang around because of name changes - Dir.glob(File.join(ext_src_dir, 'oboe*')).each { |file| File.delete(file) } + Dir.glob(File.join(ext_src_dir, 'oboe*')).each do |file| + puts "deleting #{file}" + File.delete(file) + end # oboe and bson header files FileUtils.mkdir_p(File.join(ext_src_dir, 'bson')) - files = %w[bson/bson.h bson/platform_hacks.h] - files += ['oboe.h', 'oboe_api.h', 'oboe_api.cpp', 'oboe_debug.h', 'oboe.i'] + files = %w[bson/bson.h bson/platform_hacks.h + oboe.h oboe_api.h oboe_api.cpp oboe_debug.h oboe.i] files.each do |filename| remote_file = File.join(oboe_dir, 'include', filename) @@ -198,7 +201,7 @@ task :fetch_oboe_file, [:env] do |_t, args| end FileUtils.cd(ext_src_dir) do - system('swig -c++ -ruby -module oboe_metal -o oboe_swig_wrap.cc oboe.i') + sh 'swig -c++ -ruby -module oboe_metal -o oboe_swig_wrap.cc oboe.i' FileUtils.rm('oboe.i') if args["env"] != "prod" end @@ -247,12 +250,7 @@ task :compile do so_file = File.expand_path('ext/oboe_metal/libsolarwinds_apm.so') Dir.chdir ext_dir - if ENV['OBOE_LOCAL'] - cmd = [Gem.ruby, 'extconf_local.rb'] - else - cmd = [Gem.ruby, 'extconf.rb'] - end - sh cmd.join(' ') + sh "#{Gem.ruby} extconf.rb" sh '/usr/bin/env make' File.delete(final_so) if File.exist?(final_so) diff --git a/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb b/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb index ed87b7bb..d785202f 100644 --- a/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +++ b/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb @@ -33,7 +33,8 @@ # SolarWindsAPM::Config[:hostname_alias] = 'alias_name' # - # Set Proxy for SolarWinds # This setting will be overridden if SW_APM_PROXY is set as an environment variable. + # Set Proxy for SolarWinds + # This setting will be overridden if SW_APM_PROXY is set as an environment variable. # # Please configure http_proxy if a proxy needs to be used to communicate with # the SolarWinds APM collector. diff --git a/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb b/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb index 37900760..8d25fcf0 100644 --- a/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +++ b/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb @@ -217,7 +217,7 @@ def create_xtraceoptions_response_value(liboboe_decision, parent_span_context, x # the sw.w3c.tracestate should perserve the old tracestate value for debugging purpose def calculate_attributes(attributes, liboboe_decision, trace_state, parent_span_context, xtraceoptions) SolarWindsAPM.logger.debug {"[#{self.class}/#{__method__}] new_trace_state: #{trace_state.inspect}"} - new_attributes = attributes.dup + new_attributes = attributes.dup || {} # Always (root or is_remote) set _INTERNAL_SW_KEYS if injected new_attributes[INTERNAL_SW_KEYS] = xtraceoptions.sw_keys if xtraceoptions.sw_keys diff --git a/lib/solarwinds_apm/otel_config.rb b/lib/solarwinds_apm/otel_config.rb index 7bdf9652..e9f6a2b0 100644 --- a/lib/solarwinds_apm/otel_config.rb +++ b/lib/solarwinds_apm/otel_config.rb @@ -130,7 +130,7 @@ def self.initialize print_config if SolarWindsAPM.logger.level.zero? - ENV['OTEL_TRACES_EXPORTER'] = 'none' if ENV['OTEL_TRACES_EXPORTER'].nil? + ENV['OTEL_TRACES_EXPORTER'] = 'none' if ENV['OTEL_TRACES_EXPORTER'].to_s.empty? ::OpenTelemetry::SDK.configure { |c| c.use_all(@@config_map) } diff --git a/lib/solarwinds_apm/version.rb b/lib/solarwinds_apm/version.rb index c30ae30a..7d91fe71 100644 --- a/lib/solarwinds_apm/version.rb +++ b/lib/solarwinds_apm/version.rb @@ -9,7 +9,7 @@ module Version MAJOR = 6 # breaking, MINOR = 0 # feature, PATCH = 0 # fix => BFF - PRE = 'preV2'.freeze # for pre-releases into packagecloud, set to nil for production releases into rubygems + PRE = 'preV4'.freeze # for pre-releases into packagecloud, set to nil for production releases into rubygems STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.') end diff --git a/solarwinds_apm.gemspec b/solarwinds_apm.gemspec index f880d841..b848cec5 100644 --- a/solarwinds_apm.gemspec +++ b/solarwinds_apm.gemspec @@ -40,8 +40,8 @@ Gem::Specification.new do |s| s.extensions = ['ext/oboe_metal/extconf.rb'] # OTEL dependencies - s.add_dependency('opentelemetry-sdk', '>= 1.2.1') - s.add_dependency('opentelemetry-instrumentation-all', '>= 0.33.0') + s.add_dependency('opentelemetry-sdk', '>= 1.2.0') + s.add_dependency('opentelemetry-instrumentation-all', '>= 0.31.0') # this still gives a warning, would have to be pinned to a minor version # but that is not necessary and may restrict other gems diff --git a/test/Dockerfile b/test/Dockerfile index e86887e8..c00738c1 100644 --- a/test/Dockerfile +++ b/test/Dockerfile @@ -32,6 +32,7 @@ RUN apt-get update \ libsasl2-dev \ libsqlite3-dev \ libssl-dev \ + libyaml-dev \ pkg-config \ psmisc \ ruby \ @@ -41,7 +42,6 @@ RUN apt-get update \ vim \ zlib1g-dev \ shared-mime-info \ - libyaml-dev \ && rm -rf /var/lib/apt/lists/* # rbenv setup diff --git a/test/api/opentelemetry_inspan_test.rb b/test/api/opentelemetry_inspan_test.rb index 581e6824..27ca21ad 100644 --- a/test/api/opentelemetry_inspan_test.rb +++ b/test/api/opentelemetry_inspan_test.rb @@ -29,4 +29,24 @@ finished_spans = @in_memory_exporter.finished_spans _(finished_spans.first.name).must_equal 'custom_span' end + + it 'test_in_span_wrapper_from_solarwinds_apm_with_span' do + SolarWindsAPM::API.in_span('custom_span') do |span| + span.add_attributes({"test_attribute" => "attribute_1"}) + @op.call + end + + sleep 5 # give it some time to fetch from memory + + finished_spans = @in_memory_exporter.finished_spans + _(finished_spans.first.name).must_equal 'custom_span' + _(finished_spans.first.attributes['test_attribute']).must_equal 'attribute_1' + end + + it 'test_in_span_wrapper_from_solarwinds_apm_without_block' do + SolarWindsAPM::API.in_span('custom_span') + + finished_spans = @in_memory_exporter.finished_spans + _(finished_spans.size).must_equal 0 + end end diff --git a/test/opentelemetry/solarwinds_sampler_test.rb b/test/opentelemetry/solarwinds_sampler_test.rb index 2d06a66a..f86eda9b 100644 --- a/test/opentelemetry/solarwinds_sampler_test.rb +++ b/test/opentelemetry/solarwinds_sampler_test.rb @@ -68,7 +68,7 @@ tracestate = ::OpenTelemetry::Trace::Tracestate.from_hash(content) parent_context = ::OpenTelemetry::Trace::SpanContext.new(span_id: "k1\xBF6\xB7k\xA7\x8B", trace_id: "H\x86\xC9\xC2\x16\xB2\xAA \xCE0@g\x81\xA1=P", tracestate: tracestate) trace_state = @sampler.send(:calculate_trace_state, @decision, parent_context, @xtraceoptions) - puts "trace_state #{trace_state.inspect}" + _(trace_state.to_h.keys.size).must_equal 3 _(trace_state.value("sw")).must_equal "6b31bf36b76ba78b-00" _(trace_state.value("abc")).must_equal "cba" diff --git a/test/solarwinds_apm/otel_config_exporter_test.rb b/test/solarwinds_apm/otel_config_exporter_test.rb index c6c2b8aa..107bb21b 100644 --- a/test/solarwinds_apm/otel_config_exporter_test.rb +++ b/test/solarwinds_apm/otel_config_exporter_test.rb @@ -24,9 +24,11 @@ SolarWindsAPM::OTelConfig.initialize _(SolarWindsAPM::OTelConfig.class_variable_get(:@@agent_enabled)).must_equal true - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors).count).must_equal 1 - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors)[0].class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsProcessor - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors)[0].instance_variable_get(:@exporter).class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsExporter + + span_processors = ::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors) + _(span_processors.count).must_equal 1 + _(span_processors[0].class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsProcessor + _(span_processors[0].instance_variable_get(:@exporter).class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsExporter end it 'test_exporter_with_default_otlp_exporter' do @@ -34,11 +36,12 @@ ENV['OTEL_TRACES_EXPORTER'] = 'otlp' SolarWindsAPM::OTelConfig.initialize _(SolarWindsAPM::OTelConfig.class_variable_get(:@@agent_enabled)).must_equal true - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors).count).must_equal 2 - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors)[0].class).must_equal ::OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors)[0].instance_variable_get(:@exporter).class).must_equal OpenTelemetry::Exporter::OTLP::Exporter - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors)[1].class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsProcessor - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors)[1].instance_variable_get(:@exporter).class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsExporter + span_processors = ::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors) + _(span_processors.count).must_equal 2 + _(span_processors[0].class).must_equal ::OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor + _(span_processors[0].instance_variable_get(:@exporter).class).must_equal OpenTelemetry::Exporter::OTLP::Exporter + _(span_processors[1].class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsProcessor + _(span_processors[1].instance_variable_get(:@exporter).class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsExporter end it 'test_exporter_with_bad_exporter' do @@ -46,9 +49,39 @@ ENV['OTEL_TRACES_EXPORTER'] = 'abcd' SolarWindsAPM::OTelConfig.initialize _(SolarWindsAPM::OTelConfig.class_variable_get(:@@agent_enabled)).must_equal true - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors).count).must_equal 1 - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors)[0].class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsProcessor - _(::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors)[0].instance_variable_get(:@exporter).class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsExporter + + span_processors = ::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors) + _(span_processors.count).must_equal 1 + _(span_processors[0].class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsProcessor + _(span_processors[0].instance_variable_get(:@exporter).class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsExporter + end + + it 'test_exporter_with_zipkin_jaeger_exporter' do + + ENV['OTEL_TRACES_EXPORTER'] = 'otlp,console' + SolarWindsAPM::OTelConfig.initialize + _(SolarWindsAPM::OTelConfig.class_variable_get(:@@agent_enabled)).must_equal true + + span_processors = ::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors) + _(span_processors.count).must_equal 3 + _(span_processors[0].class).must_equal ::OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor + _(span_processors[0].instance_variable_get(:@exporter).class).must_equal OpenTelemetry::Exporter::OTLP::Exporter + _(span_processors[1].class).must_equal ::OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor + _(span_processors[1].instance_variable_get(:@span_exporter).class).must_equal ::OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter + _(span_processors[2].class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsProcessor + _(span_processors[2].instance_variable_get(:@exporter).class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsExporter + end + + it 'test_exporter_with_empty_OTEL_TRACES_EXPORTER' do + + ENV['OTEL_TRACES_EXPORTER'] = '' + SolarWindsAPM::OTelConfig.initialize + _(SolarWindsAPM::OTelConfig.class_variable_get(:@@agent_enabled)).must_equal true + + span_processors = ::OpenTelemetry.tracer_provider.instance_variable_get(:@span_processors) + _(span_processors.count).must_equal 1 + _(span_processors[0].class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsProcessor + _(span_processors[0].instance_variable_get(:@exporter).class).must_equal SolarWindsAPM::OpenTelemetry::SolarWindsExporter end end