定制 dennisvanbeersel/symfony-logger-client 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

dennisvanbeersel/symfony-logger-client

最新稳定版本:v0.2.2

Composer 安装命令:

composer require dennisvanbeersel/symfony-logger-client

包简介

Symfony bundle for Application Logger error tracking platform

README 文档

README

🛡️ Privacy-First Error Tracking for Symfony - Hosted in EU

PHP Symfony License Tests PHPStan

Resilience-first error tracking with integrated JavaScript SDK - your app never slows down

Quick StartWhy This Bundle?FeaturesDocumentation

📦 TL;DR - Get Started in 2 Minutes

# 1. Install
composer require dennisvanbeersel/symfony-logger-client

# 2. Configure (config/packages/application_logger.yaml)
application_logger:
    dsn: '%env(APPLICATION_LOGGER_DSN)%'
    api_key: '%env(APPLICATION_LOGGER_API_KEY)%'

# 3. Add credentials to .env
APPLICATION_LOGGER_DSN=https://applogger.eu/your-project-uuid
APPLICATION_LOGGER_API_KEY=your-64-character-api-key-here

# 4. Clear cache
php bin/console cache:clear

Done! All PHP exceptions and JavaScript errors are now automatically tracked. No code changes needed.

🎯 Why This Bundle?

AppLogger (applogger.eu) is an EU-hosted, privacy-first error tracking SaaS platform specifically designed for Symfony applications. This bundle provides zero-config integration with production-grade resilience.

Most error tracking solutions have a critical flaw: they can slow down or even crash your application when the tracking service is down. This bundle is different.

Core Philosophy: Never Impact Your Application

We achieve this through battle-tested resilience patterns:

Feature This Bundle Typical Solutions Impact
Timeout ⚡ 2s max (configurable) ⏰ Often 30s+ or none 50ms vs 30s+ delay
Circuit Breaker ✅ Automatic failover ❌ Keep retrying Stops wasting resources
Fire & Forget ✅ Returns instantly ❌ Waits for response <1ms vs 2000ms
Exception Safety ✅ Never throws ⚠️ Can crash app 100% uptime guarantee
JS Offline Queue ✅ localStorage backup ❌ Errors lost Zero data loss
JS Rate Limiting ✅ Token bucket ❌ Can overwhelm API Protected from error storms

Real-World Impact

Without resilience patterns:

// API is down, timeout is 30s
$start = microtime(true);
errorTracker()->captureException($e);  // Blocks for 30 seconds!
$elapsed = microtime(true) - $start;   // 30,000ms
// User waited 30 seconds for page to load 😱

With this bundle:

// API is down, circuit breaker is open
$start = microtime(true);
errorTracker()->captureException($e);  // Returns instantly
$elapsed = microtime(true) - $start;   // <1ms
// User doesn't notice anything 🎉

✨ Features

PHP Backend Features

Automatic Capture

  • 🚨 Uncaught exceptions
  • 📝 Monolog error logs
  • 🔢 HTTP status codes (404, 500, etc.)
  • 👤 User context from Symfony Security
  • 📊 Request/response data
  • 🍞 Breadcrumb trails

Resilience (Production-Grade)

  • ⚡ 2s timeout (configurable 0.5-5s)
  • 🔌 Circuit breaker pattern
  • 🔥 Fire-and-forget async mode
  • 🔄 Optional smart retry (exponential backoff)
  • ✅ Zero exceptions thrown
  • 📊 Health monitoring

Security (GDPR Compliant)

  • 🔐 Automatic PII scrubbing
  • 🌐 IP anonymization
  • 🛡️ Secure DSN authentication
  • 🔒 Encrypted in transit (HTTPS)
  • 📋 Customizable scrub fields
  • 🚫 No sensitive data leaks

Developer Experience

  • 🎯 Zero configuration needed
  • 📦 Works out of the box
  • 🔧 Highly customizable
  • 🐛 Built-in debug mode
  • 📊 Circuit breaker monitoring
  • 📚 Comprehensive docs

JavaScript SDK Features (Included!)

No separate npm package needed! The JavaScript SDK is bundled with this Symfony bundle.

Automatic Capture

  • 🌐 Window errors (uncaught exceptions)
  • ❌ Unhandled promise rejections
  • 🔢 HTTP status codes from failed API calls
  • 👤 User context (auto-synced from backend)
  • 📊 Browser/platform detection
  • 🍞 Navigation and user actions

Resilience (Client-Side)

  • ⚡ 3s timeout with AbortController
  • 🔌 Circuit breaker (sessionStorage)
  • 💾 Offline queue (localStorage, 50 errors)
  • 🚦 Rate limiting (token bucket, 10/min)
  • ⚖️ Deduplication (prevents spam)
  • 📡 Beacon API (send on page close)

📊 What Gets Tracked?

This bundle provides comprehensive monitoring for both backend and frontend:

PHP Backend Tracking

  • Exceptions: All uncaught exceptions via Symfony event subscriber
  • HTTP Errors: 4xx and 5xx status codes (404, 500, etc.)
  • Monolog Integration: Error-level logs when configured
  • Context: Request/response data, user context, server metadata
  • Breadcrumbs: User actions leading up to errors

JavaScript Frontend Tracking

  • Browser Errors: Uncaught exceptions and unhandled promise rejections
  • API Failures: Failed HTTP requests with status codes
  • Error-Triggered Session Replay: Captures user actions (30s/10 clicks before and after errors) with DOM snapshots
  • Session Tracking: Page views, navigation flows, session duration
  • User Context: Browser, platform, screen resolution

GDPR Compliance

  • Automatic PII Scrubbing: Passwords, tokens, sensitive fields removed
  • IP Anonymization: Last octet masked (192.168.1.0 instead of 192.168.1.100)
  • Session Hashing: User identifiers are cryptographically hashed
  • EU Data Residency: All data stored in EU datacenters

🚀 Quick Start

Installation

composer require dennisvanbeersel/symfony-logger-client

If you're not using Symfony Flex, register the bundle in config/bundles.php:

return [
    // ...
    ApplicationLogger\Bundle\ApplicationLoggerBundle::class => ['all' => true],
];

Configuration

Minimal Configuration (Recommended)

# config/packages/application_logger.yaml
application_logger:
    dsn: '%env(APPLICATION_LOGGER_DSN)%'

Add to .env:

APPLICATION_LOGGER_DSN=https://public_key@logger.example.com/project_id
APP_VERSION=1.0.0  # Optional but recommended

Full Configuration Example

Click to see all available options
# config/packages/application_logger.yaml
application_logger:
    # Required: Your AppLogger DSN (get from applogger.eu dashboard)
    dsn: '%env(APPLICATION_LOGGER_DSN)%'

    # Optional: Enable/disable the bundle
    enabled: true

    # Optional: Application version for release tracking
    release: '%env(APP_VERSION)%'

    # Optional: Environment identifier
    environment: '%kernel.environment%'

    # Resilience Settings
    timeout: 2.0              # API timeout (0.5-5.0 seconds)
    retry_attempts: 0         # Retry failed requests (0-3, 0=fail fast)
    async: true               # Fire-and-forget mode (recommended)

    # Circuit Breaker
    circuit_breaker:
        enabled: true         # Enable circuit breaker pattern
        failure_threshold: 5  # Open after N consecutive failures
        timeout: 60           # Stay open for N seconds
        half_open_attempts: 1 # Test requests before closing

    # What to Capture
    capture_level: error      # Monolog level: debug, info, warning, error, critical

    # Breadcrumbs
    max_breadcrumbs: 50       # Maximum breadcrumbs to keep (10-100)

    # Security: Sensitive Data Scrubbing
    scrub_fields:
        - password
        - token
        - api_key
        - secret
        - authorization
        - credit_card
        - ssn

    # Session Tracking (Required for session replay)
    session_tracking:
        enabled: true              # Enable automatic session tracking (default: true)
        track_page_views: true     # Track page views as session events (default: true)
        idle_timeout: 1800         # Session idle timeout in seconds (default: 30 min)

    # Error-Triggered Session Replay
    session_replay:
        enabled: true                      # Enable session replay (default: true)
        buffer_before_error_seconds: 30    # Seconds to buffer before error (5-60, default: 30)
        buffer_before_error_clicks: 10     # Clicks to buffer before error (1-15, default: 10)
        buffer_after_error_seconds: 30     # Seconds to buffer after error (5-60, default: 30)
        buffer_after_error_clicks: 10      # Clicks to buffer after error (1-15, default: 10)
        click_debounce_ms: 1000            # Click debounce delay (100-5000ms, default: 1000)
        snapshot_throttle_ms: 1000         # DOM snapshot throttle (500-5000ms, default: 1000)
        max_snapshot_size: 1048576         # Max snapshot size in bytes (default: 1MB)
        session_timeout_minutes: 30        # Cross-page session timeout (5-120 min, default: 30)
        max_buffer_size_mb: 5              # Max localStorage size (1-20MB, default: 5MB)
        expose_api: true                   # Expose JS API for user control (default: true)

    # JavaScript SDK
    javascript:
        enabled: true         # Enable Twig globals for JS SDK
        auto_inject: true     # Auto-inject init script (recommended)
        debug: false          # Enable console.log debugging

    # Debug
    debug: '%kernel.debug%'   # Enable internal logging

Clear Cache

php bin/console cache:clear

Done! All exceptions are now automatically tracked. Visit your AppLogger dashboard at applogger.eu to see errors.

📖 Usage

1️⃣ PHP Backend Usage

Automatic Capture (Zero Code Changes)

The bundle automatically captures:

  • Uncaught exceptions via Symfony event subscriber
  • HTTP status codes (404, 403, 500, etc.)
  • Monolog error logs (when configured)
  • User context from Symfony Security
  • Request data (headers, POST data, query params)

No code changes required! Just install and configure.

Monolog Integration

Send error-level logs to AppLogger:

# config/packages/monolog.yaml
monolog:
    handlers:
        application_logger:
            type: service
            id: ApplicationLogger\Bundle\Monolog\Handler\ApplicationLoggerHandler
            level: error
            channels: ['!event']  # Exclude to avoid duplication

Now all $logger->error(), $logger->critical(), etc. calls are tracked.

Manual Error Capture

For custom error handling:

use ApplicationLogger\Bundle\Service\ApiClient;
use ApplicationLogger\Bundle\Service\BreadcrumbCollector;

class PaymentService
{
    public function __construct(
        private ApiClient $apiClient,
        private BreadcrumbCollector $breadcrumbs
    ) {}

    public function processPayment(Order $order): void
    {
        // Add breadcrumb for context
        $this->breadcrumbs->add([
            'type' => 'user',
            'category' => 'payment',
            'message' => 'Processing payment',
            'data' => ['order_id' => $order->getId()],
        ]);

        try {
            $this->chargeCustomer($order);
        } catch (\Exception $e) {
            // Manual error reporting
            $this->apiClient->sendError([
                'exception' => [
                    'type' => $e::class,
                    'value' => $e->getMessage(),
                    'stacktrace' => $this->formatStackTrace($e),
                ],
                'level' => 'error',
                'tags' => ['feature' => 'payment'],
            ]);

            throw $e; // Re-throw if needed
        }
    }
}

Adding Breadcrumbs

Track user actions leading up to errors:

use ApplicationLogger\Bundle\Service\BreadcrumbCollector;

class CheckoutController extends AbstractController
{
    public function __construct(
        private BreadcrumbCollector $breadcrumbs
    ) {}

    #[Route('/checkout/step-1')]
    public function step1(): Response
    {
        $this->breadcrumbs->add([
            'type' => 'navigation',
            'category' => 'checkout',
            'message' => 'User entered checkout',
            'level' => 'info',
        ]);

        // ... your code
    }
}

2️⃣ JavaScript SDK Usage

Zero-Config Mode (Automatic) ⭐ Recommended

Default behavior - no setup needed!

The bundle automatically:

  1. ✅ Registers JS SDK with AssetMapper
  2. ✅ Injects initialization script on all HTML pages
  3. ✅ Configures with your DSN
  4. ✅ Sets environment and release
  5. ✅ Populates user context
  6. ✅ Makes window.appLogger available

Just install the bundle - JavaScript tracking works immediately!

Manual Mode (Custom Control)

If you want control over when/where the SDK loads:

# config/packages/application_logger.yaml
application_logger:
    javascript:
        auto_inject: false  # Disable automatic injection

Then manually add to your templates:

