Passed
Push — master ( 04a456...6c0186 )
by Robin
15:03 queued 11s
created

Log::notice()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud, Inc.
7
 *
8
 * @author Arthur Schiwon <[email protected]>
9
 * @author Bart Visscher <[email protected]>
10
 * @author Bernhard Posselt <[email protected]>
11
 * @author Christoph Wurst <[email protected]>
12
 * @author Joas Schilling <[email protected]>
13
 * @author Julius Härtl <[email protected]>
14
 * @author Morris Jobke <[email protected]>
15
 * @author Olivier Paroz <[email protected]>
16
 * @author Robin Appelman <[email protected]>
17
 * @author Roeland Jago Douma <[email protected]>
18
 * @author Thomas Müller <[email protected]>
19
 * @author Victor Dubiniuk <[email protected]>
20
 *
21
 * @license AGPL-3.0
22
 *
23
 * This code is free software: you can redistribute it and/or modify
24
 * it under the terms of the GNU Affero General Public License, version 3,
25
 * as published by the Free Software Foundation.
26
 *
27
 * This program is distributed in the hope that it will be useful,
28
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30
 * GNU Affero General Public License for more details.
31
 *
32
 * You should have received a copy of the GNU Affero General Public License, version 3,
33
 * along with this program. If not, see <http://www.gnu.org/licenses/>
34
 *
35
 */
36
namespace OC;
37
38
use Nextcloud\LogNormalizer\Normalizer;
39
use OCP\Log\IDataLogger;
40
use function array_merge;
41
use OC\Log\ExceptionSerializer;
42
use OCP\ILogger;
43
use OCP\Log\IFileBased;
44
use OCP\Log\IWriter;
45
use OCP\Support\CrashReport\IRegistry;
46
use function strtr;
47
48
/**
49
 * logging utilities
50
 *
51
 * This is a stand in, this should be replaced by a Psr\Log\LoggerInterface
52
 * compatible logger. See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
53
 * for the full interface specification.
54
 *
55
 * MonoLog is an example implementing this interface.
56
 */
57
class Log implements ILogger, IDataLogger {
0 ignored issues
show
Deprecated Code introduced by
The interface OCP\ILogger has been deprecated: 20.0.0 use the PSR-3 logger \Psr\Log\LoggerInterface ( Ignorable by Annotation )

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

57
class Log implements /** @scrutinizer ignore-deprecated */ ILogger, IDataLogger {

This interface has been deprecated. The supplier of the interface has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.

Loading history...
58
59
	/** @var IWriter */
60
	private $logger;
61
62
	/** @var SystemConfig */
63
	private $config;
64
65
	/** @var boolean|null cache the result of the log condition check for the request */
66
	private $logConditionSatisfied = null;
67
68
	/** @var Normalizer */
69
	private $normalizer;
70
71
	/** @var IRegistry */
72
	private $crashReporters;
73
74
	/**
75
	 * @param IWriter $logger The logger that should be used
76
	 * @param SystemConfig $config the system config object
77
	 * @param Normalizer|null $normalizer
78
	 * @param IRegistry|null $registry
79
	 */
80
	public function __construct(IWriter $logger, SystemConfig $config = null, $normalizer = null, IRegistry $registry = null) {
81
		// FIXME: Add this for backwards compatibility, should be fixed at some point probably
82
		if ($config === null) {
83
			$config = \OC::$server->getSystemConfig();
84
		}
85
86
		$this->config = $config;
87
		$this->logger = $logger;
88
		if ($normalizer === null) {
89
			$this->normalizer = new Normalizer();
90
		} else {
91
			$this->normalizer = $normalizer;
92
		}
93
		$this->crashReporters = $registry;
94
	}
95
96
	/**
97
	 * System is unusable.
98
	 *
99
	 * @param string $message
100
	 * @param array $context
101
	 * @return void
102
	 */
103
	public function emergency(string $message, array $context = []) {
104
		$this->log(ILogger::FATAL, $message, $context);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::FATAL has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

104
		$this->log(/** @scrutinizer ignore-deprecated */ ILogger::FATAL, $message, $context);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
105
	}
106
107
	/**
108
	 * Action must be taken immediately.
109
	 *
110
	 * Example: Entire website down, database unavailable, etc. This should
111
	 * trigger the SMS alerts and wake you up.
112
	 *
113
	 * @param string $message
114
	 * @param array $context
115
	 * @return void
116
	 */
117
	public function alert(string $message, array $context = []) {
118
		$this->log(ILogger::ERROR, $message, $context);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

118
		$this->log(/** @scrutinizer ignore-deprecated */ ILogger::ERROR, $message, $context);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
119
	}
120
121
	/**
122
	 * Critical conditions.
123
	 *
124
	 * Example: Application component unavailable, unexpected exception.
125
	 *
126
	 * @param string $message
127
	 * @param array $context
128
	 * @return void
129
	 */
130
	public function critical(string $message, array $context = []) {
131
		$this->log(ILogger::ERROR, $message, $context);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

131
		$this->log(/** @scrutinizer ignore-deprecated */ ILogger::ERROR, $message, $context);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
132
	}
133
134
	/**
135
	 * Runtime errors that do not require immediate action but should typically
136
	 * be logged and monitored.
137
	 *
138
	 * @param string $message
139
	 * @param array $context
140
	 * @return void
141
	 */
142
	public function error(string $message, array $context = []) {
143
		$this->log(ILogger::ERROR, $message, $context);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

143
		$this->log(/** @scrutinizer ignore-deprecated */ ILogger::ERROR, $message, $context);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
144
	}
145
146
	/**
147
	 * Exceptional occurrences that are not errors.
148
	 *
149
	 * Example: Use of deprecated APIs, poor use of an API, undesirable things
150
	 * that are not necessarily wrong.
151
	 *
152
	 * @param string $message
153
	 * @param array $context
154
	 * @return void
155
	 */
156
	public function warning(string $message, array $context = []) {
157
		$this->log(ILogger::WARN, $message, $context);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::WARN has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

157
		$this->log(/** @scrutinizer ignore-deprecated */ ILogger::WARN, $message, $context);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
158
	}
159
160
	/**
161
	 * Normal but significant events.
162
	 *
163
	 * @param string $message
164
	 * @param array $context
165
	 * @return void
166
	 */
167
	public function notice(string $message, array $context = []) {
168
		$this->log(ILogger::INFO, $message, $context);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::INFO has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

168
		$this->log(/** @scrutinizer ignore-deprecated */ ILogger::INFO, $message, $context);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
169
	}
170
171
	/**
172
	 * Interesting events.
173
	 *
174
	 * Example: User logs in, SQL logs.
175
	 *
176
	 * @param string $message
177
	 * @param array $context
178
	 * @return void
179
	 */
180
	public function info(string $message, array $context = []) {
181
		$this->log(ILogger::INFO, $message, $context);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::INFO has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

181
		$this->log(/** @scrutinizer ignore-deprecated */ ILogger::INFO, $message, $context);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
182
	}
183
184
	/**
185
	 * Detailed debug information.
186
	 *
187
	 * @param string $message
188
	 * @param array $context
189
	 * @return void
190
	 */
191
	public function debug(string $message, array $context = []) {
192
		$this->log(ILogger::DEBUG, $message, $context);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::DEBUG has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

192
		$this->log(/** @scrutinizer ignore-deprecated */ ILogger::DEBUG, $message, $context);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
193
	}
194
195
196
	/**
197
	 * Logs with an arbitrary level.
198
	 *
199
	 * @param int $level
200
	 * @param string $message
201
	 * @param array $context
202
	 * @return void
203
	 */
204
	public function log(int $level, string $message, array $context = []) {
205
		$minLevel = $this->getLogLevel($context);
206
207
		array_walk($context, [$this->normalizer, 'format']);
208
209
		$app = $context['app'] ?? 'no app in context';
210
		$message = $this->interpolateMessage($context, $message);
211
212
		try {
213
			if ($level >= $minLevel) {
214
				$this->writeLog($app, $message, $level);
215
216
				if ($this->crashReporters !== null) {
217
					$messageContext = array_merge(
218
						$context,
219
						[
220
							'level' => $level
221
						]
222
					);
223
					$this->crashReporters->delegateMessage($message, $messageContext);
224
				}
225
			} else {
226
				if ($this->crashReporters !== null) {
227
					$this->crashReporters->delegateBreadcrumb($message, 'log', $context);
228
				}
229
			}
230
		} catch (\Throwable $e) {
231
			// make sure we dont hard crash if logging fails
232
		}
233
	}
234
235
	public function getLogLevel($context) {
236
		$logCondition = $this->config->getValue('log.condition', []);
237
238
		/**
239
		 * check for a special log condition - this enables an increased log on
240
		 * a per request/user base
241
		 */
242
		if ($this->logConditionSatisfied === null) {
243
			// default to false to just process this once per request
244
			$this->logConditionSatisfied = false;
245
			if (!empty($logCondition)) {
246
247
				// check for secret token in the request
248
				if (isset($logCondition['shared_secret'])) {
249
					$request = \OC::$server->getRequest();
250
251
					if ($request->getMethod() === 'PUT' &&
252
						strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false &&
253
						strpos($request->getHeader('Content-Type'), 'application/json') === false) {
254
						$logSecretRequest = '';
255
					} else {
256
						$logSecretRequest = $request->getParam('log_secret', '');
257
					}
258
259
					// if token is found in the request change set the log condition to satisfied
260
					if ($request && hash_equals($logCondition['shared_secret'], $logSecretRequest)) {
261
						$this->logConditionSatisfied = true;
262
					}
263
				}
264
265
				// check for user
266
				if (isset($logCondition['users'])) {
267
					$user = \OC::$server->getUserSession()->getUser();
268
269
					// if the user matches set the log condition to satisfied
270
					if ($user !== null && in_array($user->getUID(), $logCondition['users'], true)) {
271
						$this->logConditionSatisfied = true;
272
					}
273
				}
274
			}
275
		}
276
277
		// if log condition is satisfied change the required log level to DEBUG
278
		if ($this->logConditionSatisfied) {
279
			return ILogger::DEBUG;
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::DEBUG has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

279
			return /** @scrutinizer ignore-deprecated */ ILogger::DEBUG;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
280
		}
281
282
		if (isset($context['app'])) {
283
			$app = $context['app'];
284
285
			/**
286
			 * check log condition based on the context of each log message
287
			 * once this is met -> change the required log level to debug
288
			 */
289
			if (!empty($logCondition)
290
				&& isset($logCondition['apps'])
291
				&& in_array($app, $logCondition['apps'], true)) {
292
				return ILogger::DEBUG;
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::DEBUG has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

292
				return /** @scrutinizer ignore-deprecated */ ILogger::DEBUG;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
293
			}
294
		}
295
296
		return min($this->config->getValue('loglevel', ILogger::WARN), ILogger::FATAL);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::FATAL has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

296
		return min($this->config->getValue('loglevel', ILogger::WARN), /** @scrutinizer ignore-deprecated */ ILogger::FATAL);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
Deprecated Code introduced by
The constant OCP\ILogger::WARN has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

296
		return min($this->config->getValue('loglevel', /** @scrutinizer ignore-deprecated */ ILogger::WARN), ILogger::FATAL);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
297
	}
298
299
	/**
300
	 * Logs an exception very detailed
301
	 *
302
	 * @param \Exception|\Throwable $exception
303
	 * @param array $context
304
	 * @return void
305
	 * @since 8.2.0
306
	 */
307
	public function logException(\Throwable $exception, array $context = []) {
308
		$app = $context['app'] ?? 'no app in context';
309
		$level = $context['level'] ?? ILogger::ERROR;
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

309
		$level = $context['level'] ?? /** @scrutinizer ignore-deprecated */ ILogger::ERROR;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
310
311
		// if an error is raised before the autoloader is properly setup, we can't serialize exceptions
312
		try {
313
			$serializer = new ExceptionSerializer($this->config);
314
		} catch (\Throwable $e) {
315
			$this->error("Failed to load ExceptionSerializer serializer while trying to log " . $exception->getMessage());
316
			return;
317
		}
318
		$data = $serializer->serializeException($exception);
319
		$data['CustomMessage'] = $this->interpolateMessage($context, $context['message'] ?? '--');
320
321
		$minLevel = $this->getLogLevel($context);
322
323
		array_walk($context, [$this->normalizer, 'format']);
324
325
		try {
326
			if ($level >= $minLevel) {
327
				if (!$this->logger instanceof IFileBased) {
328
					$data = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_SLASHES);
329
				}
330
				$this->writeLog($app, $data, $level);
331
			}
332
333
			$context['level'] = $level;
334
			if (!is_null($this->crashReporters)) {
335
				$this->crashReporters->delegateReport($exception, $context);
336
			}
337
		} catch (\Throwable $e) {
338
			// make sure we dont hard crash if logging fails
339
		}
340
	}
341
342
	public function logData(string $message, array $data, array $context = []): void {
343
		$app = $context['app'] ?? 'no app in context';
344
		$level = $context['level'] ?? ILogger::ERROR;
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

344
		$level = $context['level'] ?? /** @scrutinizer ignore-deprecated */ ILogger::ERROR;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
345
346
		$minLevel = $this->getLogLevel($context);
347
348
		array_walk($context, [$this->normalizer, 'format']);
349
350
		try {
351
			if ($level >= $minLevel) {
352
				$data['message'] = $message;
353
				if (!$this->logger instanceof IFileBased) {
354
					$data = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_SLASHES);
355
				}
356
				$this->writeLog($app, $data, $level);
357
			}
358
359
			$context['level'] = $level;
360
		} catch (\Throwable $e) {
361
			// make sure we dont hard crash if logging fails
362
		}
363
	}
364
365
	/**
366
	 * @param string $app
367
	 * @param string|array $entry
368
	 * @param int $level
369
	 */
370
	protected function writeLog(string $app, $entry, int $level) {
371
		$this->logger->write($app, $entry, $level);
372
	}
373
374
	public function getLogPath():string {
375
		if ($this->logger instanceof IFileBased) {
376
			return $this->logger->getLogFilePath();
377
		}
378
		throw new \RuntimeException('Log implementation has no path');
379
	}
380
381
	/**
382
	 * Interpolate $message as defined in PSR-3
383
	 *
384
	 * @param array $context
385
	 * @param string $message
386
	 *
387
	 * @return string
388
	 */
389
	private function interpolateMessage(array $context, string $message): string {
390
		$replace = [];
391
		foreach ($context as $key => $val) {
392
			$replace['{' . $key . '}'] = $val;
393
		}
394
		return strtr($message, $replace);
395
	}
396
}
397