Issues (1474)

framework/Util/TSysLogRoute.php (5 issues)

1
<?php
2
3
/**
4
 * TSysLogRoute class file
5
 *
6
 * @author Brad Anderson <[email protected]>
7
 * @link https://github.com/pradosoft/prado
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 */
10
11
namespace Prado\Util;
12
13
use Prado\Exceptions\TConfigurationException;
14
use Prado\Exceptions\TLogException;
15
use Prado\Prado;
16
use Prado\TPropertyValue;
17
18
/**
19
 * TSysLogRoute class.
20
 *
21
 * Sends the log to the syslog.
22
 *
23
 * @author Brad Anderson <[email protected]>
24
 * @since 4.3.0
25
 * @link https://www.php.net/manual/en/function.openlog.php
26
 * @link https://www.php.net/manual/en/function.syslog.php
27
 */
28
class TSysLogRoute extends TLogRoute
29
{
30
	/**
31
	 * @var false|string The Prefix for openlog()
32
	 */
33
	private string|false $_sysLogPrefix = false;
34
35
	/**
36
	 * @var ?int The flags for openlog(), default null for `LOG_ODELAY | LOG_PID`
37
	 */
38
	private ?int $_sysLogFlags = null;
39
40
	/**
41
	 * @var int The facility for openlog().
42
	 */
43
	private int $_facility = LOG_USER;
44
45
	/**
46
	 * @param array $logs list of log messages
47
	 * @param bool $final is the final flush
48
	 * @param array $meta the meta data for the logs.
49
	 * @throws TLogException When failing to write to syslog.
50
	 */
51
	protected function processLogs(array $logs, bool $final, array $meta)
52
	{
53
		openlog($this->getSysLogPrefix(), $this->getSysLogFlags(), $this->getFacility());
0 ignored issues
show
It seems like $this->getSysLogPrefix() can also be of type false; however, parameter $prefix of openlog() does only seem to accept string, 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

53
		openlog(/** @scrutinizer ignore-type */ $this->getSysLogPrefix(), $this->getSysLogFlags(), $this->getFacility());
Loading history...
54
		foreach ($logs as $log) {
55
			if (syslog($this->translateLogLevel($log[TLogger::LOG_LEVEL]), $this->formatLogMessage($log)) === false) {
56
				throw new TLogException('syslogroute_log_failed');
57
			}
58
		}
59
		closelog();
60
	}
61
62
	/**
63
	 * Translates a PRADO log level attribute into one understood by syslog
64
	 * @param int $level prado log level
65
	 * @return int syslog priority
66
	 */
67
	protected static function translateLogLevel($level)
68
	{
69
		switch ($level) {
70
			case TLogger::PROFILE:
71
			case TLogger::PROFILE_BEGIN:
72
			case TLogger::PROFILE_END:
73
			case TLogger::DEBUG:
74
				return LOG_DEBUG;
75
			case TLogger::INFO:
76
				return LOG_INFO;
77
			case TLogger::NOTICE:
78
				return LOG_NOTICE;
79
			case TLogger::WARNING:
80
				return LOG_WARNING;
81
			case TLogger::ERROR:
82
				return LOG_ERR;
83
			case TLogger::ALERT:
84
				return LOG_ALERT;
85
			case TLogger::FATAL:
86
				return LOG_CRIT;
87
			default:
88
				return LOG_INFO;
89
		}
90
	}
91
92
	/**
93
	 * @return string The prefix for syslog. Defaults to false
94
	 */
95
	public function getSysLogPrefix(): string|false
96
	{
97
		return $this->_sysLogPrefix;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_sysLogPrefix could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
98
	}
99
100
	/**
101
	 * @param string $value the prefix for the syslog, via openlog
102
	 */
103
	public function setSysLogPrefix($value)
104
	{
105
		$value = TPropertyValue::ensureString($value);
106
		if ($value === '') {
107
			$value = false;
108
		}
109
		$this->_sysLogPrefix = $value;
110
	}
111
112
	/**
113
	 * @return int The options for syslog. Defaults to LOG_ODELAY | LOG_PID
114
	 */
115
	public function getSysLogFlags()
116
	{
117
		return ($this->_sysLogFlags !== null) ? $this->_sysLogFlags : LOG_ODELAY | LOG_PID;
118
	}
119
120
	/**
121
	 * This sets the `openlog` flags.  It can be an integer, a string or an array of strings.
122
	 * As a string, the delimiters are ',' and '|' acting identically.
123
	 *
124
	 * By setting to null, this will default to `LOG_ODELAY | LOG_PID`.
125
	 * @param null|int|string|string[] $value the options for syslog
126
	 * @return static The current object.
127
	 * @throw TConfigurationException When the Flags are not valid.
128
	 */
129
	public function setSysLogFlags($value): static
130
	{
131
		static $_flagsMap = [
132
			'LOG_CONS' => LOG_CONS, // Errors to console
133
			'LOG_NDELAY' => LOG_NDELAY, // open immediately
134
			'LOG_ODELAY' => LOG_ODELAY, // delay opening until a log
135
			'LOG_PERROR' => LOG_PERROR, // Print to STDERR as well.
136
			'LOG_PID' => LOG_PID, // include ProcessID
137
		];
138
139
		if ($value === null || is_int($value)) {
140
			$invalidFlags = ~array_reduce($_flagsMap, function ($flags, $flag) {
141
				return $flags | $flag;
142
			}, 0);
143
			if ($invalidFlags & ((int) $value)) {
144
				throw new TConfigurationException('syslogroute_bad_flags', '0x' . dechex($value));
0 ignored issues
show
It seems like $value can also be of type null; however, parameter $num of dechex() does only seem to accept integer, 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

144
				throw new TConfigurationException('syslogroute_bad_flags', '0x' . dechex(/** @scrutinizer ignore-type */ $value));
Loading history...
145
			}
146
			$this->_sysLogFlags = $value;
147
		} else {
148
			if (!is_array($value)) {
149
				$value = preg_split('/[|,]/', strtoupper($value));
150
			} else {
151
				$value = array_map('strtoupper', $value);
152
			}
153
			$options = array_map('trim', $value);
154
			$this->_sysLogFlags = 0;
155
			while (count($options)) {
156
				$option = array_pop($options);
157
				if (isset($_flagsMap[$option])) {
158
					$this->_sysLogFlags |= $_flagsMap[$option];
159
				}
160
			}
161
		}
162
		return $this;
163
	}
164
165
	/**
166
	 * @return int The facility for syslog.
167
	 */
168
	public function getFacility(): int
169
	{
170
		return $this->_facility;
171
	}
172
173
	/**
174
	 * @param int|string $value the options for syslog
175
	 * @return static The current object.
176
	 */
177
	public function setFacility($value): static
178
	{
179
		static $_facilitiesMap = [
180
			'LOG_AUTH' => LOG_AUTH,		// 0x20
181
			'LOG_CRON' => LOG_CRON,		// 0x48
182
			'LOG_DAEMON' => LOG_DAEMON,	// 0x18
183
			'LOG_KERN' => LOG_KERN,		// 0x00
184
			'LOG_LOCAL0' => LOG_LOCAL0,	// 0x80
185
			'LOG_LOCAL1' => LOG_LOCAL1,	// 0x88
186
			'LOG_LOCAL2' => LOG_LOCAL2,	// 0x90
187
			'LOG_LOCAL3' => LOG_LOCAL3,	// 0x98
188
			'LOG_LOCAL4' => LOG_LOCAL4,	// 0xa0
189
			'LOG_LOCAL5' => LOG_LOCAL5,	// 0xa8
190
			'LOG_LOCAL6' => LOG_LOCAL6,	// 0xb0
191
			'LOG_LOCAL7' => LOG_LOCAL7,	// 0xb8
192
			'LOG_LPR' => LOG_LPR,		// 0x30
193
			'LOG_MAIL' => LOG_MAIL,		// 0x10
194
			'LOG_NEWS' => LOG_NEWS, 	// 0x38
195
			'LOG_SYSLOG' => LOG_SYSLOG, // 0x28
196
			'LOG_USER' => LOG_USER, 	// 0x08
197
			'LOG_UUCP' => LOG_UUCP, 	// 0x40
198
		];
199
		if (defined('LOG_AUTHPRIV')) {
200
			$_facilitiesMap['LOG_AUTH'] = LOG_AUTHPRIV;
201
		}
202
203
		if (is_int($value)) {
204
			if (defined('LOG_AUTHPRIV') && $value === LOG_AUTH) {
205
				$value = LOG_AUTHPRIV;
206
			}
207
208
			if (array_search($value, $_facilitiesMap) === false) {
209
				throw new TConfigurationException('syslogroute_bad_facility', '0x' . dechex($value));
210
			}
211
		} else {
212
			$value = trim(strtoupper($value));
213
			if (isset($_facilitiesMap[$value])) {
214
				$value = $_facilitiesMap[$value];
215
			} else {
216
				throw new TConfigurationException('syslogroute_bad_facility', '0x' . dechex($value));
0 ignored issues
show
$value of type string is incompatible with the type integer expected by parameter $num of dechex(). ( Ignorable by Annotation )

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

216
				throw new TConfigurationException('syslogroute_bad_facility', '0x' . dechex(/** @scrutinizer ignore-type */ $value));
Loading history...
217
			}
218
		}
219
		$this->_facility = $value;
220
221
		return $this;
222
	}
223
224
	/**
225
	 * {@inheritdoc}
226
	 */
227
	public function formatLogMessage(array $log): string
228
	{
229
		if (!is_string($log[TLogger::LOG_MESSAGE])) {
230
			if ($log[TLogger::LOG_MESSAGE] instanceof \Exception || $log[TLogger::LOG_MESSAGE] instanceof \Throwable) {
231
				$log[TLogger::LOG_MESSAGE] = (string) $log[TLogger::LOG_MESSAGE];
232
			} else {
233
				$log[TLogger::LOG_MESSAGE] = \Prado\Util\TVarDumper::dump($log[TLogger::LOG_MESSAGE]);
234
			}
235
		}
236
237
		$prefix = $this->getLogPrefix($log);
238
239
		return $prefix . '[' . static::getLevelName($log[TLogger::LOG_LEVEL]) . '][' . $log[TLogger::LOG_CATEGORY] . '] ' . $log[TLogger::LOG_MESSAGE];
0 ignored issues
show
Bug Best Practice introduced by
The method Prado\Util\TLogRoute::getLevelName() is not static, but was called statically. ( Ignorable by Annotation )

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

239
		return $prefix . '[' . static::/** @scrutinizer ignore-call */ getLevelName($log[TLogger::LOG_LEVEL]) . '][' . $log[TLogger::LOG_CATEGORY] . '] ' . $log[TLogger::LOG_MESSAGE];
Loading history...
240
	}
241
}
242