]> git.mxchange.org Git - friendica.git/commitdiff
Fix review points
authorfabrixxm <fabrix.xm@gmail.com>
Fri, 20 Aug 2021 07:47:53 +0000 (09:47 +0200)
committerfabrixxm <fabrix.xm@gmail.com>
Fri, 20 Aug 2021 09:02:18 +0000 (11:02 +0200)
- Fix headers hierarchy
- Improve accessibility:
  - set mouse pointer
- make rows focusable
- open on key press
- add tooltip with "title"
- add role and aria attributes
- Rename `ParsedLog` to `ParsedLogLine`
- Add docs to `ReversedFileReader`'s implementation of `Iterator`'s methods
- Add docs to `ParsedLogIterator`'s implementation of `Iterator`'s methods
- Remove unnecessary comment
- Add more test for parsing log lines and fix some edge cases
- Fix function name in snake-case to camelCase
- Remove `DIRECTORY_SEPARATOR`

12 files changed:
src/Model/Log/ParsedLogIterator.php
src/Object/Log/ParsedLog.php [deleted file]
src/Object/Log/ParsedLogLine.php [new file with mode: 0644]
src/Util/ReversedFileReader.php
tests/src/Model/Log/ParsedLogIteratorTest.php
tests/src/Object/Log/ParsedLogLineTest.php [new file with mode: 0644]
tests/src/Object/Log/ParsedLogTest.php [deleted file]
view/js/module/admin/logs/view.js
view/templates/admin/logs/view.tpl
view/theme/frio/css/style.css
view/theme/frio/js/module/admin/logs/view.js
view/theme/frio/templates/admin/logs/view.tpl

index bde07ee4a83a31aef33e2610d701a3fe21c99b4a..856edb38fd8be912e31067746153469151761c41 100644 (file)
 namespace Friendica\Model\Log;
 
 use Friendica\Util\ReversedFileReader;
-use Friendica\Object\Log\ParsedLog;
+use Friendica\Object\Log\ParsedLogLine;
 
 /**
- * An iterator which returns `\Friendica\Objec\Log\ParsedLog` instances
+ * An iterator which returns `\Friendica\Objec\Log\ParsedLogLine` instances
  *
  * Uses `\Friendica\Util\ReversedFileReader` to fetch log lines
  * from newest to oldest.
@@ -35,7 +35,7 @@ class ParsedLogIterator implements \Iterator
        /** @var \Iterator */
        private $reader;
 
-       /** @var ParsedLog current iterator value*/
+       /** @var ParsedLogLine current iterator value*/
        private $value = null;
 
        /** @var int max number of lines to read */
@@ -100,19 +100,19 @@ class ParsedLogIterator implements \Iterator
         * Check if parsed log line match filters.
         * Always match if no filters are set.
         *
-        * @param ParsedLog $parsedlog
+        * @param ParsedLogLine $parsedlogline
         * @return bool
         */
-       private function filter($parsedlog)
+       private function filter($parsedlogline)
        {
                $match = true;
                foreach ($this->filters as $filter => $filtervalue) {
                        switch ($filter) {
                                case "level":
-                                       $match = $match && ($parsedlog->level == strtoupper($filtervalue));
+                                       $match = $match && ($parsedlogline->level == strtoupper($filtervalue));
                                        break;
                                case "context":
-                                       $match = $match && ($parsedlog->context == $filtervalue);
+                                       $match = $match && ($parsedlogline->context == $filtervalue);
                                        break;
                        }
                }
@@ -123,13 +123,13 @@ class ParsedLogIterator implements \Iterator
         * Check if parsed log line match search.
         * Always match if no search query is set.
         *
-        * @param ParsedLog $parsedlog
+        * @param ParsedLogLine $parsedlogline
         * @return bool
         */
-       private function search($parsedlog)
+       private function search($parsedlogline)
        {
                if ($this->search != "") {
-                       return strstr($parsedlog->logline, $this->search) !== false;
+                       return strstr($parsedlogline->logline, $this->search) !== false;
                }
                return true;
        }
@@ -138,8 +138,8 @@ class ParsedLogIterator implements \Iterator
         * Read a line from reader and parse.
         * Returns null if limit is reached or the reader is invalid.
         *
-        * @param ParsedLog $parsedlog
-        * @return ?ParsedLog
+        * @param ParsedLogLine $parsedlogline
+        * @return ?ParsedLogLine
         */
        private function read()
        {
@@ -149,22 +149,34 @@ class ParsedLogIterator implements \Iterator
                }
 
                $line = $this->reader->current();
-               return new ParsedLog($this->reader->key(), $line);
+               return new ParsedLogLine($this->reader->key(), $line);
        }
 
