Skip to content

Commit deb180c

Browse files
authored
Merge pull request #4 from kodedphp/defer
Remove deferring the messages
2 parents 0541b36 + 64b8676 commit deb180c

23 files changed

+151
-259
lines changed

Log.php

Lines changed: 41 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?php
1+
<?php declare(strict_types=1);
22

33
/*
44
* This file is part of the Koded package.
@@ -25,13 +25,7 @@
2525
*
2626
* CONFIGURATION PARAMETERS (Log class)
2727
*
28-
* - deferred (bool) [optional], default: false
29-
* A flag to set the Log instance how to dump messages.
30-
* Set to TRUE if you want to process all accumulated messages
31-
* at shutdown time. Otherwise, the default behavior is to process
32-
* the message immediately after the LoggerInterface method is called.
33-
*
34-
* - loggers (array)
28+
* - processors (array)
3529
* An array of log processors. Every processor is defined in array with it's own
3630
* configuration parameters, but ALL must have the following:
3731
*
@@ -56,67 +50,41 @@
5650
*
5751
*
5852
* CONFIGURATION PARAMETERS (Processor class)
59-
* Every processor may have it's own specific parameters.
53+
* Every processor may have its own specific parameters.
6054
*
6155
*/
6256
class Log implements Logger
6357
{
6458
use LoggerTrait;
6559

66-
/**
67-
* @var bool Flag to control the messages processing
68-
*/
69-
private bool $deferred = false;
60+
private const DATE_FORMAT = 'd/m/Y H:i:s.u';
7061

71-
/**
72-
* @var string The date format for the message.
73-
*/
74-
private string $dateFormat;
62+
private DateTimeZone $timezone;
7563

7664
/**
77-
* @var DateTimeZone Valid timezone for the message.
78-
*/
79-
private DateTimeZone|bool $timezone;
80-
81-
/**
82-
* @var Processor[] Hash with all registered log processors.
65+
* @var array<int, Processor> Hash with all registered log processors.
8366
*/
8467
private array $processors = [];
8568

86-
/**
87-
* @var array List with all accumulated messages.
88-
*/
89-
private array $messages = [];
90-
9169
/**
9270
* Creates all requested log processors.
9371
*
94-
* @param array $settings
72+
* @param array $processors a list of log processors
73+
* @param string $dateformat The date format for the messages
74+
* @param string $timezone The timezone for the messages
9575
*/
96-
public function __construct(array $settings)
76+
public function __construct(
77+
array $processors,
78+
private string $dateformat = self::DATE_FORMAT,
79+
string $timezone = 'UTC')
9780
{
98-
$this->deferred = (bool)($settings['deferred'] ?? false);
99-
$this->dateFormat = (string)($settings['dateformat'] ?? 'd/m/Y H:i:s.u');
100-
if (false === $this->timezone = @\timezone_open((string)($settings['timezone'] ?? 'UTC'))) {
101-
$this->timezone = \timezone_open('UTC');
102-
}
103-
foreach ((array)($settings['loggers'] ?? []) as $processor) {
81+
$this->timezone = @\timezone_open($timezone) ?: \timezone_open('UTC');
82+
foreach ($processors as $processor) {
10483
$this->attach(new $processor['class']($processor));
10584
}
106-
if ($this->deferred) {
107-
\register_shutdown_function([$this, 'process']);
108-
}
109-
}
110-
111-
public function attach(Processor $processor): Logger
112-
{
113-
if (0 !== $processor->levels()) {
114-
$this->processors[\spl_object_hash($processor)] = $processor;
115-
}
116-
return $this;
11785
}
11886

119-
public function log($level, $message, array $context = [])
87+
public function log($level, $message, array $context = []): void
12088
{
12189
try {
12290
$levelName = \strtoupper($level);
@@ -125,39 +93,37 @@ public function log($level, $message, array $context = [])
12593
$levelName = 'LOG';
12694
$level = -1;
12795
}
128-
$this->messages[] = [
129-
'level' => $level,
130-
'levelname' => $levelName,
131-
'message' => $this->formatMessage($message, $context),
132-
'timestamp' => \date_create_immutable('now', $this->timezone ?: null)->format($this->dateFormat),
133-
];
134-
$this->deferred || $this->process();
135-
}
136-
137-
public function process(): void
138-
{
13996
foreach ($this->processors as $processor) {
140-
$processor->update($this->messages);
97+
$processor->update([
98+
'level' => $level,
99+
'levelname' => $levelName,
100+
'message' => $this->formatMessage($message, $context),
101+
'timestamp' => $this->now(),
102+
]);
141103
}
142-
$this->messages = [];
143104
}
144105

145-
public function exception(Throwable $e, Processor $processor = null): void
106+
public function attach(Processor $processor): Logger
146107
{
147-
$logger = $processor ?? new Cli([]);
148-
$message = $e->getMessage() . PHP_EOL . ' -- [Trace]: ' . $e->getTraceAsString();
149-
150-
$this->attach($logger)->critical($message);
151-
$this->process();
152-
$this->detach($logger);
108+
if (0 !== $processor->levels()) {
109+
$this->processors[\spl_object_id($processor)] = $processor;
110+
}
111+
return $this;
153112
}
154113

155114
public function detach(Processor $processor): Logger
156115
{
157-
unset($this->processors[\spl_object_hash($processor)]);
116+
unset($this->processors[\spl_object_id($processor)]);
158117
return $this;
159118
}
160119

120+
public function exception(Throwable $e, Processor $processor = null): void
121+
{
122+
$this->attach($processor ??= new Cli([]));
123+
$this->critical($e->getMessage() . PHP_EOL . ' -- [Trace]: ' . $e->getTraceAsString());
124+
$this->detach($processor);
125+
}
126+
161127
/**
162128
* Parses the message as in the interface specification.
163129
*
@@ -174,4 +140,10 @@ private function formatMessage(object|string $message, array $params = []): stri
174140
}
175141
return \strtr((string)$message, $replacements);
176142
}
143+
144+
private function now(): string
145+
{
146+
return \date_create_immutable('now', $this->timezone)
147+
->format($this->dateformat);
148+
}
177149
}

Logger.php

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,9 @@ public function detach(Processor $processor): Logger;
5757

5858
/**
5959
* @param Throwable $e
60-
* @param Processor $processor [optional]
60+
* @param Processor|null $processor [optional]
6161
*
6262
* @return void
6363
*/
6464
public function exception(Throwable $e, Processor $processor = null): void;
65-
66-
/**
67-
* Run all log processors to save the accumulated messages
68-
* and clean the message stack (after the method is called,
69-
* the message stack is emptied).
70-
*
71-
* It may be called manually as well to instantly dump all messages
72-
* (i.e. for Exception handling).
73-
*
74-
* @return void
75-
*/
76-
public function process(): void;
7765
}

Processors/Cli.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818
*/
1919
class Cli extends Processor
2020
{
21-
protected string $format = '> [timestamp][levelname] - message';
21+
protected string $format = '> [timestamp][levelname] message';
2222

2323
/** @var resource */
2424
private $handle;
2525

2626
public function __construct(array $settings)
2727
{
2828
parent::__construct($settings);
29-
$this->handle = \defined('STDERR') ? STDERR : \fopen('php://stderr', 'w');
29+
$this->handle = \defined('STDOUT') ? STDOUT : \fopen('php://stdout', 'w');
3030
}
3131

3232
protected function process(array $message): void

Processors/Processor.php

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ abstract class Processor
2222
* @var int Packed integer for all log levels. If not specified, all levels
2323
* are included (by default)
2424
*/
25-
protected $levels = -1;
25+
protected int $levels = -1;
2626

2727
/**
2828
* @var string The log message format.
2929
*/
30-
protected string $format = 'timestamp [levelname]: message';
30+
protected string $format = 'timestamp [levelname] message';
3131

3232
/**
3333
* @var string Keeps all formatted log messages in this property.
3434
*/
35-
protected $formatted = '';
35+
protected string $formatted = '';
3636

3737
/**
3838
* @param array $settings
@@ -45,24 +45,22 @@ public function __construct(array $settings)
4545

4646
/**
4747
* Receive update from the Log instance.
48-
* This is where the messages are processed and saved.
48+
* This is where the messages are filtered and processed.
4949
*
50-
* @param array $messages List of all messages to be processed
50+
* @param array $message The message to be processed
5151
*
5252
* @return void
5353
*/
54-
public function update(array $messages): void
54+
public function update(array $message): void
5555
{
56-
foreach ($messages as $message) {
57-
if ($message['level'] & $this->levels) {
58-
$this->process($message);
59-
}
56+
if (($message['level'] ?? -1) & $this->levels) {
57+
$this->process($message);
6058
}
6159
}
6260

6361
/**
6462
* The concrete implementation of the log processor where
65-
* the message is parsed and delivered.
63+
* the message is filtered and delivered.
6664
*
6765
* @param array $message
6866
*
@@ -86,11 +84,11 @@ public function levels(): int
8684
}
8785

8886
/**
89-
* Returns all messages in some formatted way.
87+
* Returns all messages as formatted string.
9088
*
91-
* @return mixed
89+
* @return string
9290
*/
93-
public function formatted()
91+
public function formatted(): string
9492
{
9593
return $this->formatted;
9694
}

README.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,13 @@ Usage
3434
<?php
3535

3636
$settings = [
37-
'deferred' => false,
38-
'loggers' => [
37+
[
3938
['class' => Cli::class, 'levels' => Log::ERROR],
4039
['class' => File::class, 'levels' => Log::INFO]
4140
]
4241
];
4342

44-
$log = new Log($settings);
43+
$log = new Log(...$settings);
4544

4645
// This message is processed by Cli and File
4746
$log->alert('The message with {variable}', ['variable' => 'useful info']);
@@ -55,12 +54,11 @@ $log->warning("You don't see anything");
5554
Configuration
5655
-------------
5756

58-
| Param | Type | Required | Default | Description |
59-
|-----------:|:------:|:--------:|:----------------|:------------|
60-
| loggers | array | yes | (empty) | An array of log processors. Every processor is defined in array with it's own configuration parameters. See [processor directives](processor-default-directives) |
61-
| dateformat | string | no | "d/m/Y H:i:s.u" | The date format for the log message. Microseconds are prepended by default |
62-
| timezone | string | no | "UTC" | The desired timezone for the log message |
63-
| deferred | bool | no | false | A flag to set the Log instance how to dump messages. Set to TRUE if you want to process all accumulated messages at shutdown time. Otherwise, the default behavior is to process the message immediately after the LoggerInterface method is called |
57+
| Param | Type | Required | Default | Description |
58+
|-------------:|:------:|:--------:|:----------------|:------------|
59+
| (processors) | array | yes | (empty) | An array of log processors. Every processor is defined in array with it's own configuration parameters. See [processor directives](processor-default-directives) |
60+
| dateformat | string | no | "d/m/Y H:i:s.u" | The date format for the log message. Microseconds are prepended by default |
61+
| timezone | string | no | "UTC" | The desired timezone for the log message |
6462

6563

6664
### Processor default directives
@@ -96,13 +94,13 @@ Tips:
9694
Processors
9795
----------
9896

99-
| Class name | Description |
100-
|------------------:|:-------------------------------------------------------------------------------------|
101-
| ErrorLog | uses the [error_log()][error-log] function to send the message to PHP's logger |
102-
| Cli | write the messages in the console (with STDERR) |
103-
| Memory | will store all messages in an array. Useful for unit tests if the logger is involved |
104-
| SysLog **(slow)** | will open the system logger and send messages using the [syslog()][syslog] function |
105-
| File **(slow)** | saves the messages on a disk |
97+
| Class name | Description |
98+
|------------:|:-------------------------------------------------------------------------------------|
99+
| ErrorLog | uses the [error_log()][error-log] function to send the message to PHP's logger |
100+
| Cli | write the messages in the console (with STDERR) |
101+
| Memory | will store all messages in an array. Useful for unit tests if the logger is involved |
102+
| SysLog | will open the system logger and send messages using the [syslog()][syslog] function |
103+
| File | saves the messages on a disk |
106104

107105

108106
Benchmarks and tests

Tests/LogConstructorTest.php

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
<?php
1+
<?php declare(strict_types=1);
22

33
namespace Tests\Koded\Logging;
44

55
use DateTimeZone;
66
use Koded\Logging\Log;
7+
use Koded\Logging\Processors\Cli;
78
use Koded\Logging\Processors\Memory;
89
use PHPUnit\Framework\TestCase;
910

@@ -13,19 +14,21 @@ class LogConstructorTest extends TestCase
1314

1415
public function test_construction_with_config_array()
1516
{
16-
$log = new Log([
17-
'timezone' => 'Europe/Berlin',
18-
'dateformat' => 'Y-m-d',
19-
'loggers' => [
17+
$config = [
18+
[
2019
[
21-
'class' => Memory::class,
20+
'class' => Memory::class,
2221
'levels' => Log::ALERT | Log::ERROR,
2322
'format' => '[levelname] message'
2423
]
25-
]
26-
]);
24+
],
25+
'dateformat' => 'Y-m-d',
26+
'timezone' => 'Europe/Berlin'
27+
];
28+
29+
$log = new Log(...$config);
2730

28-
$this->assertSame('Y-m-d', $this->property($log, 'dateFormat'));
31+
$this->assertSame('Y-m-d', $this->property($log, 'dateformat'));
2932
$this->assertInstanceOf(DateTimeZone::class, $this->property($log, 'timezone'));
3033
$this->assertSame('Europe/Berlin', $this->property($log, 'timezone')->getName());
3134
}
@@ -34,14 +37,13 @@ public function test_construction_without_config()
3437
{
3538
$log = new Log([]);
3639

37-
$this->assertSame(false, $this->property($log, 'deferred'));
38-
$this->assertSame('d/m/Y H:i:s.u', $this->property($log, 'dateFormat'));
40+
$this->assertSame('d/m/Y H:i:s.u', $this->property($log, 'dateformat'));
3941
$this->assertSame('UTC', $this->property($log, 'timezone')->getName());
4042
}
4143

4244
public function test_invalid_timezone_setting()
4345
{
44-
$log = new Log(['timezone' => 'invalid/zone']);
46+
$log = new Log([['class' => Cli::class]], timezone: 'invalid/zone');
4547
$this->assertSame('UTC', $this->property($log, 'timezone')->getName());
4648
}
4749
}

0 commit comments

Comments
 (0)