Completed
Push — in-memory-cache2 ( 6ce751...080bd3 )
by André
32:37
created

PersistenceLogger::logCall()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the Persistence Cache SPI logger class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
declare(strict_types=1);
10
11
namespace eZ\Publish\Core\Persistence\Cache;
12
13
/**
14
 * Log un-cached & cached use of SPI Persistence.
15
 */
16
class PersistenceLogger
17
{
18
    const NAME = 'PersistenceLogger';
19
20
    /**
21
     * @var int[]
22
     */
23
    protected $stats = [
24
        'calls' => 0,
25
        'misses' => 0,
26
        'hits' => 0,
27
        'memory' => 0,
28
    ];
29
30
    /**
31
     * @var bool
32
     */
33
    protected $logCalls = true;
34
35
    /**
36
     * @var array
37
     */
38
    protected $calls = [];
39
40
    /**
41
     * @var array
42
     */
43
    protected $cached = [];
44
45
    /**
46
     * @var array
47
     */
48
    protected $unCachedHandlers = [];
49
50
    /**
51
     * @param bool $logCalls Flag to enable logging of calls or not, provides extra debug info about calls made to SPI
52
     *                       level, including where they come form. However this uses quite a bit of memory.
53
     */
54
    public function __construct(bool $logCalls = true)
55
    {
56
        $this->logCalls = $logCalls;
57
    }
58
59
    /**
60
     * Log uncached SPI calls with method name and arguments.
61
     *
62
     * NOTE: As of 7.5 this method is meant for logging calls to uncached spi method calls,
63
     *       for cache miss calls to cached SPI methods migrate to use {@see logCacheMiss()}.
64
     *
65
     * @param string $method
66
     * @param array $arguments
67
     */
68
    public function logCall(string $method, array $arguments = []): void
69
    {
70
        $this->stats['calls']++;
71
        if (!$this->logCalls) {
72
            return;
73
        }
74
75
        $this->calls[] = $this->getCacheCallData(
76
            $method,
77
            $arguments,
78
            \array_slice(
79
                \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 7),
80
                2
81
            )
82
        );
83
    }
84
85
    /**
86
     * Log Cache miss, gets info it needs by backtrace if needed.
87
     *
88
     * @since 7.5
89
     *
90
     * @param array $arguments
91
     * @param int $traceOffset
92
     */
93
    public function logCacheMiss(array $arguments = [], int $traceOffset = 2): void
94
    {
95
        $this->stats['misses']++;
96
        if (!$this->logCalls) {
97
            return;
98
        }
99
100
        $trace = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 7);
101
        $this->cached[] = $this->getCacheCallData(
102
            $trace[$traceOffset -1]['function'],
103
            $arguments,
104
            \array_slice($trace, $traceOffset),
105
            false
106
        );
107
    }
108
109
    /**
110
     * Log a Cache hit, gets info it needs by backtrace if needed.
111
     *
112
     * @since 7.5
113
     *
114
     * @param array $arguments
115
     * @param int $traceOffset
116
     * @param bool $inMemory Denotes is cache hit was from memory (php variable), as opposed to from cache pool which
117
     *                       is usually disk or remote cache service.
118
     */
119
    public function logCacheHit(array $arguments = [], int $traceOffset = 2, bool $inMemory = false): void
120
    {
121
        if ($inMemory) {
122
            $this->stats['memory']++;
123
            return;
124
        }
125
126
        $this->stats['hits']++;
127
        if (!$this->logCalls) {
128
            return;
129
        }
130
131
        // @todo Check memory usage
132
        $trace = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 7);
133
        $this->cached[] = $this->getCacheCallData(
134
            $trace[$traceOffset -1]['class'] . '::' . $trace[$traceOffset -1]['function'],
135
            $arguments,
136
            \array_slice($trace, $traceOffset),
137
            true
138
        );
139
    }
140
141
    private function getCacheCallData($method, array $arguments, array $trimmedBacktrace, bool $hit = false): array
142
    {
143
        $callData = [
144
            'method' => $method,
145
            'arguments' => $arguments,
146
            'trace' => $this->getSimpleCallTrace($trimmedBacktrace),
147
            'hit' => $hit,
148
        ];
149
150
        return $callData;
151
    }
152
153
    private function getSimpleCallTrace(array $backtrace): array
154
    {
155
        $calls = [];
156
        foreach ($backtrace as $call) {
157
            if (!isset($call['class']) || strpos($call['class'], '\\') === false) {
158
                // skip if class has no namespace (Symfony lazy proxy or plain function)
159
                continue;
160
            }
161
162
            $calls[] = $call['class'] . $call['type'] . $call['function'] . '()';
163
164
            // Break out as soon as we have listed 1 class outside of kernel
165
            if (strpos($call['class'], 'eZ\\Publish\\Core\\') !== 0) {
166
                break;
167
            }
168
        }
169
170
        return $calls;
171
    }
172
173
    /**
174
     * Log un-cached handler being loaded.
175
     *
176
     * @param string $handler
177
     */
178
    public function logUnCachedHandler(string $handler): void
179
    {
180
        if (!isset($this->unCachedHandlers[$handler])) {
181
            $this->unCachedHandlers[$handler] = 0;
182
        }
183
        ++$this->unCachedHandlers[$handler];
184
    }
185
186
    public function getName(): string
187
    {
188
        return self::NAME;
189
    }
190
191
    /**
192
     * Counts the total of spi calls (cache misses and uncached calls)
193
     *
194
     * @deprecated Since 7.5, use getStats().
195
     */
196
    public function getCount(): int
197
    {
198
        return $this->stats['calls'];
199
    }
200
201
    /**
202
     * Get stats (calls/misses/hits/memory).
203
     *
204
     * @since 7.5
205
     */
206
    public function getStats(): array
207
    {
208
        return $this->stats;
209
    }
210
211
    public function isCallsLoggingEnabled(): bool
212
    {
213
        return $this->logCalls;
214
    }
215
216
    public function getCalls(): array
217
    {
218
        return $this->calls;
219
    }
220
221
    public function getCached(): array
222
    {
223
        return $this->cached;
224
    }
225
226
    public function getLoadedUnCachedHandlers(): array
227
    {
228
        return $this->unCachedHandlers;
229
    }
230
}
231