+
+       /**
+        * Fetch next parsed log line which match with filters or search and
+        * set it as current iterator value.
+        * 
+        * @see Iterator::next()
+        * @return void
+        */
        public function next()
        {
                $parsed = $this->read();
 
-               // if read() has not retuned none and
-               // the line don't match filters or search
-               //      read the next line
                while (is_null($parsed) == false && !($this->filter($parsed) && $this->search($parsed))) {
                        $parsed = $this->read();
                }
                $this->value = $parsed;
        }
 
+
+       /**
+        * Rewind the iterator to the first matching log line
+        * 
+        * @see Iterator::rewind()
+        * @return void
+        */
        public function rewind()
        {
                $this->value = null;
@@ -172,16 +184,35 @@ class ParsedLogIterator implements \Iterator
                $this->next();
        }
 
+       /**
+        * Return current parsed log line number
+        * 
+        * @see Iterator::key()
+        * @see ReversedFileReader::key()
+        * @return int
+        */
        public function key()
        {
                return $this->reader->key();
        }
 
+       /**
+        * Return current iterator value
+        * 
+        * @see Iterator::current()
+        * @return ?ParsedLogLing
+        */
        public function current()
        {
                return $this->value;
        }
 
+       /**
+        * Checks if current iterator value is valid, that is, not null
+        * 
+        * @see Iterator::valid()
+        * @return bool
+        */
        public function valid()
        {
                return ! is_null($this->value);
diff --git a/src/Object/Log/ParsedLog.php b/src/Object/Log/ParsedLog.php
deleted file mode 100644 (file)
index b1bc821..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-<?php
-/**
- * @copyright Copyright (C) 2021, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Object\Log;
-
-/**
- * Parse a log line and offer some utility methods
- */
-class ParsedLog
-{
-       const REGEXP = '/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[^ ]*) (\w+) \[(\w*)\]: (.*)/';
-
-       /** @var int */
-       public $id = 0;
-
-       /** @var string */
-       public $date = null;
-
-       /** @var string */
-       public $context = null;
-
-       /** @var string */
-       public $level = null;
-
-       /** @var string */
-       public $message = null;
-
-       /** @var string */
-       public $data = null;
-
-       /** @var string */
-       public $source = null;
-
-       /** @var string */
-       public $logline;
-
-       /**
-        * @param int line id
-        * @param string $logline Source log line to parse
-        */
-       public function __construct(int $id, string $logline)
-       {
-               $this->id = $id;
-               $this->parse($logline);
-       }
-
-       private function parse($logline)
-       {
-               // if data is empty is serialized as '[]'. To ease the parsing
-               // let's replace it with '{""}'. It will be replaced by null later
-               $logline = str_replace(' [] - {', ' {""} - {', $logline);
-
-               // here we hope that there will not be the string ' - {' inside the $jsonsource value
-               list($logline, $jsonsource) = explode(' - {', $logline);
-               $jsonsource                 = '{' . $jsonsource;
-
-               $jsondata = null;
-
-               if (strpos($logline, '{"') > 0) {
-                       list($logline, $jsondata) = explode('{"', $logline, 2);
-
-                       $jsondata = '{"' . $jsondata;
-               }
-
-               preg_match(self::REGEXP, $logline, $matches);
-
-               $this->date    = $matches[1];
-               $this->context = $matches[2];
-               $this->level   = $matches[3];
-               $this->message = trim($matches[4]);
-               $this->data    = $jsondata == '{""}' ? null : $jsondata;
-               $this->source  = $jsonsource;
-               $this->try_fix_json();
-
-               $this->logline = $logline;
-       }
-
-       /**
-        * Fix message / data split
-        *
-        * In log boundary between message and json data is not specified.
-        * If message  contains '{' the parser thinks there starts the json data.
-        * This method try to parse the found json and if it fails, search for next '{'
-        * in json data and retry
-        */
-       private function try_fix_json()
-       {
-               if (is_null($this->data) || $this->data == '') {
-                       return;
-               }
-               try {
-                       $d = json_decode($this->data, true, 512, JSON_THROW_ON_ERROR);
-               } catch (\JsonException $e) {
-                       // try to find next { in $str and move string before to 'message'
-
-                       $pos = strpos($this->data, '{', 1);
-
-                       $this->message .= substr($this->data, 0, $pos);
-                       $this->data = substr($this->data, $pos);
-                       $this->try_fix_json();
-               }
-       }
-
-       /**
-        * Return decoded `data` as array suitable for template
-        *
-        * @return array
-        */
-       public function get_data()
-       {
-               $data = json_decode($this->data, true);
-               if ($data) {
-                       foreach ($data as $k => $v) {
-                               $data[$k] = print_r($v, true);
-                       }
-               }
-               return $data;
-       }
-
-       /**
-        * Return decoded `source` as array suitable for template
-        *
-        * @return array
-        */
-       public function get_source()
-       {
-               return json_decode($this->source, true);
-       }
-}
diff --git a/src/Object/Log/ParsedLogLine.php b/src/Object/Log/ParsedLogLine.php
new file mode 100644 (file)
index 0000000..93a1537
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+/**
+ * @copyright Copyright (C) 2021, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Object\Log;
+
+/**
+ * Parse a log line and offer some utility methods
+ */
+class ParsedLogLine
+{
+       const REGEXP = '/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[^ ]*) (\w+) \[(\w*)\]: (.*)/';
+
+       /** @var int */
+       public $id = 0;
+
+       /** @var string */
+       public $date = null;
+
+       /** @var string */
+       public $context = null;
+
+       /** @var string */
+       public $level = null;
+
+       /** @var string */
+       public $message = null;
+
+       /** @var string */
+       public $data = null;
+
+       /** @var string */
+       public $source = null;
+
+       /** @var string */
+       public $logline;
+
+       /**
+        * @param int line id
+        * @param string $logline Source log line to parse
+        */
+       public function __construct(int $id, string $logline)
+       {
+               $this->id = $id;
+               $this->parse($logline);
+       }
+
+       private function parse($logline)
+       {
+               $this->logline = $logline;
+
+               // if data is empty is serialized as '[]'. To ease the parsing
+               // let's replace it with '{""}'. It will be replaced by null later
+               $logline = str_replace(' [] - {', ' {""} - {', $logline);
+
+
+               if (strstr($logline, ' - {') === false) {
+                       // the log line is not well formed
+                       $jsonsource = null;
+               } else {
+                       // here we hope that there will not be the string ' - {' inside the $jsonsource value
+                       list($logline, $jsonsource) = explode(' - {', $logline);
+                       $jsonsource                 = '{' . $jsonsource;
+               }
+
+               $jsondata = null;
+               if (strpos($logline, '{"') > 0) {
+                       list($logline, $jsondata) = explode('{"', $logline, 2);
+
+                       $jsondata = '{"' . $jsondata;
+               }
+
+               preg_match(self::REGEXP, $logline, $matches);
+
+               if (count($matches) == 0) {
+                       // regexp not matching
+                       $this->message = $this->logline;
+               } else {
+                       $this->date    = $matches[1];
+                       $this->context = $matches[2];
+                       $this->level   = $matches[3];
+                       $this->message = $matches[4];
+                       $this->data    = $jsondata == '{""}' ? null : $jsondata;
+                       $this->source  = $jsonsource;
+                       $this->tryfixjson();
+               }
+
+               $this->message = trim($this->message);
+       }
+
+       /**
+        * Fix message / data split
+        *
+        * In log boundary between message and json data is not specified.
+        * If message  contains '{' the parser thinks there starts the json data.
+        * This method try to parse the found json and if it fails, search for next '{'
+        * in json data and retry
+        */
+       private function tryfixjson()
+       {
+               if (is_null($this->data) || $this->data == '') {
+                       return;
+               }
+               try {
+                       $d = json_decode($this->data, true, 512, JSON_THROW_ON_ERROR);
+               } catch (\JsonException $e) {
+                       // try to find next { in $str and move string before to 'message'
+
+                       $pos = strpos($this->data, '{', 1);
+                       if ($pos === false) {
+                               $this->message .= $this->data;
+                               $this->data = null;
+                               return;
+                       }
+
+                       $this->message .= substr($this->data, 0, $pos);
+                       $this->data = substr($this->data, $pos);
+                       $this->tryfixjson();
+               }
+       }
+
+       /**
+        * Return decoded `data` as array suitable for template
+        *
+        * @return array
+        */
+       public function getData()
+       {
+               $data = json_decode($this->data, true);
+               if ($data) {
+                       foreach ($data as $k => $v) {
+                               $data[$k] = print_r($v, true);
+                       }
+               }
+               return $data;
+       }
+
+       /**
+        * Return decoded `source` as array suitable for template
+        *
+        * @return array
+        */
+       public function getSource()
+       {
+               return json_decode($this->source, true);
+       }
+}
index c4fe131aee9d68d8766b99a9edaabfabd97d518d..248792f17aa81786f43bda72f9fa49adfce4f797 100644 (file)
@@ -70,6 +70,11 @@ class ReversedFileReader implements \Iterator
                return $this;
        }
 
+       /**
+        * Read $size bytes behind last position
+        * 
+        * @return string
+        */
        private function _read($size)
        {
                $this->pos -= $size;
@@ -77,6 +82,12 @@ class ReversedFileReader implements \Iterator
                return fread($this->fh, $size);
        }
 
+       /**
+        * Read next line from end of file
+        * Return null if no lines are left to read
+        * 
+        * @return ?string
+        */
        private function _readline()
        {
                $buffer = & $this->buffer;
@@ -91,12 +102,24 @@ class ReversedFileReader implements \Iterator
                }
        }
 
+       /**
+        * Fetch next line from end and set it as current iterator value.
+        * 
+        * @see Iterator::next()
+        * @return void
+        */
        public function next()
        {
                ++$this->key;
                $this->value = $this->_readline();
        }
 
+       /**
+        * Rewind iterator to the first line at the end of file
+        * 
+        * @see Iterator::rewind()
+        * @return void 
+        */
        public function rewind()
        {
                if ($this->filesize > 0) {
@@ -108,16 +131,34 @@ class ReversedFileReader implements \Iterator
                }
        }
 
+       /**
+        * Return current line number, starting from zero at the end of file
+        * 
+        * @see Iterator::key()
+        * @return int
+        */
        public function key()
        {
                return $this->key;
        }
 
