20
20
21
21
App::uses ('BaseLog ' , 'Log/Engine ' );
22
22
App::uses ('Hash ' , 'Utility ' );
23
+ App::uses ('CakeNumber ' , 'Utility ' );
23
24
24
25
/**
25
26
* File Storage stream for Logging. Writes logs to different files
29
30
*/
30
31
class FileLog extends BaseLog {
31
32
33
+ /**
34
+ * Default configuration values
35
+ *
36
+ * @var array
37
+ * @see FileLog::__construct()
38
+ */
39
+ protected $ _defaults = array (
40
+ 'path ' => LOGS ,
41
+ 'file ' => null ,
42
+ 'types ' => null ,
43
+ 'scopes ' => array (),
44
+ 'rotate ' => 10 ,
45
+ 'size ' => 10485760 // 10MB
46
+ );
47
+
32
48
/**
33
49
* Path to save log files on.
34
50
*
35
51
* @var string
36
52
*/
37
53
protected $ _path = null ;
38
54
55
+ /**
56
+ * Log file name
57
+ *
58
+ * @var string
59
+ */
60
+ protected $ _file = null ;
61
+
62
+ /**
63
+ * Max file size, used for log file rotation.
64
+ *
65
+ * @var integer
66
+ */
67
+ protected $ _size = null ;
68
+
39
69
/**
40
70
* Constructs a new File Logger.
41
71
*
42
72
* Config
43
73
*
44
74
* - `types` string or array, levels the engine is interested in
45
75
* - `scopes` string or array, scopes the engine is interested in
46
- * - `file` log file name
47
- * - `path` the path to save logs on.
76
+ * - `file` Log file name
77
+ * - `path` The path to save logs on.
78
+ * - `size` Used to implement basic log file rotation. If log file size
79
+ * reaches specified size the existing file is renamed by appending timestamp
80
+ * to filename and new log file is created. Can be integer bytes value or
81
+ * human reabable string values like '10MB', '100KB' etc.
82
+ * - `rotate` Log files are rotated specified times before being removed.
83
+ * If value is 0, old versions are removed rather then rotated.
48
84
*
49
85
* @param array $options Options for the FileLog, see above.
50
86
*/
51
87
public function __construct ($ config = array ()) {
88
+ $ config = Hash::merge ($ this ->_defaults , $ config );
52
89
parent ::__construct ($ config );
53
- $ config = Hash::merge (array (
54
- 'path ' => LOGS ,
55
- 'file ' => null ,
56
- 'types ' => null ,
57
- 'scopes ' => array (),
58
- ), $ this ->_config );
59
- $ config = $ this ->config ($ config );
60
- $ this ->_path = $ config ['path ' ];
61
- $ this ->_file = $ config ['file ' ];
62
- if (!empty ($ this ->_file ) && !preg_match ('/\.log$/ ' , $ this ->_file )) {
63
- $ this ->_file .= '.log ' ;
90
+ }
91
+
92
+ /**
93
+ * Sets protected properties based on config provided
94
+ *
95
+ * @param array $config Engine configuration
96
+ * @return array
97
+ */
98
+ public function config ($ config = array ()) {
99
+ parent ::config ($ config );
100
+
101
+ if (!empty ($ config ['path ' ])) {
102
+ $ this ->_path = $ config ['path ' ];
103
+ }
104
+ if (!empty ($ config ['file ' ])) {
105
+ $ this ->_file = $ config ['file ' ];
106
+ if (substr ($ this ->_file , -4 ) !== '.log ' ) {
107
+ $ this ->_file .= '.log ' ;
108
+ }
109
+ }
110
+ if (!empty ($ config ['size ' ])) {
111
+ if (is_numeric ($ config ['size ' ])) {
112
+ $ this ->_size = (int )$ config ['size ' ];
113
+ } else {
114
+ $ this ->_size = CakeNumber::fromReadableSize ($ config ['size ' ]);
115
+ }
64
116
}
117
+
118
+ return $ this ->_config ;
65
119
}
66
120
67
121
/**
@@ -72,21 +126,70 @@ public function __construct($config = array()) {
72
126
* @return boolean success of write.
73
127
*/
74
128
public function write ($ type , $ message ) {
129
+ $ output = date ('Y-m-d H:i:s ' ) . ' ' . ucfirst ($ type ) . ': ' . $ message . "\n" ;
130
+ $ filename = $ this ->_getFilename ($ type );
131
+ if (!empty ($ this ->_size )) {
132
+ $ this ->_rotateFile ($ filename );
133
+ }
134
+
135
+ return file_put_contents ($ this ->_path . $ filename , $ output , FILE_APPEND );
136
+ }
137
+
138
+ /**
139
+ * Get filename
140
+ * @param string $type The type of log.
141
+ * @return string File name
142
+ */
143
+ protected function _getFilename ($ type ) {
75
144
$ debugTypes = array ('notice ' , 'info ' , 'debug ' );
76
145
77
146
if (!empty ($ this ->_file )) {
78
- $ filename = $ this ->_path . $ this -> _file ;
147
+ $ filename = $ this ->_file ;
79
148
} elseif ($ type == 'error ' || $ type == 'warning ' ) {
80
- $ filename = $ this -> _path . 'error.log ' ;
149
+ $ filename = 'error.log ' ;
81
150
} elseif (in_array ($ type , $ debugTypes )) {
82
- $ filename = $ this ->_path . 'debug.log ' ;
83
- } elseif (in_array ($ type , $ this ->_config ['scopes ' ])) {
84
- $ filename = $ this ->_path . $ this ->_file ;
151
+ $ filename = 'debug.log ' ;
85
152
} else {
86
- $ filename = $ this -> _path . $ type . '.log ' ;
153
+ $ filename = $ type . '.log ' ;
87
154
}
88
- $ output = date ('Y-m-d H:i:s ' ) . ' ' . ucfirst ($ type ) . ': ' . $ message . "\n" ;
89
- return file_put_contents ($ filename , $ output , FILE_APPEND );
155
+
156
+ return $ filename ;
157
+ }
158
+
159
+ /**
160
+ * Rotate log file if size specified in config is reached.
161
+ * Also if `rotate` count is reached oldest file is removed.
162
+ *
163
+ * @param string $filename Log file name
164
+ * @return mixed True if rotated successfully or false in case of error.
165
+ * Void if file doesn't need to be rotated.
166
+ */
167
+ protected function _rotateFile ($ filename ) {
168
+ $ filepath = $ this ->_path . $ filename ;
169
+ if (version_compare (PHP_VERSION , '5.3.0 ' ) >= 0 ) {
170
+ clearstatcache (true , $ filepath );
171
+ } else {
172
+ clearstatcache ();
173
+ }
174
+
175
+ if (!file_exists ($ filepath ) ||
176
+ filesize ($ filepath ) < $ this ->_size
177
+ ) {
178
+ return ;
179
+ }
180
+
181
+ if ($ this ->_config ['rotate ' ] === 0 ) {
182
+ return unlink ($ filepath );
183
+ }
184
+
185
+ if ($ this ->_config ['rotate ' ]) {
186
+ $ files = glob ($ filepath . '.* ' );
187
+ if (count ($ files ) === $ this ->_config ['rotate ' ]) {
188
+ unlink (array_shift ($ files ));
189
+ }
190
+ }
191
+
192
+ return rename ($ filepath , $ filepath . '. ' . time ());
90
193
}
91
194
92
195
}
0 commit comments