├── .gitignore
├── tests
├── PHPUnit_Framework_TestCase.php
├── bootstrap.php
├── PsrLogCompatTest.php
└── AnalogTest.php
├── examples
├── ignore.php
├── stderr.php
├── syslog.php
├── default.php
├── slackbot.php
├── variable.php
├── mail.php
├── post.php
├── file.php
├── server.php
├── amon.php
├── psr-0.php
├── buffer.php
├── gelf.php
├── firephp.php
├── chromelogger.php
├── mongo.php
├── threshold.php
├── levelbuffer.php
├── multi.php
└── SplClassLoader.php
├── .travis.yml
├── lib
├── Analog
│ ├── Handler
│ │ ├── Buffer
│ │ │ └── Destructor.php
│ │ ├── Ignore.php
│ │ ├── Null.php
│ │ ├── Stderr.php
│ │ ├── Variable.php
│ │ ├── Mail.php
│ │ ├── File.php
│ │ ├── Amon.php
│ │ ├── Threshold.php
│ │ ├── ChromeLogger.php
│ │ ├── Slackbot.php
│ │ ├── LevelName.php
│ │ ├── Post.php
│ │ ├── WPMail.php
│ │ ├── GELF.php
│ │ ├── Multi.php
│ │ ├── Buffer.php
│ │ ├── Mongo.php
│ │ ├── LevelBuffer.php
│ │ ├── Syslog.php
│ │ └── FirePHP.php
│ ├── Logger.php
│ └── Analog.php
├── Analog.php
└── ChromePhp.php
├── phpunit.xml.dist
├── composer.json
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/*
2 | /composer.lock
3 |
--------------------------------------------------------------------------------
/tests/PHPUnit_Framework_TestCase.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/stderr.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/syslog.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/default.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/slackbot.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - 5.4
4 | - 5.5
5 | - 5.6
6 | - 7.0
7 | - hhvm
8 |
9 | before_script: composer install --no-interaction --prefer-source
10 | script:
11 | - vendor/bin/phpunit --coverage-text --verbose
12 |
--------------------------------------------------------------------------------
/examples/variable.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/mail.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/post.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/Analog/Handler/Buffer/Destructor.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/amon.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/psr-0.php:
--------------------------------------------------------------------------------
1 | register ();
7 |
8 | use \Analog\Analog;
9 |
10 | $log = '';
11 |
12 | Analog::handler (\Analog\Handler\Variable::init ($log));
13 |
14 | Analog::log ('Test one');
15 | Analog::log ('Test two');
16 |
17 | echo $log;
18 |
19 | ?>
--------------------------------------------------------------------------------
/examples/buffer.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/gelf.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/Analog/Handler/Ignore.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/chromelogger.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/mongo.php:
--------------------------------------------------------------------------------
1 | testing->log->find ();
16 | foreach ($cur as $doc) {
17 | print_r ($doc);
18 | }
19 | $m->testing->log->remove ();
20 |
21 | ?>
22 |
--------------------------------------------------------------------------------
/lib/Analog/Handler/Null.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/levelbuffer.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/Analog/Handler/Variable.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 | ./tests
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/lib/Analog.php:
--------------------------------------------------------------------------------
1 | Analog\Handler\Variable::init ($errors),
11 | Analog::WARNING => Analog\Handler\Variable::init ($warnings),
12 | Analog::DEBUG => Analog\Handler\Variable::init ($debug)
13 | )));
14 |
15 | Analog::log ('First error');
16 | Analog::log ('Emergency!', Analog::URGENT);
17 | Analog::log ('A warning...', Analog::WARNING);
18 | Analog::log ('Some info', Analog::INFO);
19 | Analog::log ('Debugging output', Analog::DEBUG);
20 |
21 | echo $errors;
22 | echo "-----\n";
23 | echo $warnings;
24 | echo "-----\n";
25 | echo $debug;
26 |
27 | ?>
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "analog/analog",
3 | "type": "library",
4 | "description": "PHP logging class that can be extended via closures. Includes several pre-built handlers including file, mail, syslog, HTTP post, and MongoDB.",
5 | "keywords": ["log", "logging", "logger", "syslog", "error", "debug", "debugging", "alerts"],
6 | "homepage": "https://github.com/jbroadway/analog",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "Johnny Broadway",
11 | "email": "johnny@johnnybroadway.com",
12 | "homepage": "http://www.johnnybroadway.com/"
13 | }
14 | ],
15 | "require": {
16 | "psr/log": "1.*",
17 | "php": ">=5.3.2"
18 | },
19 | "require-dev": {
20 | "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5"
21 | },
22 | "autoload": {
23 | "psr-0": {
24 | "Analog": "lib/"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/Analog/Handler/Mail.php:
--------------------------------------------------------------------------------
1 | handler (Variable::init ($this->log));
21 | $logger->format ("%3\$d %4\$s\n");
22 | return $logger;
23 | }
24 |
25 | public function getLogs () {
26 | $logger = $this->getLogger ();
27 |
28 | $logs = explode ("\n", trim ($this->log));
29 |
30 | foreach ($logs as $key => $line) {
31 | list ($level, $msg) = explode (' ', $line, 2);
32 | $logs[$key] = $logger->convert_log_level ((int) $level, true) . ' ' . $msg;
33 | }
34 |
35 | return $logs;
36 | }
37 | }
--------------------------------------------------------------------------------
/lib/Analog/Handler/Amon.php:
--------------------------------------------------------------------------------
1 | $host,
24 | 'port' => $port,
25 | 'application_key' => $key
26 | ));
27 |
28 | $tags = array (
29 | 0 => 'urgent',
30 | 1 => 'alert',
31 | 2 => 'critical',
32 | 3 => 'error',
33 | 4 => 'warning',
34 | 5 => 'notice',
35 | 6 => 'info',
36 | 7 => 'debug'
37 | );
38 |
39 | return function ($info) use ($tags) {
40 | \Amon::log ($info, array ($tags[$info['level']]));
41 | };
42 | }
43 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2012 Johnny Broadway
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/Analog/Handler/Threshold.php:
--------------------------------------------------------------------------------
1 | 'DEBUG',
24 | \Analog\Analog::INFO => 'INFO',
25 | \Analog\Analog::NOTICE => 'NOTICE',
26 | \Analog\Analog::WARNING => 'WARNING',
27 | \Analog\Analog::ERROR => 'ERROR',
28 | \Analog\Analog::CRITICAL => 'CRITICAL',
29 | \Analog\Analog::ALERT => 'ALERT',
30 | \Analog\Analog::URGENT => 'URGENT'
31 | );
32 |
33 | /**
34 | * This contains the handler to send to
35 | */
36 | public static $handler;
37 |
38 | public static function init ($handler) {
39 | self::$handler = $handler;
40 |
41 | return function ($info) {
42 | if (isset(self::$log_levels[$info['level']])) {
43 | $info['level'] = self::$log_levels[$info['level']];
44 | }
45 | $handler = LevelName::$handler;
46 | $handler ($info);
47 | };
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/lib/Analog/Handler/Post.php:
--------------------------------------------------------------------------------
1 | setHost ($info['machine']);
33 | $message->setLevel ($info['level']);
34 |
35 | if (is_array ($info['message'])) {
36 | $message->setShortMessage ($info['message'][0]);
37 | $message->setFullMessage ($info['message'][0]);
38 | $message->setFile ($info['message'][1]);
39 | $message->setLine ($info['message'][2]);
40 | } else {
41 | $message->setShortMessage ($info['message']);
42 | $message->setFullMessage ($info['message']);
43 | }
44 |
45 | $publisher->publish ($message);
46 | };
47 | }
48 | }
--------------------------------------------------------------------------------
/lib/Analog/Handler/Multi.php:
--------------------------------------------------------------------------------
1 | array(
14 | * Analog\Handler\Mail::init( $to, $subject, $from ),
15 | * Analog\Handler\Stderr::init()
16 | * ),
17 | *
18 | * // Warnings are sent here
19 | * Analog::WARNING => Analog\Handler\File::init( 'logs/warnings.log' ),
20 | *
21 | * // Debug and info messages sent here
22 | * Analog::DEBUG => Analog\Handler\Ignore::init() // do nothing
23 | * ) ) );
24 | *
25 | * // will be ignored
26 | * Analog::log ('Ignore me', Analog::DEBUG);
27 | *
28 | * // will be written to logs/warnings.log
29 | * Analog::log ('Log me', Analog::WARNING);
30 | *
31 | * // will trigger an email notice
32 | * Analog::log ('Uh oh...', Analog::ERROR);
33 | */
34 | class Multi {
35 | public static function init ($handlers) {
36 | return function ($info) use ($handlers) {
37 | $level = is_numeric ($info['level']) ? $info['level'] : 3;
38 | while ($level <= 7) {
39 | if ( isset ( $handlers[ $level ] ) ) {
40 |
41 | if ( ! is_array( $handlers[ $level ] ) ) {
42 | $handlers[ $level ] = array( $handlers[ $level ] );
43 | }
44 |
45 | foreach ( $handlers[ $level ] as $handler ) {
46 | $handler( $info );
47 | }
48 |
49 | return;
50 | }
51 | $level++;
52 | }
53 | };
54 | }
55 | }
--------------------------------------------------------------------------------
/lib/Analog/Handler/Buffer.php:
--------------------------------------------------------------------------------
1 | {$database};
35 | } else {
36 | if (class_exists('\MongoDB\Driver\Manager')) {
37 | $driver = 'mongodb';
38 | $manager = new \MongoDB\Driver\Manager("mongodb://$server");
39 | } else {
40 | $conn = new \MongoClient ("mongodb://$server");
41 | $db = $conn->{$database};
42 | }
43 | }
44 | if ($driver == 'mongodb') {
45 | return function ($info) use ($manager, $database, $collection) {
46 | $bulk = new \MongoDB\Driver\BulkWrite;
47 | $bulk->insert($info);
48 | $dbAndColl = $database.'.'.$collection;
49 | $manager->executeBulkWrite($dbAndColl, $bulk);
50 | };
51 | } else {
52 | return function ($info) use ($db, $collection) {
53 | $db->{$collection}->insert ($info);
54 | };
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/lib/Analog/Handler/LevelBuffer.php:
--------------------------------------------------------------------------------
1 | LOG_DEBUG,
16 | \Analog\Analog::INFO => LOG_INFO,
17 | \Analog\Analog::NOTICE => LOG_NOTICE,
18 | \Analog\Analog::WARNING => LOG_WARNING,
19 | \Analog\Analog::ERROR => LOG_ERR,
20 | \Analog\Analog::CRITICAL => LOG_CRIT,
21 | \Analog\Analog::ALERT => LOG_ALERT,
22 | \Analog\Analog::URGENT => LOG_EMERG
23 | );
24 |
25 | public static $facilities = array (
26 | 'auth' => LOG_AUTH,
27 | 'authpriv' => LOG_AUTHPRIV,
28 | 'cron' => LOG_CRON,
29 | 'daemon' => LOG_DAEMON,
30 | 'kern' => LOG_KERN,
31 | 'lpr' => LOG_LPR,
32 | 'mail' => LOG_MAIL,
33 | 'news' => LOG_NEWS,
34 | 'syslog' => LOG_SYSLOG,
35 | 'user' => LOG_USER,
36 | 'uucp' => LOG_UUCP
37 | );
38 |
39 | public static function init ($ident, $facility) {
40 | if (! defined ('PHP_WINDOWS_VERSION_BUILD')) {
41 | self::$facilities['local0'] = LOG_LOCAL0;
42 | self::$facilities['local1'] = LOG_LOCAL1;
43 | self::$facilities['local2'] = LOG_LOCAL2;
44 | self::$facilities['local3'] = LOG_LOCAL3;
45 | self::$facilities['local4'] = LOG_LOCAL4;
46 | self::$facilities['local5'] = LOG_LOCAL5;
47 | self::$facilities['local6'] = LOG_LOCAL6;
48 | self::$facilities['local7'] = LOG_LOCAL7;
49 | }
50 |
51 | if (array_key_exists (strtolower ($facility), self::$facilities)) {
52 | $facility = self::$facilities[strtolower ($facility)];
53 | } elseif (! in_array ($facility, array_values (self::$facilities), true)) {
54 | throw new \UnexpectedValueException ('Unknown facility value "' . $facility . '"');
55 | }
56 |
57 | return function ($info) use ($ident, $facility) {
58 | if (! openlog ($ident, LOG_PID, $facility)) {
59 | throw new \LogicException ('Can\'t open syslog for ident "' . $ident . '" and facility "' . $facility . '"');
60 | }
61 |
62 | syslog (Syslog::$levels[$info['level']], vsprintf ('%1$s: %4$s', $info));
63 |
64 | closelog ();
65 | };
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/Analog/Handler/FirePHP.php:
--------------------------------------------------------------------------------
1 | 'LOG',
25 | \Analog\Analog::INFO => 'INFO',
26 | \Analog\Analog::NOTICE => 'INFO',
27 | \Analog\Analog::WARNING => 'WARN',
28 | \Analog\Analog::ERROR => 'ERROR',
29 | \Analog\Analog::CRITICAL => 'ERROR',
30 | \Analog\Analog::ALERT => 'ERROR',
31 | \Analog\Analog::URGENT => 'ERROR'
32 | );
33 |
34 | /**
35 | * Message index increases by 1 each time a message is sent.
36 | */
37 | private static $message_index = 1;
38 |
39 | /**
40 | * Formats a log header to be sent.
41 | */
42 | public static function format_header ($info) {
43 | if (is_array ($info['message'])) {
44 | $extra = array (
45 | 'Type' => self::$log_levels[$info['level']],
46 | 'File' => $info['message'][1],
47 | 'Line' => $info['message'][2]
48 | );
49 | $info['message'] = $info['message'][0];
50 | } else {
51 | $extra = array ('Type' => self::$log_levels[$info['level']]);
52 | }
53 |
54 | $json = json_encode (array ($extra, $info['message']));
55 |
56 | return sprintf ('X-Wf-1-1-1-%d: %s|%s|', self::$message_index++, strlen ($json), $json);
57 | }
58 |
59 | /**
60 | * Sends the initial headers if FirePHP is available then returns a
61 | * closure that handles sending log messages.
62 | */
63 | public static function init () {
64 | if (! isset ($_SERVER['HTTP_USER_AGENT'])
65 | || preg_match ('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])
66 | || isset ($_SERVER['HTTP_X_FIREPHP_VERSION'])) {
67 |
68 | header ('X-Wf-Protocol-1: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
69 | header ('X-Wf-1-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3');
70 | header ('X-Wf-1-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
71 | }
72 |
73 | return function ($info) {
74 | header (FirePHP::format_header ($info));
75 | };
76 | }
77 | }
--------------------------------------------------------------------------------
/tests/AnalogTest.php:
--------------------------------------------------------------------------------
1 | assertStringMatchesFormat (
21 | "localhost - %d-%d-%d %d:%d:%d - 3 - Foo\n",
22 | file_get_contents (Analog::handler ())
23 | );
24 | unlink (Analog::handler ());
25 | }
26 |
27 | /**
28 | * @depends test_default
29 | */
30 | function test_format () {
31 | // Test changing the format string and write again
32 | Analog::$format = "%s, %s, %d, %s\n";
33 | Analog::log ('Foo');
34 | $this->assertStringMatchesFormat (
35 | "localhost, %d-%d-%d %d:%d:%d, 3, Foo\n",
36 | file_get_contents (Analog::handler ())
37 | );
38 | unlink (Analog::handler ());
39 | }
40 |
41 | /**
42 | * @depends test_format
43 | */
44 | function test_tz_and_dates () {
45 | // Test changing the date_format
46 | Analog::$date_format = 'r'; // RFC2822 format
47 | Analog::log ('Foo');
48 | $this->assertStringMatchesFormat (
49 | "localhost, %s, %d %s %d %d:%d:%d +0000, 3, Foo\n",
50 | file_get_contents (Analog::handler ())
51 | );
52 | unlink (Analog::handler ());
53 |
54 | // Test changing the timezone
55 | Analog::$timezone = 'CST';
56 | Analog::log ('Foo');
57 |
58 | $dt = new \DateTime ('now', new \DateTimeZone (Analog::$timezone));
59 | $zone_offset = $dt->format ('O');
60 |
61 | $this->assertStringMatchesFormat (
62 | "localhost, %s, %d %s %d %d:%d:%d $zone_offset, 3, Foo\n",
63 | file_get_contents (Analog::handler ())
64 | );
65 | unlink (Analog::handler ());
66 |
67 | Analog::$date_format = 'Y-m-d H:i:s';
68 | Analog::$timezone = 'GMT';
69 | }
70 |
71 | /**
72 | * @depends test_tz_and_dates
73 | */
74 | function test_handler () {
75 | // Test logging using a closure
76 | Analog::handler (function ($msg) {
77 | AnalogTest::$log .= vsprintf (Analog::$format, $msg);
78 | });
79 |
80 | Analog::log ('Testing');
81 | $this->assertStringMatchesFormat (
82 | "localhost, %d-%d-%d %d:%d:%d, 3, Testing\n",
83 | self::$log
84 | );
85 |
86 | self::$log = '';
87 | }
88 |
89 | /**
90 | * @depends test_handler
91 | */
92 | function test_level () {
93 | // Test default_level change
94 | Analog::$default_level = 1;
95 | Analog::log ('Testing');
96 | $this->assertStringMatchesFormat (
97 | "localhost, %d-%d-%d %d:%d:%d, 1, Testing\n",
98 | self::$log
99 | );
100 |
101 | Analog::$default_level = 3;
102 | }
103 |
104 | /*
105 | * @depends test_level
106 | * @covers Analog::urgent
107 | * @covers Analog::alert
108 | * @covers Analog::critical
109 | * @covers Analog::error
110 | * @covers Analog::warning
111 | * @covers Analog::notice
112 | * @covers Analog::info
113 | * @covers Analog::debug
114 | */
115 | function test_aliases () {
116 | self::$log = '';
117 |
118 | Analog::urgent ('Testing');
119 | $this->assertStringMatchesFormat (
120 | "localhost, %d-%d-%d %d:%d:%d, 0, Testing\n",
121 | self::$log
122 | );
123 |
124 | self::$log = '';
125 |
126 | Analog::alert ('Testing');
127 | $this->assertStringMatchesFormat (
128 | "localhost, %d-%d-%d %d:%d:%d, 1, Testing\n",
129 | self::$log
130 | );
131 |
132 | self::$log = '';
133 |
134 | Analog::error ('Testing');
135 | $this->assertStringMatchesFormat (
136 | "localhost, %d-%d-%d %d:%d:%d, 3, Testing\n",
137 | self::$log
138 | );
139 |
140 | self::$log = '';
141 |
142 | Analog::warning ('Testing');
143 | $this->assertStringMatchesFormat (
144 | "localhost, %d-%d-%d %d:%d:%d, 4, Testing\n",
145 | self::$log
146 | );
147 |
148 | self::$log = '';
149 |
150 | Analog::notice ('Testing');
151 | $this->assertStringMatchesFormat (
152 | "localhost, %d-%d-%d %d:%d:%d, 5, Testing\n",
153 | self::$log
154 | );
155 |
156 | self::$log = '';
157 |
158 | Analog::info ('Testing');
159 | $this->assertStringMatchesFormat (
160 | "localhost, %d-%d-%d %d:%d:%d, 6, Testing\n",
161 | self::$log
162 | );
163 |
164 | self::$log = '';
165 |
166 | Analog::debug ('Testing');
167 | $this->assertStringMatchesFormat (
168 | "localhost, %d-%d-%d %d:%d:%d, 7, Testing\n",
169 | self::$log
170 | );
171 |
172 | self::$log = '';
173 | }
174 | }
175 |
176 | ?>
177 |
--------------------------------------------------------------------------------
/examples/SplClassLoader.php:
--------------------------------------------------------------------------------
1 | .
19 | */
20 |
21 | /**
22 | * SplClassLoader implementation that implements the technical interoperability
23 | * standards for PHP 5.3 namespaces and class names.
24 | *
25 | * http://groups.google.com/group/php-standards/web/psr-0-final-proposal?pli=1
26 | *
27 | * // Example which loads classes for the Doctrine Common package in the
28 | * // Doctrine\Common namespace.
29 | * $classLoader = new SplClassLoader('Doctrine\Common', '/path/to/doctrine');
30 | * $classLoader->register();
31 | *
32 | * @license http://www.opensource.org/licenses/mit-license.html MIT License
33 | * @author Jonathan H. Wage
34 | * @author Roman S. Borschel
35 | * @author Matthew Weier O'Phinney
36 | * @author Kris Wallsmith
37 | * @author Fabien Potencier
38 | */
39 | class SplClassLoader
40 | {
41 | private $_fileExtension = '.php';
42 | private $_namespace;
43 | private $_includePath;
44 | private $_namespaceSeparator = '\\';
45 |
46 | /**
47 | * Creates a new SplClassLoader that loads classes of the
48 | * specified namespace.
49 | *
50 | * @param string $ns The namespace to use.
51 | */
52 | public function __construct($ns = null, $includePath = null)
53 | {
54 | $this->_namespace = $ns;
55 | $this->_includePath = $includePath;
56 | }
57 |
58 | /**
59 | * Sets the namespace separator used by classes in the namespace of this class loader.
60 | *
61 | * @param string $sep The separator to use.
62 | */
63 | public function setNamespaceSeparator($sep)
64 | {
65 | $this->_namespaceSeparator = $sep;
66 | }
67 |
68 | /**
69 | * Gets the namespace seperator used by classes in the namespace of this class loader.
70 | *
71 | * @return void
72 | */
73 | public function getNamespaceSeparator()
74 | {
75 | return $this->_namespaceSeparator;
76 | }
77 |
78 | /**
79 | * Sets the base include path for all class files in the namespace of this class loader.
80 | *
81 | * @param string $includePath
82 | */
83 | public function setIncludePath($includePath)
84 | {
85 | $this->_includePath = $includePath;
86 | }
87 |
88 | /**
89 | * Gets the base include path for all class files in the namespace of this class loader.
90 | *
91 | * @return string $includePath
92 | */
93 | public function getIncludePath()
94 | {
95 | return $this->_includePath;
96 | }
97 |
98 | /**
99 | * Sets the file extension of class files in the namespace of this class loader.
100 | *
101 | * @param string $fileExtension
102 | */
103 | public function setFileExtension($fileExtension)
104 | {
105 | $this->_fileExtension = $fileExtension;
106 | }
107 |
108 | /**
109 | * Gets the file extension of class files in the namespace of this class loader.
110 | *
111 | * @return string $fileExtension
112 | */
113 | public function getFileExtension()
114 | {
115 | return $this->_fileExtension;
116 | }
117 |
118 | /**
119 | * Installs this class loader on the SPL autoload stack.
120 | */
121 | public function register()
122 | {
123 | spl_autoload_register(array($this, 'loadClass'));
124 | }
125 |
126 | /**
127 | * Uninstalls this class loader from the SPL autoloader stack.
128 | */
129 | public function unregister()
130 | {
131 | spl_autoload_unregister(array($this, 'loadClass'));
132 | }
133 |
134 | /**
135 | * Loads the given class or interface.
136 | *
137 | * @param string $className The name of the class to load.
138 | * @return void
139 | */
140 | public function loadClass($className)
141 | {
142 | if (null === $this->_namespace || $this->_namespace.$this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace.$this->_namespaceSeparator))) {
143 | $fileName = '';
144 | $namespace = '';
145 | if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) {
146 | $namespace = substr($className, 0, $lastNsPos);
147 | $className = substr($className, $lastNsPos + 1);
148 | $fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
149 | }
150 | $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension;
151 |
152 | require ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName;
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Analog - PHP micro logging package
2 |
3 | * Copyright: (c) 2012-Present Johnny Broadway
4 | * License: http://www.opensource.org/licenses/mit-license.php
5 |
6 |
7 |
8 | A PHP logging package based on the idea of using closures
9 | for configurability and extensibility. It functions as a static class, but you can
10 | completely control the writing of log messages through a closure function
11 | (aka [anonymous functions](http://ca3.php.net/manual/en/functions.anonymous.php)),
12 | or use the `Analog\Logger` wrapper that implements the
13 | [PSR-3 specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md).
14 |
15 | By default, this class will write to a file named `sys_get_temp_dir() . '/analog.txt'`
16 | using the format `"machine - date - level - message\n"`, making it usable with no
17 | customization necessary.
18 |
19 | Analog also comes with over a dozen pre-written handlers in the Analog/Handlers folder,
20 | with examples for each in the examples folder. These include:
21 |
22 | * Amon - Send logs to the [Amon](http://amon.cx/) server monitoring tool
23 | * Buffer - Buffer messages to send all at once (works with File, Mail, Stderr, and Variable handlers)
24 | * ChromeLogger - Sends messages to [Chrome Logger](http://craig.is/writing/chrome-logger) browser plugin
25 | * File - Append messages to a file
26 | * FirePHP - Send messages to [FirePHP](http://www.firephp.org/) browser plugin
27 | * GELF - Send message to the [Graylog2](http://www.graylog2.org/) log management server
28 | * Ignore - Do nothing
29 | * LevelBuffer - Buffer messages and send only if sufficient error level reached
30 | * LevelName - Convert log level numbers to names in log output
31 | * Mail - Send email notices
32 | * Mongo - Save to MongoDB collection
33 | * Multi - Send different log levels to different handlers
34 | * Post - Send messages over HTTP POST to another machine
35 | * Slackbot - Post messages to Slack via Slackbot
36 | * Stderr - Send messages to STDERR
37 | * Syslog - Send messages to syslog
38 | * Threshold - Only writes log messages above a certain threshold
39 | * Variable - Buffer messages to a variable reference.
40 |
41 | So while it's a micro class, it's highly extensible and very capable out of the box too.
42 |
43 | ### Rationale
44 |
45 | I wrote this because I wanted something very small and simple like
46 | [KLogger](https://github.com/katzgrau/KLogger), and preferably not torn out
47 | of a wider framework if possible. After searching, I wasn't happy with the
48 | single-purpose libraries I found. With KLogger for example, I didn't want an
49 | object instance but rather a static class, and I wanted more flexibility in
50 | the back-end.
51 |
52 | I also found some that had the flexibility also had more complexity, for example
53 | [Monolog](https://github.com/Seldaek/monolog) is dozens of source files (not incl. tests).
54 | With closures, this seemed to be a good balance of small without sacrificing
55 | flexibility.
56 |
57 | > What about Analog, the logfile analyzer? Well, since it hasn't been updated
58 | > since 2004, I think it's safe to call a single-file PHP logging class the
59 | > same thing without it being considered stepping on toes :)
60 |
61 | ### Usage
62 |
63 | Basic usage, with a custom handler function:
64 |
65 | ```php
66 | mydb->log->insert ($info);
80 | });
81 |
82 | // Log an alert
83 | Analog::log ('The sky is falling!', Analog::ALERT);
84 |
85 | // Log some debug info
86 | Analog::log ('Debugging info', Analog::DEBUG);
87 |
88 | ?>
89 | ```
90 |
91 | Usage with [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md),
92 | [Composer](http://getcomposer.org/), and the FirePHP handler:
93 |
94 | 1\. Create a `composer.json` file in the root of your project with the following contents.
95 |
96 | ```json
97 | {
98 | "require": {
99 | "analog/analog": "dev-master"
100 | }
101 | }
102 | ```
103 |
104 | 2\. Run `php composer.phar install` from the terminal in the root of your project.
105 |
106 | 3\. Include Composer's autoloader and use the `Analog\Analog` class.
107 |
108 | ```php
109 |
122 | ```
123 |
124 | Usage with [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md),
125 | [Composer](http://getcomposer.org/), and the Variable handler:
126 |
127 | 1\. Create a `composer.json` file in the root of your project with the following contents.
128 |
129 | ```json
130 | {
131 | "require": {
132 | "analog/analog": "dev-master"
133 | }
134 | }
135 | ```
136 |
137 | 2\. Run `php composer.phar install` from the terminal in the root of your project.
138 |
139 | 3\. Include Composer's autoloader and use the `Analog\Logger` class.
140 |
141 | ```php
142 | handler (Analog\Handler\Variable::init ($log));
151 |
152 | $logger->alert ('Things are really happening right now!');
153 |
154 | var_dump ($log);
155 |
156 | ?>
157 | ```
158 |
159 | For more examples, see the [examples](https://github.com/jbroadway/analog/tree/master/examples) folder.
160 |
--------------------------------------------------------------------------------
/lib/Analog/Logger.php:
--------------------------------------------------------------------------------
1 | notice ('Things are really happening right now.');
48 | *
49 | * ?>
50 | *
51 | * @package Analog
52 | * @author Johnny Broadway
53 | */
54 | class Logger implements LoggerInterface {
55 | /**
56 | * Converts from PSR-3 log levels to Analog log levels.
57 | */
58 | public function convert_log_level ($level, $reverse = false) {
59 | if ($reverse) {
60 | switch ($level) {
61 | case Analog::URGENT:
62 | return LogLevel::EMERGENCY;
63 | case Analog::ALERT:
64 | return LogLevel::ALERT;
65 | case Analog::CRITICAL:
66 | return LogLevel::CRITICAL;
67 | case Analog::ERROR:
68 | return LogLevel::ERROR;
69 | case Analog::WARNING:
70 | return LogLevel::WARNING;
71 | case Analog::NOTICE:
72 | return LogLevel::NOTICE;
73 | case Analog::INFO:
74 | return LogLevel::INFO;
75 | case Analog::DEBUG:
76 | return LogLevel::DEBUG;
77 | }
78 | throw new InvalidArgumentException ('Level "' . $level . '" is not defined.');
79 | } else {
80 | switch ($level) {
81 | case LogLevel::EMERGENCY:
82 | return Analog::URGENT;
83 | case LogLevel::ALERT:
84 | return Analog::ALERT;
85 | case LogLevel::CRITICAL:
86 | return Analog::CRITICAL;
87 | case LogLevel::ERROR:
88 | return Analog::ERROR;
89 | case LogLevel::WARNING:
90 | return Analog::WARNING;
91 | case LogLevel::NOTICE:
92 | return Analog::NOTICE;
93 | case LogLevel::INFO:
94 | return Analog::INFO;
95 | case LogLevel::DEBUG:
96 | return Analog::DEBUG;
97 | }
98 | throw new InvalidArgumentException ('Level "' . $level . '" is not defined.');
99 | }
100 | }
101 |
102 | /**
103 | * Interpolates context values into the message placeholders.
104 | */
105 | private function interpolate ($message, array $context = array ()) {
106 | if (is_array ($message)) {
107 | return $message;
108 | }
109 |
110 | // build a replacement array with braces around the context keys
111 | $replace = array ();
112 | foreach ($context as $key => $val) {
113 | if (is_object ($val) && get_class ($val) === 'DateTime') {
114 | $val = $val->format ('Y-m-d H:i:s');
115 | } elseif (is_object ($val)) {
116 | $val = json_encode ($val);
117 | } elseif (is_array ($val)) {
118 | $val = json_encode ($val);
119 | } elseif (is_resource ($val)) {
120 | $val = (string) $val;
121 | }
122 | $replace['{' . $key . '}'] = $val;
123 | }
124 |
125 | // interpolate replacement values into the the message and return
126 | return strtr ($message, $replace);
127 | }
128 |
129 | /**
130 | * Sets the Analog log handler.
131 | */
132 | public function handler ($handler) {
133 | Analog::handler ($handler);
134 | }
135 |
136 | /**
137 | * Sets the log message format.
138 | */
139 | public function format ($format) {
140 | Analog::$format = $format;
141 | }
142 |
143 | /**
144 | * System is unusable.
145 | */
146 | public function emergency ($message, array $context = array ()) {
147 | $this->_log (Analog::URGENT, $message, $context);
148 | }
149 |
150 | /**
151 | * Action must be taken immediately.
152 | */
153 | public function alert ($message, array $context = array ()) {
154 | $this->_log (Analog::ALERT, $message, $context);
155 | }
156 |
157 | /**
158 | * Critical conditions.
159 | */
160 | public function critical ($message, array $context = array ()) {
161 | $this->_log (Analog::CRITICAL, $message, $context);
162 | }
163 |
164 | /**
165 | * Runtime errors that do not require immediate action but should typically
166 | * be logged and monitored.
167 | */
168 | public function error ($message, array $context = array ()) {
169 | $this->_log (Analog::ERROR, $message, $context);
170 | }
171 |
172 | /**
173 | * Exceptional occurrences that are not errors.
174 | */
175 | public function warning ($message, array $context = array ()) {
176 | $this->_log (Analog::WARNING, $message, $context);
177 | }
178 |
179 | /**
180 | * Normal but significant events.
181 | */
182 | public function notice ($message, array $context = array ()) {
183 | $this->_log (Analog::NOTICE, $message, $context);
184 | }
185 |
186 | /**
187 | * Interesting events.
188 | */
189 | public function info ($message, array $context = array ()) {
190 | $this->_log (Analog::INFO, $message, $context);
191 | }
192 |
193 | /**
194 | * Detailed debug information.
195 | */
196 | public function debug ($message, array $context = array ()) {
197 | $this->_log (Analog::DEBUG, $message, $context);
198 | }
199 |
200 | /**
201 | * Logs with an arbitrary level.
202 | */
203 | public function log ($level, $message, array $context = array ()) {
204 | $this->_log (
205 | $this->convert_log_level ($level),
206 | $message,
207 | $context
208 | );
209 | }
210 |
211 | /**
212 | * Perform the logging to Analog after the log level has been converted.
213 | */
214 | private function _log ($level, $message, $context) {
215 | Analog::log (
216 | $this->interpolate ($message, $context),
217 | $level
218 | );
219 | }
220 | }
--------------------------------------------------------------------------------
/lib/Analog/Analog.php:
--------------------------------------------------------------------------------
1 | mydb->log->insert ($info);
68 | * });
69 | *
70 | * // Log an alert
71 | * Analog::log ('The sky is falling!', Analog::ALERT);
72 | *
73 | * // Log some debug info
74 | * Analog::log ('Debugging info', Analog::DEBUG);
75 | *
76 | * ?>
77 | *
78 | * @package Analog
79 | * @author Johnny Broadway
80 | */
81 | class Analog {
82 | /**
83 | * List of severity levels.
84 | */
85 | const URGENT = 0; // It's an emergency
86 | const ALERT = 1; // Immediate action required
87 | const CRITICAL = 2; // Critical conditions
88 | const ERROR = 3; // An error occurred
89 | const WARNING = 4; // Something unexpected happening
90 | const NOTICE = 5; // Something worth noting
91 | const INFO = 6; // Information, not an error
92 | const DEBUG = 7; // Debugging messages
93 |
94 | /**
95 | * The default format for log messages (machine, date, level, message)
96 | * written to a file. To change the order of items in the string,
97 | * use `%1$s` references.
98 | */
99 | public static $format = "%s - %s - %d - %s\n";
100 |
101 | /**
102 | * The default date/time format for log messages written to a file.
103 | * Feeds into the `$format` property.
104 | */
105 | public static $date_format = 'Y-m-d H:i:s';
106 |
107 | /**
108 | * Timezone for date/time values.
109 | */
110 | public static $timezone = 'GMT';
111 |
112 | /**
113 | * Default log level.
114 | */
115 | public static $default_level = 3;
116 |
117 | /**
118 | * The method of saving the log output. See Analog::handler()
119 | * for details on setting this.
120 | */
121 | private static $handler = null;
122 |
123 | /**
124 | * The name of the current machine, defaults to $_SERVER['SERVER_ADDR']
125 | * on first call to format_message(), or 'localhost' if $_SERVER['SERVER_ADDR']
126 | * is not set (e.g., during CLI use).
127 | */
128 | public static $machine = null;
129 |
130 | /**
131 | * Handler getter/setter. If no handler is provided, it will set it to
132 | * sys_get_temp_dir() . '/analog.txt' as a default. Usage:
133 | *
134 | * Analog::handler ('my_log.txt');
135 | *
136 | * Using a closure:
137 | *
138 | * Analog::handler (function ($msg) {
139 | * return error_log ($msg);
140 | * });
141 | */
142 | public static function handler ($handler = false) {
143 | if ($handler) {
144 | self::$handler = $handler;
145 | } elseif (! self::$handler) {
146 | self::$handler = realpath (sys_get_temp_dir ()) . DIRECTORY_SEPARATOR . 'analog.txt';
147 | }
148 | return self::$handler;
149 | }
150 |
151 | /**
152 | * Get the log info as an associative array.
153 | */
154 | private static function get_struct ($message, $level) {
155 | if (self::$machine === null) {
156 | self::$machine = (isset ($_SERVER['SERVER_ADDR'])) ? $_SERVER['SERVER_ADDR'] : 'localhost';
157 | }
158 |
159 | $dt = new \DateTime ('now', new \DateTimeZone (self::$timezone));
160 |
161 | return array (
162 | 'machine' => self::$machine,
163 | 'date' => $dt->format (self::$date_format),
164 | 'level' => $level,
165 | 'message' => $message
166 | );
167 | }
168 |
169 | /**
170 | * Write a raw message to the log using a function or the default
171 | * file logging.
172 | */
173 | private static function write ($struct) {
174 | $handler = self::handler ();
175 |
176 | if (! $handler instanceof \Closure) {
177 | $handler = \Analog\Handler\File::init ($handler);
178 | }
179 | return $handler ($struct);
180 | }
181 |
182 | /**
183 | * This is the main function you will call to log messages.
184 | * Defaults to severity level Analog::ERROR, which can be
185 | * changed via the `$default_level` property.
186 | * Usage:
187 | *
188 | * Analog::log ('Debug info', Analog::DEBUG);
189 | */
190 | public static function log ($message, $level = null) {
191 | $level = ($level !== null) ? $level : self::$default_level;
192 | return self::write (self::get_struct ($message, $level));
193 | }
194 |
195 | /**
196 | * Shortcut method for Analog::log($info, Analog::URGENT)
197 | * Usage:
198 | *
199 | * Analog::urgent ('Debug info');
200 | */
201 | public static function urgent ($message) {
202 | return self::write (self::get_struct ($message, self::URGENT));
203 | }
204 |
205 | /**
206 | * Shortcut method for Analog::log($info, Analog::ALERT)
207 | * Usage:
208 | *
209 | * Analog::alert ('Debug info');
210 | */
211 | public static function alert ($message) {
212 | return self::write (self::get_struct ($message, self::ALERT));
213 | }
214 |
215 | /**
216 | * Shortcut method for Analog::log($info, Analog::ERROR)
217 | * Usage:
218 | *
219 | * Analog::error ('Debug info');
220 | */
221 | public static function error ($message) {
222 | return self::write (self::get_struct ($message, self::ERROR));
223 | }
224 |
225 | /**
226 | * Shortcut method for Analog::log($info, Analog::WARNING)
227 | * Usage:
228 | *
229 | * Analog::warning ('Debug info');
230 | */
231 | public static function warning ($message) {
232 | return self::write (self::get_struct ($message, self::WARNING));
233 | }
234 |
235 | /**
236 | * Shortcut method for Analog::log($info, Analog::NOTICE)
237 | * Usage:
238 | *
239 | * Analog::notice ('Debug info');
240 | */
241 | public static function notice ($message) {
242 | return self::write (self::get_struct ($message, self::NOTICE));
243 | }
244 |
245 | /**
246 | * Shortcut method for Analog::log($info, Analog::INFO)
247 | * Usage:
248 | *
249 | * Analog::info ('Debug info');
250 | */
251 | public static function info ($message) {
252 | return self::write (self::get_struct ($message, self::INFO));
253 | }
254 |
255 | /**
256 | * Shortcut method for Analog::log($info, Analog::DEBUG)
257 | * Usage:
258 | *
259 | * Analog::debug ('Debug info');
260 | */
261 | public static function debug ($message) {
262 | return self::write (self::get_struct ($message, self::DEBUG));
263 | }
264 |
265 | }
--------------------------------------------------------------------------------
/lib/ChromePhp.php:
--------------------------------------------------------------------------------
1 |
23 | */
24 | class ChromePhp
25 | {
26 | /**
27 | * @var string
28 | */
29 | const VERSION = '4.0.0';
30 |
31 | /**
32 | * @var string
33 | */
34 | const HEADER_NAME = 'X-ChromeLogger-Data';
35 |
36 | /**
37 | * @var string
38 | */
39 | const BACKTRACE_LEVEL = 'backtrace_level';
40 |
41 | /**
42 | * @var string
43 | */
44 | const LOG = 'log';
45 |
46 | /**
47 | * @var string
48 | */
49 | const WARN = 'warn';
50 |
51 | /**
52 | * @var string
53 | */
54 | const ERROR = 'error';
55 |
56 | /**
57 | * @var string
58 | */
59 | const GROUP = 'group';
60 |
61 | /**
62 | * @var string
63 | */
64 | const INFO = 'info';
65 |
66 | /**
67 | * @var string
68 | */
69 | const GROUP_END = 'groupEnd';
70 |
71 | /**
72 | * @var string
73 | */
74 | const GROUP_COLLAPSED = 'groupCollapsed';
75 |
76 | /**
77 | * @var string
78 | */
79 | protected $_php_version;
80 |
81 | /**
82 | * @var int
83 | */
84 | protected $_timestamp;
85 |
86 | /**
87 | * @var array
88 | */
89 | protected $_json = array(
90 | 'version' => self::VERSION,
91 | 'columns' => array('log', 'backtrace', 'type'),
92 | 'rows' => array()
93 | );
94 |
95 | /**
96 | * @var array
97 | */
98 | protected $_backtraces = array();
99 |
100 | /**
101 | * @var bool
102 | */
103 | protected $_error_triggered = false;
104 |
105 | /**
106 | * @var array
107 | */
108 | protected $_settings = array(
109 | self::BACKTRACE_LEVEL => 1
110 | );
111 |
112 | /**
113 | * @var ChromePhp
114 | */
115 | protected static $_instance;
116 |
117 | /**
118 | * Prevent recursion when working with objects referring to each other
119 | *
120 | * @var array
121 | */
122 | protected $_processed = array();
123 |
124 | /**
125 | * constructor
126 | */
127 | private function __construct()
128 | {
129 | $this->_php_version = phpversion();
130 | $this->_timestamp = $this->_php_version >= 5.1 ? $_SERVER['REQUEST_TIME'] : time();
131 | $this->_json['request_uri'] = $_SERVER['REQUEST_URI'];
132 | }
133 |
134 | /**
135 | * gets instance of this class
136 | *
137 | * @return ChromePhp
138 | */
139 | public static function getInstance()
140 | {
141 | if (self::$_instance === null) {
142 | self::$_instance = new self();
143 | }
144 | return self::$_instance;
145 | }
146 |
147 | /**
148 | * logs a variable to the console
149 | *
150 | * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
151 | * @return void
152 | */
153 | public static function log()
154 | {
155 | return self::_log('', func_get_args());
156 | }
157 |
158 | /**
159 | * logs a warning to the console
160 | *
161 | * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
162 | * @return void
163 | */
164 | public static function warn()
165 | {
166 | return self::_log(self::WARN, func_get_args());
167 | }
168 |
169 | /**
170 | * logs an error to the console
171 | *
172 | * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
173 | * @return void
174 | */
175 | public static function error()
176 | {
177 | return self::_log(self::ERROR, func_get_args());
178 | }
179 |
180 | /**
181 | * sends a group log
182 | *
183 | * @param string value
184 | */
185 | public static function group()
186 | {
187 | return self::_log(self::GROUP, func_get_args());
188 | }
189 |
190 | /**
191 | * sends an info log
192 | *
193 | * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
194 | * @return void
195 | */
196 | public static function info()
197 | {
198 | return self::_log(self::INFO, func_get_args());
199 | }
200 |
201 | /**
202 | * sends a collapsed group log
203 | *
204 | * @param string value
205 | */
206 | public static function groupCollapsed()
207 | {
208 | return self::_log(self::GROUP_COLLAPSED, func_get_args());
209 | }
210 |
211 | /**
212 | * ends a group log
213 | *
214 | * @param string value
215 | */
216 | public static function groupEnd()
217 | {
218 | return self::_log(self::GROUP_END, func_get_args());
219 | }
220 |
221 | /**
222 | * internal logging call
223 | *
224 | * @param string $type
225 | * @return void
226 | */
227 | protected static function _log($type, array $args)
228 | {
229 | // nothing passed in, don't do anything
230 | if (count($args) == 0 && $type != self::GROUP_END) {
231 | return;
232 | }
233 |
234 | $logger = self::getInstance();
235 |
236 | $logger->_processed = array();
237 |
238 | $logs = array();
239 | foreach ($args as $arg) {
240 | $logs[] = $logger->_convert($arg);
241 | }
242 |
243 | $backtrace = debug_backtrace(false);
244 | $level = $logger->getSetting(self::BACKTRACE_LEVEL);
245 |
246 | $backtrace_message = 'unknown';
247 | if (isset($backtrace[$level]['file']) && isset($backtrace[$level]['line'])) {
248 | $backtrace_message = $backtrace[$level]['file'] . ' : ' . $backtrace[$level]['line'];
249 | }
250 |
251 | $logger->_addRow($logs, $backtrace_message, $type);
252 | }
253 |
254 | /**
255 | * converts an object to a better format for logging
256 | *
257 | * @param Object
258 | * @return array
259 | */
260 | protected function _convert($object)
261 | {
262 | // if this isn't an object then just return it
263 | if (!is_object($object)) {
264 | return $object;
265 | }
266 |
267 | //Mark this object as processed so we don't convert it twice and it
268 | //Also avoid recursion when objects refer to each other
269 | $this->_processed[] = $object;
270 |
271 | $object_as_array = array();
272 |
273 | // first add the class name
274 | $object_as_array['___class_name'] = get_class($object);
275 |
276 | // loop through object vars
277 | $object_vars = get_object_vars($object);
278 | foreach ($object_vars as $key => $value) {
279 |
280 | // same instance as parent object
281 | if ($value === $object || in_array($value, $this->_processed, true)) {
282 | $value = 'recursion - parent object [' . get_class($value) . ']';
283 | }
284 | $object_as_array[$key] = $this->_convert($value);
285 | }
286 |
287 | $reflection = new ReflectionClass($object);
288 |
289 | // loop through the properties and add those
290 | foreach ($reflection->getProperties() as $property) {
291 |
292 | // if one of these properties was already added above then ignore it
293 | if (array_key_exists($property->getName(), $object_vars)) {
294 | continue;
295 | }
296 | $type = $this->_getPropertyKey($property);
297 |
298 | if ($this->_php_version >= 5.3) {
299 | $property->setAccessible(true);
300 | }
301 |
302 | try {
303 | $value = $property->getValue($object);
304 | } catch (ReflectionException $e) {
305 | $value = 'only PHP 5.3 can access private/protected properties';
306 | }
307 |
308 | // same instance as parent object
309 | if ($value === $object || in_array($value, $this->_processed, true)) {
310 | $value = 'recursion - parent object [' . get_class($value) . ']';
311 | }
312 |
313 | $object_as_array[$type] = $this->_convert($value);
314 | }
315 | return $object_as_array;
316 | }
317 |
318 | /**
319 | * takes a reflection property and returns a nicely formatted key of the property name
320 | *
321 | * @param ReflectionProperty
322 | * @return string
323 | */
324 | protected function _getPropertyKey(ReflectionProperty $property)
325 | {
326 | $static = $property->isStatic() ? ' static' : '';
327 | if ($property->isPublic()) {
328 | return 'public' . $static . ' ' . $property->getName();
329 | }
330 |
331 | if ($property->isProtected()) {
332 | return 'protected' . $static . ' ' . $property->getName();
333 | }
334 |
335 | if ($property->isPrivate()) {
336 | return 'private' . $static . ' ' . $property->getName();
337 | }
338 | }
339 |
340 | /**
341 | * adds a value to the data array
342 | *
343 | * @var mixed
344 | * @return void
345 | */
346 | protected function _addRow(array $logs, $backtrace, $type)
347 | {
348 | // if this is logged on the same line for example in a loop, set it to null to save space
349 | if (in_array($backtrace, $this->_backtraces)) {
350 | $backtrace = null;
351 | }
352 |
353 | // for group, groupEnd, and groupCollapsed
354 | // take out the backtrace since it is not useful
355 | if ($type == self::GROUP || $type == self::GROUP_END || $type == self::GROUP_COLLAPSED) {
356 | $backtrace = null;
357 | }
358 |
359 | if ($backtrace !== null) {
360 | $this->_backtraces[] = $backtrace;
361 | }
362 |
363 | $row = array($logs, $backtrace, $type);
364 |
365 | $this->_json['rows'][] = $row;
366 | $this->_writeHeader($this->_json);
367 | }
368 |
369 | protected function _writeHeader($data)
370 | {
371 | header(self::HEADER_NAME . ': ' . $this->_encode($data));
372 | }
373 |
374 | /**
375 | * encodes the data to be sent along with the request
376 | *
377 | * @param array $data
378 | * @return string
379 | */
380 | protected function _encode($data)
381 | {
382 | return base64_encode(utf8_encode(json_encode($data)));
383 | }
384 |
385 | /**
386 | * adds a setting
387 | *
388 | * @param string key
389 | * @param mixed value
390 | * @return void
391 | */
392 | public function addSetting($key, $value)
393 | {
394 | $this->_settings[$key] = $value;
395 | }
396 |
397 | /**
398 | * add ability to set multiple settings in one call
399 | *
400 | * @param array $settings
401 | * @return void
402 | */
403 | public function addSettings(array $settings)
404 | {
405 | foreach ($settings as $key => $value) {
406 | $this->addSetting($key, $value);
407 | }
408 | }
409 |
410 | /**
411 | * gets a setting
412 | *
413 | * @param string key
414 | * @return mixed
415 | */
416 | public function getSetting($key)
417 | {
418 | if (!isset($this->_settings[$key])) {
419 | return null;
420 | }
421 | return $this->_settings[$key];
422 | }
423 | }
--------------------------------------------------------------------------------