Completed
Pull Request — master (#9177)
by Robin
33:35 queued 13:32
created
lib/private/Log.php 1 patch
Indentation   +290 added lines, -290 removed lines patch added patch discarded remove patch
@@ -54,294 +54,294 @@
 block discarded – undo
54 54
  */
55 55
 class Log implements ILogger {
56 56
 
57
-	/** @var string */
58
-	private $logger;
59
-
60
-	/** @var SystemConfig */
61
-	private $config;
62
-
63
-	/** @var boolean|null cache the result of the log condition check for the request */
64
-	private $logConditionSatisfied = null;
65
-
66
-	/** @var Normalizer */
67
-	private $normalizer;
68
-
69
-	/** @var IRegistry */
70
-	private $crashReporters;
71
-
72
-	/**
73
-	 * @param string $logger The logger that should be used
74
-	 * @param SystemConfig $config the system config object
75
-	 * @param Normalizer|null $normalizer
76
-	 * @param IRegistry|null $registry
77
-	 */
78
-	public function __construct($logger = null, SystemConfig $config = null, $normalizer = null, IRegistry $registry = null) {
79
-		// FIXME: Add this for backwards compatibility, should be fixed at some point probably
80
-		if ($config === null) {
81
-			$config = \OC::$server->getSystemConfig();
82
-		}
83
-
84
-		$this->config = $config;
85
-
86
-		// FIXME: Add this for backwards compatibility, should be fixed at some point probably
87
-		if ($logger === null) {
88
-			$logType = $this->config->getValue('log_type', 'file');
89
-			$this->logger = static::getLogClass($logType);
90
-			call_user_func([$this->logger, 'init']);
91
-		} else {
92
-			$this->logger = $logger;
93
-		}
94
-		if ($normalizer === null) {
95
-			$this->normalizer = new Normalizer();
96
-		} else {
97
-			$this->normalizer = $normalizer;
98
-		}
99
-		$this->crashReporters = $registry;
100
-	}
101
-
102
-	/**
103
-	 * System is unusable.
104
-	 *
105
-	 * @param string $message
106
-	 * @param array $context
107
-	 * @return void
108
-	 */
109
-	public function emergency(string $message, array $context = []) {
110
-		$this->log(Util::FATAL, $message, $context);
111
-	}
112
-
113
-	/**
114
-	 * Action must be taken immediately.
115
-	 *
116
-	 * Example: Entire website down, database unavailable, etc. This should
117
-	 * trigger the SMS alerts and wake you up.
118
-	 *
119
-	 * @param string $message
120
-	 * @param array $context
121
-	 * @return void
122
-	 */
123
-	public function alert(string $message, array $context = []) {
124
-		$this->log(Util::ERROR, $message, $context);
125
-	}
126
-
127
-	/**
128
-	 * Critical conditions.
129
-	 *
130
-	 * Example: Application component unavailable, unexpected exception.
131
-	 *
132
-	 * @param string $message
133
-	 * @param array $context
134
-	 * @return void
135
-	 */
136
-	public function critical(string $message, array $context = []) {
137
-		$this->log(Util::ERROR, $message, $context);
138
-	}
139
-
140
-	/**
141
-	 * Runtime errors that do not require immediate action but should typically
142
-	 * be logged and monitored.
143
-	 *
144
-	 * @param string $message
145
-	 * @param array $context
146
-	 * @return void
147
-	 */
148
-	public function error(string $message, array $context = []) {
149
-		$this->log(Util::ERROR, $message, $context);
150
-	}
151
-
152
-	/**
153
-	 * Exceptional occurrences that are not errors.
154
-	 *
155
-	 * Example: Use of deprecated APIs, poor use of an API, undesirable things
156
-	 * that are not necessarily wrong.
157
-	 *
158
-	 * @param string $message
159
-	 * @param array $context
160
-	 * @return void
161
-	 */
162
-	public function warning(string $message, array $context = []) {
163
-		$this->log(Util::WARN, $message, $context);
164
-	}
165
-
166
-	/**
167
-	 * Normal but significant events.
168
-	 *
169
-	 * @param string $message
170
-	 * @param array $context
171
-	 * @return void
172
-	 */
173
-	public function notice(string $message, array $context = []) {
174
-		$this->log(Util::INFO, $message, $context);
175
-	}
176
-
177
-	/**
178
-	 * Interesting events.
179
-	 *
180
-	 * Example: User logs in, SQL logs.
181
-	 *
182
-	 * @param string $message
183
-	 * @param array $context
184
-	 * @return void
185
-	 */
186
-	public function info(string $message, array $context = []) {
187
-		$this->log(Util::INFO, $message, $context);
188
-	}
189
-
190
-	/**
191
-	 * Detailed debug information.
192
-	 *
193
-	 * @param string $message
194
-	 * @param array $context
195
-	 * @return void
196
-	 */
197
-	public function debug(string $message, array $context = []) {
198
-		$this->log(Util::DEBUG, $message, $context);
199
-	}
200
-
201
-
202
-	/**
203
-	 * Logs with an arbitrary level.
204
-	 *
205
-	 * @param int $level
206
-	 * @param string $message
207
-	 * @param array $context
208
-	 * @return void
209
-	 */
210
-	public function log(int $level, string $message, array $context = []) {
211
-		$minLevel = $this->getLogLevel($context);
212
-
213
-		array_walk($context, [$this->normalizer, 'format']);
214
-
215
-		$app = $context['app'] ?? 'no app in context';
216
-
217
-		// interpolate $message as defined in PSR-3
218
-		$replace = [];
219
-		foreach ($context as $key => $val) {
220
-			$replace['{' . $key . '}'] = $val;
221
-		}
222
-		$message = strtr($message, $replace);
223
-
224
-		if ($level >= $minLevel) {
225
-			$this->writeLog($app, $message, $level);
226
-		}
227
-	}
228
-
229
-	private function getLogLevel($context) {
230
-		/**
231
-		 * check for a special log condition - this enables an increased log on
232
-		 * a per request/user base
233
-		 */
234
-		if ($this->logConditionSatisfied === null) {
235
-			// default to false to just process this once per request
236
-			$this->logConditionSatisfied = false;
237
-			if (!empty($logCondition)) {
238
-
239
-				// check for secret token in the request
240
-				if (isset($logCondition['shared_secret'])) {
241
-					$request = \OC::$server->getRequest();
242
-
243
-					// if token is found in the request change set the log condition to satisfied
244
-					if ($request && hash_equals($logCondition['shared_secret'], $request->getParam('log_secret', ''))) {
245
-						$this->logConditionSatisfied = true;
246
-					}
247
-				}
248
-
249
-				// check for user
250
-				if (isset($logCondition['users'])) {
251
-					$user = \OC::$server->getUserSession()->getUser();
252
-
253
-					// if the user matches set the log condition to satisfied
254
-					if ($user !== null && in_array($user->getUID(), $logCondition['users'], true)) {
255
-						$this->logConditionSatisfied = true;
256
-					}
257
-				}
258
-			}
259
-		}
260
-
261
-		// if log condition is satisfied change the required log level to DEBUG
262
-		if ($this->logConditionSatisfied) {
263
-			return Util::DEBUG;
264
-		}
265
-
266
-		if (isset($context['app'])) {
267
-			$logCondition = $this->config->getValue('log.condition', []);
268
-			$app = $context['app'];
269
-
270
-			/**
271
-			 * check log condition based on the context of each log message
272
-			 * once this is met -> change the required log level to debug
273
-			 */
274
-			if (!empty($logCondition)
275
-				&& isset($logCondition['apps'])
276
-				&& in_array($app, $logCondition['apps'], true)) {
277
-				return Util::DEBUG;
278
-			}
279
-		}
280
-
281
-		return min($this->config->getValue('loglevel', Util::WARN), Util::FATAL);
282
-	}
283
-
284
-	/**
285
-	 * Logs an exception very detailed
286
-	 *
287
-	 * @param \Exception|\Throwable $exception
288
-	 * @param array $context
289
-	 * @return void
290
-	 * @since 8.2.0
291
-	 */
292
-	public function logException(\Throwable $exception, array $context = []) {
293
-		$app = $context['app'] ?? 'no app in context';
294
-		$level = $context['level'] ?? Util::ERROR;
295
-
296
-		$serializer = new ExceptionSerializer();
297
-		$data = $serializer->serializeException($exception);
298
-		$data['CustomMessage'] = $context['message'] ?? '--';
299
-
300
-		$minLevel = $this->getLogLevel($context);
301
-
302
-		array_walk($context, [$this->normalizer, 'format']);
303
-
304
-		if ($level >= $minLevel) {
305
-			if ($this->logger !== File::class) {
306
-				$data = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR);
307
-			}
308
-			$this->writeLog($app, $data, $level);
309
-		}
310
-
311
-		$context['level'] = $level;
312
-		if (!is_null($this->crashReporters)) {
313
-			$this->crashReporters->delegateReport($exception, $context);
314
-		}
315
-	}
316
-
317
-	/**
318
-	 * @param string $app
319
-	 * @param string|array $entry
320
-	 * @param int $level
321
-	 */
322
-	protected function writeLog(string $app, $entry, int $level) {
323
-		call_user_func([$this->logger, 'write'], $app, $entry, $level);
324
-	}
325
-
326
-	/**
327
-	 * @param string $logType
328
-	 * @return string
329
-	 * @internal
330
-	 */
331
-	public static function getLogClass(string $logType): string {
332
-		switch (strtolower($logType)) {
333
-			case 'errorlog':
334
-				return \OC\Log\Errorlog::class;
335
-			case 'syslog':
336
-				return \OC\Log\Syslog::class;
337
-			case 'file':
338
-				return \OC\Log\File::class;
339
-
340
-			// Backwards compatibility for old and fallback for unknown log types
341
-			case 'owncloud':
342
-			case 'nextcloud':
343
-			default:
344
-				return \OC\Log\File::class;
345
-		}
346
-	}
57
+    /** @var string */
58
+    private $logger;
59
+
60
+    /** @var SystemConfig */
61
+    private $config;
62
+
63
+    /** @var boolean|null cache the result of the log condition check for the request */
64
+    private $logConditionSatisfied = null;
65
+
66
+    /** @var Normalizer */
67
+    private $normalizer;
68
+
69
+    /** @var IRegistry */
70
+    private $crashReporters;
71
+
72
+    /**
73
+     * @param string $logger The logger that should be used
74
+     * @param SystemConfig $config the system config object
75
+     * @param Normalizer|null $normalizer
76
+     * @param IRegistry|null $registry
77
+     */
78
+    public function __construct($logger = null, SystemConfig $config = null, $normalizer = null, IRegistry $registry = null) {
79
+        // FIXME: Add this for backwards compatibility, should be fixed at some point probably
80
+        if ($config === null) {
81
+            $config = \OC::$server->getSystemConfig();
82
+        }
83
+
84
+        $this->config = $config;
85
+
86
+        // FIXME: Add this for backwards compatibility, should be fixed at some point probably
87
+        if ($logger === null) {
88
+            $logType = $this->config->getValue('log_type', 'file');
89
+            $this->logger = static::getLogClass($logType);
90
+            call_user_func([$this->logger, 'init']);
91
+        } else {
92
+            $this->logger = $logger;
93
+        }
94
+        if ($normalizer === null) {
95
+            $this->normalizer = new Normalizer();
96
+        } else {
97
+            $this->normalizer = $normalizer;
98
+        }
99
+        $this->crashReporters = $registry;
100
+    }
101
+
102
+    /**
103
+     * System is unusable.
104
+     *
105
+     * @param string $message
106
+     * @param array $context
107
+     * @return void
108
+     */
109
+    public function emergency(string $message, array $context = []) {
110
+        $this->log(Util::FATAL, $message, $context);
111
+    }
112
+
113
+    /**
114
+     * Action must be taken immediately.
115
+     *
116
+     * Example: Entire website down, database unavailable, etc. This should
117
+     * trigger the SMS alerts and wake you up.
118
+     *
119
+     * @param string $message
120
+     * @param array $context
121
+     * @return void
122
+     */
123
+    public function alert(string $message, array $context = []) {
124
+        $this->log(Util::ERROR, $message, $context);
125
+    }
126
+
127
+    /**
128
+     * Critical conditions.
129
+     *
130
+     * Example: Application component unavailable, unexpected exception.
131
+     *
132
+     * @param string $message
133
+     * @param array $context
134
+     * @return void
135
+     */
136
+    public function critical(string $message, array $context = []) {
137
+        $this->log(Util::ERROR, $message, $context);
138
+    }
139
+
140
+    /**
141
+     * Runtime errors that do not require immediate action but should typically
142
+     * be logged and monitored.
143
+     *
144
+     * @param string $message
145
+     * @param array $context
146
+     * @return void
147
+     */
148
+    public function error(string $message, array $context = []) {
149
+        $this->log(Util::ERROR, $message, $context);
150
+    }
151
+
152
+    /**
153
+     * Exceptional occurrences that are not errors.
154
+     *
155
+     * Example: Use of deprecated APIs, poor use of an API, undesirable things
156
+     * that are not necessarily wrong.
157
+     *
158
+     * @param string $message
159
+     * @param array $context
160
+     * @return void
161
+     */
162
+    public function warning(string $message, array $context = []) {
163
+        $this->log(Util::WARN, $message, $context);
164
+    }
165
+
166
+    /**
167
+     * Normal but significant events.
168
+     *
169
+     * @param string $message
170
+     * @param array $context
171
+     * @return void
172
+     */
173
+    public function notice(string $message, array $context = []) {
174
+        $this->log(Util::INFO, $message, $context);
175
+    }
176
+
177
+    /**
178
+     * Interesting events.
179
+     *
180
+     * Example: User logs in, SQL logs.
181
+     *
182
+     * @param string $message
183
+     * @param array $context
184
+     * @return void
185
+     */
186
+    public function info(string $message, array $context = []) {
187
+        $this->log(Util::INFO, $message, $context);
188
+    }
189
+
190
+    /**
191
+     * Detailed debug information.
192
+     *
193
+     * @param string $message
194
+     * @param array $context
195
+     * @return void
196
+     */
197
+    public function debug(string $message, array $context = []) {
198
+        $this->log(Util::DEBUG, $message, $context);
199
+    }
200
+
201
+
202
+    /**
203
+     * Logs with an arbitrary level.
204
+     *
205
+     * @param int $level
206
+     * @param string $message
207
+     * @param array $context
208
+     * @return void
209
+     */
210
+    public function log(int $level, string $message, array $context = []) {
211
+        $minLevel = $this->getLogLevel($context);
212
+
213
+        array_walk($context, [$this->normalizer, 'format']);
214
+
215
+        $app = $context['app'] ?? 'no app in context';
216
+
217
+        // interpolate $message as defined in PSR-3
218
+        $replace = [];
219
+        foreach ($context as $key => $val) {
220
+            $replace['{' . $key . '}'] = $val;
221
+        }
222
+        $message = strtr($message, $replace);
223
+
224
+        if ($level >= $minLevel) {
225
+            $this->writeLog($app, $message, $level);
226
+        }
227
+    }
228
+
229
+    private function getLogLevel($context) {
230
+        /**
231
+         * check for a special log condition - this enables an increased log on
232
+         * a per request/user base
233
+         */
234
+        if ($this->logConditionSatisfied === null) {
235
+            // default to false to just process this once per request
236
+            $this->logConditionSatisfied = false;
237
+            if (!empty($logCondition)) {
238
+
239
+                // check for secret token in the request
240
+                if (isset($logCondition['shared_secret'])) {
241
+                    $request = \OC::$server->getRequest();
242
+
243
+                    // if token is found in the request change set the log condition to satisfied
244
+                    if ($request && hash_equals($logCondition['shared_secret'], $request->getParam('log_secret', ''))) {
245
+                        $this->logConditionSatisfied = true;
246
+                    }
247
+                }
248
+
249
+                // check for user
250
+                if (isset($logCondition['users'])) {
251
+                    $user = \OC::$server->getUserSession()->getUser();
252
+
253
+                    // if the user matches set the log condition to satisfied
254
+                    if ($user !== null && in_array($user->getUID(), $logCondition['users'], true)) {
255
+                        $this->logConditionSatisfied = true;
256
+                    }
257
+                }
258
+            }
259
+        }
260
+
261
+        // if log condition is satisfied change the required log level to DEBUG
262
+        if ($this->logConditionSatisfied) {
263
+            return Util::DEBUG;
264
+        }
265
+
266
+        if (isset($context['app'])) {
267
+            $logCondition = $this->config->getValue('log.condition', []);
268
+            $app = $context['app'];
269
+
270
+            /**
271
+             * check log condition based on the context of each log message
272
+             * once this is met -> change the required log level to debug
273
+             */
274
+            if (!empty($logCondition)
275
+                && isset($logCondition['apps'])
276
+                && in_array($app, $logCondition['apps'], true)) {
277
+                return Util::DEBUG;
278
+            }
279
+        }
280
+
281
+        return min($this->config->getValue('loglevel', Util::WARN), Util::FATAL);
282
+    }
283
+
284
+    /**
285
+     * Logs an exception very detailed
286
+     *
287
+     * @param \Exception|\Throwable $exception
288
+     * @param array $context
289
+     * @return void
290
+     * @since 8.2.0
291
+     */
292
+    public function logException(\Throwable $exception, array $context = []) {
293
+        $app = $context['app'] ?? 'no app in context';
294
+        $level = $context['level'] ?? Util::ERROR;
295
+
296
+        $serializer = new ExceptionSerializer();
297
+        $data = $serializer->serializeException($exception);
298
+        $data['CustomMessage'] = $context['message'] ?? '--';
299
+
300
+        $minLevel = $this->getLogLevel($context);
301
+
302
+        array_walk($context, [$this->normalizer, 'format']);
303
+
304
+        if ($level >= $minLevel) {
305
+            if ($this->logger !== File::class) {
306
+                $data = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR);
307
+            }
308
+            $this->writeLog($app, $data, $level);
309
+        }
310
+
311
+        $context['level'] = $level;
312
+        if (!is_null($this->crashReporters)) {
313
+            $this->crashReporters->delegateReport($exception, $context);
314
+        }
315
+    }
316
+
317
+    /**
318
+     * @param string $app
319
+     * @param string|array $entry
320
+     * @param int $level
321
+     */
322
+    protected function writeLog(string $app, $entry, int $level) {
323
+        call_user_func([$this->logger, 'write'], $app, $entry, $level);
324
+    }
325
+
326
+    /**
327
+     * @param string $logType
328
+     * @return string
329
+     * @internal
330
+     */
331
+    public static function getLogClass(string $logType): string {
332
+        switch (strtolower($logType)) {
333
+            case 'errorlog':
334
+                return \OC\Log\Errorlog::class;
335
+            case 'syslog':
336
+                return \OC\Log\Syslog::class;
337
+            case 'file':
338
+                return \OC\Log\File::class;
339
+
340
+            // Backwards compatibility for old and fallback for unknown log types
341
+            case 'owncloud':
342
+            case 'nextcloud':
343
+            default:
344
+                return \OC\Log\File::class;
345
+        }
346
+    }
347 347
 }
