Skip to content

Instantly share code, notes, and snippets.

@abraovic
Last active February 8, 2018 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abraovic/b2978782b13a77479ca1f59df0d11e08 to your computer and use it in GitHub Desktop.
Save abraovic/b2978782b13a77479ca1f59df0d11e08 to your computer and use it in GitHub Desktop.
Enables simple logging into a file for php applications. It is tested only on linux machines so it will need some improvements to work on other platforms.
<?php
$logger = new SimpleLogger([
'log_file' => '/opt/symfony/app/cache/simplelogger/my.log',
'max_log_file_size' => 3,
'clean_old_files' => true,
'max_log_file_archive' => 3,
'log_backtrace' => true
]);
$logger->info('test');
$logger->warning('test'); // log line expample [WARNING] - 27.09.2016, Tuesday, @ 13:35:49 - test
$logger->error('test');
// config explanations:
// log_file (string) -> full path to log file (default: /tmp/simple_logger/simple_logger.log)
// max_log_file_size (int) -> number of lines that single log file can have (default: 1000)
// clean_old_files (bool) -> true/false which defines if system should clean old archive files (default: false)
// max_log_file_archive (int) -> defines what is max limit of archives that won't be deleted if previous property is true
// (default: 10)
// log_backtrace (bool) -> when log line is created at the end of it is added file path and line where is it called from
// (default: false)
<?php
/**
* The MIT License (MIT)
* Copyright (c) 2016 Ante Braović - abraovic@gmail.com - antebraovic.me
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
* EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
* OR OTHER DEALINGS IN THE SOFTWARE.
*
* @author Ante Braović - abraovic@gmail.com - antebraovic.me
*/
class SimpleLogger
{
const INFO = "INFO";
const WARNING = "WARNING";
const ERROR = "ERROR";
/**
* Defines where logs will be stored. Default value is /tmp/simple_logger/simple_logger.log.
* You can change the default by setting this attribute.
*
* @var string $logFile
*/
private $logFile = "/tmp/simple_logger/simple_logger.log";
/**
* Defines max number of lines single file can have. If the file reaches limit, new
* file will be created. Default value is 1000.
* You can change the default by setting this attribute.
*
* @var int $maxLogFileSize
*/
private $maxLogFileSize = 1000;
/**
* Defines should the system clean old log files. Default value is false. If it is
* set as true default file age is one month.
* You can change the default by setting this attribute.
*
* @var bool $cleanOldFiles
*/
private $cleanOldFiles = false;
/**
* Defines how long should system keep archive log files. Default value is 10 which means that
* archives > simple_logger.log10 will be deleted if $cleanOldFiles is true
* You can change the default by setting this attribute.
*
* @var int $maxLogFileArchive
*/
private $maxLogFileArchive = 10;
/**
* Defines if logger should write down file and line number from where that line has been
* called. Default value is false.
* You can change the default by setting this attribute.
*
* @var bool $logBacktrace
*/
private $logBacktrace = false;
/**
* @constructor
* @param $config -> array that holds configuration options
*/
function __construct($config = [
'log_file' => '',
'max_log_file_size' => 0,
'clean_old_files' => false,
'max_log_file_archive' => 0,
'log_backtrace' => false
]) {
$this->logFile = $config['log_file'] ? $config['log_file'] : $this->logFile;
$this->maxLogFileSize = $config['max_log_file_size'] ? $config['max_log_file_size'] : $this->maxLogFileSize;
$this->cleanOldFiles = $config['clean_old_files'];
$this->maxLogFileArchive = $config['max_log_file_archive'] ? $config['max_log_file_archive'] : $this->maxLogFileArchive;
$this->logBacktrace = $config['clean_old_files'];
}
/**
* Writes info
* @param $data
*/
public function info($data)
{
$this->write($data, self::INFO);
}
/**
* Writes warning
* @param $data
*/
public function warning($data)
{
$this->write($data, self::WARNING);
}
/**
* Writes error
* @param $data
*/
public function error($data)
{
$this->write($data, self::ERROR);
}
/**
* Writes data to a proper file with the given log type.
*
* @param $data
* @param $type
*/
private function write($data, $type)
{
if ($this->isReachedMaxFileSize()) {
$this->shiftFileNames();
}
$backtrace = "";
if ($this->logBacktrace) {
$dbbt = debug_backtrace();
$backtrace = " - <Called in " . $dbbt[1]['file'] . " at line " . $dbbt[1]['line'] . ">";
}
$logLine = "[" . $type . "] - " . date('d.m.Y, l, @ H:i:s') . " - " . $data . $backtrace .PHP_EOL;
file_put_contents($this->logFile, $logLine, FILE_APPEND);
$this->cleanOldFiles();
}
/**
* Checks if number of lines limit is reached
* Thanks and explanation:
* @see http://stackoverflow.com/questions/2162497/efficiently-counting-the-number-of-lines-of-a-text-file-200mb
*
* @return bool
*/
private function isReachedMaxFileSize()
{
$lineCount = 0;
try{
$handle = fopen($this->logFile, "r");
// this should throw warning, but if that is off check if handle is
// false, and if it is throw an exception manually
if (!$handle) {
throw new \Exception();
}
} catch (\Exception $e) {
// break log file path to peaces, remove file name to get folder path
// check if folder exists, and create it if not
$path = explode(DIRECTORY_SEPARATOR, $this->logFile);
unset($path[count($path)-1]);
$path = implode(DIRECTORY_SEPARATOR, $path);
if (!file_exists($path)) {
mkdir($path);
}
touch($this->logFile);
$handle = fopen($this->logFile, "r");
}
while(!feof($handle)){
$line = fgets($handle, 4096);
$last = strlen($line) - 1;
if ($line[ $last ] == "\n" || $line[ $last ] == "\r") {
$lineCount++;
}
}
fclose($handle);
return $lineCount >= $this->maxLogFileSize;
}
/**
* When line number limit is reached we need to create archive files
* named simple_logger.log[x] where x is number of archive in order. Greater
* the number older the archive.
*/
private function shiftFileNames()
{
$existingFiles = glob($this->logFile . "*");
$totalNo = count($existingFiles);
for ($i = $totalNo; $i > 1; $i--) {
$oldNo = $i - 1;
rename($this->logFile . $oldNo, $this->logFile . $i);
}
// rename current active
rename($this->logFile, $this->logFile . 1);
// create new log file
touch($this->logFile);
}
/**
* Clean old archive files. This can help prevent filling the disk with old log data.
*/
private function cleanOldFiles()
{
if (!$this->cleanOldFiles) {
return;
}
$existingFiles = glob($this->logFile . "*");
// we need to subtract 1 because it counts active log as well
// and that means there is one more file that we must not take
$totalNo = count($existingFiles) - 1;
if ($totalNo <= $this->maxLogFileArchive) {
return;
}
for ($i = $totalNo; $i > $this->maxLogFileArchive; $i--) {
unlink($this->logFile . $i);
}
return;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment