49 private ThreadSafeArray $buffer;
50 private bool $syncFlush =
false;
51 private bool $shutdown =
false;
53 public function __construct(
54 private string $logFile,
55 private ?
string $archiveDir,
56 private readonly
int $maxFileSize = 32 * 1024 * 1024
58 $this->buffer =
new ThreadSafeArray();
59 touch($this->logFile);
60 if($this->archiveDir !==
null && !@mkdir($this->archiveDir) && !is_dir($this->archiveDir)){
61 throw new \RuntimeException(
"Unable to create archive directory: " . (
62 is_file($this->archiveDir) ?
"it already exists and is not a directory" :
"permission denied"));
66 public function write(
string $line) :
void{
67 $this->
synchronized(
function() use ($line) :
void{
68 $this->buffer[] = $line;
73 public function syncFlushBuffer() :
void{
74 $this->
synchronized(
function() :
void{
75 $this->syncFlush =
true;
78 $this->
synchronized(
function() :
void{
79 while($this->syncFlush){
85 public function shutdown() :
void{
86 $this->
synchronized(
function() :
void{
87 $this->shutdown =
true;
94 private function openLogFile(
string $file,
int &$size){
95 $logResource = fopen($file,
"ab");
96 if(!is_resource($logResource)){
97 throw new \RuntimeException(
"Couldn't open log file");
99 $stat = fstat($logResource);
103 $size = $stat[
'size'];
111 private function archiveLogFile($logResource,
int &$size,
string $archiveDir){
112 fclose($logResource);
117 $date = date(
"Y-m-d\TH.i.s");
118 $baseName = pathinfo($this->logFile, PATHINFO_FILENAME);
119 $extension = pathinfo($this->logFile, PATHINFO_EXTENSION);
122 $fileName =
"$baseName.$date.$i.$extension";
123 $out = $this->archiveDir .
"/" . $fileName;
125 }
while(file_exists($out));
129 rename($this->logFile, $out);
131 $logResource = $this->openLogFile($this->logFile, $size);
132 fwrite($logResource,
"--- Starting new log file - old log file archived as $fileName ---\n");
137 private function logFileReadyToArchive(
int $size) :
bool{
138 return $size >= $this->maxFileSize;
144 private function writeLogStream(&$logResource,
int &$size, ?
string $archiveDir) :
void{
145 while(($chunk = $this->buffer->shift()) !==
null){
146 fwrite($logResource, $chunk);
147 $size += strlen($chunk);
148 if($archiveDir !==
null && $this->logFileReadyToArchive($size)){
149 $logResource = $this->archiveLogFile($logResource, $size, $archiveDir);
153 $this->
synchronized(
function() :
void{
154 if($this->syncFlush){
155 $this->syncFlush =
false;
161 public function run() :
void{
163 $logResource = $this->openLogFile($this->logFile, $size);
164 $archiveDir = $this->archiveDir;
165 if($archiveDir !==
null && $this->logFileReadyToArchive($size)){
166 $logResource = $this->archiveLogFile($logResource, $size, $archiveDir);
169 while(!$this->shutdown){
170 $this->writeLogStream($logResource, $size, $archiveDir);
171 $this->
synchronized(
function() :
void{
172 if(!$this->shutdown && !$this->syncFlush){
178 $this->writeLogStream($logResource, $size, $archiveDir);
180 fclose($logResource);