diff --git a/.github/actions/simplecov-report/action.yml b/.github/actions/simplecov-report/action.yml index 508ac1ed18..e44b794a7c 100644 --- a/.github/actions/simplecov-report/action.yml +++ b/.github/actions/simplecov-report/action.yml @@ -7,11 +7,11 @@ branding: inputs: failedThreshold: description: Failed threshold (line) - default: "94.08" + default: "93.5" required: false failedThresholdBranch: description: Failed threshold (branch) - default: "85.5" + default: "71.5" required: false resultPath: description: "json path" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6279c7313..55dd456241 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,9 @@ jobs: - name: Configure git run: 'git config --global init.defaultBranch main' - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 - - uses: ruby/setup-ruby@bc1dd263b68cb5626dbb55d5c89777d79372c484 # tag v1.151.0 + - uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # tag v1.165.1 with: - ruby-version: '3.2' + ruby-version: '3.3' - run: bundle - run: rubocop @@ -32,7 +32,7 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: [2.4.10, 3.2.2] + ruby-version: [2.4.10, 3.3.0] steps: - name: Configure git run: 'git config --global init.defaultBranch main' @@ -45,7 +45,7 @@ jobs: run: sudo apt-get update; sudo apt-get install -y --no-install-recommends libcurl4-nss-dev libsasl2-dev libxslt1-dev - name: Install Ruby ${{ matrix.ruby-version }} - uses: ruby/setup-ruby@bc1dd263b68cb5626dbb55d5c89777d79372c484 # tag v1.151.0 + uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # tag v1.165.1 with: ruby-version: ${{ matrix.ruby-version }} @@ -58,7 +58,7 @@ jobs: "2.4.10": { "rails": "norails,rails42,rails52" }, - "3.2.2": { + "3.3.0": { "rails": "norails,rails61,rails70" } } @@ -186,7 +186,7 @@ jobs: fail-fast: false matrix: multiverse: [agent, background, background_2, database, frameworks, httpclients, httpclients_2, rails, rest] - ruby-version: [2.4.10, 3.2.2] + ruby-version: [2.4.10, 3.3.0] steps: - name: Configure git @@ -200,7 +200,7 @@ jobs: run: sudo apt-get update; sudo apt-get install -y --no-install-recommends libcurl4-nss-dev libsasl2-dev libxslt1-dev - name: Install Ruby ${{ matrix.ruby-version }} - uses: ruby/setup-ruby@bc1dd263b68cb5626dbb55d5c89777d79372c484 # tag v1.151.0 + uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # tag v1.165.1 with: ruby-version: ${{ matrix.ruby-version }} @@ -286,14 +286,14 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: [2.5.9, 3.2.2] + ruby-version: [2.7.8, 3.3.0] steps: - name: Configure git run: 'git config --global init.defaultBranch main' - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 - name: Install Ruby ${{ matrix.ruby-version }} - uses: ruby/setup-ruby@bc1dd263b68cb5626dbb55d5c89777d79372c484 # tag v1.151.0 + uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # tag v1.165.1 with: ruby-version: ${{ matrix.ruby-version }} @@ -333,7 +333,7 @@ jobs: - name: Configure git run: 'git config --global init.defaultBranch main' - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 - - uses: ruby/setup-ruby@bc1dd263b68cb5626dbb55d5c89777d79372c484 # tag v1.151.0 + - uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # tag v1.165.1 with: ruby-version: '3.1' - run: bundle @@ -352,5 +352,5 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} resultPath: lib/coverage_results/.last_run.json - failedThreshold: 94.08 - failedThresholdBranch: 85.5 + failedThreshold: 93.5 + failedThresholdBranch: 71.5 diff --git a/.github/workflows/ci_cron.yml b/.github/workflows/ci_cron.yml index 5329d2588e..d83bdf8388 100644 --- a/.github/workflows/ci_cron.yml +++ b/.github/workflows/ci_cron.yml @@ -16,7 +16,7 @@ jobs: - name: Configure git run: 'git config --global init.defaultBranch main' - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 - - uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # tag v1.153.0 + - uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # tag v1.165.1 with: ruby-version: '3.2' - run: bundle @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: [2.4.10, 2.5.9, 2.6.10, 2.7.8, 3.0.6, 3.1.4, 3.2.2, 3.3.0-preview2] + ruby-version: [2.4.10, 2.5.9, 2.6.10, 2.7.8, 3.0.6, 3.1.4, 3.2.2, 3.3.0] steps: - name: Configure git @@ -50,7 +50,7 @@ jobs: run: sudo apt-get update; sudo apt-get install -y --no-install-recommends libcurl4-nss-dev libsasl2-dev libxslt1-dev - name: Install Ruby ${{ matrix.ruby-version }} - uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # tag v1.153.0 + uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # tag v1.165.1 with: ruby-version: ${{ matrix.ruby-version }} @@ -70,10 +70,10 @@ jobs: "rails": "norails,rails61,rails60,rails52,rails51,rails50,rails42" }, "2.7.8": { - "rails": "norails,rails61,rails60,rails70,railsedge" + "rails": "norails,rails61,rails60,rails70" }, "3.0.6": { - "rails": "norails,rails61,rails60,rails70,railsedge" + "rails": "norails,rails61,rails60,rails70" }, "3.1.4": { "rails": "norails,rails61,rails70,railsedge" @@ -81,7 +81,7 @@ jobs: "3.2.2": { "rails": "norails,rails61,rails70,railsedge" }, - "3.3.0-preview2": { + "3.3.0": { "rails": "norails,rails61,rails70,railsedge" } } @@ -200,7 +200,7 @@ jobs: fail-fast: false matrix: multiverse: [agent, background, background_2, database, frameworks, httpclients, httpclients_2, rails, rest] - ruby-version: [2.4.10, 2.5.9, 2.6.10, 2.7.8, 3.0.6, 3.1.4, 3.2.2, 3.3.0-preview2] + ruby-version: [2.4.10, 2.5.9, 2.6.10, 2.7.8, 3.0.6, 3.1.4, 3.2.2, 3.3.0] steps: - name: Configure git run: 'git config --global init.defaultBranch main' @@ -213,7 +213,7 @@ jobs: run: sudo apt-get update; sudo apt-get install -y --no-install-recommends libcurl4-nss-dev libsasl2-dev libxslt1-dev - name: Install Ruby ${{ matrix.ruby-version }} - uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # tag v1.153.0 + uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # tag v1.165.1 with: ruby-version: ${{ matrix.ruby-version }} @@ -278,14 +278,14 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: [2.5.9, 2.6.10, 2.7.8, 3.0.6, 3.1.4, 3.2.2, 3.3.0-preview2] + ruby-version: [2.7.8, 3.0.6, 3.1.4, 3.2.2, 3.3.0] steps: - name: Configure git run: 'git config --global init.defaultBranch main' - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 - name: Install Ruby ${{ matrix.ruby-version }} - uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # tag v1.153.0 + uses: ruby/setup-ruby@360dc864d5da99d54fcb8e9148c14a84b90d3e88 # tag v1.165.1 with: ruby-version: ${{ matrix.ruby-version }} diff --git a/.github/workflows/ci_jruby.yml b/.github/workflows/ci_jruby.yml index 41acd268cb..1dd3cb5f3a 100644 --- a/.github/workflows/ci_jruby.yml +++ b/.github/workflows/ci_jruby.yml @@ -6,16 +6,8 @@ on: workflow_dispatch: jobs: - unit_tests: + jruby_unit_tests: runs-on: ubuntu-22.04 - services: - mysql: - image: mysql:5.7 - env: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 - ports: - - "3306:3306" strategy: fail-fast: false steps: @@ -24,9 +16,9 @@ jobs: - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 - name: Install JRuby - uses: ruby/setup-ruby@bc1dd263b68cb5626dbb55d5c89777d79372c484 # tag v1.151.0 + uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: - ruby-version: jruby-9.4.3.0 + ruby-version: jruby-9.4.5.0 - name: Bundle run: bundle install @@ -36,85 +28,19 @@ jobs: with: timeout_minutes: 30 max_attempts: 2 - command: bundle exec rake test:env[norails,rails61,rails60] + command: bundle exec rake test:env[norails,rails61] env: VERBOSE_TEST_OUTPUT: true - DB_PORT: ${{ job.services.mysql.ports[3306] }} - JRUBY_OPTS: --dev --debug - + JAVA_OPTS: --add-opens java.base/sun.nio.ch=org.jruby.dist --add-opens java.base/java.io=org.jruby.dist + JRUBY_OPTS: --dev jruby_multiverse: runs-on: ubuntu-22.04 - services: - elasticsearch7: - image: elasticsearch:7.16.2 - env: - discovery.type: single-node - ports: - - 9200:9200 - options: >- - --health-cmd "curl http://localhost:9200/_cluster/health" - --health-interval 10s - --health-timeout 5s - --health-retries 10 - elasticsearch8: - image: elasticsearch:8.4.2 - env: - discovery.type: single-node - xpack.security.enabled: false - ports: - - 9250:9200 - options: >- - --health-cmd "curl http://localhost:9200/_cluster/health" - --health-interval 10s - --health-timeout 5s - --health-retries 10 - memcached: - image: memcached:latest - ports: - - 11211:11211 - options: >- - --health-cmd "timeout 5 bash -c 'cat < /dev/null > /dev/udp/127.0.0.1/11211'" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - mongodb: - image: mongo:5.0.11 - ports: - - 27017:27017 - mysql: - image: mysql:5.7 - env: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 - ports: - - "3306:3306" - postgres: - image: postgres:latest - ports: - - 5432:5432 - rabbitmq: - image: rabbitmq:latest - ports: - - 5672:5672 - options: >- - --health-cmd "rabbitmq-diagnostics -q check_port_connectivity" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - redis: - image: redis - ports: - - 6379:6379 - options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 strategy: fail-fast: false matrix: - multiverse: [agent, background, background_2, database, frameworks, httpclients, httpclients_2, rails, rest] + suite: [active_support_broadcast_logger, active_support_logger, activemerchant, agent_only, async_http, bare, deferred_instrumentation, grape, high_security, httpclient, httprb, httpx, json, logger, marshalling, rack, resque, roda, roda_agent_disabled, sequel, sinatra, sinatra_agent_disabled, stripe, thread, tilt, typhoeus] + steps: - name: Configure git run: 'git config --global init.defaultBranch main' @@ -123,22 +49,24 @@ jobs: uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 - name: Install JRuby - uses: ruby/setup-ruby@bc1dd263b68cb5626dbb55d5c89777d79372c484 # tag v1.151.0 + uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: - ruby-version: jruby-9.4.3.0 + ruby-version: jruby-9.4.5.0 - name: Bundle run: bundle install - name: Run Multiverse Tests - run: ./.github/workflows/scripts/retry_command - env: - TEST_CMD: "bundle exec rake test:multiverse[group=${{ matrix.multiverse }}]" + uses: nick-fields/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd # tag v2.8.3 + with: + timeout_minutes: 20 + max_attempts: 3 + command: "bundle exec rake test:multiverse[${{ matrix.suite }}]" + env: VERBOSE_TEST_OUTPUT: true - RETRY_ATTEMPTS: 5 SERIALIZE: 1 - DB_PORT: ${{ job.services.mysql.ports[3306] }} - JRUBY_OPTS: --dev --debug + JAVA_OPTS: --add-opens java.base/sun.nio.ch=org.jruby.dist --add-opens java.base/java.io=org.jruby.dist -Xmx4g -Xms512m + JRUBY_OPTS: --dev - name: Annotate errors if: ${{ failure() }} diff --git a/.github/workflows/config_docs.yml b/.github/workflows/config_docs.yml index d0f4807efa..b7d849496c 100644 --- a/.github/workflows/config_docs.yml +++ b/.github/workflows/config_docs.yml @@ -15,7 +15,7 @@ jobs: pull-requests: write steps: - name: Install Ruby 3.2 - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # tag v1.149.0 + uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: ruby-version: 3.2 diff --git a/.github/workflows/performance_tests.yml b/.github/workflows/performance_tests.yml index 7053d64d9f..a5afb937e1 100644 --- a/.github/workflows/performance_tests.yml +++ b/.github/workflows/performance_tests.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 with: ref: 'main' - - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # tag v1.149.0 + - uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: ruby-version: '3.2' - run: bundle diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index e7b4064dd6..f57fb82fe8 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -3,7 +3,7 @@ name: Create Prerelease on: workflow_dispatch: -jobs: +jobs: create_prerelease: runs-on: ubuntu-22.04 permissions: @@ -11,7 +11,7 @@ jobs: pull-requests: write steps: - name: Install Ruby 3.2 - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # tag v1.149.0 + uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: ruby-version: 3.2 @@ -43,7 +43,7 @@ jobs: TITLE: "Prerelease ${{env.prerelease_tag}}" BODY: "Updates the version number, changelog, and newrelic.yml (if it needs updating). This is an automated PR." LABEL: prerelease - + - name: Create pre release tag uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # tag v0.1.15 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bcee9924b8..ebf9555a9b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: with: fetch-depth: 0 - - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # tag v1.149.0 + - uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: ruby-version: 3.2 @@ -60,31 +60,3 @@ jobs: - name: Publish newrelic-infinite_tracing to rubygems.org run: ruby ./.github/workflows/scripts/rubygems-publish.rb infinite_tracing/newrelic-infinite_tracing - - - name: Update system configuration page - run: | - PAYLOAD="{ - \"system_configuration\": { - \"key\": \"ruby_agent_version\", - \"value\": \"${{ env.VERSION }}\" - } - }" - CONTENT_TYPE='Content-Type: application/json' - - # STAGING - curl -X POST 'https://staging-api.newrelic.com/v2/system_configuration.json' \ - -H "X-Api-Key:${{ secrets.NEW_RELIC_API_KEY_STAGING }}" -i \ - -H "$CONTENT_TYPE" \ - -d "$PAYLOAD" - - # PRODUCTION - curl -X POST 'https://api.newrelic.com/v2/system_configuration.json' \ - -H "X-Api-Key:${{ secrets.NEW_RELIC_API_KEY_PRODUCTION }}" -i \ - -H "$CONTENT_TYPE" \ - -d "$PAYLOAD" - - # EU PRODUCTION - curl -X POST 'https://api.eu.newrelic.com/v2/system_configuration.json' \ - -H "X-Api-Key:$ {{ secrets.NEW_RELIC_API_KEY_PRODUCTION }}" -i \ - -H "$CONTENT_TYPE" \ - -d "$PAYLOAD" diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml index 30df4096fa..0925dea0b5 100644 --- a/.github/workflows/release_notes.yml +++ b/.github/workflows/release_notes.yml @@ -12,7 +12,7 @@ jobs: contents: write pull-requests: write steps: - - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # tag v1.149.0 + - uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: ruby-version: 3.2 - name: Checkout code @@ -63,7 +63,7 @@ jobs: - name: Checkout docs website repository uses: actions/checkout@v3 - with: + with: repository: newrelic/docs-website token: ${{ secrets.NEWRELIC_RUBY_AGENT_BOT_TOKEN }} diff --git a/.github/workflows/release_pr.yml b/.github/workflows/release_pr.yml index 8837f167b4..8ee92d9de1 100644 --- a/.github/workflows/release_pr.yml +++ b/.github/workflows/release_pr.yml @@ -5,7 +5,7 @@ on: types: - closed -jobs: +jobs: create_prerelease: if: ${{ (github.event.pull_request.merged == true) && (contains(github.event.pull_request.labels.*.name, 'prerelease')) }} runs-on: ubuntu-22.04 @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - name: Install Ruby 3.2 - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # tag v1.149.0 + uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: ruby-version: 3.2 diff --git a/.github/workflows/scripts/retry_command b/.github/workflows/scripts/retry_command deleted file mode 100755 index aef5e9d348..0000000000 --- a/.github/workflows/scripts/retry_command +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# RETRY_ATTEMPTS -# TEST_CMD - -count=0 -return_val=1 - -while [[ $return_val != 0 && $count -lt $RETRY_ATTEMPTS ]]; do - $TEST_CMD - return_val=$? - count=$((count + 1)) - if [[ $return_val != 0 ]]; then - echo $'\n*************************************************************************************************************\n' - echo "FAILURE ON ATTEMPT $((count)) of $((RETRY_ATTEMPTS))" - echo $'\n*************************************************************************************************************\n' - fi -done - -exit $return_val \ No newline at end of file diff --git a/.github/workflows/slack_notifications.yml b/.github/workflows/slack_notifications.yml index 08fb311ac2..70e7bec459 100644 --- a/.github/workflows/slack_notifications.yml +++ b/.github/workflows/slack_notifications.yml @@ -8,17 +8,17 @@ jobs: gem_notifications: runs-on: ubuntu-22.04 steps: - - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # tag v1.149.0 + - uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: ruby-version: 3.2 - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 - run: gem install httparty - - name: Check for outdated gems + - name: Check for outdated gems run: ruby .github/workflows/scripts/slack_notifications/gem_notifier.rb ${{ env.gems }} env: SLACK_GEM_NOTIFICATIONS_WEBHOOK: ${{ secrets.SLACK_GEM_NOTIFICATIONS_WEBHOOK }} gems: - "activerecord + "activerecord bunny dalli delayed_job @@ -32,7 +32,7 @@ jobs: tilt rack rails - rake + rake redis resque unicorn" @@ -40,7 +40,7 @@ jobs: cve_notifications: runs-on: ubuntu-22.04 steps: - - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # tag v1.149.0 + - uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: ruby-version: 3.2 - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 07f602bd08..ab1b82ca40 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag v3.5.0 - name: Setup Ruby - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # tag v1.149.0 + uses: ruby/setup-ruby@af848b40be8bb463a751551a1180d74782ba8a72 # tag v1.162.0 with: ruby-version: 3.2 - name: Bundle diff --git a/CHANGELOG.md b/CHANGELOG.md index b3682487ee..89f91e94b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # New Relic Ruby Agent Release Notes +## v9.7.0 + + +Version 9.7.0 introduces ViewComponent instrumentation, changes the endpoint used to access the cluster name for Elasticsearch instrumentation, removes the creation of the Ruby/Thread and Ruby/Fiber spans, and adds support for Falcon. + +- **Feature: ViewComponent instrumentation** + + [ViewComponent](https://viewcomponent.org/) is a now an instrumented framework. The agent currently supports Roda versions 2.0.0+. [PR#2367](https://github.com/newrelic/newrelic-ruby-agent/pull/2367) + +- **Feature: Use root path to access Elasticsearch cluster name** + + Previously, the agent used the cluster health endpoint (`/_cluster/health`) to access the cluster name. However, this has been found to make startup unstable for large clusters. Now, the agent uses the more performant root endpoint (`/`). + + Our thanks go to [@erikkessler1](https://github.com/erikkessler1), [@gremerritt](https://github.com/gremerritt), and [@joshbranham](https://github.com/joshbranham) for reporting the issue, suggesting solutions, and testing them. [Issue#2360](https://github.com/newrelic/newrelic-ruby-agent/issues/2360) [PR#2377](https://github.com/newrelic/newrelic-ruby-agent/pull/2377) + +- **Feature: Remove base64 dependency, use direct calls to String methods** + + In version 9.6.0, the agent required the Ruby `base64` gem as a depdendency to prepare for deprecation warnings in Ruby 3.3 and the gem's removal from the Ruby standard libraries in 3.4. Including `base64` as a dependency has caused problems with version resolution in some environments. + + To resolve this, the agent now directly calls the `String` methods used in the `base64` library in the new `NewRelic::Base64` module. + + Thank you, [@Earlopain](https://github.com/Earlopain), for submitting this change. [PR#2378](https://github.com/newrelic/newrelic-ruby-agent/pull/2378) + +- **Feature: Add Falcon support** + + The agent now supports the web server [Falcon](https://socketry.github.io/falcon/). [PR#2383](https://github.com/newrelic/newrelic-ruby-agent/pull/2383) + +- **Feature: Remove spans with name Ruby/Thread and Ruby/Fiber** + + Due to the lack of helpful information and the confusion commonly caused by the spans named Ruby/Thread and Ruby/Fiber, these spans have been removed. However, the agents ability to monitor instrumented code running in a thread or fiber will remain unchanged. [PR#2389](https://github.com/newrelic/newrelic-ruby-agent/pull/2389) + ## v9.6.0 Version 9.6.0 adds instrumentation for Async::HTTP, Ethon, and HTTPX, adds the ability to ignore specific routes with Roda, gleans Docker container IDs from cgroups v2-based containers, records additional synthetics attributes, fixes an issue with Rails 7.1 that could cause duplicate log records to be sent to New Relic, fixes a deprecation warning for the Sidekiq error handler, adds additional attributes for OpenTelemetry compatibility, and resolves some technical debt, thanks to the community. @@ -69,7 +100,7 @@ Version 9.6.0 adds instrumentation for Async::HTTP, Ethon, and HTTPX, adds the a We also received some great contributions from community members to resolve some outstanding technical debt issues. Thank you for your contributions! * Add and Replace SLASH and ROOT constants: [PR#2256](https://github.com/newrelic/newrelic-ruby-agent/pull/2256) [chahmedejaz](https://github.com/chahmedejaz) - * Remove pry as a dev dependency: [PR#2665](https://github.com/newrelic/newrelic-ruby-agent/pull/2265), [PR#2273](https://github.com/newrelic/newrelic-ruby-agent/pull/2273) [AlajeBash](https://github.com/AlajeBash) + * Remove pry as a dev dependency: PR#2665, PR#2273, AlajeBash (profile no longer active) * Replace "start up" with "start-up": [PR#2249](https://github.com/newrelic/newrelic-ruby-agent/pull/2249) [chahmedejaz](https://github.com/chahmedejaz) * Remove unused variables in test suites: [PR#2250](https://github.com/newrelic/newrelic-ruby-agent/pull/2250) @@ -1465,7 +1496,7 @@ The multiverse collection of test suites requires a variety of data handling sof - **Additional Transaction Information applied to Span Events** - When Distributed Tracing and/or Infinite Tracing are enabled, the Agent will now incorporate additional information from the Transaction Event on to the root Span Event of the transaction. + When Distributed Tracing and/or Infinite Tracing are enabled, the agent will now incorporate additional information from the Transaction Event on to the root Span Event of the transaction. The following items are affected: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d8382b723..d7b8a905f3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -178,13 +178,6 @@ opensource@newrelic.com. For more information about CLAs, please check out Alex Russell’s excellent post, [“Why Do I Need to Sign This?”](https://infrequently.org/2008/06/why-do-i-need-to-sign-this/). -## Slack - -We host a public Slack with a dedicated channel for contributors and maintainers -of open source projects hosted by New Relic. If you are contributing to this -project, you're welcome to request access to the #oss-contributors channel in -the newrelicusers.slack.com workspace. To request access, please use this [link](https://join.slack.com/t/newrelicusers/shared_invite/zt-1ayj69rzm-~go~Eo1whIQGYnu3qi15ng). - ## Explorer's Hub New Relic hosts and moderates an online forum where customers can interact with diff --git a/DOCKER.md b/DOCKER.md index 6161fc97d1..b35a8345b1 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -1,8 +1,12 @@ -# Using Docker with the New Relic Ruby Agent +# Using Docker with the New Relic Ruby agent These instructions will guide you through the process of setting up Docker for -use with developing the New Relic Ruby Agent. The use of Docker containers can -provide for a consistent experience free from machine specific issues. +use with **developing** the New Relic Ruby agent. Using Docker containers +while developing the Ruby agent can provide a consistent experience free from +machine-specific issues. + +**Do not follow these instructions if you are trying to connect the agent to +an application in a containerized environment. Instead, visit [the docs][install-url]. ** ## Quick Start @@ -165,3 +169,6 @@ of entry when it comes to providing contributions to the agent project itself. For questions, feature requests, proposals to support Podman, PRs to improve behavior or documentation, etc., please see [CONTRIBUTING.md](CONTRIBUTING.md). + + +[install-docs]: https://docs.newrelic.com/docs/apm/agents/ruby-agent/installation/install-new-relic-ruby-agent/#install-the-gem-installing_the_gem diff --git a/bin/newrelic b/bin/newrelic index 78fc6850c0..1c4c103c0f 100755 --- a/bin/newrelic +++ b/bin/newrelic @@ -1,15 +1,8 @@ #!/usr/bin/env ruby # frozen_string_literal: true +# This command has been renamed "newrelic_rpm" # executes one of the commands in the new_relic/commands directory # pass the name of the command as an argument -$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) -require 'new_relic/cli/command' -begin - NewRelic::Cli::Command.run -rescue NewRelic::Cli::Command::CommandFailure => failure - STDERR.puts failure.message - STDERR.puts failure.options if failure.options - exit(1) -end +load File.dirname(__FILE__) + '/newrelic_rpm' diff --git a/bin/newrelic_cmd b/bin/newrelic_cmd deleted file mode 100755 index 5434cc3dfa..0000000000 --- a/bin/newrelic_cmd +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# This command has been renamed "newrelic" -# executes one of the commands in the new_relic/commands directory -# pass the name of the command as an argument -load File.dirname(__FILE__) + '/newrelic' diff --git a/bin/newrelic_rpm b/bin/newrelic_rpm new file mode 100755 index 0000000000..78fc6850c0 --- /dev/null +++ b/bin/newrelic_rpm @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# executes one of the commands in the new_relic/commands directory +# pass the name of the command as an argument + +$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) +require 'new_relic/cli/command' +begin + NewRelic::Cli::Command.run +rescue NewRelic::Cli::Command::CommandFailure => failure + STDERR.puts failure.message + STDERR.puts failure.options if failure.options + exit(1) +end diff --git a/infinite_tracing/lib/infinite_tracing/agent_integrations.rb b/infinite_tracing/lib/infinite_tracing/agent_integrations.rb index 3b00d5f1cc..8282e5d34f 100644 --- a/infinite_tracing/lib/infinite_tracing/agent_integrations.rb +++ b/infinite_tracing/lib/infinite_tracing/agent_integrations.rb @@ -5,7 +5,7 @@ module NewRelic::Agent module InfiniteTracing if Config.enabled? || Config.test_framework? - NewRelic::Agent.logger.debug('Integrating Infinite Tracer with Agent') + NewRelic::Agent.logger.debug('Integrating Infinite Tracer with agent') require_relative 'agent_integrations/agent' require_relative 'agent_integrations/segment' @@ -13,7 +13,7 @@ module InfiniteTracing require_relative 'agent_integrations/external_request_segment' else - NewRelic::Agent.logger.debug('Skipped Integrating Infinite Tracer with Agent') + NewRelic::Agent.logger.debug('Skipped Integrating Infinite Tracer with agent') end end end diff --git a/infinite_tracing/lib/infinite_tracing/connection.rb b/infinite_tracing/lib/infinite_tracing/connection.rb index 30c783b5d3..4c42aca1b6 100644 --- a/infinite_tracing/lib/infinite_tracing/connection.rb +++ b/infinite_tracing/lib/infinite_tracing/connection.rb @@ -94,7 +94,7 @@ def wait_for_agent_connect end end - # The metadata for the RPC calls is a blocking call waiting for the Agent to + # The metadata for the RPC calls is a blocking call waiting for the agent to # connect and receive the server side configuration, which contains the license_key # as well as the agent_id (agent_run_token). def metadata diff --git a/infinite_tracing/newrelic-infinite_tracing.gemspec b/infinite_tracing/newrelic-infinite_tracing.gemspec index 6e417bcad5..729cbef34a 100644 --- a/infinite_tracing/newrelic-infinite_tracing.gemspec +++ b/infinite_tracing/newrelic-infinite_tracing.gemspec @@ -45,7 +45,7 @@ Gem::Specification.new do |s| New Relic is a performance management system, developed by New Relic, Inc (http://www.newrelic.com). New Relic provides you with deep information about the performance of your web application as it runs - in production. The New Relic Ruby Agent is dual-purposed as a either a + in production. The New Relic Ruby agent is dual-purposed as a either a Gem or plugin, hosted on https://github.com/newrelic/newrelic-ruby-agent/ EOS diff --git a/infinite_tracing/test/infinite_tracing/agent_integrations/agent_test.rb b/infinite_tracing/test/infinite_tracing/agent_integrations/agent_test.rb index 1acd0701fc..8430438e2d 100644 --- a/infinite_tracing/test/infinite_tracing/agent_integrations/agent_test.rb +++ b/infinite_tracing/test/infinite_tracing/agent_integrations/agent_test.rb @@ -11,7 +11,7 @@ class AgentIntegrationTest < Minitest::Test include FakeTraceObserverHelpers def test_injects_infinite_tracer - assert ::NewRelic::Agent.instance, 'expected to get an Agent instance' + assert ::NewRelic::Agent.instance, 'expected to get an agent instance' assert ::NewRelic::Agent.instance.infinite_tracer end diff --git a/init.rb b/init.rb index 9c5ac9f02a..b03ab40c69 100644 --- a/init.rb +++ b/init.rb @@ -2,7 +2,7 @@ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. # frozen_string_literal: true -# This is the initialization for the New Relic Ruby Agent when used as +# This is the initialization for the New Relic Ruby agent when used as # a plugin require 'new_relic/control' @@ -12,7 +12,7 @@ # If you can't find any log files and you don't see anything in your # application log files please visit support.newrelic.com. -# Initializer for the NewRelic Ruby Agent +# Initializer for the New Relic Ruby agent # After version 2.0 of Rails we can access the configuration directly. # We need it to add dev mode routes after initialization finished. diff --git a/lib/new_relic/agent.rb b/lib/new_relic/agent.rb index c9fa9a74ea..cc93bdd18f 100644 --- a/lib/new_relic/agent.rb +++ b/lib/new_relic/agent.rb @@ -252,7 +252,7 @@ def increment_metric(metric_name, amount = 1) # THREAD_LOCAL_ACCESS # @!group Recording custom errors - # Set a filter to be applied to errors that the Ruby Agent will + # Set a filter to be applied to errors that the Ruby agent will # track. The block should evaluate to the exception to track # (which could be different from the original exception) or nil to # ignore this exception. @@ -391,13 +391,13 @@ def record_custom_event(event_type, event_attrs) # @!group Manual agent configuration and startup/shutdown - # Call this to manually start the Agent in situations where the Agent does + # Call this to manually start the agent in situations where the agent does # not auto-start. # - # When the app environment loads, so does the Agent. However, the - # Agent will only connect to the service if a web front-end is found. If + # When the app environment loads, so does the agent. However, the + # agent will only connect to the service if a web front-end is found. If # you want to selectively monitor ruby processes that don't use - # web plugins, then call this method in your code and the Agent + # web plugins, then call this method in your code and the agent # will fire up and start reporting to the service. # # Options are passed in as overrides for values in the diff --git a/lib/new_relic/agent/agent.rb b/lib/new_relic/agent/agent.rb index 0e5b947666..0b01f3caf6 100644 --- a/lib/new_relic/agent/agent.rb +++ b/lib/new_relic/agent/agent.rb @@ -47,7 +47,7 @@ module NewRelic module Agent - # The Agent is a singleton that is instantiated when the plugin is + # The agent is a singleton that is instantiated when the plugin is # activated. It collects performance data from ruby applications # in realtime as the application runs, and periodically sends that # data to the NewRelic server. diff --git a/lib/new_relic/agent/agent_helpers/shutdown.rb b/lib/new_relic/agent/agent_helpers/shutdown.rb index e6f6017e13..9bc36393bc 100644 --- a/lib/new_relic/agent/agent_helpers/shutdown.rb +++ b/lib/new_relic/agent/agent_helpers/shutdown.rb @@ -11,7 +11,7 @@ module Shutdown def shutdown return unless started? - ::NewRelic::Agent.logger.info('Starting Agent shutdown') + ::NewRelic::Agent.logger.info('Starting agent shutdown') stop_event_loop trap_signals_for_litespeed diff --git a/lib/new_relic/agent/agent_helpers/special_startup.rb b/lib/new_relic/agent/agent_helpers/special_startup.rb index 08b5d4cf33..351967636e 100644 --- a/lib/new_relic/agent/agent_helpers/special_startup.rb +++ b/lib/new_relic/agent/agent_helpers/special_startup.rb @@ -10,7 +10,7 @@ module SpecialStartup # requests, we need to wait until the children are forked # before connecting, otherwise the parent process sends useless data def using_forking_dispatcher? - if [:puma, :passenger, :unicorn].include?(Agent.config[:dispatcher]) + if [:puma, :passenger, :unicorn, :falcon].include?(Agent.config[:dispatcher]) ::NewRelic::Agent.logger.info('Deferring startup of agent reporting thread because ' \ "#{Agent.config[:dispatcher]} may fork.") true diff --git a/lib/new_relic/agent/agent_helpers/start_worker_thread.rb b/lib/new_relic/agent/agent_helpers/start_worker_thread.rb index d59f0d3229..1f0f530422 100644 --- a/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +++ b/lib/new_relic/agent/agent_helpers/start_worker_thread.rb @@ -19,12 +19,12 @@ module StartWorkerThread # See #connect for a description of connection_options. def start_worker_thread(connection_options = {}) if disable = NewRelic::Agent.config[:disable_harvest_thread] - NewRelic::Agent.logger.info('Not starting Ruby Agent worker thread because :disable_harvest_thread is ' \ + NewRelic::Agent.logger.info('Not starting Ruby agent worker thread because :disable_harvest_thread is ' \ "#{disable}") return end - ::NewRelic::Agent.logger.debug('Creating Ruby Agent worker thread.') + ::NewRelic::Agent.logger.debug('Creating Ruby agent worker thread.') @worker_thread = Threading::AgentThread.create('Worker Loop') do deferred_work!(connection_options) end diff --git a/lib/new_relic/agent/agent_helpers/startup.rb b/lib/new_relic/agent/agent_helpers/startup.rb index c71719a95d..8372476923 100644 --- a/lib/new_relic/agent/agent_helpers/startup.rb +++ b/lib/new_relic/agent/agent_helpers/startup.rb @@ -103,7 +103,7 @@ def log_ignore_url_regexes # so we can disambiguate processes in the log file and make # sure they're running a reasonable version def log_version_and_pid - ::NewRelic::Agent.logger.debug("New Relic Ruby Agent #{NewRelic::VERSION::STRING} Initialized: pid = #{$$}") + ::NewRelic::Agent.logger.debug("New Relic Ruby agent #{NewRelic::VERSION::STRING} initialized: pid = #{$$}") end # Logs the configured application names @@ -180,7 +180,7 @@ def agent_should_start? unless app_name_configured? NewRelic::Agent.logger.error('No application name configured.', - 'The Agent cannot start without at least one. Please check your ', + 'The agent cannot start without at least one. Please check your ', 'newrelic.yml and ensure that it is valid and has at least one ', "value set for app_name in the #{NewRelic::Control.instance.env} ", 'environment.') diff --git a/lib/new_relic/agent/attribute_filter.rb b/lib/new_relic/agent/attribute_filter.rb index bc082daba8..31f52c70f8 100644 --- a/lib/new_relic/agent/attribute_filter.rb +++ b/lib/new_relic/agent/attribute_filter.rb @@ -2,7 +2,7 @@ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. # frozen_string_literal: true -# This class applies filtering rules as specified in the Agent Attributes +# This class applies filtering rules as specified in the agent attributes # cross-agent spec. # # Instances of it are constructed by deriving a set of rules from the agent @@ -44,7 +44,7 @@ # 1. First, the names are compared lexicographically. This has the impact of # forcing shorter (more general) rules towards the top of the list and longer # (more specific) rules towards the bottom. This is important, because the -# Agent Attributes spec stipulates that the most specific rule for a given +# agent Attributes spec stipulates that the most specific rule for a given # destination should take precedence. Since rules are applied top-to-bottom, # this sorting guarantees that the most specific rule will be applied last. # 2. If the names are identical, we next examine the wildcard flag. Rules ending @@ -274,7 +274,7 @@ class AttributeFilterRule def initialize(attribute_name, destinations, is_include) @attribute_name = attribute_name.sub(/\*$/, '') - @wildcard = attribute_name.end_with?('*') + @wildcard = attribute_name.end_with?(ASTERISK) @is_include = is_include @destinations = is_include ? destinations : ~destinations end diff --git a/lib/new_relic/agent/configuration/default_source.rb b/lib/new_relic/agent/configuration/default_source.rb index 3066d54f82..807d2774f3 100644 --- a/lib/new_relic/agent/configuration/default_source.rb +++ b/lib/new_relic/agent/configuration/default_source.rb @@ -313,6 +313,7 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil) 'webpacker:compile' ].join(',').freeze + # rubocop:disable Metrics/CollectionLiteralLength DEFAULTS = { # Critical :agent_enabled => { @@ -336,6 +337,7 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil) :public => true, :type => String, :allowed_from_server => false, + :exclude_from_reported_settings => true, :description => 'Your New Relic .' }, :log_level => { @@ -444,7 +446,7 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil) 'before shutting down to be installed regardless of detecting scenarios where it generally should not be. ' \ 'Known use-case for this option is where Sinatra is running as an embedded service within another framework ' \ 'and the agent is detecting the Sinatra app and skipping the `at_exit` handler as a result. Sinatra classically ' \ - 'runs the entire application in an `at_exit` block and would otherwise misbehave if the Agent\'s `at_exit` handler ' \ + 'runs the entire application in an `at_exit` block and would otherwise misbehave if the agent\'s `at_exit` handler ' \ 'was also installed in those circumstances. Note: `send_data_on_exit` should also be set to `true` in tandem with this setting.' }, :high_security => { @@ -1656,6 +1658,14 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil) :allowed_from_server => false, :description => 'Controls auto-instrumentation of Stripe at startup. May be one of: `enabled`, `disabled`.' }, + :'instrumentation.view_component' => { + :default => 'auto', + :public => true, + :type => String, + :dynamic_name => true, + :allowed_from_server => false, + :description => 'Controls auto-instrumentation of ViewComponent at startup. May be one of: `auto`, `prepend`, `chain`, `disabled`.' + }, :'stripe.user_data.include' => { default: NewRelic::EMPTY_ARRAY, public: true, @@ -2227,7 +2237,8 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil) :public => false, :type => String, :allowed_from_server => true, - :description => 'JavaScript agent loader content.' + :description => 'JavaScript agent loader content.', + :exclude_from_reported_settings => true }, :keep_alive_timeout => { :default => 60, @@ -2403,6 +2414,7 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil) :description => 'This value represents the total amount of memory available to the host (not the process), in mebibytes (1024 squared or 1,048,576 bytes).' } }.freeze + # rubocop:enable Metrics/CollectionLiteralLength end end end diff --git a/lib/new_relic/agent/configuration/manager.rb b/lib/new_relic/agent/configuration/manager.rb index 66ffe29398..ddf66e3235 100644 --- a/lib/new_relic/agent/configuration/manager.rb +++ b/lib/new_relic/agent/configuration/manager.rb @@ -230,7 +230,7 @@ def apply_mask(hash) end def to_collector_hash - DottedHash.new(apply_mask(flattened)).to_hash.delete_if do |k, v| + DottedHash.new(apply_mask(flattened)).to_hash.delete_if do |k, _v| default = DEFAULTS[k] if default default[:exclude_from_reported_settings] @@ -364,7 +364,7 @@ def reset_to_defaults def reset_cache return new_cache unless defined?(@cache) && @cache - preserved = @cache.select { |_k, v| DEPENDENCY_DETECTION_VALUES.include?(v) } + preserved = @cache.dup.select { |_k, v| DEPENDENCY_DETECTION_VALUES.include?(v) } new_cache preserved.each { |k, v| @cache[k] = v } @@ -376,12 +376,13 @@ def new_cache end def log_config(direction, source) - # Just generating this log message (specifically calling - # flattened.inspect) is expensive enough that we don't want to do it - # unless we're actually going to be logging the message based on our - # current log level. + # Just generating this log message (specifically calling `flattened`) + # is expensive enough that we don't want to do it unless we're + # actually going to be logging the message based on our current log + # level, so use a `do` block. ::NewRelic::Agent.logger.debug do - "Updating config (#{direction}) from #{source.class}. Results: #{flattened.inspect}" + hash = flattened.delete_if { |k, _h| DEFAULTS.fetch(k, {}).fetch(:exclude_from_reported_settings, false) } + "Updating config (#{direction}) from #{source.class}. Results: #{hash.inspect}" end end diff --git a/lib/new_relic/agent/datastores/mongo/metric_translator.rb b/lib/new_relic/agent/datastores/mongo/metric_translator.rb index 9ba30ef232..c57dc3cce4 100644 --- a/lib/new_relic/agent/datastores/mongo/metric_translator.rb +++ b/lib/new_relic/agent/datastores/mongo/metric_translator.rb @@ -128,7 +128,7 @@ def self.create_index?(name, payload) end def self.drop_indexes?(name, payload) - name == :deleteIndexes && payload[:selector] && payload[:selector][:index] == '*' + name == :deleteIndexes && payload[:selector] && payload[:selector][:index] == ASTERISK end def self.drop_index?(name, payload) diff --git a/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb b/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb index f4f932b0fc..230c8cd641 100644 --- a/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb +++ b/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb @@ -3,7 +3,7 @@ # frozen_string_literal: true require 'json' -require 'base64' +require 'new_relic/base64' module NewRelic module Agent @@ -78,7 +78,7 @@ def from_json(serialized_payload) end def from_http_safe(http_safe_payload) - decoded_payload = Base64.strict_decode64(http_safe_payload) + decoded_payload = NewRelic::Base64.strict_decode64(http_safe_payload) from_json(decoded_payload) end @@ -156,7 +156,7 @@ def text # # @api public def http_safe - Base64.strict_encode64(text) + NewRelic::Base64.strict_encode64(text) end end end diff --git a/lib/new_relic/agent/event_loop.rb b/lib/new_relic/agent/event_loop.rb index 6e2b81901a..7b07973da2 100644 --- a/lib/new_relic/agent/event_loop.rb +++ b/lib/new_relic/agent/event_loop.rb @@ -156,7 +156,7 @@ def dispatch_event(event, args) end if !errors.empty? - ::NewRelic::Agent.logger.error("#{errors.size} error(s) running task for event '#{event}' in Agent Event Loop:", *errors) + ::NewRelic::Agent.logger.error("#{errors.size} error(s) running task for event '#{event}' in agent event loop:", *errors) end end diff --git a/lib/new_relic/agent/http_clients/abstract.rb b/lib/new_relic/agent/http_clients/abstract.rb index 21f59b1583..a79bfc6fc1 100644 --- a/lib/new_relic/agent/http_clients/abstract.rb +++ b/lib/new_relic/agent/http_clients/abstract.rb @@ -14,6 +14,10 @@ module HTTPClients # # @api public class AbstractRequest + LHOST = 'host' + UHOST = 'Host' + COLON = ':' + %i[[] []= type host_from_header host method headers uri].each do |name| define_method(name) do not_implemented(name) diff --git a/lib/new_relic/agent/http_clients/async_http_wrappers.rb b/lib/new_relic/agent/http_clients/async_http_wrappers.rb index 394d8b7f48..10ba255bee 100644 --- a/lib/new_relic/agent/http_clients/async_http_wrappers.rb +++ b/lib/new_relic/agent/http_clients/async_http_wrappers.rb @@ -31,9 +31,6 @@ def initialize(connection, method, url, headers) end ASYNC_HTTP = 'Async::HTTP' - LHOST = 'host' - UHOST = 'Host' - COLON = ':' def type ASYNC_HTTP diff --git a/lib/new_relic/agent/http_clients/curb_wrappers.rb b/lib/new_relic/agent/http_clients/curb_wrappers.rb index 6e0fca5b00..c713fddc43 100644 --- a/lib/new_relic/agent/http_clients/curb_wrappers.rb +++ b/lib/new_relic/agent/http_clients/curb_wrappers.rb @@ -7,10 +7,8 @@ module NewRelic module Agent module HTTPClients - class CurbRequest + class CurbRequest < AbstractRequest CURB = 'Curb' - LHOST = 'host' - UHOST = 'Host' def initialize(curlobj) @curlobj = curlobj diff --git a/lib/new_relic/agent/http_clients/ethon_wrappers.rb b/lib/new_relic/agent/http_clients/ethon_wrappers.rb index 190a2d4c4d..4a85dcaf5f 100644 --- a/lib/new_relic/agent/http_clients/ethon_wrappers.rb +++ b/lib/new_relic/agent/http_clients/ethon_wrappers.rb @@ -42,8 +42,6 @@ class EthonHTTPRequest < AbstractRequest DEFAULT_ACTION = 'GET' DEFAULT_HOST = 'UNKNOWN_HOST' ETHON = 'Ethon' - LHOST = 'host'.freeze - UHOST = 'Host'.freeze def initialize(easy) @easy = easy diff --git a/lib/new_relic/agent/http_clients/excon_wrappers.rb b/lib/new_relic/agent/http_clients/excon_wrappers.rb index 13e3b51630..841e01da04 100644 --- a/lib/new_relic/agent/http_clients/excon_wrappers.rb +++ b/lib/new_relic/agent/http_clients/excon_wrappers.rb @@ -47,9 +47,6 @@ class ExconHTTPRequest < AbstractRequest attr_reader :method EXCON = 'Excon' - LHOST = 'host' - UHOST = 'Host' - COLON = ':' def initialize(datum) @datum = datum diff --git a/lib/new_relic/agent/http_clients/http_rb_wrappers.rb b/lib/new_relic/agent/http_clients/http_rb_wrappers.rb index b0b041523d..3e073b8970 100644 --- a/lib/new_relic/agent/http_clients/http_rb_wrappers.rb +++ b/lib/new_relic/agent/http_clients/http_rb_wrappers.rb @@ -20,8 +20,6 @@ def to_hash class HTTPRequest < AbstractRequest HTTP_RB = 'http.rb' - HOST = 'host' - COLON = ':' def initialize(wrapped_request) @wrapped_request = wrapped_request @@ -36,7 +34,7 @@ def type end def host_from_header - if hostname = self[HOST] + if hostname = self[LHOST] hostname.split(COLON).first end end diff --git a/lib/new_relic/agent/http_clients/httpclient_wrappers.rb b/lib/new_relic/agent/http_clients/httpclient_wrappers.rb index 88578b83d2..3e83ff6553 100644 --- a/lib/new_relic/agent/http_clients/httpclient_wrappers.rb +++ b/lib/new_relic/agent/http_clients/httpclient_wrappers.rb @@ -26,9 +26,6 @@ class HTTPClientRequest < AbstractRequest attr_reader :request HTTP_CLIENT = 'HTTPClient'.freeze - LHOST = 'host'.freeze - UHOST = 'Host'.freeze - COLON = ':'.freeze def initialize(request) @request = request diff --git a/lib/new_relic/agent/http_clients/httpx_wrappers.rb b/lib/new_relic/agent/http_clients/httpx_wrappers.rb index e65412288a..f319bc33d4 100644 --- a/lib/new_relic/agent/http_clients/httpx_wrappers.rb +++ b/lib/new_relic/agent/http_clients/httpx_wrappers.rb @@ -52,8 +52,6 @@ class HTTPXHTTPRequest < AbstractRequest DEFAULT_HOST = 'UNKNOWN_HOST' TYPE = 'HTTPX' - LHOST = 'host'.freeze - UHOST = 'Host'.freeze def initialize(request) @request = request diff --git a/lib/new_relic/agent/http_clients/net_http_wrappers.rb b/lib/new_relic/agent/http_clients/net_http_wrappers.rb index fa4bb5c122..9cc9a3fca6 100644 --- a/lib/new_relic/agent/http_clients/net_http_wrappers.rb +++ b/lib/new_relic/agent/http_clients/net_http_wrappers.rb @@ -30,11 +30,8 @@ def type NET_HTTP end - HOST = 'host' - COLON = ':' - def host_from_header - if hostname = self[HOST] + if hostname = self[LHOST] hostname.split(COLON).first end end diff --git a/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb b/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb index 767edada17..d96513d1b0 100644 --- a/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb +++ b/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb @@ -48,9 +48,6 @@ def type TYPHOEUS end - LHOST = 'host'.freeze - UHOST = 'Host'.freeze - def host_from_header self[LHOST] || self[UHOST] end diff --git a/lib/new_relic/agent/instrumentation/active_merchant.rb b/lib/new_relic/agent/instrumentation/active_merchant.rb index 9086b9b2c4..4d2ce16865 100644 --- a/lib/new_relic/agent/instrumentation/active_merchant.rb +++ b/lib/new_relic/agent/instrumentation/active_merchant.rb @@ -39,7 +39,7 @@ class ActiveMerchant::Billing::Gateway executes do next unless Gem::Version.new(ActiveMerchant::VERSION) < Gem::Version.new('1.65.0') - deprecation_msg = 'The Ruby Agent is dropping support for ActiveMerchant versions below 1.65.0 ' \ + deprecation_msg = 'The Ruby agent is dropping support for ActiveMerchant versions below 1.65.0 ' \ 'in version 9.0.0. Please upgrade your ActiveMerchant version to continue receiving full support. ' \ NewRelic::Agent.logger.log_once( diff --git a/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb b/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb index c7bf7e0518..2db3d6fe34 100644 --- a/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +++ b/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb @@ -56,7 +56,7 @@ def nr_cluster_name return if nr_hosts.empty? NewRelic::Agent.disable_all_tracing do - @nr_cluster_name ||= perform_request('GET', '_cluster/health').body['cluster_name'] + @nr_cluster_name ||= perform_request('GET', '/').body['cluster_name'] end rescue StandardError => e NewRelic::Agent.logger.error('Failed to get cluster name for elasticsearch', e) diff --git a/lib/new_relic/agent/instrumentation/fiber/instrumentation.rb b/lib/new_relic/agent/instrumentation/fiber/instrumentation.rb index b094031e54..67550c1775 100644 --- a/lib/new_relic/agent/instrumentation/fiber/instrumentation.rb +++ b/lib/new_relic/agent/instrumentation/fiber/instrumentation.rb @@ -14,10 +14,7 @@ def initialize_with_newrelic_tracing def add_thread_tracing(&block) return block if !NewRelic::Agent::Tracer.thread_tracing_enabled? - NewRelic::Agent::Tracer.thread_block_with_current_transaction( - segment_name: 'Ruby/Fiber', - &block - ) + NewRelic::Agent::Tracer.thread_block_with_current_transaction(&block) end end end diff --git a/lib/new_relic/agent/instrumentation/sinatra.rb b/lib/new_relic/agent/instrumentation/sinatra.rb index bf20b63be8..5c05324f12 100644 --- a/lib/new_relic/agent/instrumentation/sinatra.rb +++ b/lib/new_relic/agent/instrumentation/sinatra.rb @@ -46,7 +46,7 @@ executes do next unless Gem::Version.new(Sinatra::VERSION) < Gem::Version.new('2.0.0') - deprecation_msg = 'The Ruby Agent is dropping support for Sinatra versions below 2.0.0 ' \ + deprecation_msg = 'The Ruby agent is dropping support for Sinatra versions below 2.0.0 ' \ 'in version 9.0.0. Please upgrade your Sinatra version to continue receiving full compatibility. ' \ NewRelic::Agent.logger.log_once( diff --git a/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb b/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb index 6e01b9a931..a2fc56f0c9 100644 --- a/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb +++ b/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb @@ -33,7 +33,7 @@ def set_newrelic_ignore(type, *routes) set(:newrelic_ignores, Hash.new([])) if !respond_to?(:newrelic_ignores) # If we call an ignore without a route, it applies to the whole app - routes = ['*'] if routes.empty? + routes = [::NewRelic::ASTERISK] if routes.empty? settings.newrelic_ignores[type] += routes.map do |r| # Ugly sending to private Base#compile, but we want to mimic diff --git a/lib/new_relic/agent/instrumentation/thread/instrumentation.rb b/lib/new_relic/agent/instrumentation/thread/instrumentation.rb index 22ba83e251..ed73b35124 100644 --- a/lib/new_relic/agent/instrumentation/thread/instrumentation.rb +++ b/lib/new_relic/agent/instrumentation/thread/instrumentation.rb @@ -16,10 +16,7 @@ def initialize_with_newrelic_tracing def add_thread_tracing(*args, &block) return block if !NewRelic::Agent::Tracer.thread_tracing_enabled? - NewRelic::Agent::Tracer.thread_block_with_current_transaction( - segment_name: 'Ruby/Thread', - &block - ) + NewRelic::Agent::Tracer.thread_block_with_current_transaction(&block) end end end diff --git a/lib/new_relic/agent/instrumentation/view_component.rb b/lib/new_relic/agent/instrumentation/view_component.rb new file mode 100644 index 0000000000..900336c67a --- /dev/null +++ b/lib/new_relic/agent/instrumentation/view_component.rb @@ -0,0 +1,26 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require_relative 'view_component/instrumentation' +require_relative 'view_component/chain' +require_relative 'view_component/prepend' + +DependencyDetection.defer do + named :view_component + + depends_on do + defined?(ViewComponent) && + ViewComponent::Base.method_defined?(:render_in) + end + + executes do + NewRelic::Agent.logger.info('Installing ViewComponent instrumentation') + + if use_prepend? + prepend_instrument ViewComponent::Base, NewRelic::Agent::Instrumentation::ViewComponent::Prepend + else + chain_instrument NewRelic::Agent::Instrumentation::ViewComponent::Chain + end + end +end diff --git a/lib/new_relic/agent/instrumentation/view_component/chain.rb b/lib/new_relic/agent/instrumentation/view_component/chain.rb new file mode 100644 index 0000000000..88885bbcde --- /dev/null +++ b/lib/new_relic/agent/instrumentation/view_component/chain.rb @@ -0,0 +1,21 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +module NewRelic::Agent::Instrumentation + module ViewComponent::Chain + def self.instrument! + ::ViewComponent::Base.class_eval do + include NewRelic::Agent::Instrumentation::ViewComponent + + alias_method(:render_in_without_tracing, :render_in) + + def render_in(*args) + render_in_with_tracing(*args) do + render_in_without_tracing(*args) + end + end + end + end + end +end diff --git a/lib/new_relic/agent/instrumentation/view_component/instrumentation.rb b/lib/new_relic/agent/instrumentation/view_component/instrumentation.rb new file mode 100644 index 0000000000..a95f31dee7 --- /dev/null +++ b/lib/new_relic/agent/instrumentation/view_component/instrumentation.rb @@ -0,0 +1,38 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +module NewRelic::Agent::Instrumentation + module ViewComponent + INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name) + + def render_in_with_tracing(*args) + NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME) + + begin + segment = NewRelic::Agent::Tracer.start_segment( + name: metric_name(self.class.identifier, self.class.name) + ) + yield + rescue => e + ::NewRelic::Agent.logger.debug('Error capturing ViewComponent segment', e) + ensure + segment&.finish + end + end + + def metric_name(identifier, component) + "View/#{metric_path(identifier)}/#{component}" + end + + def metric_path(identifier) + return 'component' unless identifier + + if (parts = identifier.split('/')).size > 1 + parts[-2..-1].join('/') # Get filepath by assuming the Rails' structure: app/components/home/example_component.rb + else + NewRelic::Agent::UNKNOWN_METRIC + end + end + end +end diff --git a/lib/new_relic/agent/instrumentation/view_component/prepend.rb b/lib/new_relic/agent/instrumentation/view_component/prepend.rb new file mode 100644 index 0000000000..190c6b9344 --- /dev/null +++ b/lib/new_relic/agent/instrumentation/view_component/prepend.rb @@ -0,0 +1,13 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +module NewRelic::Agent::Instrumentation + module ViewComponent::Prepend + include NewRelic::Agent::Instrumentation::ViewComponent + + def render_in(*args) + render_in_with_tracing(*args) { super } + end + end +end diff --git a/lib/new_relic/agent/javascript_instrumentor.rb b/lib/new_relic/agent/javascript_instrumentor.rb index 6bce7fb51c..e753d2f25c 100644 --- a/lib/new_relic/agent/javascript_instrumentor.rb +++ b/lib/new_relic/agent/javascript_instrumentor.rb @@ -2,7 +2,6 @@ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. # frozen_string_literal: true -require 'base64' require 'json' require 'new_relic/agent/obfuscator' diff --git a/lib/new_relic/agent/new_relic_service.rb b/lib/new_relic/agent/new_relic_service.rb index 00eb18c84b..4505e76e8a 100644 --- a/lib/new_relic/agent/new_relic_service.rb +++ b/lib/new_relic/agent/new_relic_service.rb @@ -414,7 +414,7 @@ def prep_request(opts) else request = Net::HTTP::Post.new(opts[:uri], headers) end - @audit_logger.log_request_headers(opts[:uri], headers) + @audit_logger.log_request_headers(filtered_uri(opts[:uri]), headers) request['user-agent'] = user_agent request.content_type = 'application/octet-stream' request.body = opts[:data] @@ -431,7 +431,7 @@ def relay_request(request, opts) rescue *CONNECTION_ERRORS => e close_shared_connection if attempts < MAX_ATTEMPTS - ::NewRelic::Agent.logger.debug("Retrying request to #{opts[:collector]}#{opts[:uri]} after #{e}") + ::NewRelic::Agent.logger.debug("Retrying request to #{opts[:collector]}#{filtered_uri(opts[:uri])} after #{e}") retry else raise ServerConnectionException, "Recoverable error talking to #{@collector} after #{attempts} attempts: #{e}" @@ -444,7 +444,7 @@ def relay_request(request, opts) def attempt_request(request, opts) conn = http_connection - ::NewRelic::Agent.logger.debug("Sending request to #{opts[:collector]}#{opts[:uri]} with #{request.method}") + ::NewRelic::Agent.logger.debug("Sending request to #{opts[:collector]}#{filtered_uri(opts[:uri])} with #{request.method}") conn.request(request) end @@ -689,9 +689,7 @@ def prep_collector(method) def invoke_remote_send_request(method, payload, data, encoding) uri = remote_method_uri(method) - full_uri = "#{@collector}#{uri}" - - @audit_logger.log_request(full_uri, payload, @marshaller) + @audit_logger.log_request("#{@collector}#{filtered_uri(uri)}", payload, @marshaller) request_send_ts = Process.clock_gettime(Process::CLOCK_MONOTONIC) response = send_request(:data => data, :uri => uri, @@ -700,6 +698,10 @@ def invoke_remote_send_request(method, payload, data, encoding) :endpoint => method) [response, request_send_ts, Process.clock_gettime(Process::CLOCK_MONOTONIC)] end + + def filtered_uri(uri) + uri.gsub(license_key, ASTERISK * license_key.size) + end end end end diff --git a/lib/new_relic/agent/new_relic_service/encoders.rb b/lib/new_relic/agent/new_relic_service/encoders.rb index b588b7ced5..5823fa2db4 100644 --- a/lib/new_relic/agent/new_relic_service/encoders.rb +++ b/lib/new_relic/agent/new_relic_service/encoders.rb @@ -2,10 +2,10 @@ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. # frozen_string_literal: true -require 'base64' require 'json' require 'stringio' require 'zlib' +require 'new_relic/base64' module NewRelic module Agent @@ -45,7 +45,7 @@ def self.encode(data, opts = {}) data = NewRelic::Agent::EncodingNormalizer.normalize_object(data) end json = ::JSON.dump(data) - Base64.encode64(Compressed::Deflate.encode(json)) + NewRelic::Base64.encode64(Compressed::Deflate.encode(json)) end end end diff --git a/lib/new_relic/agent/obfuscator.rb b/lib/new_relic/agent/obfuscator.rb index 12bc9806bb..a147f02ac6 100644 --- a/lib/new_relic/agent/obfuscator.rb +++ b/lib/new_relic/agent/obfuscator.rb @@ -2,8 +2,6 @@ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. # frozen_string_literal: true -# require 'base64' - module NewRelic module Agent class Obfuscator diff --git a/lib/new_relic/agent/pipe_channel_manager.rb b/lib/new_relic/agent/pipe_channel_manager.rb index 4ae8a85234..4f363de253 100644 --- a/lib/new_relic/agent/pipe_channel_manager.rb +++ b/lib/new_relic/agent/pipe_channel_manager.rb @@ -2,7 +2,7 @@ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. # frozen_string_literal: true -require 'base64' +require 'new_relic/base64' module NewRelic module Agent @@ -257,7 +257,7 @@ def unmarshal(data) Marshal.load(data) rescue StandardError => e ::NewRelic::Agent.logger.error('Failure unmarshalling message from Resque child process', e) - ::NewRelic::Agent.logger.debug(Base64.encode64(data)) + ::NewRelic::Agent.logger.debug(NewRelic::Base64.encode64(data)) nil end diff --git a/lib/new_relic/agent/rules_engine/segment_terms_rule.rb b/lib/new_relic/agent/rules_engine/segment_terms_rule.rb index 63a817c66a..63af772a58 100644 --- a/lib/new_relic/agent/rules_engine/segment_terms_rule.rb +++ b/lib/new_relic/agent/rules_engine/segment_terms_rule.rb @@ -8,7 +8,6 @@ class RulesEngine class SegmentTermsRule PREFIX_KEY = 'prefix'.freeze TERMS_KEY = 'terms'.freeze - SEGMENT_PLACEHOLDER = '*'.freeze ADJACENT_PLACEHOLDERS_REGEX = %r{((?:^|/)\*)(?:/\*)*}.freeze ADJACENT_PLACEHOLDERS_REPLACEMENT = '\1'.freeze @@ -52,7 +51,7 @@ def apply(string) rest = string[@trim_range] leading_slash = rest.slice!(LEADING_SLASH_REGEX) segments = rest.split(SEGMENT_SEPARATOR, -1) - segments.map! { |s| @terms.include?(s) ? s : SEGMENT_PLACEHOLDER } + segments.map! { |s| @terms.include?(s) ? s : ASTERISK } transformed_suffix = collapse_adjacent_placeholder_segments(segments) "#{@prefix}#{leading_slash}#{transformed_suffix}" diff --git a/lib/new_relic/agent/sql_sampler.rb b/lib/new_relic/agent/sql_sampler.rb index 0ff0f08a08..9d385f93b3 100644 --- a/lib/new_relic/agent/sql_sampler.rb +++ b/lib/new_relic/agent/sql_sampler.rb @@ -3,7 +3,6 @@ # frozen_string_literal: true require 'zlib' -require 'base64' require 'digest/md5' module NewRelic diff --git a/lib/new_relic/agent/tracer.rb b/lib/new_relic/agent/tracer.rb index 6313638c80..de94b3409f 100644 --- a/lib/new_relic/agent/tracer.rb +++ b/lib/new_relic/agent/tracer.rb @@ -418,16 +418,17 @@ def thread_tracing_enabled? NewRelic::Agent.config[:'instrumentation.thread.tracing'] end - def thread_block_with_current_transaction(segment_name:, parent: nil, &block) + def thread_block_with_current_transaction(segment_name: nil, parent: nil, &block) parent ||= current_segment current_txn = ::Thread.current[:newrelic_tracer_state]&.current_transaction if ::Thread.current[:newrelic_tracer_state]&.is_execution_traced? proc do |*args| begin if current_txn && !current_txn.finished? NewRelic::Agent::Tracer.state.current_transaction = current_txn + ::Thread.current[:newrelic_thread_span_parent] = parent current_txn.async = true - segment_name += "/Thread#{::Thread.current.object_id}/Fiber#{::Fiber.current.object_id}" if NewRelic::Agent.config[:'thread_ids_enabled'] - segment = NewRelic::Agent::Tracer.start_segment(name: segment_name, parent: parent) + segment_name = "#{segment_name}/Thread#{::Thread.current.object_id}/Fiber#{::Fiber.current.object_id}" if NewRelic::Agent.config[:'thread_ids_enabled'] + segment = NewRelic::Agent::Tracer.start_segment(name: segment_name, parent: parent) if segment_name end NewRelic::Agent::Tracer.capture_segment_error(segment) do yield(*args) diff --git a/lib/new_relic/agent/transaction/tracing.rb b/lib/new_relic/agent/transaction/tracing.rb index 0a81f4f3e3..a453813ef9 100644 --- a/lib/new_relic/agent/transaction/tracing.rb +++ b/lib/new_relic/agent/transaction/tracing.rb @@ -22,7 +22,7 @@ def total_time def add_segment(segment, parent = nil) segment.transaction = self - segment.parent = parent || current_segment + segment.parent = parent || thread_starting_span || current_segment set_current_segment(segment) if @segments.length < segment_limit @segments << segment @@ -39,6 +39,16 @@ def add_segment(segment, parent = nil) segment.transaction_assigned end + def thread_starting_span + # if the previous current segment was in another thread, use the thread local parent + if ::Thread.current[:newrelic_thread_span_parent] && + current_segment && + current_segment.starting_segment_key != NewRelic::Agent::Tracer.current_segment_key + + ::Thread.current[:newrelic_thread_span_parent] + end + end + def segment_complete(segment) # if parent was in another thread, remove the current_segment entry for this thread if segment.parent && segment.parent.starting_segment_key != NewRelic::Agent::Tracer.current_segment_key diff --git a/lib/new_relic/agent/vm.rb b/lib/new_relic/agent/vm.rb index 95dffa2a2b..9a561f01e0 100644 --- a/lib/new_relic/agent/vm.rb +++ b/lib/new_relic/agent/vm.rb @@ -3,7 +3,7 @@ # frozen_string_literal: true require 'new_relic/language_support' -require 'new_relic/agent/vm/mri_vm' +require 'new_relic/agent/vm/c_ruby_vm' require 'new_relic/agent/vm/jruby_vm' module NewRelic @@ -21,7 +21,7 @@ def self.create_vm if NewRelic::LanguageSupport.jruby? JRubyVM.new else - MriVM.new + CRubyVM.new end end end diff --git a/lib/new_relic/agent/vm/mri_vm.rb b/lib/new_relic/agent/vm/c_ruby_vm.rb similarity index 77% rename from lib/new_relic/agent/vm/mri_vm.rb rename to lib/new_relic/agent/vm/c_ruby_vm.rb index 93dedba5f0..abf2cf127a 100644 --- a/lib/new_relic/agent/vm/mri_vm.rb +++ b/lib/new_relic/agent/vm/c_ruby_vm.rb @@ -8,7 +8,7 @@ module NewRelic module Agent module VM - class MriVM + class CRubyVM def snapshot snap = Snapshot.new gather_stats(snap) @@ -24,7 +24,7 @@ def gather_stats(snap) def gather_gc_stats(snap) gather_gc_runs(snap) if supports?(:gc_runs) - gather_derived_stats(snap) if GC.respond_to?(:stat) + gather_derived_stats(snap) end def gather_gc_runs(snap) @@ -33,19 +33,11 @@ def gather_gc_runs(snap) def gather_derived_stats(snap) stat = GC.stat - snap.total_allocated_object = derive_from_gc_stats(%i[total_allocated_objects total_allocated_object], stat) - snap.major_gc_count = derive_from_gc_stats(:major_gc_count, stat) - snap.minor_gc_count = derive_from_gc_stats(:minor_gc_count, stat) - snap.heap_live = derive_from_gc_stats(%i[heap_live_slots heap_live_slot heap_live_num], stat) - snap.heap_free = derive_from_gc_stats(%i[heap_free_slots heap_free_slot heap_free_num], stat) - end - - def derive_from_gc_stats(keys, stat) - Array(keys).each do |key| - value = stat[key] - return value if value - end - nil + snap.total_allocated_object = stat.fetch(:total_allocated_objects, nil) + snap.major_gc_count = stat.fetch(:major_gc_count, nil) + snap.minor_gc_count = stat.fetch(:minor_gc_count, nil) + snap.heap_live = stat.fetch(:heap_live_slots, nil) + snap.heap_free = stat.fetch(:heap_free_slots, nil) end def gather_gc_time(snap) diff --git a/lib/new_relic/agent/worker_loop.rb b/lib/new_relic/agent/worker_loop.rb index 5a47875c6b..4fbe7ca4bd 100644 --- a/lib/new_relic/agent/worker_loop.rb +++ b/lib/new_relic/agent/worker_loop.rb @@ -88,7 +88,7 @@ def run_task raise rescue => e # Don't blow out the stack for anything that hasn't already propagated - ::NewRelic::Agent.logger.error('Error running task in Agent Worker Loop:', e) + ::NewRelic::Agent.logger.error('Error running task in agent worker loop:', e) end end end diff --git a/lib/new_relic/base64.rb b/lib/new_relic/base64.rb new file mode 100644 index 0000000000..669e0d6aeb --- /dev/null +++ b/lib/new_relic/base64.rb @@ -0,0 +1,25 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +module NewRelic + module Base64 + extend self + + def encode64(bin) + [bin].pack('m') + end + + def decode64(str) + str.unpack1('m') + end + + def strict_encode64(bin) + [bin].pack('m0') + end + + def strict_decode64(str) + str.unpack1('m0') + end + end +end diff --git a/lib/new_relic/cli/command.rb b/lib/new_relic/cli/command.rb index 5574459550..3898564f93 100644 --- a/lib/new_relic/cli/command.rb +++ b/lib/new_relic/cli/command.rb @@ -60,11 +60,13 @@ def self.run extra = [] options = ARGV.options do |opts| script_name = File.basename($0) - # TODO: MAJOR VERSION - remove newrelic_cmd, deprecated since version 2.13 - if /newrelic_cmd$/.match?(script_name) - $stdout.puts "warning: the 'newrelic_cmd' script has been renamed 'newrelic'" - script_name = 'newrelic' + + # TODO: MAJOR VERSION - remove newrelic, deprecated since version x.xx + if /newrelic$/.match?(script_name) + $stdout.puts "warning: the 'newrelic' script has been renamed 'newrelic_rpm'" + script_name = 'newrelic_rpm' end + opts.banner = "Usage: #{script_name} [ #{@command_names.join(' | ')} ] [options]" opts.separator("use '#{script_name} -h' to see detailed command options") opts diff --git a/lib/new_relic/constants.rb b/lib/new_relic/constants.rb index 2a8aafb1c2..b62340f348 100644 --- a/lib/new_relic/constants.rb +++ b/lib/new_relic/constants.rb @@ -3,6 +3,8 @@ # frozen_string_literal: true module NewRelic + ASTERISK = '*' + PRIORITY_PRECISION = 6 EMPTY_ARRAY = [].freeze diff --git a/lib/new_relic/control/frameworks/rails.rb b/lib/new_relic/control/frameworks/rails.rb index 5725f5f413..2811d09f5a 100644 --- a/lib/new_relic/control/frameworks/rails.rb +++ b/lib/new_relic/control/frameworks/rails.rb @@ -74,7 +74,7 @@ def install_browser_monitoring_and_agent_hooks # Might not be running if it does not think mongrel, thin, # passenger, etc. is running, if it thinks it's a rake task, or # if the agent_enabled is false. - ::NewRelic::Agent.logger.info('New Relic Agent not running. Skipping browser monitoring and agent hooks.') + ::NewRelic::Agent.logger.info('New Relic agent not running. Skipping browser monitoring and agent hooks.') else install_browser_monitoring(rails_config) install_agent_hooks(rails_config) @@ -92,9 +92,9 @@ def install_agent_hooks(config) return unless NewRelic::Rack::AgentHooks.needed? config.middleware.use(NewRelic::Rack::AgentHooks) - ::NewRelic::Agent.logger.debug('Installed New Relic Agent Hooks middleware') + ::NewRelic::Agent.logger.debug('Installed New Relic agent hooks middleware') rescue => e - ::NewRelic::Agent.logger.warn('Error installing New Relic Agent Hooks middleware', e) + ::NewRelic::Agent.logger.warn('Error installing New Relic agent hooks middleware', e) end end diff --git a/lib/new_relic/control/instrumentation.rb b/lib/new_relic/control/instrumentation.rb index e76143aae4..5dd9efe841 100644 --- a/lib/new_relic/control/instrumentation.rb +++ b/lib/new_relic/control/instrumentation.rb @@ -70,7 +70,7 @@ def _install_instrumentation def rails_32_deprecation return unless defined?(Rails::VERSION) && Gem::Version.new(Rails::VERSION::STRING) <= Gem::Version.new('3.2') - deprecation_msg = 'The Ruby Agent is dropping support for Rails 3.2 ' \ + deprecation_msg = 'The Ruby agent is dropping support for Rails 3.2 ' \ 'in a future major release. Please upgrade your Rails version to continue receiving support. ' \ Agent.logger.log_once( diff --git a/lib/new_relic/local_environment.rb b/lib/new_relic/local_environment.rb index dff2d8343f..d6a355c61d 100644 --- a/lib/new_relic/local_environment.rb +++ b/lib/new_relic/local_environment.rb @@ -74,6 +74,7 @@ def discover_dispatcher unicorn webrick fastcgi + falcon ] while dispatchers.any? && @discovered_dispatcher.nil? send('check_for_' + (dispatchers.shift)) @@ -126,16 +127,24 @@ def check_for_mongrel end def check_for_unicorn - if (defined?(::Unicorn) && defined?(::Unicorn::HttpServer)) && NewRelic::LanguageSupport.object_space_usable? - v = find_class_in_object_space(::Unicorn::HttpServer) - @discovered_dispatcher = :unicorn if v - end + return unless (defined?(::Unicorn) && defined?(::Unicorn::HttpServer)) && + NewRelic::LanguageSupport.object_space_usable? + + v = find_class_in_object_space(::Unicorn::HttpServer) + @discovered_dispatcher = :unicorn if v end def check_for_puma - if defined?(::Puma) && File.basename($0) == 'puma' - @discovered_dispatcher = :puma - end + return unless defined?(::Puma) && File.basename($0) == 'puma' + + @discovered_dispatcher = :puma + end + + def check_for_falcon + return unless defined?(::Falcon::Server) && + NewRelic::LanguageSupport.object_space_usable? + + @discovered_dispatcher = :falcon if find_class_in_object_space(::Falcon::Server) end def check_for_delayed_job @@ -178,15 +187,15 @@ def check_for_thin end def check_for_litespeed - if caller.pop.include?('fcgi-bin/RailsRunner.rb') - @discovered_dispatcher = :litespeed - end + return unless caller.pop.include?('fcgi-bin/RailsRunner.rb') + + @discovered_dispatcher = :litespeed end def check_for_passenger - if defined?(::PhusionPassenger) - @discovered_dispatcher = :passenger - end + return unless defined?(::PhusionPassenger) + + @discovered_dispatcher = :passenger end public diff --git a/lib/new_relic/supportability_helper.rb b/lib/new_relic/supportability_helper.rb index 410d3fd997..6281cf29e6 100644 --- a/lib/new_relic/supportability_helper.rb +++ b/lib/new_relic/supportability_helper.rb @@ -80,7 +80,7 @@ def valid_api_argument_class?(arg, name, klass) "Expected #{klass} for `#{name}` but got #{arg.class}" NewRelic::Agent.logger.warn(message) - nil + false end end end diff --git a/lib/new_relic/version.rb b/lib/new_relic/version.rb index f910ddda9d..029ec37fc0 100644 --- a/lib/new_relic/version.rb +++ b/lib/new_relic/version.rb @@ -6,7 +6,7 @@ module NewRelic module VERSION # :nodoc: MAJOR = 9 - MINOR = 6 + MINOR = 7 TINY = 0 STRING = "#{MAJOR}.#{MINOR}.#{TINY}" diff --git a/lib/tasks/config.rake b/lib/tasks/config.rake index 1b0e4d1b07..23e26f7382 100644 --- a/lib/tasks/config.rake +++ b/lib/tasks/config.rake @@ -19,7 +19,7 @@ namespace :newrelic do DISABLING => 'Use these settings to toggle instrumentation types during agent startup.', ATTRIBUTES => '[Attributes](/docs/features/agent-attributes) are key-value pairs containing information that determines the properties of an event or transaction. These key-value pairs can be viewed within transaction traces in APM, traced errors in APM, transaction events in dashboards, and page views in dashboards. You can customize exactly which attributes will be sent to each of these destinations', 'transaction_tracer' => 'The [transaction traces](/docs/apm/traces/transaction-traces/transaction-traces) feature collects detailed information from a selection of transactions, including a summary of the calling sequence, a breakdown of time spent, and a list of SQL queries and their query plans (on mysql and postgresql). Available features depend on your New Relic subscription level.', - 'error_collector' => "The agent collects and reports all uncaught exceptions by default. These configuration options allow you to customize the error collection.\n\nFor information on ignored and expected errors, [see this page on Error Analytics in APM](/docs/agents/manage-apm-agents/agent-data/manage-errors-apm-collect-ignore-or-mark-expected/). To set expected errors via the `NewRelic::Agent.notice_error` Ruby method, [consult the Ruby Agent API](/docs/agents/ruby-agent/api-guides/sending-handled-errors-new-relic/).", + 'error_collector' => "The agent collects and reports all uncaught exceptions by default. These configuration options allow you to customize the error collection.\n\nFor information on ignored and expected errors, [see this page on Error Analytics in APM](/docs/agents/manage-apm-agents/agent-data/manage-errors-apm-collect-ignore-or-mark-expected/). To set expected errors via the `NewRelic::Agent.notice_error` Ruby method, [consult the Ruby agent API](/docs/agents/ruby-agent/api-guides/sending-handled-errors-new-relic/).", 'browser_monitoring' => "The browser monitoring [page load timing](/docs/browser/new-relic-browser/page-load-timing/page-load-timing-process) feature (sometimes referred to as real user monitoring or RUM) gives you insight into the performance real users are experiencing with your website. This is accomplished by measuring the time it takes for your users' browsers to download and render your web pages by injecting a small amount of JavaScript code into the header and footer of each page.", 'application_logging' => "The Ruby agent supports [APM logs in context](/docs/apm/new-relic-apm/getting-started/get-started-logs-context). For some tips on configuring logs for the Ruby agent, see [Configure Ruby logs in context](/docs/logs/logs-context/configure-logs-context-ruby).\n\nAvailable logging-related config options include:", 'analytics_events' => '[New Relic dashboards](/docs/query-your-data/explore-query-data/dashboards/introduction-new-relic-one-dashboards) is a resource to gather and visualize data about your software and what it says about your business. With it you can quickly and easily create real-time dashboards to get immediate answers about end-user experiences, clickstreams, mobile activities, and server transactions.' diff --git a/lib/tasks/helpers/config.html.erb b/lib/tasks/helpers/config.html.erb index 891486c403..cd3acf313d 100644 --- a/lib/tasks/helpers/config.html.erb +++ b/lib/tasks/helpers/config.html.erb @@ -14,7 +14,7 @@ redirects: This file is automatically generated from values defined in `lib/new_relic/agent/configuration/default_source.rb`. - All changes should be made directly to `default_source.rb.` + Make all changes directly to `default_source.rb.` Submit PRs or raise issues at: https://github.com/newrelic/newrelic-ruby-agent @@ -42,9 +42,9 @@ In other words, environment variables override all other configuration settings ## View and edit config file options [#Edit] -The Ruby agent's `newrelic.yml` is a standard YAML configuration file. It typically includes a `Defaults` section at the top, plus sections below for each application environment; for example, `Development`, `Testing`, and `Production`. +The Ruby agent's `newrelic.yml` is a standard YAML configuration file. It typically includes a `Defaults` section at the top, plus sections below for each application environment (`Development`, `Test`, `Staging`, and `Production`). -The Ruby agent determines which section of the `newrelic.yml` config file to read from by looking at certain environment variables to derive the application's environment. This can be useful, for example, when you want to use `info` for the `log_level` config setting in your production environment, and you want more verbose `log_level` config settings (such as `debug` in your development environment. +The Ruby agent determines which section of the `newrelic.yml` config file to read from by looking at certain environment variables to derive the application's environment. This can be useful when you want to use `info` for the `log_level` config setting in your production environment, and you want more verbose `log_level` config settings (such as `debug`) in your development environment. Here is an example `newrelic.yml` config file: @@ -60,7 +60,7 @@ development: log_level: debug ``` -For non-Rails apps, the Ruby agent looks for the following environment variables, in this order, to determine the application environment: +The Ruby agent looks for the following environment variables, in this order, to find the application environment: 1. `NEW_RELIC_ENV` 2. `RUBY_ENV` @@ -68,9 +68,9 @@ For non-Rails apps, the Ruby agent looks for the following environment variables 4. `APP_ENV` 5. `RACK_ENV` -If the Ruby agent does not detect values for any of those environment variables, it will default the application environment to `development` and read from the `development` section of the `newrelic.yml` config file. +If the Ruby agent doesn't detect values for any of those environment variables, it will default the application environment to `development` and read from the `development` section of the `newrelic.yml` config file. -When running the Ruby agent in a Rails app, the agent first looks for the `NEW_RELIC_ENV` environment variable to determine the application environment and which section of the `newrelic.yml` to use. If `NEW_RELIC_ENV` is not present, the agent uses the Rails environment (`RAILS_ENV` or `RAILS.env`, depending on the version of Rails) . +When running the Ruby agent in a Rails app, the agent first looks for the `NEW_RELIC_ENV` environment variable to determine the application environment and which section of the `newrelic.yml` to use. If `NEW_RELIC_ENV` is not present, the agent uses the Rails environment (`RAILS_ENV`). When you edit the config file, be sure to: diff --git a/lib/tasks/helpers/newrelicyml.rb b/lib/tasks/helpers/newrelicyml.rb index 6306c1dc53..f9bc190aa4 100644 --- a/lib/tasks/helpers/newrelicyml.rb +++ b/lib/tasks/helpers/newrelicyml.rb @@ -16,7 +16,7 @@ module NewRelicYML HEADER = <<~HEADER # - # This file configures the New Relic Agent. New Relic monitors Ruby, Java, + # This file configures the New Relic agent. New Relic monitors Ruby, Java, # .NET, PHP, Python, Node, and Go applications with deep visibility and low # overhead. For more information, visit www.newrelic.com. diff --git a/newrelic.yml b/newrelic.yml index 398e662959..4eb81d8b2a 100644 --- a/newrelic.yml +++ b/newrelic.yml @@ -1,5 +1,5 @@ # -# This file configures the New Relic Agent. New Relic monitors Ruby, Java, +# This file configures the New Relic agent. New Relic monitors Ruby, Java, # .NET, PHP, Python, Node, and Go applications with deep visibility and low # overhead. For more information, visit www.newrelic.com. @@ -341,7 +341,7 @@ common: &default_settings # embedded service within another framework and the agent is detecting the Sinatra # app and skipping the at_exit handler as a result. Sinatra classically runs the # entire application in an at_exit block and would otherwise misbehave if the - # Agent's at_exit handler was also installed in those circumstances. Note: + # agent's at_exit handler was also installed in those circumstances. Note: # send_data_on_exit should also be set to true in tandem with this setting. # force_install_exit_handler: false @@ -535,6 +535,10 @@ common: &default_settings # prepend, chain, disabled. # instrumentation.typhoeus: auto + # Controls auto-instrumentation of ViewComponent at startup. May be one of: auto, + # prepend, chain, disabled. + # instrumentation.view_component: auto + # A dictionary of label names and values that will be applied to the data sent # from this agent. May also be expressed as a semicolon-delimited ; string of # colon-separated : pairs. For example, Server:One;Data Center:Primary. diff --git a/newrelic_rpm.gemspec b/newrelic_rpm.gemspec index 4f08a56439..cf83a9fe40 100644 --- a/newrelic_rpm.gemspec +++ b/newrelic_rpm.gemspec @@ -21,8 +21,8 @@ Gem::Specification.new do |s| https://github.com/newrelic/newrelic-ruby-agent/ EOS s.email = 'support@newrelic.com' - # TODO: MAJOR VERSION - remove newrelic_cmd, deprecated since version 2.13 - s.executables = %w[newrelic_cmd newrelic nrdebug] + # TODO: MAJOR VERSION - remove newrelic, deprecated since version xxx. + s.executables = %w[newrelic_rpm newrelic nrdebug] s.extra_rdoc_files = [ 'CHANGELOG.md', 'LICENSE', @@ -50,8 +50,6 @@ Gem::Specification.new do |s| s.require_paths = ['lib'] s.summary = 'New Relic Ruby Agent' - s.add_dependency 'base64' - s.add_development_dependency 'bundler' s.add_development_dependency 'feedjira', '3.2.1' unless ENV['CI'] || RUBY_VERSION < '2.5' # for Gabby s.add_development_dependency 'httparty' unless ENV['CI'] # for perf tests and Gabby @@ -62,7 +60,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rack' s.add_development_dependency 'rake', '12.3.3' - s.add_development_dependency 'rubocop', '1.54' unless ENV['CI'] && RUBY_VERSION < '3.0.0' + s.add_development_dependency 'rubocop', '1.57.2' unless ENV['CI'] && RUBY_VERSION < '3.0.0' s.add_development_dependency 'rubocop-ast', '1.28.1' unless ENV['CI'] && RUBY_VERSION < '3.0.0' s.add_development_dependency 'rubocop-minitest', '0.27.0' unless ENV['CI'] && RUBY_VERSION < '3.0.0' s.add_development_dependency 'rubocop-performance', '1.16.0' unless ENV['CI'] && RUBY_VERSION < '3.0.0' diff --git a/test/agent_helper.rb b/test/agent_helper.rb index 39cf6f4cb8..5d12ed5e17 100644 --- a/test/agent_helper.rb +++ b/test/agent_helper.rb @@ -845,7 +845,7 @@ def with_ignore_error_filter(filter, &blk) end def json_dump_and_encode(object) - Base64.encode64(JSON.dump(object)) + NewRelic::Base64.encode64(JSON.dump(object)) end def get_last_analytics_event @@ -871,7 +871,7 @@ def load_cross_agent_test(name) test_file_path = File.join(cross_agent_tests_dir, "#{name}.json") data = File.read(test_file_path) data.gsub!('callCount', 'call_count') - data = JSON.load(data) + data = JSON.parse(data) data.each { |testcase| testcase['testname'].tr!(' ', '_') if String === testcase['testname'] } data end @@ -1025,3 +1025,15 @@ def defer_testing_to_min_supported_rails(test_file, min_rails_version, supports_ puts "Skipping tests in #{File.basename(test_file)} because Rails >= #{min_rails_version} is unavailable" if ENV['VERBOSE_TEST_OUTPUT'] end end + +def first_call_for(subject) + items = $collector.calls_for(subject) + + if defined?(JRUBY_VERSION) + refute_predicate items.size, :zero?, "Expected at least one call for '#{subject}'" + else + assert_equal 1, items.size, "Expected exactly one call for '#{subject}'" + end + + items.first +end diff --git a/test/fixtures/cross_agent_tests/.gitignore b/test/fixtures/cross_agent_tests/.gitignore new file mode 100644 index 0000000000..485dee64bc --- /dev/null +++ b/test/fixtures/cross_agent_tests/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/test/fixtures/cross_agent_tests/README.md b/test/fixtures/cross_agent_tests/README.md index b6a6817395..1efcf58d7b 100644 --- a/test/fixtures/cross_agent_tests/README.md +++ b/test/fixtures/cross_agent_tests/README.md @@ -17,7 +17,7 @@ Push access to this repository is granted via membership in the agents GHE group | [rum_footer_insertion_location](rum_footer_insertion_location) | Describe where the RUM footer (aka "client config") should be inserted. These tests do not apply to agents which insert the footer directly after the loader. | | [rules.json](rules.json) | Describe how url/metric/txn-name rules should be applied. | | [rum_client_config.json](rum_client_config.json) | These tests dictate the format and contents of the browser monitoring client configuration. For more information see: [SPEC](https://newrelic.atlassian.net/wiki/display/eng/BAM+Agent+Auto-Instrumentation) | -| [sql_parsing.json](sql_parsing.json) | These tests show how an SQL string should be parsed for the operation and table name. *Java Note*: The Java Agent is [out-of-sync with these tests](https://source.datanerd.us/java-agent/java_agent/blob/master/newrelic-agent/src/main/java/com/newrelic/agent/database/DefaultDatabaseStatementParser.java), [has its own tests](https://source.datanerd.us/java-agent/java_agent/blob/master/newrelic-agent/src/test/java/com/newrelic/agent/database/DatabaseStatementResponseParserTest.java), and cannot implement these without a breaking change. | +| [sql_parsing.json](sql_parsing.json) | These tests show how an SQL string should be parsed for the operation and table name. *Java Note*: The Java agent is [out-of-sync with these tests](https://source.datanerd.us/java-agent/java_agent/blob/master/newrelic-agent/src/main/java/com/newrelic/agent/database/DefaultDatabaseStatementParser.java), [has its own tests](https://source.datanerd.us/java-agent/java_agent/blob/master/newrelic-agent/src/test/java/com/newrelic/agent/database/DatabaseStatementResponseParserTest.java), and cannot implement these without a breaking change. | | [url_clean.json](url_clean.json) | These tests show how URLs should be cleaned before putting them into a trace segment's parameter hash (under the key 'uri'). | | [url_domain_extraction.json](url_domain_extraction.json) | These tests show how the domain of a URL should be extracted (for the purpose of creating external metrics). | | [postgres_explain_obfuscation](postgres_explain_obfuscation) | These tests show how plain-text explain plan output from PostgreSQL should be obfuscated when SQL obfuscation is enabled. | @@ -30,6 +30,7 @@ Push access to this repository is granted via membership in the agents GHE group | [transaction_segment_terms.json](transaction_segment_terms.json) | These tests cover agent implementations of the `transaction_segment_terms` transaction renaming rules introduced in collector protocol 14. See [the spec](https://newrelic.atlassian.net/wiki/display/eng/Language+agent+transaction+segment+terms+rules) for details. | | [synthetics](synthetics) | These tests cover agent support for Synthetics. For details, see [Agent Support for Synthetics: Forced Transaction Traces and Analytic Events](https://source.datanerd.us/agents/agent-specs/blob/master/Synthetics-PORTED.md). | | [docker_container_id](docker_container_id) | These tests cover parsing of Docker container IDs from `/proc/*/cgroup` on Linux hosts. | +| [docker_container_id_v2](docker_container_id_v2) | These tests cover parsing of Docker container IDs from `/proc/*/mountinfo` on Linux hosts. | | [utilization](utilization) | These tests cover the collection and validation of metadata for billing purposes as per the [Utilization spec](https://source.datanerd.us/agents/agent-specs/blob/master/Utilization.md). | | [utilization_vendor_specific](utilization_vendor_specific) | These tests cover the collection and validation of metadata for AWS, Pivotal Cloud Foundry, Google Cloud Platform, and Azure as per the [Utilization spec](https://source.datanerd.us/agents/agent-specs/blob/master/Utilization.md). | | [distributed_tracing](distributed_tracing) | distributed tracing, a.k.a. CAT CATs | diff --git a/test/fixtures/cross_agent_tests/docker_container_id_v2/cases.json b/test/fixtures/cross_agent_tests/docker_container_id_v2/cases.json new file mode 100644 index 0000000000..83d6360a31 --- /dev/null +++ b/test/fixtures/cross_agent_tests/docker_container_id_v2/cases.json @@ -0,0 +1,36 @@ +[ + { + "filename": "docker-20.10.16.txt", + "containerId": "84cf3472a20d1bfb4b50e48b6ff50d96dfcd812652d76dd907951e6f98997bce", + "expectedMetrics": null + }, + { + "filename": "docker-24.0.2.txt", + "containerId": "b0a24eed1b031271d8ba0784b8f354b3da892dfd08bbcf14dd7e8a1cf9292f65", + "expectedMetrics": null + }, + { + "filename": "empty.txt", + "containerId": null, + "expectedMetrics": null + }, + { + "filename": "invalid-characters.txt", + "containerId": null, + "expectedMetrics": null + }, + { + "filename": "docker-too-long.txt", + "containerId": null, + "expectedMetrics": null + }, + { + "filename": "invalid-length.txt", + "containerId": null, + "expectedMetrics": { + "Supportability/utilization/docker/error": { + "callCount": 1 + } + } + } +] diff --git a/test/fixtures/cross_agent_tests/docker_container_id_v2/docker-20.10.16.txt b/test/fixtures/cross_agent_tests/docker_container_id_v2/docker-20.10.16.txt new file mode 100644 index 0000000000..ce2b1bedf6 --- /dev/null +++ b/test/fixtures/cross_agent_tests/docker_container_id_v2/docker-20.10.16.txt @@ -0,0 +1,24 @@ +519 413 0:152 / / rw,relatime master:180 - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/l/YCID3333O5VYPYDNTQRZX4GI67:/var/lib/docker/overlay2/l/G7H4TULAFM2UBFRL7QFQPUNXY5:/var/lib/docker/overlay2/l/RLC4GCL75VGXXXYJJO57STHIYN:/var/lib/docker/overlay2/l/YOZKNWFAP6YX74XEKPHX4KG4UN:/var/lib/docker/overlay2/l/46EQ6YX5PQQZ4Z3WCSMQ6Z4YWI:/var/lib/docker/overlay2/l/KGKX3Z5ZMOCDWOFKBS2FSHMQMQ:/var/lib/docker/overlay2/l/CKFYAF4TXZD4RCE6RG6UNL5WVI,upperdir=/var/lib/docker/overlay2/358c429f7b04ee5a228b94efaebe3413a98fcc676b726f078fe875727e3bddd2/diff,workdir=/var/lib/docker/overlay2/358c429f7b04ee5a228b94efaebe3413a98fcc676b726f078fe875727e3bddd2/work +520 519 0:155 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw +521 519 0:156 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +522 521 0:157 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +523 519 0:158 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro +524 523 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw +525 521 0:154 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw +526 521 0:159 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k +527 519 254:1 /docker/volumes/3237dea4f8022f1addd7b6f072a9c847eb3e5b8df0d599f462ba7040884d4618/_data /data rw,relatime master:28 - ext4 /dev/vda1 rw +528 519 254:1 /docker/containers/84cf3472a20d1bfb4b50e48b6ff50d96dfcd812652d76dd907951e6f98997bce/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw +529 519 254:1 /docker/containers/84cf3472a20d1bfb4b50e48b6ff50d96dfcd812652d76dd907951e6f98997bce/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw +530 519 254:1 /docker/containers/84cf3472a20d1bfb4b50e48b6ff50d96dfcd812652d76dd907951e6f98997bce/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw +414 521 0:157 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +415 520 0:155 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw +416 520 0:155 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw +417 520 0:155 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw +418 520 0:155 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw +419 520 0:155 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw +420 520 0:160 / /proc/acpi ro,relatime - tmpfs tmpfs ro +421 520 0:156 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +422 520 0:156 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +423 520 0:156 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +424 520 0:156 /null /proc/sched_debug rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +425 523 0:161 / /sys/firmware ro,relatime - tmpfs tmpfs ro diff --git a/test/fixtures/cross_agent_tests/docker_container_id_v2/docker-24.0.2.txt b/test/fixtures/cross_agent_tests/docker_container_id_v2/docker-24.0.2.txt new file mode 100644 index 0000000000..1725e7726a --- /dev/null +++ b/test/fixtures/cross_agent_tests/docker_container_id_v2/docker-24.0.2.txt @@ -0,0 +1,21 @@ +1014 1013 0:269 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw +1019 1013 0:270 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +1020 1019 0:271 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +1021 1013 0:272 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro +1022 1021 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw +1023 1019 0:268 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw +1024 1019 0:273 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k +1025 1013 254:1 /docker/containers/b0a24eed1b031271d8ba0784b8f354b3da892dfd08bbcf14dd7e8a1cf9292f65/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw,discard +1026 1013 254:1 /docker/containers/b0a24eed1b031271d8ba0784b8f354b3da892dfd08bbcf14dd7e8a1cf9292f65/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw,discard +1027 1013 254:1 /docker/containers/b0a24eed1b031271d8ba0784b8f354b3da892dfd08bbcf14dd7e8a1cf9292f65/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw,discard +717 1019 0:271 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +718 1014 0:269 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw +719 1014 0:269 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw +720 1014 0:269 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw +721 1014 0:269 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw +723 1014 0:269 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw +726 1014 0:274 / /proc/acpi ro,relatime - tmpfs tmpfs ro +727 1014 0:270 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +728 1014 0:270 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +729 1014 0:270 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +730 1021 0:275 / /sys/firmware ro,relatime - tmpfs tmpfs ro diff --git a/test/fixtures/cross_agent_tests/docker_container_id_v2/docker-too-long.txt b/test/fixtures/cross_agent_tests/docker_container_id_v2/docker-too-long.txt new file mode 100644 index 0000000000..608eaf7a49 --- /dev/null +++ b/test/fixtures/cross_agent_tests/docker_container_id_v2/docker-too-long.txt @@ -0,0 +1,21 @@ +1014 1013 0:269 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw +1019 1013 0:270 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +1020 1019 0:271 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +1021 1013 0:272 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro +1022 1021 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw +1023 1019 0:268 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw +1024 1019 0:273 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k +1025 1013 254:1 /docker/containers/3ccfa00432798ff38f85839de1e396f771b4acbe9f4ddea0a761c39b9790a7821/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw,discard +1026 1013 254:1 /docker/containers/3ccfa00432798ff38f85839de1e396f771b4acbe9f4ddea0a761c39b9790a7821/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw,discard +1027 1013 254:1 /docker/containers/3ccfa00432798ff38f85839de1e396f771b4acbe9f4ddea0a761c39b9790a7821/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw,discard +717 1019 0:271 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +718 1014 0:269 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw +719 1014 0:269 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw +720 1014 0:269 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw +721 1014 0:269 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw +723 1014 0:269 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw +726 1014 0:274 / /proc/acpi ro,relatime - tmpfs tmpfs ro +727 1014 0:270 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +728 1014 0:270 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +729 1014 0:270 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +730 1021 0:275 / /sys/firmware ro,relatime - tmpfs tmpfs ro diff --git a/test/fixtures/cross_agent_tests/docker_container_id_v2/empty.txt b/test/fixtures/cross_agent_tests/docker_container_id_v2/empty.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/cross_agent_tests/docker_container_id_v2/invalid-characters.txt b/test/fixtures/cross_agent_tests/docker_container_id_v2/invalid-characters.txt new file mode 100644 index 0000000000..b561475ac6 --- /dev/null +++ b/test/fixtures/cross_agent_tests/docker_container_id_v2/invalid-characters.txt @@ -0,0 +1,21 @@ +1014 1013 0:269 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw +1019 1013 0:270 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +1020 1019 0:271 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +1021 1013 0:272 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro +1022 1021 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw +1023 1019 0:268 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw +1024 1019 0:273 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k +1025 1013 254:1 /docker/containers/WRONGINCORRECTINVALIDCHARSERRONEOUSBADPHONYBROKEN2TERRIBLENOPE55/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw,discard +1026 1013 254:1 /docker/containers/WRONGINCORRECTINVALIDCHARSERRONEOUSBADPHONYBROKEN2TERRIBLENOPE55/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw,discard +1027 1013 254:1 /docker/containers/WRONGINCORRECTINVALIDCHARSERRONEOUSBADPHONYBROKEN2TERRIBLENOPE55/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw,discard +717 1019 0:271 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +718 1014 0:269 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw +719 1014 0:269 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw +720 1014 0:269 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw +721 1014 0:269 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw +723 1014 0:269 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw +726 1014 0:274 / /proc/acpi ro,relatime - tmpfs tmpfs ro +727 1014 0:270 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +728 1014 0:270 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +729 1014 0:270 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +730 1021 0:275 / /sys/firmware ro,relatime - tmpfs tmpfs ro diff --git a/test/fixtures/cross_agent_tests/docker_container_id_v2/invalid-length.txt b/test/fixtures/cross_agent_tests/docker_container_id_v2/invalid-length.txt new file mode 100644 index 0000000000..a8987df707 --- /dev/null +++ b/test/fixtures/cross_agent_tests/docker_container_id_v2/invalid-length.txt @@ -0,0 +1,21 @@ +1014 1013 0:269 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw +1019 1013 0:270 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +1020 1019 0:271 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +1021 1013 0:272 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro +1022 1021 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw +1023 1019 0:268 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw +1024 1019 0:273 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k +1025 1013 254:1 /docker/containers/47cbd16b77c5/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw,discard +1026 1013 254:1 /docker/containers/47cbd16b77c5/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw,discard +1027 1013 254:1 /docker/containers/47cbd16b77c5/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw,discard +717 1019 0:271 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666 +718 1014 0:269 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw +719 1014 0:269 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw +720 1014 0:269 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw +721 1014 0:269 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw +723 1014 0:269 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw +726 1014 0:274 / /proc/acpi ro,relatime - tmpfs tmpfs ro +727 1014 0:270 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +728 1014 0:270 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +729 1014 0:270 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755 +730 1021 0:275 / /sys/firmware ro,relatime - tmpfs tmpfs ro diff --git a/test/multiverse/README.md b/test/multiverse/README.md index 8beeddc6b5..6d92d2a5c3 100644 --- a/test/multiverse/README.md +++ b/test/multiverse/README.md @@ -3,7 +3,7 @@ ## Testing in a multitude of environments Multiverse was created to solve a specific problem experienced by the Agent -team. Not only does the New Relic Agent run in a wide variety of environments, +team. Not only does the New Relic agent run in a wide variety of environments, but its expected behavior *changes* based on the environment. Instrumentation is toggled on and off based on the presence of certain libraries, and some of these libraries are incompatible with each other. Effective testing requires us to @@ -41,9 +41,9 @@ If you are using [Homebrew](https://brew.sh/), then you may make use of this project's [Brewfile](../../../Brewfile) file to automatically install all of those applications as well as these additional dependency packages: -* [pkg-config](https://freedesktop.org/wiki/Software/pkg-config/) -* [OpenSSL](https://www.openssl.org/) -* [ImageMagick](https://imagemagick.org/) +* [pkg-config](https://freedesktop.org/wiki/Software/pkg-config/) +* [OpenSSL](https://www.openssl.org/) +* [ImageMagick](https://imagemagick.org/) To use the project [Brewfile](../../../Brewfile) file, run the following from the root of the project git clone where the file resides: diff --git a/test/multiverse/lib/multiverse/envfile.rb b/test/multiverse/lib/multiverse/envfile.rb index b16997dd03..51ada5a864 100644 --- a/test/multiverse/lib/multiverse/envfile.rb +++ b/test/multiverse/lib/multiverse/envfile.rb @@ -153,7 +153,7 @@ def unshift_rails_edge(gem_version_array = []) # NOTE: The Rails Edge version is not tested unless the Ruby version in # play is greater than or equal to (>=) the version number at the # end of the unshifted inner array - gem_version_array.unshift(["github: 'rails'", 3.0]) + gem_version_array.unshift(["github: 'rails'", 3.1]) end # are we running in a CI context intended for PR approvals? diff --git a/test/multiverse/lib/multiverse/suite.rb b/test/multiverse/lib/multiverse/suite.rb index dc4fba020d..5a3efd9005 100755 --- a/test/multiverse/lib/multiverse/suite.rb +++ b/test/multiverse/lib/multiverse/suite.rb @@ -9,9 +9,9 @@ require_relative '../../../warning_test_helper' require_relative '../../../simplecov_test_helper' +require_relative '../../../../lib/new_relic/base64' require 'rubygems' -require 'base64' require 'fileutils' require 'digest' require_relative 'bundler_patch' @@ -33,11 +33,11 @@ def initialize(directory, opts = {}) end def self.encode_options(decoded_opts) - Base64.encode64(Marshal.dump(decoded_opts)).delete("\n") + NewRelic::Base64.encode64(Marshal.dump(decoded_opts)).delete("\n") end def self.decode_options(encoded_opts) - Marshal.load(Base64.decode64(encoded_opts)) + Marshal.load(NewRelic::Base64.decode64(encoded_opts)) end def suite diff --git a/test/multiverse/suites/active_record_pg/Envfile b/test/multiverse/suites/active_record_pg/Envfile index bceecb7278..68d718c94d 100644 --- a/test/multiverse/suites/active_record_pg/Envfile +++ b/test/multiverse/suites/active_record_pg/Envfile @@ -12,7 +12,8 @@ end serialize! ACTIVERECORD_VERSIONS = [ - [nil, 2.7], + [nil, 3.1], + ['7.1.0', 2.7], ['7.0.0', 2.7], ['6.1.0', 2.5], ['6.0.0', 2.5, 2.7], diff --git a/test/multiverse/suites/active_record_pg/active_record_test.rb b/test/multiverse/suites/active_record_pg/active_record_test.rb index 9924429919..c670380fd8 100644 --- a/test/multiverse/suites/active_record_pg/active_record_test.rb +++ b/test/multiverse/suites/active_record_pg/active_record_test.rb @@ -585,6 +585,51 @@ def test_with_database_metric_name ) end + def test_metrics_for_async_find_by_sql + skip_unless_active_record_7_1_or_above + + in_web_transaction do + Order.async_find_by_sql('SELECT * FROM orders') + end + + assert_activerecord_metrics(Order, 'find') + end + + def test_metrics_for_async_count_by_sql + skip_unless_active_record_7_1_or_above + + in_web_transaction do + Order.create(:name => 'wendy') + Order.count_by_sql("SELECT * FROM orders where name = 'wendy'") + end + + assert_activerecord_metrics(Order, 'find') + end + + def test_metrics_for_async_pluck + skip_unless_active_record_7_1_or_above + + in_web_transaction do + Order.async_pluck(:id) + end + + assert_activerecord_metrics(Order, 'pluck') + end + + def test_metrics_for_async_calculation_methods + skip_unless_active_record_7_1_or_above + + in_web_transaction do + Order.async_count + Order.async_average(:id) + Order.async_minimum(:id) + Order.async_maximum(:id) + Order.async_sum(:id) + end + + assert_activerecord_metrics(Order, 'find') + end + ## helpers def adapter @@ -639,4 +684,8 @@ def assert_generic_rollup_metrics(operation) 'Datastore/all' ]) end + + def skip_unless_active_record_7_1_or_above + skip unless active_record_major_version >= 7 && active_record_minor_version >= 1 + end end diff --git a/test/multiverse/suites/active_support_broadcast_logger/Envfile b/test/multiverse/suites/active_support_broadcast_logger/Envfile index 597fe228bf..9a274d0c4f 100644 --- a/test/multiverse/suites/active_support_broadcast_logger/Envfile +++ b/test/multiverse/suites/active_support_broadcast_logger/Envfile @@ -7,7 +7,8 @@ instrumentation_methods :chain, :prepend # ActiveSupport::BroadcastLogger introduced in Rails 7.1. # Rails 7.1 is the latest version at the time of writing. ACTIVE_SUPPORT_VERSIONS = [ - [nil, 2.7] + [nil, 3.1], + ['7.1.0', 2.7] ] unshift_rails_edge(ACTIVE_SUPPORT_VERSIONS) diff --git a/test/multiverse/suites/agent_only/cross_application_tracing_test.rb b/test/multiverse/suites/agent_only/cross_application_tracing_test.rb index 7ac4a14064..eec38cb0a1 100644 --- a/test/multiverse/suites/agent_only/cross_application_tracing_test.rb +++ b/test/multiverse/suites/agent_only/cross_application_tracing_test.rb @@ -35,27 +35,27 @@ def test_cross_app_doesnt_modify_without_header end def test_cross_app_doesnt_modify_with_invalid_header - get('/', nil, {'HTTP_X_NEWRELIC_ID' => Base64.encode64('otherjunk')}) + get('/', nil, {'HTTP_X_NEWRELIC_ID' => NewRelic::Base64.encode64('otherjunk')}) refute last_response.headers['X-NewRelic-App-Data'] end def test_cross_app_writes_out_information - get('/', nil, {'HTTP_X_NEWRELIC_ID' => Base64.encode64('1#234')}) + get('/', nil, {'HTTP_X_NEWRELIC_ID' => NewRelic::Base64.encode64('1#234')}) refute_nil last_response.headers['X-NewRelic-App-Data'] assert_metrics_recorded(['ClientApplication/1#234/all']) end def test_cross_app_doesnt_modify_if_txn_is_ignored - get('/', {'transaction_name' => 'ignored_transaction'}, {'HTTP_X_NEWRELIC_ID' => Base64.encode64('1#234')}) + get('/', {'transaction_name' => 'ignored_transaction'}, {'HTTP_X_NEWRELIC_ID' => NewRelic::Base64.encode64('1#234')}) refute last_response.headers['X-NewRelic-App-Data'] end def test_cross_app_error_attaches_process_id_to_intrinsics assert_raises(RuntimeError) do - get('/', {'fail' => 'true'}, {'HTTP_X_NEWRELIC_ID' => Base64.encode64('1#234')}) + get('/', {'fail' => 'true'}, {'HTTP_X_NEWRELIC_ID' => NewRelic::Base64.encode64('1#234')}) end assert_includes attributes_for(last_traced_error, :intrinsic), :client_cross_process_id @@ -66,7 +66,7 @@ def test_cross_app_error_attaches_process_id_to_intrinsics if !test_case['outboundRequests'] if test_case['inboundPayload'] request_headers = { - 'HTTP_X_NEWRELIC_ID' => Base64.encode64('1#234'), + 'HTTP_X_NEWRELIC_ID' => NewRelic::Base64.encode64('1#234'), 'HTTP_X_NEWRELIC_TRANSACTION' => json_dump_and_encode(test_case['inboundPayload']) } else diff --git a/test/multiverse/suites/agent_only/custom_analytics_events_test.rb b/test/multiverse/suites/agent_only/custom_analytics_events_test.rb index 8898a9c029..f67dc8a8ec 100644 --- a/test/multiverse/suites/agent_only/custom_analytics_events_test.rb +++ b/test/multiverse/suites/agent_only/custom_analytics_events_test.rb @@ -79,10 +79,7 @@ def test_post_includes_metadata end def last_custom_event_post - posts = $collector.calls_for('custom_event_data') - - assert_equal(1, posts.size) - posts.first + first_call_for('custom_event_data') end def last_posted_events diff --git a/test/multiverse/suites/agent_only/encoding_handling_test.rb b/test/multiverse/suites/agent_only/encoding_handling_test.rb index efb4add481..9afda7cf6d 100644 --- a/test/multiverse/suites/agent_only/encoding_handling_test.rb +++ b/test/multiverse/suites/agent_only/encoding_handling_test.rb @@ -97,10 +97,8 @@ def assert_endpoint_received_string(endpoint, string) agent.send(:transmit_custom_event_data) agent.send(:transmit_error_event_data) agent.send(:transmit_span_event_data) - requests = $collector.calls_for(endpoint) + request = first_call_for(endpoint) - assert_equal(1, requests.size) - request = requests.first request.decode! if request.respond_to?(:decode!) assert_contains_string(request, string) diff --git a/test/multiverse/suites/agent_only/http_response_code_test.rb b/test/multiverse/suites/agent_only/http_response_code_test.rb index 2d328eb227..bd25b200ef 100644 --- a/test/multiverse/suites/agent_only/http_response_code_test.rb +++ b/test/multiverse/suites/agent_only/http_response_code_test.rb @@ -21,7 +21,7 @@ def test_request_entity_too_large # make sure the data gets thrown away after we called collector without crashing assert_metrics_not_recorded(['Custom/too_big']) - assert_equal(1, $collector.calls_for('metric_data').size) + first_call_for('metric_data') end def test_unsupported_media_type @@ -35,6 +35,6 @@ def test_unsupported_media_type # make sure the data gets thrown away after we called collector without crashing assert_metrics_not_recorded(['Custom/too_big']) - assert_equal(1, $collector.calls_for('metric_data').size) + first_call_for('metric_data') end end diff --git a/test/multiverse/suites/agent_only/key_transactions_test.rb b/test/multiverse/suites/agent_only/key_transactions_test.rb index 0b41622042..88aed5fe0b 100644 --- a/test/multiverse/suites/agent_only/key_transactions_test.rb +++ b/test/multiverse/suites/agent_only/key_transactions_test.rb @@ -81,10 +81,9 @@ def test_applied_correct_tt_threshold NewRelic::Agent.instance.send(:harvest_and_send_transaction_traces) - traces = $collector.calls_for('transaction_sample_data') + trace = first_call_for('transaction_sample_data') - assert_equal 1, traces.size - assert_equal(WEB_KEY_TXN, traces[0].metric_name) + assert_equal(WEB_KEY_TXN, trace.metric_name) end def test_applied_correct_apdex_t_to_background_key_txn @@ -109,9 +108,8 @@ def test_applied_correct_tt_threshold_to_background NewRelic::Agent.instance.send(:harvest_and_send_transaction_traces) - traces = $collector.calls_for('transaction_sample_data') + trace = first_call_for('transaction_sample_data') - assert_equal 1, traces.size - assert_equal(OTHER_KEY_TXN, traces[0].metric_name) + assert_equal(OTHER_KEY_TXN, trace.metric_name) end end diff --git a/test/multiverse/suites/agent_only/rum_instrumentation_test.rb b/test/multiverse/suites/agent_only/rum_instrumentation_test.rb index a84497b165..a10b7beda8 100644 --- a/test/multiverse/suites/agent_only/rum_instrumentation_test.rb +++ b/test/multiverse/suites/agent_only/rum_instrumentation_test.rb @@ -98,7 +98,7 @@ def test_rum_headers_are_not_injected_in_ignored_txn def assert_response_includes(*texts) texts.each do |text| assert_match(Regexp.new(text), last_response.body, - "Response missing #{text} for JS Agent instrumentation:\n #{last_response.body}") + "Response missing #{text} for JS agent instrumentation:\n #{last_response.body}") end end end diff --git a/test/multiverse/suites/agent_only/synthetics_test.rb b/test/multiverse/suites/agent_only/synthetics_test.rb index 90eaeaab3e..bfd6ca6d25 100644 --- a/test/multiverse/suites/agent_only/synthetics_test.rb +++ b/test/multiverse/suites/agent_only/synthetics_test.rb @@ -17,20 +17,18 @@ def app end def last_sent_analytics_event - calls = $collector.calls_for(:analytic_event_data) + call = first_call_for(:analytic_event_data) - assert_equal(1, calls.size) - events = calls.first.events + events = call.events assert_equal(1, events.size) events.first end def last_sent_transaction_trace - calls = $collector.calls_for(:transaction_sample_data) + call = first_call_for(:transaction_sample_data) - assert_equal(1, calls.size) - traces = calls.first.samples + traces = call.samples assert_equal(1, traces.size) traces.first diff --git a/test/multiverse/suites/async_http/Envfile b/test/multiverse/suites/async_http/Envfile index 4488de786d..70bc98dead 100644 --- a/test/multiverse/suites/async_http/Envfile +++ b/test/multiverse/suites/async_http/Envfile @@ -4,6 +4,10 @@ instrumentation_methods :chain, :prepend +suite_condition('async-http needs native C extensions') do + RUBY_PLATFORM != 'java' +end + ASYNC_HTTP_VERSIONS = [ [nil, 2.5], ['0.59.0', 2.5] diff --git a/test/multiverse/suites/async_http/async_http_instrumentation_test.rb b/test/multiverse/suites/async_http/async_http_instrumentation_test.rb index 3ea376388f..43c54bc61d 100644 --- a/test/multiverse/suites/async_http/async_http_instrumentation_test.rb +++ b/test/multiverse/suites/async_http/async_http_instrumentation_test.rb @@ -31,6 +31,7 @@ def request_and_wait(method, url, headers = nil, body = nil) internet = Async::HTTP::Internet.new resp = internet.send(method, url, headers) @read_resp = resp&.read + rescue Async::TimeoutError, EOFError ensure internet&.close end diff --git a/test/multiverse/suites/grpc/Envfile b/test/multiverse/suites/grpc/Envfile index 70cc46add1..67d059dc07 100644 --- a/test/multiverse/suites/grpc/Envfile +++ b/test/multiverse/suites/grpc/Envfile @@ -8,8 +8,9 @@ end instrumentation_methods(:chain, :prepend) +# TODO: permit testing of the nil (latest) version against Ruby 3.3+ GRPC_VERSIONS = [ - [nil, 2.6], + [nil, 2.6, 3.2], ['1.48.0', 2.5, 3.1] ] diff --git a/test/multiverse/suites/infinite_tracing/Envfile b/test/multiverse/suites/infinite_tracing/Envfile index 6fabe440f9..b69f3335a5 100644 --- a/test/multiverse/suites/infinite_tracing/Envfile +++ b/test/multiverse/suites/infinite_tracing/Envfile @@ -2,8 +2,13 @@ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. # frozen_string_literal: true +# gRPC's Ruby mainainers have broken the 'grpc' gem's compatibility with +# Ruby 2.5 by updating (grpc dependency) 'google-protobuf' with a Ruby 2.7+ +# requirement. So for Ruby 2.5 and 2.6, use a known-to-work older 'grpc' gem +# version. For Rubies >= 2.7, use '' (no version constraint - permit Bundler to +# grab the latest stable version). def grpc_version - RUBY_VERSION < '2.6.0' ? ", '1.49.1'" : '' + RUBY_VERSION < '2.7.0' ? ", '1.49.1'" : '' end gemfile <<~RB diff --git a/test/multiverse/suites/rails/Envfile b/test/multiverse/suites/rails/Envfile index 52ae1d6d3b..556b895d78 100644 --- a/test/multiverse/suites/rails/Envfile +++ b/test/multiverse/suites/rails/Envfile @@ -3,7 +3,8 @@ # frozen_string_literal: true RAILS_VERSIONS = [ - [nil, 2.7], + [nil, 3.1], + ['7.1.0', 2.7], ['7.0.4', 2.7], ['6.1.7', 2.5], ['6.0.6', 2.5, 2.7], diff --git a/test/multiverse/suites/rails/activejob_test.rb b/test/multiverse/suites/rails/activejob_test.rb index 5ecc6a769d..ccf7c7273b 100644 --- a/test/multiverse/suites/rails/activejob_test.rb +++ b/test/multiverse/suites/rails/activejob_test.rb @@ -131,6 +131,16 @@ def test_record_perform_metrics_in_web assert_metrics_recorded("#{PERFORM_PREFIX}/default") end + def test_record_perform_all_later_metrics_in_web + skip if Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new('7.1.0') + + in_web_transaction do + ActiveJob.perform_all_later(MyJob.new, MyJob.new, MyJob.new) + end + + assert_metrics_recorded("#{PERFORM_PREFIX}/default") + end + def test_record_perform_metrics_with_alternate_queue_in_web in_web_transaction do MyJobWithAlternateQueue.perform_later diff --git a/test/multiverse/suites/rails/ignore_test.rb b/test/multiverse/suites/rails/ignore_test.rb index 1e12e027ef..43fe4b2a5b 100644 --- a/test/multiverse/suites/rails/ignore_test.rb +++ b/test/multiverse/suites/rails/ignore_test.rb @@ -73,7 +73,7 @@ def test_ignored_transaction_traces_dont_leak def test_should_not_write_cat_response_headers_for_ignored_transactions get('/ignored/action_to_ignore', - headers: {'X-NewRelic-ID' => Base64.encode64('1#234')}) + headers: {'X-NewRelic-ID' => NewRelic::Base64.encode64('1#234')}) refute @response.headers['X-NewRelic-App-Data'] end diff --git a/test/multiverse/suites/rails/rails3_app/my_app.rb b/test/multiverse/suites/rails/rails3_app/my_app.rb index 561c6c88b0..c6bf971dfb 100644 --- a/test/multiverse/suites/rails/rails3_app/my_app.rb +++ b/test/multiverse/suites/rails/rails3_app/my_app.rb @@ -94,6 +94,8 @@ class MyApp < Rails::Application post '/parameter_capture', :to => 'parameter_capture#create' + get '/view_components', :to => 'view_component#index' # This app and route is used in ViewComponent tests + get '/:controller(/:action(/:id))' end diff --git a/test/multiverse/suites/rails/request_statistics_test.rb b/test/multiverse/suites/rails/request_statistics_test.rb index e8adc862c5..37332f9a06 100644 --- a/test/multiverse/suites/rails/request_statistics_test.rb +++ b/test/multiverse/suites/rails/request_statistics_test.rb @@ -102,8 +102,8 @@ def test_request_should_include_referring_guid_if_needed :'encoding_key' => "\0", :'trusted_account_ids' => [1]) do rack_env = { - 'HTTP_X_NEWRELIC_ID' => Base64.encode64('1#234'), - 'HTTP_X_NEWRELIC_TRANSACTION' => Base64.encode64('["8badf00d",1]') + 'HTTP_X_NEWRELIC_ID' => NewRelic::Base64.encode64('1#234'), + 'HTTP_X_NEWRELIC_TRANSACTION' => NewRelic::Base64.encode64('["8badf00d",1]') } get('/request_stats/cross_app_action', headers: rack_env) diff --git a/test/multiverse/suites/rails_prepend/Envfile b/test/multiverse/suites/rails_prepend/Envfile index 7fef1f93a6..5b0def4bd5 100644 --- a/test/multiverse/suites/rails_prepend/Envfile +++ b/test/multiverse/suites/rails_prepend/Envfile @@ -3,7 +3,8 @@ # frozen_string_literal: true RAILS_VERSIONS = [ - [nil, 2.7], + [nil, 3.1], + ['7.1.0', 2.7], ['7.0.0', 2.7], ['6.1.0', 2.5], ['6.0.0', 2.5, 2.7], diff --git a/test/multiverse/suites/resque/instrumentation_test.rb b/test/multiverse/suites/resque/instrumentation_test.rb index 8e4fd8959e..e4753416b2 100644 --- a/test/multiverse/suites/resque/instrumentation_test.rb +++ b/test/multiverse/suites/resque/instrumentation_test.rb @@ -107,10 +107,9 @@ def test_arguments_are_captured_on_transaction_and_span_events_when_enabled end def assert_metric_and_call_count(name, expected_call_count) - metric_data = $collector.calls_for('metric_data') + metric_data = first_call_for('metric_data') - assert_equal(1, metric_data.size, 'expected exactly one metric_data post from agent') - metric = metric_data.first.metrics.find { |m| m[0]['name'] == name } + metric = metric_data.metrics.find { |m| m[0]['name'] == name } assert(metric, "could not find metric named #{name}") diff --git a/test/multiverse/suites/sinatra/ignoring_test.rb b/test/multiverse/suites/sinatra/ignoring_test.rb index b589b914c7..9412a7542e 100644 --- a/test/multiverse/suites/sinatra/ignoring_test.rb +++ b/test/multiverse/suites/sinatra/ignoring_test.rb @@ -3,31 +3,53 @@ # frozen_string_literal: true class SinatraIgnoreTestApp < Sinatra::Base - get '/record' do request.path_info end + get '/record' do + request.path_info + end newrelic_ignore '/ignore' - get '/ignore' do request.path_info end + get '/ignore' do + request.path_info + end newrelic_ignore '/splat*' - get '/splattered' do request.path_info end + get '/splattered' do + request.path_info + end newrelic_ignore '/named/:id' - get '/named/:id' do request.path_info end + get '/named/:id' do + request.path_info + end newrelic_ignore '/v1', '/v2' - get '/v1' do request.path_info end - get '/v2' do request.path_info end - get '/v3' do request.path_info end + get '/v1' do + request.path_info + end + get '/v2' do + request.path_info + end + get '/v3' do + request.path_info + end newrelic_ignore(/\/.+regex.*/) - get '/skip_regex' do request.path_info end - get '/regex_seen' do request.path_info end + get '/skip_regex' do + request.path_info + end + get '/regex_seen' do + request.path_info + end newrelic_ignore '/ignored_erroring' - get '/ignored_erroring' do raise 'boom'; end + get '/ignored_erroring' do + raise 'boom'; + end newrelic_ignore_apdex '/no_apdex' - get '/no_apdex' do request.path_info end + get '/no_apdex' do + request.path_info + end newrelic_ignore_enduser '/no_enduser' @@ -202,7 +224,9 @@ def name_for_route(path) class SinatraIgnoreItAllApp < Sinatra::Base newrelic_ignore - get '/' do request.path_info end + get '/' do + request.path_info + end end class SinatraIgnoreItAllTest < SinatraTestCase @@ -227,7 +251,9 @@ class SinatraIgnoreApdexAndEndUserApp < Sinatra::Base newrelic_ignore_apdex newrelic_ignore_enduser - get '/' do request.path_info end + get '/' do + request.path_info + end end class SinatraIgnoreApdexAndEndUserTest < SinatraTestCase diff --git a/test/multiverse/suites/thread/thread_fiber_instrumentation_test.rb b/test/multiverse/suites/thread/thread_fiber_instrumentation_test.rb index 043c485731..b18d1cc8be 100644 --- a/test/multiverse/suites/thread/thread_fiber_instrumentation_test.rb +++ b/test/multiverse/suites/thread/thread_fiber_instrumentation_test.rb @@ -33,21 +33,22 @@ def run_nested_parent_test(async_class1, async_class2 = nil) async_class2 ||= async_class1 in_transaction do |txn| + parent_segment = NewRelic::Agent::Tracer.current_segment do_segment(name: 'Outer') do |outer_segment| async1 = async_class1.new do - fiber_segment = NewRelic::Agent::Tracer.current_segment - - assert_parent outer_segment, fiber_segment + assert_parent parent_segment, outer_segment async2 = nil do_segment(name: 'Inner') do |inner_segment| - assert_parent fiber_segment, inner_segment + assert_parent outer_segment, inner_segment async2 = async_class2.new do - assert_parent inner_segment, NewRelic::Agent::Tracer.current_segment + do_segment(name: 'InnerInner') do |inner_inner_segment| + assert_parent inner_segment, inner_inner_segment + end end end do_segment(name: 'Inner2') do |inner_2_segment| - assert_parent fiber_segment, inner_2_segment + assert_parent outer_segment, inner_2_segment end run_or_wait(async2) end @@ -55,7 +56,7 @@ def run_nested_parent_test(async_class1, async_class2 = nil) end end - assert_equal 6, harvest_span_events![0][:events_seen] + assert_equal 5, harvest_span_events![0][:events_seen] end def test_parents_thread_thread diff --git a/test/multiverse/suites/view_component/Envfile b/test/multiverse/suites/view_component/Envfile new file mode 100644 index 0000000000..10100d9ee3 --- /dev/null +++ b/test/multiverse/suites/view_component/Envfile @@ -0,0 +1,21 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +instrumentation_methods :chain, :prepend + +VIEW_COMPONENT_VERSIONS = [ + [nil, 2.7], + ['2.53.0', 2.4] +] + +def gem_list(view_component_version = nil) + <<~RB + gem 'rails' + gem 'view_component'#{view_component_version} + gem 'rack-test' + gem 'loofah', '~> 2.20.0' if RUBY_VERSION >= '2.4.0' && RUBY_VERSION < '2.5.0' + RB +end + +create_gemfiles(VIEW_COMPONENT_VERSIONS) diff --git a/test/multiverse/suites/view_component/config/newrelic.yml b/test/multiverse/suites/view_component/config/newrelic.yml new file mode 100644 index 0000000000..2c2a295aec --- /dev/null +++ b/test/multiverse/suites/view_component/config/newrelic.yml @@ -0,0 +1,19 @@ +--- +development: + error_collector: + enabled: true + apdex_t: 0.5 + monitor_mode: true + license_key: bootstrap_newrelic_admin_license_key_000 + instrumentation: + view_component: <%= $instrumentation_method %> + app_name: test + log_level: debug + host: 127.0.0.1 + api_host: 127.0.0.1 + transaction_trace: + record_sql: obfuscated + enabled: true + stack_trace_threshold: 0.5 + transaction_threshold: 1.0 + capture_params: false diff --git a/test/multiverse/suites/view_component/view_component_instrumentation_test.rb b/test/multiverse/suites/view_component/view_component_instrumentation_test.rb new file mode 100644 index 0000000000..9805313f03 --- /dev/null +++ b/test/multiverse/suites/view_component/view_component_instrumentation_test.rb @@ -0,0 +1,54 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require_relative '../rails/app' + +class ExampleComponent < ViewComponent::Base + <<~ERB + <%= @title %> + ERB + + def initialize(title:) + @title = title + end +end + +class ViewComponentController < ActionController::Base + def index + render(ExampleComponent.new(title: 'Hello World')) + end +end + +class DummyViewComponentInstrumentationClass + include NewRelic::Agent::Instrumentation::ViewComponent +end + +class ViewComponentInstrumentationTest < ActionDispatch::IntegrationTest + include MultiverseHelpers + setup_and_teardown_agent + + FAKE_CLASS = DummyViewComponentInstrumentationClass.new + + def test_metric_recorded + get('/view_components') + + assert_metrics_recorded('View/view_component/view_component_instrumentation_test.rb/ExampleComponent') + end + + def test_records_nothing_if_tracing_disabled + NewRelic::Agent.disable_all_tracing do + get('/view_components') + end + + assert_metrics_not_recorded('View/view_component/view_component_instrumentation_test.rb/ExampleComponent') + end + + def test_metric_path_falsey + assert(FAKE_CLASS.metric_path(nil), 'component') + end + + def test_metric_path_unknown_file_pattern + assert(FAKE_CLASS.metric_path('nothing_to_see_here'), 'unknown') + end +end diff --git a/test/new_relic/agent/agent_logger_test.rb b/test/new_relic/agent/agent_logger_test.rb index eb356b9ae1..9b5f6b7ff4 100644 --- a/test/new_relic/agent/agent_logger_test.rb +++ b/test/new_relic/agent/agent_logger_test.rb @@ -204,7 +204,7 @@ def test_default_format_contains_full_year logger.info('The nice thing about standards is that you have so many to choose from. -- ast') - assert_logged(/#{Date.today.strftime("%Y-%m-%d")}/) + assert_logged(/#{Date.today.strftime('%Y-%m-%d')}/) end end diff --git a/test/new_relic/agent/agent_test.rb b/test/new_relic/agent/agent_test.rb index 843dc7a565..05910c6511 100644 --- a/test/new_relic/agent/agent_test.rb +++ b/test/new_relic/agent/agent_test.rb @@ -763,7 +763,7 @@ def test_discarding_logs_message end # This test reproduces a bug in Net::HTTP related to Gzip decoding - # that can trigger the Agent's worker thread to hang on shutdown. + # that can trigger the agent's worker thread to hang on shutdown. # See also: # https://bugs.ruby-lang.org/issues/13882#note-4 # https://github.com/newrelic/newrelic-ruby-agent/issues/340 diff --git a/test/new_relic/agent/commands/thread_profiler_session_test.rb b/test/new_relic/agent/commands/thread_profiler_session_test.rb index b40b50af07..82ec9ef4ce 100644 --- a/test/new_relic/agent/commands/thread_profiler_session_test.rb +++ b/test/new_relic/agent/commands/thread_profiler_session_test.rb @@ -3,13 +3,13 @@ # frozen_string_literal: true require_relative '../../../test_helper' -require 'base64' require 'thread' require 'timeout' require 'zlib' require 'new_relic/agent/threading/backtrace_service' require 'new_relic/agent/threading/threaded_test_case' require 'new_relic/agent/commands/thread_profiler_session' +require 'new_relic/base64' module ThreadProfilerSessionTestHelpers START = { diff --git a/test/new_relic/agent/configuration/manager_test.rb b/test/new_relic/agent/configuration/manager_test.rb index 65c4010486..ad3522294a 100644 --- a/test/new_relic/agent/configuration/manager_test.rb +++ b/test/new_relic/agent/configuration/manager_test.rb @@ -530,6 +530,13 @@ def test_unsatisfied_values_stay_cached end end + def test_logger_does_not_receive_excluded_settings + log = with_array_logger(:debug) { @manager.log_config('direction', 'source') }.array.join('') + + assert_includes(log, ':app_name') + refute_includes(log, ':license_key') + end + private def assert_parsed_labels(expected) diff --git a/test/new_relic/agent/connect/request_builder_test.rb b/test/new_relic/agent/connect/request_builder_test.rb index 75e874b231..4c9ca88d22 100644 --- a/test/new_relic/agent/connect/request_builder_test.rb +++ b/test/new_relic/agent/connect/request_builder_test.rb @@ -90,4 +90,13 @@ def test_enviroment_metadata ensure ENV[key] = nil end + + def test_excluded_config_settings_are_in_fact_excluded + excluded = NewRelic::Agent::Configuration::DEFAULTS.select { |k, h| + k if h.fetch(:exclude_from_reported_settings, false) + } + + refute @request_builder.connect_payload[:settings].keys.detect { |k| excluded.include?(k) }, + "Did not expect the request builder's connect payload to include any excluded config settings" + end end diff --git a/test/new_relic/agent/javascript_instrumentor_test.rb b/test/new_relic/agent/javascript_instrumentor_test.rb index f56d318e5d..423c9238a4 100644 --- a/test/new_relic/agent/javascript_instrumentor_test.rb +++ b/test/new_relic/agent/javascript_instrumentor_test.rb @@ -4,7 +4,6 @@ require_relative '../../test_helper' require 'new_relic/agent/javascript_instrumentor' -require 'base64' class NewRelic::Agent::JavaScriptInstrumentorTest < Minitest::Test attr_reader :instrumentor diff --git a/test/new_relic/agent/monitors/cross_app_monitor_test.rb b/test/new_relic/agent/monitors/cross_app_monitor_test.rb index 3e7139d8f8..62c6a347b4 100644 --- a/test/new_relic/agent/monitors/cross_app_monitor_test.rb +++ b/test/new_relic/agent/monitors/cross_app_monitor_test.rb @@ -239,7 +239,7 @@ def when_request_runs(request = for_id(REQUEST_CROSS_APP_ID), name = 'transactio end def for_id(id) - encoded_id = id == '' ? '' : Base64.encode64(id) + encoded_id = id == '' ? '' : NewRelic::Base64.encode64(id) encoded_txn_info = json_dump_and_encode([REF_TRANSACTION_GUID, false]) return { @@ -255,7 +255,7 @@ def response_app_data def unpacked_response return nil unless response_app_data - ::JSON.load(Base64.decode64(response_app_data)) + ::JSON.load(NewRelic::Base64.decode64(response_app_data)) end end end diff --git a/test/new_relic/agent/new_relic_service_test.rb b/test/new_relic/agent/new_relic_service_test.rb index 01c9172fb3..36772c7ac2 100644 --- a/test/new_relic/agent/new_relic_service_test.rb +++ b/test/new_relic/agent/new_relic_service_test.rb @@ -275,11 +275,13 @@ def test_preconnect_never_uses_redirect_host initial_connect_log = with_array_logger(level = :debug) { @service.connect } assert_log_contains initial_connect_log, 'Sending request to localhost' + assert_log_contains initial_connect_log, Regexp.escape('license_key=***********') # If we need to reconnect, preconnect should use the locally configured collector again reconnect_log = with_array_logger(level = :debug) { @service.preconnect } assert_log_contains reconnect_log, 'Sending request to somewhere.example.com' + assert_log_contains reconnect_log, Regexp.escape('license_key=***********') end def test_preconnect_with_no_token_and_no_lasp @@ -749,7 +751,7 @@ def test_normalization_should_account_for_to_collector_array_with_nested_encodin assert_equal(expected_string, result[0]) base64_encoded_compressed_json_field = result[1] - compressed_json_field = Base64.decode64(base64_encoded_compressed_json_field) + compressed_json_field = NewRelic::Base64.decode64(base64_encoded_compressed_json_field) json_field = Zlib::Inflate.inflate(compressed_json_field) field = JSON.parse(json_field) @@ -1029,6 +1031,13 @@ def test_headers_cleared_on_subsequent_connect refute_includes header_keys, 'x-nr-metadata' end + def test_filtered_uri + base = 'https://cdpr_cp2077.com?license_key=' + filtered = @service.send(:filtered_uri, base + @service.send(:license_key)) + + assert_equal base + '***********', filtered + end + def build_stats_hash(items = {}) hash = NewRelic::Agent::StatsHash.new items.each do |key, value| diff --git a/test/new_relic/agent/obfuscator_test.rb b/test/new_relic/agent/obfuscator_test.rb index b7169f14a9..0e1b762d2d 100644 --- a/test/new_relic/agent/obfuscator_test.rb +++ b/test/new_relic/agent/obfuscator_test.rb @@ -74,8 +74,8 @@ def assert_encoded(key_length, text, expected) assert_equal(expected, output) - unoutput = obfuscator.obfuscate(Base64.decode64(output)) + unoutput = obfuscator.obfuscate(NewRelic::Base64.decode64(output)) - assert_equal Base64.encode64(text).delete("\n"), unoutput + assert_equal NewRelic::Base64.encode64(text).delete("\n"), unoutput end end diff --git a/test/new_relic/agent/tracer_test.rb b/test/new_relic/agent/tracer_test.rb index 5b7eabd91b..0d875df4ea 100644 --- a/test/new_relic/agent/tracer_test.rb +++ b/test/new_relic/agent/tracer_test.rb @@ -383,7 +383,6 @@ def test_current_segment_in_nested_threads_auto threads.each(&:join) txn.finish - assert_equal 2, txn.segments.count { |s| s.name == 'Ruby/Thread' } assert_nil Tracer.current_segment end end @@ -419,11 +418,11 @@ def test_thread_ids_included_when_enabled Thread.new { 'woof' }.join end - assert_match %r{Ruby/Thread/Thread\d+/Fiber\d+}, txn.segments.last.name + assert_match %r{/Thread\d+/Fiber\d+}, txn.segments.last.name end end - def test_thread_ids_absent_when_disabled + def test_thread_spans_absent_when_ids_disabled with_config( :'instrumentation.thread.tracing' => true, :'thread_ids_enabled' => false @@ -432,7 +431,7 @@ def test_thread_ids_absent_when_disabled Thread.new { 'woof' }.join end - assert_match %r{Ruby/Thread$}, txn.segments.last.name + assert_equal 1, txn.segments.count end end diff --git a/test/new_relic/agent/vm/mri_vm_test.rb b/test/new_relic/agent/vm/c_ruby_vm_test.rb similarity index 91% rename from test/new_relic/agent/vm/mri_vm_test.rb rename to test/new_relic/agent/vm/c_ruby_vm_test.rb index afccec49e4..213d6c3e4f 100644 --- a/test/new_relic/agent/vm/mri_vm_test.rb +++ b/test/new_relic/agent/vm/c_ruby_vm_test.rb @@ -3,16 +3,16 @@ # frozen_string_literal: true require_relative '../../../test_helper' -require 'new_relic/agent/vm/mri_vm' +require 'new_relic/agent/vm/c_ruby_vm' unless NewRelic::LanguageSupport.jruby? module NewRelic module Agent module VM - class MriVMTest < Minitest::Test + class CRubyVMTest < Minitest::Test def setup @snap = Snapshot.new - @vm = MriVM.new + @vm = CRubyVM.new end def test_gather_gc_time_sets_gc_total_time_if_gc_profiler_is_enabled diff --git a/test/new_relic/fake_collector.rb b/test/new_relic/fake_collector.rb index 3fc7e7ffbe..c373c38d47 100644 --- a/test/new_relic/fake_collector.rb +++ b/test/new_relic/fake_collector.rb @@ -227,7 +227,7 @@ def unblob(blob) def self.unblob(blob) return unless blob - JSON.load(Zlib::Inflate.inflate(Base64.decode64(blob))) + JSON.load(Zlib::Inflate.inflate(NewRelic::Base64.decode64(blob))) end end diff --git a/test/new_relic/healthy_urls_test.rb b/test/new_relic/healthy_urls_test.rb index 0c51f6610b..a444e9c3e9 100644 --- a/test/new_relic/healthy_urls_test.rb +++ b/test/new_relic/healthy_urls_test.rb @@ -36,7 +36,7 @@ class HealthyUrlsTest < Minitest::Test LICENSE mega-runner newrelic - newrelic_cmd + newrelic_rpm nrdebug Rakefile run_tests diff --git a/test/new_relic/marshalling_test_cases.rb b/test/new_relic/marshalling_test_cases.rb index c9b23ee44c..8122b7b195 100644 --- a/test/new_relic/marshalling_test_cases.rb +++ b/test/new_relic/marshalling_test_cases.rb @@ -12,10 +12,9 @@ def test_sends_metrics transmit_data - result = $collector.calls_for('metric_data') + result = first_call_for('metric_data') - assert_equal 1, result.length - assert_includes result.first.metric_names, 'Boo' + assert_includes result.metric_names, 'Boo' end def test_sends_errors @@ -25,11 +24,10 @@ def test_sends_errors transmit_data - result = $collector.calls_for('error_data') + result = first_call_for('error_data') - assert_equal 1, result.length - assert_equal 1, result.first.errors.length - assert_equal 'StandardError', result.first.errors.first.exception_class_name + assert_equal 1, result.errors.length + assert_equal 'StandardError', result.errors.first.exception_class_name end def test_sends_transaction_traces @@ -41,10 +39,9 @@ def test_sends_transaction_traces transmit_data - result = $collector.calls_for('transaction_sample_data') + result = first_call_for('transaction_sample_data') - assert_equal 1, result.length - assert_equal 'TestTransaction/do_it', result.first.metric_name + assert_equal 'TestTransaction/do_it', result.metric_name end def test_sends_transaction_events @@ -56,10 +53,9 @@ def test_sends_transaction_events transmit_event_data - result = $collector.calls_for('analytic_event_data') + result = first_call_for('analytic_event_data') - assert_equal 1, result.length - events = result.first.events + events = result.events assert_equal 1, events.length @@ -91,10 +87,9 @@ def test_sends_custom_events transmit_event_data - result = $collector.calls_for('custom_event_data') + result = first_call_for('custom_event_data') - assert_equal 1, result.length - events = result.first.events + events = result.events assert_equal 1, events.length @@ -125,10 +120,9 @@ def test_sends_error_events transmit_data - result = $collector.calls_for('error_event_data') + result = first_call_for('error_event_data') - assert_equal 1, result.length - events = result.first.events + events = result.events assert_equal 1, events.length @@ -175,18 +169,16 @@ def test_sends_log_events transmit_data - result = $collector.calls_for('log_event_data') + result = first_call_for('log_event_data') - assert_equal 1, result.length - - common = result.first.common['attributes'] + common = result.common['attributes'] refute_nil common['hostname'] # Excluding this explicitly vs classic logs-in-context to save space assert_nil common['entity.type'] - logs = result.first.logs + logs = result.logs refute_empty logs diff --git a/test/new_relic/multiverse_helpers.rb b/test/new_relic/multiverse_helpers.rb index b14a5f8f47..490dfbb8a4 100644 --- a/test/new_relic/multiverse_helpers.rb +++ b/test/new_relic/multiverse_helpers.rb @@ -193,40 +193,43 @@ def run_harvest end def single_transaction_trace_posted - posts = $collector.calls_for('transaction_sample_data') - - assert_equal 1, posts.length, 'Unexpected post count' - - transactions = posts.first.samples + post = first_call_for('transaction_sample_data') + transactions = post.samples assert_equal 1, transactions.length, 'Unexpected trace count' - transactions.first end def single_error_posted - assert_equal 1, $collector.calls_for('error_data').length - assert_equal 1, $collector.calls_for('error_data').first.errors.length + error_data = first_call_for('error_data') + + if defined?(JRUBY_VERSION) + refute_predicate error_data.errors.length, :zero?, 'Expected at least 1 error' + else + assert_equal 1, error_data.errors.length, 'Expected exactly 1 error' + end - $collector.calls_for('error_data').first.errors.first + error_data.errors.first end def single_event_posted - assert_equal 1, $collector.calls_for('analytic_event_data').length - assert_equal 1, $collector.calls_for('analytic_event_data').first.events.length + analytic_data = first_call_for('analytic_event_data') + + if defined?(JRUBY_VERSION) + refute_predicate analytic_data.events.length, :zero?, 'Expected at least 1 analytic event' + else + assert_equal 1, analytic_data.events.length, 'Expected exactly 1 analytic event' + end - $collector.calls_for('analytic_event_data').first.events.first + analytic_data.events.first end def single_metrics_post - assert_equal 1, $collector.calls_for('metric_data').length - - $collector.calls_for('metric_data').first + first_call_for('metric_data') end def single_connect_posted - assert_equal 1, $collector.calls_for(:connect).size - $collector.calls_for(:connect).first + first_call_for('connect') end def capture_js_data diff --git a/test/new_relic/newrelic_rpm_test.rb b/test/new_relic/newrelic_rpm_test.rb index abfac7b43f..a3f8639e90 100644 --- a/test/new_relic/newrelic_rpm_test.rb +++ b/test/new_relic/newrelic_rpm_test.rb @@ -15,10 +15,10 @@ def test_add_method_tracer_in_initializer_gets_traced_when_agent_initialized_aft skip unless defined?(Rails::VERSION) skip RAILS_32_SKIP_MESSAGE if Rails::VERSION::MAJOR == 3 - assert Bloodhound.newrelic_method_exists?('sniff'), + assert ::Bloodhound.newrelic_method_exists?('sniff'), 'Bloodhound#sniff not found by' \ 'NewRelic::Agent::MethodTracer::ClassMethods::AddMethodTracer.newrelic_method_exists?' - assert Bloodhound.method_traced?('sniff'), + assert ::Bloodhound.method_traced?('sniff'), 'Bloodhound#sniff not found by' \ 'NewRelic::Agent::MethodTracer::ClassMethods::AddMethodTracer.method_traced?' end diff --git a/test/new_relic/transaction_ignoring_test_cases.rb b/test/new_relic/transaction_ignoring_test_cases.rb index e1f6d3d5c6..fdf77fe441 100644 --- a/test/new_relic/transaction_ignoring_test_cases.rb +++ b/test/new_relic/transaction_ignoring_test_cases.rb @@ -41,11 +41,8 @@ def test_does_not_record_traced_errors_for_ignored_transactions NewRelic::Agent.instance.send(:harvest_and_send_errors) - posts = $collector.calls_for('error_data') - - assert_equal(1, posts.size) - - errors = posts.first.errors + post = first_call_for('error_data') + errors = post.errors assert_equal(1, errors.size) assert_equal('Buffy lives :)', errors.first.message) @@ -56,12 +53,12 @@ def test_does_not_record_transaction_trace_for_ignored_transactions trigger_transaction('accepted_transaction') NewRelic::Agent.instance.send(:harvest_and_send_transaction_traces) - assert_equal(1, $collector.calls_for('transaction_sample_data').size) + first_call_for('transaction_sample_data') trigger_transaction('ignored_transaction') NewRelic::Agent.instance.send(:harvest_and_send_transaction_traces) - assert_equal(1, $collector.calls_for('transaction_sample_data').size) + first_call_for('transaction_sample_data') end end @@ -71,11 +68,9 @@ def test_does_not_record_analytics_for_ignored_transactions NewRelic::Agent.instance.send(:harvest_and_send_analytic_event_data) - posts = $collector.calls_for('analytic_event_data') + post = first_call_for('analytic_event_data') - assert_equal(1, posts.size) - - events = posts.first.events + events = post.events assert_equal(1, events.size) assert_equal(TXN_PREFIX + 'accepted_transaction', events.first[0]['name']) @@ -87,11 +82,9 @@ def test_does_not_record_sql_traces_for_ignored_transactions NewRelic::Agent.instance.send(:harvest_and_send_slowest_sql) - posts = $collector.calls_for('sql_trace_data') - - assert_equal(1, posts.size) + post = first_call_for('sql_trace_data') - traces = posts.first.traces + traces = post.traces assert_equal(1, traces.size) diff --git a/test/performance/README.md b/test/performance/README.md index 24f128f02f..a186e01489 100644 --- a/test/performance/README.md +++ b/test/performance/README.md @@ -1,14 +1,14 @@ # Ruby Agent Performance Tests -This is a performance testing framework for the Ruby Agent. +This is a performance testing framework for the Ruby agent. ## Motivation There are two main goals driving the development of this framework: -1. Add a way for automated performance tests to be run against the Ruby Agent +1. Add a way for automated performance tests to be run against the Ruby agent and ingested into a system for tracking these results over time. -2. Provide a tool for Ruby Agent engineers to use while working on performance +2. Provide a tool for Ruby agent engineers to use while working on performance improvements. ## Examples