{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
<body>
    {% block body %}{% endblock %}

    {# Manually place the initialization script #}
    {{ application_logger_init() }}
</body>
</html>

Using the JavaScript SDK

Once loaded, use window.appLogger:

// Capture exceptions
try {
    riskyOperation();
} catch (error) {
    window.appLogger.captureException(error, {
        tags: { component: 'checkout' },
        extra: { orderId: 12345 }
    });
}

// Capture messages
window.appLogger.captureMessage('Payment processed', 'info');

// Add breadcrumbs
window.appLogger.addBreadcrumb({
    type: 'user',
    message: 'User clicked checkout button',
    data: { cartTotal: 99.99 }
});

// Set user context
window.appLogger.setUser({
    id: 'user-123',
    email: 'user@example.com'
});

// Check circuit breaker status
window.appLogger.transport.getStats();
// {queueSize: 0, rateLimitTokens: 9.2, circuitBreaker: {state: 'closed'}}

🛡️ Resilience Features Explained

Circuit Breaker Pattern

Problem: When the API is down, your app wastes resources retrying.

Solution: Circuit breaker with three states:

CLOSED (normal) → [5 failures] → OPEN (service down)
                                      ↓
                               [60 seconds wait]
                                      ↓
CLOSED ← [success] ← HALF_OPEN ← [timeout passed]
         [failure] → OPEN

PHP Implementation:

  • Uses Symfony Cache for state persistence
  • After 5 consecutive failures → opens for 60 seconds
  • While OPEN: all API calls skip immediately (zero overhead)
  • After 60s: enters HALF_OPEN, tries 1 request
  • Success → CLOSED, failure → OPEN for another 60s

JavaScript Implementation:

  • Uses sessionStorage for state persistence
  • Same 3-state logic as PHP
  • Prevents browser from hitting failing API

Monitoring:

// PHP
$state = $apiClient->getCircuitBreakerState();
// ['state' => 'closed', 'failureCount' => 2, 'openedAt' => null]
// JavaScript
window.appLogger.transport.circuitBreaker.getState();
// {state: 'closed', failureCount: 0, openedAt: null}

Timeout Protection

PHP:

  • Maximum 2 seconds per API call (configurable 0.5-5s)
  • Configured at HTTP client level
  • After timeout: connection aborted, circuit breaker records failure

JavaScript:

  • Maximum 3 seconds per API call
  • Uses AbortController to forcefully abort
  • After timeout: error queued to localStorage

Fire-and-Forget Mode (PHP)

When async: true (default):

// With async: false (synchronous)
$start = microtime(true);
$apiClient->sendError($payload);
$elapsed = microtime(true) - $start;
// $elapsed could be 2000ms (full timeout)

// With async: true (fire-and-forget)
$start = microtime(true);
$apiClient->sendError($payload);
$elapsed = microtime(true) - $start;
// $elapsed is typically < 1ms (request queued, method returns)

Offline Queue (JavaScript)

When API is unreachable:

  1. Errors stored in localStorage (FIFO queue)
  2. Maximum 50 errors (oldest removed first)
  3. Errors expire after 24 hours
  4. On next successful connection: queue automatically flushed

Handles quota errors gracefully:

  • If localStorage full → removes oldest 50%
  • If still full → clears entire queue

Rate Limiting (JavaScript)

Token bucket algorithm prevents error storms:

  • Capacity: 10 tokens
  • Refill rate: ~1 token per 6 seconds (~10 per minute)
  • Behavior: No tokens → error goes to offline queue
window.appLogger.transport.getStats();
// {rateLimitTokens: 8.5, queueSize: 0, ...}

Deduplication (JavaScript)

Prevents sending the same error repeatedly:

  • Creates hash from: error type + message + top 3 stack frames
  • Remembers recently sent errors for 5 seconds
  • Duplicate detected → ignored

Beacon API (JavaScript)

Problem: When user closes tab, errors in queue are lost.

Solution: navigator.sendBeacon() API

  • Listens to beforeunload and visibilitychange
  • Flushes up to 10 most recent errors
  • Guaranteed delivery even as page closes

🔒 Security Features

Automatic Data Scrubbing

Sensitive data automatically removed from error reports:

Default scrubbed fields:

  • password, passwd, pwd
  • token, api_key, secret
  • authorization, auth
  • credit_card, ssn, private_key

How it works:

  • Recursive key check (case-insensitive substring matching)
  • Replaces values with [REDACTED]
  • Applies to: request data, headers, cookies, extra context

Example:

$request->request->all();
// ['email' => 'user@example.com', 'password' => 'secret123']

// Sent to API as:
// ['email' => 'user@example.com', 'password' => '[REDACTED]']

Custom scrub fields:

application_logger:
    scrub_fields:
        - password
        - credit_card
        - my_custom_secret

IP Address Anonymization

IPv4: Masks last octet

192.168.1.100 → 192.168.1.0

IPv6: Masks last 80 bits

2001:0db8:85a3:0000:0000:8a2e:0370:7334
→ 2001:0db8:85a3:0000:0000:0000:0000:0000

Why: GDPR compliance - IP addresses are personal data.

🔧 Advanced Configuration

Disable in Development

# config/packages/dev/application_logger.yaml
application_logger:
    enabled: false

Or use .env.local:

APPLICATION_LOGGER_ENABLED=false

Multiple Projects

Send errors to different AppLogger projects:

# config/services.yaml
services:
    app.logger.project_a:
        class: ApplicationLogger\Bundle\Service\ApiClient
        arguments:
            $dsn: '%env(LOGGER_DSN_PROJECT_A)%'
            $timeout: 2.0
            $circuitBreaker: '@ApplicationLogger\Bundle\Service\CircuitBreaker'

    app.logger.project_b:
        class: ApplicationLogger\Bundle\Service\ApiClient
        arguments:
            $dsn: '%env(LOGGER_DSN_PROJECT_B)%'
            $timeout: 2.0
            $circuitBreaker: '@ApplicationLogger\Bundle\Service\CircuitBreaker'

Custom Error Handler

use ApplicationLogger\Bundle\Service\ApiClient;
use ApplicationLogger\Bundle\Service\BreadcrumbCollector;
use ApplicationLogger\Bundle\Service\ContextCollector;

class CustomErrorHandler
{
    public function __construct(
        private ApiClient $apiClient,
        private ContextCollector $contextCollector,
        private BreadcrumbCollector $breadcrumbs
    ) {}

    public function handleBusinessError(BusinessException $e): void
    {
        $this->apiClient->sendError([
            'exception' => [
                'type' => $e::class,
                'value' => $e->getMessage(),
                'stacktrace' => $this->formatTrace($e),
            ],
            'level' => 'warning', // Business errors are warnings
            'context' => $this->contextCollector->collectContext(),
            'breadcrumbs' => $this->breadcrumbs->get(),
            'tags' => [
                'error_type' => 'business',
                'rule' => $e->getBusinessRule(),
            ],
        ]);
    }
}

🐛 Troubleshooting

Errors Not Appearing in Dashboard

1. Check bundle is enabled:

php bin/console debug:config application_logger

2. Check DSN is correct:

php bin/console debug:container --parameters | grep application_logger.dsn

3. Check circuit breaker:

$cbState = $this->apiClient->getCircuitBreakerState();
// If state is 'open', wait 60s or clear cache

4. Enable debug mode:

application_logger:
    debug: true

Check var/log/dev.log for details.

Circuit Breaker Stuck Open

Solution 1: Wait for timeout (default 60 seconds)

Solution 2: Clear cache:

php bin/console cache:clear

Solution 3: Manually reset:

$cache->delete('app_logger_circuit_breaker_state');
JavaScript SDK Not Loading

1. Check AssetMapper:

php bin/console debug:asset-map | grep application-logger

2. Check browser console for import errors

3. Verify meta tag exists:

<meta name="app-logger-dsn" content="https://...">
DSN Format Error

Correct format:

https://public_key@your-host.com/project_id

Common mistakes:

❌ http://public_key@host/project       (use https://)
❌ https://host/project                 (missing public_key@)
❌ https://public_key:secret@host/proj  (secret not needed)
❌ https://public_key@host              (missing /project_id)

🛠️ Development

Code Quality

composer lint        # PHP-CS-Fixer + PHPStan
composer cs-check    # Check PSR-12
composer cs-fix      # Auto-fix PSR-12
composer phpstan     # Static analysis (level 6)
npm run lint         # ESLint
npm run lint:fix     # Auto-fix ESLint

Testing

# PHP tests
composer test
vendor/bin/phpunit

# JavaScript tests
npm test
npm run test:coverage

Requirements

Minimum:

  • PHP 8.2+
  • Symfony 6.4 or 7.x
  • ext-json, ext-curl

Recommended:

  • PHP 8.3+
  • Symfony 7.1+
  • APCu or Redis (production cache)

📚 Documentation

Document Description
AppLogger Website Sign up and get your DSN
API Reference REST API documentation
Architecture Technical architecture
Security & Testing Security practices and testing guidelines

📝 License

Part of the AppLogger project - see main LICENSE file.

🙏 Credits

Key Design Principles:

  1. Resilience first - never impact the host application
  2. Secure by default - no sensitive data exposure
  3. Zero configuration - works out of the box
  4. Production ready - battle-tested patterns
  5. Developer friendly - comprehensive docs

Built with ❤️ for the Symfony community.

Questions? Issues? Feedback?

📖 Documentation🐛 Report Bug💬 Discussions

⬆ Back to Top

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: JavaScript

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-10-27