Initial commit

This commit is contained in:
Tommy Stigen Olsen 2024-03-20 13:57:55 +01:00
commit 6f4cc23526
No known key found for this signature in database
6 changed files with 1507 additions and 0 deletions

19
composer.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "okawari/job-logging",
"description": "Simple dependency for logging information about jobs",
"type": "library",
"require": {
"illuminate/support": "^11.0"
},
"autoload": {
"psr-4": {
"Okawari\\JobLogging\\": "src/"
}
},
"authors": [
{
"name": "Tommy Stigen Olsen",
"email": "tommysolsen@gmail.com"
}
]
}

1263
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

12
job-logging.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="Okawari\JobLogging" />
<excludeFolder url="file://$MODULE_DIR$/vendor" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

65
src/JobLog.php Normal file
View File

@ -0,0 +1,65 @@
<?php
namespace Okawari\JobLogging;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* App\Models\JobLog
*
* @property int $id
* @property string|null $job_id
* @property string $class
* @property string $name
* @property string|null $owner_type
* @property int|null $owner_id
* @property bool|null $success
* @property string $output
* @property \Illuminate\Support\Carbon|null $started_at
* @property \Illuminate\Support\Carbon|null $ended_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read Model|\Eloquent $owner
* @method static \Illuminate\Database\Eloquent\Builder|JobLog newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|JobLog newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|JobLog query()
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereClass($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereEndedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereJobId($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereOutput($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereOwnerId($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereOwnerType($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereStartedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereSuccess($value)
* @method static \Illuminate\Database\Eloquent\Builder|JobLog whereUpdatedAt($value)
* @mixin \Eloquent
*/
class JobLog extends Model
{
use HasFactory;
protected $casts = [
'started_at' => 'datetime',
'ended_at' => 'datetime',
];
protected $fillable = [
'job_id',
'class',
'name',
'owner_type',
'owner_id',
'output',
'started_at',
'ended_at',
'success'
];
public function owner(): MorphTo {
return $this->morphTo();
}
}

123
src/JobLogProvider.php Normal file
View File

@ -0,0 +1,123 @@
<?php
namespace Okawari\JobLogging;
use App\Contracts\NamedJob;
use App\Contracts\OwnedJob;
use App\Models\JobLog;
use App\Services\JobLogger;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\Jobs\SyncJob;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
class JobLogProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
$driver = Log::getDefaultDriver();
Event::listen(function (JobProcessing $event) {
/** @var \Monolog\Logger $logger */
$logger = Log::getLogger();
$job = $event->job;
$owner = $job instanceof OwnedJob ? $job->getOwner() : null;
$name = $job instanceof NamedJob ? $job->getJobLogName() : $event->job->getName();
if ($job instanceof SyncJob) {
$job = unserialize(data_get(json_decode($job->getRawBody()), 'data.command'));
$owner = $job instanceof OwnedJob ? $job->getOwner() : $owner;
$name = $job instanceof NamedJob ? $job->getJobLogName() : $name;
}
$jobLog = app(JobLog::class)::create([
'job_id' => $event->job->getJobId() ?: '',
'class' => get_class($event->job),
'name' => $name,
'owner_type' => $owner ? get_class($owner) : null,
'owner_id' => $owner?->id,
'output' => '',
'started_at' => now(),
]);
$logger->pushProcessor(new JobLogger($jobLog));
});
Event::listen(function (JobFailed $event) {
/** @var \Monolog\Logger $logger */
$jobLogger = $this->getJobLogger();
if (!$jobLogger) {
return;
}
$job = $event->job;
$logLine = $jobLogger->log;
$owner = $job instanceof OwnedJob ? $job->getOwner() : null;
$name = $job instanceof NamedJob ? $job->getJobLogName() : $event->job->getName();
if ($owner) $logLine->owner()->associate($owner);
if ($name) $logLine->name = $name ?: $logLine->name;
$logLine->output .= "[EXCEPTION] " . now()->toIso8601ZuluString() . ": {$event->exception->getMessage()}";
$success = $event->job?->hasFailed() === false;
$logLine->success = $success;
$logLine->ended_at = now();
$logLine->save();
});
Event::listen(function (JobProcessed|JobFailed $event) {
try {
/** @var \Monolog\Logger $logger */
$jobLogger = $this->getJobLogger();
if (!$jobLogger) {
return;
}
$job = $event->job;
$logLine = $jobLogger->log;
$owner = $job instanceof OwnedJob ? $job->getOwner() : null;
$name = $job instanceof NamedJob ? $job->getJobLogName() : $event->job->getName();
if ($owner) $logLine->owner()->associate($owner);
if ($name) $logLine->name = $name ?: $logLine->name;
$logLine->success = !$event->job->hasFailed();
$logLine->ended_at = now();
$logLine->save();
} catch (\Throwable $t) {
dd($t, debug_backtrace());
}
});
}
/**
* Bootstrap services.
*/
public function boot(): void
{
}
/**
* @return array
*/
function getJobLogger(): ?JobLogger
{
$logger = Log::driver()->getLogger();
$jobLogger = $logger->popProcessor();
if ($jobLogger instanceof JobLogger) {
return $jobLogger;
} else {
$logger->pushProcessor($jobLogger);
return null;
}
}
}

25
src/JobProcessor.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace Okawari\JobLogging;
use App\Models\JobLog;
use Monolog\LogRecord;
use Monolog\Processor\ProcessorInterface;
class JobProcessor implements ProcessorInterface
{
public function __construct(public JobLog $log)
{
}
public function __invoke(LogRecord $record)
{
$line = "[{$record->level->getName()}] $record->datetime: $record->message";
$this->log->output = "{$this->log->output}$line\r\n";
return $record;
}
}