Passed
Push — master ( 849420...11d690 )
by Bastien
02:22
created

DeamonLoggerExtraWebProcessor::__construct()   B

Complexity

Conditions 9
Paths 16

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 14
ccs 10
cts 10
cp 1
rs 8.0555
cc 9
nc 16
nop 1
crap 9
1
<?php
2
3
namespace Deamon\LoggerExtraBundle\Processors\Monolog;
4
5
use Deamon\LoggerExtraBundle\Services\DeamonLoggerExtraContext;
6
use Monolog\Processor\WebProcessor as BaseWebProcessor;
7
use Symfony\Component\HttpFoundation\Request;
8
use Symfony\Component\HttpFoundation\RequestStack;
9
use Symfony\Component\HttpKernel\Event\RequestEvent;
10
use Symfony\Component\HttpKernel\KernelEvents;
11
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
12
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
13
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
14
use Symfony\Component\Security\Core\User\UserInterface;
15
16
class DeamonLoggerExtraWebProcessor extends BaseWebProcessor
17
{
18
    /**
19
     * @var string
20
     */
21
    private $environment = null;
22
23
    /**
24
     * @var DeamonLoggerExtraContext
25
     */
26
    private $loggerExtraContext = null;
27
28
    /**
29
     * @var TokenStorageInterface
30
     */
31
    private $tokenStorage = null;
32
33
    /**
34
     * @var RequestStack
35
     */
36
    private $requestStack = null;
37
38
    /**
39
     * @var array|null
40
     */
41
    private $displayConfig;
42
43
    /**
44
     * @var string
45
     */
46
    private $channelPrefix;
47
48
    /** @var string */
49
    private $userClass;
50
51
    /** @var array */
52
    private $userMethods;
53
54
    /**
55
     * @var array
56
     */
57
    private $record;
58
59 11
    public function __construct(?array $config = null)
60
    {
61 11
        parent::__construct([]);
62 11
        if (!empty($config) && array_key_exists('channel_prefix', $config)) {
63 10
            $this->channelPrefix = $config['channel_prefix'];
64
        }
65 11
        if (!empty($config) && array_key_exists('display', $config)) {
66 10
            $this->displayConfig = $config['display'];
67
        }
68 11
        if (!empty($config) && array_key_exists('user_class', $config)) {
69 10
            $this->userClass = $config['user_class'];
70
        }
71 11
        if (!empty($config) && array_key_exists('user_methods', $config)) {
72 10
            $this->userMethods = $config['user_methods'];
73
        }
74 11
    }
75
76
    /**
77
     * @param array $record
78
     *
79
     * @return array
80
     */
81 11
    public function __invoke(array $record): array
82
    {
83 11
        $this->record = parent::__invoke($record);
84
85 11
        $this->addContextInfo();
86 11
        $this->addRequestInfo();
87 11
        $this->addUserInfo();
88 11
        $this->addChannelInfo();
89
90 11
        return $this->record;
91
    }
92
93
    /**
94
     * Add extra info about the context of the generated log.
95
     */
96 11
    private function addContextInfo(): void
97
    {
98 11
        if (null !== $this->environment) {
99 2
            $this->addInfo('env', $this->environment);
100
        }
101
102 11
        if (null !== $this->loggerExtraContext) {
103 2
            $this->addInfo('locale', $this->loggerExtraContext->getLocale());
104 2
            if ($this->configShowExtraInfo('application_name')) {
105 2
                $this->record['extra']['application'] = $this->loggerExtraContext->getApplicationName();
106
            }
107
        }
108 11
    }
109
110
    /**
111
     * Add extra info about the request generating the log.
112
     */
113 11
    private function addRequestInfo(): void
114
    {
115 11
        if (null !== $this->requestStack) {
116 1
            $request = $this->requestStack->getCurrentRequest();
117 1
            if ($request instanceof Request) {
118 1
                $this->addInfo('url', $request->getRequestUri());
119 1
                $this->addInfo('route', $request->get('_route'));
120 1
                $this->addInfo('user_agent', $request->server->get('HTTP_USER_AGENT'));
121 1
                $this->addInfo('accept_encoding', $request->headers->get('Accept-Encoding'));
122 1
                $this->addInfo('client_ip', $request->getClientIp());
123
            }
124
        }
125 11
    }
126
127
    /**
128
     * Add extra info on the user generating the log.
129
     */
130 11
    private function addUserInfo(): void
131
    {
132 11
        if (!$this->configShowExtraInfo('user')) {
133 7
            return;
134
        }
135
136 4
        if (!class_exists($this->userClass) && !interface_exists($this->userClass)) {
137 1
            return;
138
        }
139
140 3
        if (!$this->tokenStorage instanceof TokenStorageInterface) {
0 ignored issues
show
introduced by
$this->tokenStorage is always a sub-type of Symfony\Component\Securi...e\TokenStorageInterface.
Loading history...
141 1
            return;
142
        }
143
144 2
        $token = $this->tokenStorage->getToken();
145 2
        if ($this->isUserInstanceValid($token) && null !== $user = $token->getUser()) {
146 1
            $this->appendUserMethodInfo($user);
0 ignored issues
show
Bug introduced by
It seems like $user can also be of type string; however, parameter $user of Deamon\LoggerExtraBundle...:appendUserMethodInfo() does only seem to accept Symfony\Component\Security\Core\User\UserInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

146
            $this->appendUserMethodInfo(/** @scrutinizer ignore-type */ $user);
Loading history...
147
        }
148 2
    }
149
150
    /**
151
     * append method result of user object.
152
     *
153
     * @param UserInterface $user
154
     */
155 1
    private function appendUserMethodInfo(UserInterface $user): void
156
    {
157 1
        foreach ($this->userMethods as $name => $method) {
158 1
            if (method_exists($user, $method)) {
159 1
                $this->record['extra'][$name] = $user->$method();
160
            }
161
        }
162 1
    }
163
164
    /**
165
     * Check if passed token is an instance of TokenInterface and an instance of config UserClass.
166
     *
167
     * @param TokenInterface|null $token
168
     *
169
     * @return bool
170
     */
171 2
    private function isUserInstanceValid(?TokenInterface $token): bool
172
    {
173 2
        return $token instanceof TokenInterface && $token->getUser() instanceof $this->userClass;
174
    }
175
176
    /**
177
     * Add channel info to ease the log interpretation.
178
     */
179 11
    private function addChannelInfo(): void
180
    {
181 11
        if (!array_key_exists('global_channel', $this->record['extra'])) {
182 11
            $this->addInfo('global_channel', $this->record['channel']);
183
        }
184
185 11
        if ($this->channelPrefix !== null && substr($this->record['channel'], 0, strlen($this->channelPrefix)) !== $this->channelPrefix) {
186 2
            $this->record['channel'] = sprintf('%s.%s', $this->channelPrefix, $this->record['channel']);
187
        }
188 11
    }
189
190
    /**
191
     * Add the extra info if configured to.
192
     *
193
     * @param string $key
194
     * @param mixed  $value
195
     */
196 11
    private function addInfo(string $key, $value): void
197
    {
198 11
        if ($this->configShowExtraInfo($key) && $value !== null) {
199 6
            $this->record['extra'][$key] = $value;
200
        }
201 11
    }
202
203
    /**
204
     * Tells if the config to display the extra info is enabled or not.
205
     *
206
     * @param string $extraInfo
207
     *
208
     * @return bool
209
     */
210 11
    private function configShowExtraInfo(string $extraInfo): bool
211
    {
212 11
        return isset($this->displayConfig[$extraInfo]) && $this->displayConfig[$extraInfo];
213
    }
214
215
    public function onKernelRequest(RequestEvent $event)
216
    {
217
        if ($event->isMasterRequest()) {
218
            $this->serverData = $event->getRequest()->server->all();
219
            $this->serverData['REMOTE_ADDR'] = $event->getRequest()->getClientIp();
220
        }
221
    }
222
223
    public static function getSubscribedEvents()
224
    {
225
        return [
226
            KernelEvents::REQUEST => ['onKernelRequest', 4096],
227
        ];
228
    }
229
230
    /**
231
     * @param DeamonLoggerExtraContext $loggerExtraContext
232
     */
233 2
    public function setLoggerExtraContext(DeamonLoggerExtraContext $loggerExtraContext): void
234
    {
235 2
        $this->loggerExtraContext = $loggerExtraContext;
236 2
    }
237
238
    /**
239
     * @param string $environment
240
     */
241 2
    public function setEnvironment(string $environment): void
242
    {
243 2
        $this->environment = $environment;
244 2
    }
245
246
    /**
247
     * @param TokenStorageInterface $tokenStorage
248
     */
249 2
    public function setTokenStorage(TokenStorageInterface $tokenStorage): void
250
    {
251 2
        $this->tokenStorage = $tokenStorage;
252 2
    }
253
254
    /**
255
     * @param RequestStack $requestStack
256
     */
257 1
    public function setRequestStack(RequestStack $requestStack): void
258
    {
259 1
        $this->requestStack = $requestStack;
260 1
    }
261
}
262