├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── sample.png
└── src
├── CILogViewer.php
└── Views
└── logs.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | /vendor/
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | CHANGELOG
2 | ===========
3 |
4 | V2.0.0
5 | ------
6 | - Added support for CodeIgniter 4 and deprecate support for CodeIgniter 3
7 |
8 | V1.1.2
9 | -------
10 | - Fix issue [#13](https://github.com/SeunMatt/codeigniter-log-viewer/issues/13) and improve regex patterns
11 |
12 | V1.1.1
13 | -------
14 | - Fix security bug with file download [#8](https://github.com/SeunMatt/codeigniter-log-viewer/issues/8)
15 | - Updated required PHP version to >=7.1
16 |
17 | V1.1.0
18 | -------
19 | - Added API capability, such that log and log files can be obtained in a JSON response
20 | - Added log folder path and log file pattern configuration such that they can be configured via CodeIgniter's config.php file
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Seun Matt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | CodeIgniter Log Viewer
2 | ======================
3 |
4 | [](https://packagist.org/packages/seunmatt/codeigniter-log-viewer) [](https://packagist.org/packages/seunmatt/codeigniter-log-viewer) [](https://packagist.org/packages/seunmatt/codeigniter-log-viewer)
5 |
6 | This is a simple Log Viewer for viewing CodeIgniter logs in the browser or via API calls (that returns a JSON response)
7 |
8 | This project is inspired by the [laravel-log-viewer project](https://github.com/rap2hpoutre/laravel-log-viewer).
9 |
10 | A typical log view looks like this:
11 |
12 | 
13 |
14 | Usage
15 | =====
16 |
17 | **For CodeIgniter 3, see this [reference guide](https://github.com/SeunMatt/codeigniter-log-viewer/wiki/CodeIgniter-3-Guide)**
18 |
19 | Requirements
20 | -----------
21 | - PHP >= 7.4
22 | - CodeIgniter 4
23 |
24 | Composer Installation
25 | ---------------------
26 | ```
27 | composer require seunmatt/codeigniter-log-viewer
28 | ```
29 |
30 | Controller Integration for Browser Display
31 | ------------------------------------------
32 |
33 |
34 | All that is required is to execute the `showLogs()` method in a Controller that is mapped to a route:
35 |
36 | A typical Controller *(LogViewerController.php)* will have the following content:
37 |
38 | ```php
39 | namespace App\Controllers;
40 | use CILogViewer\CILogViewer;
41 |
42 | class LogViewerController extends BaseController
43 | {
44 | public function index() {
45 | $logViewer = new CILogViewer();
46 | return $logViewer->showLogs();
47 | }
48 | }
49 | ```
50 |
51 | Then the route `app/Config/Routes.php` can be configured like:
52 |
53 | ```php
54 | $routes->get('logs', "LogViewerController::index");
55 | ```
56 |
57 | And that's all! If you visit `/logs` on your browser
58 | you should see all the logs that are in `writable/logs` folder and their content
59 |
60 |
61 | Configuration
62 | ==============
63 |
64 | The package allows you to configure some of its parameters by creating a `CILogViewer` class in CodeIgniter's `Config` folder and then adding the following variables:
65 |
66 | - The folder path for log files can be configured with the `$logFolderPath` config var.
67 |
68 | - The file pattern for matching all the log files in the log folder can be configured by adding `$logFilePattern` config var.
69 | - The name of the view that renders the logs page can be changed using the `$viewName` config var. Please note that this can be a route relative to your `View` path or a namespace route.
70 |
71 | Example configuration file `app/Config/CILogViewer.php`:
72 |
73 | ```php
74 | logViewer->showLogs();`
96 | - Finally, map your controller function to a route.
97 |
98 | API Commands
99 | ------------
100 |
101 | The API is implemented via a set of query params that can be appended to the `/logs` path.
102 |
103 | Query:
104 |
105 | - `/logs?api=list` will list all the log files available in the configured folder
106 |
107 | Response:
108 |
109 | ```json
110 | {
111 | "status": true,
112 | "log_files": [
113 | {
114 | "file_b64": "bG9nLTIwMTgtMDEtMTkucGhw",
115 | "file_name": "log-2018-01-19.php"
116 | },
117 | {
118 | "file_b64": "bG9nLTIwMTgtMDEtMTcucGhw",
119 | "file_name": "log-2018-01-17.php"
120 | }
121 | ]
122 | }
123 | ```
124 |
125 | **file_b64 is the base64 encoded name of the file that will be used in further operations and API calls**
126 |
127 | Query:
128 |
129 | - `/logs?api=view&f=bG9nLTIwMTgtMDEtMTcucGhw` will return the logs contained in the log file specified by the `f` parameter.
130 |
131 | The value of the `f` (*f stands for file*) is the base64 encoded format of the log file name. It is obtained from the `/logs?api=list` API call.
132 | A list of all available log files is also returned.
133 |
134 | Response:
135 |
136 | ```json
137 | {
138 | "log_files": [
139 | {
140 | "file_b64": "bG9nLTIwMTgtMDEtMTkucGhw",
141 | "file_name": "log-2018-01-19.php"
142 | },
143 | {
144 | "file_b64": "bG9nLTIwMTgtMDEtMTcucGhw",
145 | "file_name": "log-2018-01-17.php"
146 | }
147 | ],
148 | "status": true,
149 | "logs": [
150 | "ERROR - 2018-01-23 07:12:31 --> 404 Page Not Found: admin/Logs/index",
151 | "ERROR - 2018-01-23 07:12:37 --> 404 Page Not Found: admin//index",
152 | "ERROR - 2018-01-23 15:23:02 --> 404 Page Not Found: Faviconico/index"
153 | ]
154 | }
155 | ```
156 |
157 | The API Query can also take one last parameter, `sline` that will determine how the logs are returned
158 | When it's `true` the logs are returned in a single line:
159 |
160 | Query:
161 |
162 | `/logs?api=view&f=bG9nLTIwMTgtMDEtMTkucGhw&sline=true`
163 |
164 | Response:
165 |
166 | ```json
167 | {
168 | "log_files": [
169 | {
170 | "file_b64": "bG9nLTIwMTgtMDEtMTkucGhw",
171 | "file_name": "log-2018-01-19.php"
172 | },
173 | {
174 | "file_b64": "bG9nLTIwMTgtMDEtMTcucGhw",
175 | "file_name": "log-2018-01-17.php"
176 | }
177 | ],
178 | "status": true,
179 | "logs": "ERROR - 2018-01-23 07:12:31 --> 404 Page Not Found: admin/Logs/index\r\nERROR - 2018-01-23 07:12:37 --> 404 Page Not Found: admin//index\r\nERROR - 2018-01-23 15:23:02 --> 404 Page Not Found: Faviconico/index\r\n"
180 | }
181 | ```
182 |
183 |
184 | When it's `false` (**Default**), the logs are returned in as an array, where each element is a line in the log file:
185 |
186 | Query:
187 |
188 | `/logs?api=view&f=bG9nLTIwMTgtMDEtMTkucGhw&sline=false` OR `logs?api=view&f=bG9nLTIwMTgtMDEtMTkucGhw`
189 |
190 | Response:
191 |
192 | ```json
193 | {
194 |
195 | "logs": [
196 | "ERROR - 2018-01-23 07:12:31 --> 404 Page Not Found: admin/Logs/index",
197 | "ERROR - 2018-01-23 07:12:37 --> 404 Page Not Found: admin//index",
198 | "ERROR - 2018-01-23 15:23:02 --> 404 Page Not Found: Faviconico/index"
199 | ]
200 | }
201 | ```
202 |
203 | Query:
204 |
205 | `/logs?api=delete&f=bG9nLTIwMTgtMDEtMTkucGhw` will delete a single log file. The **f** parameter is the base64 encoded name of the file
206 | and can be obtained from the view api above.
207 |
208 | Query:
209 |
210 | `/logs?api=delete&f=all` will delete all log files in the configured folder path. Take note of the value for **f** which is the literal '**all**'.
211 |
212 | **IF A FILE IS TOO LARGE (> 50MB), YOU CAN DOWNLOAD IT WITH THIS API QUERY `/logs?dl=bG9nLTIwMTgtMDEtMTcucGhw`**
213 |
214 |
215 | SECURITY NOTE
216 | =============
217 | **It is Highly Recommended that you protect/secure the route for your logs. It should not be an open resource!**
218 |
219 | Change Log
220 | ==========
221 | [Change Log is available here](./CHANGELOG.md)
222 |
223 |
224 | Author
225 | ======
226 | - [Seun Matt](https://smattme.com)
227 |
228 | Contributors
229 | ============
230 | - [Miguel Martinez](https://github.com/savioret)
231 |
232 |
233 | LICENSE
234 | =======
235 | [MIT](LICENSE)
236 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "seunmatt/codeigniter-log-viewer",
3 | "description": "This is a simple Log Viewer for viewing CodeIgniter log files on the browser",
4 | "keywords": ["Code Igniter", "Code Igniter 4", "ci4", "Log Viewer", "PHP"],
5 | "license": "MIT",
6 | "github": "https://github.com/SeunMatt/codeigniter-log-viewer",
7 | "authors": [
8 | {
9 | "name": "Miguel Martinez",
10 | "email": "rodilla@gmail.com"
11 | },
12 | {
13 | "name": "Seun Matt",
14 | "email": "smatt382@gmail.com"
15 | }
16 | ],
17 | "archive" : {
18 | "exclude" : ["/sample.png"]
19 | },
20 | "require": {
21 | "php": ">=7.4"
22 | },
23 | "autoload": {
24 | "psr-4": {
25 | "CILogViewer\\": "src/"
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SeunMatt/codeigniter-log-viewer/8f17091797e945063d555134675743c46a00570c/sample.png
--------------------------------------------------------------------------------
/src/CILogViewer.php:
--------------------------------------------------------------------------------
1 | 'glyphicon glyphicon-warning-sign',
13 | 'NOTICE' => 'glyphicon glyphicon-warning-sign',
14 | 'WARNING' => 'glyphicon glyphicon-warning-sign',
15 | 'ALERT' => 'glyphicon glyphicon-warning-sign',
16 | 'INFO' => 'glyphicon glyphicon-info-sign',
17 | 'ERROR' => 'glyphicon glyphicon-warning-sign',
18 | 'DEBUG' => 'glyphicon glyphicon-exclamation-sign',
19 | 'EMERGENCY' => 'glyphicon glyphicon-warning-sign',
20 | 'ALL' => 'glyphicon glyphicon-minus',
21 | ];
22 |
23 | private static $levelClasses = [
24 | 'CRITICAL' => 'danger',
25 | 'INFO' => 'info',
26 | 'ERROR' => 'danger',
27 | 'DEBUG' => 'warning',
28 | 'NOTICE' => 'info',
29 | 'WARNING' => 'warning',
30 | 'EMERGENCY' => 'danger',
31 | 'ALERT' => 'warning',
32 | 'ALL' => 'muted',
33 | ];
34 |
35 | const LOG_LINE_HEADER_PATTERN = '/^([A-Z]+)\s*\-\s*([\-\d]+\s+[\:\d]+)\s*\-\->\s*(.+)$/';
36 |
37 | //this is the path (folder) on the system where the log files are stored
38 | private $logFolderPath = WRITEPATH . 'logs/';
39 |
40 | //this is the pattern to pick all log files in the $logFilePath
41 | private $logFilePattern = "log-*.log";
42 |
43 | //this is a combination of the LOG_FOLDER_PATH and LOG_FILE_PATTERN
44 | private $fullLogFilePath = "";
45 |
46 | /**
47 | * Name of the view to pass to the renderer
48 | * Note that it allows namespaced views if your view is outside
49 | * the View folder.
50 | *
51 | * @var string
52 | */
53 | private $viewName = "CILogViewer\Views\logs";
54 |
55 | const MAX_LOG_SIZE = 52428800; //50MB
56 | const MAX_STRING_LENGTH = 300; //300 chars
57 |
58 | /**
59 | * These are the constants representing the
60 | * various API commands there are
61 | */
62 | private const API_QUERY_PARAM = "api";
63 | private const API_FILE_QUERY_PARAM = "f";
64 | private const API_LOG_STYLE_QUERY_PARAM = "sline";
65 | private const API_CMD_LIST = "list";
66 | private const API_CMD_VIEW = "view";
67 | private const API_CMD_DELETE = "delete";
68 |
69 |
70 | public function __construct() {
71 | $this->init();
72 | }
73 |
74 | /**
75 | * Bootstrap the library
76 | * sets the configuration variables
77 | * @throws \Exception
78 | */
79 | private function init() {
80 | $viewerConfig = config('CILogViewer');
81 |
82 | //configure the log folder path and the file pattern for all the logs in the folder
83 | if($viewerConfig) {
84 | if(isset($viewerConfig->viewName)) {
85 | $this->viewName = $viewerConfig->viewName;
86 | }
87 | if(isset($viewerConfig->logFilePattern)) {
88 | $this->logFilePattern = $viewerConfig->logFilePattern;
89 | }
90 | if(isset($viewerConfig->logFolderPath)) {
91 | $this->logFolderPath = $viewerConfig->logFolderPath;
92 | }
93 | }
94 |
95 | //concatenate to form Full Log Path
96 | $this->fullLogFilePath = $this->logFolderPath . $this->logFilePattern;
97 | }
98 |
99 | /*
100 | * This function will return the processed HTML page
101 | * and return it's content that can then be echoed
102 | *
103 | * @param $fileName optional base64_encoded filename of the log file to process.
104 | * @returns the parse view file content as a string that can be echoed
105 | * */
106 | public function showLogs() {
107 |
108 | $request = \Config\Services::request();
109 |
110 | if(!is_null($request->getGet("del"))) {
111 | $this->deleteFiles(base64_decode($request->getGet("del")));
112 | $uri = \Config\Services::request()->uri->getPath();
113 | return redirect()->to('/'.$uri);
114 | }
115 |
116 | //process download of log file command
117 | //if the supplied file exists, then perform download
118 | //otherwise, just ignore which will resolve to page reloading
119 | $dlFile = $request->getGet("dl");
120 | if(!is_null($dlFile) && file_exists($this->logFolderPath . basename(base64_decode($dlFile))) ) {
121 | $file = $this->logFolderPath . basename(base64_decode($dlFile));
122 | $this->downloadFile($file);
123 | }
124 |
125 | if(!is_null($request->getGet(self::API_QUERY_PARAM))) {
126 | return $this->processAPIRequests($request->getGet(self::API_QUERY_PARAM));
127 | }
128 |
129 | //it will either get the value of f or return null
130 | $fileName = $request->getGet("f");
131 |
132 | //get the log files from the log directory
133 | $files = $this->getFiles();
134 |
135 | //let's determine what the current log file is
136 | if(!is_null($fileName)) {
137 | $currentFile = $this->logFolderPath . basename(base64_decode($fileName));
138 | }
139 | else if(is_null($fileName) && !empty($files)) {
140 | $currentFile = $this->logFolderPath . $files[0];
141 | } else {
142 | $currentFile = null;
143 | }
144 |
145 | //if the resolved current file is too big
146 | //just trigger a download of the file
147 | //otherwise process its content as log
148 |
149 | if(!is_null($currentFile) && file_exists($currentFile)) {
150 |
151 | $fileSize = filesize($currentFile);
152 |
153 | if(is_int($fileSize) && $fileSize > self::MAX_LOG_SIZE) {
154 | //trigger a download of the current file instead
155 | $logs = null;
156 | }
157 | else {
158 | $logs = $this->processLogs($this->getLogs($currentFile));
159 | }
160 | }
161 | else {
162 | $logs = [];
163 | }
164 |
165 | $data['logs'] = $logs;
166 | $data['files'] = !empty($files) ? $files : [];
167 | $data['currentFile'] = !is_null($currentFile) ? basename($currentFile) : "";
168 | return view($this->viewName, $data);
169 | }
170 |
171 |
172 | private function processAPIRequests($command) {
173 | $request = \Config\Services::request();
174 | if($command === self::API_CMD_LIST) {
175 | //respond with a list of all the files
176 | $response["status"] = true;
177 | $response["log_files"] = $this->getFilesBase64Encoded();
178 | }
179 | else if($command === self::API_CMD_VIEW) {
180 | //respond to view the logs of a particular file
181 | $file = $request->getGet(self::API_FILE_QUERY_PARAM);
182 | $response["log_files"] = $this->getFilesBase64Encoded();
183 |
184 | if(is_null($file) || empty($file)) {
185 | $response["status"] = false;
186 | $response["error"]["message"] = "Invalid File Name Supplied: [" . json_encode($file) . "]";
187 | $response["error"]["code"] = 400;
188 | }
189 | else {
190 | $singleLine = $request->getGet(self::API_LOG_STYLE_QUERY_PARAM);
191 | $singleLine = !is_null($singleLine) && ($singleLine === true || $singleLine === "true" || $singleLine === "1") ? true : false;
192 | $logs = $this->processLogsForAPI($file, $singleLine);
193 | $response["status"] = true;
194 | $response["logs"] = $logs;
195 | }
196 | }
197 | else if($command === self::API_CMD_DELETE) {
198 |
199 | $file = $request->getGet(self::API_FILE_QUERY_PARAM);
200 |
201 | if(is_null($file)) {
202 | $response["status"] = false;
203 | $response["error"]["message"] = "NULL value is not allowed for file param";
204 | $response["error"]["code"] = 400;
205 | }
206 | else {
207 |
208 | //decode file if necessary
209 | $fileExists = false;
210 |
211 | if($file !== "all") {
212 | $file = basename(base64_decode($file));
213 | $fileExists = file_exists($this->logFolderPath . $file);
214 | }
215 | else {
216 | //check if the directory exists
217 | $fileExists = file_exists($this->logFolderPath);
218 | }
219 |
220 |
221 | if($fileExists) {
222 | $this->deleteFiles($file);
223 | $response["status"] = true;
224 | $response["message"] = "File [" . $file . "] deleted";
225 | }
226 | else {
227 | $response["status"] = false;
228 | $response["error"]["message"] = "File does not exist";
229 | $response["error"]["code"] = 404;
230 | }
231 |
232 |
233 | }
234 | }
235 | else {
236 | $response["status"] = false;
237 | $response["error"]["message"] = "Unsupported Query Command [" . $command . "]";
238 | $response["error"]["code"] = 400;
239 | }
240 |
241 | //convert response to json and respond
242 | header("Content-Type: application/json");
243 | if(!$response["status"]) {
244 | //set a generic bad request code
245 | http_response_code(400);
246 | } else {
247 | http_response_code(200);
248 | }
249 | return json_encode($response);
250 | }
251 |
252 |
253 | /*
254 | * This function will process the logs. Extract the log level, icon class and other information
255 | * from each line of log and then arrange them in another array that is returned to the view for processing
256 | *
257 | * @params logs. The raw logs as read from the log file
258 | * @return array. An [[], [], [] ...] where each element is a processed log line
259 | * */
260 | private function processLogs($logs) {
261 |
262 | if(is_null($logs)) {
263 | return null;
264 | }
265 |
266 | $superLog = [];
267 |
268 | foreach ($logs as $log) {
269 |
270 | if($this->getLogHeaderLine($log, $level, $logDate, $logMessage)) {
271 | //this is actually the start of a new log and not just another line from previous log
272 | $data = [
273 | "level" => $level,
274 | "date" => $logDate,
275 | "icon" => self::$levelsIcon[$level],
276 | "class" => self::$levelClasses[$level],
277 | ];
278 |
279 | if(strlen($logMessage) > self::MAX_STRING_LENGTH) {
280 | $data['content'] = substr($logMessage, 0, self::MAX_STRING_LENGTH);
281 | $data["extra"] = substr($logMessage, (self::MAX_STRING_LENGTH + 1));
282 | } else {
283 | $data["content"] = $logMessage;
284 | }
285 |
286 | array_push($superLog, $data);
287 |
288 | } else if(!empty($superLog)) {
289 | //this log line is a continuation of previous logline
290 | //so let's add them as extra
291 | $prevLog = $superLog[count($superLog) - 1];
292 | $extra = (array_key_exists("extra", $prevLog)) ? $prevLog["extra"] : "";
293 | $prevLog["extra"] = $extra . "
" . $log;
294 | $superLog[count($superLog) - 1] = $prevLog;
295 | }
296 | }
297 |
298 | return $superLog;
299 | }
300 |
301 |
302 | /**
303 | * This function will extract the logs in the supplied
304 | * fileName
305 | * @param $fileNameInBase64
306 | * @param bool $singleLine
307 | * @return array|null
308 | * @internal param $logs
309 | */
310 | private function processLogsForAPI($fileNameInBase64, $singleLine = false) {
311 |
312 | $logs = null;
313 |
314 | //let's prepare the log file name sent from the client
315 | $currentFile = $this->prepareRawFileName($fileNameInBase64);
316 |
317 | //if the resolved current file is too big
318 | //just return null
319 | //otherwise process its content as log
320 | if(!is_null($currentFile)) {
321 |
322 | $fileSize = filesize($currentFile);
323 |
324 | if (is_int($fileSize) && $fileSize > self::MAX_LOG_SIZE) {
325 | //trigger a download of the current file instead
326 | $logs = null;
327 | } else {
328 | $logs = $this->getLogsForAPI($currentFile, $singleLine);
329 | }
330 | }
331 |
332 | return $logs;
333 | }
334 |
335 | private function getLogHeaderLine($logLine, &$level, &$dateTime, &$message) {
336 | $matches = [];
337 | if(preg_match(self::LOG_LINE_HEADER_PATTERN, $logLine, $matches)) {
338 | $level = $matches[1];
339 | $dateTime = $matches[2];
340 | $message = $matches[3];
341 | }
342 | return $matches;
343 | }
344 |
345 | /*
346 | * returns an array of the file contents
347 | * each element in the array is a line
348 | * in the underlying log file
349 | * @returns array | each line of file contents is an entry in the returned array.
350 | * @params complete fileName
351 | * */
352 | private function getLogs($fileName) {
353 | $size = filesize($fileName);
354 | if(!$size || $size > self::MAX_LOG_SIZE){
355 | return null;
356 | }
357 | return file($fileName, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
358 | }
359 |
360 | /**
361 | * This function will get the contents of the log
362 | * file as a string. It will first check for the
363 | * size of the file before attempting to get the contents.
364 | *
365 | * By default it will return all the log contents as an array where the
366 | * elements of the array is the individual lines of the files
367 | * otherwise, it will return all file content as a single string with each line ending
368 | * in line break character "\n"
369 | * @param $fileName
370 | * @param bool $singleLine
371 | * @return bool|string
372 | */
373 | private function getLogsForAPI($fileName, $singleLine = false) {
374 | $size = filesize($fileName);
375 | if(!$size || $size > self::MAX_LOG_SIZE) {
376 | return "File Size too Large. Please donwload it locally";
377 | }
378 |
379 | return (!$singleLine) ? file($fileName, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) : file_get_contents($fileName);
380 | }
381 |
382 |
383 | /*
384 | * This will get all the files in the logs folder
385 | * It will reverse the files fetched and
386 | * make sure the latest log file is in the first index
387 | *
388 | * @param boolean. If true returns the basename of the files otherwise full path
389 | * @returns array of file
390 | * */
391 | private function getFiles($basename = true)
392 | {
393 |
394 | $files = glob($this->fullLogFilePath);
395 |
396 | $files = array_reverse($files);
397 | $files = array_filter($files, 'is_file');
398 | if ($basename && is_array($files)) {
399 | foreach ($files as $k => $file) {
400 | $files[$k] = basename($file);
401 | }
402 | }
403 | return array_values($files);
404 | }
405 |
406 |
407 | /**
408 | * This function will return an array of available log
409 | * files
410 | * The array will containt the base64encoded name
411 | * as well as the real name of the fiile
412 | * @return array
413 | * @internal param bool $appendURL
414 | * @internal param bool $basename
415 | */
416 | private function getFilesBase64Encoded() {
417 |
418 | $files = glob($this->fullLogFilePath);
419 |
420 | $files = array_reverse($files);
421 | $files = array_filter($files, 'is_file');
422 |
423 | $finalFiles = [];
424 |
425 | //if we're to return the base name of the files
426 | //let's do that here
427 | foreach ($files as $file) {
428 | array_push($finalFiles, ["file_b64" => base64_encode(basename($file)), "file_name" => basename($file)]);
429 | }
430 |
431 | return $finalFiles;
432 | }
433 |
434 | /*
435 | * Delete one or more log file in the logs directory
436 | * @param filename. It can be all - to delete all log files - or specific for a file
437 | * */
438 | private function deleteFiles($fileName) {
439 |
440 | if($fileName == "all") {
441 | array_map("unlink", glob($this->fullLogFilePath));
442 | }
443 | else {
444 | unlink($this->logFolderPath . basename($fileName));
445 | }
446 | }
447 |
448 | /*
449 | * Download a particular file to local disk
450 | * This should only be called if the file exists
451 | * hence, the file exist check has ot be done by the caller
452 | * @param $fileName the complete file path
453 | * */
454 | private function downloadFile($file) {
455 | header('Content-Description: File Transfer');
456 | header('Content-Type: application/octet-stream');
457 | header('Content-Disposition: attachment; filename="'.basename($file).'"');
458 | header('Expires: 0');
459 | header('Cache-Control: must-revalidate');
460 | header('Pragma: public');
461 | header('Content-Length: ' . filesize($file));
462 | readfile($file);
463 | exit;
464 | }
465 |
466 |
467 | /**
468 | * This function will take in the raw file
469 | * name as sent from the browser/client
470 | * and append the LOG_FOLDER_PREFIX and decode it from base64
471 | * @param $fileNameInBase64
472 | * @return null|string
473 | * @internal param $fileName
474 | */
475 | private function prepareRawFileName($fileNameInBase64) {
476 |
477 | //let's determine what the current log file is
478 | if(!is_null($fileNameInBase64) && !empty($fileNameInBase64)) {
479 | $currentFile = $this->logFolderPath . basename(base64_decode($fileNameInBase64));
480 | }
481 | else {
482 | $currentFile = null;
483 | }
484 |
485 | return $currentFile;
486 | }
487 |
488 |
489 |
490 | }
491 |
492 |
493 |
494 |
--------------------------------------------------------------------------------
/src/Views/logs.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Level | 77 |Date | 78 |Content | 79 |
---|---|---|
87 | 88 | = $log['level']; ?> 89 | | 90 |= $log['date']; ?> | 91 |92 | 93 | 95 | 96 | 97 | 98 | = $log['content']; ?> 99 | 100 | 104 | 105 | 106 | | 107 |