Skip to content

Commit

Permalink
Add LoggingCompositeBuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
elazar committed Feb 21, 2022
1 parent 0f05e9b commit 2ea68fe
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 0 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,24 @@ $logger = new Logger;
ServiceLocator::set(LoggerInterface::class, $logger);
```

[Core buffer implementations](#buffering) do not implement logging. However, as of Flystream 0.4.0, a buffer instance can be wrapped in an instance of the `LoggingCompositeBuffer` class to log calls to its methods. An example of doing this with the default `MemoryBuffer` buffer implementation is shown below.

```php
<?php

use Elazar\Flystream\BufferInterface;
use Elazar\Flystream\LoggingCompositeBuffer;
use Elazar\Flystream\MemoryBuffer;
use Elazar\Flystream\ServiceLocator;
use Monolog\Logger;
use Psr\Log\LoggerInterface;

$logger = new Logger;
// configure $logger here
$buffer = new LoggingCompositeBuffer(new MemoryBuffer, $logger);
ServiceLocator::set(BufferInterface::class, $buffer);
```

## Design

### Service Locator
Expand Down
84 changes: 84 additions & 0 deletions src/LoggingCompositeBuffer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace Elazar\Flystream;

use League\Flysystem\FilesystemOperator;
use Psr\Log\LoggerInterface;

class LoggingCompositeBuffer implements BufferInterface
{
private BufferInterface $buffer;

private LoggerInterface $logger;

public function __construct(
BufferInterface $buffer,
LoggerInterface $logger
) {
$this->buffer = $buffer;
$this->logger = $logger;
}

/**
* {@inheritdoc}
*/
public function write(string $data)
{
$context = $this->getContext([
'data' => $data,
]);

$context['when'] = 'before';
$this->logger->info(__METHOD__, $context);

$return = $context['return'] = $this->buffer->write($data);

$context['when'] = 'after';
$this->logger->info(__METHOD__, $context);

return $return;
}

public function flush(
FilesystemOperator $filesystem,
string $path,
array $context
): void {
$context = $this->getContext([
'filesystem_class' => get_class($filesystem),
'filesystem_id' => spl_object_id($filesystem),
'path' => $path,
'context' => $context,
]);

$context['when'] = 'before';
$this->logger->info(__METHOD__, $context);

$this->buffer->flush($filesystem, $path, $context);

$context['when'] = 'after';
$this->logger->info(__METHOD__, $context);
}

public function close(): void
{
$context = $this->getContext();

$context['when'] = 'before';
$this->logger->info(__METHOD__, $context);

$this->buffer->close();

$context['when'] = 'after';
$this->logger->info(__METHOD__, $context);
}

private function getContext(array $add = []): array
{
$default = [
'buffer_class' => get_class($this->buffer),
'buffer_id' => spl_object_id($this->buffer),
];
return array_merge($default, $add);
}
}
140 changes: 140 additions & 0 deletions tests/LoggingCompositeBufferTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php

use Elazar\Flystream\BufferInterface;
use Elazar\Flystream\LoggingCompositeBuffer;
use League\Flysystem\Filesystem;
use League\Flysystem\FilesystemOperator;
use League\Flysystem\InMemory\InMemoryFilesystemAdapter;
use Monolog\Handler\TestHandler;
use Monolog\Logger;

beforeEach(function () {
$this->logger = new Logger(__FILE__);
$this->logger->pushHandler(new TestHandler());

$this->nestedBuffer = new class implements BufferInterface {
public bool $flushCalled = false;
public bool $closeCalled = false;
public function write(string $data) {
return strlen($data);
}
public function flush(
FilesystemOperator $filesystem,
string $path,
array $context
): void {
$this->flushCalled = true;
}
public function close(): void {
$this->closeCalled = true;
}
};

$this->buffer = new LoggingCompositeBuffer($this->nestedBuffer, $this->logger);
$this->filesystem = new Filesystem(new InMemoryFilesystemAdapter());
});

it('logs writes', function () {
$result = $this->buffer->write('foo');
expect($result)->toBe(3);

$records = $this->logger->popHandler()->getRecords();
expect($records)
->toBeArray()
->toHaveCount(2)
->toHaveKeys([0, 1])
->each
->toBeArray()
->toHaveKeys(['message', 'context', 'level_name']);

expect($records[0]['message'])->toBe('Elazar\Flystream\LoggingCompositeBuffer::write');
expect($records[0]['context'])
->toHaveKeys(['buffer_class', 'buffer_id'])
->toMatchArray([
'data' => 'foo',
'when' => 'before',
]);
expect($records[0]['level_name'])->toBe('INFO');

expect($records[1]['message'])->toBe('Elazar\Flystream\LoggingCompositeBuffer::write');
expect($records[1]['context'])
->toHaveKeys(['buffer_class', 'buffer_id'])
->toMatchArray([
'data' => 'foo',
'when' => 'after',
'return' => 3,
]);
expect($records[1]['level_name'])->toBe('INFO');
});

it('logs flushes', function () {
expect($this->nestedBuffer->flushCalled)->toBeFalse();
$this->buffer->flush(
$this->filesystem,
'/foo',
['bar' => 'baz']
);
expect($this->nestedBuffer->flushCalled)->toBeTrue();

$records = $this->logger->popHandler()->getRecords();
expect($records)
->toBeArray()
->toHaveCount(2)
->toHaveKeys([0, 1])
->each
->toBeArray()
->toHaveKeys(['message', 'context', 'level_name']);

expect($records[0]['message'])->toBe('Elazar\Flystream\LoggingCompositeBuffer::flush');
expect($records[0]['context'])
->toHaveKeys(['buffer_class', 'buffer_id', 'filesystem_id'])
->toMatchArray([
'filesystem_class' => 'League\Flysystem\Filesystem',
'path' => '/foo',
'context' => ['bar' => 'baz'],
'when' => 'before',
]);
expect($records[0]['level_name'])->toBe('INFO');

expect($records[1]['message'])->toBe('Elazar\Flystream\LoggingCompositeBuffer::flush');
expect($records[1]['context'])
->toHaveKeys(['buffer_class', 'buffer_id', 'filesystem_id'])
->toMatchArray([
'filesystem_class' => 'League\Flysystem\Filesystem',
'path' => '/foo',
'context' => ['bar' => 'baz'],
'when' => 'after',
]);
expect($records[1]['level_name'])->toBe('INFO');
});

it('logs closes', function () {
expect($this->nestedBuffer->closeCalled)->toBeFalse();
$this->buffer->close();
expect($this->nestedBuffer->closeCalled)->toBeTrue();

$records = $this->logger->popHandler()->getRecords();
expect($records)
->toBeArray()
->toHaveCount(2)
->toHaveKeys([0, 1])
->each
->toBeArray()
->toHaveKeys(['message', 'context', 'level_name']);

expect($records[0]['message'])->toBe('Elazar\Flystream\LoggingCompositeBuffer::close');
expect($records[0]['context'])
->toHaveKeys(['buffer_class', 'buffer_id'])
->toMatchArray([
'when' => 'before',
]);
expect($records[0]['level_name'])->toBe('INFO');

expect($records[1]['message'])->toBe('Elazar\Flystream\LoggingCompositeBuffer::close');
expect($records[1]['context'])
->toHaveKeys(['buffer_class', 'buffer_id'])
->toMatchArray([
'when' => 'after',
]);
expect($records[1]['level_name'])->toBe('INFO');
});

0 comments on commit 2ea68fe

Please sign in to comment.