[4.x] Add LogChannelBootstrapper#1381
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1381 +/- ##
============================================
+ Coverage 86.37% 86.57% +0.19%
- Complexity 1200 1221 +21
============================================
Files 186 187 +1
Lines 3524 3575 +51
============================================
+ Hits 3044 3095 +51
Misses 480 480 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ook used by the slack channel correctly)
There was a problem hiding this comment.
Pull Request Overview
This PR adds a new LogTenancyBootstrapper to provide tenant-specific logging configuration. The bootstrapper automatically configures storage path channels to use tenant-specific directories and supports custom channel overrides for more complex logging scenarios.
Key changes:
- Implements
LogTenancyBootstrapperwith automatic storage path channel configuration and custom override support - Adds comprehensive test coverage for default behavior, custom overrides, and real-world usage scenarios
- Registers the new bootstrapper in the test environment
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/Bootstrappers/LogTenancyBootstrapper.php |
Main implementation of the log tenancy bootstrapper with storage path handling and channel override functionality |
tests/Bootstrappers/LogTenancyBootstrapperTest.php |
Comprehensive test suite covering default behavior, custom overrides, stack channels, and real logging scenarios |
tests/TestCase.php |
Adds import and singleton registration for the new bootstrapper in test environment |
Comments suppressed due to low confidence (1)
tests/Bootstrappers/LogTenancyBootstrapperTest.php:345
- The test relies on catching exceptions to verify webhook URLs, but this approach is fragile and may not work reliably across different environments or Laravel versions. Consider mocking the HTTP client or using a more deterministic testing approach.
try {
…is set (otherwise, just skip the override and keep the default config value)
…ehavior, improve test by making assertions more specific
|
@coderabbitai resume |
✅ Actions performedReviews resumed. |
too many mentions of "default" behavior when there are just different approaches for different channels
|
Seems some of the minor logic changes here b51d5ca (the getAttribute thing perhaps?) made a test start failing. |
The bootstrapper should be able to map nested tenant attributes to config, and `getAttribute()` doesn't support dot notation, so e.g. `$tenant->getAttribute('logging.slackUrl')` would return `null` instead of the actual attribute, so the test failed.
`data_get()` supports dot notation, so we'll use that.
…nels Reverting the LogTenancyBootstrapper changes will make the new "stack channels that include any configured channel are re-resolved" test fali
Yes, the test started failing because in the test, // 'slack channel uses correct webhook urls'
LogTenancyBootstrapper::$channelOverrides = [
'slack' => ['url' => 'logging.slackUrl'],
]
Fixed by using |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/Bootstrappers/LogTenancyBootstrapperTest.php`:
- Around line 373-390: The test currently verifies `custom_stack` only while
tenancy is active, so it does not cover the channel reset path after
`tenancy()->end()`. After the existing tenant log assertions in
`LogTenancyBootstrapperTest`, exercise `Log::channel('custom_stack')` again once
tenancy has ended and add an assertion that the post-revert log goes back to the
central log (using the existing `centralLogPath` and `tenantLogPath` checks).
This will validate the `forgetChannel()` behavior triggered by `TenancyEnded`
and ensure the stack channel is re-resolved back to the non-tenant
configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f50ddf7a-bc8b-43b4-a5a0-df590bf44372
📒 Files selected for processing (2)
src/Bootstrappers/LogTenancyBootstrapper.phptests/Bootstrappers/LogTenancyBootstrapperTest.php
we use FilesystemTenancyBootstrapper::getBoundCentralStoragePath() in LogTenancyBootstrapper to simplify the logic for extracting a path inside the central storage directory we could use this method in more places, such as DeleteTenantStorage: $centralStoragePath = tenancy()->central(fn () => storage_path()); $tenantStoragePath = tenancy()->run($this->tenant, fn () => storage_path()); however for consistency (we're getting both the central and tenant paths there) we're keeping it simple as it is now using the newly added method would also introduce more coupling to FilesystemTenancyBootstrapper - its specific implementation. this isn't a big issue in LogTenancyBootstrapper which already has several dependencies on the FS bootstrapper, and we do use app(static::class) in the added method so extending the bootstrapper should work
the main change here is adding similar assertions related to *re-resolving* channels to the 'stack channels that include any configured channel are re-resolved' test, similar to the 'channels are forgotten and re-resolved during bootstrap and revert' test
|
This bootstrapper should be renamed following our existing naming pattern, where XTenancyBootstrapper is only used for core Laravel components like databases or cache: 'bootstrappers' => [
// Basic Laravel features
Bootstrappers\DatabaseTenancyBootstrapper::class,
Bootstrappers\CacheTenancyBootstrapper::class,
// Bootstrappers\CacheTagsBootstrapper::class, // Alternative to CacheTenancyBootstrapper
// Bootstrappers\DatabaseCacheBootstrapper::class, // Separates cache by DB rather than by prefix, must run after DatabaseTenancyBootstrapper
Bootstrappers\FilesystemTenancyBootstrapper::class,
Bootstrappers\QueueTenancyBootstrapper::class,
// Bootstrappers\RedisTenancyBootstrapper::class, // Note: phpredis is needed
// Adds support for the database session driver
Bootstrappers\DatabaseSessionBootstrapper::class,
// Configurable bootstrappers
// Bootstrappers\TenantConfigBootstrapper::class,
// Bootstrappers\RootUrlBootstrapper::class,
// Bootstrappers\UrlGeneratorBootstrapper::class,
// Bootstrappers\MailConfigBootstrapper::class,
// Bootstrappers\LogChannelBootstrapper::class,
// Bootstrappers\BroadcastingConfigBootstrapper::class,
// Bootstrappers\BroadcastChannelPrefixBootstrapper::class,
// Integration bootstrappers
// Bootstrappers\Integrations\FortifyRouteBootstrapper::class,
// Bootstrappers\Integrations\ScoutPrefixBootstrapper::class,
// Bootstrappers\PostgresRLSBootstrapper::class,
],I think |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Bootstrappers/LogChannelBootstrapper.php (1)
159-165: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winValidate storage-path channel paths before rebasing. A custom
storagePathChannelsentry can still reach this branch with a missing or non-stringpath, and an out-of-root path will be rebased into an invalid tenant path. Guard the config before callingStr::after()/storage_path().🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Bootstrappers/LogChannelBootstrapper.php` around lines 159 - 165, The channel path rebasing in LogChannelBootstrapper::bootstrap currently assumes each storagePathChannels entry has a valid string path inside the central storage root. Add a guard before calling Str::after() and storage_path() to verify the configured logging.channels.{channel}.path exists, is a string, and is actually rooted under FilesystemTenancyBootstrapper::getBoundCentralStoragePath(); if not, skip rebasing or handle it explicitly so invalid tenant paths are not generated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/Bootstrappers/LogChannelBootstrapper.php`:
- Around line 159-165: The channel path rebasing in
LogChannelBootstrapper::bootstrap currently assumes each storagePathChannels
entry has a valid string path inside the central storage root. Add a guard
before calling Str::after() and storage_path() to verify the configured
logging.channels.{channel}.path exists, is a string, and is actually rooted
under FilesystemTenancyBootstrapper::getBoundCentralStoragePath(); if not, skip
rebasing or handle it explicitly so invalid tenant paths are not generated.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7c934def-6b6a-440b-b4b0-dc38e12efc53
📒 Files selected for processing (4)
assets/config.phpsrc/Bootstrappers/LogChannelBootstrapper.phptests/Bootstrappers/LogChannelBootstrapperTest.phptests/TestCase.php
This PR adds the LogChannelBootstrapper to provide tenant-specific logging configuration. The bootstrapper automatically configures storage path channels to use tenant-specific directories (NOTE: for this to work correctly, the bootstrapper has to run AFTER FilesystemTenancyBootstrapper, otherwise, the logs still won't be separated, unless you use overrides) and supports custom channel overrides for custom logging scenarios -- mapping tenant properties to the channel config, or using custom closures an array with the logging config, e.g. for making the slack channel (that's not handled by the bootstrapper by default) tenant-specific.
The bootstrapper first modifies the channel config, then forgets the channel from LogManager so that on the next logging attempt, the channel is re-resolved with the modified config. Otherwise, the channel would just use the initial config if the channel was resolved before. If the channel wasn't resolved before, it'll always be resolved with the updated (tenant) config, unless the configuration fails. In that case, the config will be reverted (the central config will be restored) and the error will be logged using the original channel.
When using a channel stack, the
stackchannel itself also has to be forgotten, since the LogManager could retain e.g. the originalstackchannel's webhook URL, while the underlyingslackchannel would use the updated one, and while logging, the app would actually use the initial webhook URL instead of the updated one (encountered this issue while testing).Note that all channels in
$storagePathChannelsand$channelOverridesare affected.Also, adding
'attachment' => 'false'to the slack channel's config makes the slack channel work with Discord webhooks (just a cool thing we figured out whlle testing the bootstrapper).Summary by CodeRabbit
New Features
Tests