+       /**
+        * Return current line
+        * 
+        * @see Iterator::current()
+        * @return string
+        */
        public function current()
        {
                return $this->value;
        }
 
+       /**
+        * Checks if current iterator value is valid, that is, we readed all lines in files
+        * 
+        * @see Iterator::valid()
+        * @return bool
+        */
        public function valid()
        {
                return ! is_null($this->value);
index 846deab1cea31af42ae460302a3699b835badd77..df9f8393a97b81ae03d9e6331b1d7022cd14c840 100644 (file)
@@ -42,12 +42,7 @@ class ParsedLogIteratorTest extends TestCase
 
        protected function setUp(): void
        {
-               $logfile = dirname(__DIR__) . DIRECTORY_SEPARATOR .
-                       '..' . DIRECTORY_SEPARATOR .
-                       '..' . DIRECTORY_SEPARATOR .
-                       'datasets' . DIRECTORY_SEPARATOR .
-                       'log' . DIRECTORY_SEPARATOR .
-                       'friendica.log.txt';
+               $logfile = dirname(__DIR__) . '/../../datasets/log/friendica.log.txt';
 
                $reader    = new ReversedFileReader();
                $this->pli = new ParsedLogIterator($reader);
diff --git a/tests/src/Object/Log/ParsedLogLineTest.php b/tests/src/Object/Log/ParsedLogLineTest.php
new file mode 100644 (file)
index 0000000..9705c34
--- /dev/null
@@ -0,0 +1,183 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Test\src\Object\Log;
+
+use Friendica\Object\Log\ParsedLogLine;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Log parser testing class
+ */
+class ParsedLogLineTest extends TestCase
+{
+       public static function do_log_line($logline, $expected_data)
+       {
+               $parsed = new ParsedLogLine(0, $logline);
+               foreach ($expected_data as $k => $v) {
+                       self::assertSame($parsed->$k, $v, '"'.$k.'" does not match expectation');
+               }
+       }
+
+       /**
+        * test parsing a generic log line
+        */
+       public function testGenericLogLine()
+       {
+               self::do_log_line(
+                       '2021-05-24T15:40:01Z worker [WARNING]: Spool file does does not start with "item-" {"file":".","worker_id":"560c8b6","worker_cmd":"SpoolPost"} - {"file":"SpoolPost.php","line":40,"function":"execute","uid":"fd8c37","process_id":20846}',
+                       [
+                               'date'    => '2021-05-24T15:40:01Z',
+                               'context' => 'worker',
+                               'level'   => 'WARNING',
+                               'message' => 'Spool file does does not start with "item-"',
+                               'data'    => '{"file":".","worker_id":"560c8b6","worker_cmd":"SpoolPost"}',
+                               'source'  => '{"file":"SpoolPost.php","line":40,"function":"execute","uid":"fd8c37","process_id":20846}',
+                       ]
+               );
+       }
+
+       /**
+        * test parsing a log line with empty data
+        */
+       public function testEmptyDataLogLine()
+       {
+               self::do_log_line(
+                       '2021-05-24T15:23:58Z index [INFO]: No HTTP_SIGNATURE header [] - {"file":"HTTPSignature.php","line":476,"function":"getSigner","uid":"0a3934","process_id":14826}',
+                       [
+                               'date'    => '2021-05-24T15:23:58Z',
+                               'context' => 'index',
+                               'level'   => 'INFO',
+                               'message' => 'No HTTP_SIGNATURE header',
+                               'data'    => null,
+                               'source'  => '{"file":"HTTPSignature.php","line":476,"function":"getSigner","uid":"0a3934","process_id":14826}',
+                       ]
+               );
+       }
+
+       /**
+        * test parsing a log line with various " - " in it
+        */
+       public function testTrickyDashLogLine()
+       {
+               self::do_log_line(
+                       '2021-05-24T15:30:01Z worker [NOTICE]: Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10 {"worker_id":"ece8fc8","worker_cmd":"Cron"} - {"file":"Worker.php","line":786,"function":"tooMuchWorkers","uid":"364d3c","process_id":20754}',
+                       [
+                               'date'    => '2021-05-24T15:30:01Z',
+                               'context' => 'worker',
+                               'level'   => 'NOTICE',
+                               'message' => 'Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10',
+                               'data'    => '{"worker_id":"ece8fc8","worker_cmd":"Cron"}',
+                               'source'  => '{"file":"Worker.php","line":786,"function":"tooMuchWorkers","uid":"364d3c","process_id":20754}',
+                       ]
+               );
+       }
+
+       /**
+        * test non conforming log line
+        */
+       public function testNonConformingLogLine()
+       {
+               self::do_log_line(
+                       'this log line is not formatted as expected',
+                       [
+                               'date'    => null,
+                               'context' => null,
+                               'level'   => null,
+                               'message' => 'this log line is not formatted as expected',
+                               'data'    => null,
+                               'source'  => null,
+                       ]
+               );
+       }
+
+       /**
+        * test missing source
+        */
+       public function testMissingSource()
+       {
+               self::do_log_line(
+                       '2021-05-24T15:30:01Z worker [NOTICE]: Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10 {"worker_id":"ece8fc8","worker_cmd":"Cron"}',
+                       [
+                               'date'    => '2021-05-24T15:30:01Z',
+                               'context' => 'worker',
+                               'level'   => 'NOTICE',
+                               'message' => 'Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10',
+                               'data'    => '{"worker_id":"ece8fc8","worker_cmd":"Cron"}',
+                               'source'  => null,
+                       ]
+               );
+       }
+
+       /**
+        * test missing data
+        */
+       public function testMissingData()
+       {
+               self::do_log_line(
+                       '2021-05-24T15:30:01Z worker [NOTICE]: Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10 - {"file":"Worker.php","line":786,"function":"tooMuchWorkers","uid":"364d3c","process_id":20754}',
+                       [
+                               'date'    => '2021-05-24T15:30:01Z',
+                               'context' => 'worker',
+                               'level'   => 'NOTICE',
+                               'message' => 'Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10',
+                               'data'    => null,
+                               'source'  => '{"file":"Worker.php","line":786,"function":"tooMuchWorkers","uid":"364d3c","process_id":20754}',
+                       ]
+               );
+       }
+
+       /**
+        * test missing data and source
+        */
+       public function testMissingDataAndSource()
+       {
+               self::do_log_line(
+                       '2021-05-24T15:30:01Z worker [NOTICE]: Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10',
+                       [
+                               'date'    => '2021-05-24T15:30:01Z',
+                               'context' => 'worker',
+                               'level'   => 'NOTICE',
+                               'message' => 'Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10',
+                               'data'    => null,
+                               'source'  => null,
+                       ]
+               );
+       }
+
+       /**
+        * test missing source and invalid data
+        */
+       public function testMissingSourceAndInvalidData()
+       {
+               self::do_log_line(
+                       '2021-05-24T15:30:01Z worker [NOTICE]: Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10 {"invalidjson {really',
+                       [
+                               'date'    => '2021-05-24T15:30:01Z',
+                               'context' => 'worker',
+                               'level'   => 'NOTICE',
+                               'message' => 'Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10 {"invalidjson {really',
+                               'data'    => null,
+                               'source'  => null,
+                       ]
+               );
+       }
+}
diff --git a/tests/src/Object/Log/ParsedLogTest.php b/tests/src/Object/Log/ParsedLogTest.php
deleted file mode 100644 (file)
index ff3d06f..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-/**
- * @copyright Copyright (C) 2010-2021, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Test\src\Object\Log;
-
-use Friendica\Object\Log\ParsedLog;
-use PHPUnit\Framework\TestCase;
-
-/**
- * Log parser testing class
- */
-class ParsedLogTest extends TestCase
-{
-       public static function do_log_line($logline, $expected_data)
-       {
-               $parsed = new ParsedLog(0, $logline);
-               foreach ($expected_data as $k => $v) {
-                       self::assertSame($parsed->$k, $v, '"'.$k.'" does not match expectation');
-               }
-       }
-
-       /**
-        * test parsing a generic log line
-        */
-       public function testGenericLogLine()
-       {
-               self::do_log_line(
-                       '2021-05-24T15:40:01Z worker [WARNING]: Spool file does does not start with "item-" {"file":".","worker_id":"560c8b6","worker_cmd":"SpoolPost"} - {"file":"SpoolPost.php","line":40,"function":"execute","uid":"fd8c37","process_id":20846}',
-                       [
-                               'date'    => '2021-05-24T15:40:01Z',
-                               'context' => 'worker',
-                               'level'   => 'WARNING',
-                               'message' => 'Spool file does does not start with "item-"',
-                               'data'    => '{"file":".","worker_id":"560c8b6","worker_cmd":"SpoolPost"}',
-                               'source'  => '{"file":"SpoolPost.php","line":40,"function":"execute","uid":"fd8c37","process_id":20846}',
-                       ]
-               );
-       }
-
-       /**
-        * test parsing a log line with empty data
-        */
-       public function testEmptyDataLogLine()
-       {
-               self::do_log_line(
-                       '2021-05-24T15:23:58Z index [INFO]: No HTTP_SIGNATURE header [] - {"file":"HTTPSignature.php","line":476,"function":"getSigner","uid":"0a3934","process_id":14826}',
-                       [
-                               'date'    => '2021-05-24T15:23:58Z',
-                               'context' => 'index',
-                               'level'   => 'INFO',
-                               'message' => 'No HTTP_SIGNATURE header',
-                               'data'    => null,
-                               'source'  => '{"file":"HTTPSignature.php","line":476,"function":"getSigner","uid":"0a3934","process_id":14826}',
-                       ]
-               );
-       }
-
-       /**
-        * test parsing a log line with various " - " in it
-        */
-       public function testTrickyDashLogLine()
-       {
-               self::do_log_line(
-                       '2021-05-24T15:30:01Z worker [NOTICE]: Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10 {"worker_id":"ece8fc8","worker_cmd":"Cron"} - {"file":"Worker.php","line":786,"function":"tooMuchWorkers","uid":"364d3c","process_id":20754}',
-                       [
-                               'date'    => '2021-05-24T15:30:01Z',
-                               'context' => 'worker',
-                               'level'   => 'NOTICE',
-                               'message' => 'Load: 0.01/20 - processes: 0/1/6 (0:0, 30:1) - maximum: 10/10',
-                               'data'    => '{"worker_id":"ece8fc8","worker_cmd":"Cron"}',
-                               'source'  => '{"file":"Worker.php","line":786,"function":"tooMuchWorkers","uid":"364d3c","process_id":20754}',
-                       ]
-               );
-       }
-}
index 45d08a5ed177fc0a1df25c9b9b966e414aa2b3c9..5ad4e0a71718ae270aca3627d405995fedcc6594 100644 (file)
@@ -1,7 +1,33 @@
-function log_show_details(id) {
+(function(){
+       function log_show_details(elm) {
+               const id = elm.id;
+               var hidden = true;
+               document
+                       .querySelectorAll('[data-id="' + id + '"]')
+                       .forEach(edetails => {
+                               hidden = edetails.classList.toggle('hidden');
+                       });
+               document
+                       .querySelectorAll('[aria-expanded="true"]')
+                       .forEach(eexpanded => {
+                               eexpanded.setAttribute('aria-expanded', false);
+                       });
+               
+               if (!hidden) {
+                       elm.setAttribute('aria-expanded', true);
+               }
+       }
+
        document
-               .querySelectorAll('[data-id="' + id + '"]')
+               .querySelectorAll('.log-event')
                .forEach(elm => {
-                       elm.classList.toggle('hidden')
+                       elm.addEventListener("click", evt => {
+                               log_show_details(evt.currentTarget);
+                       });
+                       elm.addEventListener("keydown", evt => {
+                               if (evt.keyCode == 13 || evt.keyCode == 32) {
+                                       log_show_details(evt.currentTarget);
+                               }
+                       });
                });
-}
+})();
\ No newline at end of file
index 523cb6a2014c5a8845a42820e8699e04d19f128a..e15a4a01b31db659f3965ab512e5bc2bd940dafc 100644 (file)
@@ -1,7 +1,7 @@
 <div id="adminpage">
        <h1>{{$title}} - {{$page}}</h1>
 
-       <h3>{{$logname}}</h3>
+       <h2>{{$logname}}</h2>
        {{if $error }}
                <div id="admin-error-message-wrapper" class="alert alert-warning">
                        <p>{{$error nofilter}}</p>
                                </thead>
                                <tbody>
                                        {{foreach $data as $row}}
-                                               <tr id="ev-{{$row->id}}" onClick="log_show_details('ev-{{$row->id}}')">
+                                               <tr id="ev-{{$row->id}}" class="log-event"
+                                                role="button" tabIndex="0"
+                                                aria-label="View details" aria-haspopup="true" aria-expanded="false"
+                                                style="cursor:pointer;"
+                                                title="Click to view details">
                                                        <td>{{$row->date}}</td>
                                                        <td>{{$row->level}}</td>
                                                        <td>{{$row->context}}</td>
                                                        <td>{{$row->message}}</td>
                                                </tr>
                                                <tr class="hidden" data-id="ev-{{$row->id}}"><th colspan="4">Data</th></tr>
-                                               {{foreach $row->get_data() as $k=>$v}}
+                                               {{foreach $row->getData() as $k=>$v}}
                                                        <tr class="hidden" data-id="ev-{{$row->id}}">
                                                                <th>{{$k}}</th>
                                                                <td colspan="3">
@@ -60,7 +64,7 @@
                                                        </tr>
                                                {{/foreach}}
                                                <tr class="hidden" data-id="ev-{{$row->id}}"><th colspan="4">Source</th></tr>
-                                               {{foreach $row->get_source() as $k=>$v}}
+                                               {{foreach $row->getSource() as $k=>$v}}
                                                        <tr class="hidden" data-id="ev-{{$row->id}}">
                                                                <th>{{$k}}</th>
                                                                <td colspan="3">{{$v}}</td>
index 1bf908e1da7f83876e065c0a5462a423644223d4..bcf1557ae6dabeec3d05b96040801eaa5146c202 100644 (file)
@@ -98,6 +98,13 @@ details summary {
        display: list-item;
 }
 
+/**
+ * clickable table rows
+ */
+.table > tbody > td[role="button"] {
+       cursor: pointer;
+}
+
 /**
  * mobile aside
  */
index 149d019e90c23737802cb5129eabbab08bf4c843..245518822f10153372aaca951b597b482dc48288 100644 (file)
@@ -21,6 +21,12 @@ $(function(){
        $(".log-event").on("click", function(ev) {
                show_details_for_element(ev.currentTarget);
        });
+       $(".log-event").on("keydown", function(ev) {
+               if (ev.keyCode == 13 || ev.keyCode == 32) {
+                       show_details_for_element(ev.currentTarget);
+               }
+       });
+
 
        $("[data-previous").on("click", function(ev){ 
                var currentid = document.getElementById("logdetail").dataset.rowId;
@@ -37,9 +43,15 @@ $(function(){
        });
 
 
-       function show_details_for_element(element) {
-               var $modal = $("#logdetail");
+       const $modal = $("#logdetail");
 
+       $modal.on("hidden.bs.modal", function(ev){
+               document
+                       .querySelectorAll('[aria-expanded="true"]')
+                       .forEach(elm => elm.setAttribute("aria-expanded", false))
+       });
+
+       function show_details_for_element(element) {
                $modal[0].dataset.rowId = element.id;
 
                var tr = $modal.find(".main-data tbody tr")[0];
@@ -64,6 +76,7 @@ $(function(){
                $("[data-next").prop("disabled", $(element).next().length == 0);
                
                $modal.modal({})
+               element.setAttribute("aria-expanded", true);
        }
 
        function recursive_details(s, data, lev=0) {
index 1bc02691607fcf8f69e3ee9475215e2a038195fc..1e1123fbd917b0b62d6719b95f2059a99b7450d0 100755 (executable)
@@ -1,7 +1,7 @@
 <div id="adminpage">
        <h1>{{$title}} - {{$page}}</h1>
 
-       <h3>{{$logname}}</h3>
+       <h2>{{$logname}}</h2>
        {{if $error }}
                <div id="admin-error-message-wrapper" class="alert alert-warning">
                        <p>{{$error nofilter}}</p>
@@ -59,7 +59,9 @@
                        </thead>
                        <tbody>
                                {{foreach $data as $row}}
-                               <tr id="ev-{{$row->id}}" class="log-event"
+                               <tr id="ev-{{$row->id}}" class="log-event" 
+                                       role="button" tabIndex="0"
+                                       aria-label="View details" aria-haspopup="true" aria-expanded="false"
                                        data-data="{{$row->data}}" data-source="{{$row->source}}">
                                        <td>{{$row->date}}</td>
                                        <td class="