Passed
Push — semantic-logger ( 82e17c )
by Akihito
03:03
created

AutoPersistSemanticInvoker::invoke()   A

Complexity

Conditions 2
Paths 5

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 14
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 23
rs 9.7998
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource\SemanticLog;
6
7
use BEAR\Resource\AbstractRequest;
8
use BEAR\Resource\InvokerInterface;
9
use BEAR\Resource\ResourceObject;
10
use Koriym\SemanticLogger\SemanticLoggerInterface;
11
use Override;
12
use Ray\Di\Di\Named;
13
use Throwable;
14
15
use function date;
16
use function file_put_contents;
17
use function json_encode;
18
use function sprintf;
19
use function sys_get_temp_dir;
20
use function uniqid;
21
22
use const JSON_PRETTY_PRINT;
23
24
/**
25
 * SemanticInvoker with automatic log persistence on close
26
 * 
27
 * Extends the standard SemanticInvoker to automatically write log files
28
 * immediately after each operation completes (either successfully or with error).
29
 * This ensures logs are persisted without relying on shutdown functions.
30
 */
31
final class AutoPersistSemanticInvoker implements InvokerInterface
32
{
33
    public function __construct(
34
        #[Named('original')]
35
        private InvokerInterface $invoker,
36
        private SemanticLoggerInterface $logger,
37
        private ContextFactoryInterface $factory,
38
        #[Named('log_directory')]
39
        private string $logDirectory = '',
40
    ) {
41
        // Use system temp directory if no directory specified
42
        if ($this->logDirectory === '') {
43
            $this->logDirectory = sys_get_temp_dir();
44
        }
45
    }
46
47
    #[Override]
48
    public function invoke(AbstractRequest $request): ResourceObject
49
    {
50
        $openContext = $this->factory->createOpenContext($request);
51
        $openId = $this->logger->open($openContext);
52
53
        try {
54
            $result = $this->invoker->invoke($request);
55
            $closeContext = $this->factory->createCompleteContext($result, $openContext);
56
            $this->logger->close($closeContext, $openId);
57
            
58
            // Immediately persist the completed log
59
            $this->persistLog('complete');
60
            
61
            return $result;
62
        } catch (Throwable $e) {
63
            $errorContext = $this->factory->createErrorContext($e);
64
            $this->logger->close($errorContext, $openId);
65
            
66
            // Immediately persist the error log
67
            $this->persistLog('error');
68
            
69
            throw $e;
70
        }
71
    }
72
73
    private function persistLog(string $type): void
74
    {
75
        $logJson = $this->logger->flush();
76
        $jsonContent = json_encode($logJson, JSON_PRETTY_PRINT);
77
        if ($jsonContent === false) {
78
            return; // Skip if JSON encoding fails
79
        }
80
81
        $timestamp = date('Y-m-d_H-i-s');
82
        $uniqueId = uniqid();
83
        $filename = sprintf(
84
            '%s/semantic-log_%s_%s_%s.json',
85
            $this->logDirectory,
86
            $type,
87
            $timestamp,
88
            $uniqueId,
89
        );
90
91
        file_put_contents($filename, $jsonContent);
92
    }
93
}