Prechádzať zdrojové kódy

Add handling of inode changes to reopen files in StreamHandler (#1963)

Cesar Garcia 2 mesiacov pred
rodič
commit
282b43c996

+ 31 - 0
src/Monolog/Handler/StreamHandler.php

@@ -38,6 +38,7 @@ class StreamHandler extends AbstractProcessingHandler
     /** @var true|null */
     private bool|null $dirCreated = null;
     private bool $retrying = false;
+    private int|null $inodeUrl = null;
 
     /**
      * @param resource|string $stream         If a missing path can't be created, an UnexpectedValueException will be thrown on first write
@@ -133,6 +134,13 @@ class StreamHandler extends AbstractProcessingHandler
      */
     protected function write(LogRecord $record): void
     {
+        if ($this->hasUrlInodeWasChanged()) {
+            $this->close();
+            $this->write($record);
+
+            return;
+        }
+
         if (!\is_resource($this->stream)) {
             $url = $this->url;
             if (null === $url || '' === $url) {
@@ -157,6 +165,7 @@ class StreamHandler extends AbstractProcessingHandler
             }
             stream_set_chunk_size($stream, $this->streamChunkSize);
             $this->stream = $stream;
+            $this->inodeUrl = $this->getInodeFromUrl();
         }
 
         $stream = $this->stream;
@@ -246,4 +255,26 @@ class StreamHandler extends AbstractProcessingHandler
         }
         $this->dirCreated = true;
     }
+
+    private function getInodeFromUrl(): ?int
+    {
+        if ($this->url === null || $this->url === 'php://memory') {
+            return null;
+        }
+
+        $inode = @fileinode($this->url);
+
+        return $inode === false ? null : $inode;
+    }
+
+    private function hasUrlInodeWasChanged(): bool
+    {
+        if ($this->inodeUrl === null || $this->retrying || $this->inodeUrl === $this->getInodeFromUrl()) {
+            return false;
+        }
+
+        $this->retrying = true;
+
+        return true;
+    }
 }

+ 12 - 0
tests/Monolog/Handler/StreamHandlerTest.php

@@ -365,4 +365,16 @@ The exception occurred while attempting to log: test');
             ini_set('memory_limit', $previousValue);
         }
     }
+
+    public function testReopensFileIfInodeChanges()
+    {
+        $filename = __DIR__ . '/test.log';
+        $handler = new StreamHandler($filename);
+        $handler->setFormatter($this->getIdentityFormatter());
+        $handler->handle($this->getRecord(Level::Warning, 'test1'));
+        @unlink($filename);
+        $handler->handle($this->getRecord(Level::Warning, 'test2'));
+        $data = @file_get_contents($filename);
+        $this->assertEquals('test2', $data);
+    }
 }