ArtisanPack UI
DocsCoreLogging

Logging

Core v1.2.0

Every ArtisanPack UI package routes its log messages through the same logging stack so an application owner can filter, aggregate, and forward those...

Every ArtisanPack UI package routes its log messages through the same logging stack so an application owner can filter, aggregate, and forward those messages from a single place. The stack consists of:

  • ArtisanPackLogger — the interface every package logger implements
  • Logger — the default implementation backed by Laravel's LogManager
  • LoggerFactory — a container singleton that caches one Logger per package
  • ArtisanPackLog — the static facade for the factory
  • AuditLogCreated — the event emitted by $log->audit() calls

Resolution

Resolve a logger scoped to your package via the facade, the helper, or container injection:

use ArtisanPackUI\Core\Facades\ArtisanPackLog;
use ArtisanPackUI\Core\Logging\LoggerFactory;

$log = ArtisanPackLog::make( 'media-library' );

// Or
$log = app( LoggerFactory::class )->make( 'media-library' );

// Or inject directly
public function __construct( protected LoggerFactory $loggers ) {}

$log = $this->loggers->make( 'media-library' );

The factory caches one logger per package — calling make() multiple times returns the same instance.

Writing entries

The four PSR-3 severities every package implementation emits:

$log->debug( 'Processing thumbnail.', [ 'media_id' => 42 ] );
$log->info( 'Image uploaded.',      [ 'media_id' => 42, 'size' => '1.2 MB' ] );
$log->warning( 'Slow transcode.',   [ 'media_id' => 42, 'duration_ms' => 2400 ] );
$log->error( 'Transcode failed.',   [ 'media_id' => 42, 'reason' => 'unsupported codec' ] );

Every entry is automatically prefixed with the owning package's short name so aggregated streams can be filtered cleanly:

[2026-05-23 14:32:09] local.INFO: [media-library] Image uploaded. {"media_id":42,"size":"1.2 MB"}

Audit entries

audit() is for compliance-relevant or security-relevant actions. It writes an [AUDIT] prefixed info-level entry AND dispatches an AuditLogCreated event with structured metadata:

$log->audit( 'media.deleted', [
    'media_id' => 42,
    'reason'   => 'user_request',
] );

The dispatched event payload includes:

  • package — the owning package's short name
  • action — the action identifier (e.g. media.deleted)
  • data — the caller-supplied array
  • user_id — the resolved actor identifier (from auth() by default)
  • ip_address — the resolved request IP (from request() by default)
  • timestamp — UTC ISO 8601 timestamp

Listening for audit events

Applications listen for AuditLogCreated to persist records, forward them to an external aggregator (Elasticsearch, Datadog, Splunk), or trigger notifications:

use ArtisanPackUI\Core\Events\AuditLogCreated;
use Illuminate\Support\Facades\Event;

Event::listen( AuditLogCreated::class, function ( AuditLogCreated $event ): void {
    AuditEntry::create( [
        'package'    => $event->data['package'],
        'action'     => $event->data['action'],
        'data'       => $event->data['data'],
        'user_id'    => $event->data['user_id'],
        'ip_address' => $event->data['ip_address'],
        'occurred_at' => $event->data['timestamp'],
    ] );
} );

Disabling audit logging

Set artisanpack.core.logging.audit.enabled = false in config/artisanpack.php (or ARTISANPACK_AUDIT_ENABLED=false in your .env if the binding is wired up) to silently drop audit() calls. Useful for environments that should not record audit log noise.

Choosing a log channel

Set artisanpack.core.logging.channel (or the ARTISANPACK_LOG_CHANNEL env var) to the name of a Laravel log channel defined in config/logging.php to route every ArtisanPack UI entry through that channel:

// config/logging.php
'channels' => [
    'artisanpack' => [
        'driver' => 'daily',
        'path'   => storage_path( 'logs/artisanpack.log' ),
        'days'   => 14,
    ],
],

// config/artisanpack.php
'core' => [
    'logging' => [
        'channel' => 'artisanpack',
    ],
],

Leaving the channel as null (the default) falls back to Laravel's default channel.

Custom actor / request resolvers

The default Logger resolves the actor identifier via auth()->id() and the IP via request()->ip(). Console commands, queue workers, and background jobs may not have an authenticated user or an HTTP request available — in those contexts you can construct a logger with your own resolver closures:

use ArtisanPackUI\Core\Logging\Logger;

$log = new Logger(
    package:          'media-library',
    logManager:       app( \Illuminate\Log\LogManager::class ),
    dispatcher:       app( \Illuminate\Contracts\Events\Dispatcher::class ),
    channel:          'artisanpack',
    auditEnabled:     true,
    actorResolver:    fn () => 'cli:thumbnail-generator',
    requestResolver: fn () => gethostbyname( gethostname() ),
);

In ordinary HTTP request handling, prefer the factory — it caches loggers, applies the configured channel + audit settings, and uses the default auth() / request() resolvers.

Facade method map

Facade method Factory method
ArtisanPackLog::make( $package ) LoggerFactory::make()
ArtisanPackLog::has( $package ) LoggerFactory::has()
ArtisanPackLog::forget( ?$package = null ) LoggerFactory::forget()
ArtisanPackLog::all() LoggerFactory::all()