├── .gitignore
├── .travis.yml
├── LICENSE
├── Makefile
├── README.md
├── bin
├── .gitkeep
└── epilog
├── bootstrap.php
├── box.json
├── commands.php
├── composer.json
├── epilog
├── phpunit.xml.dist
├── src
├── DataBag.php
├── Epilog.php
├── FakeLogTail.php
├── FlowException.php
├── InputReader.php
├── Interfaces
│ ├── InputReaderInterface.php
│ ├── LineParserInterface.php
│ ├── LinePrinterInterface.php
│ ├── TailInterface.php
│ └── ThemeInterface.php
├── LogFinder
│ ├── Generic.php
│ └── Laravel.php
├── LogFinderFactory.php
├── LogTail.php
├── MonologLineParser.php
├── MonologLinePrinter.php
├── Theme.php
└── Ticker.php
├── test
├── DataBagTest.php
├── EpilogTest.php
├── InputReaderTest.php
├── LogFinderTest.php
├── LogTailTest.php
├── MonologLineParserTest.php
├── TickerTest.php
└── fixtures
│ ├── laravel_a
│ └── app
│ │ └── storage
│ │ └── logs
│ │ └── log.log
│ └── laravel_b
│ └── app
│ └── storage
│ └── logs
│ └── log.txt
└── themes
├── chaplin.yml
├── default.yml
├── forest.yml
├── punchcard.yml
├── scrapbook.yml
├── sunrise.yml
├── sunset.yml
└── traffic.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | build/
3 | composer.lock
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | - 7.0
8 | - hhvm
9 |
10 | matrix:
11 | allow_failures:
12 | - php: hhvm
13 | - php: 7.0
14 |
15 | before_script:
16 | - composer self-update
17 | - composer require satooshi/php-coveralls:dev-master --no-update --dev
18 | - composer install --dev --prefer-source
19 |
20 | after_script:
21 | - php vendor/bin/coveralls
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Márcio Almada
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | COMPILE = box build
2 | TARGET = bin/epilog.phar
3 | OUTPUT = bin/epilog
4 | DELETE_FILE = rm
5 | CHMOD_FILE = chmod +x
6 | MOVE_FILE = mv
7 | CP_FILE = cp
8 | INSTALL_PATH = /usr/local/bin
9 |
10 | default:
11 | $(COMPILE)
12 | $(MOVE_FILE) $(TARGET) $(OUTPUT)
13 | $(CHMOD_FILE) $(OUTPUT)
14 |
15 | run:
16 | ./$(OUTPUT) --help
17 |
18 | pretend:
19 | ./$(OUTPUT) pretend
20 |
21 | clean:
22 | $(DELETE_FILE) $(OUTPUT)
23 |
24 | install:
25 | $(CP_FILE) $(OUTPUT) $(INSTALL_PATH)
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [![Build Status][t-badge]][t-link]
6 | [![Coverage Status][c-badge]][c-link]
7 | [![Scrutinizer Quality Score][s-badge]][s-link]
8 | [![Latest Stable Version][v-badge]][p-link]
9 | [![Total Downloads][d-badge]][p-link]
10 | [![License][l-badge]][p-link]
11 | [![SensioLabsInsight][sl-badge]][sl-link]
12 |
13 | The lightweight, themeable and interactive PSR-3 log viewer. Monitor monologs with style:
14 |
15 | [](https://asciinema.org/a/12309?autoplay=true&speed=2)
16 |
17 | ## Install
18 |
19 | As a composer project dependency:
20 |
21 | ```json
22 | {
23 | "require": {
24 | "marc/epilog": "~1.0"
25 | }
26 | }
27 | ```
28 |
29 | As a packed phar:
30 |
31 | ```bash
32 | wget https://github.com/marcioAlmada/epilog/raw/master/bin/epilog && chmod +x epilog
33 | mv epilog /usr/local/bin/ # or somewhere in your $PATH
34 | ```
35 |
36 | As a composer global package:
37 |
38 | ```bash
39 | composer global require marc/epilog:~1.0
40 | ```
41 |
42 | Or build the phar yourself:
43 |
44 | ```bash
45 | # install box2 -> https://github.com/box-project/box2
46 | git clone https://github.com/marcioAlmada/epilog.git
47 | cd epilog
48 | composer install
49 | make # phar will available at bin/ folder
50 | sudo make install # will put epilog at /usr/local/bin/ folder
51 | ```
52 |
53 | ### Quick test
54 |
55 | Run **epilog** `pretend` command and tail a fake log stream. Check if output looks good:
56 |
57 | ```bash
58 | epilog pretend
59 | ```
60 |
61 | ## Usage
62 |
63 | Basic usage is:
64 |
65 | ```bash
66 | epilog watch /path/to/monolog/file.log []...
67 | ```
68 |
69 | While epilog is monitoring the log file (or a fake stream), hit `[return]` to see a nice interactive menu:
70 |
71 | ```
72 | $ [ ⏎ ]
73 |
74 | Woot! Epilog here. Please type a theme number, a valid regexp or a valid flag:
75 |
76 | [#] load another theme:
77 |
78 | 1:chaplin 2:forest
79 | 3:scrapbook 4:punchcard
80 | 5:sunset 6:sunrise
81 | 7:traffic 8:usa
82 |
83 | [ r ] load random theme from list above.
84 | [ i ] toggle invert theme.
85 | [ c ] clear screen.
86 | [ - ] reset regexp filter.
87 | [ q ] quit.
88 | ```
89 |
90 | ## Framework Integration
91 |
92 | Epilog can use [log finders](https://github.com/marcioAlmada/epilog/tree/master/src/LogFinder) to look for
93 | logs inside app directories according to framework conventions so you don't need to aim at your latest
94 | log files mannualy. Ex:
95 |
96 | ```bash
97 | $ epilog watch ~/www/my-project --app laravel
98 | # instead of long and boring
99 | $ epilog watch ~/www/my-project/app/storage/logs/log-cli-server-2015-01-11.txt
100 | ```
101 |
102 | > PRs implementing log finders to support your framework of choice are highly encouraged :octocat:
103 |
104 | ## Themes
105 |
106 | Epilog themes are very simple `yml` files with hooks where you can put ANSI color escape sequences and literal text.
107 | The hooks will decorate each log line, [nyancatyze](http://youtu.be/QH2-TGUlwu4) your terminal
108 | and sometimes [puke rainbows](http://youtu.be/-Cec2iAgW1Q). Here is a theme example:
109 |
110 | ```yaml
111 | name: Punched Card
112 | author: Márcio Almada
113 | theme:
114 | extends: default
115 | # the log line template
116 | # template tags are: {date}, {level}, {logger}, {message}, {context}, {extra}
117 | template: "{level} {date} {message} [{logger}] [{context}] [{extra}]"
118 | # literal string that will be prepended to entire line
119 | prepend: ""
120 | # literal string that will be appended to entire line
121 | append: "\e[0m\n"
122 | # level section
123 | level:
124 | padding: 10
125 | DEBUG:
126 | prepend: " • \e[2m"
127 | INFO:
128 | prepend: " • \e[2m"
129 | NOTICE:
130 | prepend: " • \e[2m"
131 | WARNING:
132 | prepend: " • \e[2m"
133 | ERROR:
134 | prepend: " • \e[1m"
135 | CRITICAL:
136 | prepend: " • \e[1m"
137 | ALERT:
138 | prepend: " • \e[1m"
139 | EMERGENCY:
140 | prepend: " • \e[1m"
141 | DEFAULT:
142 | prepend: " -------- \e[1m"
143 | ```
144 |
145 | Which will make log lines look like the following, when interpreted:
146 |
147 | 
148 |
149 | ## Roadmap
150 |
151 | - [x] Basic functionalities
152 | - [x] Add `--app` option to allow easy framework integration. Ex: `epilog watch /my/project --app laravel`
153 | - [ ] Add `listen` command to aggregate log entries through a REST API. Ex: `epilog listen --port 8888`
154 | - [ ] Add `server` command to view logs in a browser instead of terminal `epilog server --port 8888`
155 | - [ ] Add better unicode support, more themes etc
156 | - [ ] Bother with windows ... anyone?
157 | - [ ] Other cool things, probably
158 | - [ ] Release stable version
159 |
160 | ## Contributions
161 |
162 | Found a bug? Have an improvement? Take a look at the [issues](https://github.com/marcioAlmada/epilog/issues).
163 |
164 | ### Guide
165 |
166 | 0. Fork [marc/epilog](https://github.com/marcioAlmada/epilog/fork)
167 | 0. Clone forked repository
168 | 0. Install composer dependencies `$ composer install`
169 | 0. Run unit tests `$ phpunit`
170 | 0. Modify code: correct bug, implement features
171 | 0. Back to step 4
172 |
173 | ## Copyright
174 |
175 | Copyright (c) 2014-2015 Márcio Almada. Distributed under the terms of an MIT-style license.
176 | See LICENSE for details.
177 |
178 | [t-link]: https://travis-ci.org/marcioAlmada/epilog "Travis Build"
179 | [c-link]: https://coveralls.io/r/marcioAlmada/epilog?branch=master "Code Coverage"
180 | [s-link]: https://scrutinizer-ci.com/g/marcioAlmada/epilog/?branch=master "Code Quality"
181 | [p-link]: https://packagist.org/packages/marc/epilog "Packagist"
182 | [sl-link]: https://insight.sensiolabs.com/projects/02d1fd01-8a70-4fe4-a550-381a3c0e33f3 "Sensiolabs Insight"
183 |
184 | [t-badge]: https://travis-ci.org/marcioAlmada/epilog.png?branch=master
185 | [c-badge]: https://coveralls.io/repos/marcioAlmada/epilog/badge.png?branch=master
186 | [s-badge]: https://scrutinizer-ci.com/g/marcioAlmada/epilog/badges/quality-score.png?b=master
187 | [v-badge]: https://poser.pugx.org/marc/epilog/v/stable.png
188 | [d-badge]: https://poser.pugx.org/marc/epilog/downloads.png
189 | [l-badge]: https://poser.pugx.org/marc/epilog/license.png
190 | [sl-badge]: https://insight.sensiolabs.com/projects/02d1fd01-8a70-4fe4-a550-381a3c0e33f3/mini.png
191 |
--------------------------------------------------------------------------------
/bin/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marcioAlmada/epilog/e5d8d718c4f929a2a723383261f3687f179d480d/bin/.gitkeep
--------------------------------------------------------------------------------
/bin/epilog:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marcioAlmada/epilog/e5d8d718c4f929a2a723383261f3687f179d480d/bin/epilog
--------------------------------------------------------------------------------
/bootstrap.php:
--------------------------------------------------------------------------------
1 | [...]
22 | *
23 | * Commands are:
24 | * epilog watch Monitor a log files
25 | * epilog pretend Display a fake log stream, for testing only
26 | *
27 | * See 'epilog -h' to read about a specific subcommand.
28 | *
29 | * Options:
30 | * -h --help Show this screen.
31 | * -v --version Show version.
32 | */
33 | function epilog()
34 | {
35 | $specReader = AnnotationsReader::createFromDefaults();
36 | $handler = new Handler([
37 | 'version'=> 'Epilog ' . Epilog::VERSION . ' by Márcio Almada', 'optionsFirst' => true ]);
38 |
39 | $response = $handler->handle(
40 | $specReader->getFunctionAnnotations(__FUNCTION__)->get('command.spec'));
41 |
42 | $command = __NAMESPACE__ . '\\' . $response[''];
43 |
44 | if(! function_exists($command))
45 | throw new FlowException("Command `{$response['']}` not defined, try epilog --help", 1);
46 |
47 | $handler->optionsFirst = false;
48 | $response = $handler->handle(
49 | $specReader->getFunctionAnnotations($command)->get('command.spec'));
50 |
51 | $command($response);
52 | }
53 |
54 | /**
55 | * @command.spec
56 | *
57 | * Usage:
58 | * epilog pretend [--filter=][--sleep-interval=][--theme=][--theme-invert][--no-follow][--debug]
59 | *
60 | * Options:
61 | * -f --filter= Filter log entries with a given regular expression.
62 | * -s --sleep-interval= Sleep interval [default: .5].
63 | * -t --theme= Theme, see `Epilogs themes` to see list the list of themes [default: sunrise].
64 | * -i --theme-invert Invert theme foreground vs background colors.
65 | * -n --no-follow Print last logged lines and quit.
66 | * -d --debug Reloads theme on every loop. Slow, but useful while building new themes.
67 | * -h --help Show this screen.
68 | */
69 | function pretend(Response $response)
70 | {
71 | (new Epilog($response))->run(new FakeLogTail);
72 | }
73 |
74 | /**
75 | * @command.spec
76 | *
77 | * Usage:
78 | * epilog watch [--filter=][--sleep-interval=][--theme=][--theme-invert][--no-follow][--app=][--debug]
79 | *
80 | * Options:
81 | * -f --filter= Filter log entries with a given regular expression.
82 | * -s --sleep-interval= Sleep interval [default: .5].
83 | * -t --theme= Theme, see `Epilogs themes` to see list the list of themes [default: sunrise].
84 | * -i --theme-invert Invert theme foreground vs background colors.
85 | * -n --no-follow Print last logged lines and quit.
86 | * -a --app= Finds latest project log automatically by framework (ex: --app laravel) [default: generic].
87 | * -d --debug Reloads theme on every loop. Slow, but useful while building new themes.
88 | * -h --help Show this screen.
89 | */
90 | function watch(Response $response)
91 | {
92 | $epilog = new Epilog($response);
93 | $logFinder = LogFinderFactory::getLogFinder($response['--app']);
94 | $log = new LogTail($logFinder->find($response['']));
95 | $epilog->run($log);
96 | }
97 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "marc/epilog",
3 | "description": "Epilog is a CLI log viewer with style",
4 | "keywords": ["log", "viewer"],
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Márcio Almada",
9 | "email": "marcio3w@gmail.com"
10 | }
11 | ],
12 | "autoload": {
13 | "psr-4": {
14 | "Epilog\\": "src/"
15 | },
16 | "files": [
17 | "commands.php"
18 | ]
19 | },
20 | "bin": [
21 | "epilog"
22 | ],
23 | "require": {
24 | "docopt/docopt": "~1.0",
25 | "minime/annotations": "~2.3",
26 | "igorw/get-in": "~1.0",
27 | "symfony/yaml": "2.5.3",
28 | "regex-guard/regex-guard": "~1.0"
29 | },
30 | "require-dev": {
31 | "mockery/mockery": "dev-master",
32 | "mikey179/vfsStream": "~1.2.0"
33 | },
34 | "minimum-stability": "dev"
35 | }
36 |
--------------------------------------------------------------------------------
/epilog:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | getMessage(), "\n";
11 | return $e->getCode();
12 | }
13 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 | test/
17 |
18 |
19 |
20 |
21 |
22 | src/
23 |
24 |
25 |
26 |
27 |
31 |
35 |
42 |
46 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/DataBag.php:
--------------------------------------------------------------------------------
1 | data = $data;
12 | }
13 |
14 | public function __get($key)
15 | {
16 | return $this->get($key);
17 | }
18 |
19 | public function __set($key, $value)
20 | {
21 | return $this->set($key, $value);
22 | }
23 |
24 | public function get($key, $default = null)
25 | {
26 | return \igorw\get_in($this->data, $this->makePath($key), $default);
27 | }
28 |
29 | public function set($key, $value)
30 | {
31 | return $this->data = \igorw\assoc_in($this->data, $this->makePath($key), $value);
32 | }
33 |
34 | public function all()
35 | {
36 | return $this->data;
37 | }
38 |
39 | public function keys()
40 | {
41 | return array_keys($this->data);
42 | }
43 |
44 | public function values()
45 | {
46 | return array_values($this->data);
47 | }
48 |
49 | protected function makePath($key)
50 | {
51 | return explode('.', str_replace(' ', '.', $key));
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Epilog.php:
--------------------------------------------------------------------------------
1 | 'default',
21 | 2 => 'chaplin',
22 | 3 => 'forest',
23 | 4 => 'scrapbook',
24 | 5 => 'punchcard',
25 | 6 => 'sunset',
26 | 7 => 'sunrise',
27 | 8 => 'traffic',
28 | ];
29 |
30 | /**
31 | * table of callable subcommands
32 | *
33 | * @var array
34 | */
35 | protected $commands = [];
36 |
37 | /**
38 | * Epilog command line args container
39 | *
40 | * @var \Docopt\Response
41 | */
42 | protected $args;
43 |
44 | /**
45 | * Input reader
46 | *
47 | * @var \Epilog\Interfaces\InputReaderInterface
48 | */
49 | protected $stdin;
50 |
51 | /**
52 | * Line printer
53 | *
54 | * @var \Epilog\Interfaces\LinePrinterInterface
55 | */
56 | protected $printer;
57 |
58 | /**
59 | * @var \Epilog\Ticker
60 | */
61 | protected $ticker;
62 |
63 | /**
64 | * Regex validator
65 | *
66 | * @var \RegexGuard\RegexGuard
67 | */
68 | protected $regexGuard;
69 |
70 | public function __construct(Response $args)
71 | {
72 | $this->args = $args;
73 | $args['--sleep-interval'] = (float) $args['--sleep-interval'];
74 | $this->ticker = new Ticker;
75 | $this->printer = $this->loadPrinter();
76 | $this->regexGuard = RegexGuard::getGuard();
77 | $this->commands[''] = function () {};
78 | $this->commands['q'] = $this->commands[false] = [$this, 'quit'];
79 | $this->commands['r'] = [$this, 'loadRandomTheme'];
80 | $this->commands['c'] = [$this, 'clear'];
81 | $this->commands['i'] = [$this, 'invertTheme'];
82 | $this->commands['-'] = [$this, 'clearFilter'];
83 | $this->commands['default'] = function ($command) {
84 | if ($this->regexGuard->isRegexValid($command)) {
85 | $this->args['--filter'] = $command;
86 | } elseif (isset(self::$themes[$command])) {
87 | $this->args['--theme'] = self::$themes[$command];
88 | $this->printer = $this->loadPrinter();
89 | } else {
90 | $this->output(" Invalid option \"{$command}\" given.\n");
91 | }
92 | };
93 | }
94 |
95 | public function run(TailInterface $log, InputReaderInterface $stdin = null)
96 | {
97 | $this->stdin = $stdin ?: new InputReader;
98 | $this->stdin->block(false);
99 | while (true) {
100 | $log->seekLastLineRead();
101 | while (! $log->eof()) {
102 | $line = $log->fgets();
103 | $filter = $this->args['--filter'];
104 | if (! empty($filter))
105 | if(! $this->regexGuard->match($filter, $line)) continue;
106 | $this->output($this->printer->format($line));
107 | }
108 | if($this->args['--no-follow']) $this->quit();
109 | $this->output($this->status($log->getRealPath()));
110 | $this->sleep(); // wait before trigger new iteration
111 | if($this->args['--debug']) $this->printer = $this->loadPrinter();
112 | $this->handleInteraction();
113 | }
114 | }
115 |
116 | public function args()
117 | {
118 | return $this->args;
119 | }
120 |
121 | protected function handleInteraction()
122 | {
123 | if (false !== $this->stdin->readChar()) {
124 | $this->output(
125 | "\n Woot! Epilog here. Please type a theme number,"
126 | . " a valid regexp to filter messages or a valid flag: \n"
127 | . "\n [#] load another theme:\n"
128 | );
129 | foreach (self::$themes as $key => $theme) {
130 | $this->output((! ($key & 1) ? " \t" : "\n ") . "{$key}:$theme");
131 | }
132 | $this->output(
133 | "\n\n [ r ] load random theme from list above."
134 | . "\n [ i ] toggle invert theme."
135 | . "\n [ c ] clear screen."
136 | . "\n [ - ] reset regexp filter."
137 | . "\n [ q ] quit."
138 | . "\n\n [ ⏎ ] "
139 | );
140 |
141 | $command = $input = $this->stdin->block()->readLine();
142 | if(! isset($this->commands[$command])) $command = 'default';
143 | call_user_func_array($this->commands[$command], [$input]);
144 | $this->output($this->printer->unformat());
145 | $this->stdin->block(false);
146 | }
147 | }
148 |
149 | protected function output($string)
150 | {
151 | if (! isset($this->args['--silent'])) echo $string;
152 | }
153 |
154 | /**
155 | * Renders ANSI escaped status line
156 | *
157 | * @param string $message status line message
158 | * @return string ANSI escaped status line
159 | */
160 | protected function status($message)
161 | {
162 | $clear = $this->printer->clearLinesUp(1);
163 | $statusLine = "{$clear}\n[{$this->ticker}] {$message} [ ⏎ ]";
164 | $screenWidth = $this->printer->getScreenWidth() + 5;
165 |
166 | return $this->printer->pad($statusLine, $screenWidth) . $this->printer->unformat();
167 | }
168 |
169 | protected function quit()
170 | {
171 | throw new FlowException('Bye!', 0);
172 | }
173 |
174 | protected function sleep()
175 | {
176 | usleep($this->args['--sleep-interval'] * 1000000);
177 | }
178 |
179 | protected function loadPrinter()
180 | {
181 | $theme = __DIR__ . '/../themes/'. $this->args['--theme'] . '.yml';
182 |
183 | return new MonologLinePrinter(
184 | new MonologLineParser, new Theme($theme), $this->args['--theme-invert']);
185 | }
186 |
187 | protected function loadRandomTheme()
188 | {
189 | $this->args['--theme'] = self::$themes[array_rand(self::$themes)];
190 | $this->printer = $this->loadPrinter();
191 | }
192 |
193 | protected function clear()
194 | {
195 | $this->output($this->printer->clearAll());
196 | }
197 |
198 | protected function invertTheme()
199 | {
200 | $this->args['--theme-invert'] = $this->printer->invert();
201 | }
202 |
203 | protected function clearFilter()
204 | {
205 | $this->args['--filter'] = null;
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/FakeLogTail.php:
--------------------------------------------------------------------------------
1 | format('Y-m-d H:i:s');
48 | $message = self::sentence(rand(5, 10));
49 |
50 | return "[{$date}] log.{$level}: {$message} [] []";
51 | }
52 |
53 | public function eof()
54 | {
55 | return $this->eof = ! $this->eof;
56 | }
57 |
58 | public function getRealPath()
59 | {
60 | return 'nsa://';
61 | }
62 |
63 | /**
64 | * Generate an array of random words
65 | *
66 | * @example array('Lorem', 'ipsum', 'dolor')
67 | * @param integer $nb how many words to return
68 | * @param bool $asText if true the sentences are returned as one string
69 | * @return array|string
70 | */
71 | protected static function words($nb = 3, $asText = false)
72 | {
73 | $words = [];
74 | for ($i=0; $i < $nb; $i++) {
75 | $words []= static::randomElement(self::$wordList);
76 | }
77 |
78 | return $asText ? join(' ', $words) : $words;
79 | }
80 |
81 | /**
82 | * Generate a random sentence
83 | *
84 | * @example 'Lorem ipsum dolor sit amet.'
85 | * @param integer $nbWords around how many words the sentence should contain
86 | * @param boolean $variableNbWords set to false if you want exactly $nbWords returned,
87 | * otherwise $nbWords may vary by +/-40% with a minimum of 1
88 | * @return string
89 | */
90 | protected static function sentence($nbWords = 6, $variableNbWords = true)
91 | {
92 | if ($variableNbWords) {
93 | $nbWords = self::randomizeNbElements($nbWords);
94 | }
95 |
96 | $words = static::words($nbWords);
97 | $words[0] = ucwords($words[0]);
98 |
99 | return join($words, ' ') . '.';
100 | }
101 |
102 | /**
103 | * @param integer $nbElements
104 | */
105 | protected static function randomizeNbElements($nbElements)
106 | {
107 | return (int) ($nbElements * mt_rand(60, 140) / 100) + 1;
108 | }
109 |
110 | /**
111 | * Returns random elements from a provided array
112 | *
113 | * @param array $array Array to take elements from. Defaults to a-f
114 | * @param integer $count Number of elements to take.
115 | *
116 | * @return array New array with $count elements from $array
117 | */
118 | protected static function randomElements(array $array = ['a', 'b', 'c'], $count = 1)
119 | {
120 | $allKeys = array_keys($array);
121 | $numKeys = count($allKeys);
122 | $highKey = $numKeys - 1;
123 | $elements = [];
124 | $numElements = 0;
125 |
126 | while ($numElements < $count) {
127 | $num = mt_rand(0, $highKey);
128 | $elements[] = $array[$allKeys[$num]];
129 | $numElements++;
130 | }
131 |
132 | return $elements;
133 | }
134 |
135 | /**
136 | * Returns a random element from a passed array
137 | *
138 | * @param array $array
139 | * @return mixed
140 | */
141 | protected static function randomElement($array = ['a', 'b', 'c'])
142 | {
143 | $elements = static::randomElements($array, 1);
144 |
145 | return $elements[0];
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/FlowException.php:
--------------------------------------------------------------------------------
1 | resource = fopen($wrapper, 'r');
14 | }
15 |
16 | public function block($block = true)
17 | {
18 | stream_set_blocking($this->resource, (int) $block);
19 |
20 | return $this;
21 | }
22 |
23 | public function readChar()
24 | {
25 | return fgetc($this->resource);
26 | }
27 |
28 | public function readLine()
29 | {
30 | $input = fgets($this->resource);
31 |
32 | return (false !== $input) ? trim(chop($input)) : $input;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Interfaces/InputReaderInterface.php:
--------------------------------------------------------------------------------
1 | getLineCount();
16 | $this->index = ($lineCount > 10) ? ($lineCount - 10) : $lineCount;
17 | }
18 |
19 | public function seekLastLineRead()
20 | {
21 | return parent::seek($this->index);
22 | }
23 |
24 | protected function getLineCount()
25 | {
26 | $this->fastForward();
27 |
28 | return $this->key();
29 | }
30 |
31 | protected function fastForward()
32 | {
33 | while(! $this->eof()) $this->fgets();
34 | }
35 |
36 | public function eof()
37 | {
38 | if($eof = parent::eof())
39 | $this->index = ($this->key() > 1) ? ($this->key() - 1) : $this->key();
40 |
41 | return $eof;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/MonologLineParser.php:
--------------------------------------------------------------------------------
1 | .*)\]\s(?P\w+).(?P\w+):\s(?P.+?)\s(?P\{.*\}|\[.*\])\s(?P\{.*\}|\[.*\])$/';
10 |
11 | /**
12 | * Constructor
13 | * @param string $pattern
14 | */
15 | public function __construct($pattern = null)
16 | {
17 | $this->pattern = ($pattern) ?: $this->pattern;
18 | }
19 |
20 | /**
21 | * {@inheritdoc}
22 | */
23 | public function parse($log)
24 | {
25 | if ( !is_string($log) || strlen($log) === 0) return null;
26 |
27 | preg_match($this->pattern, $log, $data);
28 |
29 | if (! isset($data['date'])) return null;
30 | return [
31 | '{date}' => \DateTime::createFromFormat('Y-m-d H:i:s', $data['date']),
32 | '{logger}' => $data['logger'],
33 | '{level}' => $data['level'],
34 | '{message}' => $data['message'],
35 | '{context}' => json_decode($data['context'], true),
36 | '{extra}' => json_decode($data['extra'], true)
37 | ];
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/MonologLinePrinter.php:
--------------------------------------------------------------------------------
1 | parser = $parser;
18 | $this->invert = $invert;
19 | $this->theme = $theme;
20 | }
21 |
22 | public function invert()
23 | {
24 | return $this->invert = ! $this->invert;
25 | }
26 |
27 | public function format($raw)
28 | {
29 | $raw = trim($raw);
30 | if(empty($raw)) return;
31 | $data = $this->parser->parse($raw);
32 | $line = $raw . "\n";
33 | if ($data) {
34 | $data = new DataBag($data);
35 | $data->{'{date}'} = $data->{'{date}'}->format($this->theme->{'date format'});
36 | $data->{'{extra}'} = json_encode($data->{'{extra}'});
37 | $data->{'{context}'} = json_encode($data->{'{context}'});
38 | $data->{'{date}'} = $this->theme->{'date prepend'} . $data->{'{date}'} . $this->theme->{'date append'};
39 | $data->{'{logger}'} = $this->theme->{'logger prepend'} . $data->{'{logger}'} . $this->theme->{'logger append'};
40 | $data->{'{message}'} = $this->theme->{'message prepend'} . $data->{'{message}'} . $this->theme->{'message append'};
41 | $levelFormat = $this->theme->get('level ' . $data->{'{level}'}, $this->theme->{'level DEFAULT'});
42 | $data->{'{level}'} =
43 | $this->theme->{'level prepend'}
44 | . $levelFormat['prepend']
45 | . $this->pad($data->{'{level}'}, $this->theme->{'level pad'}, constant($this->theme->{'level pad-type'}))
46 | . $levelFormat['append']
47 | . $this->theme->{'level append'};
48 | $line = (($this->invert) ? "\e[7m" : '')
49 | . $this->theme->{'prepend'}
50 | . $this->pad(
51 | str_replace($data->keys(), $data->values(), $this->theme->{'template'}),
52 | $this->getScreenWidth() + $this->theme->{'compensation'})
53 | . $this->theme->{'append'};
54 | }
55 |
56 | return $this->clearLine() . $line;
57 | }
58 |
59 | public function pad($input, $pad_length, $pad_style = STR_PAD_RIGHT, $encoding = 'UTF-8')
60 | {
61 | return str_pad($input, strlen($input) - mb_strlen($input,$encoding) + $pad_length, ' ', $pad_style);
62 | }
63 |
64 | public function clearLinesUp($lines = 1)
65 | {
66 | return "\e[{$lines}A";
67 | }
68 |
69 | public function clearLine()
70 | {
71 | return "\r\e[K";
72 | }
73 |
74 | public function clearAll()
75 | {
76 | return "\e[2J";
77 | }
78 |
79 | public function unformat()
80 | {
81 | return "\e[0m";
82 | }
83 |
84 | public function getScreenWidth()
85 | {
86 | return (int) exec('tput cols');
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Theme.php:
--------------------------------------------------------------------------------
1 | data = call_user_func_array(
27 | 'array_replace_recursive', array_reverse($themes))['theme'];
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Ticker.php:
--------------------------------------------------------------------------------
1 | rewind();
13 | }
14 |
15 | public function __toString()
16 | {
17 | return $this->current() . $this->next();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/DataBagTest.php:
--------------------------------------------------------------------------------
1 | [
16 | 'b' => [
17 | 'c' => 'd'
18 | ]
19 | ]
20 | ];
21 |
22 | public function setUp()
23 | {
24 | $this->bag = new DataBag($this->data);
25 | }
26 |
27 | public function testAll()
28 | {
29 | $this->assertEquals($this->data, $this->bag->all());
30 | }
31 |
32 | public function testSet()
33 | {
34 | $this->bag->set('a.b.c', 'PATCH'); // update nested index
35 | $this->assertArraySubset(['a' => ['b' => ['c' => 'PATCH']]], $this->bag->all());
36 |
37 | $this->bag->set('a.b.c.d', 'PATCH'); // override non-array value
38 | $this->assertArraySubset(['a' => ['b' => ['c' => ['d' => 'PATCH']]]], $this->bag->all());
39 |
40 | $this->bag->{'x y z'} = 'PATCH'; // create nested index
41 | $this->assertArraySubset(['x' => ['y' => ['z' => 'PATCH']]], $this->bag->all());
42 | }
43 |
44 | public function testGet()
45 | {
46 | $this->assertEquals('d', $this->bag->get('a.b.c'));
47 | $this->assertEquals('d', $this->bag->{'a b c'});
48 | $this->assertEquals('default', $this->bag->get('not set', 'default'));
49 | }
50 |
51 | public function testKeys()
52 | {
53 | $this->assertSame(['a'], $this->bag->keys());
54 | }
55 |
56 | public function testValues()
57 | {
58 | $this->bag->set('a', 'b');
59 | $this->assertSame(['b'], $this->bag->values());
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/test/EpilogTest.php:
--------------------------------------------------------------------------------
1 | false,
17 | '--theme' => 'default',
18 | '--theme-invert' => false,
19 | '--filter' => null,
20 | '--debug' => false,
21 | '--sleep-interval' => 0,
22 | '--silent' => true
23 | ];
24 |
25 | public function tearDown()
26 | {
27 | M::close();
28 | }
29 |
30 | /**
31 | * @expectedException Epilog\FlowException
32 | * @expectedExceptionMessage Bye!
33 | */
34 | public function testNoFollowOption()
35 | {
36 | $this->getEpilog(['--no-follow' => true])
37 | ->run(new FakeLogTail);
38 | }
39 |
40 | /**
41 | * @expectedException RegexGuard\RegexException
42 | */
43 | public function testInvalidFilterOption()
44 | {
45 | $this->getEpilog(['--filter' => '/bad-regex'])
46 | ->run(new FakeLogTail);
47 | }
48 |
49 | /**
50 | * @expectedException Epilog\FlowException
51 | * @expectedExceptionMessage Bye!
52 | */
53 | public function testQuitInteraction()
54 | {
55 | $stdin = $this->getStdinMockTemplate()
56 | ->shouldReceive('readLine')->twice()
57 | ->andReturn('', 'q')
58 | ->getMock();
59 | $epilog = $this->getEpilog();
60 | $epilog->run(new FakeLogTail, $stdin);
61 | }
62 |
63 | /**
64 | * @expectedException Epilog\FlowException
65 | */
66 | public function testInvalidInteraction()
67 | {
68 | $this->expectOutputRegex('#Invalid option "\?" given\.#');
69 | $stdin = $this->getStdinMockTemplate()
70 | ->shouldReceive('readLine')->twice()
71 | ->andReturn('?', 'q')
72 | ->getMock();
73 | $this->getEpilog(['--silent' => null])
74 | ->run(new FakeLogTail, $stdin);
75 | }
76 |
77 | /**
78 | * @expectedException Epilog\FlowException
79 | */
80 | public function testClearInteraction()
81 | {
82 | $this->expectOutputRegex('#\\e\[2J#');
83 | $stdin = $this->getStdinMockTemplate()
84 | ->shouldReceive('readLine')->twice()
85 | ->andReturn('c', 'q')
86 | ->getMock();
87 | $this->getEpilog(['--silent' => null])
88 | ->run(new FakeLogTail, $stdin);
89 |
90 | }
91 |
92 | public function testThemeInteraction()
93 | {
94 | $stdin = $this->getStdinMockTemplate()
95 | ->shouldReceive('readLine')->times(4)
96 | ->andReturn('3', 'q', 'r', 'q')
97 | ->getMock();
98 | $epilog = $this->getEpilog();
99 |
100 | $this->assertEquals($epilog::$themes['3'], $this->sandbox($epilog, $stdin)->args()['--theme']);
101 | $this->sandbox($epilog, $stdin);
102 | }
103 |
104 | /**
105 | * @expectedException Epilog\FlowException
106 | */
107 | public function testThemeInvertInteraction()
108 | {
109 | $this->expectOutputRegex('#\\e\[7m#');
110 | $stdin = $this->getStdinMockTemplate()
111 | ->shouldReceive('readLine')->twice()
112 | ->andReturn('i', 'q')
113 | ->getMock();
114 | $this->getEpilog(['--silent' => null])
115 | ->run(new FakeLogTail, $stdin);
116 | }
117 |
118 | public function testDebugInteraction()
119 | {
120 | $stdin = $this->getStdinMockTemplate()
121 | ->shouldReceive('readLine')->times(2)
122 | ->andReturn('d', 'q')
123 | ->getMock();
124 | $epilog = $this->getEpilog(['--debug' => true]);
125 | $this->assertTrue($this->sandbox($epilog, $stdin)->args()['--debug']);
126 | }
127 |
128 | public function testFilterInteraction()
129 | {
130 | $stdin = $this->getStdinMockTemplate()
131 | ->shouldReceive('readLine')->times(4)
132 | ->andReturn('/DEBUG/', 'q', '-', 'q')
133 | ->getMock();
134 | $epilog = $this->getEpilog();
135 | $this->assertEquals('/DEBUG/', $this->sandbox($epilog, $stdin)->args()['--filter']);
136 | $this->assertNull($this->sandbox($epilog, $stdin)->args()['--filter']);
137 | }
138 |
139 | protected function getEpilog(array $options = [])
140 | {
141 | $args = array_replace_recursive($this->defaults, $options);
142 |
143 | return new Epilog(new Response($args));
144 | }
145 |
146 | protected function getStdinMockTemplate()
147 | {
148 | return M::mock('Epilog\Interfaces\InputReaderInterface')
149 | ->shouldReceive('block')
150 | ->andReturn(M::self())
151 | ->shouldReceive('readChar')
152 | ->andReturn('r');
153 | }
154 |
155 | protected function sandbox($epilog, $stdin)
156 | {
157 | try {
158 | $epilog->run(new FakeLogTail, $stdin);
159 | } catch (\Epilog\FlowException $e) {
160 | return $epilog;
161 | }
162 | $this->fail('Epilog console did not quit ptoperly');
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/test/InputReaderTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('BLOCKING', $stdinReader->readLine());
17 | $this->assertEquals('A', $stdinReader->readChar());
18 | $this->assertEquals(PHP_EOL, $stdinReader->readChar());
19 | $stdinReader->block(false);
20 | $this->assertEquals('UNBLOCKING', $stdinReader->readLine());
21 | $this->assertEquals('B', $stdinReader->readChar());
22 | $this->assertEquals(PHP_EOL, $stdinReader->readChar());
23 | $stdinReader->block(true);
24 | $this->assertEquals('BLOCKING', $stdinReader->readLine());
25 | $this->assertEquals('C', $stdinReader->readChar());
26 | $this->assertFalse($stdinReader->readChar());
27 | $this->assertFalse($stdinReader->readLine());
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/test/LogFinderTest.php:
--------------------------------------------------------------------------------
1 | at($dir);
25 | $this->assertEquals('vfs://dir/allow.log', $finder->find(Vsf::url('dir/allow.log'))); // pass
26 |
27 | Vsf::newFile('dir/deny.log')->at($dir)->withContent('*')->chmod(555);
28 | $finder->find(Vsf::url('dir/deny.log')); // throw
29 | }
30 |
31 | public function laravelLogProvider()
32 | {
33 | return [
34 | ['/fixtures/laravel_a/app/storage/logs/log.log', '/fixtures/laravel_a/'],
35 | ['/fixtures/laravel_a/app/storage/logs/log.log', '/fixtures/laravel_a/app/storage/logs/log.log'],
36 | ['/fixtures/laravel_b/app/storage/logs/log.txt', '/fixtures/laravel_b/'],
37 | ['/fixtures/laravel_b/app/storage/logs/log.txt', '/fixtures/laravel_b/app/storage/logs/log.txt'],
38 | ];
39 | }
40 |
41 | /**
42 | * @dataProvider laravelLogProvider
43 | */
44 | public function testLaravelFinder($log, $app)
45 | {
46 | $finder = LogFinderFactory::getLogFinder('laravel');
47 | $this->assertEquals(__DIR__ . $log, $finder->find(__DIR__ . $app));
48 | }
49 |
50 | /**
51 | * @expectedException Epilog\FlowException
52 | * @expectedExceptionMessage Could not read latest log file
53 | */
54 | public function testLaravelFinderFailure()
55 | {
56 | $finder = LogFinderFactory::getLogFinder('laravel');
57 | $finder->find('missing');
58 | }
59 |
60 | /**
61 | * @expectedException Epilog\FlowException
62 | * @expectedExceptionMessage Log finder for Foo is not available
63 | */
64 | public function testLogFinderFactoryFailure()
65 | {
66 | LogFinderFactory::getLogFinder('Foo');
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/test/LogTailTest.php:
--------------------------------------------------------------------------------
1 | fixture = tempnam(sys_get_temp_dir(), 'epilog-');
18 | }
19 |
20 | public function testLogTailWithEmptyFile()
21 | {
22 | $log = new LogTail($this->fixture, 'r');
23 | $this->assertEquals(0, $log->key());
24 | $log->seekLastLineRead();
25 | $this->assertEquals(0, $log->key());
26 | $this->assertEquals('', $log->fgets());
27 | $this->assertEquals(0, $log->key());
28 | $this->assertTrue($log->eof());
29 | }
30 |
31 | public function testLogTailWithShortFile()
32 | {
33 | $this->growLog("line {i}", 3);
34 | $log = new LogTail($this->fixture, 'r');
35 | $log->seekLastLineRead();
36 | $this->assertEquals(3, $log->key());
37 | $this->assertTrue($log->eof());
38 | }
39 |
40 | public function testLogTailWithLongerFile()
41 | {
42 | $this->growLog("line {i}", 11);
43 | $log = new LogTail($this->fixture, 'r');
44 | $log->seekLastLineRead();
45 | $this->assertEquals(1, $log->key());
46 | $log->fgets();
47 | $log->fgets();
48 | $log->fgets();
49 | $log->fgets();
50 | $log->fgets();
51 | $log->fgets();
52 | $log->fgets();
53 | $log->fgets();
54 | $this->assertEquals("line 10\n", $log->fgets());
55 | $this->assertEquals('', $log->fgets());
56 | $this->assertTrue($log->eof());
57 |
58 | $this->growLog('bump!');
59 | $log->seekLastLineRead();
60 | $this->assertFalse($log->eof());
61 | $this->assertEquals("bump!\n", $log->fgets());
62 | $this->assertEquals('', $log->fgets());
63 | $this->assertTrue($log->eof());
64 | }
65 |
66 | public function testLogTailWhenFileIsTruncated()
67 | {
68 | $this->growLog("line {i}", 100);
69 | $log = new LogTail($this->fixture, 'r');
70 | $log->seekLastLineRead();
71 | $this->assertEquals(90, $log->key());
72 | $this->truncateLog();
73 | $log->seekLastLineRead();
74 | $this->assertEquals(1, $log->key());
75 | }
76 |
77 | protected function growLog($text, $times = 1)
78 | {
79 | for ($i=0; $i < $times; $i++)
80 | file_put_contents(
81 | $this->fixture, str_replace('{i}', $i, $text) . "\n", FILE_APPEND);
82 | }
83 |
84 | protected function truncateLog()
85 | {
86 | file_put_contents($this->fixture, "\n");
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/test/MonologLineParserTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($parsed, $parser->parse($entry));
19 | }
20 |
21 | public function logEntryProvider()
22 | {
23 | return [
24 | [
25 | "[2014-09-22 18:01:29] log.EMERGENCY: Ea ratione eaque quae adipisci odio. [] []",
26 | [
27 | '{date}' => \DateTime::createFromFormat('Y-m-d H:i:s', '2014-09-22 18:01:29'),
28 | '{logger}' => 'log',
29 | '{level}' => 'EMERGENCY',
30 | '{message}' => 'Ea ratione eaque quae adipisci odio.',
31 | '{context}' => json_decode('[]', true),
32 | '{extra}' => json_decode('[]', true)
33 | ]
34 | ],
35 | [
36 | "[2014-09-22 18:01:30] log.NOTICE: [\"lala\", \"lele\",{\"lili\": {\"lolo\" : \"lulu\"}}] [] []",
37 | [
38 | '{date}' => \DateTime::createFromFormat('Y-m-d H:i:s', '2014-09-22 18:01:30'),
39 | '{logger}' => 'log',
40 | '{level}' => 'NOTICE',
41 | '{message}' => "[\"lala\", \"lele\",{\"lili\": {\"lolo\" : \"lulu\"}}]",
42 | '{context}' => json_decode('[]', true),
43 | '{extra}' => json_decode('[]', true)
44 | ]
45 | ],
46 | [
47 | '[2014-12-11 20:23:47] log.WARNING: POST http://just/some-url/657B_A-YD {"data":"[object] (stdClass: {\"serialized\":\"object\",\"sometimestamp\":\"2014-12-11T20:23:46.764Z\"})","someobj":{"string":"ABC-DEF"}} []',
48 | [
49 | '{date}' => \DateTime::createFromFormat('Y-m-d H:i:s', '2014-12-11 20:23:47'),
50 | '{logger}' => 'log',
51 | '{level}' => 'WARNING',
52 | '{message}' => 'POST http://just/some-url/657B_A-YD',
53 | '{context}' => json_decode('{"data":"[object] (stdClass: {\"serialized\":\"object\",\"sometimestamp\":\"2014-12-11T20:23:46.764Z\"})","someobj":{"string":"ABC-DEF"}}', true),
54 | '{extra}' => json_decode('[]', true),
55 | ]
56 | ],
57 | [
58 | "invalid log Consequatur vitae molestias ut ipsa praesentium. [] []",
59 | null
60 | ]
61 | ];
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/test/TickerTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('0', $ticker->__toString());
16 | $this->assertEquals('1', $ticker->__toString());
17 | $this->assertEquals('2', $ticker->__toString());
18 | $this->assertEquals('3', $ticker->__toString());
19 | $this->assertEquals('0', $ticker->__toString());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/fixtures/laravel_a/app/storage/logs/log.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marcioAlmada/epilog/e5d8d718c4f929a2a723383261f3687f179d480d/test/fixtures/laravel_a/app/storage/logs/log.log
--------------------------------------------------------------------------------
/test/fixtures/laravel_b/app/storage/logs/log.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marcioAlmada/epilog/e5d8d718c4f929a2a723383261f3687f179d480d/test/fixtures/laravel_b/app/storage/logs/log.txt
--------------------------------------------------------------------------------
/themes/chaplin.yml:
--------------------------------------------------------------------------------
1 | name: Chaplin
2 | author: Márcio Almada
3 | theme:
4 | extends: default
5 |
6 | # the log line template
7 | template: "{level} {date} {message} [{logger}] [{context}] [{extra}]"
8 |
9 | # literal string that will be prepended to entire line
10 | prepend: "\e[0m"
11 |
12 | # literal string that will be appended to entire line
13 | append: "\n"
14 |
15 | compensation: 24
16 |
17 | # level section
18 | level:
19 | DEBUG:
20 | prepend: "\e[048;5;255m\e[038;5;235m "
21 | INFO:
22 | prepend: "\e[048;5;255m\e[038;5;235m "
23 | NOTICE:
24 | prepend: "\e[048;5;255m\e[038;5;235m "
25 | WARNING:
26 | prepend: "\e[048;5;255m\e[038;5;235m "
27 | ERROR:
28 | prepend: "\e[048;5;235m\e[038;5;255m "
29 | CRITICAL:
30 | prepend: "\e[048;5;235m\e[038;5;255m "
31 | ALERT:
32 | prepend: "\e[048;5;235m\e[038;5;255m "
33 | EMERGENCY:
34 | prepend: "\e[048;5;235m\e[038;5;255m "
35 | DEFAULT:
36 | prepend: "\e[048;5;235m\e[038;5;255m "
37 |
38 | # message segent
39 | message:
40 | prepend: '"'
41 | append: '"'
42 |
--------------------------------------------------------------------------------
/themes/default.yml:
--------------------------------------------------------------------------------
1 | name: #theme name
2 | author: #author name
3 | theme:
4 | # the theme to inherit properties
5 | # extends: # default does not inherit
6 |
7 | # the log line template
8 | # you can use {date}, {level}, {logger}, {message}, {context}, {extra} template tags
9 | # !!
10 | template: "{date} {level}.{logger} {message} {context} {extra}"
11 |
12 | # style to be applied at line start (ANSI escaped seq or literal string)
13 | prepend:
14 |
15 | # style to be applied at line ends (ANSI escaped seq or literal string)
16 | append: "\n"
17 |
18 | # padding to compensate ANSI escaped characters
19 | compensation: 0
20 |
21 | # level section
22 | level:
23 | pad: 10
24 | pad-type: STR_PAD_RIGHT
25 | prepend:
26 | append:
27 | DEBUG:
28 | prepend:
29 | append:
30 | INFO:
31 | prepend:
32 | append:
33 | NOTICE:
34 | prepend:
35 | append:
36 | WARNING:
37 | prepend:
38 | append:
39 | ERROR:
40 | prepend:
41 | append:
42 | CRITICAL:
43 | prepend:
44 | append:
45 | ALERT:
46 | prepend:
47 | append:
48 | EMERGENCY:
49 | prepend:
50 | append:
51 | DEFAULT:
52 | prepend:
53 | append:
54 |
55 | # message segent
56 | message:
57 | prepend:
58 | append:
59 |
60 | # date segment
61 | date:
62 | format: "Y-m-d H:i:s"
63 | prepend:
64 | append:
65 |
66 | # logger section
67 | logger:
68 | prepend:
69 | append:
70 |
71 | # context section
72 | context:
73 | prepend:
74 | append:
75 |
76 | # extra section
77 | extra:
78 | prepend:
79 | append:
80 |
--------------------------------------------------------------------------------
/themes/forest.yml:
--------------------------------------------------------------------------------
1 | name: Amazônia
2 | author: Márcio Almada
3 | theme:
4 | extends: default
5 | # the line template
6 | template: "{level} ▴ {date} ▴ {message} [{logger}] [{context}] [{extra}]"
7 |
8 | # padding compensation in case of ANSI escaped characters
9 | compensation: 13
10 |
11 | # level section
12 | level:
13 | DEBUG:
14 | prepend: "\e[038;05;072m ▴ "
15 | INFO:
16 | prepend: "\e[038;05;072m ▴ ▴ "
17 | NOTICE:
18 | prepend: "\e[038;05;072m ▴▴ ▴ "
19 | WARNING:
20 | prepend: "\e[038;05;035m ▴▴ ▴ ▴ "
21 | ERROR:
22 | prepend: "\e[038;05;035m ▴▴▴ ▴▴ "
23 | CRITICAL:
24 | prepend: "\e[038;05;035m ▴▴ ▴▴ ▴▴ "
25 | ALERT:
26 | prepend: "\e[038;05;029m ▴▴▴ ▴▴▴▴ "
27 | EMERGENCY:
28 | prepend: "\e[038;05;029m ▴▴ ▴▴▴▴▴▴ "
29 | append:
30 | DEFAULT:
31 | prepend: "\e[038;05;029m . . .. ."
32 |
--------------------------------------------------------------------------------
/themes/punchcard.yml:
--------------------------------------------------------------------------------
1 | name: Punched Card
2 | author: Márcio Almada
3 | theme:
4 | extends: default
5 |
6 | # the log line template
7 | template: "{level} {date} {message} [{logger}] [{context}] [{extra}]"
8 |
9 | # literal string that will be prepended to entire line
10 | prepend: ""
11 |
12 | # literal string that will be appended to entire line
13 | append: "\e[0m\n"
14 |
15 | # level section
16 | level:
17 | padding: 10
18 | DEBUG:
19 | prepend: " • \e[2m"
20 | INFO:
21 | prepend: " • \e[2m"
22 | NOTICE:
23 | prepend: " • \e[2m"
24 | WARNING:
25 | prepend: " • \e[2m"
26 | ERROR:
27 | prepend: " • \e[1m"
28 | CRITICAL:
29 | prepend: " • \e[1m"
30 | ALERT:
31 | prepend: " • \e[1m"
32 | EMERGENCY:
33 | prepend: " • \e[1m"
34 | DEFAULT:
35 | prepend: " -------- \e[1m"
36 |
--------------------------------------------------------------------------------
/themes/scrapbook.yml:
--------------------------------------------------------------------------------
1 | name: Minial
2 | author: Márcio Almada
3 | theme:
4 | extends: default
5 | # the line template
6 | template: "\n [{date}] [{logger}.{level}]\n Message:{message}\n Context:{context}\n Extra:{extra}\n"
7 | append: "\n=================================================================================\n"
8 | level:
9 | pad: 0
10 | #date section
11 | date:
12 | format: "H:i:s"
13 |
--------------------------------------------------------------------------------
/themes/sunrise.yml:
--------------------------------------------------------------------------------
1 | name: Sunrise
2 | author: Márcio Almada
3 | theme:
4 | extends: sunset
5 | prepend: "\e[038;5;237m"
6 | level:
7 | DEBUG:
8 | prepend: "\e[048;5;189m "
9 | INFO:
10 | prepend: "\e[048;5;153m "
11 | NOTICE:
12 | prepend: "\e[048;5;225m "
13 | WARNING:
14 | prepend: "\e[048;5;224m "
15 | ERROR:
16 | prepend: "\e[048;5;223m "
17 | CRITICAL:
18 | prepend: "\e[048;5;222m "
19 | ALERT:
20 | prepend: "\e[048;5;221m "
21 | EMERGENCY:
22 | prepend: "\e[048;5;220m "
23 | DEFAULT:
24 | prepend: "\e[048;5;184m "
25 |
--------------------------------------------------------------------------------
/themes/sunset.yml:
--------------------------------------------------------------------------------
1 | name: Sunset
2 | author: Márcio Almada
3 | theme:
4 | # the theme to inherit properties
5 | extends: default
6 |
7 | # the line template
8 | template: "{level} {date} {message} [{logger}] [{context}] [{extra}]"
9 |
10 | # style to be applied at line start (ANSI escaped seq or literal string)
11 | prepend: "\e[038;5;15m"
12 |
13 | # padding to compensate ANSI escaped characters
14 | compensation: 12
15 |
16 | # level section
17 | level:
18 | DEBUG:
19 | prepend: "\e[048;5;099m - "
20 | append:
21 | INFO:
22 | prepend: "\e[048;5;135m -- "
23 | append:
24 | NOTICE:
25 | prepend: "\e[048;5;171m --- "
26 | append:
27 | WARNING:
28 | prepend: "\e[048;5;207m ---- "
29 | append:
30 | ERROR:
31 | prepend: "\e[048;5;206m ----- "
32 | append:
33 | CRITICAL:
34 | prepend: "\e[048;5;205m ------ "
35 | append:
36 | ALERT:
37 | prepend: "\e[048;5;204m ------- "
38 | append:
39 | EMERGENCY:
40 | prepend: "\e[048;5;203m -------- "
41 | append:
42 | DEFAULT:
43 | prepend: "\e[048;5;167m -------- "
44 | append:
45 |
--------------------------------------------------------------------------------
/themes/traffic.yml:
--------------------------------------------------------------------------------
1 | name: Traffic
2 | author: Márcio Almada
3 | theme:
4 | extends: default
5 |
6 | # the log line template
7 | template: "{date} {logger} # {level} # {message} [{context}] [{extra}]"
8 |
9 | # level section
10 | level:
11 | padding: 10
12 | pad-type: STR_PAD_BOTH
13 | DEBUG:
14 | prepend: " | "
15 | append: " | "
16 | INFO:
17 | prepend: " | "
18 | append: " | "
19 | NOTICE:
20 | prepend: " | "
21 | append: " | "
22 | WARNING:
23 | prepend: " | "
24 | append: " | "
25 | ERROR:
26 | prepend: " | "
27 | append: " | "
28 | CRITICAL:
29 | prepend: " | "
30 | append: " | "
31 | ALERT:
32 | prepend: " | "
33 | append: " | "
34 | EMERGENCY:
35 | prepend: " | "
36 | append: " | "
37 | DEFAULT:
38 | prepend: " "
39 | append:
40 |
41 | date:
42 | format: H:i:s
43 |
--------------------------------------------------------------------------------