1<?php
2/**
3 * Zend Framework (http://framework.zend.com/)
4 *
5 * @link      http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license   http://framework.zend.com/license/new-bsd New BSD License
8 */
9
10namespace Zend\Log\Writer;
11
12use Traversable;
13use Zend\Log\Exception;
14use Zend\Log\Formatter\Simple as SimpleFormatter;
15use Zend\Log\Logger;
16
17/**
18 * Writes log messages to syslog
19 */
20class Syslog extends AbstractWriter
21{
22    /**
23     * Maps Zend\Log priorities to PHP's syslog priorities
24     *
25     * @var array
26     */
27    protected $priorities = array(
28        Logger::EMERG  => LOG_EMERG,
29        Logger::ALERT  => LOG_ALERT,
30        Logger::CRIT   => LOG_CRIT,
31        Logger::ERR    => LOG_ERR,
32        Logger::WARN   => LOG_WARNING,
33        Logger::NOTICE => LOG_NOTICE,
34        Logger::INFO   => LOG_INFO,
35        Logger::DEBUG  => LOG_DEBUG,
36    );
37
38    /**
39     * The default log priority - for unmapped custom priorities
40     *
41     * @var string
42     */
43    protected $defaultPriority = LOG_NOTICE;
44
45    /**
46     * Last application name set by a syslog-writer instance
47     *
48     * @var string
49     */
50    protected static $lastApplication;
51
52    /**
53     * Last facility name set by a syslog-writer instance
54     *
55     * @var string
56     */
57    protected static $lastFacility;
58
59    /**
60     * Application name used by this syslog-writer instance
61     *
62     * @var string
63     */
64    protected $appName = 'Zend\Log';
65
66    /**
67     * Facility used by this syslog-writer instance
68     *
69     * @var int
70     */
71    protected $facility = LOG_USER;
72
73    /**
74     * Types of program available to logging of message
75     *
76     * @var array
77     */
78    protected $validFacilities = array();
79
80    /**
81     * Constructor
82     *
83     * @param  array $params Array of options; may include "application" and "facility" keys
84     * @return Syslog
85     */
86    public function __construct($params = null)
87    {
88        if ($params instanceof Traversable) {
89            $params = iterator_to_array($params);
90        }
91
92        $runInitializeSyslog = true;
93
94        if (is_array($params)) {
95            parent::__construct($params);
96
97            if (isset($params['application'])) {
98                $this->appName = $params['application'];
99            }
100
101            if (isset($params['facility'])) {
102                $this->setFacility($params['facility']);
103                $runInitializeSyslog = false;
104            }
105        }
106
107        if ($runInitializeSyslog) {
108            $this->initializeSyslog();
109        }
110
111        if ($this->formatter === null) {
112            $this->setFormatter(new SimpleFormatter('%message%'));
113        }
114    }
115
116    /**
117     * Initialize values facilities
118     *
119     * @return void
120     */
121    protected function initializeValidFacilities()
122    {
123        $constants = array(
124            'LOG_AUTH',
125            'LOG_AUTHPRIV',
126            'LOG_CRON',
127            'LOG_DAEMON',
128            'LOG_KERN',
129            'LOG_LOCAL0',
130            'LOG_LOCAL1',
131            'LOG_LOCAL2',
132            'LOG_LOCAL3',
133            'LOG_LOCAL4',
134            'LOG_LOCAL5',
135            'LOG_LOCAL6',
136            'LOG_LOCAL7',
137            'LOG_LPR',
138            'LOG_MAIL',
139            'LOG_NEWS',
140            'LOG_SYSLOG',
141            'LOG_USER',
142            'LOG_UUCP'
143        );
144
145        foreach ($constants as $constant) {
146            if (defined($constant)) {
147                $this->validFacilities[] = constant($constant);
148            }
149        }
150    }
151
152    /**
153     * Initialize syslog / set application name and facility
154     *
155     * @return void
156     */
157    protected function initializeSyslog()
158    {
159        static::$lastApplication = $this->appName;
160        static::$lastFacility    = $this->facility;
161        openlog($this->appName, LOG_PID, $this->facility);
162    }
163
164    /**
165     * Set syslog facility
166     *
167     * @param int $facility Syslog facility
168     * @return Syslog
169     * @throws Exception\InvalidArgumentException for invalid log facility
170     */
171    public function setFacility($facility)
172    {
173        if ($this->facility === $facility) {
174            return $this;
175        }
176
177        if (!count($this->validFacilities)) {
178            $this->initializeValidFacilities();
179        }
180
181        if (!in_array($facility, $this->validFacilities)) {
182            throw new Exception\InvalidArgumentException(
183                'Invalid log facility provided; please see http://php.net/openlog for a list of valid facility values'
184            );
185        }
186
187        if ('WIN' == strtoupper(substr(PHP_OS, 0, 3))
188            && ($facility !== LOG_USER)
189        ) {
190            throw new Exception\InvalidArgumentException(
191                'Only LOG_USER is a valid log facility on Windows'
192            );
193        }
194
195        $this->facility = $facility;
196        $this->initializeSyslog();
197        return $this;
198    }
199
200    /**
201     * Set application name
202     *
203     * @param string $appName Application name
204     * @return Syslog
205     */
206    public function setApplicationName($appName)
207    {
208        if ($this->appName === $appName) {
209            return $this;
210        }
211
212        $this->appName = $appName;
213        $this->initializeSyslog();
214        return $this;
215    }
216
217    /**
218     * Close syslog.
219     *
220     * @return void
221     */
222    public function shutdown()
223    {
224        closelog();
225    }
226
227    /**
228     * Write a message to syslog.
229     *
230     * @param array $event event data
231     * @return void
232     */
233    protected function doWrite(array $event)
234    {
235        if (array_key_exists($event['priority'], $this->priorities)) {
236            $priority = $this->priorities[$event['priority']];
237        } else {
238            $priority = $this->defaultPriority;
239        }
240
241        if ($this->appName !== static::$lastApplication
242            || $this->facility !== static::$lastFacility
243        ) {
244            $this->initializeSyslog();
245        }
246
247        $message = $this->formatter->format($event);
248
249        syslog($priority, $message);
250    }
251}
252