Please login to merge, or discard this patch.
lib/private/Log/ExceptionSerializer.php 1 patch
Indentation   +113 added lines, -113 removed lines patch added patch discarded remove patch
@@ -24,117 +24,117 @@
 block discarded – undo
24 24
 use OC\HintException;
25 25
 
26 26
 class ExceptionSerializer {
27
-	const methodsWithSensitiveParameters = [
28
-		// Session/User
29
-		'completeLogin',
30
-		'login',
31
-		'checkPassword',
32
-		'checkPasswordNoLogging',
33
-		'loginWithPassword',
34
-		'updatePrivateKeyPassword',
35
-		'validateUserPass',
36
-		'loginWithToken',
37
-		'{closure}',
38
-
39
-		// TokenProvider
40
-		'getToken',
41
-		'isTokenPassword',
42
-		'getPassword',
43
-		'decryptPassword',
44
-		'logClientIn',
45
-		'generateToken',
46
-		'validateToken',
47
-
48
-		// TwoFactorAuth
49
-		'solveChallenge',
50
-		'verifyChallenge',
51
-
52
-		// ICrypto
53
-		'calculateHMAC',
54
-		'encrypt',
55
-		'decrypt',
56
-
57
-		// LoginController
58
-		'tryLogin',
59
-		'confirmPassword',
60
-
61
-		// LDAP
62
-		'bind',
63
-		'areCredentialsValid',
64
-		'invokeLDAPMethod',
65
-
66
-		// Encryption
67
-		'storeKeyPair',
68
-		'setupUser',
69
-	];
70
-
71
-	private function filterTrace(array $trace) {
72
-		$sensitiveValues = [];
73
-		$trace = array_map(function (array $traceLine) use (&$sensitiveValues) {
74
-			foreach (self::methodsWithSensitiveParameters as $sensitiveMethod) {
75
-				if (strpos($traceLine['function'], $sensitiveMethod) !== false) {
76
-					$sensitiveValues = array_merge($sensitiveValues, $traceLine['args']);
77
-					$traceLine['args'] = ['*** sensitive parameters replaced ***'];
78
-					return $traceLine;
79
-				}
80
-			}
81
-			return $traceLine;
82
-		}, $trace);
83
-		return array_map(function (array $traceLine) use ($sensitiveValues) {
84
-			$traceLine['args'] = $this->removeValuesFromArgs($traceLine['args'], $sensitiveValues);
85
-			return $traceLine;
86
-		}, $trace);
87
-	}
88
-
89
-	private function removeValuesFromArgs($args, $values) {
90
-		foreach ($args as &$arg) {
91
-			if (in_array($arg, $values, true)) {
92
-				$arg = '*** sensitive parameter replaced ***';
93
-			} else if (is_array($arg)) {
94
-				$arg = $this->removeValuesFromArgs($arg, $values);
95
-			}
96
-		}
97
-		return $args;
98
-	}
99
-
100
-	private function encodeTrace($trace) {
101
-		$filteredTrace = $this->filterTrace($trace);
102
-		return array_map(function (array $line) {
103
-			$line['args'] = array_map([$this, 'encodeArg'], $line['args']);
104
-			return $line;
105
-		}, $filteredTrace);
106
-	}
107
-
108
-	private function encodeArg($arg) {
109
-		if (is_object($arg)) {
110
-			$data = get_object_vars($arg);
111
-			$data['__class__'] = get_class($arg);
112
-			return array_map([$this, 'encodeArg'], $data);
113
-		} else if (is_array($arg)) {
114
-			return array_map([$this, 'encodeArg'], $arg);
115
-		} else {
116
-			return $arg;
117
-		}
118
-	}
119
-
120
-	public function serializeException(\Throwable $exception) {
121
-		$data = [
122
-			'Exception' => get_class($exception),
123
-			'Message' => $exception->getMessage(),
124
-			'Code' => $exception->getCode(),
125
-			'Trace' => $this->encodeTrace($exception->getTrace()),
126
-			'File' => $exception->getFile(),
127
-			'Line' => $exception->getLine(),
128
-		];
129
-
130
-		if ($exception instanceof HintException) {
131
-			$data['Hint'] = $exception->getHint();
132
-		}
133
-
134
-		if ($exception->getPrevious()) {
135
-			$data['Previous'] = $this->serializeException($exception->getPrevious());
136
-		}
137
-
138
-		return $data;
139
-	}
27
+    const methodsWithSensitiveParameters = [
28
+        // Session/User
29
+        'completeLogin',
30
+        'login',
31
+        'checkPassword',
32
+        'checkPasswordNoLogging',
33
+        'loginWithPassword',
34
+        'updatePrivateKeyPassword',
35
+        'validateUserPass',
36
+        'loginWithToken',
37
+        '{closure}',
38
+
39
+        // TokenProvider
40
+        'getToken',
41
+        'isTokenPassword',
42
+        'getPassword',
43
+        'decryptPassword',
44
+        'logClientIn',
45
+        'generateToken',
46
+        'validateToken',
47
+
48
+        // TwoFactorAuth
49
+        'solveChallenge',
50
+        'verifyChallenge',
51
+
52
+        // ICrypto
53
+        'calculateHMAC',
54
+        'encrypt',
55
+        'decrypt',
56
+
57
+        // LoginController
58
+        'tryLogin',
59
+        'confirmPassword',
60
+
61
+        // LDAP
62
+        'bind',
63
+        'areCredentialsValid',
64
+        'invokeLDAPMethod',
65
+
66
+        // Encryption
67
+        'storeKeyPair',
68
+        'setupUser',
69
+    ];
70
+
71
+    private function filterTrace(array $trace) {
72
+        $sensitiveValues = [];
73
+        $trace = array_map(function (array $traceLine) use (&$sensitiveValues) {
74
+            foreach (self::methodsWithSensitiveParameters as $sensitiveMethod) {
75
+                if (strpos($traceLine['function'], $sensitiveMethod) !== false) {
76
+                    $sensitiveValues = array_merge($sensitiveValues, $traceLine['args']);
77
+                    $traceLine['args'] = ['*** sensitive parameters replaced ***'];
78
+                    return $traceLine;
79
+                }
80
+            }
81
+            return $traceLine;
82
+        }, $trace);
83
+        return array_map(function (array $traceLine) use ($sensitiveValues) {
84
+            $traceLine['args'] = $this->removeValuesFromArgs($traceLine['args'], $sensitiveValues);
85
+            return $traceLine;
86
+        }, $trace);
87
+    }
88
+
89
+    private function removeValuesFromArgs($args, $values) {
90
+        foreach ($args as &$arg) {
91
+            if (in_array($arg, $values, true)) {
92
+                $arg = '*** sensitive parameter replaced ***';
93
+            } else if (is_array($arg)) {
94
+                $arg = $this->removeValuesFromArgs($arg, $values);
95
+            }
96
+        }
97
+        return $args;
98
+    }
99
+
100
+    private function encodeTrace($trace) {
101
+        $filteredTrace = $this->filterTrace($trace);
102
+        return array_map(function (array $line) {
103
+            $line['args'] = array_map([$this, 'encodeArg'], $line['args']);
104
+            return $line;
105
+        }, $filteredTrace);
106
+    }
107
+
108
+    private function encodeArg($arg) {
109
+        if (is_object($arg)) {
110
+            $data = get_object_vars($arg);
111
+            $data['__class__'] = get_class($arg);
112
+            return array_map([$this, 'encodeArg'], $data);
113
+        } else if (is_array($arg)) {
114
+            return array_map([$this, 'encodeArg'], $arg);
115
+        } else {
116
+            return $arg;
117
+        }
118
+    }
119
+
120
+    public function serializeException(\Throwable $exception) {
121
+        $data = [
122
+            'Exception' => get_class($exception),
123
+            'Message' => $exception->getMessage(),
124
+            'Code' => $exception->getCode(),
125
+            'Trace' => $this->encodeTrace($exception->getTrace()),
126
+            'File' => $exception->getFile(),
127
+            'Line' => $exception->getLine(),
128
+        ];
129
+
130
+        if ($exception instanceof HintException) {
131
+            $data['Hint'] = $exception->getHint();
132
+        }
133
+
134
+        if ($exception->getPrevious()) {
135
+            $data['Previous'] = $this->serializeException($exception->getPrevious());
136
+        }
137
+
138
+        return $data;
139
+    }
140 140
 }
141 141
\ No newline at end of file
Please login to merge, or discard this patch.