Passed
Push — master ( 345089...22c30c )
by Alexander
02:10
created

FileTarget   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 91
Duplicated Lines 0 %

Test Coverage

Coverage 78.38%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 41
c 1
b 0
f 0
dl 0
loc 91
ccs 29
cts 37
cp 0.7838
rs 10
wmc 12

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
B export() 0 48 11
1
<?php
2
namespace Yiisoft\Log\Target\File;
3
4
use Yiisoft\Files\FileHelper;
5
use Yiisoft\Log\LogRuntimeException;
6
use Yiisoft\Log\Target;
7
8
/**
9
 * FileTarget records log messages in a file.
10
 *
11
 * The log file is specified via [[logFile]]. If the size of the log file exceeds
12
 * [[maxFileSize]] (in kilo-bytes), a rotation will be performed, which renames
13
 * the current log file by suffixing the file name with '.1'. All existing log
14
 * files are moved backwards by one place, i.e., '.2' to '.3', '.1' to '.2', and so on.
15
 * The property [[maxLogFiles]] specifies how many history files to keep.
16
 */
17
class FileTarget extends Target
18
{
19
    /**
20
     * @var string log file path. If not set, it will use the "/tmp/app.log" file.
21
     * The directory containing the log files will be automatically created if not existing.
22
     */
23
    private $logFile;
24
    /**
25
     * @var int the permission to be set for newly created log files.
26
     * This value will be used by PHP chmod() function. No umask will be applied.
27
     * If not set, the permission will be determined by the current environment.
28
     */
29
    private $fileMode;
30
    /**
31
     * @var int the permission to be set for newly created directories.
32
     * This value will be used by PHP chmod() function. No umask will be applied.
33
     * Defaults to 0775, meaning the directory is read-writable by owner and group,
34
     * but read-only for other users.
35
     */
36
    private $dirMode;
37
38
    /**
39
     * @var FileRotatorInterface
40
     */
41
    private $rotator;
42
43 3
    public function __construct(
44
        string $logFile = '/tmp/app.log',
45
        FileRotatorInterface $rotator = null,
46
        int $dirMode = 0775,
47
        int $fileMode = null
48
    ) {
49 3
        $this->logFile = $logFile;
50 3
        $this->rotator = $rotator;
51 3
        $this->dirMode = $dirMode;
52 3
        $this->fileMode = $fileMode;
53
    }
54
55
    /**
56
     * Writes log messages to a file.
57
     * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.
58
     * @throws LogRuntimeException if unable to open or write complete log to file
59
     */
60 2
    public function export(): void
61
    {
62 2
        $logPath = dirname($this->logFile);
63
64 2
        if (!file_exists($logPath)) {
65
            FileHelper::createDirectory($logPath, $this->dirMode);
66
        }
67
68 2
        $text = implode("\n", array_map([$this, 'formatMessage'], $this->getMessages())) . "\n";
69
70 2
        if (($fp = fopen($this->logFile, 'ab')) === false) {
71
            throw new LogRuntimeException("Unable to append to log file: {$this->logFile}");
72
        }
73
74 2
        @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for flock(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

74
        /** @scrutinizer ignore-unhandled */ @flock($fp, LOCK_EX);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
75 2
        if ($this->rotator !== null) {
76
            // clear stat cache to ensure getting the real current file size and not a cached one
77
            // this may result in rotating twice when cached file size is used on subsequent calls
78 2
            clearstatcache();
79
        }
80 2
        if ($this->rotator !== null && @filesize($this->logFile) > $this->rotator->getMaxFileSize() * 1024) {
0 ignored issues
show
Bug introduced by
The method getMaxFileSize() does not exist on Yiisoft\Log\Target\File\FileRotatorInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\Log\Target\File\FileRotatorInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

80
        if ($this->rotator !== null && @filesize($this->logFile) > $this->rotator->/** @scrutinizer ignore-call */ getMaxFileSize() * 1024) {
Loading history...
81 2
            @flock($fp, LOCK_UN);
82 2
            @fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

82
            /** @scrutinizer ignore-unhandled */ @fclose($fp);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
83 2
            $this->rotator->rotateFile($this->logFile);
84 2
            $writeResult = @file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
85 2
            if ($writeResult === false) {
86
                $error = error_get_last();
87
                throw new LogRuntimeException("Unable to export log through file!: {$error['message']}");
88
            }
89 2
            $textSize = strlen($text);
90 2
            if ($writeResult < $textSize) {
91 2
                throw new LogRuntimeException("Unable to export whole log through file! Wrote $writeResult out of $textSize bytes.");
92
            }
93
        } else {
94 2
            $writeResult = @fwrite($fp, $text);
95 2
            if ($writeResult === false) {
96
                $error = error_get_last();
97
                throw new LogRuntimeException("Unable to export log through file!: {$error['message']}");
98
            }
99 2
            $textSize = strlen($text);
100 2
            if ($writeResult < $textSize) {
101
                throw new LogRuntimeException("Unable to export whole log through file! Wrote $writeResult out of $textSize bytes.");
102
            }
103 2
            @flock($fp, LOCK_UN);
104 2
            @fclose($fp);
105
        }
106 2
        if ($this->fileMode !== null) {
107
            @chmod($this->logFile, $this->fileMode);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

107
            /** @scrutinizer ignore-unhandled */ @chmod($this->logFile, $this->fileMode);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
108
        }
109
    }
110
}
111