Merge remote-tracking branch 'MyIgel/logentry-model'

This commit is contained in:
Igor Scheller 2018-09-16 12:06:18 +02:00
commit 0734807eef
13 changed files with 257 additions and 115 deletions

View File

@ -0,0 +1,47 @@
<?php
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateLogEntriesTable extends Migration
{
/**
* Run the migration
*/
public function up()
{
$this->schema->create('log_entries', function (Blueprint $table) {
$table->increments('id');
$table->string('level', 20);
$table->text('message');
$table->timestamp('created_at')->nullable();
});
$this->schema->getConnection()->unprepared('
INSERT INTO log_entries (`id`, `level`, `message`, `created_at`)
SELECT `id`, `level`, `message`, FROM_UNIXTIME(`timestamp`) FROM LogEntries
');
$this->schema->dropIfExists('LogEntries');
}
/**
* Reverse the migration
*/
public function down()
{
$this->schema->create('LogEntries', function (Blueprint $table) {
$table->increments('id');
$table->string('level', 20);
$table->text('message');
$table->integer('timestamp');
});
$this->schema->getConnection()->unprepared('
INSERT INTO LogEntries (`id`, `level`, `message`, `timestamp`)
SELECT `id`, `level`, `message`, UNIX_TIMESTAMP(`created_at`) FROM log_entries
');
$this->schema->dropIfExists('log_entries');
}
}

View File

@ -13,7 +13,6 @@ $includeFiles = [
__DIR__ . '/../includes/model/AngelType_model.php', __DIR__ . '/../includes/model/AngelType_model.php',
__DIR__ . '/../includes/model/EventConfig_model.php', __DIR__ . '/../includes/model/EventConfig_model.php',
__DIR__ . '/../includes/model/LogEntries_model.php',
__DIR__ . '/../includes/model/Message_model.php', __DIR__ . '/../includes/model/Message_model.php',
__DIR__ . '/../includes/model/NeededAngelTypes_model.php', __DIR__ . '/../includes/model/NeededAngelTypes_model.php',
__DIR__ . '/../includes/model/Room_model.php', __DIR__ . '/../includes/model/Room_model.php',

View File

@ -1,62 +0,0 @@
<?php
use Engelsystem\Database\DB;
/**
* Creates a log entry.
*
* @param string $logLevel Log level
* @param string $message Log Message
* @return bool
*/
function LogEntry_create($logLevel, $message)
{
return DB::insert('
INSERT INTO `LogEntries` (`timestamp`, `level`, `message`)
VALUES(?, ?, ?)
', [time(), $logLevel, $message]);
}
/**
* Returns log entries with maximum count of 10000.
*
* @return array
*/
function LogEntries()
{
return DB::select('SELECT * FROM `LogEntries` ORDER BY `timestamp` DESC LIMIT 10000');
}
/**
* Returns log entries filtered by a keyword
*
* @param string $keyword
* @return array
*/
function LogEntries_filter($keyword)
{
if ($keyword == '') {
return LogEntries();
}
$keyword = '%' . $keyword . '%';
return DB::select('
SELECT *
FROM `LogEntries`
WHERE `level` LIKE ?
OR `message` LIKE ?
ORDER BY `timestamp` DESC
',
[$keyword, $keyword]
);
}
/**
* Delete all log entries.
*
* @return bool
*/
function LogEntries_clear_all()
{
return DB::connection()->statement('TRUNCATE `LogEntries`');
}

View File

@ -1,5 +1,7 @@
<?php <?php
use Engelsystem\Models\LogEntry;
/** /**
* @return string * @return string
*/ */
@ -17,10 +19,14 @@ function admin_log()
if (request()->has('keyword')) { if (request()->has('keyword')) {
$filter = strip_request_item('keyword'); $filter = strip_request_item('keyword');
} }
$log_entries = LogEntries_filter($filter);
foreach ($log_entries as &$log_entry) { $log_entries = LogEntry::filter($filter);
$log_entry['date'] = date('d.m.Y H:i', $log_entry['timestamp']);
$entries = [];
foreach ($log_entries as $entry) {
$data = $entry->toArray();
$data['created_at'] = date_format($entry->created_at, 'd.m.Y H:i');
$entries[] = $data;
} }
return page_with_title(admin_log_title(), [ return page_with_title(admin_log_title(), [
@ -30,9 +36,9 @@ function admin_log()
form_submit(__('Search'), 'Go') form_submit(__('Search'), 'Go')
]), ]),
table([ table([
'date' => 'Time', 'created_at' => 'Time',
'level' => 'Type', 'level' => 'Type',
'message' => 'Log Entry' 'message' => 'Log Entry'
], $log_entries) ], $entries)
]); ]);
} }

View File

@ -2,6 +2,7 @@
namespace Engelsystem\Logger; namespace Engelsystem\Logger;
use Engelsystem\Models\LogEntry;
use Psr\Log\AbstractLogger; use Psr\Log\AbstractLogger;
use Psr\Log\InvalidArgumentException; use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
@ -19,6 +20,14 @@ class EngelsystemLogger extends AbstractLogger
LogLevel::WARNING, LogLevel::WARNING,
]; ];
/** @var LogEntry */
protected $log;
public function __construct(LogEntry $log)
{
$this->log = $log;
}
/** /**
* Logs with an arbitrary level. * Logs with an arbitrary level.
* *
@ -33,12 +42,12 @@ class EngelsystemLogger extends AbstractLogger
public function log($level, $message, array $context = []) public function log($level, $message, array $context = [])
{ {
if (!$this->checkLevel($level)) { if (!$this->checkLevel($level)) {
throw new InvalidArgumentException(); throw new InvalidArgumentException('Unknown log level: ' . $level);
} }
$message = $this->interpolate($message, $context); $message = $this->interpolate($message, $context);
LogEntry_create($level, $message); $this->log->create(['level' => $level, 'message' => $message]);
} }
/** /**

23
src/Models/BaseModel.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace Engelsystem\Models;
use Illuminate\Database\Eloquent\Model;
abstract class BaseModel extends Model
{
/** @var bool Disable timestamps by default because of "Datensparsamkeit" */
public $timestamps = false;
/**
* @param array $attributes
* @return BaseModel
*/
public function create(array $attributes = [])
{
$instance = new static($attributes);
$instance->save();
return $instance;
}
}

46
src/Models/LogEntry.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace Engelsystem\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
class LogEntry extends BaseModel
{
/** @var bool enable timestamps for created_at */
public $timestamps = true;
/** @var null Disable updated_at */
const UPDATED_AT = null;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'level',
'message',
];
/**
* @param $keyword
* @return Builder[]|Collection|LogEntry[]
*/
public static function filter($keyword = null)
{
$query = LogEntry::query()
->select()
->orderByDesc('created_at')
->orderByDesc('id')
->limit(10000);
if (!empty($keyword)) {
$query
->where('level', '=', $keyword)
->orWhere('message', 'LIKE', '%' . $keyword . '%');
}
return $query->get();
}
}

View File

