acc-create-psr3-logger
Generates PSR-3 Logger implementation for PHP 8.5. Creates LoggerInterface implementations with log levels, context interpolation, and LoggerAwareTrait usage. Includes unit tests.
Best use case
acc-create-psr3-logger is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Generates PSR-3 Logger implementation for PHP 8.5. Creates LoggerInterface implementations with log levels, context interpolation, and LoggerAwareTrait usage. Includes unit tests.
Teams using acc-create-psr3-logger should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/acc-create-psr3-logger/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How acc-create-psr3-logger Compares
| Feature / Agent | acc-create-psr3-logger | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Generates PSR-3 Logger implementation for PHP 8.5. Creates LoggerInterface implementations with log levels, context interpolation, and LoggerAwareTrait usage. Includes unit tests.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# PSR-3 Logger Generator
## Overview
Generates PSR-3 compliant logger implementations following `Psr\Log\LoggerInterface`.
## When to Use
- Implementing custom logging solutions
- Creating application-specific loggers
- Building logging adapters for external services
- Testing with mock/null loggers
## Generated Components
| Component | Description | Location |
|-----------|-------------|----------|
| Logger Implementation | Concrete logger class | `src/Infrastructure/Logger/` |
| LoggerAware Trait | For classes needing logger | `src/Infrastructure/Logger/` |
| Null Logger | Testing/no-op logger | `src/Infrastructure/Logger/` |
| Unit Tests | PHPUnit tests | `tests/Unit/Infrastructure/Logger/` |
## File Naming
| Type | Pattern | Example |
|------|---------|---------|
| File Logger | `{Name}Logger.php` | `FileLogger.php` |
| Stream Logger | `StreamLogger.php` | `StreamLogger.php` |
| Null Logger | `NullLogger.php` | `NullLogger.php` |
| Trait | `LoggerAwareTrait.php` | `LoggerAwareTrait.php` |
## Template: File Logger
```php
<?php
declare(strict_types=1);
namespace App\Infrastructure\Logger;
use DateTimeImmutable;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Stringable;
final class FileLogger implements LoggerInterface
{
private const DATE_FORMAT = 'Y-m-d H:i:s.u';
public function __construct(
private readonly string $logFile,
private readonly string $minLevel = LogLevel::DEBUG,
) {
}
public function emergency(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::EMERGENCY, $message, $context);
}
public function alert(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::ALERT, $message, $context);
}
public function critical(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::CRITICAL, $message, $context);
}
public function error(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::ERROR, $message, $context);
}
public function warning(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::WARNING, $message, $context);
}
public function notice(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::NOTICE, $message, $context);
}
public function info(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::INFO, $message, $context);
}
public function debug(string|Stringable $message, array $context = []): void
{
$this->log(LogLevel::DEBUG, $message, $context);
}
public function log(mixed $level, string|Stringable $message, array $context = []): void
{
if (!$this->shouldLog($level)) {
return;
}
$timestamp = (new DateTimeImmutable())->format(self::DATE_FORMAT);
$interpolated = $this->interpolate((string) $message, $context);
$entry = sprintf(
"[%s] %s: %s%s\n",
$timestamp,
strtoupper($level),
$interpolated,
$this->formatContext($context),
);
file_put_contents($this->logFile, $entry, FILE_APPEND | LOCK_EX);
}
private function shouldLog(string $level): bool
{
$levels = [
LogLevel::DEBUG => 0,
LogLevel::INFO => 1,
LogLevel::NOTICE => 2,
LogLevel::WARNING => 3,
LogLevel::ERROR => 4,
LogLevel::CRITICAL => 5,
LogLevel::ALERT => 6,
LogLevel::EMERGENCY => 7,
];
return ($levels[$level] ?? 0) >= ($levels[$this->minLevel] ?? 0);
}
private function interpolate(string $message, array $context): string
{
$replace = [];
foreach ($context as $key => $value) {
if (is_string($value) || $value instanceof Stringable) {
$replace['{' . $key . '}'] = (string) $value;
}
}
return strtr($message, $replace);
}
private function formatContext(array $context): string
{
if (empty($context)) {
return '';
}
$filtered = array_filter(
$context,
fn($v) => !is_string($v) && !$v instanceof Stringable,
);
if (empty($filtered)) {
return '';
}
return ' ' . json_encode($filtered, JSON_UNESCAPED_SLASHES);
}
}
```
## Template: Null Logger
```php
<?php
declare(strict_types=1);
namespace App\Infrastructure\Logger;
use Psr\Log\LoggerInterface;
use Stringable;
final readonly class NullLogger implements LoggerInterface
{
public function emergency(string|Stringable $message, array $context = []): void
{
}
public function alert(string|Stringable $message, array $context = []): void
{
}
public function critical(string|Stringable $message, array $context = []): void
{
}
public function error(string|Stringable $message, array $context = []): void
{
}
public function warning(string|Stringable $message, array $context = []): void
{
}
public function notice(string|Stringable $message, array $context = []): void
{
}
public function info(string|Stringable $message, array $context = []): void
{
}
public function debug(string|Stringable $message, array $context = []): void
{
}
public function log(mixed $level, string|Stringable $message, array $context = []): void
{
}
}
```
## Template: Logger Aware
```php
<?php
declare(strict_types=1);
namespace App\Infrastructure\Logger;
use Psr\Log\LoggerInterface;
trait LoggerAwareTrait
{
private ?LoggerInterface $logger = null;
public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
protected function getLogger(): LoggerInterface
{
return $this->logger ?? new NullLogger();
}
}
```
## Template: Unit Test
```php
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Infrastructure\Logger;
use App\Infrastructure\Logger\FileLogger;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel;
#[Group('unit')]
#[CoversClass(FileLogger::class)]
final class FileLoggerTest extends TestCase
{
private string $logFile;
protected function setUp(): void
{
$this->logFile = sys_get_temp_dir() . '/test_' . uniqid() . '.log';
}
protected function tearDown(): void
{
if (file_exists($this->logFile)) {
unlink($this->logFile);
}
}
#[Test]
public function it_logs_message_with_level(): void
{
$logger = new FileLogger($this->logFile);
$logger->error('Test error message');
$content = file_get_contents($this->logFile);
self::assertStringContainsString('ERROR', $content);
self::assertStringContainsString('Test error message', $content);
}
#[Test]
public function it_interpolates_context_placeholders(): void
{
$logger = new FileLogger($this->logFile);
$logger->info('User {username} logged in', ['username' => 'john']);
$content = file_get_contents($this->logFile);
self::assertStringContainsString('User john logged in', $content);
}
#[Test]
public function it_respects_minimum_log_level(): void
{
$logger = new FileLogger($this->logFile, LogLevel::ERROR);
$logger->debug('Debug message');
$logger->info('Info message');
$logger->error('Error message');
$content = file_get_contents($this->logFile);
self::assertStringNotContainsString('Debug message', $content);
self::assertStringNotContainsString('Info message', $content);
self::assertStringContainsString('Error message', $content);
}
#[Test]
public function it_includes_context_in_log(): void
{
$logger = new FileLogger($this->logFile);
$logger->error('Error occurred', ['code' => 500, 'trace' => 'stack']);
$content = file_get_contents($this->logFile);
self::assertStringContainsString('500', $content);
}
}
```
## Usage Examples
### Basic Logging
```php
<?php
use App\Infrastructure\Logger\FileLogger;
use Psr\Log\LogLevel;
$logger = new FileLogger('/var/log/app.log', LogLevel::INFO);
$logger->info('Application started');
$logger->error('Something went wrong', ['exception' => $e->getMessage()]);
```
### With Context Interpolation
```php
<?php
$logger->info('User {user_id} performed {action}', [
'user_id' => 123,
'action' => 'login',
'ip' => '192.168.1.1',
]);
// Output: User 123 performed login {"ip":"192.168.1.1"}
```
### Logger Aware Service
```php
<?php
declare(strict_types=1);
namespace App\Application\User\Handler;
use App\Infrastructure\Logger\LoggerAwareTrait;
use Psr\Log\LoggerAwareInterface;
final class CreateUserHandler implements LoggerAwareInterface
{
use LoggerAwareTrait;
public function __invoke(CreateUserCommand $command): void
{
$this->getLogger()->info('Creating user', ['email' => $command->email]);
// ... create user
$this->getLogger()->info('User created successfully');
}
}
```
## Requirements
```json
{
"require": {
"psr/log": "^3.0"
}
}
```
## See Also
- `references/templates.md` - Additional logger implementations
- `references/examples.md` - Real-world usage examplesRelated Skills
action_logger
Keep an audit trail of changes, commands, and verification.
acc-create-value-object
Generates DDD Value Objects for PHP 8.5. Creates immutable, self-validating objects with equality comparison. Includes unit tests.
acc-create-use-case
Generates Application Use Cases for PHP 8.5. Creates orchestration services that coordinate domain objects, handle transactions, and dispatch events. Includes unit tests.
acc-create-unit-test
Generates PHPUnit unit tests for PHP 8.5. Creates isolated tests with AAA pattern, proper naming, attributes, and one behavior per test. Supports Value Objects, Entities, Services.
acc-create-test-double
Generates test doubles (Mocks, Stubs, Fakes, Spies) for PHP 8.5. Creates appropriate double type based on testing needs with PHPUnit MockBuilder patterns.
acc-create-test-builder
Generates Test Data Builder and Object Mother patterns for PHP 8.5. Creates fluent builders with sensible defaults and factory methods for test data creation.
acc-create-strategy
Generates Strategy pattern for PHP 8.5. Creates interchangeable algorithm families with context class, strategy interface, and concrete implementations. Includes unit tests.
acc-create-state
Generates State pattern for PHP 8.5. Creates state machines with context, state interface, and concrete states for behavior changes. Includes unit tests.
acc-create-specification
Generates DDD Specification for PHP 8.5. Creates reusable business rule objects for validation, filtering, and querying with composite pattern support. Includes unit tests.
acc-create-saga-pattern
Generates Saga pattern components for PHP 8.5. Creates Saga interfaces, steps, orchestrator, state management, and compensation logic with unit tests.
acc-create-retry-pattern
Generates Retry pattern for PHP 8.5. Creates resilience component with exponential backoff, jitter, and configurable retry strategies. Includes unit tests.
acc-create-responder
Generates ADR Responder classes for PHP 8.5. Creates HTTP response builders with PSR-7/PSR-17 support. Includes unit tests.