Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Psr3 Message Assertions Laravel Package

fr3d/psr3-message-assertions

PHPUnit helper to assert your application’s PSR-3 log messages follow the Logger spec. Use the included TestLogger as a logger dependency during tests to validate message formatting and context placeholders. Composer-installable, BSD-2-Clause.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Package

    composer require fr3d/psr3-message-assertions
    
  2. Replace Your Logger in Tests In your test class, inject TestLogger (from fr3d\Psr3MessageAssertions\PhpUnit\TestLogger) instead of your production logger. Example:

    use fr3d\Psr3MessageAssertions\PhpUnit\TestLogger;
    
    public function testLoggerCompliance()
    {
        $logger = new TestLogger();
        $this->app->instance(\Psr\Log\LoggerInterface::class, $logger);
    
        // Trigger logging (e.g., via a service or facade)
        Log::info('Test message');
    }
    
  3. First Use Case Run a test where you log messages. If any message violates PSR-3 (e.g., non-string context values, invalid log levels), the test will fail with a descriptive assertion message.


Implementation Patterns

Workflows

  1. Unit Testing Log Compliance Use TestLogger in unit tests to enforce PSR-3 compliance for critical logging paths (e.g., error handling, auditing). Example:

    public function testErrorLogging()
    {
        $logger = new TestLogger();
        Log::setLogger($logger); // If using a facade
    
        // Simulate an error
        Log::error('Failed to process', ['user_id' => 123, 'invalid_key' => new stdClass()]);
        // Fails: `invalid_key` is not a string, scalar, or array of scalars.
    }
    
  2. Integration Testing Replace the logger in integration tests where external services (e.g., Sentry, Monolog) are involved. Verify logs are structured correctly before they reach downstream systems.

  3. Custom Assertions Extend TestLogger to add custom validation (e.g., regex patterns for message formats):

    use fr3d\Psr3MessageAssertions\PhpUnit\TestLogger;
    
    class CustomTestLogger extends TestLogger
    {
        protected function assertMessageFormat(string $message): void
        {
            if (!preg_match('/^\[ERROR\] .*/', $message)) {
                $this->fail("Error message must start with '[ERROR]'");
            }
        }
    }
    

Integration Tips

  • Monolog Integration Wrap Monolog’s Logger with TestLogger to validate logs before they’re processed:

    $monolog = new Monolog\Logger('name');
    $testLogger = new TestLogger();
    $testLogger->pushHandler($monolog->getHandlers()[0]);
    Log::setLogger($testLogger);
    
  • Facade Usage If using Laravel’s Log facade, bind TestLogger in tests:

    $this->app->bind(\Psr\Log\LoggerInterface::class, function () {
        return new TestLogger();
    });
    
  • Mocking vs. Assertions Prefer TestLogger over mocks when you need to verify PSR-3 compliance (not just check if logging was called). Mocks won’t catch invalid message formats.


Gotchas and Tips

Pitfalls

  1. False Positives in Context TestLogger rejects non-string/non-scalar context values (e.g., objects, resources). If your app intentionally logs objects (e.g., for debugging), either:

    • Cast them to arrays/strings before logging, or
    • Extend TestLogger to allow specific classes:
      protected function assertContext(array $context): void
      {
          foreach ($context as $key => $value) {
              if (!is_scalar($value) && !$value instanceof AllowedClass) {
                  $this->fail("Context value for '$key' must be scalar or AllowedClass");
              }
          }
      }
      
  2. Level Validation The package enforces PSR-3 log levels (DEBUG, INFO, etc.). If you use custom levels (e.g., CUSTOM), extend TestLogger to whitelist them:

    protected function assertLevel($level): void
    {
        $validLevels = array_merge(parent::getValidLevels(), ['CUSTOM']);
        if (!in_array($level, $validLevels)) {
            $this->fail("Invalid log level: $level");
        }
    }
    
  3. Performance in CI TestLogger adds overhead. Disable it in non-test environments or use it selectively:

    if (app()->environment('testing')) {
        $this->app->bind(\Psr\Log\LoggerInterface::class, TestLogger::class);
    }
    
  4. Facade Caching Laravel’s service container caches bindings. Clear the cache after tests if reusing the same container:

    $this->app->forgetInstance(\Psr\Log\LoggerInterface::class);
    

Debugging Tips

  • Inspect Failed Assertions TestLogger provides detailed failure messages. Example output:

    Failed asserting that log message 'User loaded' has a valid context.
    Context key 'user' has value [object](App\Models\User).
    Expected scalar or array of scalars.
    
  • Log Level Mismatches If tests fail with "Invalid log level," check for typos (e.g., log::emergency() vs. Log::emergency()).

  • Context Arrays Nested arrays in context are allowed, but all leaf values must be scalar. Flatten them if needed:

    Log::info('Data', ['nested' => ['id' => 123, 'name' => 'Test']]); // Passes
    Log::info('Data', ['nested' => ['obj' => new stdClass()]]); // Fails
    

Extension Points

  1. Custom Assertions Override methods like:

    • assertMessage(): Validate message format (e.g., required prefixes).
    • assertContext(): Relax scalar requirements for specific keys.
    • assertLevel(): Add custom log levels.
  2. Handler Integration Extend TestLogger to support PSR-3 handlers:

    class HandlerTestLogger extends TestLogger
    {
        public function pushHandler(HandlerInterface $handler)
        {
            $this->handlers[] = $handler;
        }
    }
    
  3. Laravel-Specific Extensions For Laravel, create a test trait to auto-configure TestLogger:

    trait LogsPsr3Compliant
    {
        protected function setUp(): void
        {
            $this->app->bind(\Psr\Log\LoggerInterface::class, TestLogger::class);
        }
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui