Skip to content

Instantly share code, notes, and snippets.

@rcubitto
Last active January 9, 2019 18:52
Show Gist options
  • Save rcubitto/10e2c772922b448f1e174c0e3d430000 to your computer and use it in GitHub Desktop.
Save rcubitto/10e2c772922b448f1e174c0e3d430000 to your computer and use it in GitHub Desktop.
Class for "logging" data into a buffer in memory and then save all the logs into a file.
<?php
namespace App\Traits;
use App\Facades\FileLogger;
use Carbon\Carbon;
/**
* Makes easier the action of beginning/ending a file log.
*
* Class FileLoggable
* @package App\Traits
*/
trait FileLoggable
{
/** @var Carbon */
protected $start;
/** @var Carbon */
protected $end;
/**
* Clear existent entries and set the beginning time.
*/
public function beginFileLog()
{
$this->clearFileLog();
$this->start = Carbon::now();
FileLogger::logCentered($this->start->toFormattedDateString().' @ '.$this->start->format('h:i A'), '-');
}
/**
* Log elapsed time and store the buffer into the storage path defined.
*
* @param string $storagePath
* @param string $filename
* @return string
*/
public function endFileLog($storagePath, $filename)
{
$end = Carbon::now();
$timeElapsed = $this->start->diff($end);
$endLine = $end->toFormattedDateString() . ' @ ' . $end->format('h:i A');
$endLine .= " | Time elapsed: $timeElapsed->h hours, $timeElapsed->i minutes, $timeElapsed->s seconds.";
FileLogger::logCentered($endLine, '-');
return FileLogger::storeAs($storagePath, $filename);
}
////////////////////////////////////////////////////////////
private function clearFileLog()
{
FileLogger::clear();
}
}
<?php
namespace App\Libraries;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
class FileLogger
{
protected static $columns = 80;
/**
* A collection containing an element per line.
*
* @var \Illuminate\Support\Collection
*/
protected $buffer;
/**
* Indicate if we are faking the usage of the logger.
*
* @var bool
*/
protected $fake = false;
public function __construct()
{
$this->buffer = collect();
}
/**
* Mark the instance as a fake.
*
* @return FileLogger
*/
public function fake()
{
$this->fake = true;
return $this;
}
/**
* Store the buffer's content into a file.
*
* @param $path
* @return string
*/
public function store($path)
{
$filename = $this->generateFilename();
return $this->storeAs($path, $filename);
}
/**
* Store the buffer's content into a file setting the file's name.
*
* @param $path
* @param $filename
* @return string
*/
public function storeAs($path, $filename)
{
$filepath = $this->sanitizePath($path) . $filename;
return $this->write($filepath);
}
/**
* Store a line into the logger's buffer.
*
* @param $col
* @param null $content
* @return $this
*/
public function log($col, $content = null)
{
if (!$content) {
$content = $col;
$col = 0;
}
$spaces = str_repeat(' ', $col);
$this->buffer->push($spaces . $content);
return $this;
}
/**
* Store an empty line into the logger's buffer.
*
* @param int $amount
* @return FileLogger
*/
public function logBreak($amount = 1)
{
for ($i = 1; $i <= $amount; $i++) {
$this->log('');
}
return $this;
}
/**
* Store a horizontal line that works as a separator.
*
* @param string $separator
* @return FileLogger
*/
public function logSeparator($separator = '-')
{
return $this->log(str_repeat($separator, self::$columns));
}
/**
* Store a centered text, with the possibility to set the wings on each side.
*
* @param $text
* @param string $leftSeparator
* @param null $rightSeparator
* @return FileLogger
*/
public function logCentered($text, $leftSeparator = ' ', $rightSeparator = null)
{
$rightSeparator = $rightSeparator ?: $leftSeparator;
$amountOfCharsEachSide = (self::$columns - strlen($text) - 2) / 2;
$amountOnTheLeft = floor($amountOfCharsEachSide); // 5.5 => 5.0
$amountOnTheRight = round($amountOfCharsEachSide); // 5.5 => 6.0 we add the extra char on the right side.
$left = str_repeat($leftSeparator, $amountOnTheLeft);
$right = str_repeat($rightSeparator, $amountOnTheRight);
return $this->log("$left $text $right");
}
/**
* @return \Illuminate\Support\Collection
*/
public function buffer()
{
return $this->buffer;
}
/**
* Clear all the logged entries from the buffer.
*
* @return $this
*/
public function clear()
{
$this->buffer = collect();
return $this;
}
// Private Methods ////////////////////////////////////////////////////////////
/**
* Adds a trailing slash to the end of the path, if missing.
*
* @param $path
* @return string
*/
private function sanitizePath($path)
{
return ends_with($path, '/') ? $path : ($path . '/');
}
/**
* Write the content of the logger to the corresponding filepath.
*
* @param $filepath
* @return string
*/
private function write($filepath)
{
// If we have the fake flag on, that means we want to fake the usage of the logger.
// That's why we prevent storing real files and clearing the buffer.
// The idea is to make assertions directly to the buffer.
if ($this->fake) {
return $filepath;
}
Storage::put($filepath, $this->buffer()->implode(PHP_EOL));
$this->clear();
return $filepath;
}
/**
* Generate a random file name using current timestamp.
*
* @return string
*/
private function generateFilename()
{
return Carbon::now()->format('YmdHis') . '.txt';
}
}
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class FileLogger extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor() { return 'filelogger'; }
}
<?php
namespace Tests\Feature;
use App\Facades\FileLogger;
use Illuminate\Support\Facades\Storage;
class FileLoggerTest extends TestCase
{
/**
* Storage relative path where we store invoice files -> storage/app/invoices
*
* @var string
*/
protected $storagePath = 'invoices';
/**
* Remove storage path with all its files.
*/
public function tearDown()
{
\Storage::deleteDirectory($this->storagePath);
parent::tearDown();
}
// Tests ///////////////////////////////////////////////////////////////
/** @test */
public function set_the_storage_path_when_storing_a_file()
{
$filepath = FileLogger::store($this->storagePath);
$this->assertFileInStorage($filepath);
}
/** @test */
public function set_the_name_of_the_file_to_store()
{
$filepath = FileLogger::storeAs($this->storagePath, 'foo.txt');
$this->assertFileInStorage($filepath);
$this->assertTrue(ends_with($filepath, 'foo.txt'));
}
/** @test */
public function set_a_line_into_the_buffer_setting_the_colum_space()
{
FileLogger::log(1, 'Hello World'); // ' Hello World' (4 spaces = 1 unit)
$buffer = FileLogger::buffer();
$this->assertTrue($buffer instanceof \Illuminate\Support\Collection);
$this->assertEquals(1, $buffer->count());
$this->assertEquals(' Hello World', $buffer->first());
}
/** @test */
public function set_various_lines_into_the_buffer()
{
FileLogger::log(1, 'Hello World');
FileLogger::log(2, 'Foo');
FileLogger::log('First line without any indent');
$buffer = FileLogger::buffer();
$this->assertEquals(3, $buffer->count());
$this->assertEquals(' Hello World', $buffer->first());
$this->assertEquals(' Foo', $buffer->get(1));
$this->assertEquals('First line without any indent', $buffer->get(2));
}
/** @test */
public function log_a_couple_of_lines_and_store_them_in_a_file()
{
FileLogger::log(1, 'Hello World');
FileLogger::log(2, 'Foo');
FileLogger::log('First line without any indent');
$filename = FileLogger::store($this->storagePath);
$content = Storage::get($filename);
$this->assertEquals(" Hello World\n Foo\nFirst line without any indent", $content);
}
/** @test */
public function log_a_couple_of_lines_and_choose_the_name_of_the_file()
{
FileLogger::log(1, 'Hello World');
FileLogger::log(2, 'Foo');
FileLogger::log('First line without any indent');
FileLogger::storeAs($this->storagePath, 'test.txt');
$content = Storage::get('invoices/test.txt');
$this->assertEquals(" Hello World\n Foo\nFirst line without any indent", $content);
}
/** @test */
public function log_empty_lines()
{
FileLogger::log('Foo');
FileLogger::logBreak();
FileLogger::log(1, 'Bar');
FileLogger::logBreak();
FileLogger::log('Baz');
$buffer = FileLogger::buffer();
$this->assertEquals('Foo', $buffer->first());
$this->assertEquals('', $buffer->get(1));
$this->assertEquals(' Bar', $buffer->get(2));
$this->assertEquals('', $buffer->get(3));
$this->assertEquals('Baz', $buffer->get(4));
}
/** @test */
public function log_a_centered_text()
{
FileLogger::logCentered('SomeRandomString');
$buffer = FileLogger::buffer();
$this->assertEquals(' SomeRandomString ', $buffer->first());
}
/** @test */
public function log_a_centered_text_and_set_the_wings()
{
FileLogger::logCentered('FooBarBaz', '-');
$buffer = FileLogger::buffer();
$this->assertEquals('---------------------------------- FooBarBaz -----------------------------------', $buffer->first());
}
/** @test */
public function log_a_centered_text_and_set_different_wings_on_each_side()
{
FileLogger::logCentered('FooBarBaz', '>', '<');
$buffer = FileLogger::buffer();
$this->assertEquals('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> FooBarBaz <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<', $buffer->first());
}
/** @test */
public function clear_the_buffer_after_storing_to_a_file()
{
FileLogger::log('Foo');
FileLogger::store($this->storagePath);
$this->assertTrue(FileLogger::buffer()->isEmpty());
}
// Private Methods ////////////////////////////////////////////////
/**
* @param $filepath
*/
public function assertFileInStorage($filepath)
{
$this->assertTrue(
file_exists(storage_path('app/' . $filepath)),
"Failed asserting that the file {$filepath} exists."
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment