diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/01-feature.yml
similarity index 91%
rename from .github/ISSUE_TEMPLATE/feature.yml
rename to .github/ISSUE_TEMPLATE/01-feature.yml
index 9f331d37..a7aab2fd 100644
--- a/.github/ISSUE_TEMPLATE/feature.yml
+++ b/.github/ISSUE_TEMPLATE/01-feature.yml
@@ -1,6 +1,6 @@
name: π‘ Feature Request
-description: Create a feature request for this SDK.
-labels: 'enhancement'
+description: Propose new functionality for the SDK
+labels: ["Symfony", "Feature"]
body:
- type: markdown
attributes:
diff --git a/.github/ISSUE_TEMPLATE/02-improvement.yml b/.github/ISSUE_TEMPLATE/02-improvement.yml
new file mode 100644
index 00000000..ab64fdca
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/02-improvement.yml
@@ -0,0 +1,30 @@
+name: π‘ Improvement
+description: Propose an improvement for existing functionality of the SDK
+labels: ["Symfony", "Improvement"]
+body:
+ - type: markdown
+ attributes:
+ value: Thanks for taking the time to file a request! Please fill out this form as completely as possible.
+ - type: textarea
+ id: problem
+ attributes:
+ label: Problem Statement
+ description: A clear and concise description of what you want and what your use case is.
+ placeholder: |-
+ I want to make whirled peas, but Sentry doesn't blend.
+ validations:
+ required: true
+ - type: textarea
+ id: expected
+ attributes:
+ label: Solution Brainstorm
+ description: We know you have bright ideas to share ... share away, friend.
+ placeholder: |-
+ Add a blender to Sentry.
+ validations:
+ required: true
+ - type: markdown
+ attributes:
+ value: |-
+ ## Thanks π
+ Check our [triage docs](https://open.sentry.io/triage/) for what to expect next.
diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/03-bug.yml
similarity index 97%
rename from .github/ISSUE_TEMPLATE/bug.yml
rename to .github/ISSUE_TEMPLATE/03-bug.yml
index 7d62230e..a4a1d26b 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/03-bug.yml
@@ -1,5 +1,6 @@
name: π Bug Report
description: Tell us about something that's not working the way we (probably) intend.
+labels: ["Symfony", "Bug"]
body:
- type: dropdown
id: type
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..a0982451
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,16 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: monthly
+ - package-ecosystem: "composer"
+ directory: "/"
+ allow:
+ - dependency-name: "*phpstan*"
+ schedule:
+ interval: monthly
+ groups:
+ composer:
+ patterns:
+ - "*phpstan*"
diff --git a/.github/workflows/publish-release.yaml b/.github/workflows/publish-release.yaml
index ea19fe8d..a1be1e49 100644
--- a/.github/workflows/publish-release.yaml
+++ b/.github/workflows/publish-release.yaml
@@ -10,20 +10,31 @@ on:
description: Force a release even when there are release-blockers (optional)
required: false
+permissions:
+ contents: read
+
jobs:
release:
runs-on: ubuntu-latest
name: Release version
steps:
- - uses: actions/checkout@v2
+ - name: Get auth token
+ id: token
+ uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # v1.11.5
+ with:
+ app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }}
+ private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }}
+
+ - name: Checkout
+ uses: actions/checkout@v4
with:
- token: ${{ secrets.GH_RELEASE_PAT }}
+ token: ${{ steps.token.outputs.token }}
fetch-depth: 0
- name: Prepare release
uses: getsentry/action-prepare-release@v1
env:
- GITHUB_TOKEN: ${{ secrets.GH_RELEASE_PAT }}
+ GITHUB_TOKEN: ${{ steps.token.outputs.token }}
with:
version: ${{ github.event.inputs.version }}
force: ${{ github.event.inputs.force }}
diff --git a/.github/workflows/static-analysis.yaml b/.github/workflows/static-analysis.yaml
index bcf4466e..91c0cee4 100644
--- a/.github/workflows/static-analysis.yaml
+++ b/.github/workflows/static-analysis.yaml
@@ -8,57 +8,66 @@ on:
- develop
- release/**
+permissions:
+ contents: read
+
jobs:
php-cs-fixer:
name: PHP-CS-Fixer
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
- php-version: '8.1'
+ php-version: '8.3'
- name: Install dependencies
- run: composer update --no-progress --no-interaction --prefer-dist
+ uses: ramsey/composer-install@v3
+ with:
+ composer-options: --prefer-dist
- name: Run script
- run: composer phpcs
+ run: vendor/bin/php-cs-fixer fix --verbose --diff --dry-run
phpstan:
name: PHPStan
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
- php-version: '8.2'
+ php-version: '8.3'
- name: Install dependencies
- run: composer update --no-progress --no-interaction --prefer-dist
+ uses: ramsey/composer-install@v3
+ with:
+ composer-options: --prefer-dist
- name: Run script
- run: composer phpstan
+ run: vendor/bin/phpstan analyse
psalm:
name: Psalm
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
- php-version: '8.2'
+ php-version: '8.3'
- name: Install dependencies
- run: composer update --no-progress --no-interaction --prefer-dist
+ uses: ramsey/composer-install@v3
+ with:
+ composer-options: --prefer-dist
- name: Run script
- run: composer psalm -- --php-version=8.0
+ run: vendor/bin/psalm --php-version=8.0
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 16c124ff..8c3dabe9 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -1,13 +1,15 @@
-name: Continuous Integration
+name: CI
on:
- pull_request: null
+ pull_request:
push:
branches:
- master
- - develop
- release/**
+permissions:
+ contents: read
+
jobs:
tests:
name: Tests
@@ -24,10 +26,13 @@ jobs:
- '8.0'
- '8.1'
- '8.2'
+ - '8.3'
+ - '8.4'
symfony-version:
- 4.4.*
- 5.*
- 6.*
+ - 7.*
dependencies:
- highest
exclude:
@@ -37,6 +42,18 @@ jobs:
symfony-version: 6.*
- php: '7.4'
symfony-version: 6.*
+ - php: '7.2'
+ symfony-version: 7.*
+ - php: '7.3'
+ symfony-version: 7.*
+ - php: '7.4'
+ symfony-version: 7.*
+ - php: '8.0'
+ symfony-version: 7.*
+ - php: '8.1'
+ symfony-version: 7.*
+ - php: '8.4'
+ symfony-version: 4.4.*
include:
- php: '7.2'
symfony-version: 4.4.*
@@ -47,10 +64,13 @@ jobs:
- php: '8.0'
symfony-version: 6.*
dependencies: lowest
+ - php: '8.2'
+ symfony-version: 7.*
+ dependencies: lowest
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
with:
fetch-depth: 2
@@ -64,12 +84,12 @@ jobs:
- name: Setup Problem Matchers for PHPUnit
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- - name: Update PHPUnit
- run: composer require --dev phpunit/phpunit ^9.3.9 --no-update
- if: matrix.php == '8.0' && matrix.dependencies == 'lowest'
+ # These dependencies are not used running the tests but can cause deprecation warnings so we remove them before running the tests
+ - name: Remove unused dependencies
+ run: composer remove vimeo/psalm phpstan/phpstan friendsofphp/php-cs-fixer --dev --no-interaction --no-update
- name: Install dependencies
- uses: ramsey/composer-install@v1
+ uses: ramsey/composer-install@v3
with:
dependency-versions: ${{ matrix.dependencies }}
composer-options: --prefer-dist
@@ -78,9 +98,10 @@ jobs:
run: vendor/bin/phpunit --coverage-clover=build/coverage-report.xml
- name: Upload code coverage
- uses: codecov/codecov-action@v1
+ uses: codecov/codecov-action@v5
with:
file: build/coverage-report.xml
+ token: ${{ secrets.CODECOV_TOKEN }}
missing-optional-packages-tests:
name: Tests without optional packages
@@ -99,14 +120,12 @@ jobs:
- php: '8.0'
dependencies: lowest
symfony-version: 4.4.*
- - php: '8.1'
- dependencies: highest
- - php: '8.2'
+ - php: '8.4'
dependencies: highest
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -122,7 +141,7 @@ jobs:
run: composer remove doctrine/dbal doctrine/doctrine-bundle symfony/messenger symfony/twig-bundle symfony/cache symfony/http-client --dev --no-update
- name: Install dependencies
- uses: ramsey/composer-install@v1
+ uses: ramsey/composer-install@v3
with:
dependency-versions: ${{ matrix.dependencies }}
composer-options: --prefer-dist
@@ -131,6 +150,7 @@ jobs:
run: vendor/bin/phpunit --coverage-clover=build/coverage-report.xml
- name: Upload code coverage
- uses: codecov/codecov-action@v1
+ uses: codecov/codecov-action@v5
with:
file: build/coverage-report.xml
+ token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index 7136604b..3f0de29f 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -14,10 +14,14 @@
'imports_order' => ['class', 'function', 'const'],
],
'declare_strict_types' => true,
+ 'get_class_to_class_keyword' => false,
'random_api_migration' => true,
'yoda_style' => true,
'self_accessor' => false,
+ 'nullable_type_declaration_for_default_null_value' => false,
+ 'no_null_property_initialization' => false,
'phpdoc_no_useless_inheritdoc' => false,
+ 'no_superfluous_phpdoc_tags' => false,
'phpdoc_to_comment' => false,
'phpdoc_align' => [
'tags' => ['param', 'return', 'throws', 'type', 'var'],
@@ -27,6 +31,10 @@
'method' => 'multi',
'property' => 'multi',
],
+ 'trailing_comma_in_multiline' => [
+ 'after_heredoc' => false,
+ 'elements' => ['arrays'],
+ ],
])
->setFinder(
PhpCsFixer\Finder::create()
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 80b5b361..db5b004b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,410 +1,168 @@
-# Changelog
+# CHANGELOG
-## 4.10.0
+## 5.2.0
-The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v4.10.0.
+The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v5.2.0.
### Features
-- Tracing without Performance [(#742)](https://github.com/getsentry/sentry-symfony/pull/742)
+- Allow to configure the logger via the `sentry.yaml` configuration file [(#899)](https://github.com/getsentry/sentry-symfony/pull/899)
- The SDK will now continue a trace from incoming HTTP requests, even if performance is not enabled.
- To continue a trace outward, you may attach the Sentry tracing headers to any HTTP client request.
- You can fetch the required header values by calling \Sentry\getBaggage() and \Sentry\getTraceparent().
-
-- Add `ignore_exceptions` and `ignore_transactions` options [(#724)](https://github.com/getsentry/sentry-symfony/pull/724)
-
-### Misc
-
-- Improve setting logged-in users on the scope [(#720)](https://github.com/getsentry/sentry-symfony/pull/720)
-- Move DB span tags to span data [(#743)](https://github.com/getsentry/sentry-symfony/pull/743)
-- Set the span status when tracing an HTTP client request [(#748)](https://github.com/getsentry/sentry-symfony/pull/748)
-
-## 4.9.2
-
-The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v4.9.2.
+ ```yaml
+ sentry:
+ dsn: "%env(SENTRY_DSN)%"
+ options:
+ logger: "sentry.logger"
+
+ services:
+ sentry.logger:
+ class: 'Sentry\Logger\DebugFileLogger'
+ arguments:
+ $filePath: '../../var/log/sentry.log'
+ ```
### Bug Fixes
-- We decided to revert two previous PRs that aimed to remove deprecation warnings during test runs [(#736)](https://github.com/getsentry/sentry-symfony/pull/736)
+- Fixed updating the user context when a route is marked as stateless [(#910)](https://github.com/getsentry/sentry-symfony/pull/910)
- - Revert: Add a new Doctrine DBAL tracing driver that does not implement the deprecated `VersionAwarePlatformDriver` [(#723)](https://github.com/getsentry/sentry-symfony/pull/723)
- - Revert: Fix a regression in `TracingDriverForV32` by adding `VersionAwarePlatformDriver::createDatabasePlatformForVersion` [(#731)](https://github.com/getsentry/sentry-symfony/pull/731)
-
-We are sorry for the inconvenience caused by these changes.
-
-## 4.9.1
-
-The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v4.9.1.
-
-### Bug Fixes
+### Misc
-- Fix a regression in `TracingDriverForV32` by adding `VersionAwarePlatformDriver::createDatabasePlatformForVersion` [(#731)](https://github.com/getsentry/sentry-symfony/pull/731)
+- Remove `symfony/security-core` and `symfony/security-http` as dependencies [(#912)](https://github.com/getsentry/sentry-symfony/pull/912)
-## 4.9.0
+## 5.1.0
-The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v4.9.0.
+The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v5.1.0.
### Features
-- Add a new Doctrine DBAL tracing driver that does not implement the deprecated `VersionAwarePlatformDriver` [(#723)](https://github.com/getsentry/sentry-symfony/pull/723)
-
- The driver is automatically picked if `doctrine/dbal` version `3.2.0` or higher is installed.
+- The SDK was updated to support PHP 8.4 [(#893)](https://github.com/getsentry/sentry-symfony/pull/893)
+- Set the status for CLI command transactions based on the exit code [(#891)](https://github.com/getsentry/sentry-symfony/pull/891)
### Bug Fixes
-- Fix config type of `http_connect_timeout`and `http_timeout` options [(#721)](https://github.com/getsentry/sentry-symfony/pull/721)
+- Fix including request data on transactions [(#879)](https://github.com/getsentry/sentry-symfony/pull/879)
-### Misc
+## 5.0.1
-- Bump the underlying PHP SDK to version `^3.19` [(#725)](https://github.com/getsentry/sentry-symfony/pull/725)
+The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v5.0.1.
-## 4.8.0
+### Bug Fixes
-The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v4.8.0.
+- Add missing `setCallbackWrapper` method to `TraceableCacheAdapterTrait` [(#841)](https://github.com/getsentry/sentry-symfony/pull/841)
+- Fix detection of the `symfony/http-client` being installed [(#858)](https://github.com/getsentry/sentry-symfony/pull/858)
-### Features
+## 5.0.0
-- Set cache keys as span descriptions [(#677)](https://github.com/getsentry/sentry-symfony/pull/677)
+The Sentry SDK team is thrilled to announce the immediate availability of Sentry Symfony SDK v5.0.0.
- To better identify the source of a cache operation, we now set the cache key as the description of `cache` op spans.
+### Breaking Change
-### Bug Fixes
+Please refer to the [UPGRADE-5.0.md](https://github.com/getsentry/sentry-symfony/blob/master/UPGRADE-5.0.md) guide for a complete list of breaking changes.
-- Add direct dependency for `guzzlehttp/psr7` [(#708)](https://github.com/getsentry/sentry-symfony/pull/708)
-- Drop `kernel.build_dir` param below Symfony 5.2 [(#711)](https://github.com/getsentry/sentry-symfony/pull/711)
+This version adds support for the underlying [Sentry PHP SDK v4.0](https://github.com/getsentry/sentry-php).
+Please refer to the PHP SDK [sentry-php/UPGRADE-4.0.md](https://github.com/getsentry/sentry-php/blob/master/UPGRADE-4.0.md) guide for a complete list of breaking changes.
-## 4.7.0
+- This version exclusively uses the [envelope endpoint](https://develop.sentry.dev/sdk/envelopes/) to send event data to Sentry.
-The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v4.7.0.
+ If you are using [sentry.io](https://sentry.io), no action is needed.
+ If you are using an on-premise/self-hosted installation of Sentry, the minimum requirement is now version `>= v20.6.0`.
-### Features
+- You need to have `ext-curl` installed to use the SDK.
-- Add `profiles_sample_rate` config option [(#698)](https://github.com/getsentry/sentry-symfony/pull/698)
+- The `IgnoreErrorsIntegration` integration was removed. Use the `ignore_exceptions` option instead.
+ Previously, both `Symfony\Component\ErrorHandler\Error\FatalError` and `Symfony\Component\Debug\Exception\FatalErrorException` were ignored by default.
+ To continue ignoring these exceptions, make the following changes to the config file:
- With this new config option, you can now use our new profiling feature in Symfony as well.
- Please consult https://github.com/getsentry/sentry-php/releases/3.15.0 for setup instructions.
+ ```yaml
+ // config/packages/sentry.yaml
-## 4.6.0
+ sentry:
+ options:
+ ignore_exceptions:
+ - 'Symfony\Component\ErrorHandler\Error\FatalError'
+ - 'Symfony\Component\Debug\Exception\FatalErrorException'
+ ```
-The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v4.6.0.
-This release contains a colorful bouquet of new features.
+ This option performs an [`is_a`](https://www.php.net/manual/en/function.is-a.php) check now, so you can also ignore more generic exceptions.
### Features
-- Report exceptions to Sentry as unhandled by default [(#674)](https://github.com/getsentry/sentry-symfony/pull/674)
+- Add support for Sentry Developer Metrics [(#1619)](https://github.com/getsentry/sentry-php/pull/1619)
- All unhandled exceptions will now be marked as `handled: false`. You can query for such events on the issues list page,
- by searching for `error.handled:false`.
-
-- Exceptions from messages which will be retried are sent to Sentry as handled [(#674)](https://github.com/getsentry/sentry-symfony/pull/674)
+ ```php
+ use function Sentry\metrics;
- All unhandled exceptions happening on the message bus will now be unpacked and reported individually.
- The `WorkerMessageFailedEvent::willRetry` property is used to determine the `handled` value of the event sent to Sentry.
+ // Add 4 to a counter named hits
+ metrics()->increment(key: 'hits', value: 4);
-- Add `register_error_handler` config option [(#687)](https://github.com/getsentry/sentry-symfony/pull/687)
+ // Add 25 to a distribution named response_time with unit milliseconds
+ metrics()->distribution(key: 'response_time', value: 25, unit: MetricsUnit::millisecond());
- With this option, you can disable the global error and exception handlers of the base PHP SDK.
- If disabled, only events logged by Monolog will be sent to Sentry.
+ // Add 2 to gauge named parallel_requests, tagged with type: "a"
+ metrics()->gauge(key: 'parallel_requests', value: 2, tags: ['type': 'a']);
- ```yaml
- sentry:
- dsn: '%env(SENTRY_DSN)%'
- register_error_listener: false
- register_error_handler: false
-
- monolog:
- handlers:
- sentry:
- type: sentry
- level: !php/const Monolog\Logger::ERROR
- hub_id: Sentry\State\HubInterface
+ // Add a user's email to a set named users.sessions, tagged with role: "admin"
+ metrics()->set('users.sessions', 'jane.doe@example.com', null, ['role' => User::admin()]);
```
-- Add `before_send_transaction` [(#691)](https://github.com/getsentry/sentry-symfony/pull/691)
+ Metrics are automatically sent to Sentry at the end of a request, hooking into Symfony's `kernel.terminate` event.
- Similar to `before_send`, you can now apply additional logic for `transaction` events.
- You can mutate the `transaction` event before it is sent to Sentry. If your callback returns `null`,
- the event is dropped.
-
- ```yaml
- sentry:
- options:
- before_send_transaction: 'sentry.callback.before_send_transaction'
-
- services:
- sentry.callback.before_send_transaction:
- class: 'App\Service\Sentry'
- factory: [ '@App\Service\Sentry', 'getBeforeSendTransaction' ]
- ```
+- Add new fluent APIs [(#1601)](https://github.com/getsentry/sentry-php/pull/1601)
```php
- setName('GET /example');
+ $transactionContext->setOp('http.server');
+
+ // After
+ $transactionContext = (new TransactionContext())
+ ->setName('GET /example');
+ ->setOp('http.server');
```
-- Use the `_route` attribute as the transaction name [(#692)](https://github.com/getsentry/sentry-symfony/pull/692)
-
- If you're using named routes, the SDK will default to use this attribute as the transaction name.
- With this change, you should be able to see a full list of your transactions on the performance page,
- instead of `<< unparameterized >>`.
-
- You may need to update your starred transactions as well as your dashboards due to this change.
-
-### Bug Fixes
-
-- Sanatize HTTP client spans [(#690)](https://github.com/getsentry/sentry-symfony/pull/690)
-
-## 4.5.0 (2022-11-28)
-
-- Symfony version 3.4 is no longer supported
- - Drop Symfony support below 4.4 (#643)
-- feat: Add support for tracing of Symfony HTTP client requests (#606)
- - feat: Add support for HTTP client baggage propagation (#663)
- - ref: Add proper HTTP client span descriptions (#680)
-- feat: Support logging the impersonator user, if any (#647)
-- ref: Use a constant for the SDK version (#662)
-
-## 4.4.0 (2022-10-20)
-
-- feat: Add support for Dynamic Sampling (#665)
-
-## 4.3.1 (2022-10-10)
-
-- fix: Update span ops (#655)
-
-## 4.3.0 (2022-05-30)
-
-- Fix compatibility issue with Symfony `>= 6.1.0` (#635)
-- Add `TracingDriverConnectionInterface::getNativeConnection()` method to get the original driver connection (#597)
-- Add `options.http_timeout` and `options.http_connect_timeout` configuration options (#593)
-
-## 4.2.10 (2022-05-17)
-
-- Fix compatibility issue with Twig >= 3.4.0 (#628)
-
-## 4.2.9 (2022-05-03)
-
-- Fix deprecation notice thrown when instrumenting the `PDOStatement::bindParam()` method and passing `$length = null` on DBAL `2.x` (#613)
-
-## 4.2.8 (2022-03-31)
-
-- Fix compatibility issue with Doctrine Bundle `>= 2.6.0` (#608)
-
-## 4.2.7 (2022-02-18)
-
-- Fix deprecation notice thrown when instrumenting the `PDOStatement::bindParam()` method and passing `$length = null` (#586)
-
-## 4.2.6 (2022-01-10)
-
-- Add support for `symfony/cache-contracts` package version `3.x` (#588)
-
-## 4.2.5 (2021-12-13)
-
-- Add support for Symfony 6 (#566)
-- Fix fatal errors logged twice on Symfony `3.4` (#570)
-
-## 4.2.4 (2021-10-20)
-
-- Add return typehints to the methods of the `SentryExtension` class to prepare for Symfony 6 (#563)
-- Fix setting the IP address on the user context when it's not available (#565)
-- Fix wrong method existence check in `TracingDriverConnection::errorCode()` (#568)
-- Fix decoration of the Doctrine DBAL connection when it implemented the `ServerInfoAwareConnection` interface (#567)
-
-## 4.2.3 (2021-09-21)
-
-- Fix: Test if `TracingStatement` exists before attempting to create the class alias, otherwise it breaks when opcache is enabled. (#552)
-- Fix: Pass logger from `logger` config option to `TransportFactory` (#555)
-- Improve the compatibility layer with Doctrine DBAL to avoid deprecations notices (#553)
-
-## 4.2.2 (2021-08-30)
+- Simplify the breadcrumb API [(#1603)](https://github.com/getsentry/sentry-php/pull/1603)
-- Fix missing instrumentation of the `Statement::execute()` method of Doctrine DBAL (#548)
-
-## 4.2.1 (2021-08-24)
-
-- Fix return type for `TracingDriver::getDatabase()` method (#541)
-- Avoid throwing exception from the `TraceableCacheAdapterTrait::prune()` and `TraceableCacheAdapterTrait::reset()` methods when the decorated adapter does not implement the respective interfaces (#543)
-
-## 4.2.0 (2021-08-12)
-
-- Log the bus name, receiver name and message class name as event tags when using Symfony Messenger (#492)
-- Make the transport factory configurable in the bundle's config (#504)
-- Add the `sentry_trace_meta()` Twig function to print the `sentry-trace` HTML meta tag (#510)
-- Make the list of commands for which distributed tracing is active configurable (#515)
-- Introduce `TracingDriverConnection::getWrappedConnection()` (#536)
-- Add the `logger` config option to ease setting a PSR-3 logger to debug the SDK (#538)
-- Bump requirement for DBAL tracing to `^2.13|^3`; simplify the DBAL tracing feature (#527)
-
-## 4.1.4 (2021-06-18)
-
-- Fix decoration of cache adapters inheriting parent service (#525)
-- Fix extraction of the username of the logged-in user in Symfony `5.3` (#518)
-
-## 4.1.3 (2021-05-31)
-
-- Fix missing require of the `symfony/cache-contracts` package (#506)
-
-## 4.1.2 (2021-05-17)
-
-- Fix the check of the existence of the `CacheItem` class while attempting to enable the cache instrumentation (#501)
-
-## 4.1.1 (2021-05-10)
-
-- Fix the conditions to automatically enable the cache instrumentation when possible (#487)
-- Fix deprecations triggered by Symfony `5.3` (#489)
-- Fix fatal error when the `SERVER_PROTOCOL` header is missing (#495)
-
-## 4.1.0 (2021-04-19)
-
-- Avoid failures when the `RequestFetcher` fails to translate the `Request` (#472)
-- Add support for distributed tracing of Symfony request events (#423)
-- Add support for distributed tracing of Twig template rendering (#430)
-- Add support for distributed tracing of SQL queries while using Doctrine DBAL (#426)
-- Add support for distributed tracing when running a console command (#455)
-- Add support for distributed tracing of cache pools (#477)
-- Add the full CLI command string to the extra context (#352)
-- Deprecate the `Sentry\SentryBundle\EventListener\ConsoleCommandListener` class in favor of its parent class `Sentry\SentryBundle\EventListener\ConsoleListener` (#429)
-- Lower the required version of `symfony/psr-http-message-bridge` to allow installing it on a project that uses Symfony `3.4.x` components only (#480)
-
-## 4.0.3 (2021-03-03)
-
-- Fix regression from #454 for `null` value on DSN not disabling Sentry (#457)
-
-## 4.0.2 (2021-03-03)
-
-- Add `kernel.project_dir` to `prefixes` default value to trim paths to the project root (#434)
-- Fix `null`, `false` or empty value not disabling Sentry (#454)
-
-## 4.0.1 (2021-01-26)
-
-- Add missing `capture-soft-fails` option to the XSD schema for the XML config (#417)
-- Fix regression that send PII even when the `send_default_pii` option is off (#425)
-- Fix capture of console errors when the `register_error_listener` option is disabled (#427)
-
-## 4.0.0 (2021-01-19)
-
-**Breaking Change**: This version uses the [envelope endpoint](https://develop.sentry.dev/sdk/envelopes/). If you are
-using an on-premise installation it requires Sentry version `>= v20.6.0` to work. If you are using
-[sentry.io](https://sentry.io) nothing will change and no action is needed.
-
-- Enable back all error listeners from base SDK integration (#322)
-- Add `options.traces_sampler` and `options.traces_sample_rate` configuration options (#385)
-- [BC BREAK] Remove the `options.project_root` configuration option. Instead of setting it, use a combination of `options.in_app_include` and `options.in_app_exclude` (#385)
-- [BC BREAK] Remove the `options.excluded_exceptions` configuration option. Instead of setting it, configure the `IgnoreErrorsIntegration` integration (#385)
-- [BC BREAK] Refactorize the `ConsoleCommandListener`, `ErrorListener`, `RequestListener` and `SubRequestListener` event listeners (#387)
-- Registered the CLI commands as lazy services (#373)
-- [BC BREAK] Refactorize the configuration tree and the definitions of some container services (#401)
-- Support the XML format for the bundle configuration (#401)
-- PHP 8 support (#399, thanks to @Yozhef)
-- Retrieve the request from the `RequestStack` when using the `RequestIntegration` integration (#361)
-- Reorganize the folder structure and change CS standard (#405)
-- [BC BREAK] Remove the `monolog` configuration option. Instead, register the service manually (#406)
-- [BC BREAK] Remove the `listener_priorities` configuration option. Instead, use a compiler pass to change the priority of the listeners (#407)
-- Prefer usage of the existing `Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface` service for the `RequestFetcher` class (#409)
-- [BC BREAK] Change the priorities of the `RequestListener` and `SubRequestListener` listeners (#414)
-
-## 3.5.3 (2020-10-13)
-
-- Refactors and fixes class aliases for more robustness (#315 #359, thanks to @guilliamxavier)
-
-## 3.5.2 (2020-07-08)
-
-- Use `jean85/pretty-package-versions` `^1.5` to leverage the new `getRootPackageVersion` method (c8799ac)
-- Fix support for PHP preloading (#354, thanks to @annuh)
-- Fix `capture_soft_fails: false` option for the Messenger (#353)
-
-## 3.5.1 (2020-05-07)
-
-- Capture events using the `Hub` in the `MessengerListener` to avoid loosing `Scope` data (#339, thanks to @sh41)
-- Capture multiple events if multiple exceptions are generated in a Messenger Worker context (#340, thanks to @emarref)
-
-## 3.5.0 (2020-05-04)
-
-- Capture and flush messages in a Messenger Worker context (#326, thanks to @emarref)
-- Support Composer 2 (#335)
-- Avoid issues with dependency lower bound, fix #331 (#335)
-
-## 3.4.4 (2020-03-16)
-
-- Improve `release` option default value (#325)
-
-## 3.4.3 (2020-02-03)
-
-- Change default of `in_app_include` to empty, due to getsentry/sentry-php#958 (#311)
-- Improve class_alias robustness (#315)
-
-## 3.4.2 (2020-01-29)
-
-- Remove space from classname used with `class_alias` (#313)
-
-## 3.4.1 (2020-01-24)
-
-- Fix issue due to usage of `class_alias` to fix deprecations, which could break BC layers of third party packages (#309, thanks to @scheb)
-
-## 3.4.0 (2020-01-20)
-
-- Add support for `sentry/sentry` 2.3 (#298)
-- Drop support for `sentry/sentry` < 2.3 (#298)
-- Add support to `in_app_include` client option (#298)
-- Remap `excluded_exceptions` option to use the new `IgnoreErrorsIntegration` (#298)
-
-## 3.3.2 (2020-01-16)
-
-- Fix issue with exception listener under Symfony 4.3 (#301)
-
-## 3.3.1 (2020-01-14)
-
-- Fixed Release
-
-## 3.3.0 (2020-01-14)
-
-- Add support for Symfony 5.0 (#266, thanks to @Big-Shark)
-- Drop support for Symfony < 3.4 (#277)
-- Add default value for the `release` option, using the detected root package version (#291 #292, thanks to @Ocramius)
-
-## 3.2.1 (2019-12-19)
-
-- Fix handling of command with no name on `ConsoleListener` (#261)
-- Remove check by AuthorizationChecker in `RequestListener` (#264)
-- Fixed undefined variable in `RequestListener` (#263)
-
-## 3.2.0 (2019-10-04)
-
-- Add forward compatibility with Symfony 5 (#235, thanks to @garak)
-- Fix Hub initialization for `ErrorListener` (#243, thanks to @teohhanhui)
-- Fix compatibility with sentry/sentry 2.2+ (#244)
-- Add support for `class_serializers` option (#245)
-- Add support for `max_request_body_size` option (#249)
-- Add option to disable the error listener completely (#247, thanks to @HypeMC)
-- Add options to register the Monolog Handler (#247, thanks to @HypeMC)
-
-## 3.1.0 (2019-07-02)
+ ```php
+ // Before
+ \Sentry\addBreadcrumb(
+ new \Sentry\Breadcrumb(
+ \Sentry\Breadcrumb::LEVEL_INFO,
+ \Sentry\Breadcrumb::TYPE_DEFAULT,
+ 'auth', // category
+ 'User authenticated', // message (optional)
+ ['user_id' => $userId] // data (optional)
+ )
+ );
+
+ // After
+ \Sentry\addBreadcrumb(
+ category: 'auth',
+ message: 'User authenticated', // optional
+ metadata: ['user_id' => $userId], // optional
+ level: Breadcrumb::LEVEL_INFO, // set by default
+ type: Breadcrumb::TYPE_DEFAULT, // set by default
+ );
+ ```
-- Add support for Symfony 2.8 (#233, thanks to @nocive)
-- Fix handling of ESI requests (#213, thanks to @franmomu)
+- New default cURL HTTP client [(#1589)](https://github.com/getsentry/sentry-php/pull/1589)
-## 3.0.0 (2019-05-10)
+ The SDK now ships with its own HTTP client based on cURL. A few new options were added.
-- Add the `sentry:test` command, to test if the Sentry SDK is functioning properly.
+ ```yaml
+ // config/packages/sentry.yaml
-## 3.0.0-beta2 (2019-03-22)
+ sentry:
+ options:
+ - http_proxy_authentication: 'username:password' // user name and password to use for proxy authentication
+ - http_ssl_verify_peer: false // default true, verify the peer's SSL certificate
+ - http_compression: false // default true, http request body compression
+ ```
-- Disable Sentry's ErrorHandler, and report all errors using Symfony's events (#204)
+ To use a different client, you may use the `http_client` option.
+ To use a different transport, you may use the `transport` option. A custom transport must implement the `TransportInterface`.
+ If you use the `transport` option, the `http_client` option has no effect.
-## 3.0.0-beta1 (2019-03-06)
+### Misc
-The 3.0 major release has multiple breaking changes. The most notable one is the upgrade to the 2.0 base SDK client.
-Refer to the [UPGRADE-3.0.md](https://github.com/getsentry/sentry-symfony/blob/master/UPGRADE-3.0.md) document for a
-detailed explanation.
+- The abandoned package `php-http/message-factory` was removed.
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 4f116379..00000000
--- a/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-.PHONY: pre-commit-check
-
-cs:
- vendor/bin/php-cs-fixer fix --verbose
-
-cs-dry-run:
- vendor/bin/php-cs-fixer fix --verbose --dry-run
-
-phpstan:
- vendor/bin/phpstan analyze
-
-psalm:
- vendor/bin/psalm
-
-test:
- vendor/bin/phpunit
-
-pre-commit-check: cs phpstan psalm test
-
-setup-git:
- git config branch.autosetuprebase always
diff --git a/README.md b/README.md
index 780d82e0..87b0db9e 100644
--- a/README.md
+++ b/README.md
@@ -13,157 +13,41 @@ _Bad software is everywhere, and we're tired of it. Sentry is on a mission to he
[](https://packagist.org/packages/sentry/sentry-symfony)
[](https://packagist.org/packages/sentry/sentry-symfony)
- [![Coverage Status][Master Code Coverage Image]][Master Code Coverage]
+[](https://github.com/getsentry/sentry-symfony/actions/workflows/tests.yaml)
+[![Coverage Status][Master Code Coverage Image]][Master Code Coverage]
[](https://discord.gg/cWnMQeA)
This is the official Symfony SDK for [Sentry](https://getsentry.com/).
## Getting Started
-Using this `sentry-symfony` SDK provides you with the following benefits:
-
- * Quickly integrate and configure Sentry for your Symfony app
- * Out of the box, each event will contain the following data by default
- - The currently authenticated user
- - The Symfony environment
-
### Install
-To install the SDK you will need to be using [Composer]([https://getcomposer.org/)
-in your project. To install it please see the [docs](https://getcomposer.org/download/).
+Install the SDK using [Composer](https://getcomposer.org/).
```bash
composer require sentry/sentry-symfony
```
-If you're using the [Symfony Flex](https://symfony.com/doc/current/setup/flex.html) Composer plugin, you might encounter a message similar to this:
-
-```
-The recipe for this package comes from the "contrib" repository, which is open to community contributions.
-Review the recipe at https://github.com/symfony/recipes-contrib/tree/master/sentry/sentry-symfony/3.0
-
-Do you want to execute this recipe?
-```
-
-Just type `y`, press return, and the procedure will continue.
-
-**Caution:** Due to a bug in the [`SensioFrameworkExtra`](https://github.com/sensiolabs/SensioFrameworkExtraBundle) bundle, affecting version 6.0 and below, you might run into a missing `Nyholm\Psr7\Factory\Psr17Factory::class` error while executing the commands mentioned above.
-If you are not using the PSR-7 bridge, you can work around this issue by changing the configuration of the bundle as follows:
-
-```yaml
-sensio_framework_extra:
- psr_message:
- enabled: false
-```
-
-For more details about the issue see https://github.com/sensiolabs/SensioFrameworkExtraBundle/pull/710.
-
-### Enable the Bundle
-
-If you installed the package using the Flex recipe, the bundle will be automatically enabled. Otherwise, enable it by adding it to the list
-of registered bundles in the `Kernel.php` file of your project:
-
-```php
-class AppKernel extends \Symfony\Component\HttpKernel\Kernel
-{
- public function registerBundles(): array
- {
- return [
- // ...
- new \Sentry\SentryBundle\SentryBundle(),
- ];
- }
-
- // ...
-}
-```
-
-The bundle will be enabled in all environments by default.
-To enable event reporting, you'll need to add a DSN (see the next step).
-
### Configure
-Add the [Sentry DSN](https://docs.sentry.io/quickstart/#configure-the-dsn) of your project.
-If you're using Symfony 3.4, add the DSN to your `app/config/config_prod.yml` file.
-For Symfony 4 or newer, add the DSN to your `config/packages/sentry.yaml` file.
-
-Keep in mind that by leaving the `dsn` value empty (or undeclared), you will disable Sentry's event reporting.
-
-```yaml
-sentry:
- dsn: "https://public:secret@sentry.example.com/1"
- messenger:
- enabled: true # flushes Sentry messages at the end of each message handling
- capture_soft_fails: true # captures exceptions marked for retry too
- options:
- environment: '%kernel.environment%'
- release: '%env(VERSION)%' #your app version
-```
-
-The parameter `options` allows to fine-tune exceptions. To discover more options, please refer to
-[the Unified APIs](https://docs.sentry.io/development/sdk-dev/unified-api/#options) options and
-the [PHP specific](https://docs.sentry.io/platforms/php/#php-specific-options) ones.
-
-#### Optional: use custom HTTP factory/transport
-
-Since the SDK 2.0 uses HTTPlug to remain transport-agnostic, you need to install two packages that provide
-[`php-http/async-client-implementation`](https://packagist.org/providers/php-http/async-client-implementation)
-and [`psr/http-message-implementation`](https://packagist.org/providers/psr/http-message-implementation).
-
-This bundle depends on `sentry/sdk`, which is a metapackage that already solves this need, requiring our suggested HTTP
-packages: the Symfony HTTP client (`symfony/http-client`) and Guzzle's message factories (`http-interop/http-factory-guzzle`).
-
-If you want to use a different HTTP client or message factory, you can override the `sentry/sdk` package by adding the following to your `composer.json` after the `require` section:
+Add the [Sentry DSN](https://docs.sentry.io/quickstart/#configure-the-dsn) to your `.env` file.
-```json
- "replace": {
- "sentry/sdk": "*"
- }
```
-
-For example when you want to use Guzzle's components:
-
-```bash
-composer require php-http/guzzle6-adapter guzzlehttp/psr7
-```
-
-If you don't have a compatible HTTP client and/or message factory implementation installed `php-http/discovery` will try to install it for you using a Composer plugin.
-
-## Maintained versions
-
- * 4.x is actively maintained and developed on the master branch, and uses Sentry SDK 3.0;
- * 3.x is supported only for fixes and uses Sentry SDK 2.0;
- * 2.x is no longer maintained; from this version onwards it requires Symfony 3+ and PHP 7.1+;
- * 1.x is no longer maintained; you can use it for Symfony < 2.8 and PHP 5.6/7.0;
- * 0.8.x is no longer maintained.
-
-### Upgrading to 4.0
-
-The 4.0 version of the bundle uses the newest version (3.x) of the underlying Sentry SDK. If you need to migrate from previous versions, please check the `UPGRADE-4.0.md` document.
-
-#### Custom serializers
-
-The option class_serializers can be used to send customized objects serialization.
-```yml
-sentry:
- options:
- class_serializers:
- YourValueObject: 'ValueObjectSerializer'
+###> sentry/sentry-symfony ###
+SENTRY_DSN="https://public@sentry.example.com/1"
+###< sentry/sentry-symfony ###
```
-Several serializers can be added and the serializable check is done by using the **instanceof** type operator.
-The serializer must implement the `__invoke` method, which needs to return an **array**, containing the information that should be send to Sentry. The class name is always sent by default.
+### Usage
-Serializer example:
```php
-final class ValueObjectSerializer
-{
- public function __invoke(YourValueObject $vo): array
- {
- return [
- 'value' => $vo->value()
- ];
- }
+use function Sentry\captureException;
+
+try {
+ $this->functionThatMayFail();
+} catch (\Throwable $exception) {
+ captureException($exception);
}
```
@@ -171,6 +55,12 @@ final class ValueObjectSerializer
Please refer to [CONTRIBUTING.md](CONTRIBUTING.md).
+### Thanks to all the people who contributed so far!
+
+
+
+
+
## Getting help/support
If you need help setting up or configuring the Symfony SDK (or anything else in the Sentry universe) please head over to the [Sentry Community on Discord](https://discord.com/invite/Ww9hbqr). There is a ton of great people in our Discord community ready to help you!
@@ -190,3 +80,4 @@ Licensed under the MIT license, see [`LICENSE`](LICENSE)
[Packagist link]: https://packagist.org/packages/sentry/sentry-symfony
[Master Code Coverage]: https://codecov.io/gh/getsentry/sentry-symfony/branch/master
[Master Code Coverage Image]: https://img.shields.io/codecov/c/github/getsentry/sentry-symfony/master?logo=codecov
+
diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md
new file mode 100644
index 00000000..5737c2cc
--- /dev/null
+++ b/UPGRADE-5.0.md
@@ -0,0 +1,54 @@
+# Upgrade 4.x to 5.0
+
+This version adds support for the underlying [Sentry PHP SDK v4.0](https://github.com/getsentry/sentry-php).
+Please refer to the PHP SDK [sentry-php/UPGRADE-4.0.md](https://github.com/getsentry/sentry-php/blob/master/UPGRADE-4.0.md) guide for a complete list of breaking changes.
+
+- This version exclusively uses the [envelope endpoint](https://develop.sentry.dev/sdk/envelopes/) to send event data to Sentry.
+
+ If you are using [sentry.io](https://sentry.io), no action is needed.
+ If you are using an on-premise/self-hosted installation of Sentry, the minimum requirement is now version `>= v20.6.0`.
+
+- You need to have `ext-curl` installed to use the SDK.
+
+- The `IgnoreErrorsIntegration` integration was removed. Use the `ignore_exceptions` option instead.
+ Previously, both `Symfony\Component\ErrorHandler\Error\FatalError` and `Symfony\Component\Debug\Exception\FatalErrorException` were ignored by default.
+ To continue ignoring these exceptions, make the following changes to your `config/packages/sentry.yaml` file:
+
+ ```yaml
+ // config/packages/sentry.yaml
+
+ sentry:
+ options:
+ ignore_exceptions:
+ - 'Symfony\Component\ErrorHandler\Error\FatalError'
+ - 'Symfony\Component\Debug\Exception\FatalErrorException'
+ ```
+
+ This option performs an [`is_a`](https://www.php.net/manual/en/function.is-a.php) check now, so you can also ignore more generic exceptions.
+
+- Removed support for `guzzlehttp/psr7: ^1.8.4`.
+
+- The `RequestFetcher` now relies on `guzzlehttp/psr7: ^2.1.1`.
+
+- Continue traces from the W3C `traceparent` request header.
+- Inject the W3C `traceparent` header on outgoing HTTP client calls.
+- Added `Sentry\SentryBundle\Twig\SentryExtension::getW3CTraceMeta()`.
+
+- The new default value for the `sentry.options.trace_propagation_targets` option is now `null`. To not attach any headers to outgoing requests, set this option to `[]`.
+
+- Added the `sentry.options.enable_tracing` option.
+- Added the `sentry.options.attach_metric_code_locations` option.
+- Added the `sentry.options.spotlight` option.
+- Added the `sentry.options.spotlight_url` option.
+- Added the `sentry.options.transport` option.
+- Added the `sentry.options.http_client` option.
+- Added the `sentry.options.http_proxy_authentication` option.
+- Added the `sentry.options.http_ssl_verify_peer` option.
+- Added the `sentry.options.http_compression` option.
+
+- Removed the `sentry.transport_factory` option. Use `sentry.options.transport` to use a custom transport.
+- Removed the `sentry.options.send_attempts` option. You may use a custom transport if you rely on this behaviour.
+- Removed the `sentry.options.enable_compression` option. Use `sentry.options.http_compression` instead.
+
+- Removed `Sentry\SentryBundle\Transport\TransportFactory`.
+- Removed `Sentry\State\HubInterface\Sentry\State\HubInterface`.
diff --git a/composer.json b/composer.json
index 55b77290..580a952c 100644
--- a/composer.json
+++ b/composer.json
@@ -7,64 +7,48 @@
"license": "MIT",
"authors": [
{
- "name": "David Cramer",
- "email": "dcramer@gmail.com"
- },
- {
- "name": "Alessandro Lai",
- "email": "alessandro.lai85@gmail.com"
+ "name": "Sentry",
+ "email": "accounts@sentry.io"
}
],
- "config": {
- "sort-packages": true,
- "allow-plugins": {
- "composer/package-versions-deprecated": true,
- "phpstan/extension-installer": true,
- "php-http/discovery": false
- }
- },
"require": {
"php": "^7.2||^8.0",
- "guzzlehttp/psr7": "^1.7 || ^2.0",
- "jean85/pretty-package-versions": "^1.5 || ^2.0",
- "sentry/sdk": "^3.4",
- "sentry/sentry": "^3.20.1",
+ "guzzlehttp/psr7": "^2.1.1",
+ "jean85/pretty-package-versions": "^1.5||^2.0",
+ "sentry/sentry": "^4.11.0",
"symfony/cache-contracts": "^1.1||^2.4||^3.0",
- "symfony/config": "^4.4.20||^5.0.11||^6.0",
- "symfony/console": "^4.4.20||^5.0.11||^6.0",
- "symfony/dependency-injection": "^4.4.20||^5.0.11||^6.0",
- "symfony/event-dispatcher": "^4.4.20||^5.0.11||^6.0",
- "symfony/http-kernel": "^4.4.20||^5.0.11||^6.0",
+ "symfony/config": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/console": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/dependency-injection": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/event-dispatcher": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/http-kernel": "^4.4.20||^5.0.11||^6.0||^7.0",
"symfony/polyfill-php80": "^1.22",
- "symfony/psr-http-message-bridge": "^1.2||^2.0",
- "symfony/security-core": "^4.4.20||^5.0.11||^6.0",
- "symfony/security-http": "^4.4.20||^5.0.11||^6.0"
+ "symfony/psr-http-message-bridge": "^1.2||^2.0||^6.4||^7.0"
},
"require-dev": {
- "doctrine/dbal": "^2.13||^3.0",
- "doctrine/doctrine-bundle": "^1.12||^2.5",
- "friendsofphp/php-cs-fixer": "^2.19||<=3.16.0",
- "jangregor/phpstan-prophecy": "^1.0",
- "monolog/monolog": "^1.3||^2.0",
- "phpspec/prophecy": "!=1.11.0",
- "phpspec/prophecy-phpunit": "^1.1||^2.0",
+ "doctrine/dbal": "^2.13||^3.3||^4.0",
+ "doctrine/doctrine-bundle": "^2.6",
+ "friendsofphp/php-cs-fixer": "^2.19||^3.40",
+ "masterminds/html5": "^2.8",
"phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^1.3",
- "phpstan/phpstan-phpunit": "^1.0",
- "phpstan/phpstan-symfony": "^1.0",
- "phpunit/phpunit": "^8.5.14||^9.3.9",
- "symfony/browser-kit": "^4.4.20||^5.0.11||^6.0",
- "symfony/cache": "^4.4.20||^5.0.11||^6.0",
- "symfony/dom-crawler": "^4.4.20||^5.0.11||^6.0",
- "symfony/framework-bundle": "^4.4.20||^5.0.11||^6.0",
- "symfony/http-client": "^4.4.20||^5.0.11||^6.0",
- "symfony/messenger": "^4.4.20||^5.0.11||^6.0",
+ "phpstan/phpstan": "1.12.5",
+ "phpstan/phpstan-phpunit": "1.4.0",
+ "phpstan/phpstan-symfony": "1.4.10",
+ "phpunit/phpunit": "^8.5.40||^9.6.21",
+ "symfony/browser-kit": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/cache": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/dom-crawler": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/framework-bundle": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/http-client": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/messenger": "^4.4.20||^5.0.11||^6.0||^7.0",
"symfony/monolog-bundle": "^3.4",
- "symfony/phpunit-bridge": "^5.2.6||^6.0",
- "symfony/process": "^4.4.20||^5.0.11||^6.0",
- "symfony/twig-bundle": "^4.4.20||^5.0.11||^6.0",
- "symfony/yaml": "^4.4.20||^5.0.11||^6.0",
- "vimeo/psalm": "^4.3"
+ "symfony/phpunit-bridge": "^5.2.6||^6.0||^7.0",
+ "symfony/process": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/security-core": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/security-http": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/twig-bundle": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "symfony/yaml": "^4.4.20||^5.0.11||^6.0||^7.0",
+ "vimeo/psalm": "^4.3||^5.16.0"
},
"suggest": {
"monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler.",
@@ -86,24 +70,23 @@
}
},
"scripts": {
- "tests": [
- "vendor/bin/phpunit --verbose"
+ "check": [
+ "@cs-check",
+ "@phpstan",
+ "@psalm",
+ "@tests"
],
- "phpcs": [
- "vendor/bin/php-cs-fixer fix --verbose --diff --dry-run"
- ],
- "phpstan": [
- "vendor/bin/phpstan analyse"
- ],
- "psalm": [
- "vendor/bin/psalm"
- ]
+ "tests": "vendor/bin/phpunit --verbose",
+ "cs-check": "vendor/bin/php-cs-fixer fix --verbose --diff --dry-run",
+ "cs-fix": "vendor/bin/php-cs-fixer fix --verbose --diff",
+ "phpstan": "vendor/bin/phpstan analyse",
+ "psalm": "vendor/bin/psalm"
},
- "extra": {
- "branch-alias": {
- "releases/3.2.x": "3.2.x-dev",
- "releases/2.x": "2.x-dev",
- "releases/1.x": "1.x-dev"
+ "config": {
+ "sort-packages": true,
+ "allow-plugins": {
+ "composer/package-versions-deprecated": true,
+ "phpstan/extension-installer": true
}
}
}
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index c04293a5..4313a41a 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1,10 +1,5 @@
parameters:
ignoreErrors:
- -
- message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, mixed given\\.$#"
- count: 1
- path: src/DependencyInjection/Compiler/DbalTracingPass.php
-
-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\TreeBuilder\\:\\:root\\(\\)\\.$#"
count: 1
@@ -12,22 +7,32 @@ parameters:
-
message: "#^Cannot access offset 'before_breadcrumb' on mixed\\.$#"
- count: 3
+ count: 1
path: src/DependencyInjection/SentryExtension.php
-
message: "#^Cannot access offset 'before_send' on mixed\\.$#"
- count: 3
+ count: 1
+ path: src/DependencyInjection/SentryExtension.php
+
+ -
+ message: "#^Cannot access offset 'before_send_check_in' on mixed\\.$#"
+ count: 1
+ path: src/DependencyInjection/SentryExtension.php
+
+ -
+ message: "#^Cannot access offset 'before_send_metrics' on mixed\\.$#"
+ count: 1
path: src/DependencyInjection/SentryExtension.php
-
message: "#^Cannot access offset 'before_sendβ¦' on mixed\\.$#"
- count: 3
+ count: 1
path: src/DependencyInjection/SentryExtension.php
-
message: "#^Cannot access offset 'class_serializers' on mixed\\.$#"
- count: 3
+ count: 1
path: src/DependencyInjection/SentryExtension.php
-
@@ -50,6 +55,11 @@ parameters:
count: 1
path: src/DependencyInjection/SentryExtension.php
+ -
+ message: "#^Cannot access offset 'http_client' on mixed\\.$#"
+ count: 1
+ path: src/DependencyInjection/SentryExtension.php
+
-
message: "#^Cannot access offset 'in_app_exclude' on mixed\\.$#"
count: 2
@@ -60,13 +70,18 @@ parameters:
count: 2
path: src/DependencyInjection/SentryExtension.php
+ -
+ message: "#^Cannot access offset 'logger' on mixed\\.$#"
+ count: 1
+ path: src/DependencyInjection/SentryExtension.php
+
-
message: "#^Cannot access offset 'traces_sampler' on mixed\\.$#"
- count: 3
+ count: 1
path: src/DependencyInjection/SentryExtension.php
-
- message: "#^Class Symfony\\\\Component\\\\Debug\\\\Exception\\\\FatalErrorException not found\\.$#"
+ message: "#^Cannot access offset 'transport' on mixed\\.$#"
count: 1
path: src/DependencyInjection/SentryExtension.php
@@ -82,7 +97,7 @@ parameters:
-
message: "#^Parameter \\#1 \\$id of class Symfony\\\\Component\\\\DependencyInjection\\\\Reference constructor expects string, mixed given\\.$#"
- count: 6
+ count: 10
path: src/DependencyInjection/SentryExtension.php
-
@@ -96,7 +111,7 @@ parameters:
path: src/DependencyInjection/SentryExtension.php
-
- message: "#^Parameter \\#2 \\$callback of function array_filter expects callable\\(mixed\\)\\: mixed, Closure\\(string\\)\\: bool given\\.$#"
+ message: "#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(mixed\\)\\: bool\\)\\|null, Closure\\(string\\)\\: bool given\\.$#"
count: 1
path: src/DependencyInjection/SentryExtension.php
@@ -135,11 +150,6 @@ parameters:
count: 4
path: src/DependencyInjection/SentryExtension.php
- -
- message: "#^Parameter \\#2 \\$registerErrorListener of method Sentry\\\\SentryBundle\\\\DependencyInjection\\\\SentryExtension\\:\\:configureErrorListenerIntegration\\(\\) expects bool, mixed given\\.$#"
- count: 1
- path: src/DependencyInjection/SentryExtension.php
-
-
message: "#^Parameter \\#2 \\$useDefaultIntegrations of method Sentry\\\\SentryBundle\\\\DependencyInjection\\\\SentryExtension\\:\\:configureRequestIntegration\\(\\) expects bool, mixed given\\.$#"
count: 1
@@ -155,100 +165,70 @@ parameters:
count: 1
path: src/EventListener/AbstractTracingRequestListener.php
- -
- message: "#^Class Sentry\\\\SentryBundle\\\\EventListener\\\\ConsoleCommandListener extends @final class Sentry\\\\SentryBundle\\\\EventListener\\\\ConsoleListener\\.$#"
- count: 1
- path: src/EventListener/ConsoleCommandListener.php
-
-
message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\KernelEvent\\:\\:isMasterRequest\\(\\)\\.$#"
count: 1
path: src/EventListener/LoginListener.php
-
- message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\KernelEvent\\:\\:isMasterRequest\\(\\)\\.$#"
+ message: "#^Instanceof between Throwable and Symfony\\\\Component\\\\Messenger\\\\Exception\\\\DelayedMessageHandlingException will always evaluate to false\\.$#"
count: 1
- path: src/EventListener/RequestListener.php
+ path: src/EventListener/MessengerListener.php
-
- message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\KernelEvent\\:\\:isMasterRequest\\(\\)\\.$#"
+ message: "#^Instanceof between Throwable and Symfony\\\\Component\\\\Messenger\\\\Exception\\\\HandlerFailedException will always evaluate to false\\.$#"
count: 1
- path: src/EventListener/SubRequestListener.php
+ path: src/EventListener/MessengerListener.php
-
- message: "#^Parameter \\#1 \\$driver of method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverMiddleware\\:\\:wrap\\(\\) expects Doctrine\\\\DBAL\\\\Driver, mixed given\\.$#"
- count: 1
- path: src/Tracing/Doctrine/DBAL/ConnectionConfigurator.php
-
- -
- message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:errorInfo\\(\\) return type has no value type specified in iterable type array\\.$#"
- count: 1
- path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
-
- -
- message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:exec\\(\\) has parameter \\$sql with no type specified\\.$#"
- count: 1
- path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
-
- -
- message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:prepare\\(\\) has parameter \\$sql with no type specified\\.$#"
- count: 1
- path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
-
- -
- message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:query\\(\\) has parameter \\$args with no type specified\\.$#"
- count: 1
- path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
+ message: "#^Result of && is always false\\.$#"
+ count: 2
+ path: src/EventListener/MessengerListener.php
-
- message: "#^Parameter \\#1 \\$sql of method Doctrine\\\\DBAL\\\\Driver\\\\Connection\\:\\:query\\(\\) expects string, string\\|null given\\.$#"
+ message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\KernelEvent\\:\\:isMasterRequest\\(\\)\\.$#"
count: 1
- path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
+ path: src/EventListener/RequestListener.php
-
- message: "#^Parameter \\#2 \\$spanDescription of method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:traceFunction\\(\\) expects string, string\\|null given\\.$#"
+ message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\KernelEvent\\:\\:isMasterRequest\\(\\)\\.$#"
count: 1
- path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
+ path: src/EventListener/SubRequestListener.php
-
- message: "#^Property Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:\\$spanTags is never read, only written\\.$#"
+ message: "#^Parameter \\#1 \\$driver of method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverMiddleware\\:\\:wrap\\(\\) expects Doctrine\\\\DBAL\\\\Driver, mixed given\\.$#"
count: 1
- path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
+ path: src/Tracing/Doctrine/DBAL/ConnectionConfigurator.php
-
- message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingServerInfoAwareDriverConnection\\:\\:errorInfo\\(\\) return type has no value type specified in iterable type array\\.$#"
+ message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnectionForV4\\:\\:errorInfo\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
- path: src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php
+ path: src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4.php
-
- message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingServerInfoAwareDriverConnection\\:\\:exec\\(\\) has parameter \\$sql with no type specified\\.$#"
+ message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnectionForV4\\:\\:exec\\(\\) should return int\\|numeric\\-string but returns int\\|string\\.$#"
count: 1
- path: src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php
+ path: src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4.php
-
- message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingServerInfoAwareDriverConnection\\:\\:prepare\\(\\) has parameter \\$sql with no type specified\\.$#"
+ message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnectionForV4\\:\\:prepare\\(\\) has parameter \\$sql with no type specified\\.$#"
count: 1
- path: src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php
+ path: src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4.php
-
- message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingServerInfoAwareDriverConnection\\:\\:query\\(\\) has parameter \\$args with no type specified\\.$#"
+ message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnectionForV4\\:\\:query\\(\\) has parameter \\$args with no type specified\\.$#"
count: 1
- path: src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php
+ path: src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4.php
-
message: "#^Parameter \\#1 \\$sql of method Doctrine\\\\DBAL\\\\Driver\\\\Connection\\:\\:query\\(\\) expects string, string\\|null given\\.$#"
count: 1
- path: src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php
-
- -
- message: "#^Parameter \\#2 \\$callback of method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\AbstractTracingStatement\\:\\:traceFunction\\(\\) expects callable\\(mixed \\.\\.\\.\\)\\: Doctrine\\\\DBAL\\\\Driver\\\\Result, array\\{Doctrine\\\\DBAL\\\\Driver\\\\Statement, 'execute'\\} given\\.$#"
- count: 1
- path: src/Tracing/Doctrine/DBAL/TracingStatementForV3.php
+ path: src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4.php
-
- message: "#^Parameter \\#4 \\$length of method Doctrine\\\\DBAL\\\\Driver\\\\Statement\\:\\:bindParam\\(\\) expects int\\|null, mixed given\\.$#"
+ message: "#^Parameter \\#2 \\$spanDescription of method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnectionForV4\\:\\:traceFunction\\(\\) expects string, string\\|null given\\.$#"
count: 1
- path: src/Tracing/Doctrine/DBAL/TracingStatementForV3.php
+ path: src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4.php
-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\HttpClient\\\\AbstractTraceableHttpClient\\:\\:request\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
@@ -291,9 +271,14 @@ parameters:
path: tests/DependencyInjection/ConfigurationTest.php
-
- message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
+ message: "#^Function Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\Configurator\\\\ref not found\\.$#"
count: 1
- path: tests/DependencyInjection/SentryExtensionTest.php
+ path: tests/DependencyInjection/Fixtures/php/release_option_fallback_to_env_var.php
+
+ -
+ message: "#^Used function Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\Configurator\\\\ref not found\\.$#"
+ count: 1
+ path: tests/DependencyInjection/Fixtures/php/release_option_fallback_to_env_var.php
-
message: "#^Cannot access offset 'dsn' on mixed\\.$#"
@@ -306,7 +291,7 @@ parameters:
path: tests/DependencyInjection/SentryExtensionTest.php
-
- message: "#^Class Symfony\\\\Component\\\\Debug\\\\Exception\\\\FatalErrorException not found\\.$#"
+ message: "#^Cannot access offset 'release' on mixed\\.$#"
count: 1
path: tests/DependencyInjection/SentryExtensionTest.php
@@ -316,12 +301,17 @@ parameters:
path: tests/End2End/TracingEnd2EndTest.php
-
- message: "#^Call to function method_exists\\(\\) with \\$this\\(Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\AuthenticatedTokenStub\\) and 'setAuthenticated' will always evaluate to false\\.$#"
+ message: "#^Access to undefined constant Symfony\\\\Component\\\\HttpKernel\\\\HttpKernelInterface\\:\\:MASTER_REQUEST\\.$#"
count: 1
+ path: tests/EventListener/ErrorListenerTest.php
+
+ -
+ message: "#^Access to undefined constant Symfony\\\\Component\\\\HttpKernel\\\\HttpKernelInterface\\:\\:MASTER_REQUEST\\.$#"
+ count: 2
path: tests/EventListener/LoginListenerTest.php
-
- message: "#^Parameter \\#1 \\$user of method Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\AbstractToken\\:\\:setUser\\(\\) expects Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface, string\\|Stringable\\|Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface given\\.$#"
+ message: "#^Call to function method_exists\\(\\) with \\$this\\(Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\AuthenticatedTokenStub\\) and 'setAuthenticated' will always evaluate to false\\.$#"
count: 1
path: tests/EventListener/LoginListenerTest.php
@@ -341,15 +331,30 @@ parameters:
path: tests/EventListener/LoginListenerTest.php
-
- message: "#^Parameter \\#5 \\$originatedFromUri of class Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\SwitchUserToken constructor expects string\\|null, Sentry\\\\SentryBundle\\\\Tests\\\\EventListener\\\\AuthenticatedTokenStub given\\.$#"
- count: 1
- path: tests/EventListener/LoginListenerTest.php
+ message: "#^Access to undefined constant Symfony\\\\Component\\\\HttpKernel\\\\HttpKernelInterface\\:\\:MASTER_REQUEST\\.$#"
+ count: 6
+ path: tests/EventListener/RequestListenerTest.php
+
+ -
+ message: "#^Access to undefined constant Symfony\\\\Component\\\\HttpKernel\\\\HttpKernelInterface\\:\\:MASTER_REQUEST\\.$#"
+ count: 2
+ path: tests/EventListener/SubRequestListenerTest.php
-
message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\KernelEvent\\:\\:isMasterRequest\\(\\)\\.$#"
count: 1
path: tests/EventListener/SubRequestListenerTest.php
+ -
+ message: "#^Access to undefined constant Symfony\\\\Component\\\\HttpKernel\\\\HttpKernelInterface\\:\\:MASTER_REQUEST\\.$#"
+ count: 3
+ path: tests/EventListener/TracingRequestListenerTest.php
+
+ -
+ message: "#^Access to undefined constant Symfony\\\\Component\\\\HttpKernel\\\\HttpKernelInterface\\:\\:MASTER_REQUEST\\.$#"
+ count: 2
+ path: tests/EventListener/TracingSubRequestListenerTest.php
+
-
message: "#^Call to an undefined method TCacheAdapter of Symfony\\\\Component\\\\Cache\\\\Adapter\\\\AdapterInterface\\:\\:delete\\(\\)\\.$#"
count: 2
@@ -385,91 +390,26 @@ parameters:
count: 1
path: tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php
+ -
+ message: "#^Property Sentry\\\\SentryBundle\\\\Tests\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnectionFactoryV4Test\\:\\:\\$databasePlatform is never read, only written\\.$#"
+ count: 1
+ path: tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV4Test.php
+
-
message: "#^Trying to mock an undefined method errorCode\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Connection\\.$#"
count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingDriverConnectionTest.php
+ path: tests/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4Test.php
-
message: "#^Trying to mock an undefined method errorInfo\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Connection\\.$#"
count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingDriverConnectionTest.php
+ path: tests/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4Test.php
-
message: "#^Parameter \\#1 \\$hubOrConnectionFactory of class Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverMiddleware constructor expects Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnectionFactoryInterface\\|Sentry\\\\State\\\\HubInterface, null given\\.$#"
count: 1
path: tests/Tracing/Doctrine/DBAL/TracingDriverMiddlewareTest.php
- -
- message: "#^Trying to mock an undefined method errorCode\\(\\) on class Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnectionInterface\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnectionTest.php
-
- -
- message: "#^Trying to mock an undefined method errorInfo\\(\\) on class Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnectionInterface\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnectionTest.php
-
- -
- message: "#^Trying to mock an undefined method requiresQueryForServerVersion\\(\\) on class Sentry\\\\SentryBundle\\\\Tests\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Fixture\\\\ServerInfoAwareConnectionStub\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnectionTest.php
-
- -
- message: "#^Parameter \\#2 \\$decoratedStatement of class Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingStatementForV2 constructor expects Doctrine\\\\DBAL\\\\Driver\\\\Statement, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Sentry\\\\SentryBundle\\\\Tests\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingStatementForV2Stub given\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Parameter \\#4 \\$length of method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingStatementForV2\\:\\:bindParam\\(\\) expects int\\|null, mixed given\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Trying to mock an undefined method closeCursor\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Trying to mock an undefined method columnCount\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Trying to mock an undefined method errorCode\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Trying to mock an undefined method errorInfo\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Trying to mock an undefined method fetch\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Trying to mock an undefined method fetchAll\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Trying to mock an undefined method fetchColumn\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Trying to mock an undefined method rowCount\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
- -
- message: "#^Trying to mock an undefined method setFetchMode\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
- count: 1
- path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
-
-
message: "#^Parameter \\#1 \\$responses of method Sentry\\\\SentryBundle\\\\Tracing\\\\HttpClient\\\\AbstractTraceableHttpClient\\:\\:stream\\(\\) expects iterable\\<\\(int\\|string\\), Symfony\\\\Contracts\\\\HttpClient\\\\ResponseInterface\\>\\|Symfony\\\\Contracts\\\\HttpClient\\\\ResponseInterface, stdClass given\\.$#"
count: 1
diff --git a/phpstan.neon b/phpstan.neon
index 684f3989..3608367b 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -2,6 +2,7 @@ includes:
- phpstan-baseline.neon
parameters:
+ reportUnmatchedIgnoredErrors: true
level: 9
paths:
- src
@@ -12,14 +13,26 @@ parameters:
- src/aliases.php
- src/Tracing/Cache/TraceableCacheAdapterForV2.php
- src/Tracing/Cache/TraceableTagAwareCacheAdapterForV2.php
- - src/Tracing/Doctrine/DBAL/TracingStatementForV2.php
+ - src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryForV2V3.php
+ - src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV2V3.php
- src/Tracing/Doctrine/DBAL/TracingDriverForV2.php
+ - src/Tracing/Doctrine/DBAL/TracingDriverForV3.php
+ - src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php
+ - src/Tracing/Doctrine/DBAL/TracingStatementForV2.php
+ - src/Tracing/Doctrine/DBAL/TracingStatementForV3.php
- src/Tracing/HttpClient/TraceableHttpClientForV4.php
- src/Tracing/HttpClient/TraceableHttpClientForV5.php
- src/Tracing/HttpClient/TraceableResponseForV4.php
- src/Tracing/HttpClient/TraceableResponseForV5.php
- tests/End2End/App
+ - tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV2Test.php
+ - tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV3Test.php
+ - tests/Tracing/Doctrine/DBAL/TracingDriverConnectionForV2V3Test.php
- tests/Tracing/Doctrine/DBAL/TracingDriverForV2Test.php
+ - tests/Tracing/Doctrine/DBAL/TracingDriverForV3Test.php
+ - tests/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnectionTest.php
+ - tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
+ - tests/Tracing/Doctrine/DBAL/TracingStatementForV3Test.php
- tests/EventListener/Fixtures/UserWithoutIdentifierStub.php
dynamicConstantNames:
- Symfony\Component\HttpKernel\Kernel::VERSION
diff --git a/phpunit.xml b/phpunit.xml
index 72fe2378..e4dadb92 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -20,17 +20,11 @@
src
+
+ src/aliases.php
+
-
-
- src
-
- src/aliases.php
-
-
-
-
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 32016223..fc8ebf3e 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -1,95 +1,60 @@
-
+
-
- FatalErrorException
+
+
-
- ConsoleListener
+
+
-
- public function __construct(HubInterface $hub, bool $captureErrors = true)
+
+
-
-
- $event instanceof ExceptionEvent
-
-
- getException
-
-
-
- isMasterRequest
+
+
-
- iterable
+
+
-
-
-
-
- ExceptionConverterDriver
-
-
-
-
- $this->decoratedDriver->getSchemaManager($conn, $platform)
-
-
- AbstractSchemaManager<T>
-
-
- $params
+
+
+
+
+
+
+
+
+ getServerVersion())]]>
+
+
+
+ getDatabasePlatform($versionProvider)]]>
+
-
- toStream
+
+
-
- toStream
+
+
-
- TracingDriverForV2
+
+
-
- FilterControllerEvent
- FilterResponseEvent
- GetResponseEvent
- GetResponseEvent
- GetResponseForExceptionEvent
- PostResponseEvent
-
-
-
-
- $container->getParameter('sentry.tracing.cache.enabled')
-
-
-
-
- $container->getParameter('sentry.tracing.enabled')
- $container->getParameter('sentry.tracing.http_client.enabled')
-
-
-
-
- $container->getParameter('sentry.tracing.enabled')
- $container->getParameter('sentry.tracing.dbal.enabled')
- $container->getParameter('sentry.tracing.dbal.connections')
- $container->getParameter('doctrine.connections')
-
diff --git a/psalm.xml b/psalm.xml
index 89240539..71321eb0 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -3,6 +3,8 @@
errorLevel="4"
memoizeMethodCallResults="true"
errorBaseline="psalm-baseline.xml"
+ findUnusedBaselineEntry="false"
+ findUnusedCode="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
@@ -10,7 +12,13 @@
+
+
+
+
+
+
diff --git a/src/Command/SentryTestCommand.php b/src/Command/SentryTestCommand.php
index 582f37c2..5c28a46e 100644
--- a/src/Command/SentryTestCommand.php
+++ b/src/Command/SentryTestCommand.php
@@ -5,18 +5,37 @@
namespace Sentry\SentryBundle\Command;
use Sentry\SentrySdk;
+use Sentry\State\HubInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
+/**
+ * @final since version 4.12
+ */
class SentryTestCommand extends Command
{
+ /**
+ * @var HubInterface
+ */
+ private $hub;
+
+ public function __construct(?HubInterface $hub = null)
+ {
+ parent::__construct();
+
+ if (null === $hub) {
+ @trigger_error(\sprintf('Not passing an instance of the "%s" interface as argument of the constructor is deprecated since version 4.12 and will not work since version 5.0.', HubInterface::class), \E_USER_DEPRECATED);
+ }
+
+ $this->hub = $hub ?? SentrySdk::getCurrentHub();
+ }
+
protected function execute(InputInterface $input, OutputInterface $output): int
{
- $currentHub = SentrySdk::getCurrentHub();
- $client = $currentHub->getClient();
+ $client = $this->hub->getClient();
- if (!$client) {
+ if (null === $client) {
$output->writeln('No client found');
$output->writeln('Your DSN is probably missing, check your configuration');
@@ -25,28 +44,27 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$dsn = $client->getOptions()->getDsn();
- if ($dsn) {
- $output->writeln('DSN correctly configured in the current client');
- } else {
+ if (null === $dsn) {
$output->writeln('No DSN configured in the current client, please check your configuration');
$output->writeln('To debug further, try bin/console debug:config sentry');
return 1;
}
+ $output->writeln('DSN correctly configured in the current client');
$output->writeln('Sending test message...');
- $eventId = $currentHub->captureMessage('This is a test message from the Sentry bundle');
+ $eventId = $this->hub->captureMessage('This is a test message from the Sentry bundle');
- if ($eventId) {
- $output->writeln("Message sent successfully with ID $eventId");
- } else {
+ if (null === $eventId) {
$output->writeln('Message not sent!');
$output->writeln('Check your DSN or your before_send callback if used');
return 1;
}
+ $output->writeln("Message sent successfully with ID $eventId");
+
return 0;
}
}
diff --git a/src/DependencyInjection/Compiler/AddLoginListenerTagPass.php b/src/DependencyInjection/Compiler/AddLoginListenerTagPass.php
index 24d3110c..5f935cb6 100644
--- a/src/DependencyInjection/Compiler/AddLoginListenerTagPass.php
+++ b/src/DependencyInjection/Compiler/AddLoginListenerTagPass.php
@@ -17,9 +17,17 @@ final class AddLoginListenerTagPass implements CompilerPassInterface
*/
public function process(ContainerBuilder $container): void
{
+ if (!$container->hasDefinition(LoginListener::class)) {
+ return;
+ }
$listenerDefinition = $container->getDefinition(LoginListener::class);
- if (!class_exists(LoginSuccessEvent::class)) {
+ if (class_exists(LoginSuccessEvent::class)) {
+ $listenerDefinition->addTag('kernel.event_listener', [
+ 'event' => LoginSuccessEvent::class,
+ 'method' => 'handleLoginSuccessEvent',
+ ]);
+ } elseif (class_exists(AuthenticationSuccessEvent::class)) {
$listenerDefinition->addTag('kernel.event_listener', [
'event' => AuthenticationSuccessEvent::class,
'method' => 'handleAuthenticationSuccessEvent',
diff --git a/src/DependencyInjection/Compiler/DbalTracingPass.php b/src/DependencyInjection/Compiler/DbalTracingPass.php
index d1e4d37f..e4cbcdd7 100644
--- a/src/DependencyInjection/Compiler/DbalTracingPass.php
+++ b/src/DependencyInjection/Compiler/DbalTracingPass.php
@@ -9,7 +9,6 @@
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverMiddleware;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
final class DbalTracingPass implements CompilerPassInterface
@@ -20,12 +19,6 @@ final class DbalTracingPass implements CompilerPassInterface
*/
private const CONNECTION_SERVICE_NAME_FORMAT = 'doctrine.dbal.%s_connection';
- /**
- * This is the format used by the DoctrineBundle bundle to register the
- * services for each connection's configuration.
- */
- private const CONNECTION_CONFIG_SERVICE_NAME_FORMAT = 'doctrine.dbal.%s_connection.configuration';
-
/**
* {@inheritdoc}
*/
@@ -52,8 +45,8 @@ public function process(ContainerBuilder $container): void
}
foreach ($connectionsToTrace as $connectionName) {
- if (!\in_array(sprintf(self::CONNECTION_SERVICE_NAME_FORMAT, $connectionName), $connections, true)) {
- throw new \InvalidArgumentException(sprintf('The Doctrine connection "%s" does not exists and cannot be instrumented.', $connectionName));
+ if (!\in_array(\sprintf(self::CONNECTION_SERVICE_NAME_FORMAT, $connectionName), $connections, true)) {
+ throw new \InvalidArgumentException(\sprintf('The Doctrine connection "%s" does not exists and cannot be instrumented.', $connectionName));
}
if (class_exists(Result::class)) {
@@ -66,35 +59,16 @@ public function process(ContainerBuilder $container): void
private function configureConnectionForDoctrineDBALVersion3(ContainerBuilder $container, string $connectionName): void
{
- $configurationDefinition = $container->getDefinition(sprintf(self::CONNECTION_CONFIG_SERVICE_NAME_FORMAT, $connectionName));
- $setMiddlewaresMethodCallArguments = $this->getSetMiddlewaresMethodCallArguments($configurationDefinition);
- $setMiddlewaresMethodCallArguments[0] = array_merge($setMiddlewaresMethodCallArguments[0] ?? [], [new Reference(TracingDriverMiddleware::class)]);
-
- $configurationDefinition
- ->removeMethodCall('setMiddlewares')
- ->addMethodCall('setMiddlewares', $setMiddlewaresMethodCallArguments);
+ $tracingMiddlewareDefinition = $container->getDefinition(TracingDriverMiddleware::class);
+ $tracingMiddlewareDefinition->addTag('doctrine.middleware', ['connection' => $connectionName]);
}
private function configureConnectionForDoctrineDBALVersion2(ContainerBuilder $container, string $connectionName): void
{
- $connectionDefinition = $container->getDefinition(sprintf(self::CONNECTION_SERVICE_NAME_FORMAT, $connectionName));
+ $connectionDefinition = $container->getDefinition(\sprintf(self::CONNECTION_SERVICE_NAME_FORMAT, $connectionName));
$connectionDefinition->setConfigurator([new Reference(ConnectionConfigurator::class), 'configure']);
}
- /**
- * @return mixed[]
- */
- private function getSetMiddlewaresMethodCallArguments(Definition $definition): array
- {
- foreach ($definition->getMethodCalls() as $methodCall) {
- if ('setMiddlewares' === $methodCall[0]) {
- return $methodCall[1];
- }
- }
-
- return [];
- }
-
private function assertRequiredDbalVersion(): void
{
if (interface_exists(Result::class)) {
diff --git a/src/DependencyInjection/Compiler/HttpClientTracingPass.php b/src/DependencyInjection/Compiler/HttpClientTracingPass.php
index 2464532b..99f7364d 100644
--- a/src/DependencyInjection/Compiler/HttpClientTracingPass.php
+++ b/src/DependencyInjection/Compiler/HttpClientTracingPass.php
@@ -12,6 +12,17 @@
final class HttpClientTracingPass implements CompilerPassInterface
{
+ /**
+ * List of service IDs that can be registered in the container by the
+ * framework when decorating the mock client. The order is from the
+ * outermost decorator to the innermost.
+ */
+ private const MOCK_HTTP_CLIENT_SERVICE_IDS = [
+ 'http_client.retryable.inner.mock_client',
+ '.debug.http_client.inner.mock_client',
+ 'http_client.mock_client',
+ ];
+
/**
* {@inheritdoc}
*/
@@ -21,12 +32,48 @@ public function process(ContainerBuilder $container): void
return;
}
- foreach ($container->findTaggedServiceIds('http_client.client') as $id => $tags) {
- $container->register('.sentry.traceable.' . $id, TraceableHttpClient::class)
- ->setDecoratedService($id)
- ->setArgument(0, new Reference('.sentry.traceable.' . $id . '.inner'))
- ->setArgument(1, new Reference(HubInterface::class))
- ->addTag('kernel.reset', ['method' => 'reset']);
+ $decoratedService = $this->getDecoratedService($container);
+
+ if (null === $decoratedService) {
+ return;
}
+
+ $container->register(TraceableHttpClient::class, TraceableHttpClient::class)
+ ->setArgument(0, new Reference(TraceableHttpClient::class . '.inner'))
+ ->setArgument(1, new Reference(HubInterface::class))
+ ->setDecoratedService($decoratedService[0], null, $decoratedService[1]);
+ }
+
+ /**
+ * @return array{string, int}|null
+ */
+ private function getDecoratedService(ContainerBuilder $container): ?array
+ {
+ // Starting from Symfony 6.3, the raw HTTP client that serves as adapter
+ // for the transport is registered as a separate service, so that the
+ // scoped clients can inject it before any decoration is applied on them.
+ // Since we need to access the full URL of the request, and such information
+ // is available after the `ScopingHttpClient` class did its job, we have
+ // to decorate such service. For more details, see https://github.com/symfony/symfony/pull/49513.
+ if ($container->hasDefinition('http_client.transport')) {
+ return ['http_client.transport', -15];
+ }
+
+ // On versions of Symfony prior to 6.3, when the mock client is in-use,
+ // each HTTP client is decorated by referencing explicitly the innermost
+ // service rather than by using the standard decoration feature. Hence,
+ // we have to look for the specific names of those services, and decorate
+ // them instead of the raw HTTP client.
+ foreach (self::MOCK_HTTP_CLIENT_SERVICE_IDS as $httpClientServiceId) {
+ if ($container->hasDefinition($httpClientServiceId)) {
+ return [$httpClientServiceId, 15];
+ }
+ }
+
+ if ($container->hasDefinition('http_client')) {
+ return ['http_client', 15];
+ }
+
+ return null;
}
}
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index fa7005fe..ca095895 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -5,10 +5,8 @@
namespace Sentry\SentryBundle\DependencyInjection;
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
-use Jean85\PrettyVersions;
use Sentry\Options;
use Sentry\SentryBundle\ErrorTypesParser;
-use Sentry\Transport\TransportFactoryInterface;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
@@ -44,7 +42,7 @@ public function getConfigTreeBuilder(): TreeBuilder
$rootNode
->children()
->scalarNode('dsn')
- ->info('If this value is not provided, the SDK will try to read it from the SENTRY_DSN environment variable. If that variable also does not exist, the SDK will just not send any events.')
+ ->info('If this value is not provided, the SDK will try to read it from the SENTRY_DSN environment variable. If that variable also does not exist, the SDK will not send any events.')
->end()
->booleanNode('register_error_listener')->defaultTrue()->end()
->booleanNode('register_error_handler')->defaultTrue()->end()
@@ -52,10 +50,6 @@ public function getConfigTreeBuilder(): TreeBuilder
->info('The service ID of the PSR-3 logger used to log messages coming from the SDK client. Be aware that setting the same logger of the application may create a circular loop when an event fails to be sent.')
->defaultNull()
->end()
- ->scalarNode('transport_factory')
- ->info('The service ID of the transport factory used by the default SDK client.')
- ->defaultValue(TransportFactoryInterface::class)
- ->end()
->arrayNode('options')
->addDefaultsIfNotSet()
->fixXmlConfig('integration')
@@ -70,7 +64,6 @@ public function getConfigTreeBuilder(): TreeBuilder
->scalarPrototype()->end()
->end()
->booleanNode('default_integrations')->end()
- ->integerNode('send_attempts')->min(0)->end()
->arrayNode('prefixes')
->defaultValue(array_merge(['%kernel.project_dir%'], array_filter(explode(\PATH_SEPARATOR, get_include_path() ?: ''))))
->scalarPrototype()->end()
@@ -80,33 +73,46 @@ public function getConfigTreeBuilder(): TreeBuilder
->max(1.0)
->info('The sampling factor to apply to events. A value of 0 will deny sending any event, and a value of 1 will send all events.')
->end()
+ ->booleanNode('enable_tracing')->end()
->floatNode('traces_sample_rate')
->min(0.0)
->max(1.0)
->info('The sampling factor to apply to transactions. A value of 0 will deny sending any transaction, and a value of 1 will send all transactions.')
->end()
+ ->scalarNode('traces_sampler')->end()
->floatNode('profiles_sample_rate')
->min(0.0)
->max(1.0)
->info('The sampling factor to apply to profiles. A value of 0 will deny sending any profiles, and a value of 1 will send all profiles. Profiles are sampled in relation to traces_sample_rate')
->end()
- ->scalarNode('traces_sampler')->end()
- ->variableNode('trace_propagation_targets')->end()
->booleanNode('attach_stacktrace')->end()
+ ->booleanNode('attach_metric_code_locations')->end()
->integerNode('context_lines')->min(0)->end()
- ->booleanNode('enable_compression')->end()
->scalarNode('environment')
->cannotBeEmpty()
->defaultValue('%kernel.environment%')
->end()
->scalarNode('logger')->end()
+ ->booleanNode('spotlight')->end()
+ ->scalarNode('spotlight_url')->end()
->scalarNode('release')
->cannotBeEmpty()
- ->defaultValue(PrettyVersions::getRootPackageVersion()->getPrettyVersion())
+ ->defaultValue('%env(default::SENTRY_RELEASE)%')
->end()
->scalarNode('server_name')->end()
+ ->arrayNode('ignore_exceptions')
+ ->scalarPrototype()->end()
+ ->beforeNormalization()->castToArray()->end()
+ ->end()
+ ->arrayNode('ignore_transactions')
+ ->scalarPrototype()->end()
+ ->beforeNormalization()->castToArray()->end()
+ ->end()
->scalarNode('before_send')->end()
->scalarNode('before_send_transaction')->end()
+ ->scalarNode('before_send_check_in')->end()
+ ->scalarNode('before_send_metrics')->end()
+ ->variableNode('trace_propagation_targets')->end()
->arrayNode('tags')
->useAttributeAsKey('name')
->normalizeKeys(false)
@@ -133,7 +139,10 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->booleanNode('send_default_pii')->end()
->integerNode('max_value_length')->min(0)->end()
+ ->scalarNode('transport')->end()
+ ->scalarNode('http_client')->end()
->scalarNode('http_proxy')->end()
+ ->scalarNode('http_proxy_authentication')->end()
->floatNode('http_connect_timeout')
->min(0)
->info('The maximum number of seconds to wait while trying to connect to a server. It works only when using the default transport.')
@@ -142,6 +151,8 @@ public function getConfigTreeBuilder(): TreeBuilder
->min(0)
->info('The maximum execution time for the request+response as a whole. It works only when using the default transport.')
->end()
+ ->booleanNode('http_ssl_verify_peer')->end()
+ ->booleanNode('http_compression')->end()
->booleanNode('capture_silenced_errors')->end()
->enumNode('max_request_body_size')
->values([
@@ -156,14 +167,6 @@ public function getConfigTreeBuilder(): TreeBuilder
->normalizeKeys(false)
->scalarPrototype()->end()
->end()
- ->arrayNode('ignore_exceptions')
- ->scalarPrototype()->end()
- ->beforeNormalization()->castToArray()->end()
- ->end()
- ->arrayNode('ignore_transactions')
- ->scalarPrototype()->end()
- ->beforeNormalization()->castToArray()->end()
- ->end()
->end()
->end()
->end();
diff --git a/src/DependencyInjection/SentryExtension.php b/src/DependencyInjection/SentryExtension.php
index 5e559a16..a05a62e8 100644
--- a/src/DependencyInjection/SentryExtension.php
+++ b/src/DependencyInjection/SentryExtension.php
@@ -5,16 +5,17 @@
namespace Sentry\SentryBundle\DependencyInjection;
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
+use Jean85\PrettyVersions;
use Psr\Log\NullLogger;
use Sentry\Client;
use Sentry\ClientBuilder;
-use Sentry\Integration\IgnoreErrorsIntegration;
use Sentry\Integration\IntegrationInterface;
use Sentry\Integration\RequestFetcherInterface;
use Sentry\Integration\RequestIntegration;
use Sentry\Options;
use Sentry\SentryBundle\EventListener\ConsoleListener;
use Sentry\SentryBundle\EventListener\ErrorListener;
+use Sentry\SentryBundle\EventListener\LoginListener;
use Sentry\SentryBundle\EventListener\MessengerListener;
use Sentry\SentryBundle\EventListener\TracingConsoleListener;
use Sentry\SentryBundle\EventListener\TracingRequestListener;
@@ -25,19 +26,16 @@
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverMiddleware;
use Sentry\SentryBundle\Tracing\Twig\TwigTracingExtension;
use Sentry\Serializer\RepresentationSerializer;
-use Sentry\Serializer\Serializer;
-use Sentry\Transport\TransportFactoryInterface;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Config\FileLocator;
-use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\ErrorHandler\Error\FatalError;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
final class SentryExtension extends ConfigurableExtension
{
@@ -67,6 +65,10 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.xml');
+ if (!$container->hasParameter('env(SENTRY_RELEASE)')) {
+ $container->setParameter('env(SENTRY_RELEASE)', PrettyVersions::getRootPackageVersion()->getPrettyVersion());
+ }
+
$this->registerConfiguration($container, $mergedConfig);
$this->registerErrorListenerConfiguration($container, $mergedConfig);
$this->registerMessengerListenerConfiguration($container, $mergedConfig['messenger']);
@@ -75,6 +77,10 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
$this->registerTwigTracingConfiguration($container, $mergedConfig['tracing']);
$this->registerCacheTracingConfiguration($container, $mergedConfig['tracing']);
$this->registerHttpClientTracingConfiguration($container, $mergedConfig['tracing']);
+
+ if (!interface_exists(TokenStorageInterface::class)) {
+ $container->removeDefinition(LoginListener::class);
+ }
}
/**
@@ -94,6 +100,10 @@ private function registerConfiguration(ContainerBuilder $container, array $confi
});
}
+ if (isset($options['logger'])) {
+ $options['logger'] = new Reference($options['logger']);
+ }
+
if (isset($options['traces_sampler'])) {
$options['traces_sampler'] = new Reference($options['traces_sampler']);
}
@@ -106,6 +116,14 @@ private function registerConfiguration(ContainerBuilder $container, array $confi
$options['before_send_transaction'] = new Reference($options['before_send_transaction']);
}
+ if (isset($options['before_send_check_in'])) {
+ $options['before_send_check_in'] = new Reference($options['before_send_check_in']);
+ }
+
+ if (isset($options['before_send_metrics'])) {
+ $options['before_send_metrics'] = new Reference($options['before_send_metrics']);
+ }
+
if (isset($options['before_breadcrumb'])) {
$options['before_breadcrumb'] = new Reference($options['before_breadcrumb']);
}
@@ -116,6 +134,14 @@ private function registerConfiguration(ContainerBuilder $container, array $confi
}, $options['class_serializers']);
}
+ if (isset($options['transport'])) {
+ $options['transport'] = new Reference($options['transport']);
+ }
+
+ if (isset($options['http_client'])) {
+ $options['http_client'] = new Reference($options['http_client']);
+ }
+
$container->getDefinition(IntegrationConfigurator::class)
->setArgument(0, $this->configureIntegrationsOption($options['integrations'], $config))
->setArgument(1, $config['register_error_handler']);
@@ -126,10 +152,6 @@ private function registerConfiguration(ContainerBuilder $container, array $confi
->setPublic(false)
->setArgument(0, $options);
- $serializer = (new Definition(Serializer::class))
- ->setPublic(false)
- ->setArgument(0, new Reference('sentry.client.options'));
-
$representationSerializerDefinition = (new Definition(RepresentationSerializer::class))
->setPublic(false)
->setArgument(0, new Reference('sentry.client.options'));
@@ -138,15 +160,10 @@ private function registerConfiguration(ContainerBuilder $container, array $confi
? new Reference(NullLogger::class, ContainerBuilder::IGNORE_ON_INVALID_REFERENCE)
: new Reference($config['logger']);
- $factoryBuilderDefinition = $container->getDefinition(TransportFactoryInterface::class);
- $factoryBuilderDefinition->setArgument('$logger', $loggerReference);
-
$clientBuilderDefinition = (new Definition(ClientBuilder::class))
->setArgument(0, new Reference('sentry.client.options'))
->addMethodCall('setSdkIdentifier', [SentryBundle::SDK_IDENTIFIER])
->addMethodCall('setSdkVersion', [SentryBundle::SDK_VERSION])
- ->addMethodCall('setTransportFactory', [new Reference($config['transport_factory'])])
- ->addMethodCall('setSerializer', [$serializer])
->addMethodCall('setRepresentationSerializer', [$representationSerializerDefinition])
->addMethodCall('setLogger', [$loggerReference]);
@@ -280,36 +297,11 @@ private function configureIntegrationsOption(array $integrations, array $config)
return new Reference($value);
}, $integrations);
- $integrations = $this->configureErrorListenerIntegration($integrations, $config['register_error_listener']);
$integrations = $this->configureRequestIntegration($integrations, $config['options']['default_integrations'] ?? true);
return $integrations;
}
- /**
- * @param array $integrations
- *
- * @return array
- */
- private function configureErrorListenerIntegration(array $integrations, bool $registerErrorListener): array
- {
- if ($registerErrorListener && !$this->isIntegrationEnabled(IgnoreErrorsIntegration::class, $integrations)) {
- // Prepend this integration to the beginning of the array so that
- // we can save some performance by skipping the rest of the integrations
- // if the error must be ignored
- array_unshift($integrations, new Definition(IgnoreErrorsIntegration::class, [
- [
- 'ignore_exceptions' => [
- FatalError::class,
- FatalErrorException::class,
- ],
- ],
- ]));
- }
-
- return $integrations;
- }
-
/**
* @param array $integrations
*
diff --git a/src/ErrorTypesParser.php b/src/ErrorTypesParser.php
index aa31d486..b717c641 100644
--- a/src/ErrorTypesParser.php
+++ b/src/ErrorTypesParser.php
@@ -54,14 +54,28 @@ private static function convertErrorConstants(string $value): string
{
$output = preg_replace_callback('/(E_[A-Z_]+)/', static function (array $matches) {
if (\defined($matches[1])) {
- return \constant($matches[1]);
+ $constant = \constant($matches[1]);
+
+ if (\is_string($constant)) {
+ return $constant;
+ } elseif (\is_int($constant)) {
+ return (string) $constant;
+ } elseif (\is_array($constant)) {
+ return implode(' | ', array_map(static function ($value) {
+ return \is_string($value) ? $value : (string) $value;
+ }, $constant));
+ } elseif (\is_object($constant)) {
+ return \get_class($constant);
+ } else { // Non-scalar values
+ return '';
+ }
}
return $matches[0];
}, $value);
if (null === $output) {
- throw new \InvalidArgumentException(sprintf('The "%s" value could not be parsed.', $value));
+ throw new \InvalidArgumentException(\sprintf('The "%s" value could not be parsed.', $value));
}
return $output;
diff --git a/src/EventListener/AbstractTracingRequestListener.php b/src/EventListener/AbstractTracingRequestListener.php
index 022f2d1b..95c31543 100644
--- a/src/EventListener/AbstractTracingRequestListener.php
+++ b/src/EventListener/AbstractTracingRequestListener.php
@@ -67,7 +67,7 @@ protected function getRouteName(Request $request): string
$route = $request->attributes->get('_controller');
if (\is_array($route) && \is_callable($route, true)) {
- $route = sprintf('%s::%s', \is_object($route[0]) ? get_debug_type($route[0]) : $route[0], $route[1]);
+ $route = \sprintf('%s::%s', \is_object($route[0]) ? get_debug_type($route[0]) : $route[0], $route[1]);
}
}
diff --git a/src/EventListener/ConsoleCommandListener.php b/src/EventListener/ConsoleCommandListener.php
deleted file mode 100644
index 1f64ecb3..00000000
--- a/src/EventListener/ConsoleCommandListener.php
+++ /dev/null
@@ -1,20 +0,0 @@
-tokenStorage || !$this->isMainRequest($event)) {
+ if (
+ null === $this->tokenStorage
+ || !$this->isMainRequest($event)
+ || $event->getRequest()->attributes->get('_stateless')
+ ) {
return;
}
@@ -109,7 +113,7 @@ private function updateUserContext(TokenInterface $token): void
private function isTokenAuthenticated(TokenInterface $token): bool
{
- if (method_exists($token, 'isAuthenticated') && !$token->isAuthenticated()) {
+ if (method_exists($token, 'isAuthenticated') && !$token->isAuthenticated(false)) {
return false;
}
diff --git a/src/EventListener/MessengerListener.php b/src/EventListener/MessengerListener.php
index f90d5574..b66b962c 100644
--- a/src/EventListener/MessengerListener.php
+++ b/src/EventListener/MessengerListener.php
@@ -11,7 +11,9 @@
use Sentry\State\Scope;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
+use Symfony\Component\Messenger\Exception\DelayedMessageHandlingException;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
+use Symfony\Component\Messenger\Exception\WrappedExceptionsInterface;
use Symfony\Component\Messenger\Stamp\BusNameStamp;
final class MessengerListener
@@ -93,8 +95,16 @@ public function handleWorkerMessageHandledEvent(WorkerMessageHandledEvent $event
*/
private function captureException(\Throwable $exception, bool $willRetry): void
{
- if ($exception instanceof HandlerFailedException) {
- foreach ($exception->getNestedExceptions() as $nestedException) {
+ if ($exception instanceof WrappedExceptionsInterface) {
+ $exception = $exception->getWrappedExceptions();
+ } elseif ($exception instanceof HandlerFailedException && method_exists($exception, 'getNestedExceptions')) {
+ $exception = $exception->getNestedExceptions();
+ } elseif ($exception instanceof DelayedMessageHandlingException && method_exists($exception, 'getExceptions')) {
+ $exception = $exception->getExceptions();
+ }
+
+ if (\is_array($exception)) {
+ foreach ($exception as $nestedException) {
$this->captureException($nestedException, $willRetry);
}
diff --git a/src/EventListener/RequestListener.php b/src/EventListener/RequestListener.php
index 9c4951fb..3d5948b8 100644
--- a/src/EventListener/RequestListener.php
+++ b/src/EventListener/RequestListener.php
@@ -11,7 +11,7 @@
use Symfony\Component\HttpKernel\Event\RequestEvent;
/**
- * This listener ensures that a new {@see \Sentry\State\Scope} is created for
+ * This listener ensures that a new {@see Scope} is created for
* each request and that it is filled with useful information, e.g. the IP
* address of the client.
*/
diff --git a/src/EventListener/TracingConsoleListener.php b/src/EventListener/TracingConsoleListener.php
index 5efbaf26..57c187a6 100644
--- a/src/EventListener/TracingConsoleListener.php
+++ b/src/EventListener/TracingConsoleListener.php
@@ -7,6 +7,7 @@
use Sentry\State\HubInterface;
use Sentry\Tracing\Span;
use Sentry\Tracing\SpanContext;
+use Sentry\Tracing\SpanStatus;
use Sentry\Tracing\Transaction;
use Sentry\Tracing\TransactionContext;
use Sentry\Tracing\TransactionSource;
@@ -59,18 +60,20 @@ public function handleConsoleCommandEvent(ConsoleCommandEvent $event): void
$currentSpan = $this->hub->getSpan();
if (null === $currentSpan) {
- $transactionContext = new TransactionContext();
- $transactionContext->setOp('console.command');
- $transactionContext->setName($this->getSpanName($command));
- $transactionContext->setSource(TransactionSource::task());
-
- $span = $this->hub->startTransaction($transactionContext);
+ $span = $this->hub->startTransaction(
+ TransactionContext::make()
+ ->setOp('console.command')
+ ->setOrigin('auto.console')
+ ->setName($this->getSpanName($command))
+ ->setSource(TransactionSource::task())
+ );
} else {
- $spanContext = new SpanContext();
- $spanContext->setOp('console.command');
- $spanContext->setDescription($this->getSpanName($command));
-
- $span = $currentSpan->startChild($spanContext);
+ $span = $currentSpan->startChild(
+ SpanContext::make()
+ ->setOp('console.command')
+ ->setOrigin('auto.console')
+ ->setDescription($this->getSpanName($command))
+ );
}
$this->hub->setSpan($span);
@@ -91,6 +94,7 @@ public function handleConsoleTerminateEvent(ConsoleTerminateEvent $event): void
$span = $this->hub->getSpan();
if (null !== $span) {
+ $span->setStatus(0 === $event->getExitCode() ? SpanStatus::ok() : SpanStatus::internalError());
$span->finish();
}
}
diff --git a/src/EventListener/TracingRequestListener.php b/src/EventListener/TracingRequestListener.php
index 56782fc5..f94f9dda 100644
--- a/src/EventListener/TracingRequestListener.php
+++ b/src/EventListener/TracingRequestListener.php
@@ -4,12 +4,16 @@
namespace Sentry\SentryBundle\EventListener;
+use Sentry\Integration\RequestFetcherInterface;
+use Sentry\SentryBundle\Integration\RequestFetcher;
+use Sentry\State\HubInterface;
use Sentry\Tracing\TransactionSource;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
use function Sentry\continueTrace;
+use function Sentry\metrics;
/**
* This event listener acts on the master requests and starts a transaction
@@ -19,6 +23,18 @@
*/
final class TracingRequestListener extends AbstractTracingRequestListener
{
+ /**
+ * @var RequestFetcherInterface|null
+ */
+ private $requestFetcher;
+
+ public function __construct(HubInterface $hub, ?RequestFetcherInterface $requestFetcher = null)
+ {
+ parent::__construct($hub);
+
+ $this->requestFetcher = $requestFetcher;
+ }
+
/**
* This method is called for each subrequest handled by the framework and
* starts a new {@see Transaction}.
@@ -34,26 +50,32 @@ public function handleKernelRequestEvent(RequestEvent $event): void
/** @var Request $request */
$request = $event->getRequest();
+ if ($this->requestFetcher instanceof RequestFetcher) {
+ $this->requestFetcher->setRequest($request);
+ }
+
/** @var float $requestStartTime */
$requestStartTime = $request->server->get('REQUEST_TIME_FLOAT', microtime(true));
$context = continueTrace(
- $request->headers->get('sentry-trace', ''),
+ $request->headers->get('sentry-trace') ?? $request->headers->get('traceparent', ''),
$request->headers->get('baggage', '')
);
+
$context->setOp('http.server');
+ $context->setOrigin('auto.http.server');
$routeName = $request->attributes->get('_route');
if (null !== $routeName && \is_string($routeName)) {
- $context->setName(sprintf('%s %s', $request->getMethod(), $routeName));
+ $context->setName(\sprintf('%s %s', $request->getMethod(), $routeName));
$context->setSource(TransactionSource::route());
} else {
- $context->setName(sprintf('%s %s%s%s', $request->getMethod(), $request->getSchemeAndHttpHost(), $request->getBaseUrl(), $request->getPathInfo()));
+ $context->setName(\sprintf('%s %s%s%s', $request->getMethod(), $request->getSchemeAndHttpHost(), $request->getBaseUrl(), $request->getPathInfo()));
$context->setSource(TransactionSource::url());
}
$context->setStartTimestamp($requestStartTime);
- $context->setTags($this->getTags($request));
+ $context->setData($this->getData($request));
$this->hub->setSpan($this->hub->startTransaction($context));
}
@@ -73,41 +95,47 @@ public function handleKernelTerminateEvent(TerminateEvent $event): void
}
$transaction->finish();
+ metrics()->flush();
+
+ if ($this->requestFetcher instanceof RequestFetcher) {
+ $this->requestFetcher->setRequest(null);
+ }
}
/**
- * Gets the tags to attach to the transaction.
+ * Gets the data to attach to the transaction.
*
* @param Request $request The HTTP request
*
* @return array
*/
- private function getTags(Request $request): array
+ private function getData(Request $request): array
{
$client = $this->hub->getClient();
$httpFlavor = $this->getHttpFlavor($request);
- $tags = [
+
+ $data = [
'net.host.port' => (string) $request->getPort(),
- 'http.method' => $request->getMethod(),
+ 'http.request.method' => $request->getMethod(),
'http.url' => $request->getUri(),
'route' => $this->getRouteName($request),
];
if (null !== $httpFlavor) {
- $tags['http.flavor'] = $httpFlavor;
+ $data['http.flavor'] = $httpFlavor;
}
if (false !== filter_var($request->getHost(), \FILTER_VALIDATE_IP)) {
- $tags['net.host.ip'] = $request->getHost();
+ $data['net.host.ip'] = $request->getHost();
} else {
- $tags['net.host.name'] = $request->getHost();
+ $data['net.host.name'] = $request->getHost();
}
if (null !== $request->getClientIp() && null !== $client && $client->getOptions()->shouldSendDefaultPii()) {
- $tags['net.peer.ip'] = $request->getClientIp();
+ $data['net.peer.ip'] = $request->getClientIp();
}
- return $tags;
+ return $data;
}
/**
diff --git a/src/EventListener/TracingSubRequestListener.php b/src/EventListener/TracingSubRequestListener.php
index b61b15b4..71198bb1 100644
--- a/src/EventListener/TracingSubRequestListener.php
+++ b/src/EventListener/TracingSubRequestListener.php
@@ -34,16 +34,19 @@ public function handleKernelRequestEvent(RequestEvent $event): void
return;
}
- $spanContext = new SpanContext();
- $spanContext->setOp('http.server');
- $spanContext->setDescription(sprintf('%s %s%s%s', $request->getMethod(), $request->getSchemeAndHttpHost(), $request->getBaseUrl(), $request->getPathInfo()));
- $spanContext->setTags([
- 'http.method' => $request->getMethod(),
- 'http.url' => $request->getUri(),
- 'route' => $this->getRouteName($request),
- ]);
-
- $this->hub->setSpan($span->startChild($spanContext));
+ $this->hub->setSpan(
+ $span->startChild(
+ SpanContext::make()
+ ->setOp('http.server')
+ ->setData([
+ 'http.request.method' => $request->getMethod(),
+ 'http.url' => $request->getUri(),
+ 'route' => $this->getRouteName($request),
+ ])
+ ->setOrigin('auto.http.server')
+ ->setDescription(\sprintf('%s %s%s%s', $request->getMethod(), $request->getSchemeAndHttpHost(), $request->getBaseUrl(), $request->getPathInfo()))
+ )
+ );
}
/**
diff --git a/src/Integration/RequestFetcher.php b/src/Integration/RequestFetcher.php
index f5911b2c..2f2d2cd2 100644
--- a/src/Integration/RequestFetcher.php
+++ b/src/Integration/RequestFetcher.php
@@ -4,11 +4,12 @@
namespace Sentry\SentryBundle\Integration;
-use Http\Discovery\Psr17FactoryDiscovery;
+use GuzzleHttp\Psr7\HttpFactory;
use Psr\Http\Message\ServerRequestInterface;
use Sentry\Integration\RequestFetcherInterface;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
/**
@@ -23,6 +24,11 @@ final class RequestFetcher implements RequestFetcherInterface
*/
private $requestStack;
+ /**
+ * @var Request|null The current request
+ */
+ private $currentRequest;
+
/**
* @var HttpMessageFactoryInterface The factory to convert Symfony requests to PSR-7 requests
*/
@@ -38,10 +44,10 @@ public function __construct(RequestStack $requestStack, ?HttpMessageFactoryInter
{
$this->requestStack = $requestStack;
$this->httpMessageFactory = $httpMessageFactory ?? new PsrHttpFactory(
- Psr17FactoryDiscovery::findServerRequestFactory(),
- Psr17FactoryDiscovery::findStreamFactory(),
- Psr17FactoryDiscovery::findUploadedFileFactory(),
- Psr17FactoryDiscovery::findResponseFactory()
+ new HttpFactory(),
+ new HttpFactory(),
+ new HttpFactory(),
+ new HttpFactory()
);
}
@@ -50,7 +56,7 @@ public function __construct(RequestStack $requestStack, ?HttpMessageFactoryInter
*/
public function fetchRequest(): ?ServerRequestInterface
{
- $request = $this->requestStack->getCurrentRequest();
+ $request = $this->currentRequest ?? $this->requestStack->getCurrentRequest();
if (null === $request) {
return null;
@@ -62,4 +68,9 @@ public function fetchRequest(): ?ServerRequestInterface
return null;
}
}
+
+ public function setRequest(?Request $request): void
+ {
+ $this->currentRequest = $request;
+ }
}
diff --git a/src/Resources/config/schema/sentry-1.0.xsd b/src/Resources/config/schema/sentry-1.0.xsd
index 78ed5033..3bad5d57 100644
--- a/src/Resources/config/schema/sentry-1.0.xsd
+++ b/src/Resources/config/schema/sentry-1.0.xsd
@@ -35,28 +35,38 @@
-
+
+
+
+
+
+
+
+
-
+
+
+
+
diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml
index 3f67a790..ff0407e0 100644
--- a/src/Resources/config/services.xml
+++ b/src/Resources/config/services.xml
@@ -9,15 +9,6 @@
-
-
-
-
-
- null
-
-
-
@@ -57,6 +48,7 @@
+
@@ -91,10 +83,11 @@
-
+
+
@@ -104,6 +97,7 @@
+
diff --git a/src/SentryBundle.php b/src/SentryBundle.php
index ce44e51f..596b5b94 100644
--- a/src/SentryBundle.php
+++ b/src/SentryBundle.php
@@ -8,6 +8,7 @@
use Sentry\SentryBundle\DependencyInjection\Compiler\CacheTracingPass;
use Sentry\SentryBundle\DependencyInjection\Compiler\DbalTracingPass;
use Sentry\SentryBundle\DependencyInjection\Compiler\HttpClientTracingPass;
+use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -15,13 +16,13 @@ final class SentryBundle extends Bundle
{
public const SDK_IDENTIFIER = 'sentry.php.symfony';
- public const SDK_VERSION = '4.10.0';
+ public const SDK_VERSION = '5.2.0';
public function build(ContainerBuilder $container): void
{
parent::build($container);
- $container->addCompilerPass(new DbalTracingPass());
+ $container->addCompilerPass(new DbalTracingPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 10);
$container->addCompilerPass(new CacheTracingPass());
$container->addCompilerPass(new HttpClientTracingPass());
$container->addCompilerPass(new AddLoginListenerTagPass());
diff --git a/src/Tracing/Cache/TraceableCacheAdapterForV2.php b/src/Tracing/Cache/TraceableCacheAdapterForV2.php
index 7a5b57c9..e8ed9330 100644
--- a/src/Tracing/Cache/TraceableCacheAdapterForV2.php
+++ b/src/Tracing/Cache/TraceableCacheAdapterForV2.php
@@ -40,11 +40,11 @@ public function __construct(HubInterface $hub, AdapterInterface $decoratedAdapte
*
* @return mixed
*/
- public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
+ public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
{
return $this->traceFunction('cache.get_item', function () use ($key, $callback, $beta, &$metadata) {
if (!$this->decoratedAdapter instanceof CacheInterface) {
- throw new \BadMethodCallException(sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
+ throw new \BadMethodCallException(\sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
}
return $this->decoratedAdapter->get($key, $callback, $beta, $metadata);
diff --git a/src/Tracing/Cache/TraceableCacheAdapterForV3.php b/src/Tracing/Cache/TraceableCacheAdapterForV3.php
index 564acff3..ef276620 100644
--- a/src/Tracing/Cache/TraceableCacheAdapterForV3.php
+++ b/src/Tracing/Cache/TraceableCacheAdapterForV3.php
@@ -38,11 +38,11 @@ public function __construct(HubInterface $hub, AdapterInterface $decoratedAdapte
*
* @param mixed[] $metadata
*/
- public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed
+ public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null): mixed
{
return $this->traceFunction('cache.get_item', function () use ($key, $callback, $beta, &$metadata) {
if (!$this->decoratedAdapter instanceof CacheInterface) {
- throw new \BadMethodCallException(sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
+ throw new \BadMethodCallException(\sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
}
return $this->decoratedAdapter->get($key, $callback, $beta, $metadata);
diff --git a/src/Tracing/Cache/TraceableCacheAdapterTrait.php b/src/Tracing/Cache/TraceableCacheAdapterTrait.php
index 0bf2bc2f..b2af9a59 100644
--- a/src/Tracing/Cache/TraceableCacheAdapterTrait.php
+++ b/src/Tracing/Cache/TraceableCacheAdapterTrait.php
@@ -70,7 +70,7 @@ public function delete(string $key): bool
{
return $this->traceFunction('cache.delete_item', function () use ($key): bool {
if (!$this->decoratedAdapter instanceof CacheInterface) {
- throw new \BadMethodCallException(sprintf('The %s::delete() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
+ throw new \BadMethodCallException(\sprintf('The %s::delete() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
}
return $this->decoratedAdapter->delete($key);
@@ -168,13 +168,15 @@ public function reset(): void
*
* @phpstan-return TResult
*/
- private function traceFunction(string $spanOperation, \Closure $callback, string $spanDescription = null)
+ private function traceFunction(string $spanOperation, \Closure $callback, ?string $spanDescription = null)
{
$span = $this->hub->getSpan();
if (null !== $span) {
- $spanContext = new SpanContext();
- $spanContext->setOp($spanOperation);
+ $spanContext = SpanContext::make()
+ ->setOp($spanOperation)
+ ->setOrigin('auto.cache');
+
if (null !== $spanDescription) {
$spanContext->setDescription(urldecode($spanDescription));
}
@@ -190,4 +192,17 @@ private function traceFunction(string $spanOperation, \Closure $callback, string
}
}
}
+
+ /**
+ * @phpstan-param \Closure(CacheItem): CacheItem $callback
+ * @phpstan-param string $key
+ *
+ * @phpstan-return callable(): CacheItem
+ */
+ private function setCallbackWrapper(callable $callback, string $key): callable
+ {
+ return function () use ($callback, $key): CacheItem {
+ return $callback($this->decoratedAdapter->getItem($key));
+ };
+ }
}
diff --git a/src/Tracing/Cache/TraceableTagAwareCacheAdapterForV2.php b/src/Tracing/Cache/TraceableTagAwareCacheAdapterForV2.php
index a89d911f..62477cc2 100644
--- a/src/Tracing/Cache/TraceableTagAwareCacheAdapterForV2.php
+++ b/src/Tracing/Cache/TraceableTagAwareCacheAdapterForV2.php
@@ -41,11 +41,11 @@ public function __construct(HubInterface $hub, TagAwareAdapterInterface $decorat
*
* @return mixed
*/
- public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
+ public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
{
return $this->traceFunction('cache.get_item', function () use ($key, $callback, $beta, &$metadata) {
if (!$this->decoratedAdapter instanceof CacheInterface) {
- throw new \BadMethodCallException(sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
+ throw new \BadMethodCallException(\sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
}
return $this->decoratedAdapter->get($key, $callback, $beta, $metadata);
diff --git a/src/Tracing/Cache/TraceableTagAwareCacheAdapterForV3.php b/src/Tracing/Cache/TraceableTagAwareCacheAdapterForV3.php
index 733b4555..cc5cc7b4 100644
--- a/src/Tracing/Cache/TraceableTagAwareCacheAdapterForV3.php
+++ b/src/Tracing/Cache/TraceableTagAwareCacheAdapterForV3.php
@@ -39,11 +39,11 @@ public function __construct(HubInterface $hub, TagAwareAdapterInterface $decorat
*
* @param mixed[] $metadata
*/
- public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed
+ public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null): mixed
{
return $this->traceFunction('cache.get_item', function () use ($key, $callback, $beta, &$metadata) {
if (!$this->decoratedAdapter instanceof CacheInterface) {
- throw new \BadMethodCallException(sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
+ throw new \BadMethodCallException(\sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
}
return $this->decoratedAdapter->get($key, $callback, $beta, $metadata);
diff --git a/src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactory.php b/src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryForV2V3.php
similarity index 95%
rename from src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactory.php
rename to src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryForV2V3.php
index 7b5def41..9404f71b 100644
--- a/src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactory.php
+++ b/src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryForV2V3.php
@@ -18,7 +18,7 @@
/**
* @internal
*/
-final class TracingDriverConnectionFactory implements TracingDriverConnectionFactoryInterface
+final class TracingDriverConnectionFactoryForV2V3 implements TracingDriverConnectionFactoryInterface
{
/**
* @var HubInterface The current hub
diff --git a/src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryForV4.php b/src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryForV4.php
new file mode 100644
index 00000000..46c64bb6
--- /dev/null
+++ b/src/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryForV4.php
@@ -0,0 +1,78 @@
+hub = $hub;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function create(Connection $connection, AbstractPlatform $databasePlatform, array $params): TracingDriverConnectionInterface
+ {
+ $tracingDriverConnection = new TracingDriverConnection(
+ $this->hub,
+ $connection,
+ $this->getDatabasePlatform($databasePlatform),
+ $params
+ );
+
+ return $tracingDriverConnection;
+ }
+
+ private function getDatabasePlatform(AbstractPlatform $databasePlatform): string
+ {
+ // https://github.com/open-telemetry/opentelemetry-specification/blob/33113489fb5a1b6da563abb4ffa541447b87f515/specification/trace/semantic_conventions/database.md#connection-level-attributes
+ switch (true) {
+ case $databasePlatform instanceof AbstractMySQLPlatform:
+ return 'mysql';
+
+ case $databasePlatform instanceof DB2Platform:
+ return 'db2';
+
+ case $databasePlatform instanceof OraclePlatform:
+ return 'oracle';
+
+ case $databasePlatform instanceof PostgreSQLPlatform:
+ return 'postgresql';
+
+ case $databasePlatform instanceof SQLitePlatform:
+ return 'sqlite';
+
+ case $databasePlatform instanceof SQLServerPlatform:
+ return 'mssql';
+
+ default:
+ return 'other_sql';
+ }
+ }
+}
diff --git a/src/Tracing/Doctrine/DBAL/TracingDriverConnection.php b/src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV2V3.php
similarity index 83%
rename from src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
rename to src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV2V3.php
index 6233cd17..1010fb5b 100644
--- a/src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
+++ b/src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV2V3.php
@@ -14,11 +14,11 @@
/**
* This implementation wraps a driver connection and adds distributed tracing
* capabilities to Doctrine DBAL. This implementation IS and MUST be compatible
- * with all versions of Doctrine DBAL >= 2.10.
+ * with all versions of Doctrine DBAL >= 2.10 and >= 3.3.
*
* @phpstan-import-type Params from \Doctrine\DBAL\DriverManager as ConnectionParams
*/
-final class TracingDriverConnection implements TracingDriverConnectionInterface
+final class TracingDriverConnectionForV2V3 implements TracingDriverConnectionInterface
{
/**
* @internal
@@ -60,13 +60,6 @@ final class TracingDriverConnection implements TracingDriverConnectionInterface
*/
private $decoratedConnection;
- /**
- * @var array The span tags
- *
- * @deprecated since version 4.10, to be removed in 5.0. Use $spanData instead.
- */
- private $spanTags = [];
-
/**
* @var array The data to attach to the span
*/
@@ -90,7 +83,7 @@ public function __construct(
) {
$this->hub = $hub;
$this->decoratedConnection = $decoratedConnection;
- $this->spanData = $this->getSpanTags($databasePlatform, $params);
+ $this->spanData = $this->getSpanData($databasePlatform, $params);
}
/**
@@ -183,7 +176,7 @@ public function rollBack(): bool
public function getNativeConnection()
{
if (!method_exists($this->decoratedConnection, 'getNativeConnection')) {
- throw new \BadMethodCallException(sprintf('The connection "%s" does not support accessing the native connection.', \get_class($this->decoratedConnection)));
+ throw new \BadMethodCallException(\sprintf('The connection "%s" does not support accessing the native connection.', \get_class($this->decoratedConnection)));
}
return $this->decoratedConnection->getNativeConnection();
@@ -198,7 +191,7 @@ public function errorCode(): ?string
return $this->decoratedConnection->errorCode();
}
- throw new \BadMethodCallException(sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
+ throw new \BadMethodCallException(\sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
}
/**
@@ -210,7 +203,7 @@ public function errorInfo(): array
return $this->decoratedConnection->errorInfo();
}
- throw new \BadMethodCallException(sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
+ throw new \BadMethodCallException(\sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
}
public function getWrappedConnection(): DriverConnectionInterface
@@ -230,12 +223,13 @@ private function traceFunction(string $spanOperation, string $spanDescription, \
$span = $this->hub->getSpan();
if (null !== $span) {
- $spanContext = new SpanContext();
- $spanContext->setOp($spanOperation);
- $spanContext->setDescription($spanDescription);
- $spanContext->setData($this->spanData);
-
- $span = $span->startChild($spanContext);
+ $span = $span->startChild(
+ SpanContext::make()
+ ->setOp($spanOperation)
+ ->setData($this->spanData)
+ ->setOrigin('auto.db')
+ ->setDescription($spanDescription)
+ );
}
try {
@@ -258,7 +252,7 @@ private function traceFunction(string $spanOperation, string $spanDescription, \
*
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md
*/
- private function getSpanTags(string $databasePlatform, array $params): array
+ private function getSpanData(string $databasePlatform, array $params): array
{
$data = ['db.system' => $databasePlatform];
@@ -272,20 +266,20 @@ private function getSpanTags(string $databasePlatform, array $params): array
if (isset($params['host']) && !empty($params['host']) && !isset($params['memory'])) {
if (false === filter_var($params['host'], \FILTER_VALIDATE_IP)) {
- $data['net.peer.name'] = $params['host'];
+ $data['server.address'] = $params['host'];
} else {
- $data['net.peer.ip'] = $params['host'];
+ $data['server.address'] = $params['host'];
}
}
if (isset($params['port'])) {
- $data['net.peer.port'] = (string) $params['port'];
+ $data['server.port'] = (string) $params['port'];
}
if (isset($params['unix_socket'])) {
- $data['net.transport'] = 'Unix';
+ $data['server.socket.address'] = 'Unix';
} elseif (isset($params['memory'])) {
- $data['net.transport'] = 'inproc';
+ $data['server.socket.address'] = 'inproc';
}
return $data;
diff --git a/src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4.php b/src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4.php
new file mode 100644
index 00000000..2f506b25
--- /dev/null
+++ b/src/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4.php
@@ -0,0 +1,285 @@
+= 4.0.
+ *
+ * @phpstan-import-type Params from \Doctrine\DBAL\DriverManager as ConnectionParams
+ */
+final class TracingDriverConnectionForV4 implements TracingDriverConnectionInterface
+{
+ /**
+ * @internal
+ */
+ public const SPAN_OP_CONN_PREPARE = 'db.sql.prepare';
+
+ /**
+ * @internal
+ */
+ public const SPAN_OP_CONN_QUERY = 'db.sql.query';
+
+ /**
+ * @internal
+ */
+ public const SPAN_OP_CONN_EXEC = 'db.sql.exec';
+
+ /**
+ * @internal
+ */
+ public const SPAN_OP_CONN_BEGIN_TRANSACTION = 'db.sql.transaction.begin';
+
+ /**
+ * @internal
+ */
+ public const SPAN_OP_TRANSACTION_COMMIT = 'db.sql.transaction.commit';
+
+ /**
+ * @internal
+ */
+ public const SPAN_OP_TRANSACTION_ROLLBACK = 'db.sql.transaction.rollback';
+
+ /**
+ * @var HubInterface The current hub
+ */
+ private $hub;
+
+ /**
+ * @var DriverConnectionInterface The decorated connection
+ */
+ private $decoratedConnection;
+
+ /**
+ * @var array The data to attach to the span
+ */
+ private $spanData;
+
+ /**
+ * Constructor.
+ *
+ * @param HubInterface $hub The current hub
+ * @param DriverConnectionInterface $decoratedConnection The connection to decorate
+ * @param string $databasePlatform The name of the database platform
+ * @param array $params The connection params
+ *
+ * @phpstan-param ConnectionParams $params
+ */
+ public function __construct(
+ HubInterface $hub,
+ DriverConnectionInterface $decoratedConnection,
+ string $databasePlatform,
+ array $params
+ ) {
+ $this->hub = $hub;
+ $this->decoratedConnection = $decoratedConnection;
+ $this->spanData = $this->getSpanData($databasePlatform, $params);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prepare($sql): Statement
+ {
+ $statement = $this->traceFunction(self::SPAN_OP_CONN_PREPARE, $sql, function () use ($sql): Statement {
+ return $this->decoratedConnection->prepare($sql);
+ });
+
+ return new TracingStatement($this->hub, $statement, $sql, $this->spanData);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function query(?string $sql = null, ...$args): Result
+ {
+ return $this->traceFunction(self::SPAN_OP_CONN_QUERY, $sql, function () use ($sql, $args): Result {
+ return $this->decoratedConnection->query($sql, ...$args);
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function quote(string $value): string
+ {
+ return $this->decoratedConnection->quote($value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function exec(string $sql): int|string
+ {
+ return $this->traceFunction(self::SPAN_OP_CONN_EXEC, $sql, function () use ($sql): int|string {
+ return $this->decoratedConnection->exec($sql);
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return string|int
+ */
+ public function lastInsertId(): string|int
+ {
+ return $this->decoratedConnection->lastInsertId();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function beginTransaction(): void
+ {
+ $this->traceFunction(self::SPAN_OP_CONN_BEGIN_TRANSACTION, 'BEGIN TRANSACTION', function (): void {
+ $this->decoratedConnection->beginTransaction();
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function commit(): void
+ {
+ $this->traceFunction(self::SPAN_OP_TRANSACTION_COMMIT, 'COMMIT', function (): void {
+ $this->decoratedConnection->commit();
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function rollBack(): void
+ {
+ $this->traceFunction(self::SPAN_OP_TRANSACTION_ROLLBACK, 'ROLLBACK', function (): void {
+ $this->decoratedConnection->rollBack();
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return resource|object
+ */
+ public function getNativeConnection()
+ {
+ return $this->decoratedConnection->getNativeConnection();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function errorCode(): ?string
+ {
+ if (method_exists($this->decoratedConnection, 'errorCode')) {
+ return $this->decoratedConnection->errorCode();
+ }
+
+ throw new \BadMethodCallException(\sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function errorInfo(): array
+ {
+ if (method_exists($this->decoratedConnection, 'errorInfo')) {
+ return $this->decoratedConnection->errorInfo();
+ }
+
+ throw new \BadMethodCallException(\sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
+ }
+
+ public function getWrappedConnection(): DriverConnectionInterface
+ {
+ return $this->decoratedConnection;
+ }
+
+ public function getServerVersion(): string
+ {
+ return $this->decoratedConnection->getServerVersion();
+ }
+
+ /**
+ * @phpstan-template T
+ *
+ * @phpstan-param \Closure(): T $callback
+ *
+ * @phpstan-return T
+ */
+ private function traceFunction(string $spanOperation, string $spanDescription, \Closure $callback)
+ {
+ $span = $this->hub->getSpan();
+
+ if (null !== $span) {
+ $span = $span->startChild(
+ SpanContext::make()
+ ->setOp($spanOperation)
+ ->setData($this->spanData)
+ ->setOrigin('auto.db')
+ ->setDescription($spanDescription)
+ );
+ }
+
+ try {
+ return $callback();
+ } finally {
+ if (null !== $span) {
+ $span->finish();
+ }
+ }
+ }
+
+ /**
+ * Gets a map of key-value pairs that will be set as the span data.
+ *
+ * @param array $params The connection params
+ *
+ * @return array
+ *
+ * @phpstan-param ConnectionParams $params
+ *
+ * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md
+ */
+ private function getSpanData(string $databasePlatform, array $params): array
+ {
+ $data = ['db.system' => $databasePlatform];
+
+ if (isset($params['user'])) {
+ $data['db.user'] = $params['user'];
+ }
+
+ if (isset($params['dbname'])) {
+ $data['db.name'] = $params['dbname'];
+ }
+
+ if (isset($params['host']) && !empty($params['host']) && !isset($params['memory'])) {
+ if (false === filter_var($params['host'], \FILTER_VALIDATE_IP)) {
+ $data['server.address'] = $params['host'];
+ } else {
+ $data['server.address'] = $params['host'];
+ }
+ }
+
+ if (isset($params['port'])) {
+ $data['server.port'] = (string) $params['port'];
+ }
+
+ if (isset($params['unix_socket'])) {
+ $data['server.socket.address'] = 'Unix';
+ } elseif (isset($params['memory'])) {
+ $data['server.socket.address'] = 'inproc';
+ }
+
+ return $data;
+ }
+}
diff --git a/src/Tracing/Doctrine/DBAL/TracingDriverForV3.php b/src/Tracing/Doctrine/DBAL/TracingDriverForV3.php
index 6b46bf47..894aecca 100644
--- a/src/Tracing/Doctrine/DBAL/TracingDriverForV3.php
+++ b/src/Tracing/Doctrine/DBAL/TracingDriverForV3.php
@@ -4,12 +4,8 @@
namespace Sentry\SentryBundle\Tracing\Doctrine\DBAL;
-use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
-use Doctrine\DBAL\Driver\API\ExceptionConverter;
-use Doctrine\DBAL\Platforms\AbstractPlatform;
-use Doctrine\DBAL\Schema\AbstractSchemaManager;
-use Doctrine\DBAL\VersionAwarePlatformDriver;
+use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
/**
* This is a simple implementation of the {@see Driver} interface that decorates
@@ -20,18 +16,13 @@
*
* @phpstan-import-type Params from \Doctrine\DBAL\DriverManager as ConnectionParams
*/
-final class TracingDriverForV3 implements Driver, VersionAwarePlatformDriver
+final class TracingDriverForV3 extends AbstractDriverMiddleware
{
/**
* @var TracingDriverConnectionFactoryInterface The connection factory
*/
private $connectionFactory;
- /**
- * @var Driver|VersionAwarePlatformDriver The instance of the decorated driver
- */
- private $decoratedDriver;
-
/**
* Constructor.
*
@@ -40,8 +31,9 @@ final class TracingDriverForV3 implements Driver, VersionAwarePlatformDriver
*/
public function __construct(TracingDriverConnectionFactoryInterface $connectionFactory, Driver $decoratedDriver)
{
+ parent::__construct($decoratedDriver);
+
$this->connectionFactory = $connectionFactory;
- $this->decoratedDriver = $decoratedDriver;
}
/**
@@ -52,51 +44,9 @@ public function __construct(TracingDriverConnectionFactoryInterface $connectionF
public function connect(array $params): TracingDriverConnectionInterface
{
return $this->connectionFactory->create(
- $this->decoratedDriver->connect($params),
- $this->decoratedDriver->getDatabasePlatform(),
+ parent::connect($params),
+ $this->getDatabasePlatform(),
$params
);
}
-
- /**
- * {@inheritdoc}
- */
- public function getDatabasePlatform(): AbstractPlatform
- {
- return $this->decoratedDriver->getDatabasePlatform();
- }
-
- /**
- * {@inheritdoc}
- *
- * @phpstan-template T of AbstractPlatform
- *
- * @phpstan-param T $platform
- *
- * @phpstan-return AbstractSchemaManager
- */
- public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager
- {
- return $this->decoratedDriver->getSchemaManager($conn, $platform);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getExceptionConverter(): ExceptionConverter
- {
- return $this->decoratedDriver->getExceptionConverter();
- }
-
- /**
- * {@inheritdoc}
- */
- public function createDatabasePlatformForVersion($version): AbstractPlatform
- {
- if ($this->decoratedDriver instanceof VersionAwarePlatformDriver) {
- return $this->decoratedDriver->createDatabasePlatformForVersion($version);
- }
-
- return $this->getDatabasePlatform();
- }
}
diff --git a/src/Tracing/Doctrine/DBAL/TracingDriverForV4.php b/src/Tracing/Doctrine/DBAL/TracingDriverForV4.php
new file mode 100644
index 00000000..1f9c4f56
--- /dev/null
+++ b/src/Tracing/Doctrine/DBAL/TracingDriverForV4.php
@@ -0,0 +1,56 @@
+= 4.0.
+ *
+ * @internal
+ *
+ * @psalm-import-type Params from \Doctrine\DBAL\DriverManager
+ */
+final class TracingDriverForV4 extends AbstractDriverMiddleware
+{
+ /**
+ * @var TracingDriverConnectionFactoryInterface The connection factory
+ */
+ private $connectionFactory;
+
+ /**
+ * Constructor.
+ *
+ * @param TracingDriverConnectionFactoryInterface $connectionFactory The connection factory
+ * @param Driver $decoratedDriver The instance of the driver to decorate
+ */
+ public function __construct(TracingDriverConnectionFactoryInterface $connectionFactory, Driver $decoratedDriver)
+ {
+ parent::__construct($decoratedDriver);
+
+ $this->connectionFactory = $connectionFactory;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @psalm-param Params $params All connection parameters.
+ */
+ public function connect(array $params): TracingDriverConnectionInterface
+ {
+ $connection = parent::connect($params);
+ $versionProvider = new StaticServerVersionProvider($connection->getServerVersion());
+
+ return $this->connectionFactory->create(
+ $connection,
+ $this->getDatabasePlatform($versionProvider),
+ $params
+ );
+ }
+}
diff --git a/src/Tracing/Doctrine/DBAL/TracingDriverMiddleware.php b/src/Tracing/Doctrine/DBAL/TracingDriverMiddleware.php
index 0ef93b70..d9129441 100644
--- a/src/Tracing/Doctrine/DBAL/TracingDriverMiddleware.php
+++ b/src/Tracing/Doctrine/DBAL/TracingDriverMiddleware.php
@@ -31,11 +31,11 @@ public function __construct($hubOrConnectionFactory)
if ($hubOrConnectionFactory instanceof TracingDriverConnectionFactoryInterface) {
$this->connectionFactory = $hubOrConnectionFactory;
} elseif ($hubOrConnectionFactory instanceof HubInterface) {
- @trigger_error(sprintf('Not passing an instance of the "%s" interface as argument of the constructor is deprecated since version 4.2 and will not work since version 5.0.', TracingDriverConnectionFactoryInterface::class), \E_USER_DEPRECATED);
+ @trigger_error(\sprintf('Not passing an instance of the "%s" interface as argument of the constructor is deprecated since version 4.2 and will not work since version 5.0.', TracingDriverConnectionFactoryInterface::class), \E_USER_DEPRECATED);
$this->connectionFactory = new TracingDriverConnectionFactory($hubOrConnectionFactory);
} else {
- throw new \InvalidArgumentException(sprintf('The constructor requires either an instance of the "%s" interface or an instance of the "%s" interface.', HubInterface::class, TracingDriverConnectionFactoryInterface::class));
+ throw new \InvalidArgumentException(\sprintf('The constructor requires either an instance of the "%s" interface or an instance of the "%s" interface.', HubInterface::class, TracingDriverConnectionFactoryInterface::class));
}
}
diff --git a/src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php b/src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php
index de1270bd..30945e97 100644
--- a/src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php
+++ b/src/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnection.php
@@ -109,7 +109,7 @@ public function getServerVersion(): string
$wrappedConnection = $this->getWrappedConnection();
if (!$wrappedConnection instanceof ServerInfoAwareConnection) {
- throw new \BadMethodCallException(sprintf('The wrapped connection must be an instance of the "%s" interface.', ServerInfoAwareConnection::class));
+ throw new \BadMethodCallException(\sprintf('The wrapped connection must be an instance of the "%s" interface.', ServerInfoAwareConnection::class));
}
return $wrappedConnection->getServerVersion();
@@ -123,7 +123,7 @@ public function getServerVersion(): string
public function getNativeConnection()
{
if (!method_exists($this->decoratedConnection, 'getNativeConnection')) {
- throw new \BadMethodCallException(sprintf('The connection "%s" does not support accessing the native connection.', \get_class($this->decoratedConnection)));
+ throw new \BadMethodCallException(\sprintf('The connection "%s" does not support accessing the native connection.', \get_class($this->decoratedConnection)));
}
return $this->decoratedConnection->getNativeConnection();
@@ -137,11 +137,11 @@ public function requiresQueryForServerVersion(): bool
$wrappedConnection = $this->getWrappedConnection();
if (!$wrappedConnection instanceof ServerInfoAwareConnection) {
- throw new \BadMethodCallException(sprintf('The wrapped connection must be an instance of the "%s" interface.', ServerInfoAwareConnection::class));
+ throw new \BadMethodCallException(\sprintf('The wrapped connection must be an instance of the "%s" interface.', ServerInfoAwareConnection::class));
}
if (!method_exists($wrappedConnection, 'requiresQueryForServerVersion')) {
- throw new \BadMethodCallException(sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
+ throw new \BadMethodCallException(\sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
}
return $wrappedConnection->requiresQueryForServerVersion();
@@ -156,7 +156,7 @@ public function errorCode(): ?string
return $this->decoratedConnection->errorCode();
}
- throw new \BadMethodCallException(sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
+ throw new \BadMethodCallException(\sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
}
/**
@@ -168,7 +168,7 @@ public function errorInfo(): array
return $this->decoratedConnection->errorInfo();
}
- throw new \BadMethodCallException(sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
+ throw new \BadMethodCallException(\sprintf('The %s() method is not supported on Doctrine DBAL 3.0.', __METHOD__));
}
public function getWrappedConnection(): Connection
diff --git a/src/Tracing/Doctrine/DBAL/TracingStatementForV2.php b/src/Tracing/Doctrine/DBAL/TracingStatementForV2.php
index ca9728ce..a506a17a 100644
--- a/src/Tracing/Doctrine/DBAL/TracingStatementForV2.php
+++ b/src/Tracing/Doctrine/DBAL/TracingStatementForV2.php
@@ -116,10 +116,11 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le
*/
public function execute($params = null): bool
{
- $spanContext = new SpanContext();
- $spanContext->setOp(self::SPAN_OP_STMT_EXECUTE);
- $spanContext->setDescription($this->sqlQuery);
- $spanContext->setData($this->spanData);
+ $spanContext = SpanContext::make()
+ ->setOp(self::SPAN_OP_STMT_EXECUTE)
+ ->setData($this->spanData)
+ ->setOrigin('auto.db')
+ ->setDescription($this->sqlQuery);
return $this->traceFunction($spanContext, [$this->decoratedStatement, 'execute'], $params);
}
diff --git a/src/Tracing/Doctrine/DBAL/TracingStatementForV3.php b/src/Tracing/Doctrine/DBAL/TracingStatementForV3.php
index b2e8f1a0..45487a5a 100644
--- a/src/Tracing/Doctrine/DBAL/TracingStatementForV3.php
+++ b/src/Tracing/Doctrine/DBAL/TracingStatementForV3.php
@@ -35,10 +35,11 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le
*/
public function execute($params = null): Result
{
- $spanContext = new SpanContext();
- $spanContext->setOp(self::SPAN_OP_STMT_EXECUTE);
- $spanContext->setDescription($this->sqlQuery);
- $spanContext->setData($this->spanData);
+ $spanContext = SpanContext::make()
+ ->setOp(self::SPAN_OP_STMT_EXECUTE)
+ ->setData($this->spanData)
+ ->setOrigin('auto.db')
+ ->setDescription($this->sqlQuery);
return $this->traceFunction($spanContext, [$this->decoratedStatement, 'execute'], $params);
}
diff --git a/src/Tracing/Doctrine/DBAL/TracingStatementForV4.php b/src/Tracing/Doctrine/DBAL/TracingStatementForV4.php
new file mode 100644
index 00000000..5537549e
--- /dev/null
+++ b/src/Tracing/Doctrine/DBAL/TracingStatementForV4.php
@@ -0,0 +1,38 @@
+decoratedStatement->bindValue($param, $value, $type);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function execute(): Result
+ {
+ $spanContext = SpanContext::make()
+ ->setOp(self::SPAN_OP_STMT_EXECUTE)
+ ->setData($this->spanData)
+ ->setOrigin('auto.db')
+ ->setDescription($this->sqlQuery);
+
+ return $this->traceFunction($spanContext, [$this->decoratedStatement, 'execute']);
+ }
+}
diff --git a/src/Tracing/HttpClient/AbstractTraceableHttpClient.php b/src/Tracing/HttpClient/AbstractTraceableHttpClient.php
index 8dc86020..d1f83b2c 100644
--- a/src/Tracing/HttpClient/AbstractTraceableHttpClient.php
+++ b/src/Tracing/HttpClient/AbstractTraceableHttpClient.php
@@ -18,6 +18,7 @@
use function Sentry\getBaggage;
use function Sentry\getTraceparent;
+use function Sentry\getW3CTraceparent;
/**
* This is an implementation of the {@see HttpClientInterface} that decorates
@@ -58,6 +59,7 @@ public function request(string $method, string $url, array $options = []): Respo
if (self::shouldAttachTracingHeaders($client, $uri)) {
$headers['baggage'] = getBaggage();
$headers['sentry-trace'] = getTraceparent();
+ $headers['traceparent'] = getW3CTraceparent();
}
$options['headers'] = $headers;
@@ -72,23 +74,29 @@ public function request(string $method, string $url, array $options = []): Respo
'path' => $uri->getPath(),
]);
- $context = new SpanContext();
- $context->setOp('http.client');
- $context->setDescription($method . ' ' . (string) $partialUri);
- $context->setTags([
- 'http.method' => $method,
+ $context = SpanContext::make()
+ ->setOp('http.client')
+ ->setOrigin('auto.http.client')
+ ->setDescription($method . ' ' . (string) $partialUri);
+
+ $contextData = [
'http.url' => (string) $partialUri,
- ]);
- $context->setData([
- 'http.query' => $uri->getQuery(),
- 'http.fragment' => $uri->getFragment(),
- ]);
+ 'http.request.method' => $method,
+ ];
+ if ('' !== $uri->getQuery()) {
+ $contextData['http.query'] = $uri->getQuery();
+ }
+ if ('' !== $uri->getFragment()) {
+ $contextData['http.fragment'] = $uri->getFragment();
+ }
+ $context->setData($contextData);
$childSpan = $span->startChild($context);
if (self::shouldAttachTracingHeaders($client, $uri)) {
$headers['baggage'] = $childSpan->toBaggage();
$headers['sentry-trace'] = $childSpan->toTraceparent();
+ $headers['traceparent'] = $childSpan->toW3CTraceparent();
}
$options['headers'] = $headers;
@@ -99,12 +107,12 @@ public function request(string $method, string $url, array $options = []): Respo
/**
* {@inheritdoc}
*/
- public function stream($responses, float $timeout = null): ResponseStreamInterface
+ public function stream($responses, ?float $timeout = null): ResponseStreamInterface
{
if ($responses instanceof AbstractTraceableResponse) {
$responses = [$responses];
} elseif (!is_iterable($responses)) {
- throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of TraceableResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
+ throw new \TypeError(\sprintf('"%s()" expects parameter 1 to be an iterable of TraceableResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
}
return new ResponseStream(AbstractTraceableResponse::stream($this->client, $responses, $timeout));
@@ -134,12 +142,8 @@ private static function shouldAttachTracingHeaders(?ClientInterface $client, Uri
// Check if the request destination is allow listed in the trace_propagation_targets option.
if (
- null !== $sdkOptions->getTracePropagationTargets() &&
- // Due to BC, we treat an empty array (the default) as all hosts are allow listed
- (
- [] === $sdkOptions->getTracePropagationTargets() ||
- \in_array($uri->getHost(), $sdkOptions->getTracePropagationTargets())
- )
+ null === $sdkOptions->getTracePropagationTargets()
+ || \in_array($uri->getHost(), $sdkOptions->getTracePropagationTargets())
) {
return true;
}
diff --git a/src/Tracing/HttpClient/AbstractTraceableResponse.php b/src/Tracing/HttpClient/AbstractTraceableResponse.php
index f53ecd8d..aba53b8e 100644
--- a/src/Tracing/HttpClient/AbstractTraceableResponse.php
+++ b/src/Tracing/HttpClient/AbstractTraceableResponse.php
@@ -107,7 +107,7 @@ public static function stream(HttpClientInterface $client, iterable $responses,
foreach ($responses as $response) {
if (!$response instanceof self) {
- throw new \TypeError(sprintf('"%s::stream()" expects parameter 1 to be an iterable of TraceableResponse objects, "%s" given.', TraceableHttpClient::class, get_debug_type($response)));
+ throw new \TypeError(\sprintf('"%s::stream()" expects parameter 1 to be an iterable of TraceableResponse objects, "%s" given.', TraceableHttpClient::class, get_debug_type($response)));
}
$traceableMap[$response->response] = $response;
diff --git a/src/Tracing/HttpClient/TraceableResponseForV5.php b/src/Tracing/HttpClient/TraceableResponseForV5.php
index 0ca572a2..c079fe4f 100644
--- a/src/Tracing/HttpClient/TraceableResponseForV5.php
+++ b/src/Tracing/HttpClient/TraceableResponseForV5.php
@@ -16,7 +16,7 @@ final class TraceableResponseForV5 extends AbstractTraceableResponse implements
*
* @return mixed
*/
- public function getInfo(string $type = null)
+ public function getInfo(?string $type = null)
{
return $this->response->getInfo($type);
}
diff --git a/src/Tracing/HttpClient/TraceableResponseForV6.php b/src/Tracing/HttpClient/TraceableResponseForV6.php
index 43dbbbf2..64cc7f28 100644
--- a/src/Tracing/HttpClient/TraceableResponseForV6.php
+++ b/src/Tracing/HttpClient/TraceableResponseForV6.php
@@ -14,7 +14,7 @@ final class TraceableResponseForV6 extends AbstractTraceableResponse implements
/**
* {@inheritdoc}
*/
- public function getInfo(string $type = null): mixed
+ public function getInfo(?string $type = null): mixed
{
return $this->response->getInfo($type);
}
diff --git a/src/Tracing/Twig/TwigTracingExtension.php b/src/Tracing/Twig/TwigTracingExtension.php
index ec06e58c..e03996d4 100644
--- a/src/Tracing/Twig/TwigTracingExtension.php
+++ b/src/Tracing/Twig/TwigTracingExtension.php
@@ -46,11 +46,12 @@ public function enter(Profile $profile): void
return;
}
- $spanContext = new SpanContext();
- $spanContext->setOp('view.render');
- $spanContext->setDescription($this->getSpanDescription($profile));
-
- $this->spans[$profile] = $transaction->startChild($spanContext);
+ $this->spans[$profile] = $transaction->startChild(
+ SpanContext::make()
+ ->setOp('view.render')
+ ->setOrigin('auto.view')
+ ->setDescription($this->getSpanDescription($profile))
+ );
}
/**
@@ -95,7 +96,7 @@ private function getSpanDescription(Profile $profile): string
return $profile->getTemplate();
default:
- return sprintf('%s::%s(%s)', $profile->getTemplate(), $profile->getType(), $profile->getName());
+ return \sprintf('%s::%s(%s)', $profile->getTemplate(), $profile->getType(), $profile->getName());
}
}
}
diff --git a/src/Transport/TransportFactory.php b/src/Transport/TransportFactory.php
deleted file mode 100644
index 6ddd272b..00000000
--- a/src/Transport/TransportFactory.php
+++ /dev/null
@@ -1,65 +0,0 @@
-decoratedTransportFactory = new DefaultTransportFactory(
- $streamFactory,
- $requestFactory,
- new HttpClientFactory(
- $uriFactory,
- $responseFactory,
- $streamFactory,
- $httpClient,
- 'sentry.php.symfony',
- SentryBundle::SDK_VERSION
- ),
- $logger
- );
- }
-
- public function create(Options $options): TransportInterface
- {
- return $this->decoratedTransportFactory->create($options);
- }
-}
diff --git a/src/Twig/SentryExtension.php b/src/Twig/SentryExtension.php
index ba705155..56fd71b7 100644
--- a/src/Twig/SentryExtension.php
+++ b/src/Twig/SentryExtension.php
@@ -10,13 +10,14 @@
use function Sentry\getBaggage;
use function Sentry\getTraceparent;
+use function Sentry\getW3CTraceparent;
final class SentryExtension extends AbstractExtension
{
/**
* @param HubInterface $hub The current hub
*/
- public function __construct(HubInterface $hub = null)
+ public function __construct(?HubInterface $hub = null)
{
}
@@ -27,6 +28,7 @@ public function getFunctions(): array
{
return [
new TwigFunction('sentry_trace_meta', [$this, 'getTraceMeta'], ['is_safe' => ['html']]),
+ new TwigFunction('sentry_w3c_trace_meta', [$this, 'getW3CTraceMeta'], ['is_safe' => ['html']]),
new TwigFunction('sentry_baggage_meta', [$this, 'getBaggageMeta'], ['is_safe' => ['html']]),
];
}
@@ -36,7 +38,15 @@ public function getFunctions(): array
*/
public function getTraceMeta(): string
{
- return sprintf('', getTraceparent());
+ return \sprintf('', getTraceparent());
+ }
+
+ /**
+ * Returns an HTML meta tag named `traceparent`.
+ */
+ public function getW3CTraceMeta(): string
+ {
+ return \sprintf('', getW3CTraceparent());
}
/**
@@ -44,6 +54,6 @@ public function getTraceMeta(): string
*/
public function getBaggageMeta(): string
{
- return sprintf('', getBaggage());
+ return \sprintf('', getBaggage());
}
}
diff --git a/src/aliases.php b/src/aliases.php
index 839a55c9..92272bef 100644
--- a/src/aliases.php
+++ b/src/aliases.php
@@ -5,6 +5,7 @@
namespace Sentry\SentryBundle;
use Doctrine\DBAL\Result;
+use Doctrine\DBAL\VersionAwarePlatformDriver;
use Sentry\SentryBundle\Tracing\Cache\TraceableCacheAdapter;
use Sentry\SentryBundle\Tracing\Cache\TraceableCacheAdapterForV2;
use Sentry\SentryBundle\Tracing\Cache\TraceableCacheAdapterForV3;
@@ -12,11 +13,19 @@
use Sentry\SentryBundle\Tracing\Cache\TraceableTagAwareCacheAdapterForV2;
use Sentry\SentryBundle\Tracing\Cache\TraceableTagAwareCacheAdapterForV3;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriver;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnection;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionFactory;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionFactoryForV2V3;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionFactoryForV4;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionForV2V3;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionForV4;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverForV2;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverForV3;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverForV4;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingStatement;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingStatementForV2;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingStatementForV3;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingStatementForV4;
use Sentry\SentryBundle\Tracing\HttpClient\TraceableHttpClient;
use Sentry\SentryBundle\Tracing\HttpClient\TraceableHttpClientForV4;
use Sentry\SentryBundle\Tracing\HttpClient\TraceableHttpClientForV5;
@@ -27,8 +36,9 @@
use Sentry\SentryBundle\Tracing\HttpClient\TraceableResponseForV6;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\DoctrineProvider;
+use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\Response\StreamableInterface;
-use Symfony\Contracts\HttpClient\ResponseInterface;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
if (interface_exists(AdapterInterface::class)) {
if (!class_exists(DoctrineProvider::class, false) && version_compare(\PHP_VERSION, '8.0.0', '>=')) {
@@ -51,20 +61,29 @@ class_alias(TraceableTagAwareCacheAdapterForV2::class, TraceableTagAwareCacheAda
}
if (!class_exists(TracingStatement::class)) {
- if (class_exists(Result::class)) {
+ if (class_exists(Result::class) && !interface_exists(VersionAwarePlatformDriver::class)) {
+ class_alias(TracingStatementForV4::class, TracingStatement::class);
+ class_alias(TracingDriverForV4::class, TracingDriver::class);
+ class_alias(TracingDriverConnectionForV4::class, TracingDriverConnection::class);
+ class_alias(TracingDriverConnectionFactoryForV4::class, TracingDriverConnectionFactory::class);
+ } elseif (class_exists(Result::class)) {
class_alias(TracingStatementForV3::class, TracingStatement::class);
class_alias(TracingDriverForV3::class, TracingDriver::class);
+ class_alias(TracingDriverConnectionForV2V3::class, TracingDriverConnection::class);
+ class_alias(TracingDriverConnectionFactoryForV2V3::class, TracingDriverConnectionFactory::class);
} elseif (interface_exists(Result::class)) {
class_alias(TracingStatementForV2::class, TracingStatement::class);
class_alias(TracingDriverForV2::class, TracingDriver::class);
+ class_alias(TracingDriverConnectionForV2V3::class, TracingDriverConnection::class);
+ class_alias(TracingDriverConnectionFactoryForV2V3::class, TracingDriverConnectionFactory::class);
}
}
-if (!class_exists(TraceableResponse::class) && interface_exists(ResponseInterface::class)) {
+if (!class_exists(TraceableResponse::class) && class_exists(HttpClient::class)) {
if (!interface_exists(StreamableInterface::class)) {
class_alias(TraceableResponseForV4::class, TraceableResponse::class);
class_alias(TraceableHttpClientForV4::class, TraceableHttpClient::class);
- } elseif (version_compare(\PHP_VERSION, '8.0', '>=')) {
+ } elseif (method_exists(HttpClientInterface::class, 'withOptions')) {
class_alias(TraceableResponseForV6::class, TraceableResponse::class);
class_alias(TraceableHttpClientForV6::class, TraceableHttpClient::class);
} else {
diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php
deleted file mode 100644
index 37937e02..00000000
--- a/tests/BaseTestCase.php
+++ /dev/null
@@ -1,24 +0,0 @@
-hub = $this->createMock(HubInterface::class);
+ $this->client = $this->createMock(ClientInterface::class);
+ $this->command = new CommandTester(new SentryTestCommand($this->hub));
}
- public function testExecuteSuccessfully(): void
+ public function testExecute(): void
{
- $options = new Options(['dsn' => 'http://public:secret@example.com/sentry/1']);
- $client = $this->prophesize(ClientInterface::class);
- $client->getOptions()
- ->willReturn($options);
-
- $hub = $this->prophesize(HubInterface::class);
- $hub->getClient()
- ->willReturn($client->reveal());
$lastEventId = EventId::generate();
- $hub->captureMessage(Argument::containingString('test'), Argument::cetera())
- ->shouldBeCalled()
- ->willReturn($lastEventId);
- SentrySdk::setCurrentHub($hub->reveal());
+ $this->client->expects($this->once())
+ ->method('getOptions')
+ ->willReturn(new Options(['dsn' => 'https://public:secret@example.com/sentry/1']));
+
+ $this->hub->expects($this->once())
+ ->method('getClient')
+ ->willReturn($this->client);
+
+ $this->hub->expects($this->once())
+ ->method('captureMessage')
+ ->with('This is a test message from the Sentry bundle')
+ ->willReturn($lastEventId);
- $commandTester = $this->executeCommand();
+ $exitCode = $this->command->execute([]);
+ $output = $this->command->getDisplay();
- $output = $commandTester->getDisplay();
- $this->assertStringContainsString('DSN correctly configured', $output);
- $this->assertStringContainsString('Sending test message', $output);
- $this->assertStringContainsString('Message sent', $output);
- $this->assertStringContainsString((string) $lastEventId, $output);
- $this->assertSame(0, $commandTester->getStatusCode());
+ $this->assertSame(0, $exitCode);
+ $this->assertStringContainsString('DSN correctly configured in the current client', $output);
+ $this->assertStringContainsString('Sending test message...', $output);
+ $this->assertStringContainsString("Message sent successfully with ID $lastEventId", $output);
}
public function testExecuteFailsDueToMissingDSN(): void
{
- $client = $this->prophesize(ClientInterface::class);
- $client->getOptions()
+ $this->client->expects($this->once())
+ ->method('getOptions')
->willReturn(new Options());
- $hub = $this->prophesize(HubInterface::class);
- $hub->getClient()
- ->willReturn($client->reveal());
+ $this->hub->expects($this->once())
+ ->method('getClient')
+ ->willReturn($this->client);
- SentrySdk::setCurrentHub($hub->reveal());
+ $exitCode = $this->command->execute([]);
+ $output = $this->command->getDisplay();
- $commandTester = $this->executeCommand();
-
- $this->assertNotSame(0, $commandTester->getStatusCode());
- $output = $commandTester->getDisplay();
- $this->assertStringContainsString('No DSN configured', $output);
- $this->assertStringContainsString('try bin/console debug:config sentry', $output);
+ $this->assertSame(1, $exitCode);
+ $this->assertStringContainsString('No DSN configured in the current client, please check your configuration', $output);
+ $this->assertStringContainsString('To debug further, try bin/console debug:config sentry', $output);
}
public function testExecuteFailsDueToMessageNotSent(): void
{
- $options = new Options(['dsn' => 'http://public:secret@example.com/sentry/1']);
- $client = $this->prophesize(ClientInterface::class);
- $client->getOptions()
- ->willReturn($options);
-
- $hub = $this->prophesize(HubInterface::class);
- $hub->getClient()
- ->willReturn($client->reveal());
- $hub->captureMessage(Argument::containingString('test'), Argument::cetera())
- ->shouldBeCalled()
- ->willReturn(null);
+ $this->client->expects($this->once())
+ ->method('getOptions')
+ ->willReturn(new Options(['dsn' => 'https://public:secret@example.com/sentry/1']));
- SentrySdk::setCurrentHub($hub->reveal());
+ $this->hub->expects($this->once())
+ ->method('getClient')
+ ->willReturn($this->client);
+
+ $this->hub->expects($this->once())
+ ->method('captureMessage')
+ ->with('This is a test message from the Sentry bundle')
+ ->willReturn(null);
- $commandTester = $this->executeCommand();
+ $exitCode = $this->command->execute([]);
+ $output = $this->command->getDisplay();
- $this->assertNotSame(0, $commandTester->getStatusCode());
- $output = $commandTester->getDisplay();
- $this->assertStringContainsString('DSN correctly configured', $output);
- $this->assertStringContainsString('Sending test message', $output);
- $this->assertStringContainsString('Message not sent', $output);
+ $this->assertSame(1, $exitCode);
+ $this->assertStringContainsString('DSN correctly configured in the current client', $output);
+ $this->assertStringContainsString('Sending test message...', $output);
+ $this->assertStringContainsString('Message not sent!', $output);
+ $this->assertStringContainsString('Check your DSN or your before_send callback if used', $output);
}
public function testExecuteFailsDueToMissingClient(): void
{
- $hub = $this->prophesize(HubInterface::class);
- $hub->getClient()
+ $this->hub->expects($this->once())
+ ->method('getClient')
->willReturn(null);
- SentrySdk::setCurrentHub($hub->reveal());
+ $exitCode = $this->command->execute([]);
+ $output = $this->command->getDisplay();
- $commandTester = $this->executeCommand();
-
- $this->assertNotSame(0, $commandTester->getStatusCode());
- $output = $commandTester->getDisplay();
+ $this->assertSame(1, $exitCode);
$this->assertStringContainsString('No client found', $output);
- $this->assertStringContainsString('DSN is probably missing', $output);
+ $this->assertStringContainsString('Your DSN is probably missing, check your configuration', $output);
}
- private function executeCommand(): CommandTester
+ /**
+ * @group legacy
+ */
+ public function testConstructorTriggersDeprecationErrorIfHubIsNotPassedToConstructor(): void
{
- $command = new SentryTestCommand();
- $command->setName('sentry:test');
-
- $application = new Application();
- $application->add($command);
-
- $command = $application->find('sentry:test');
- $commandTester = new CommandTester($command);
- $commandTester->execute([
- 'command' => $command->getName(),
- ]);
+ $this->expectDeprecation('Not passing an instance of the "Sentry\State\HubInterface" interface as argument of the constructor is deprecated since version 4.12 and will not work since version 5.0.');
- return $commandTester;
+ new SentryTestCommand();
}
}
diff --git a/tests/DependencyInjection/Compiler/AddLoginListenerTagPassTest.php b/tests/DependencyInjection/Compiler/AddLoginListenerTagPassTest.php
index cc91f8f7..6fb3248b 100644
--- a/tests/DependencyInjection/Compiler/AddLoginListenerTagPassTest.php
+++ b/tests/DependencyInjection/Compiler/AddLoginListenerTagPassTest.php
@@ -28,4 +28,20 @@ public function testProcess(): void
$this->assertSame([['event' => AuthenticationSuccessEvent::class, 'method' => 'handleAuthenticationSuccessEvent']], $listenerDefinition->getTag('kernel.event_listener'));
}
+
+ public function testProcessLoginSuccess(): void
+ {
+ if (!class_exists(LoginSuccessEvent::class)) {
+ $this->markTestSkipped('Skipping this test because LoginSuccessEvent does not exist.');
+ }
+
+ $container = new ContainerBuilder();
+ $container->register(LoginListener::class)->setPublic(true);
+ $container->addCompilerPass(new AddLoginListenerTagPass());
+ $container->compile();
+
+ $listenerDefinition = $container->getDefinition(LoginListener::class);
+
+ $this->assertSame([['event' => LoginSuccessEvent::class, 'method' => 'handleLoginSuccessEvent']], $listenerDefinition->getTag('kernel.event_listener'));
+ }
}
diff --git a/tests/DependencyInjection/Compiler/DbalTracingPassTest.php b/tests/DependencyInjection/Compiler/DbalTracingPassTest.php
index 93a6b5d7..6fabc85f 100644
--- a/tests/DependencyInjection/Compiler/DbalTracingPassTest.php
+++ b/tests/DependencyInjection/Compiler/DbalTracingPassTest.php
@@ -16,72 +16,43 @@
final class DbalTracingPassTest extends DoctrineTestCase
{
- public function testProcessWithDoctrineDBALVersionAtLeast30(): void
+ public function testProcessWithDoctrineDBALVersion4(): void
{
- if (!self::isDoctrineDBALVersion3Installed()) {
- $this->markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.0.');
+ if (!self::isDoctrineDBALVersion4Installed()) {
+ $this->markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 4.0.');
}
$container = $this->createContainerBuilder();
- $container->setParameter('sentry.tracing.dbal.connections', ['foo', 'bar', 'baz']);
+ $container->setParameter('sentry.tracing.dbal.connections', ['foo', 'bar']);
+ $container->compile();
- $container
- ->register('foo.service', \stdClass::class)
- ->setPublic(true);
+ $tracingMiddlewareDefinition = $container->getDefinition(TracingDriverMiddleware::class);
+ $doctrineMiddlewareTags = $tracingMiddlewareDefinition->getTag('doctrine.middleware');
- $container
- ->register('doctrine.dbal.foo_connection.configuration', Configuration::class)
- ->setPublic(true);
-
- $container
- ->register('doctrine.dbal.bar_connection.configuration', Configuration::class)
- ->addMethodCall('setMiddlewares', [[]])
- ->setPublic(true);
+ $this->assertCount(2, $doctrineMiddlewareTags);
+ $this->assertSame(['connection' => 'foo'], $doctrineMiddlewareTags[0]);
+ $this->assertSame(['connection' => 'bar'], $doctrineMiddlewareTags[1]);
+ }
- $container
- ->register('doctrine.dbal.baz_connection.configuration', Configuration::class)
- ->addMethodCall('setMiddlewares', [[new Reference('foo.service')]])
- ->setPublic(true);
+ public function testProcessWithDoctrineDBALVersion3(): void
+ {
+ if (!self::isDoctrineDBALVersion3Installed()) {
+ $this->markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
+ }
+ $container = $this->createContainerBuilder();
+ $container->setParameter('sentry.tracing.dbal.connections', ['foo', 'bar']);
$container->compile();
- $this->assertEquals(
- [
- [
- 'setMiddlewares',
- [[new Reference(TracingDriverMiddleware::class)]],
- ],
- ],
- $container->getDefinition('doctrine.dbal.foo_connection.configuration')->getMethodCalls()
- );
-
- $this->assertEquals(
- [
- [
- 'setMiddlewares',
- [[new Reference(TracingDriverMiddleware::class)]],
- ],
- ],
- $container->getDefinition('doctrine.dbal.bar_connection.configuration')->getMethodCalls()
- );
-
- $this->assertEquals(
- [
- [
- 'setMiddlewares',
- [
- [
- new Reference('foo.service'),
- new Reference(TracingDriverMiddleware::class),
- ],
- ],
- ],
- ],
- $container->getDefinition('doctrine.dbal.baz_connection.configuration')->getMethodCalls()
- );
+ $tracingMiddlewareDefinition = $container->getDefinition(TracingDriverMiddleware::class);
+ $doctrineMiddlewareTags = $tracingMiddlewareDefinition->getTag('doctrine.middleware');
+
+ $this->assertCount(2, $doctrineMiddlewareTags);
+ $this->assertSame(['connection' => 'foo'], $doctrineMiddlewareTags[0]);
+ $this->assertSame(['connection' => 'bar'], $doctrineMiddlewareTags[1]);
}
- public function testProcessWithDoctrineDBALVersionLowerThan30(): void
+ public function testProcessWithDoctrineDBALVersion2(): void
{
if (!self::isDoctrineDBALVersion2Installed()) {
$this->markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be ^2.13.');
diff --git a/tests/DependencyInjection/Compiler/HttpClientTracingPassTest.php b/tests/DependencyInjection/Compiler/HttpClientTracingPassTest.php
index 9f3d12a7..8b2ccb66 100644
--- a/tests/DependencyInjection/Compiler/HttpClientTracingPassTest.php
+++ b/tests/DependencyInjection/Compiler/HttpClientTracingPassTest.php
@@ -9,7 +9,6 @@
use Sentry\SentryBundle\Tracing\HttpClient\TraceableHttpClient;
use Sentry\State\HubInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
final class HttpClientTracingPassTest extends TestCase
@@ -21,12 +20,46 @@ public static function setUpBeforeClass(): void
}
}
- public function testProcess(): void
+ /**
+ * @dataProvider processDataProvider
+ */
+ public function testProcess(string $httpClientServiceId): void
+ {
+ $container = $this->createContainerBuilder(true, true, $httpClientServiceId);
+ $container->compile();
+
+ $this->assertSame(TraceableHttpClient::class, $container->getDefinition($httpClientServiceId)->getClass());
+ }
+
+ public function processDataProvider(): \Generator
+ {
+ yield 'The framework version is >=6.3' => [
+ 'http_client.transport',
+ ];
+
+ yield 'The framework version is <6.3 and the mocked HTTP client is decorated by the retryable client' => [
+ 'http_client.retryable.inner.mock_client',
+ ];
+
+ yield 'The framework version is <6.3 and the mocked HTTP client is decorated by the profiler' => [
+ '.debug.http_client.inner.mock_client',
+ ];
+
+ yield 'The framework version is <6.3 and the mocked HTTP client is not decorated' => [
+ 'http_client.mock_client',
+ ];
+
+ yield 'The framework version is <6.3 and the HTTP client is not mocked' => [
+ 'http_client',
+ ];
+ }
+
+ public function testProcessDoesNothingIfHttpClientServiceCannotBeFound(): void
{
- $container = $this->createContainerBuilder(true, true);
+ $container = $this->createContainerBuilder(true, true, null);
$container->compile();
- $this->assertSame(TraceableHttpClient::class, $container->findDefinition('http.client')->getClass());
+ $this->assertFalse($container->hasDefinition('http_client'));
}
/**
@@ -34,10 +67,10 @@ public function testProcess(): void
*/
public function testProcessDoesNothingIfConditionsForEnablingTracingAreMissing(bool $isTracingEnabled, bool $isHttpClientTracingEnabled): void
{
- $container = $this->createContainerBuilder($isTracingEnabled, $isHttpClientTracingEnabled);
+ $container = $this->createContainerBuilder($isTracingEnabled, $isHttpClientTracingEnabled, 'http_client.transport');
$container->compile();
- $this->assertSame(HttpClient::class, $container->getDefinition('http.client')->getClass());
+ $this->assertSame(HttpClientInterface::class, $container->getDefinition('http_client.transport')->getClass());
}
/**
@@ -61,7 +94,7 @@ public function processDoesNothingIfConditionsForEnablingTracingAreMissingDataPr
];
}
- private function createContainerBuilder(bool $isTracingEnabled, bool $isHttpClientTracingEnabled): ContainerBuilder
+ private function createContainerBuilder(bool $isTracingEnabled, bool $isHttpClientTracingEnabled, ?string $httpClientServiceId): ContainerBuilder
{
$container = new ContainerBuilder();
$container->addCompilerPass(new HttpClientTracingPass());
@@ -71,9 +104,10 @@ private function createContainerBuilder(bool $isTracingEnabled, bool $isHttpClie
$container->register(HubInterface::class, HubInterface::class)
->setPublic(true);
- $container->register('http.client', HttpClient::class)
- ->setPublic(true)
- ->addTag('http_client.client');
+ if (null !== $httpClientServiceId) {
+ $container->register($httpClientServiceId, HttpClientInterface::class)
+ ->setPublic(true);
+ }
return $container;
}
diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php
index 4eb21812..c993f6f2 100644
--- a/tests/DependencyInjection/ConfigurationTest.php
+++ b/tests/DependencyInjection/ConfigurationTest.php
@@ -5,7 +5,6 @@
namespace Sentry\SentryBundle\Tests\DependencyInjection;
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
-use Jean85\PrettyVersions;
use PHPUnit\Framework\TestCase;
use Sentry\SentryBundle\DependencyInjection\Configuration;
use Symfony\Bundle\TwigBundle\TwigBundle;
@@ -24,12 +23,13 @@ public function testProcessConfigurationWithDefaultConfiguration(): void
'register_error_listener' => true,
'register_error_handler' => true,
'logger' => null,
- 'transport_factory' => 'Sentry\\Transport\\TransportFactoryInterface',
'options' => [
'integrations' => [],
'prefixes' => array_merge(['%kernel.project_dir%'], array_filter(explode(\PATH_SEPARATOR, get_include_path() ?: ''))),
'environment' => '%kernel.environment%',
- 'release' => PrettyVersions::getRootPackageVersion()->getPrettyVersion(),
+ 'release' => '%env(default::SENTRY_RELEASE)%',
+ 'ignore_exceptions' => [],
+ 'ignore_transactions' => [],
'tags' => [],
'in_app_exclude' => [
'%kernel.cache_dir%',
@@ -38,8 +38,6 @@ public function testProcessConfigurationWithDefaultConfiguration(): void
],
'in_app_include' => [],
'class_serializers' => [],
- 'ignore_exceptions' => [],
- 'ignore_transactions' => [],
],
'messenger' => [
'enabled' => interface_exists(MessageBusInterface::class),
diff --git a/tests/DependencyInjection/Fixtures/StubEnvVarLoader.php b/tests/DependencyInjection/Fixtures/StubEnvVarLoader.php
new file mode 100644
index 00000000..f83c1ac0
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/StubEnvVarLoader.php
@@ -0,0 +1,28 @@
+
+ */
+ private $envs = [];
+
+ /**
+ * @param array $envs
+ */
+ public function __construct(array $envs)
+ {
+ $this->envs = $envs;
+ }
+
+ public function loadEnvVars(): array
+ {
+ return $this->envs;
+ }
+}
diff --git a/tests/DependencyInjection/Fixtures/php/error_types.php b/tests/DependencyInjection/Fixtures/php/error_types.php
index 9c8473e5..6d072037 100644
--- a/tests/DependencyInjection/Fixtures/php/error_types.php
+++ b/tests/DependencyInjection/Fixtures/php/error_types.php
@@ -7,6 +7,7 @@
/** @var ContainerBuilder $container */
$container->loadFromExtension('sentry', [
'options' => [
- 'error_types' => \E_ALL & ~(\E_NOTICE | \E_STRICT | \E_DEPRECATED),
+ // 2048 is \E_STRICT which has been deprecated in PHP 8.4 so we should not reference it directly to prevent deprecation notices
+ 'error_types' => \E_ALL & ~(\E_NOTICE | 2048 | \E_DEPRECATED),
],
]);
diff --git a/tests/DependencyInjection/Fixtures/php/full.php b/tests/DependencyInjection/Fixtures/php/full.php
index 7c69cc19..aa11c22f 100644
--- a/tests/DependencyInjection/Fixtures/php/full.php
+++ b/tests/DependencyInjection/Fixtures/php/full.php
@@ -7,27 +7,32 @@
/** @var ContainerBuilder $container */
$container->loadFromExtension('sentry', [
'dsn' => 'https://examplePublicKey@o0.ingest.sentry.io/0',
- 'transport_factory' => 'App\\Sentry\\Transport\\TransportFactory',
'logger' => 'app.logger',
'options' => [
'integrations' => ['App\\Sentry\\Integration\\FooIntegration'],
'default_integrations' => false,
- 'send_attempts' => 1,
'prefixes' => ['%kernel.project_dir%'],
'sample_rate' => 1,
+ 'enable_tracing' => true,
'traces_sample_rate' => 1,
- 'profiles_sample_rate' => 1,
'traces_sampler' => 'App\\Sentry\\Tracing\\TracesSampler',
- 'trace_propagation_targets' => ['website.invalid'],
+ 'profiles_sample_rate' => 1,
'attach_stacktrace' => true,
+ 'attach_metric_code_locations' => true,
'context_lines' => 0,
- 'enable_compression' => true,
'environment' => 'development',
- 'logger' => 'php',
+ 'logger' => Sentry\Logger\DebugStdOutLogger::class,
+ 'spotlight' => true,
+ 'spotlight_url' => 'http://localhost:8969',
'release' => '4.0.x-dev',
'server_name' => 'localhost',
+ 'ignore_exceptions' => ['Symfony\Component\HttpKernel\Exception\BadRequestHttpException'],
+ 'ignore_transactions' => ['GET tracing_ignored_transaction'],
'before_send' => 'App\\Sentry\\BeforeSendCallback',
'before_send_transaction' => 'App\\Sentry\\BeforeSendTransactionCallback',
+ 'before_send_check_in' => 'App\\Sentry\\BeforeSendCheckInCallback',
+ 'before_send_metrics' => 'App\\Sentry\\BeforeSendMetricsCallback',
+ 'trace_propagation_targets' => ['website.invalid'],
'tags' => [
'context' => 'development',
],
@@ -38,14 +43,17 @@
'in_app_include' => ['%kernel.project_dir%'],
'send_default_pii' => true,
'max_value_length' => 255,
+ 'transport' => 'App\\Sentry\\Transport',
+ 'http_client' => 'App\\Sentry\\HttpClient',
'http_proxy' => 'proxy.example.com:8080',
- 'http_timeout' => 10,
+ 'http_proxy_authentication' => 'user:password',
'http_connect_timeout' => 15,
+ 'http_timeout' => 10,
+ 'http_ssl_verify_peer' => true,
+ 'http_compression' => true,
'capture_silenced_errors' => true,
'max_request_body_size' => 'none',
'class_serializers' => ['App\\FooClass' => 'App\\Sentry\\Serializer\\FooClassSerializer'],
- 'ignore_exceptions' => ['Symfony\Component\HttpKernel\Exception\BadRequestHttpException'],
- 'ignore_transactions' => ['GET tracing_ignored_transaction'],
],
'messenger' => [
'enabled' => true,
diff --git a/tests/DependencyInjection/Fixtures/php/release_option_fallback_to_composer_version.php b/tests/DependencyInjection/Fixtures/php/release_option_fallback_to_composer_version.php
new file mode 100644
index 00000000..ba092c8a
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/php/release_option_fallback_to_composer_version.php
@@ -0,0 +1,8 @@
+loadFromExtension('sentry', []);
diff --git a/tests/DependencyInjection/Fixtures/php/release_option_fallback_to_env_var.php b/tests/DependencyInjection/Fixtures/php/release_option_fallback_to_env_var.php
new file mode 100644
index 00000000..aa23806f
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/php/release_option_fallback_to_env_var.php
@@ -0,0 +1,27 @@
+extension('sentry', []);
+
+ $container->services()
+ ->set(StubEnvVarLoader::class)
+ ->tag('container.env_var_loader')
+ ->args([['SENTRY_RELEASE' => '1.0.x-dev']])
+
+ ->set(EnvVarProcessor::class)
+ ->tag('container.env_var_processor')
+ ->args([
+ function_exists('Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\service') ? service('service_container') : ref('service_container'),
+ tagged_iterator('container.env_var_loader'),
+ ]);
+};
diff --git a/tests/DependencyInjection/Fixtures/php/release_option_from_config.php b/tests/DependencyInjection/Fixtures/php/release_option_from_config.php
new file mode 100644
index 00000000..3c594daf
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/php/release_option_from_config.php
@@ -0,0 +1,12 @@
+loadFromExtension('sentry', [
+ 'options' => [
+ 'release' => '1.0.x-dev',
+ ],
+]);
diff --git a/tests/DependencyInjection/Fixtures/php/release_option_from_env_var.php b/tests/DependencyInjection/Fixtures/php/release_option_from_env_var.php
new file mode 100644
index 00000000..6d1b0d82
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/php/release_option_from_env_var.php
@@ -0,0 +1,13 @@
+setParameter('env(APP_RELEASE)', '1.0.x-dev');
+$container->loadFromExtension('sentry', [
+ 'options' => [
+ 'release' => '%env(APP_RELEASE)%',
+ ],
+]);
diff --git a/tests/DependencyInjection/Fixtures/xml/error_types.xml b/tests/DependencyInjection/Fixtures/xml/error_types.xml
index c0c46ab9..4be73596 100644
--- a/tests/DependencyInjection/Fixtures/xml/error_types.xml
+++ b/tests/DependencyInjection/Fixtures/xml/error_types.xml
@@ -7,6 +7,6 @@
https://sentry.io/schema/dic/sentry-symfony https://sentry.io/schema/dic/sentry-symfony/sentry-1.0.xsd">
-
+
diff --git a/tests/DependencyInjection/Fixtures/xml/full.xml b/tests/DependencyInjection/Fixtures/xml/full.xml
index f9d84f93..df951eb4 100644
--- a/tests/DependencyInjection/Fixtures/xml/full.xml
+++ b/tests/DependencyInjection/Fixtures/xml/full.xml
@@ -8,32 +8,40 @@
diff --git a/tests/DependencyInjection/Fixtures/xml/release_option_fallback_to_composer_version.xml b/tests/DependencyInjection/Fixtures/xml/release_option_fallback_to_composer_version.xml
new file mode 100644
index 00000000..1ffe0852
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/xml/release_option_fallback_to_composer_version.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/tests/DependencyInjection/Fixtures/xml/release_option_fallback_to_env_var.xml b/tests/DependencyInjection/Fixtures/xml/release_option_fallback_to_env_var.xml
new file mode 100644
index 00000000..2c32ce7d
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/xml/release_option_fallback_to_env_var.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+ 1.0.x-dev
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/DependencyInjection/Fixtures/xml/release_option_from_config.xml b/tests/DependencyInjection/Fixtures/xml/release_option_from_config.xml
new file mode 100644
index 00000000..b10573d2
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/xml/release_option_from_config.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/tests/DependencyInjection/Fixtures/xml/release_option_from_env_var.xml b/tests/DependencyInjection/Fixtures/xml/release_option_from_env_var.xml
new file mode 100644
index 00000000..19162356
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/xml/release_option_from_env_var.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ 1.0.x-dev
+
+
+
+
+
+
diff --git a/tests/DependencyInjection/Fixtures/yml/error_types.yml b/tests/DependencyInjection/Fixtures/yml/error_types.yml
index 95149254..8c976cd2 100644
--- a/tests/DependencyInjection/Fixtures/yml/error_types.yml
+++ b/tests/DependencyInjection/Fixtures/yml/error_types.yml
@@ -1,3 +1,3 @@
sentry:
options:
- error_types: E_ALL & ~(E_NOTICE|E_STRICT|E_DEPRECATED)
+ error_types: E_ALL & ~(E_NOTICE|2048|E_DEPRECATED)
diff --git a/tests/DependencyInjection/Fixtures/yml/full.yml b/tests/DependencyInjection/Fixtures/yml/full.yml
index d2f13c5e..8a42e682 100644
--- a/tests/DependencyInjection/Fixtures/yml/full.yml
+++ b/tests/DependencyInjection/Fixtures/yml/full.yml
@@ -1,29 +1,36 @@
sentry:
dsn: https://examplePublicKey@o0.ingest.sentry.io/0
- transport_factory: App\Sentry\Transport\TransportFactory
logger: app.logger
options:
integrations:
- App\Sentry\Integration\FooIntegration
default_integrations: false
- send_attempts: 1
prefixes:
- '%kernel.project_dir%'
sample_rate: 1
+ enable_tracing: true
traces_sample_rate: 1
- profiles_sample_rate: 1
traces_sampler: App\Sentry\Tracing\TracesSampler
- trace_propagation_targets:
- - 'website.invalid'
+ profiles_sample_rate: 1
attach_stacktrace: true
+ attach_metric_code_locations: true
context_lines: 0
- enable_compression: true
environment: development
- logger: php
+ logger: Sentry\Logger\DebugStdOutLogger
+ spotlight: true
+ spotlight_url: http://localhost:8969
release: 4.0.x-dev
server_name: localhost
+ ignore_exceptions:
+ - Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+ ignore_transactions:
+ - GET tracing_ignored_transaction
before_send: App\Sentry\BeforeSendCallback
before_send_transaction: App\Sentry\BeforeSendTransactionCallback
+ before_send_check_in: App\Sentry\BeforeSendCheckInCallback
+ before_send_metrics: App\Sentry\BeforeSendMetricsCallback
+ trace_propagation_targets:
+ - 'website.invalid'
tags:
context: development
error_types: !php/const E_ALL
@@ -35,17 +42,18 @@ sentry:
- '%kernel.project_dir%'
send_default_pii: true
max_value_length: 255
+ transport: App\Sentry\Transport
+ http_client: App\Sentry\HttpClient
http_proxy: proxy.example.com:8080
- http_timeout: 10
+ http_proxy_authentication: user:password
http_connect_timeout: 15
+ http_timeout: 10
+ http_ssl_verify_peer: true
+ http_compression: true
capture_silenced_errors: true
max_request_body_size: 'none'
class_serializers:
App\FooClass: App\Sentry\Serializer\FooClassSerializer
- ignore_exceptions:
- - Symfony\Component\HttpKernel\Exception\BadRequestHttpException
- ignore_transactions:
- - GET tracing_ignored_transaction
messenger:
enabled: true
capture_soft_fails: false
diff --git a/tests/DependencyInjection/Fixtures/yml/release_option_fallback_to_composer_version.yml b/tests/DependencyInjection/Fixtures/yml/release_option_fallback_to_composer_version.yml
new file mode 100644
index 00000000..64d9acea
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/yml/release_option_fallback_to_composer_version.yml
@@ -0,0 +1,2 @@
+sentry:
+ options: ~
diff --git a/tests/DependencyInjection/Fixtures/yml/release_option_fallback_to_env_var.yml b/tests/DependencyInjection/Fixtures/yml/release_option_fallback_to_env_var.yml
new file mode 100644
index 00000000..5ed6fba2
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/yml/release_option_fallback_to_env_var.yml
@@ -0,0 +1,16 @@
+services:
+ Sentry\SentryBundle\Tests\DependencyInjection\Fixtures\StubEnvVarLoader:
+ arguments:
+ - { SENTRY_RELEASE: 1.0.x-dev }
+ tags:
+ - { name: container.env_var_loader }
+
+ Symfony\Component\DependencyInjection\EnvVarProcessor:
+ arguments:
+ - '@service_container'
+ - !tagged_iterator container.env_var_loader
+ tags:
+ - { name: container.env_var_processor }
+
+sentry:
+ options: ~
diff --git a/tests/DependencyInjection/Fixtures/yml/release_option_from_config.yml b/tests/DependencyInjection/Fixtures/yml/release_option_from_config.yml
new file mode 100644
index 00000000..fa629ca0
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/yml/release_option_from_config.yml
@@ -0,0 +1,3 @@
+sentry:
+ options:
+ release: 1.0.x-dev
diff --git a/tests/DependencyInjection/Fixtures/yml/release_option_from_env_var.yml b/tests/DependencyInjection/Fixtures/yml/release_option_from_env_var.yml
new file mode 100644
index 00000000..dd62ce81
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/yml/release_option_from_env_var.yml
@@ -0,0 +1,6 @@
+parameters:
+ env(APP_RELEASE): 1.0.x-dev
+
+sentry:
+ options:
+ release: '%env(APP_RELEASE)%'
diff --git a/tests/DependencyInjection/SentryExtensionTest.php b/tests/DependencyInjection/SentryExtensionTest.php
index 30c73ccf..db949243 100644
--- a/tests/DependencyInjection/SentryExtensionTest.php
+++ b/tests/DependencyInjection/SentryExtensionTest.php
@@ -5,14 +5,16 @@
namespace Sentry\SentryBundle\Tests\DependencyInjection;
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
+use Jean85\PrettyVersions;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Sentry\ClientInterface;
-use Sentry\Integration\IgnoreErrorsIntegration;
+use Sentry\Logger\DebugStdOutLogger;
use Sentry\Options;
use Sentry\SentryBundle\DependencyInjection\SentryExtension;
use Sentry\SentryBundle\EventListener\ConsoleListener;
use Sentry\SentryBundle\EventListener\ErrorListener;
+use Sentry\SentryBundle\EventListener\LoginListener;
use Sentry\SentryBundle\EventListener\MessengerListener;
use Sentry\SentryBundle\EventListener\RequestListener;
use Sentry\SentryBundle\EventListener\SubRequestListener;
@@ -25,17 +27,17 @@
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverMiddleware;
use Sentry\SentryBundle\Tracing\Twig\TwigTracingExtension;
use Sentry\Serializer\RepresentationSerializer;
-use Sentry\Serializer\Serializer;
-use Sentry\Transport\TransportFactoryInterface;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Symfony\Component\Console\ConsoleEvents;
-use Symfony\Component\Debug\Exception\FatalErrorException;
+use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass;
+use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass;
+use Symfony\Component\DependencyInjection\Compiler\ValidateEnvPlaceholdersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\ErrorHandler\Error\FatalError;
use Symfony\Component\HttpClient\HttpClient;
+use Symfony\Component\HttpClient\TraceableHttpClient;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
@@ -198,25 +200,38 @@ public function testClientIsCreatedFromOptions(): void
$container = $this->createContainerFromFixture('full');
$optionsDefinition = $container->getDefinition('sentry.client.options');
$expectedOptions = [
+ 'dsn' => 'https://examplePublicKey@o0.ingest.sentry.io/0',
'integrations' => new Reference(IntegrationConfigurator::class),
'default_integrations' => false,
- 'send_attempts' => 1,
'prefixes' => [$container->getParameter('kernel.project_dir')],
'sample_rate' => 1,
+ 'enable_tracing' => true,
'traces_sample_rate' => 1,
- 'profiles_sample_rate' => 1,
'traces_sampler' => new Reference('App\\Sentry\\Tracing\\TracesSampler'),
- 'trace_propagation_targets' => ['website.invalid'],
+ 'profiles_sample_rate' => 1,
'attach_stacktrace' => true,
+ 'attach_metric_code_locations' => true,
'context_lines' => 0,
- 'enable_compression' => true,
'environment' => 'development',
- 'logger' => 'php',
+ 'logger' => new Reference(DebugStdOutLogger::class),
+ 'spotlight' => true,
+ 'spotlight_url' => 'http://localhost:8969',
'release' => '4.0.x-dev',
'server_name' => 'localhost',
+ 'ignore_exceptions' => [
+ 'Symfony\Component\HttpKernel\Exception\BadRequestHttpException',
+ ],
+ 'ignore_transactions' => [
+ 'GET tracing_ignored_transaction',
+ ],
'before_send' => new Reference('App\\Sentry\\BeforeSendCallback'),
'before_send_transaction' => new Reference('App\\Sentry\\BeforeSendTransactionCallback'),
- 'tags' => ['context' => 'development'],
+ 'before_send_check_in' => new Reference('App\\Sentry\\BeforeSendCheckInCallback'),
+ 'before_send_metrics' => new Reference('App\\Sentry\\BeforeSendMetricsCallback'),
+ 'trace_propagation_targets' => ['website.invalid'],
+ 'tags' => [
+ 'context' => 'development',
+ ],
'error_types' => \E_ALL,
'max_breadcrumbs' => 1,
'before_breadcrumb' => new Reference('App\\Sentry\\BeforeBreadcrumbCallback'),
@@ -224,21 +239,19 @@ public function testClientIsCreatedFromOptions(): void
'in_app_include' => [$container->getParameter('kernel.project_dir')],
'send_default_pii' => true,
'max_value_length' => 255,
+ 'transport' => new Reference('App\\Sentry\\Transport'),
+ 'http_client' => new Reference('App\\Sentry\\HttpClient'),
'http_proxy' => 'proxy.example.com:8080',
+ 'http_proxy_authentication' => 'user:password',
'http_timeout' => 10,
'http_connect_timeout' => 15,
+ 'http_ssl_verify_peer' => true,
+ 'http_compression' => true,
'capture_silenced_errors' => true,
'max_request_body_size' => 'none',
'class_serializers' => [
'App\\FooClass' => new Reference('App\\Sentry\\Serializer\\FooClassSerializer'),
],
- 'dsn' => 'https://examplePublicKey@o0.ingest.sentry.io/0',
- 'ignore_exceptions' => [
- 'Symfony\Component\HttpKernel\Exception\BadRequestHttpException',
- ],
- 'ignore_transactions' => [
- 'GET tracing_ignored_transaction',
- ],
];
$this->assertSame(Options::class, $optionsDefinition->getClass());
@@ -246,14 +259,6 @@ public function testClientIsCreatedFromOptions(): void
$integrationConfiguratorDefinition = $container->getDefinition(IntegrationConfigurator::class);
$expectedIntegrations = [
- new Definition(IgnoreErrorsIntegration::class, [
- [
- 'ignore_exceptions' => [
- FatalError::class,
- FatalErrorException::class,
- ],
- ],
- ]),
new Reference('App\\Sentry\\Integration\\FooIntegration'),
];
@@ -269,32 +274,15 @@ public function testClientIsCreatedFromOptions(): void
$methodCalls = $factory[0]->getMethodCalls();
- $this->assertCount(6, $methodCalls);
+ $this->assertCount(4, $methodCalls);
$this->assertDefinitionMethodCallAt($methodCalls[0], 'setSdkIdentifier', [SentryBundle::SDK_IDENTIFIER]);
$this->assertDefinitionMethodCallAt($methodCalls[1], 'setSdkVersion', [SentryBundle::SDK_VERSION]);
- $this->assertDefinitionMethodCallAt($methodCalls[2], 'setTransportFactory', [new Reference('App\\Sentry\\Transport\\TransportFactory')]);
- $this->assertDefinitionMethodCallAt($methodCalls[5], 'setLogger', [new Reference('app.logger')]);
-
- $this->assertSame('setSerializer', $methodCalls[3][0]);
- $this->assertInstanceOf(Definition::class, $methodCalls[3][1][0]);
- $this->assertSame(Serializer::class, $methodCalls[3][1][0]->getClass());
- $this->assertEquals($methodCalls[3][1][0]->getArgument(0), new Reference('sentry.client.options'));
-
- $this->assertSame('setRepresentationSerializer', $methodCalls[4][0]);
- $this->assertInstanceOf(Definition::class, $methodCalls[4][1][0]);
- $this->assertSame(RepresentationSerializer::class, $methodCalls[4][1][0]->getClass());
- $this->assertEquals($methodCalls[4][1][0]->getArgument(0), new Reference('sentry.client.options'));
- }
-
- public function testLoggerIsPassedToTransportFactory(): void
- {
- $container = $this->createContainerFromFixture('full');
-
- $transportFactoryDefinition = $container->findDefinition(TransportFactoryInterface::class);
- $logger = $transportFactoryDefinition->getArgument('$logger');
+ $this->assertDefinitionMethodCallAt($methodCalls[3], 'setLogger', [new Reference('app.logger')]);
- $this->assertInstanceOf(Reference::class, $logger);
- $this->assertSame('app.logger', $logger->__toString());
+ $this->assertSame('setRepresentationSerializer', $methodCalls[2][0]);
+ $this->assertInstanceOf(Definition::class, $methodCalls[2][1][0]);
+ $this->assertSame(RepresentationSerializer::class, $methodCalls[2][1][0]->getClass());
+ $this->assertEquals($methodCalls[2][1][0]->getArgument(0), new Reference('sentry.client.options'));
}
public function testErrorTypesOptionIsParsedFromStringToIntegerValue(): void
@@ -302,26 +290,8 @@ public function testErrorTypesOptionIsParsedFromStringToIntegerValue(): void
$container = $this->createContainerFromFixture('error_types');
$optionsDefinition = $container->getDefinition('sentry.client.options');
- $this->assertSame(\E_ALL & ~(\E_NOTICE | \E_STRICT | \E_DEPRECATED), $optionsDefinition->getArgument(0)['error_types']);
- }
-
- public function testIgnoreErrorsIntegrationIsNotAddedTwiceIfAlreadyConfigured(): void
- {
- $container = $this->createContainerFromFixture('ignore_errors_integration_overridden');
- $integrations = $container->getDefinition(IntegrationConfigurator::class)->getArgument(0);
- $ignoreErrorsIntegrationsCount = 0;
-
- foreach ($integrations as $integration) {
- if ($integration instanceof Reference && IgnoreErrorsIntegration::class === (string) $integration) {
- ++$ignoreErrorsIntegrationsCount;
- }
-
- if ($integration instanceof Definition && IgnoreErrorsIntegration::class === $integration->getClass()) {
- ++$ignoreErrorsIntegrationsCount;
- }
- }
-
- $this->assertSame(1, $ignoreErrorsIntegrationsCount);
+ // 2048 is \E_STRICT which has been deprecated in PHP 8.4 so we should not reference it directly to prevent deprecation notices
+ $this->assertSame(\E_ALL & ~(\E_NOTICE | 2048 | \E_DEPRECATED), $optionsDefinition->getArgument(0)['error_types']);
}
/**
@@ -369,6 +339,7 @@ public function testInstrumentationIsDisabledWhenTracingIsDisabled(): void
$this->assertFalse($container->hasDefinition(TracingDriverMiddleware::class));
$this->assertFalse($container->hasDefinition(ConnectionConfigurator::class));
$this->assertFalse($container->hasDefinition(TwigTracingExtension::class));
+ $this->assertFalse($container->hasDefinition(TraceableHttpClient::class));
$this->assertFalse($container->getParameter('sentry.tracing.enabled'));
$this->assertEmpty($container->getParameter('sentry.tracing.dbal.connections'));
}
@@ -445,7 +416,48 @@ public function testLoggerOptionFallbackToNullLoggerIfNotSet(): void
$methodCalls = $factory[0]->getMethodCalls();
- $this->assertDefinitionMethodCallAt($methodCalls[5], 'setLogger', [new Reference(NullLogger::class, ContainerBuilder::IGNORE_ON_INVALID_REFERENCE)]);
+ $this->assertDefinitionMethodCallAt($methodCalls[3], 'setLogger', [new Reference(NullLogger::class, ContainerBuilder::IGNORE_ON_INVALID_REFERENCE)]);
+ }
+
+ public function testLoginListener(): void
+ {
+ $container = $this->createContainerFromFixture('full');
+ $this->assertTrue($container->hasDefinition(LoginListener::class));
+ }
+
+ /**
+ * @dataProvider releaseOptionDataProvider
+ */
+ public function testReleaseOption(string $fixtureFile, string $expectedRelease): void
+ {
+ $container = $this->createContainerFromFixture($fixtureFile);
+ $optionsDefinition = $container->getDefinition('sentry.client.options');
+
+ $this->assertSame(Options::class, $optionsDefinition->getClass());
+ $this->assertSame($expectedRelease, $container->resolveEnvPlaceholders($optionsDefinition->getArgument(0)['release'], true));
+ }
+
+ public function releaseOptionDataProvider(): \Generator
+ {
+ yield 'If the release option is set to a concrete value, then no fallback occurs' => [
+ 'release_option_from_config',
+ '1.0.x-dev',
+ ];
+
+ yield 'If the release option is set and references an environment variable, then no fallback occurs' => [
+ 'release_option_from_env_var',
+ '1.0.x-dev',
+ ];
+
+ yield 'If the release option is unset and the SENTRY_RELEASE environment variable is set, then the latter is used as fallback' => [
+ 'release_option_fallback_to_env_var',
+ '1.0.x-dev',
+ ];
+
+ yield 'If both the release option and the SENTRY_RELEASE environment variable are unset, then the root package version is used as fallback' => [
+ 'release_option_fallback_to_composer_version',
+ PrettyVersions::getRootPackageVersion()->getPrettyVersion(),
+ ];
}
private function createContainerFromFixture(string $fixtureFile): ContainerBuilder
@@ -454,14 +466,19 @@ private function createContainerFromFixture(string $fixtureFile): ContainerBuild
'kernel.cache_dir' => __DIR__,
'kernel.build_dir' => __DIR__,
'kernel.project_dir' => __DIR__,
+ 'kernel.environment' => 'dev',
'doctrine.default_connection' => 'default',
'doctrine.connections' => ['default'],
]));
$container->registerExtension(new SentryExtension());
- $container->getCompilerPassConfig()->setOptimizationPasses([]);
$container->getCompilerPassConfig()->setRemovingPasses([]);
$container->getCompilerPassConfig()->setAfterRemovingPasses([]);
+ $container->getCompilerPassConfig()->setOptimizationPasses([
+ new ValidateEnvPlaceholdersPass(),
+ new ResolveParameterPlaceHoldersPass(),
+ new ResolveTaggedIteratorArgumentPass(),
+ ]);
$this->loadFixture($container, $fixtureFile);
diff --git a/tests/DoctrineTestCase.php b/tests/DoctrineTestCase.php
index 6d4c7626..350d1ddf 100644
--- a/tests/DoctrineTestCase.php
+++ b/tests/DoctrineTestCase.php
@@ -7,6 +7,7 @@
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\ResultStatement;
+use Doctrine\DBAL\VersionAwarePlatformDriver;
use PHPUnit\Framework\TestCase;
abstract class DoctrineTestCase extends TestCase
@@ -25,7 +26,16 @@ protected static function isDoctrineDBALVersion2Installed(): bool
protected static function isDoctrineDBALVersion3Installed(): bool
{
return self::isDoctrineDBALInstalled()
- && !self::isDoctrineDBALVersion2Installed();
+ && !self::isDoctrineDBALVersion2Installed()
+ && interface_exists(VersionAwarePlatformDriver::class);
+ }
+
+ protected static function isDoctrineDBALVersion4Installed(): bool
+ {
+ return self::isDoctrineDBALInstalled()
+ && !self::isDoctrineDBALVersion2Installed()
+ && !self::isDoctrineDBALVersion3Installed()
+ && !interface_exists(VersionAwarePlatformDriver::class);
}
protected static function isDoctrineBundlePackageInstalled(): bool
diff --git a/tests/End2End/App/Controller/TracingController.php b/tests/End2End/App/Controller/TracingController.php
index 37579ae4..fa86876a 100644
--- a/tests/End2End/App/Controller/TracingController.php
+++ b/tests/End2End/App/Controller/TracingController.php
@@ -21,7 +21,7 @@ class TracingController
*/
private $connection;
- public function __construct(HubInterface $hub, Connection $connection = null)
+ public function __construct(HubInterface $hub, ?Connection $connection = null)
{
$this->hub = $hub;
$this->connection = $connection;
diff --git a/tests/End2End/App/Kernel.php b/tests/End2End/App/Kernel.php
index 47ab8beb..c3467c61 100644
--- a/tests/End2End/App/Kernel.php
+++ b/tests/End2End/App/Kernel.php
@@ -44,10 +44,21 @@ public function registerContainerConfiguration(LoaderInterface $loader): void
$loader->load(__DIR__ . '/deprecations_for_5.yml');
}
+ if (self::VERSION_ID >= 50400 && self::VERSION_ID <= 60000) {
+ // Check if class for Messenger is present (component symfony/messenger is not mandatory)
+ if (interface_exists(MessageBusInterface::class)) {
+ $loader->load(__DIR__ . '/deprecations_for_54.yml');
+ }
+ }
+
if (self::VERSION_ID >= 60000) {
$loader->load(__DIR__ . '/deprecations_for_6.yml');
}
+ if (self::VERSION_ID >= 60400) {
+ $loader->load(__DIR__ . '/deprecations_for_64.yml');
+ }
+
if (interface_exists(MessageBusInterface::class) && self::VERSION_ID >= 40300) {
$loader->load(__DIR__ . '/messenger.yml');
}
diff --git a/tests/End2End/App/Messenger/FooMessageHandler.php b/tests/End2End/App/Messenger/FooMessageHandler.php
index a2f47549..28518d96 100644
--- a/tests/End2End/App/Messenger/FooMessageHandler.php
+++ b/tests/End2End/App/Messenger/FooMessageHandler.php
@@ -5,14 +5,13 @@
namespace Sentry\SentryBundle\Tests\End2End\App\Messenger;
use Symfony\Component\Messenger\Exception\UnrecoverableExceptionInterface;
-use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
-class FooMessageHandler implements MessageHandlerInterface
+class FooMessageHandler
{
public function __invoke(FooMessage $message): void
{
if (!$message->shouldRetry()) {
- throw new class() extends \Exception implements UnrecoverableExceptionInterface { };
+ throw new class extends \Exception implements UnrecoverableExceptionInterface { };
}
throw new \Exception('This is an intentional failure while handling a message of class ' . \get_class($message));
diff --git a/tests/End2End/App/config.yml b/tests/End2End/App/config.yml
index 50aebfec..87aa6cf3 100644
--- a/tests/End2End/App/config.yml
+++ b/tests/End2End/App/config.yml
@@ -6,23 +6,27 @@ sentry:
capture_silenced_errors: false
error_types: E_ALL & ~E_USER_DEPRECATED
traces_sample_rate: 0
- ignore_exceptions: 'Symfony\Component\HttpKernel\Exception\BadRequestHttpException'
+ ignore_exceptions:
+ - 'Symfony\Component\HttpKernel\Exception\BadRequestHttpException'
+ - 'Symfony\Component\ErrorHandler\Error\FatalError'
+ - 'Symfony\Component\Debug\Exception\FatalErrorException'
ignore_transactions: 'GET tracing_ignored_transaction'
+ transport: 'Sentry\SentryBundle\Tests\End2End\StubTransport'
framework:
router: { resource: "%routing_config_dir%/routing.yml" }
secret: secret
test: ~
+ annotations: false
+ php_errors:
+ log: true
services:
test.hub:
alias: Sentry\State\HubInterface
public: true
- Sentry\SentryBundle\Tests\End2End\StubTransportFactory: ~
-
- Sentry\Transport\TransportFactoryInterface:
- alias: Sentry\SentryBundle\Tests\End2End\StubTransportFactory
+ Sentry\SentryBundle\Tests\End2End\StubTransport: ~
Sentry\SentryBundle\Tests\End2End\App\Controller\MainController:
autowire: true
diff --git a/tests/End2End/App/deprecations_for_54.yml b/tests/End2End/App/deprecations_for_54.yml
new file mode 100644
index 00000000..abd0417d
--- /dev/null
+++ b/tests/End2End/App/deprecations_for_54.yml
@@ -0,0 +1,3 @@
+framework:
+ messenger:
+ reset_on_message: true
diff --git a/tests/End2End/App/deprecations_for_64.yml b/tests/End2End/App/deprecations_for_64.yml
new file mode 100644
index 00000000..54dd667e
--- /dev/null
+++ b/tests/End2End/App/deprecations_for_64.yml
@@ -0,0 +1,2 @@
+framework:
+ handle_all_throwables: true
diff --git a/tests/End2End/App/messenger.yml b/tests/End2End/App/messenger.yml
index 5430583e..c729467d 100644
--- a/tests/End2End/App/messenger.yml
+++ b/tests/End2End/App/messenger.yml
@@ -7,6 +7,8 @@ services:
Sentry\SentryBundle\Tests\End2End\App\Messenger\FooMessageHandler:
class: \Sentry\SentryBundle\Tests\End2End\App\Messenger\FooMessageHandler
+ tags:
+ - { name: messenger.message_handler }
Sentry\SentryBundle\Tests\End2End\App\Controller\MessengerController:
autowire: true
diff --git a/tests/End2End/End2EndTest.php b/tests/End2End/End2EndTest.php
index 3ec26438..19ddfe06 100644
--- a/tests/End2End/End2EndTest.php
+++ b/tests/End2End/End2EndTest.php
@@ -198,8 +198,7 @@ public function testNotice(): void
public function testCommand(): void
{
- self::bootKernel();
- $application = new Application(self::$kernel);
+ $application = new Application(self::bootKernel());
try {
$application->doRun(new ArgvInput(['bin/console', 'main-command', '--option1', '--option2=foo', 'bar']), new NullOutput());
@@ -208,10 +207,10 @@ public function testCommand(): void
}
$this->assertEventCount(1);
- $this->assertCount(1, StubTransportFactory::$events);
+ $this->assertCount(1, StubTransport::$events);
$this->assertSame(
['Full command' => 'main-command --option1 --option2=foo bar'],
- StubTransportFactory::$events[0]->getExtra()
+ StubTransport::$events[0]->getExtra()
);
}
@@ -282,7 +281,7 @@ private function assertEventCount(int $expectedCount): void
{
$events = file_get_contents(self::SENT_EVENTS_LOG);
$this->assertNotFalse($events, 'Cannot read sent events log');
- $listOfEvents = array_filter(explode(StubTransportFactory::SEPARATOR, trim($events)));
+ $listOfEvents = array_filter(explode(StubTransport::SEPARATOR, trim($events)));
$this->assertCount($expectedCount, $listOfEvents, 'Wrong number of events sent: ' . \PHP_EOL . $events);
}
diff --git a/tests/End2End/StubTransport.php b/tests/End2End/StubTransport.php
new file mode 100644
index 00000000..3f3b7644
--- /dev/null
+++ b/tests/End2End/StubTransport.php
@@ -0,0 +1,52 @@
+getMessage()) {
+ $message = $event->getMessage();
+ } elseif ($event->getExceptions()) {
+ $message = $event->getExceptions()[0]->getValue();
+ } elseif ($event->getTransaction()) {
+ $message = 'TRACING: ' . $event->getTransaction();
+ foreach ($event->getSpans() as $i => $span) {
+ $message .= \PHP_EOL . $i . ') ' . $span->getDescription();
+ }
+ } else {
+ $message = 'NO MESSAGE NOR EXCEPTIONS';
+ }
+
+ file_put_contents(
+ End2EndTest::SENT_EVENTS_LOG,
+ $event->getId() . ': ' . $message . \PHP_EOL . self::SEPARATOR . \PHP_EOL,
+ \FILE_APPEND
+ );
+
+ return new Result(ResultStatus::success(), $event);
+ }
+
+ public function close(?int $timeout = null): Result
+ {
+ return new Result(ResultStatus::success());
+ }
+}
diff --git a/tests/End2End/StubTransportFactory.php b/tests/End2End/StubTransportFactory.php
deleted file mode 100644
index 061f068b..00000000
--- a/tests/End2End/StubTransportFactory.php
+++ /dev/null
@@ -1,61 +0,0 @@
-getMessage()) {
- $message = $event->getMessage();
- } elseif ($event->getExceptions()) {
- $message = $event->getExceptions()[0]->getValue();
- } elseif ($event->getTransaction()) {
- $message = 'TRACING: ' . $event->getTransaction();
- foreach ($event->getSpans() as $i => $span) {
- $message .= \PHP_EOL . $i . ') ' . $span->getDescription();
- }
- } else {
- $message = 'NO MESSAGE NOR EXCEPTIONS';
- }
-
- file_put_contents(
- End2EndTest::SENT_EVENTS_LOG,
- $event->getId() . ': ' . $message . \PHP_EOL . StubTransportFactory::SEPARATOR . \PHP_EOL,
- \FILE_APPEND
- );
-
- return new FulfilledPromise(new Response(ResponseStatus::success(), $event));
- }
-
- public function close(?int $timeout = null): PromiseInterface
- {
- return new FulfilledPromise(true);
- }
- };
- }
-}
diff --git a/tests/End2End/TracingEnd2EndTest.php b/tests/End2End/TracingEnd2EndTest.php
index b3204d0c..0a764b32 100644
--- a/tests/End2End/TracingEnd2EndTest.php
+++ b/tests/End2End/TracingEnd2EndTest.php
@@ -87,7 +87,7 @@ private function assertTracingEventCount(int $expectedCount): void
{
$events = file_get_contents(self::SENT_EVENTS_LOG);
$this->assertNotFalse($events, 'Cannot read sent events log');
- $listOfTracingEvents = array_filter(explode(StubTransportFactory::SEPARATOR, trim($events)), static function (string $elem) {
+ $listOfTracingEvents = array_filter(explode(StubTransport::SEPARATOR, trim($events)), static function (string $elem) {
return str_contains('TRACING', $elem);
});
diff --git a/tests/ErrorTypesParserTest.php b/tests/ErrorTypesParserTest.php
index e30d6c78..9e3cd532 100644
--- a/tests/ErrorTypesParserTest.php
+++ b/tests/ErrorTypesParserTest.php
@@ -71,5 +71,8 @@ public function parseThrowsExceptionIfArgumentContainsInvalidCharactersDataProvi
yield ['('];
yield [')'];
yield ['()'];
+ // Non scalar values (probably misstypes, but still valid PHP code)
+ yield ['[8, 8192]'];
+ yield [\stdClass::class];
}
}
diff --git a/tests/EventListener/ConsoleCommandListenerTest.php b/tests/EventListener/ConsoleCommandListenerTest.php
deleted file mode 100644
index e217dca4..00000000
--- a/tests/EventListener/ConsoleCommandListenerTest.php
+++ /dev/null
@@ -1,23 +0,0 @@
-createMock(HttpKernelInterface::class),
new Request(),
- HttpKernelInterface::MASTER_REQUEST,
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST,
new \Exception()
),
];
diff --git a/tests/EventListener/Fixtures/UserWithIdentifierStub.php b/tests/EventListener/Fixtures/UserWithIdentifierStub.php
index 9b82b8ea..97eccde9 100644
--- a/tests/EventListener/Fixtures/UserWithIdentifierStub.php
+++ b/tests/EventListener/Fixtures/UserWithIdentifierStub.php
@@ -9,20 +9,29 @@
final class UserWithIdentifierStub implements UserInterface
{
/**
- * @var string
+ * @var non-empty-string
*/
private $username;
+ /**
+ * @param non-empty-string $username
+ */
public function __construct(string $username = 'foo_user')
{
$this->username = $username;
}
+ /**
+ * @return non-empty-string
+ */
public function getUserIdentifier(): string
{
return $this->getUsername();
}
+ /**
+ * @return non-empty-string
+ */
public function getUsername(): string
{
return $this->username;
diff --git a/tests/EventListener/LoginListenerTest.php b/tests/EventListener/LoginListenerTest.php
index 44c1838b..da037bef 100644
--- a/tests/EventListener/LoginListenerTest.php
+++ b/tests/EventListener/LoginListenerTest.php
@@ -88,7 +88,7 @@ public function testHandleKernelRequestEvent(TokenInterface $token, ?UserDataBag
$this->listener->handleKernelRequestEvent(new RequestEvent(
$this->createMock(HttpKernelInterface::class),
new Request(),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
));
$event = $scope->applyToEvent(Event::createEvent());
@@ -183,17 +183,33 @@ public function testHandleAuthenticationSuccessEvent(TokenInterface $token, ?Use
public function authenticationTokenDataProvider(): \Generator
{
- yield 'If the username is already set on the User context, then it is not overridden' => [
- new AuthenticatedTokenStub(new UserWithIdentifierStub()),
- new UserDataBag('bar_user'),
- new UserDataBag('bar_user'),
- ];
+ if (version_compare(Kernel::VERSION, '5.4', '<')) {
+ yield 'If the username is already set on the User context, then it is not overridden' => [
+ new LegacyAuthenticatedTokenStub(new UserWithIdentifierStub()),
+ new UserDataBag('bar_user'),
+ new UserDataBag('bar_user'),
+ ];
+ } else {
+ yield 'If the username is already set on the User context, then it is not overridden' => [
+ new AuthenticatedTokenStub(new UserWithIdentifierStub()),
+ new UserDataBag('bar_user'),
+ new UserDataBag('bar_user'),
+ ];
+ }
- yield 'If the username is not set on the User context, then it is retrieved from the token' => [
- new AuthenticatedTokenStub(new UserWithIdentifierStub()),
- null,
- new UserDataBag('foo_user'),
- ];
+ if (version_compare(Kernel::VERSION, '5.4', '<')) {
+ yield 'If the username is not set on the User context, then it is retrieved from the token' => [
+ new LegacyAuthenticatedTokenStub(new UserWithIdentifierStub()),
+ null,
+ new UserDataBag('foo_user'),
+ ];
+ } else {
+ yield 'If the username is not set on the User context, then it is retrieved from the token' => [
+ new AuthenticatedTokenStub(new UserWithIdentifierStub()),
+ null,
+ new UserDataBag('foo_user'),
+ ];
+ }
yield 'If the user is being impersonated, then the username of the impersonator is set on the User context' => [
(static function (): SwitchUserToken {
@@ -203,7 +219,8 @@ public function authenticationTokenDataProvider(): \Generator
null,
'foo_provider',
['ROLE_USER'],
- new AuthenticatedTokenStub(new UserWithIdentifierStub('bar_user'))
+ // @phpstan-ignore-next-line
+ new LegacyAuthenticatedTokenStub(new UserWithIdentifierStub('bar_user'))
);
}
@@ -228,28 +245,69 @@ public function authenticationTokenForSymfonyVersionLowerThan54DataProvider(): \
return;
}
- yield 'If the user is a string, then the value is used as-is' => [
- new AuthenticatedTokenStub('foo_user'),
- null,
- new UserDataBag('foo_user'),
- ];
+ if (version_compare(Kernel::VERSION, '5.0', '<')) {
+ yield 'If the user is a string, then the value is used as-is' => [
+ new LegacyAuthenticatedTokenStub('foo_user'),
+ null,
+ new UserDataBag('foo_user'),
+ ];
+ } else {
+ yield 'If the user is a string, then the value is used as-is' => [
+ new AuthenticatedTokenStub('foo_user'),
+ null,
+ new UserDataBag('foo_user'),
+ ];
+ }
- yield 'If the user is an instance of the UserInterface interface but the getUserIdentifier() method does not exist, then the getUsername() method is invoked' => [
- new AuthenticatedTokenStub(new UserWithoutIdentifierStub()),
- null,
- new UserDataBag('foo_user'),
- ];
+ if (version_compare(Kernel::VERSION, '5.0', '<')) {
+ yield 'If the user is an instance of the UserInterface interface but the getUserIdentifier() method does not exist, then the getUsername() method is invoked' => [
+ new LegacyAuthenticatedTokenStub(new UserWithoutIdentifierStub()),
+ null,
+ new UserDataBag('foo_user'),
+ ];
+ } else {
+ yield 'If the user is an instance of the UserInterface interface but the getUserIdentifier() method does not exist, then the getUsername() method is invoked' => [
+ new AuthenticatedTokenStub(new UserWithoutIdentifierStub()),
+ null,
+ new UserDataBag('foo_user'),
+ ];
+ }
- yield 'If the user is an object implementing the Stringable interface, then the __toString() method is invoked' => [
- new AuthenticatedTokenStub(new class() implements \Stringable {
- public function __toString(): string
- {
- return 'foo_user';
- }
- }),
- null,
- new UserDataBag('foo_user'),
- ];
+ if (version_compare(Kernel::VERSION, '5.0', '<')) {
+ yield 'If the user is an object implementing the Stringable interface, then the __toString() method is invoked' => [
+ new LegacyAuthenticatedTokenStub(new class implements \Stringable {
+ public function __toString(): string
+ {
+ return 'foo_user';
+ }
+ }),
+ null,
+ new UserDataBag('foo_user'),
+ ];
+ } else {
+ yield 'If the user is an object implementing the Stringable interface, then the __toString() method is invoked' => [
+ new AuthenticatedTokenStub(new class implements \Stringable {
+ public function __toString(): string
+ {
+ return 'foo_user';
+ }
+ }),
+ null,
+ new UserDataBag('foo_user'),
+ ];
+ }
+ }
+
+ public function testHandleKernelRequestEventDoesNothingIfRequestIsForStatelessRoute(): void
+ {
+ $this->tokenStorage->expects($this->never())
+ ->method('getToken');
+
+ $this->listener->handleKernelRequestEvent(new RequestEvent(
+ $this->createMock(HttpKernelInterface::class),
+ new Request([], [], ['_stateless' => true]),
+ HttpKernelInterface::SUB_REQUEST
+ ));
}
public function testHandleKernelRequestEventDoesNothingIfRequestIsNotMain(): void
@@ -273,7 +331,7 @@ public function testHandleKernelRequestEventDoesNothingIfTokenIsNotSet(): void
$this->listener->handleKernelRequestEvent(new RequestEvent(
$this->createMock(HttpKernelInterface::class),
new Request(),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
));
}
@@ -312,14 +370,25 @@ public function testHandleLoginSuccessEventDoesNothingIfClientIsNotSetOnHub(): v
$this->hub->expects($this->never())
->method('configureScope');
- $this->listener->handleLoginSuccessEvent(new LoginSuccessEvent(
- $this->createMock(AuthenticatorInterface::class),
- new SelfValidatingPassport(new UserBadge('foo_passport_user')),
- new AuthenticatedTokenStub(new UserWithIdentifierStub()),
- new Request(),
- null,
- 'main'
- ));
+ if (version_compare(Kernel::VERSION, '5.4', '<')) {
+ $this->listener->handleLoginSuccessEvent(new LoginSuccessEvent(
+ $this->createMock(AuthenticatorInterface::class),
+ new SelfValidatingPassport(new UserBadge('foo_passport_user')),
+ new LegacyAuthenticatedTokenStub(new UserWithIdentifierStub()),
+ new Request(),
+ null,
+ 'main'
+ ));
+ } else {
+ $this->listener->handleLoginSuccessEvent(new LoginSuccessEvent(
+ $this->createMock(AuthenticatorInterface::class),
+ new SelfValidatingPassport(new UserBadge('foo_passport_user')),
+ new AuthenticatedTokenStub(new UserWithIdentifierStub()),
+ new Request(),
+ null,
+ 'main'
+ ));
+ }
}
public function testHandleLoginSuccessEventDoesNothingIfSendingDefaultPiiIsDisabled(): void
@@ -340,14 +409,25 @@ public function testHandleLoginSuccessEventDoesNothingIfSendingDefaultPiiIsDisab
$this->hub->expects($this->never())
->method('configureScope');
- $this->listener->handleLoginSuccessEvent(new LoginSuccessEvent(
- $this->createMock(AuthenticatorInterface::class),
- new SelfValidatingPassport(new UserBadge('foo_passport_user')),
- new AuthenticatedTokenStub(new UserWithIdentifierStub()),
- new Request(),
- null,
- 'main'
- ));
+ if (version_compare(Kernel::VERSION, '5.4', '<')) {
+ $this->listener->handleLoginSuccessEvent(new LoginSuccessEvent(
+ $this->createMock(AuthenticatorInterface::class),
+ new SelfValidatingPassport(new UserBadge('foo_passport_user')),
+ new LegacyAuthenticatedTokenStub(new UserWithIdentifierStub()),
+ new Request(),
+ null,
+ 'main'
+ ));
+ } else {
+ $this->listener->handleLoginSuccessEvent(new LoginSuccessEvent(
+ $this->createMock(AuthenticatorInterface::class),
+ new SelfValidatingPassport(new UserBadge('foo_passport_user')),
+ new AuthenticatedTokenStub(new UserWithIdentifierStub()),
+ new Request(),
+ null,
+ 'main'
+ ));
+ }
}
public function testHandleAuthenticationSuccessEventDoesNothingIfTokenIsNotAuthenticated(): void
@@ -378,7 +458,11 @@ public function testHandleAuthenticationSuccessEventDoesNothingIfClientIsNotSetO
$this->hub->expects($this->never())
->method('configureScope');
- $this->listener->handleAuthenticationSuccessEvent(new AuthenticationSuccessEvent(new AuthenticatedTokenStub(new UserWithIdentifierStub())));
+ if (version_compare(Kernel::VERSION, '5.4', '<')) {
+ $this->listener->handleAuthenticationSuccessEvent(new AuthenticationSuccessEvent(new LegacyAuthenticatedTokenStub(new UserWithIdentifierStub())));
+ } else {
+ $this->listener->handleAuthenticationSuccessEvent(new AuthenticationSuccessEvent(new AuthenticatedTokenStub(new UserWithIdentifierStub())));
+ }
}
public function testHandleAuthenticationSuccessEventDoesNothingIfSendingDefaultPiiIsDisabled(): void
@@ -399,7 +483,11 @@ public function testHandleAuthenticationSuccessEventDoesNothingIfSendingDefaultP
$this->hub->expects($this->never())
->method('configureScope');
- $this->listener->handleAuthenticationSuccessEvent(new AuthenticationSuccessEvent(new AuthenticatedTokenStub(new UserWithIdentifierStub())));
+ if (version_compare(Kernel::VERSION, '5.4', '<')) {
+ $this->listener->handleAuthenticationSuccessEvent(new AuthenticationSuccessEvent(new LegacyAuthenticatedTokenStub(new UserWithIdentifierStub())));
+ } else {
+ $this->listener->handleAuthenticationSuccessEvent(new AuthenticationSuccessEvent(new AuthenticatedTokenStub(new UserWithIdentifierStub())));
+ }
}
}
@@ -416,8 +504,47 @@ public function getCredentials(): ?string
}
}
+class LegacyAuthenticatedTokenStub extends AbstractToken
+{
+ /**
+ * @var bool
+ *
+ * @phpstan-ignore-next-line
+ */
+ private $authenticated = false;
+
+ /**
+ * @param UserInterface|\Stringable|string|null $user
+ */
+ public function __construct($user)
+ {
+ parent::__construct();
+
+ if (null !== $user) {
+ // @phpstan-ignore-next-line
+ $this->setUser($user);
+ }
+
+ if (version_compare(Kernel::VERSION, '5.4', '<') && method_exists($this, 'setAuthenticated')) {
+ $this->setAuthenticated(true);
+ } else {
+ $this->authenticated = true;
+ }
+ }
+
+ public function getCredentials(): ?string
+ {
+ return null;
+ }
+}
+
final class AuthenticatedTokenStub extends AbstractToken
{
+ /**
+ * @var bool
+ */
+ private $authenticated = false;
+
/**
* @param UserInterface|\Stringable|string|null $user
*/
@@ -426,14 +553,22 @@ public function __construct($user)
parent::__construct();
if (null !== $user) {
+ // @phpstan-ignore-next-line
$this->setUser($user);
}
- if (method_exists($this, 'setAuthenticated')) {
+ if (version_compare(Kernel::VERSION, '5.4', '<') && method_exists($this, 'setAuthenticated')) {
$this->setAuthenticated(true);
+ } else {
+ $this->authenticated = true;
}
}
+ public function isAuthenticated(): bool
+ {
+ return $this->authenticated;
+ }
+
public function getCredentials(): ?string
{
return null;
diff --git a/tests/EventListener/MessengerListenerTest.php b/tests/EventListener/MessengerListenerTest.php
index 0404edad..8e850aaf 100644
--- a/tests/EventListener/MessengerListenerTest.php
+++ b/tests/EventListener/MessengerListenerTest.php
@@ -15,6 +15,7 @@
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
+use Symfony\Component\Messenger\Exception\DelayedMessageHandlingException;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\BusNameStamp;
@@ -113,6 +114,16 @@ public function handleWorkerMessageFailedEventDataProvider(): \Generator
false,
];
+ yield 'envelope.throwable INSTANCEOF DelayedMessageHandlingException' => [
+ $exceptions,
+ $this->getMessageFailedEvent($envelope, 'receiver', new DelayedMessageHandlingException($exceptions, $envelope), false),
+ [
+ 'messenger.receiver_name' => 'receiver',
+ 'messenger.message_class' => \get_class($envelope->getMessage()),
+ ],
+ false,
+ ];
+
yield 'envelope.throwable INSTANCEOF HandlerFailedException - RETRYING' => [
$exceptions,
$this->getMessageFailedEvent($envelope, 'receiver', new HandlerFailedException($envelope, $exceptions), true),
diff --git a/tests/EventListener/RequestListenerTest.php b/tests/EventListener/RequestListenerTest.php
index 440db6e7..5532df9d 100644
--- a/tests/EventListener/RequestListenerTest.php
+++ b/tests/EventListener/RequestListenerTest.php
@@ -82,7 +82,7 @@ public function handleKernelRequestEventDataProvider(): \Generator
new RequestEvent(
$this->createMock(HttpKernelInterface::class),
new Request([], [], [], [], [], ['REMOTE_ADDR' => '127.0.0.1']),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
),
$this->getMockedClientWithOptions(new Options(['send_default_pii' => false])),
new UserDataBag(),
@@ -93,7 +93,7 @@ public function handleKernelRequestEventDataProvider(): \Generator
new RequestEvent(
$this->createMock(HttpKernelInterface::class),
new Request(),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
),
$this->getMockedClientWithOptions(new Options(['send_default_pii' => true])),
new UserDataBag(),
@@ -104,7 +104,7 @@ public function handleKernelRequestEventDataProvider(): \Generator
new RequestEvent(
$this->createMock(HttpKernelInterface::class),
new Request([], [], [], [], [], ['REMOTE_ADDR' => '127.0.0.1']),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
),
$this->getMockedClientWithOptions(new Options(['send_default_pii' => true])),
new UserDataBag('foo_user'),
@@ -115,7 +115,7 @@ public function handleKernelRequestEventDataProvider(): \Generator
new RequestEvent(
$this->createMock(HttpKernelInterface::class),
new Request([], [], [], [], [], ['REMOTE_ADDR' => '127.0.0.1']),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
),
$this->getMockedClientWithOptions(new Options(['send_default_pii' => true])),
new UserDataBag('foo_user', null, '::1'),
@@ -168,7 +168,7 @@ static function () {
static function () {
},
new Request(),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
),
[],
];
@@ -179,7 +179,7 @@ static function () {
static function () {
},
new Request([], [], ['_route' => 'homepage']),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
),
[
'route' => 'homepage',
diff --git a/tests/EventListener/SubRequestListenerTest.php b/tests/EventListener/SubRequestListenerTest.php
index c8c0b915..3b5b63a4 100644
--- a/tests/EventListener/SubRequestListenerTest.php
+++ b/tests/EventListener/SubRequestListenerTest.php
@@ -53,7 +53,7 @@ public function testHandleKernelRequestEvent(RequestEvent $event): void
public function handleKernelRequestEventDataProvider(): \Generator
{
yield [
- new RequestEvent($this->createMock(HttpKernelInterface::class), new Request(), HttpKernelInterface::MASTER_REQUEST),
+ new RequestEvent($this->createMock(HttpKernelInterface::class), new Request(), \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST),
];
yield [
@@ -80,7 +80,7 @@ public function testHandleKernelFinishRequestEvent($event): void
public function handleKernelFinishRequestEventDataProvider(): \Generator
{
yield [
- new FinishRequestEvent($this->createMock(HttpKernelInterface::class), new Request(), HttpKernelInterface::MASTER_REQUEST),
+ new FinishRequestEvent($this->createMock(HttpKernelInterface::class), new Request(), \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST),
];
yield [
diff --git a/tests/EventListener/TracingConsoleListenerTest.php b/tests/EventListener/TracingConsoleListenerTest.php
index 3987ea04..b619dfe7 100644
--- a/tests/EventListener/TracingConsoleListenerTest.php
+++ b/tests/EventListener/TracingConsoleListenerTest.php
@@ -44,6 +44,9 @@ public function testHandleConsoleCommandEventStartsTransactionIfNoSpanIsSetOnHub
$this->hub->expects($this->once())
->method('startTransaction')
->with($this->callback(function (TransactionContext $context) use ($expectedTransactionContext): bool {
+ // This value is random when the metadata is constructed, thus we set it to a fixed expected value since we don't care for the value here
+ $context->getMetadata()->setSampleRand(0.1337);
+
$this->assertEquals($expectedTransactionContext, $context);
return true;
@@ -71,7 +74,9 @@ public function handleConsoleCommandEventStartsTransactionIfNoSpanIsSetOnHubData
$transactionContext = new TransactionContext();
$transactionContext->setOp('console.command');
$transactionContext->setName('');
+ $transactionContext->setOrigin('auto.console');
$transactionContext->setSource(TransactionSource::task());
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield [
new Command(),
@@ -81,7 +86,9 @@ public function handleConsoleCommandEventStartsTransactionIfNoSpanIsSetOnHubData
$transactionContext = new TransactionContext();
$transactionContext->setOp('console.command');
$transactionContext->setName('app:command');
+ $transactionContext->setOrigin('auto.console');
$transactionContext->setSource(TransactionSource::task());
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield [
new Command('app:command'),
diff --git a/tests/EventListener/TracingRequestListenerTest.php b/tests/EventListener/TracingRequestListenerTest.php
index 5dfa1ff4..1c0c3193 100644
--- a/tests/EventListener/TracingRequestListenerTest.php
+++ b/tests/EventListener/TracingRequestListenerTest.php
@@ -65,6 +65,9 @@ public function testHandleKernelRequestEvent(Options $options, Request $request,
$this->hub->expects($this->once())
->method('startTransaction')
->with($this->callback(function (TransactionContext $context) use ($expectedTransactionContext): bool {
+ // This value is random when the metadata is constructed, thus we set it to a fixed expected value since we don't care for the value here
+ $context->getMetadata()->setSampleRand(0.1337);
+
$this->assertEquals($expectedTransactionContext, $context);
return true;
@@ -78,7 +81,7 @@ public function testHandleKernelRequestEvent(Options $options, Request $request,
$this->listener->handleKernelRequestEvent(new RequestEvent(
$this->createMock(HttpKernelInterface::class),
$request,
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
));
}
@@ -97,16 +100,18 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://www.example.com/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'http.flavor' => '1.1',
'route' => '',
'net.host.name' => 'www.example.com',
]);
$transactionContext->getMetadata()->setDynamicSamplingContext($samplingContext);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.headers.sentry-trace EXISTS' => [
new Options(),
@@ -124,6 +129,45 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext,
];
+ $samplingContext = DynamicSamplingContext::fromHeader('');
+ $samplingContext->freeze();
+
+ $transactionContext = new TransactionContext();
+ $transactionContext->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'));
+ $transactionContext->setParentSpanId(new SpanId('566e3688a61d4bc8'));
+ $transactionContext->setParentSampled(true);
+ $transactionContext->setName('GET http://www.example.com/');
+ $transactionContext->setSource(TransactionSource::url());
+ $transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
+ $transactionContext->setStartTimestamp(1613493597.010275);
+ $transactionContext->setData([
+ 'net.host.port' => '80',
+ 'http.request.method' => 'GET',
+ 'http.url' => 'http://www.example.com/',
+ 'http.flavor' => '1.1',
+ 'route' => '',
+ 'net.host.name' => 'www.example.com',
+ ]);
+ $transactionContext->getMetadata()->setDynamicSamplingContext($samplingContext);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
+
+ yield 'request.headers.traceparent EXISTS' => [
+ new Options(),
+ Request::create(
+ 'http://www.example.com',
+ 'GET',
+ [],
+ [],
+ [],
+ [
+ 'REQUEST_TIME_FLOAT' => 1613493597.010275,
+ 'HTTP_traceparent' => '00-566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8-01',
+ ]
+ ),
+ $transactionContext,
+ ];
+
$samplingContext = DynamicSamplingContext::fromHeader('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-public_key=public,sentry-sample_rate=1');
$samplingContext->freeze();
@@ -134,16 +178,19 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://www.example.com/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'http.flavor' => '1.1',
'route' => '',
'net.host.name' => 'www.example.com',
]);
$transactionContext->getMetadata()->setDynamicSamplingContext($samplingContext);
+ $transactionContext->getMetadata()->setParentSamplingRate(1.0);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.headers.sentry-trace and headers.baggage EXISTS' => [
new Options(),
@@ -166,15 +213,17 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://www.example.com/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'http.flavor' => '1.1',
'route' => '',
'net.host.name' => 'www.example.com',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
$request = Request::create('http://www.example.com');
$request->server->remove('REQUEST_TIME_FLOAT');
@@ -189,15 +238,17 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://127.0.0.1/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://127.0.0.1/',
'http.flavor' => '1.1',
'route' => '',
'net.host.ip' => '127.0.0.1',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.server.HOST IS IPV4' => [
new Options(),
@@ -220,15 +271,17 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET app_homepage');
$transactionContext->setSource(TransactionSource::route());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/path',
'http.flavor' => '1.1',
'route' => 'app_homepage',
'net.host.name' => 'www.example.com',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.attributes.route IS STRING' => [
new Options(),
@@ -244,15 +297,17 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://www.example.com/path');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/path',
'http.flavor' => '1.1',
'route' => '/path',
'net.host.name' => 'www.example.com',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.attributes.route IS INSTANCEOF Symfony\Component\Routing\Route' => [
new Options(),
@@ -268,15 +323,17 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://www.example.com/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'http.flavor' => '1.1',
'route' => 'App\\Controller::indexAction',
'net.host.name' => 'www.example.com',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.attributes._controller IS STRING' => [
new Options(),
@@ -292,15 +349,17 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://www.example.com/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'http.flavor' => '1.1',
'route' => 'App\\Controller::indexAction',
'net.host.name' => 'www.example.com',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.attributes._controller IS CALLABLE (1)' => [
new Options(),
@@ -310,21 +369,23 @@ public function handleKernelRequestEventDataProvider(): \Generator
$request = Request::create('http://www.example.com/');
$request->server->set('REQUEST_TIME_FLOAT', 1613493597.010275);
- $request->attributes->set('_controller', [new class() {}, 'indexAction']);
+ $request->attributes->set('_controller', [new class {}, 'indexAction']);
$transactionContext = new TransactionContext();
$transactionContext->setName('GET http://www.example.com/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'http.flavor' => '1.1',
'route' => 'class@anonymous::indexAction',
'net.host.name' => 'www.example.com',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.attributes._controller IS CALLABLE (2)' => [
new Options(),
@@ -340,15 +401,17 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://www.example.com/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'http.flavor' => '1.1',
'route' => '',
'net.host.name' => 'www.example.com',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.attributes._controller IS ARRAY and NOT VALID CALLABLE' => [
new Options(),
@@ -364,16 +427,18 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://www.example.com/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '80',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'http.flavor' => '1.1',
'route' => '',
'net.host.name' => 'www.example.com',
'net.peer.ip' => '127.0.0.1',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.server.REMOTE_ADDR EXISTS and client.options.send_default_pii = TRUE' => [
new Options(['send_default_pii' => true]),
@@ -388,14 +453,16 @@ public function handleKernelRequestEventDataProvider(): \Generator
$transactionContext->setName('GET http://:/');
$transactionContext->setSource(TransactionSource::url());
$transactionContext->setOp('http.server');
+ $transactionContext->setOrigin('auto.http.server');
$transactionContext->setStartTimestamp(1613493597.010275);
- $transactionContext->setTags([
+ $transactionContext->setData([
'net.host.port' => '',
- 'http.method' => 'GET',
+ 'http.request.method' => 'GET',
'http.url' => 'http://:/',
'route' => '',
'net.host.name' => '',
]);
+ $transactionContext->getMetadata()->setSampleRand(0.1337);
yield 'request.server.SERVER_PROTOCOL NOT EXISTS' => [
new Options(),
@@ -427,12 +494,11 @@ public function testHandleResponseRequestEvent(): void
$this->listener->handleKernelResponseEvent(new ResponseEvent(
$this->createMock(HttpKernelInterface::class),
new Request(),
- HttpKernelInterface::MASTER_REQUEST,
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST,
new Response()
));
$this->assertSame(SpanStatus::ok(), $transaction->getStatus());
- $this->assertSame(['http.status_code' => '200'], $transaction->getTags());
}
public function testHandleResponseRequestEventDoesNothingIfNoTransactionIsSetOnHub(): void
@@ -444,7 +510,7 @@ public function testHandleResponseRequestEventDoesNothingIfNoTransactionIsSetOnH
$this->listener->handleKernelResponseEvent(new ResponseEvent(
$this->createMock(HttpKernelInterface::class),
new Request(),
- HttpKernelInterface::MASTER_REQUEST,
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST,
new Response()
));
}
diff --git a/tests/EventListener/TracingSubRequestListenerTest.php b/tests/EventListener/TracingSubRequestListenerTest.php
index 9503e2a3..75effa05 100644
--- a/tests/EventListener/TracingSubRequestListenerTest.php
+++ b/tests/EventListener/TracingSubRequestListenerTest.php
@@ -75,8 +75,8 @@ public function handleKernelRequestEventDataProvider(): \Generator
$span = new Span();
$span->setOp('http.server');
$span->setDescription('GET http://www.example.com/path');
- $span->setTags([
- 'http.method' => 'GET',
+ $span->setData([
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/path',
'route' => 'App\\Controller::indexAction',
]);
@@ -92,8 +92,8 @@ public function handleKernelRequestEventDataProvider(): \Generator
$span = new Span();
$span->setOp('http.server');
$span->setDescription('GET http://www.example.com/');
- $span->setTags([
- 'http.method' => 'GET',
+ $span->setData([
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'route' => 'App\\Controller::indexAction',
]);
@@ -104,13 +104,13 @@ public function handleKernelRequestEventDataProvider(): \Generator
];
$request = Request::create('http://www.example.com/');
- $request->attributes->set('_controller', [new class() {}, 'indexAction']);
+ $request->attributes->set('_controller', [new class {}, 'indexAction']);
$span = new Span();
$span->setOp('http.server');
$span->setDescription('GET http://www.example.com/');
- $span->setTags([
- 'http.method' => 'GET',
+ $span->setData([
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'route' => 'class@anonymous::indexAction',
]);
@@ -126,8 +126,8 @@ public function handleKernelRequestEventDataProvider(): \Generator
$span = new Span();
$span->setOp('http.server');
$span->setDescription('GET http://www.example.com/');
- $span->setTags([
- 'http.method' => 'GET',
+ $span->setData([
+ 'http.request.method' => 'GET',
'http.url' => 'http://www.example.com/',
'route' => '',
]);
@@ -146,7 +146,7 @@ public function testHandleKernelRequestEventDoesNothingIfRequestTypeIsMasterRequ
$this->listener->handleKernelRequestEvent(new RequestEvent(
$this->createMock(HttpKernelInterface::class),
new Request(),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
));
}
@@ -191,7 +191,7 @@ public function testHandleKernelFinishRequestEventDoesNothingIfRequestTypeIsMast
$this->listener->handleKernelFinishRequestEvent(new FinishRequestEvent(
$this->createMock(HttpKernelInterface::class),
new Request(),
- HttpKernelInterface::MASTER_REQUEST
+ \defined(HttpKernelInterface::class . '::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST
));
}
@@ -224,7 +224,6 @@ public function testHandleResponseRequestEvent(): void
));
$this->assertSame(SpanStatus::ok(), $span->getStatus());
- $this->assertSame(['http.status_code' => '200'], $span->getTags());
}
public function testHandleResponseRequestEventDoesNothingIfNoTransactionIsSetOnHub(): void
diff --git a/tests/Integration/IntegrationConfiguratorTest.php b/tests/Integration/IntegrationConfiguratorTest.php
index 44206b5c..4fd59339 100644
--- a/tests/Integration/IntegrationConfiguratorTest.php
+++ b/tests/Integration/IntegrationConfiguratorTest.php
@@ -50,7 +50,7 @@ public function integrationsDataProvider(): iterable
$environmentIntegration = new EnvironmentIntegration();
$modulesIntegration = new ModulesIntegration();
- $userIntegration1 = new class() implements IntegrationInterface {
+ $userIntegration1 = new class implements IntegrationInterface {
public function setupOnce(): void
{
}
diff --git a/tests/Integration/RequestFetcherTest.php b/tests/Integration/RequestFetcherTest.php
index 62597ce3..57916b8c 100644
--- a/tests/Integration/RequestFetcherTest.php
+++ b/tests/Integration/RequestFetcherTest.php
@@ -2,9 +2,8 @@
declare(strict_types=1);
-namespace Sentry\SentryBundle\Test\Integration;
+namespace Sentry\SentryBundle\Tests\Integration;
-use GuzzleHttp\Psr7\ServerRequest;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface;
@@ -37,16 +36,16 @@ protected function setUp(): void
$this->requestFetcher = new RequestFetcher($this->requestStack, $this->httpMessageFactory);
}
- /**
- * @dataProvider fetchRequestDataProvider
- */
- public function testFetchRequest(?Request $request, ?ServerRequestInterface $expectedRequest): void
+ public function testFetchRequest(): void
{
+ $request = Request::create('https://www.example.com');
+ $expectedRequest = $this->createMock(ServerRequestInterface::class);
+
$this->requestStack->expects($this->once())
->method('getCurrentRequest')
->willReturn($request);
- $this->httpMessageFactory->expects(null !== $expectedRequest ? $this->once() : $this->never())
+ $this->httpMessageFactory->expects($this->once())
->method('createRequest')
->with($request)
->willReturn($expectedRequest);
@@ -54,7 +53,19 @@ public function testFetchRequest(?Request $request, ?ServerRequestInterface $exp
$this->assertSame($expectedRequest, $this->requestFetcher->fetchRequest());
}
- public function testFetchRequestFailsSilently(): void
+ public function testFetchRequestReturnsNullIfTheRequestStackIsEmpty(): void
+ {
+ $this->requestStack->expects($this->once())
+ ->method('getCurrentRequest')
+ ->willReturn(null);
+
+ $this->httpMessageFactory->expects($this->never())
+ ->method('createRequest');
+
+ $this->assertNull($this->requestFetcher->fetchRequest());
+ }
+
+ public function testFetchRequestReturnsNullIfTheRequestFactoryThrowsAnException(): void
{
$this->requestStack->expects($this->once())
->method('getCurrentRequest')
@@ -66,20 +77,4 @@ public function testFetchRequestFailsSilently(): void
$this->assertNull($this->requestFetcher->fetchRequest());
}
-
- /**
- * @return \Generator
- */
- public function fetchRequestDataProvider(): \Generator
- {
- yield [
- null,
- null,
- ];
-
- yield [
- Request::create('http://www.example.com'),
- new ServerRequest('GET', 'http://www.example.com'),
- ];
- }
}
diff --git a/tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php b/tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php
index 75583c8b..4b49194f 100644
--- a/tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php
+++ b/tests/Tracing/Cache/AbstractTraceableCacheAdapterTest.php
@@ -159,7 +159,7 @@ public function testGetThrowsExceptionIfDecoratedAdapterDoesNotImplementTheCache
$adapter = $this->createCacheAdapter($this->createMock(static::getAdapterClassFqcn()));
$this->expectException(\BadMethodCallException::class);
- $this->expectExceptionMessage(sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "Symfony\\Contracts\\Cache\\CacheInterface" interface.', \get_class($adapter)));
+ $this->expectExceptionMessage(\sprintf('The %s::get() method is not supported because the decorated adapter does not implement the "Symfony\\Contracts\\Cache\\CacheInterface" interface.', \get_class($adapter)));
$adapter->get('foo', static function () {});
}
@@ -197,7 +197,7 @@ public function testDeleteThrowsExceptionIfDecoratedAdapterDoesNotImplementTheCa
$adapter = $this->createCacheAdapter($this->createMock(static::getAdapterClassFqcn()));
$this->expectException(\BadMethodCallException::class);
- $this->expectExceptionMessage(sprintf('The %s::delete() method is not supported because the decorated adapter does not implement the "Symfony\\Contracts\\Cache\\CacheInterface" interface.', \get_class($adapter)));
+ $this->expectExceptionMessage(\sprintf('The %s::delete() method is not supported because the decorated adapter does not implement the "Symfony\\Contracts\\Cache\\CacheInterface" interface.', \get_class($adapter)));
$adapter->delete('foo');
}
diff --git a/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV2Test.php b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV2Test.php
new file mode 100644
index 00000000..29bd7218
--- /dev/null
+++ b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV2Test.php
@@ -0,0 +1,104 @@
+hub = $this->createMock(HubInterface::class);
+ $this->databasePlatform = $this->createMock(AbstractPlatform::class);
+ $this->tracingDriverConnectionFactory = new TracingDriverConnectionFactory($this->hub);
+ }
+
+ /**
+ * @dataProvider createDataProvider
+ *
+ * @param class-string $databasePlatformFqcn
+ */
+ public function testCreate(string $databasePlatformFqcn, string $expectedDatabasePlatform): void
+ {
+ $connection = $this->createMock(Connection::class);
+ $databasePlatform = $this->createMock($databasePlatformFqcn);
+ $driverConnection = $this->tracingDriverConnectionFactory->create($connection, $databasePlatform, []);
+ $expectedDriverConnection = new TracingDriverConnectionForV2V3($this->hub, $connection, $expectedDatabasePlatform, []);
+
+ $this->assertEquals($expectedDriverConnection, $driverConnection);
+ }
+
+ public static function createDataProvider(): \Generator
+ {
+ yield [
+ AbstractMySQLPlatform::class,
+ 'mysql',
+ ];
+
+ yield [
+ DB2Platform::class,
+ 'db2',
+ ];
+
+ yield [
+ OraclePlatform::class,
+ 'oracle',
+ ];
+
+ yield [
+ PostgreSQLPlatform::class,
+ 'postgresql',
+ ];
+
+ yield [
+ SqlitePlatform::class,
+ 'sqlite',
+ ];
+
+ yield [
+ SQLServerPlatform::class,
+ 'mssql',
+ ];
+
+ yield [
+ AbstractPlatform::class,
+ 'other_sql',
+ ];
+ }
+}
diff --git a/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryTest.php b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV3Test.php
similarity index 89%
rename from tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryTest.php
rename to tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV3Test.php
index d3a74639..900dd288 100644
--- a/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryTest.php
+++ b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV3Test.php
@@ -17,10 +17,11 @@
use Sentry\SentryBundle\Tests\Tracing\Doctrine\DBAL\Fixture\ServerInfoAwareConnectionStub;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnection;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionFactory;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionForV2V3;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingServerInfoAwareDriverConnection;
use Sentry\State\HubInterface;
-final class TracingDriverConnectionFactoryTest extends DoctrineTestCase
+final class TracingDriverConnectionFactoryV3Test extends DoctrineTestCase
{
/**
* @var MockObject&HubInterface
@@ -39,8 +40,8 @@ final class TracingDriverConnectionFactoryTest extends DoctrineTestCase
public static function setUpBeforeClass(): void
{
- if (!self::isDoctrineDBALInstalled()) {
- self::markTestSkipped('This test requires the "doctrine/dbal" Composer package.');
+ if (!self::isDoctrineDBALVersion3Installed()) {
+ self::markTestSkipped('This test requires the "doctrine/dbal: ^3.3" Composer package.');
}
}
@@ -61,7 +62,7 @@ public function testCreate(string $databasePlatformFqcn, string $expectedDatabas
$connection = $this->createMock(Connection::class);
$databasePlatform = $this->createMock($databasePlatformFqcn);
$driverConnection = $this->tracingDriverConnectionFactory->create($connection, $databasePlatform, []);
- $expectedDriverConnection = new TracingDriverConnection($this->hub, $connection, $expectedDatabasePlatform, []);
+ $expectedDriverConnection = new TracingDriverConnectionForV2V3($this->hub, $connection, $expectedDatabasePlatform, []);
$this->assertEquals($expectedDriverConnection, $driverConnection);
}
@@ -106,10 +107,6 @@ public static function createDataProvider(): \Generator
public function testCreateWithServerInfoAwareConnection(): void
{
- if (!self::isDoctrineDBALVersion3Installed()) {
- self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.0.');
- }
-
$connection = $this->createMock(ServerInfoAwareConnectionStub::class);
$driverConnection = $this->tracingDriverConnectionFactory->create($connection, $this->databasePlatform, []);
$expectedDriverConnection = new TracingServerInfoAwareDriverConnection(new TracingDriverConnection($this->hub, $connection, 'other_sql', []));
diff --git a/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV4Test.php b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV4Test.php
new file mode 100644
index 00000000..e4357611
--- /dev/null
+++ b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionFactoryV4Test.php
@@ -0,0 +1,104 @@
+hub = $this->createMock(HubInterface::class);
+ $this->databasePlatform = $this->createMock(AbstractPlatform::class);
+ $this->tracingDriverConnectionFactory = new TracingDriverConnectionFactory($this->hub);
+ }
+
+ /**
+ * @dataProvider createDataProvider
+ *
+ * @param class-string $databasePlatformFqcn
+ */
+ public function testCreate(string $databasePlatformFqcn, string $expectedDatabasePlatform): void
+ {
+ $connection = $this->createMock(Connection::class);
+ $databasePlatform = $this->createMock($databasePlatformFqcn);
+ $driverConnection = $this->tracingDriverConnectionFactory->create($connection, $databasePlatform, []);
+ $expectedDriverConnection = new TracingDriverConnectionForV4($this->hub, $connection, $expectedDatabasePlatform, []);
+
+ $this->assertEquals($expectedDriverConnection, $driverConnection);
+ }
+
+ public static function createDataProvider(): \Generator
+ {
+ yield [
+ AbstractMySQLPlatform::class,
+ 'mysql',
+ ];
+
+ yield [
+ DB2Platform::class,
+ 'db2',
+ ];
+
+ yield [
+ OraclePlatform::class,
+ 'oracle',
+ ];
+
+ yield [
+ PostgreSQLPlatform::class,
+ 'postgresql',
+ ];
+
+ yield [
+ SQLitePlatform::class,
+ 'sqlite',
+ ];
+
+ yield [
+ SQLServerPlatform::class,
+ 'mssql',
+ ];
+
+ yield [
+ AbstractPlatform::class,
+ 'other_sql',
+ ];
+ }
+}
diff --git a/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionTest.php b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionForV2V3Test.php
similarity index 94%
rename from tests/Tracing/Doctrine/DBAL/TracingDriverConnectionTest.php
rename to tests/Tracing/Doctrine/DBAL/TracingDriverConnectionForV2V3Test.php
index b8dae823..3ad7ede2 100644
--- a/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionTest.php
+++ b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionForV2V3Test.php
@@ -11,7 +11,7 @@
use PHPUnit\Framework\MockObject\MockObject;
use Sentry\SentryBundle\Tests\DoctrineTestCase;
use Sentry\SentryBundle\Tests\Tracing\Doctrine\DBAL\Fixture\NativeDriverConnectionInterfaceStub;
-use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnection;
+use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionForV4 as TracingDriverConnection;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionInterface;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingStatement;
use Sentry\State\HubInterface;
@@ -21,7 +21,7 @@
/**
* @phpstan-import-type Params from \Doctrine\DBAL\DriverManager as ConnectionParams
*/
-final class TracingDriverConnectionTest extends DoctrineTestCase
+final class TracingDriverConnectionForV2V3Test extends DoctrineTestCase
{
/**
* @var MockObject&HubInterface
@@ -40,7 +40,10 @@ final class TracingDriverConnectionTest extends DoctrineTestCase
public static function setUpBeforeClass(): void
{
- if (!self::isDoctrineBundlePackageInstalled()) {
+ if (
+ !self::isDoctrineDBALVersion2Installed()
+ || !self::isDoctrineDBALVersion3Installed()
+ ) {
self::markTestSkipped();
}
}
@@ -240,8 +243,7 @@ public function testBeginTransaction(array $params, array $expectedData): void
->willReturn($transaction);
$this->decoratedConnection->expects($this->once())
- ->method('beginTransaction')
- ->willReturn(false);
+ ->method('beginTransaction');
$this->assertFalse($connection->beginTransaction());
$this->assertNotNull($transaction->getSpanRecorder());
@@ -262,8 +264,7 @@ public function testBeginTransactionDoesNothingIfNoSpanIsSetOnHub(): void
->willReturn(null);
$this->decoratedConnection->expects($this->once())
- ->method('beginTransaction')
- ->willReturn(false);
+ ->method('beginTransaction');
$this->assertFalse($this->connection->beginTransaction());
}
@@ -287,8 +288,7 @@ public function testCommit(array $params, array $expectedData): void
->willReturn($transaction);
$this->decoratedConnection->expects($this->once())
- ->method('commit')
- ->willReturn(false);
+ ->method('commit');
$this->assertFalse($connection->commit());
$this->assertNotNull($transaction->getSpanRecorder());
@@ -309,8 +309,7 @@ public function testCommitDoesNothingIfNoSpanIsSetOnHub(): void
->willReturn(null);
$this->decoratedConnection->expects($this->once())
- ->method('commit')
- ->willReturn(false);
+ ->method('commit');
$this->assertFalse($this->connection->commit());
}
@@ -334,8 +333,7 @@ public function testRollBack(array $params, array $expectedData): void
->willReturn($transaction);
$this->decoratedConnection->expects($this->once())
- ->method('rollBack')
- ->willReturn(false);
+ ->method('rollBack');
$this->assertFalse($connection->rollBack());
$this->assertNotNull($transaction->getSpanRecorder());
@@ -356,8 +354,7 @@ public function testRollBackDoesNothingIfNoSpanIsSetOnHub(): void
->willReturn(null);
$this->decoratedConnection->expects($this->once())
- ->method('rollBack')
- ->willReturn(false);
+ ->method('rollBack');
$this->assertFalse($this->connection->rollBack());
}
@@ -378,7 +375,7 @@ public function testErrorCode(): void
public function testErrorCodeThrowsExceptionIfDecoratedConnectionDoesNotImplementMethod(): void
{
if (!self::isDoctrineDBALVersion3Installed()) {
- self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.0.');
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
}
$this->expectException(\BadMethodCallException::class);
@@ -403,7 +400,7 @@ public function testErrorInfo(): void
public function testErrorInfoThrowsExceptionIfDecoratedConnectionDoesNotImplementMethod(): void
{
if (!self::isDoctrineDBALVersion3Installed()) {
- self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.0.');
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
}
$this->expectException(\BadMethodCallException::class);
@@ -421,7 +418,7 @@ public function testGetWrappedConnection(): void
public function testGetNativeConnection(): void
{
- $nativeConnection = new class() {
+ $nativeConnection = new class {
};
$decoratedConnection = $this->createMock(NativeDriverConnectionInterfaceStub::class);
@@ -466,8 +463,8 @@ public function spanDataDataProvider(): \Generator
'db.system' => 'foo_platform',
'db.user' => 'root',
'db.name' => 'INFORMATION_SCHEMA',
- 'net.peer.port' => '3306',
- 'net.transport' => 'Unix',
+ 'server.port' => '3306',
+ 'server.socket.address' => 'Unix',
],
];
@@ -482,8 +479,8 @@ public function spanDataDataProvider(): \Generator
'db.system' => 'foo_platform',
'db.user' => 'root',
'db.name' => 'INFORMATION_SCHEMA',
- 'net.peer.port' => '3306',
- 'net.transport' => 'inproc',
+ 'server.port' => '3306',
+ 'server.socket.address' => 'inproc',
],
];
@@ -493,7 +490,7 @@ public function spanDataDataProvider(): \Generator
],
[
'db.system' => 'foo_platform',
- 'net.peer.name' => 'localhost',
+ 'server.address' => 'localhost',
],
];
@@ -503,7 +500,7 @@ public function spanDataDataProvider(): \Generator
],
[
'db.system' => 'foo_platform',
- 'net.peer.ip' => '127.0.0.1',
+ 'server.address' => '127.0.0.1',
],
];
}
diff --git a/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4Test.php b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4Test.php
new file mode 100644
index 00000000..77490bb3
--- /dev/null
+++ b/tests/Tracing/Doctrine/DBAL/TracingDriverConnectionForV4Test.php
@@ -0,0 +1,490 @@
+hub = $this->createMock(HubInterface::class);
+ $this->decoratedConnection = $this->createMock(DriverConnectionInterface::class);
+ $this->connection = new TracingDriverConnection($this->hub, $this->decoratedConnection, 'foo_platform', []);
+ }
+
+ /**
+ * @dataProvider spanDataDataProvider
+ *
+ * @param array $params
+ * @param array $expectedData
+ *
+ * @phpstan-param ConnectionParams $params
+ */
+ public function testPrepare(array $params, array $expectedData): void
+ {
+ $sql = 'SELECT 1 + 1';
+ $statement = $this->createMock(DriverStatementInterface::class);
+ $resultStatement = new TracingStatement($this->hub, $statement, $sql, $expectedData);
+ $connection = new TracingDriverConnection($this->hub, $this->decoratedConnection, 'foo_platform', $params);
+
+ $transaction = new Transaction(new TransactionContext(), $this->hub);
+ $transaction->initSpanRecorder();
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn($transaction);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('prepare')
+ ->with($sql)
+ ->willReturn($statement);
+
+ $this->assertEquals($resultStatement, $connection->prepare($sql));
+ $this->assertNotNull($transaction->getSpanRecorder());
+
+ $spans = $transaction->getSpanRecorder()->getSpans();
+
+ $this->assertCount(2, $spans);
+ $this->assertSame(TracingDriverConnection::SPAN_OP_CONN_PREPARE, $spans[1]->getOp());
+ $this->assertSame($sql, $spans[1]->getDescription());
+ $this->assertSame($expectedData, $spans[1]->getData());
+ $this->assertNotNull($spans[1]->getEndTimestamp());
+ }
+
+ public function testPrepareDoesNothingIfNoSpanIsSetOnHub(): void
+ {
+ $sql = 'SELECT 1 + 1';
+ $statement = $this->createMock(DriverStatementInterface::class);
+ $resultStatement = new TracingStatement($this->hub, $statement, $sql, ['db.system' => 'foo_platform']);
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn(null);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('prepare')
+ ->with($sql)
+ ->willReturn($statement);
+
+ $this->assertEquals($resultStatement, $this->connection->prepare($sql));
+ }
+
+ /**
+ * @dataProvider spanDataDataProvider
+ *
+ * @param array $params
+ * @param array $expectedData
+ *
+ * @phpstan-param ConnectionParams $params
+ */
+ public function testQuery(array $params, array $expectedData): void
+ {
+ $result = $this->createMock(DriverResultInterface::class);
+ $connection = new TracingDriverConnection($this->hub, $this->decoratedConnection, 'foo_platform', $params);
+ $sql = 'SELECT 1 + 1';
+
+ $transaction = new Transaction(new TransactionContext(), $this->hub);
+ $transaction->initSpanRecorder();
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn($transaction);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('query')
+ ->with($sql)
+ ->willReturn($result);
+
+ $this->assertSame($result, $connection->query($sql));
+ $this->assertNotNull($transaction->getSpanRecorder());
+
+ $spans = $transaction->getSpanRecorder()->getSpans();
+
+ $this->assertCount(2, $spans);
+ $this->assertSame(TracingDriverConnection::SPAN_OP_CONN_QUERY, $spans[1]->getOp());
+ $this->assertSame($sql, $spans[1]->getDescription());
+ $this->assertSame($expectedData, $spans[1]->getData());
+ $this->assertNotNull($spans[1]->getEndTimestamp());
+ }
+
+ public function testQueryDoesNothingIfNoSpanIsSetOnHub(): void
+ {
+ $result = $this->createMock(DriverResultInterface::class);
+ $sql = 'SELECT 1 + 1';
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn(null);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('query')
+ ->with($sql)
+ ->willReturn($result);
+
+ $this->assertSame($result, $this->connection->query($sql));
+ }
+
+ public function testQuote(): void
+ {
+ $this->decoratedConnection->expects($this->once())
+ ->method('quote')
+ ->with('foo')
+ ->willReturn('foo');
+
+ $this->assertSame('foo', $this->connection->quote('foo'));
+ }
+
+ /**
+ * @dataProvider spanDataDataProvider
+ *
+ * @param array $params
+ * @param array $expectedData
+ *
+ * @phpstan-param ConnectionParams $params
+ */
+ public function testExec(array $params, array $expectedData): void
+ {
+ $connection = new TracingDriverConnection($this->hub, $this->decoratedConnection, 'foo_platform', $params);
+ $sql = 'SELECT 1 + 1';
+
+ $transaction = new Transaction(new TransactionContext(), $this->hub);
+ $transaction->initSpanRecorder();
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn($transaction);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('exec')
+ ->with($sql)
+ ->willReturn(10);
+
+ $this->assertSame(10, $connection->exec($sql));
+ $this->assertNotNull($transaction->getSpanRecorder());
+
+ $spans = $transaction->getSpanRecorder()->getSpans();
+
+ $this->assertCount(2, $spans);
+ $this->assertSame(TracingDriverConnection::SPAN_OP_CONN_EXEC, $spans[1]->getOp());
+ $this->assertSame($sql, $spans[1]->getDescription());
+ $this->assertSame($expectedData, $spans[1]->getData());
+ $this->assertNotNull($spans[1]->getEndTimestamp());
+ }
+
+ public function testLastInsertId(): void
+ {
+ $this->decoratedConnection->expects($this->once())
+ ->method('lastInsertId')
+ ->willReturn('10');
+
+ $this->assertSame('10', $this->connection->lastInsertId());
+ }
+
+ /**
+ * @dataProvider spanDataDataProvider
+ *
+ * @param array $params
+ * @param array $expectedData
+ *
+ * @phpstan-param ConnectionParams $params
+ */
+ public function testBeginTransaction(array $params, array $expectedData): void
+ {
+ $connection = new TracingDriverConnection($this->hub, $this->decoratedConnection, 'foo_platform', $params);
+ $transaction = new Transaction(new TransactionContext(), $this->hub);
+ $transaction->initSpanRecorder();
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn($transaction);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('beginTransaction');
+
+ $connection->beginTransaction();
+ $this->assertNotNull($transaction->getSpanRecorder());
+
+ $spans = $transaction->getSpanRecorder()->getSpans();
+
+ $this->assertCount(2, $spans);
+ $this->assertSame(TracingDriverConnection::SPAN_OP_CONN_BEGIN_TRANSACTION, $spans[1]->getOp());
+ $this->assertSame('BEGIN TRANSACTION', $spans[1]->getDescription());
+ $this->assertSame($expectedData, $spans[1]->getData());
+ $this->assertNotNull($spans[1]->getEndTimestamp());
+ }
+
+ public function testBeginTransactionDoesNothingIfNoSpanIsSetOnHub(): void
+ {
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn(null);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('beginTransaction');
+
+ $this->connection->beginTransaction();
+ }
+
+ /**
+ * @dataProvider spanDataDataProvider
+ *
+ * @param array $params
+ * @param array $expectedData
+ *
+ * @phpstan-param ConnectionParams $params
+ */
+ public function testCommit(array $params, array $expectedData): void
+ {
+ $connection = new TracingDriverConnection($this->hub, $this->decoratedConnection, 'foo_platform', $params);
+ $transaction = new Transaction(new TransactionContext(), $this->hub);
+ $transaction->initSpanRecorder();
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn($transaction);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('commit');
+
+ $connection->commit();
+ $this->assertNotNull($transaction->getSpanRecorder());
+
+ $spans = $transaction->getSpanRecorder()->getSpans();
+
+ $this->assertCount(2, $spans);
+ $this->assertSame(TracingDriverConnection::SPAN_OP_TRANSACTION_COMMIT, $spans[1]->getOp());
+ $this->assertSame('COMMIT', $spans[1]->getDescription());
+ $this->assertSame($expectedData, $spans[1]->getData());
+ $this->assertNotNull($spans[1]->getEndTimestamp());
+ }
+
+ public function testCommitDoesNothingIfNoSpanIsSetOnHub(): void
+ {
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn(null);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('commit');
+
+ $this->connection->commit();
+ }
+
+ /**
+ * @dataProvider spanDataDataProvider
+ *
+ * @param array $params
+ * @param array $expectedData
+ *
+ * @phpstan-param ConnectionParams $params
+ */
+ public function testRollBack(array $params, array $expectedData): void
+ {
+ $connection = new TracingDriverConnection($this->hub, $this->decoratedConnection, 'foo_platform', $params);
+ $transaction = new Transaction(new TransactionContext(), $this->hub);
+ $transaction->initSpanRecorder();
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn($transaction);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('rollBack');
+
+ $connection->rollBack();
+ $this->assertNotNull($transaction->getSpanRecorder());
+
+ $spans = $transaction->getSpanRecorder()->getSpans();
+
+ $this->assertCount(2, $spans);
+ $this->assertSame(TracingDriverConnection::SPAN_OP_TRANSACTION_ROLLBACK, $spans[1]->getOp());
+ $this->assertSame('ROLLBACK', $spans[1]->getDescription());
+ $this->assertSame($expectedData, $spans[1]->getData());
+ $this->assertNotNull($spans[1]->getEndTimestamp());
+ }
+
+ public function testRollBackDoesNothingIfNoSpanIsSetOnHub(): void
+ {
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn(null);
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('rollBack');
+
+ $this->connection->rollBack();
+ }
+
+ public function testErrorCode(): void
+ {
+ if (!self::isDoctrineDBALVersion2Installed()) {
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be ^2.13.');
+ }
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('errorCode')
+ ->willReturn('1002');
+
+ $this->assertSame('1002', $this->connection->errorCode());
+ }
+
+ public function testErrorCodeThrowsExceptionIfDecoratedConnectionDoesNotImplementMethod(): void
+ {
+ if (!self::isDoctrineDBALVersion3Installed()) {
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
+ }
+
+ $this->expectException(\BadMethodCallException::class);
+ $this->expectExceptionMessage('The Sentry\\SentryBundle\\Tracing\\Doctrine\\DBAL\\TracingDriverConnection::errorCode() method is not supported on Doctrine DBAL 3.0.');
+
+ $this->connection->errorCode();
+ }
+
+ public function testErrorInfo(): void
+ {
+ if (!self::isDoctrineDBALVersion2Installed()) {
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be ^2.13.');
+ }
+
+ $this->decoratedConnection->expects($this->once())
+ ->method('errorInfo')
+ ->willReturn(['foobar']);
+
+ $this->assertSame(['foobar'], $this->connection->errorInfo());
+ }
+
+ public function testErrorInfoThrowsExceptionIfDecoratedConnectionDoesNotImplementMethod(): void
+ {
+ if (!self::isDoctrineDBALVersion3Installed()) {
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
+ }
+
+ $this->expectException(\BadMethodCallException::class);
+ $this->expectExceptionMessage('The Sentry\\SentryBundle\\Tracing\\Doctrine\\DBAL\\TracingDriverConnection::errorInfo() method is not supported on Doctrine DBAL 3.0.');
+
+ $this->connection->errorInfo();
+ }
+
+ public function testGetWrappedConnection(): void
+ {
+ $connection = new TracingDriverConnection($this->hub, $this->decoratedConnection, 'foo_platform', []);
+
+ $this->assertSame($this->decoratedConnection, $connection->getWrappedConnection());
+ }
+
+ public function testGetNativeConnection(): void
+ {
+ $nativeConnection = new class {
+ };
+
+ $decoratedConnection = $this->createMock(NativeDriverConnectionInterfaceStub::class);
+ $decoratedConnection->expects($this->once())
+ ->method('getNativeConnection')
+ ->willReturn($nativeConnection);
+
+ $connection = new TracingDriverConnection($this->hub, $decoratedConnection, 'foo_platform', []);
+
+ $this->assertSame($nativeConnection, $connection->getNativeConnection());
+ }
+
+ /**
+ * @return \Generator
+ */
+ public function spanDataDataProvider(): \Generator
+ {
+ yield [
+ [],
+ ['db.system' => 'foo_platform'],
+ ];
+
+ yield [
+ [
+ 'user' => 'root',
+ 'dbname' => 'INFORMATION_SCHEMA',
+ 'port' => 3306,
+ 'unix_socket' => '/var/run/mysqld/mysqld.sock',
+ ],
+ [
+ 'db.system' => 'foo_platform',
+ 'db.user' => 'root',
+ 'db.name' => 'INFORMATION_SCHEMA',
+ 'server.port' => '3306',
+ 'server.socket.address' => 'Unix',
+ ],
+ ];
+
+ yield [
+ [
+ 'user' => 'root',
+ 'dbname' => 'INFORMATION_SCHEMA',
+ 'port' => 3306,
+ 'memory' => true,
+ ],
+ [
+ 'db.system' => 'foo_platform',
+ 'db.user' => 'root',
+ 'db.name' => 'INFORMATION_SCHEMA',
+ 'server.port' => '3306',
+ 'server.socket.address' => 'inproc',
+ ],
+ ];
+
+ yield [
+ [
+ 'host' => 'localhost',
+ ],
+ [
+ 'db.system' => 'foo_platform',
+ 'server.address' => 'localhost',
+ ],
+ ];
+
+ yield [
+ [
+ 'host' => '127.0.0.1',
+ ],
+ [
+ 'db.system' => 'foo_platform',
+ 'server.address' => '127.0.0.1',
+ ],
+ ];
+ }
+}
diff --git a/tests/Tracing/Doctrine/DBAL/TracingDriverForV2Test.php b/tests/Tracing/Doctrine/DBAL/TracingDriverForV2Test.php
index efaac777..0f7ba7c3 100644
--- a/tests/Tracing/Doctrine/DBAL/TracingDriverForV2Test.php
+++ b/tests/Tracing/Doctrine/DBAL/TracingDriverForV2Test.php
@@ -179,7 +179,7 @@ public function testConvertExceptionWhenDriverDoesNotImplementInterface(): void
}
}
-if (interface_exists(Driver::class)) {
+if (interface_exists(Driver::class) && interface_exists(VersionAwarePlatformDriver::class)) {
interface StubVersionAwarePlatformDriver extends Driver, VersionAwarePlatformDriver
{
}
diff --git a/tests/Tracing/Doctrine/DBAL/TracingDriverForV3Test.php b/tests/Tracing/Doctrine/DBAL/TracingDriverForV3Test.php
index af3a857b..06d7f13b 100644
--- a/tests/Tracing/Doctrine/DBAL/TracingDriverForV3Test.php
+++ b/tests/Tracing/Doctrine/DBAL/TracingDriverForV3Test.php
@@ -4,13 +4,9 @@
namespace Sentry\SentryBundle\Tests\Tracing\Doctrine\DBAL;
-use Doctrine\DBAL\Connection;
-use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\Connection as DriverConnectionInterface;
use Doctrine\DBAL\Platforms\AbstractPlatform;
-use Doctrine\DBAL\Schema\AbstractSchemaManager;
-use Doctrine\DBAL\VersionAwarePlatformDriver as VersionAwarePlatformDriverInterface;
use PHPUnit\Framework\MockObject\MockObject;
use Sentry\SentryBundle\Tests\DoctrineTestCase;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionFactoryInterface;
@@ -27,7 +23,7 @@ final class TracingDriverForV3Test extends DoctrineTestCase
public static function setUpBeforeClass(): void
{
if (!self::isDoctrineDBALVersion3Installed()) {
- self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.0.');
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
}
}
@@ -62,78 +58,4 @@ public function testConnect(): void
$this->assertSame($tracingDriverConnection, $driver->connect($params));
}
-
- public function testGetDatabasePlatform(): void
- {
- $databasePlatform = $this->createMock(AbstractPlatform::class);
-
- $decoratedDriver = $this->createMock(DriverInterface::class);
- $decoratedDriver->expects($this->once())
- ->method('getDatabasePlatform')
- ->willReturn($databasePlatform);
-
- $driver = new TracingDriverForV3($this->connectionFactory, $decoratedDriver);
-
- $this->assertSame($databasePlatform, $driver->getDatabasePlatform());
- }
-
- public function testGetSchemaManager(): void
- {
- $connection = $this->createMock(Connection::class);
- $databasePlatform = $this->createMock(AbstractPlatform::class);
- $schemaManager = $this->createMock(AbstractSchemaManager::class);
-
- $decoratedDriver = $this->createMock(DriverInterface::class);
- $decoratedDriver->expects($this->once())
- ->method('getSchemaManager')
- ->with($connection, $databasePlatform)
- ->willReturn($schemaManager);
-
- $driver = new TracingDriverForV3($this->connectionFactory, $decoratedDriver);
-
- $this->assertSame($schemaManager, $driver->getSchemaManager($connection, $databasePlatform));
- }
-
- public function testGetExceptionConverter(): void
- {
- $exceptionConverter = $this->createMock(ExceptionConverter::class);
-
- $decoratedDriver = $this->createMock(DriverInterface::class);
- $decoratedDriver->expects($this->once())
- ->method('getExceptionConverter')
- ->willReturn($exceptionConverter);
-
- $driver = new TracingDriverForV3($this->connectionFactory, $decoratedDriver);
-
- $this->assertSame($exceptionConverter, $driver->getExceptionConverter());
- }
-
- public function testCreateDatabasePlatformForVersion(): void
- {
- $databasePlatform = $this->createMock(AbstractPlatform::class);
-
- $decoratedDriver = $this->createMock(VersionAwarePlatformDriverInterface::class);
- $decoratedDriver->expects($this->once())
- ->method('createDatabasePlatformForVersion')
- ->with('5.7')
- ->willReturn($databasePlatform);
-
- $driver = new TracingDriverForV3($this->connectionFactory, $decoratedDriver);
-
- $this->assertSame($databasePlatform, $driver->createDatabasePlatformForVersion('5.7'));
- }
-
- public function testCreateDatabasePlatformForVersionWhenDriverDoesNotImplementInterface(): void
- {
- $databasePlatform = $this->createMock(AbstractPlatform::class);
-
- $decoratedDriver = $this->createMock(DriverInterface::class);
- $decoratedDriver->expects($this->once())
- ->method('getDatabasePlatform')
- ->willReturn($databasePlatform);
-
- $driver = new TracingDriverForV3($this->connectionFactory, $decoratedDriver);
-
- $this->assertSame($databasePlatform, $driver->createDatabasePlatformForVersion('5.7'));
- }
}
diff --git a/tests/Tracing/Doctrine/DBAL/TracingDriverForV4Test.php b/tests/Tracing/Doctrine/DBAL/TracingDriverForV4Test.php
new file mode 100644
index 00000000..85d872d0
--- /dev/null
+++ b/tests/Tracing/Doctrine/DBAL/TracingDriverForV4Test.php
@@ -0,0 +1,61 @@
+= 4.0.');
+ }
+ }
+
+ protected function setUp(): void
+ {
+ $this->connectionFactory = $this->createMock(TracingDriverConnectionFactoryInterface::class);
+ }
+
+ public function testConnect(): void
+ {
+ $params = ['host' => 'localhost'];
+ $databasePlatform = $this->createMock(AbstractPlatform::class);
+ $driverConnection = $this->createMock(DriverConnectionInterface::class);
+ $tracingDriverConnection = $this->createMock(TracingDriverConnectionInterface::class);
+ $decoratedDriver = $this->createMock(DriverInterface::class);
+
+ $decoratedDriver->expects($this->once())
+ ->method('connect')
+ ->with($params)
+ ->willReturn($driverConnection);
+
+ $decoratedDriver->expects($this->once())
+ ->method('getDatabasePlatform')
+ ->willReturn($databasePlatform);
+
+ $this->connectionFactory->expects($this->once())
+ ->method('create')
+ ->with($driverConnection, $databasePlatform, $params)
+ ->willReturn($tracingDriverConnection);
+
+ $driver = new TracingDriverForV4($this->connectionFactory, $decoratedDriver);
+
+ $this->assertSame($tracingDriverConnection, $driver->connect($params));
+ }
+}
diff --git a/tests/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnectionTest.php b/tests/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnectionTest.php
index 29bcbb78..13342222 100644
--- a/tests/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnectionTest.php
+++ b/tests/Tracing/Doctrine/DBAL/TracingServerInfoAwareDriverConnectionTest.php
@@ -29,7 +29,10 @@ final class TracingServerInfoAwareDriverConnectionTest extends DoctrineTestCase
public static function setUpBeforeClass(): void
{
- if (!self::isDoctrineBundlePackageInstalled()) {
+ if (
+ !self::isDoctrineDBALVersion2Installed()
+ && !self::isDoctrineDBALVersion3Installed()
+ ) {
self::markTestSkipped();
}
}
@@ -185,7 +188,7 @@ public function testRequiresQueryForServerVersionThrowsExceptionIfWrappedConnect
public function testRequiresQueryForServerVersionThrowsExceptionIfWrappedConnectionDoesNotImplementMethod(): void
{
if (!self::isDoctrineDBALVersion3Installed()) {
- self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.0.');
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
}
$this->decoratedConnection->expects($this->once())
@@ -214,7 +217,7 @@ public function testErrorCode(): void
public function testErrorCodeThrowsExceptionIfDecoratedConnectionDoesNotImplementMethod(): void
{
if (!self::isDoctrineDBALVersion3Installed()) {
- self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.0.');
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
}
$this->expectException(\BadMethodCallException::class);
@@ -239,7 +242,7 @@ public function testErrorInfo(): void
public function testErrorInfoThrowsExceptionIfDecoratedConnectionDoesNotImplementMethod(): void
{
if (!self::isDoctrineDBALVersion3Installed()) {
- self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.0.');
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
}
$this->expectException(\BadMethodCallException::class);
@@ -261,7 +264,7 @@ public function testGetWrappedConnection(): void
public function testGetNativeConnection(): void
{
- $nativeConnection = new class() {
+ $nativeConnection = new class {
};
$decoratedConnection = $this->createMock(NativeDriverConnectionInterfaceStub::class);
diff --git a/tests/Tracing/Doctrine/DBAL/TracingStatementForV3Test.php b/tests/Tracing/Doctrine/DBAL/TracingStatementForV3Test.php
index 11924259..722b1c27 100644
--- a/tests/Tracing/Doctrine/DBAL/TracingStatementForV3Test.php
+++ b/tests/Tracing/Doctrine/DBAL/TracingStatementForV3Test.php
@@ -34,7 +34,7 @@ final class TracingStatementForV3Test extends DoctrineTestCase
public static function setUpBeforeClass(): void
{
if (!self::isDoctrineDBALVersion3Installed()) {
- self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.0.');
+ self::markTestSkipped('This test requires the version of the "doctrine/dbal" Composer package to be >= 3.3.');
}
}
@@ -70,7 +70,7 @@ public function testBindParam(): void
public function testBindParamForwardsLengthParamOnlyWhenExplicitlySet(): void
{
$variable = 'bar';
- $decoratedStatement = new class() implements Statement {
+ $decoratedStatement = new class implements Statement {
/**
* @var int
*/
diff --git a/tests/Tracing/Doctrine/DBAL/TracingStatementForV4Test.php b/tests/Tracing/Doctrine/DBAL/TracingStatementForV4Test.php
new file mode 100644
index 00000000..476e7938
--- /dev/null
+++ b/tests/Tracing/Doctrine/DBAL/TracingStatementForV4Test.php
@@ -0,0 +1,97 @@
+= 4.0.');
+ }
+ }
+
+ protected function setUp(): void
+ {
+ $this->hub = $this->createMock(HubInterface::class);
+ $this->decoratedStatement = $this->createMock(Statement::class);
+ $this->statement = new TracingStatementForV4($this->hub, $this->decoratedStatement, 'SELECT 1', ['db.system' => 'sqlite']);
+ }
+
+ public function testBindValue(): void
+ {
+ $this->decoratedStatement->expects($this->once())
+ ->method('bindValue')
+ ->with('foo', 'bar', ParameterType::INTEGER);
+
+ $this->statement->bindValue('foo', 'bar', ParameterType::INTEGER);
+ }
+
+ public function testExecute(): void
+ {
+ $driverResult = $this->createMock(Result::class);
+ $transaction = new Transaction(new TransactionContext(), $this->hub);
+ $transaction->initSpanRecorder();
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn($transaction);
+
+ $this->decoratedStatement->expects($this->once())
+ ->method('execute')
+ ->willReturn($driverResult);
+
+ $this->assertSame($driverResult, $this->statement->execute());
+ $this->assertNotNull($transaction->getSpanRecorder());
+
+ $spans = $transaction->getSpanRecorder()->getSpans();
+
+ $this->assertCount(2, $spans);
+ $this->assertSame(TracingStatementForV4::SPAN_OP_STMT_EXECUTE, $spans[1]->getOp());
+ $this->assertSame('SELECT 1', $spans[1]->getDescription());
+ $this->assertSame(['db.system' => 'sqlite'], $spans[1]->getData());
+ $this->assertNotNull($spans[1]->getEndTimestamp());
+ }
+
+ public function testExecuteDoesNothingIfNoSpanIsSetOnHub(): void
+ {
+ $driverResult = $this->createMock(Result::class);
+
+ $this->hub->expects($this->once())
+ ->method('getSpan')
+ ->willReturn(null);
+
+ $this->decoratedStatement->expects($this->once())
+ ->method('execute')
+ ->willReturn($driverResult);
+
+ $this->assertSame($driverResult, $this->statement->execute());
+ }
+}
diff --git a/tests/Tracing/HttpClient/TraceableHttpClientTest.php b/tests/Tracing/HttpClient/TraceableHttpClientTest.php
index 5778a31f..4306378e 100644
--- a/tests/Tracing/HttpClient/TraceableHttpClientTest.php
+++ b/tests/Tracing/HttpClient/TraceableHttpClientTest.php
@@ -8,7 +8,6 @@
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\NullLogger;
-use Sentry\Client;
use Sentry\ClientInterface;
use Sentry\Options;
use Sentry\SentryBundle\Tracing\HttpClient\AbstractTraceableResponse;
@@ -23,7 +22,7 @@
use Sentry\Tracing\TraceId;
use Sentry\Tracing\Transaction;
use Sentry\Tracing\TransactionContext;
-use Sentry\Transport\NullTransport;
+use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -48,7 +47,7 @@ final class TraceableHttpClientTest extends TestCase
public static function setUpBeforeClass(): void
{
- if (!self::isHttpClientPackageInstalled()) {
+ if (!class_exists(HttpClient::class)) {
self::markTestSkipped('This test requires the "symfony/http-client" Composer package to be installed.');
}
}
@@ -66,8 +65,7 @@ public function testRequest(): void
'dsn' => 'http://public:secret@example.com/sentry/1',
]);
$client = $this->createMock(ClientInterface::class);
- $client
- ->expects($this->once())
+ $client->expects($this->once())
->method('getOptions')
->willReturn($options);
@@ -94,16 +92,15 @@ public function testRequest(): void
$this->assertSame(200, $response->getStatusCode());
$this->assertSame('GET', $response->getInfo('http_method'));
$this->assertSame('https://username:password@www.example.com/test-page?foo=bar#baz', $response->getInfo('url'));
- $this->assertSame(['sentry-trace: ' . $spans[1]->toTraceparent()], $mockResponse->getRequestOptions()['normalized_headers']['sentry-trace']);
- $this->assertSame(['baggage: ' . $transaction->toBaggage()], $mockResponse->getRequestOptions()['normalized_headers']['baggage']);
+ $this->assertSame([\sprintf('sentry-trace: %s', $spans[1]->toTraceparent())], $mockResponse->getRequestOptions()['normalized_headers']['sentry-trace']);
+ $this->assertSame([\sprintf('traceparent: %s', $spans[1]->toW3CTraceparent())], $mockResponse->getRequestOptions()['normalized_headers']['traceparent']);
+ $this->assertSame([\sprintf('baggage: %s', $transaction->toBaggage())], $mockResponse->getRequestOptions()['normalized_headers']['baggage']);
$this->assertNotNull($transaction->getSpanRecorder());
$spans = $transaction->getSpanRecorder()->getSpans();
- $expectedTags = [
- 'http.method' => 'GET',
- 'http.url' => 'https://www.example.com/test-page',
- ];
$expectedData = [
+ 'http.url' => 'https://www.example.com/test-page',
+ 'http.request.method' => 'GET',
'http.query' => 'foo=bar',
'http.fragment' => 'baz',
];
@@ -119,7 +116,6 @@ public function testRequest(): void
$this->assertSame('http.client', $spans[1]->getOp());
$this->assertSame('GET https://www.example.com/test-page', $spans[1]->getDescription());
$this->assertSame(SpanStatus::ok(), $spans[1]->getStatus());
- $this->assertSame($expectedTags, $spans[1]->getTags());
$this->assertSame($expectedData, $spans[1]->getData());
}
@@ -127,7 +123,7 @@ public function testRequestDoesNotContainTracingHeaders(): void
{
$options = new Options([
'dsn' => 'http://public:secret@example.com/sentry/1',
- 'trace_propagation_targets' => null,
+ 'trace_propagation_targets' => [],
]);
$client = $this->createMock(ClientInterface::class);
$client->expects($this->once())
@@ -154,30 +150,35 @@ public function testRequestDoesNotContainTracingHeaders(): void
$this->assertSame('PUT', $response->getInfo('http_method'));
$this->assertSame('https://www.example.com/test-page', $response->getInfo('url'));
$this->assertArrayNotHasKey('sentry-trace', $mockResponse->getRequestOptions()['normalized_headers']);
+ $this->assertArrayNotHasKey('traceparent', $mockResponse->getRequestOptions()['normalized_headers']);
$this->assertArrayNotHasKey('baggage', $mockResponse->getRequestOptions()['normalized_headers']);
$this->assertNotNull($transaction->getSpanRecorder());
$spans = $transaction->getSpanRecorder()->getSpans();
- $expectedTags = [
- 'http.method' => 'PUT',
+ $expectedData = [
'http.url' => 'https://www.example.com/test-page',
+ 'http.request.method' => 'PUT',
];
$this->assertCount(2, $spans);
$this->assertNull($spans[1]->getEndTimestamp());
$this->assertSame('http.client', $spans[1]->getOp());
$this->assertSame('PUT https://www.example.com/test-page', $spans[1]->getDescription());
- $this->assertSame($expectedTags, $spans[1]->getTags());
+ $this->assertSame($expectedData, $spans[1]->getData());
}
public function testRequestDoesContainsTracingHeadersWithoutTransaction(): void
{
- $client = new Client(new Options([
+ $options = new Options([
'dsn' => 'http://public:secret@example.com/sentry/1',
'release' => '1.0.0',
'environment' => 'test',
'trace_propagation_targets' => ['www.example.com'],
- ]), new NullTransport());
+ ]);
+ $client = $this->createMock(ClientInterface::class);
+ $client->expects($this->exactly(5))
+ ->method('getOptions')
+ ->willReturn($options);
$propagationContext = PropagationContext::fromDefaults();
$propagationContext->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'));
@@ -198,25 +199,29 @@ public function testRequestDoesContainsTracingHeadersWithoutTransaction(): void
$this->assertSame(200, $response->getStatusCode());
$this->assertSame('POST', $response->getInfo('http_method'));
$this->assertSame('https://www.example.com/test-page', $response->getInfo('url'));
- $this->assertSame(['sentry-trace: 566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8'], $mockResponse->getRequestOptions()['normalized_headers']['sentry-trace']);
- $this->assertSame(['baggage: sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-public_key=public,sentry-release=1.0.0,sentry-environment=test'], $mockResponse->getRequestOptions()['normalized_headers']['baggage']);
+ $this->assertSame([\sprintf('sentry-trace: %s', $propagationContext->toTraceparent())], $mockResponse->getRequestOptions()['normalized_headers']['sentry-trace']);
+ $this->assertSame([\sprintf('traceparent: %s', $propagationContext->toW3CTraceparent())], $mockResponse->getRequestOptions()['normalized_headers']['traceparent']);
+ $this->assertSame([\sprintf('baggage: %s', $propagationContext->toBaggage())], $mockResponse->getRequestOptions()['normalized_headers']['baggage']);
}
public function testRequestSetsUnknownErrorAsSpanStatusIfResponseStatusCodeIsUnavailable(): void
{
+ $options = new Options([
+ 'dsn' => 'http://public:secret@example.com/sentry/1',
+ ]);
$client = $this->createMock(ClientInterface::class);
- $client->expects($this->once())
+ $client->expects($this->exactly(2))
->method('getOptions')
- ->willReturn(new Options(['dsn' => 'http://public:secret@example.com/sentry/1']));
+ ->willReturn($options);
- $transaction = new Transaction(new TransactionContext());
+ $transaction = new Transaction(new TransactionContext(), $this->hub);
$transaction->initSpanRecorder();
$this->hub->expects($this->once())
->method('getSpan')
->willReturn($transaction);
- $this->hub->expects($this->once())
+ $this->hub->expects($this->exactly(2))
->method('getClient')
->willReturn($client);
@@ -269,9 +274,9 @@ public function testStream(): void
$this->assertNotNull($transaction->getSpanRecorder());
$spans = $transaction->getSpanRecorder()->getSpans();
- $expectedTags = [
- 'http.method' => 'GET',
+ $expectedData = [
'http.url' => 'https://www.example.com/test-page',
+ 'http.request.method' => 'GET',
];
$this->assertSame('foobar', implode('', $chunks));
@@ -280,7 +285,7 @@ public function testStream(): void
$this->assertNotNull($spans[1]->getEndTimestamp());
$this->assertSame('http.client', $spans[1]->getOp());
$this->assertSame('GET https://www.example.com/test-page', $spans[1]->getDescription());
- $this->assertSame($expectedTags, $spans[1]->getTags());
+ $this->assertSame($expectedData, $spans[1]->getData());
$loopIndex = 0;
@@ -356,13 +361,10 @@ public function testWithOptions(): void
$this->assertSame('GET', $response->getInfo('http_method'));
$this->assertSame('https://www.example.org/test-page', $response->getInfo('url'));
}
+}
- private static function isHttpClientPackageInstalled(): bool
+if (interface_exists(HttpClientInterface::class)) {
+ interface TestableHttpClientInterface extends HttpClientInterface, LoggerAwareInterface, ResetInterface
{
- return interface_exists(HttpClientInterface::class);
}
}
-
-interface TestableHttpClientInterface extends HttpClientInterface, LoggerAwareInterface, ResetInterface
-{
-}
diff --git a/tests/Tracing/HttpClient/TraceableResponseTest.php b/tests/Tracing/HttpClient/TraceableResponseTest.php
index e67616d5..5a4bb370 100644
--- a/tests/Tracing/HttpClient/TraceableResponseTest.php
+++ b/tests/Tracing/HttpClient/TraceableResponseTest.php
@@ -12,6 +12,7 @@
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\Transaction;
use Sentry\Tracing\TransactionContext;
+use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -28,6 +29,13 @@ final class TraceableResponseTest extends TestCase
*/
private $hub;
+ public static function setUpBeforeClass(): void
+ {
+ if (!class_exists(HttpClient::class)) {
+ self::markTestSkipped('This test requires the "symfony/http-client" Composer package to be installed.');
+ }
+ }
+
protected function setUp(): void
{
$this->client = $this->createMock(HttpClientInterface::class);
@@ -47,7 +55,7 @@ public function testInstanceCannotBeUnserialized(): void
$this->expectException(\BadMethodCallException::class);
$this->expectExceptionMessage('Unserializing instances of this class is forbidden.');
- unserialize(sprintf('O:%u:"%s":0:{}', \strlen(TraceableResponse::class), TraceableResponse::class));
+ unserialize(\sprintf('O:%u:"%s":0:{}', \strlen(TraceableResponse::class), TraceableResponse::class));
}
public function testDestructor(): void
diff --git a/tests/Transport/TransportFactoryTest.php b/tests/Transport/TransportFactoryTest.php
deleted file mode 100644
index 667590dc..00000000
--- a/tests/Transport/TransportFactoryTest.php
+++ /dev/null
@@ -1,100 +0,0 @@
-create(new Options(['dsn' => 'http://public@example.com/sentry/1']));
-
- $this->assertInstanceOf(HttpTransport::class, $transport);
-
- try {
- $transport->send(Event::createEvent())->wait();
-
- $this->fail('Failed asserting that the transport returns a rejected promise on error.');
- } catch (RejectionException $exception) {
- $this->assertInstanceOf(Response::class, $exception->getReason());
- }
- }
-
- public function testCreateWithCustomFactories(): void
- {
- $uriFactory = $this->createMock(UriFactoryInterface::class);
- $requestFactory = $this->createMock(RequestFactoryInterface::class);
- $responseFactory = $this->createMock(ResponseFactoryInterface::class);
- $streamFactory = $this->createMock(StreamFactoryInterface::class);
- $httpClient = $this->createMock(HttpAsyncClientInterface::class);
- $logger = $this->createMock(LoggerInterface::class);
- $transportFactory = new TransportFactory(
- $uriFactory,
- $requestFactory,
- $responseFactory,
- $streamFactory,
- $httpClient,
- $logger
- );
-
- $requestFactory->expects($this->once())
- ->method('createRequest')
- ->willReturnCallback(static function (...$arguments): RequestInterface {
- return Psr17FactoryDiscovery::findRequestFactory()->createRequest(...$arguments);
- });
-
- $streamFactory->expects($this->atLeastOnce())
- ->method('createStream')
- ->willReturnCallback(static function (...$arguments): StreamInterface {
- return Psr17FactoryDiscovery::findStreamFactory()->createStream(...$arguments);
- });
-
- $httpClient->expects($this->once())
- ->method('sendAsyncRequest')
- ->willReturnCallback(static function (RequestInterface $request): HttpPromiseInterface {
- return new RejectedPromise(new NetworkException('foo', $request));
- });
-
- $logger->expects($this->once())
- ->method('error')
- ->withAnyParameters();
-
- $event = Event::createEvent();
- $transport = $transportFactory->create(new Options(['dsn' => 'http://public@example.com/sentry/1', 'send_attempts' => 0]));
-
- try {
- $transport->send($event)->wait();
-
- $this->fail('Failed asserting that the transport returns a rejected promise on error.');
- } catch (RejectionException $exception) {
- /** @var Response $response */
- $response = $exception->getReason();
-
- $this->assertInstanceOf(Response::class, $response);
- $this->assertSame(ResponseStatus::failed(), $response->getStatus());
- $this->assertSame($event, $response->getEvent());
- }
- }
-}
diff --git a/tests/Twig/SentryExtensionTest.php b/tests/Twig/SentryExtensionTest.php
index f5bf7c75..5cbfd25b 100644
--- a/tests/Twig/SentryExtensionTest.php
+++ b/tests/Twig/SentryExtensionTest.php
@@ -5,7 +5,6 @@
namespace Sentry\SentryBundle\Tests\Twig;
use PHPUnit\Framework\TestCase;
-use Sentry\Client;
use Sentry\ClientInterface;
use Sentry\Options;
use Sentry\SentryBundle\Twig\SentryExtension;
@@ -17,7 +16,6 @@
use Sentry\Tracing\TraceId;
use Sentry\Tracing\Transaction;
use Sentry\Tracing\TransactionContext;
-use Sentry\Transport\NullTransport;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
@@ -68,10 +66,55 @@ public function testTraceMetaFunctionWithActiveSpan(): void
$transaction = new Transaction(new TransactionContext());
$transaction->setTraceId(new TraceId('a3c01c41d7b94b90aee23edac90f4319'));
$transaction->setSpanId(new SpanId('e69c2aef0ec34f2a'));
+ $transaction->setSampled(true);
$hub->setSpan($transaction);
- $this->assertSame('', $environment->render('foo.twig'));
+ $this->assertSame('', $environment->render('foo.twig'));
+ }
+
+ public function testW3CTraceMetaFunctionWithNoActiveSpan(): void
+ {
+ $environment = new Environment(new ArrayLoader(['foo.twig' => '{{ sentry_w3c_trace_meta() }}']));
+ $environment->addExtension(new SentryExtension());
+
+ $propagationContext = PropagationContext::fromDefaults();
+ $propagationContext->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'));
+ $propagationContext->setSpanId(new SpanId('566e3688a61d4bc8'));
+
+ $hub = new Hub(null, new Scope($propagationContext));
+
+ SentrySdk::setCurrentHub($hub);
+
+ $this->assertSame('', $environment->render('foo.twig'));
+ }
+
+ public function testW3CTraceMetaFunctionWithActiveSpan(): void
+ {
+ $environment = new Environment(new ArrayLoader(['foo.twig' => '{{ sentry_w3c_trace_meta() }}']));
+ $environment->addExtension(new SentryExtension());
+
+ $client = $this->createMock(ClientInterface::class);
+ $client->expects($this->atLeastOnce())
+ ->method('getOptions')
+ ->willReturn(new Options([
+ 'traces_sample_rate' => 1.0,
+ 'release' => '1.0.0',
+ 'environment' => 'development',
+ ]));
+
+ $hub = new Hub($client);
+
+ SentrySdk::setCurrentHub($hub);
+
+ $transaction = new Transaction(new TransactionContext());
+ $transaction->setTraceId(new TraceId('a3c01c41d7b94b90aee23edac90f4319'));
+ $transaction->setSpanId(new SpanId('e69c2aef0ec34f2a'));
+ $transaction->setSampled(true);
+
+ $hub->setSpan($transaction);
+
+ $this->assertSame('', $environment->render('foo.twig'));
}
public function testBaggageMetaFunctionWithNoActiveSpan(): void
@@ -95,7 +138,7 @@ public function testBaggageMetaFunctionWithNoActiveSpan(): void
SentrySdk::setCurrentHub($hub);
- $this->assertSame('', $environment->render('foo.twig'));
+ $this->assertSame(\sprintf('', $propagationContext->toBaggage()), $environment->render('foo.twig'));
}
public function testBaggageMetaFunctionWithActiveSpan(): void
@@ -103,11 +146,14 @@ public function testBaggageMetaFunctionWithActiveSpan(): void
$environment = new Environment(new ArrayLoader(['foo.twig' => '{{ sentry_baggage_meta() }}']));
$environment->addExtension(new SentryExtension());
- $client = new Client(new Options([
- 'traces_sample_rate' => 1.0,
- 'release' => '1.0.0',
- 'environment' => 'development',
- ]), new NullTransport());
+ $client = $this->createMock(ClientInterface::class);
+ $client->expects($this->atLeastOnce())
+ ->method('getOptions')
+ ->willReturn(new Options([
+ 'traces_sample_rate' => 1.0,
+ 'release' => '1.0.0',
+ 'environment' => 'development',
+ ]));
$hub = new Hub($client);
@@ -118,7 +164,7 @@ public function testBaggageMetaFunctionWithActiveSpan(): void
$hub->setSpan($transaction);
- $this->assertSame('', $environment->render('foo.twig'));
+ $this->assertSame(\sprintf('', $transaction->toBaggage()), $environment->render('foo.twig'));
}
private static function isTwigBundlePackageInstalled(): bool