@ -3,6 +3,7 @@
namespace Engelsystem\Test\Feature\Logger; namespace Engelsystem\Test\Feature\Logger;
use Engelsystem\Logger\EngelsystemLogger; use Engelsystem\Logger\EngelsystemLogger;
use Engelsystem\Models\LogEntry;
use Engelsystem\Test\Feature\ApplicationFeatureTest; use Engelsystem\Test\Feature\ApplicationFeatureTest;
use Psr\Log\InvalidArgumentException; use Psr\Log\InvalidArgumentException;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -15,12 +16,13 @@ class EngelsystemLoggerTest extends ApplicationFeatureTest
*/ */
public function getLogger() public function getLogger()
{ {
return new EngelsystemLogger(); $logEntry = new LogEntry();
return new EngelsystemLogger($logEntry);
} }
public function testImplements() public function testImplements()
{ {
$this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); $this->assertInstanceOf(LoggerInterface::class, $this->getLogger());
} }
/** /**
@ -46,21 +48,20 @@ class EngelsystemLoggerTest extends ApplicationFeatureTest
*/ */
public function testAllLevels($level) public function testAllLevels($level)
{ {
LogEntry::query()->truncate();
$logger = $this->getLogger(); $logger = $this->getLogger();
LogEntries_clear_all();
$logger->log($level, 'First log message'); $logger->log($level, 'First log message');
$logger->{$level}('Second log message'); $logger->{$level}('Second log message');
$entries = LogEntries(); $entries = LogEntry::all();
$this->assertCount(2, $entries); $this->assertCount(2, $entries);
} }
public function testContextReplacement() public function testContextReplacement()
{ {
LogEntry::query()->truncate();
$logger = $this->getLogger(); $logger = $this->getLogger();
LogEntries_clear_all();
$logger->log(LogLevel::INFO, 'My username is {username}', ['username' => 'Foo']); $logger->log(LogLevel::INFO, 'My username is {username}', ['username' => 'Foo']);
@ -100,8 +101,8 @@ class EngelsystemLoggerTest extends ApplicationFeatureTest
public function testContextToString() public function testContextToString()
{ {
LogEntry::query()->truncate();
$logger = $this->getLogger(); $logger = $this->getLogger();
LogEntries_clear_all();
$mock = $this->getMockBuilder('someDataProvider') $mock = $this->getMockBuilder('someDataProvider')
->setMethods(['__toString']) ->setMethods(['__toString'])
@ -132,14 +133,14 @@ class EngelsystemLoggerTest extends ApplicationFeatureTest
*/ */
public function getLastEntry() public function getLastEntry()
{ {
$entries = LogEntries(); $entries = LogEntry::all();
$entry = array_pop($entries); $entry = $entries->last();
return $entry; return $entry;
} }
public function tearDown() public function tearDown()
{ {
LogEntries_clear_all(); LogEntry::query()->truncate();
} }
} }

View File

@ -1,33 +0,0 @@
<?php
namespace Engelsystem\Test\Feature\Model;
use Engelsystem\Test\Feature\ApplicationFeatureTest;
use Psr\Log\LogLevel;
class LogEntriesModelTest extends ApplicationFeatureTest
{
public function testCreateLogEntry()
{
LogEntries_clear_all();
$count = count(LogEntries());
$this->assertNotFalse(LogEntry_create(LogLevel::WARNING, 'test_LogEntry_create'));
// There should be one more log entry now
$this->assertEquals(count(LogEntries()), $count + 1);
}
public function testClearAllLogEntries()
{
LogEntry_create(LogLevel::WARNING, 'test');
$this->assertTrue(count(LogEntries()) > 0);
$this->assertNotFalse(LogEntries_clear_all());
$this->assertCount(0, LogEntries());
}
public function tearDown()
{
LogEntries_clear_all();
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Engelsystem\Test\Feature\Model;
use Engelsystem\Models\LogEntry;
use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel;
class LogEntryTest extends TestCase
{
/**
* @covers \Engelsystem\Models\LogEntry::filter
*/
public function testFilter()
{
foreach ([
'Lorem Ipsum' => LogLevel::INFO,
'Some test content' => LogLevel::ERROR,
'Foo bar bartz!' => LogLevel::INFO,
'Someone did something?' => LogLevel::NOTICE,
'This is a Test!' => LogLevel::INFO,
'I\'m verbose notice!' => LogLevel::DEBUG,
'The newest stuff!!' => LogLevel::ERROR,
] as $message => $level) {
$entry = new LogEntry(['level' => $level, 'message' => $message]);
$entry->save();
}
$model = new LogEntry();
$return = $model->filter();
$this->assertCount(7, $return);
/** @var LogEntry $first */
$first = $return->first();
$this->assertEquals('The newest stuff!!', $first->message);
$return = $model->filter(LogLevel::INFO);
$this->assertCount(3, $return);
$return = $model->filter('notice');
$this->assertCount(2, $return);
$return = $model->filter('bartz');
$this->assertCount(1, $return);
}
/**
* This method is called before a test is executed.
*/
public function setUp()
{
LogEntry::query()->truncate();
}
}

View File

@ -17,6 +17,7 @@ class LoggerServiceProviderTest extends ServiceProviderTest
{ {
/** @var PHPUnit_Framework_MockObject_MockObject|EngelsystemLogger $logger */ /** @var PHPUnit_Framework_MockObject_MockObject|EngelsystemLogger $logger */
$logger = $this->getMockBuilder(EngelsystemLogger::class) $logger = $this->getMockBuilder(EngelsystemLogger::class)
->disableOriginalConstructor()
->getMock(); ->getMock();
$app = $this->getApp(['make', 'instance', 'bind']); $app = $this->getApp(['make', 'instance', 'bind']);

View File

@ -0,0 +1,22 @@
<?php
namespace Engelsystem\Test\Unit\Models;
use Engelsystem\Test\Unit\Models\Stub\BaseModelImplementation;
use PHPUnit\Framework\TestCase;
class BaseModelTest extends TestCase
{
/**
* @covers \Engelsystem\Models\BaseModel::create
*/
public function testCreate()
{
$model = new BaseModelImplementation();
$newModel = $model->create(['foo' => 'bar']);
$this->assertNotEquals($model, $newModel);
$this->assertEquals('bar', $newModel->foo);
$this->assertEquals(1, $newModel->saveCount);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Engelsystem\Test\Unit\Models\Stub;
use Engelsystem\Models\BaseModel;
/**
* @property string foo
*/
class BaseModelImplementation extends BaseModel
{
/** @var array */
protected $fillable = ['foo'];
/** @var int */
public $saveCount = 0;
/**
* @param array $options
* @return bool
*/
public function save(array $options = [])
{
$this->saveCount++;
return true;
}
}