├── .gitignore ├── screenshots ├── preview1.jpg └── preview2.jpg ├── src ├── views │ └── default │ │ ├── _counts.php │ │ ├── clean.php │ │ ├── zip.php │ │ ├── history.php │ │ └── index.php ├── models │ ├── CleanForm.php │ └── ZipLogForm.php ├── Module.php ├── Log.php └── controllers │ └── DefaultController.php ├── composer.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | composer.lock 3 | vendor -------------------------------------------------------------------------------- /screenshots/preview1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krissss/yii2-log-reader/HEAD/screenshots/preview1.jpg -------------------------------------------------------------------------------- /screenshots/preview2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krissss/yii2-log-reader/HEAD/screenshots/preview2.jpg -------------------------------------------------------------------------------- /src/views/default/_counts.php: -------------------------------------------------------------------------------- 1 | context->module; 11 | 12 | foreach ($log->getCounts() as $level => $count) { 13 | if (isset($module->levelClasses[$level])) { 14 | $class = $module->levelClasses[$level]; 15 | } else { 16 | $class = $module->defaultLevelClass; 17 | } 18 | echo Html::tag('span', $count, [ 19 | 'class' => 'label ' . $class, 20 | 'title' => $level, 21 | ]); 22 | echo ' '; 23 | } 24 | -------------------------------------------------------------------------------- /src/views/default/clean.php: -------------------------------------------------------------------------------- 1 | title = $model->log->name . ' clean'; 11 | $this->params['breadcrumbs'][] = ['label' => 'Logs', 'url' => ['index']]; 12 | $this->params['breadcrumbs'][] = ['label' => $model->log->name, 'url' => ['history', 'slug' => $model->log->slug]]; 13 | $this->params['breadcrumbs'][] = $this->title; 14 | 15 | $form = ActiveForm::begin(); 16 | 17 | echo $form->field($model, 'start')->input('date'); 18 | echo $form->field($model, 'end')->input('date'); 19 | 20 | echo Html::submitInput('Submit', ['class' => 'btn btn-primary', 'data-confirm' => 'Confirm Delete?']); 21 | 22 | $form->end(); 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kriss/yii2-log-reader", 3 | "description": "Yii2 log reader", 4 | "type": "yii2-extension", 5 | "keywords": ["yii2","extension","module","log","log reader"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "kriss", 10 | "email": "462679766@qq.com" 11 | } 12 | ], 13 | "require": { 14 | "yiisoft/yii2": "~2.0.13" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "kriss\\logReader\\": "src" 19 | } 20 | }, 21 | "repositories": [ 22 | { 23 | "type": "composer", 24 | "url": "https://asset-packagist.org" 25 | } 26 | ], 27 | "config": { 28 | "allow-plugins": { 29 | "yiisoft/yii2-composer": true 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/views/default/zip.php: -------------------------------------------------------------------------------- 1 | title = $model->log->name . ' zip'; 11 | $this->params['breadcrumbs'][] = ['label' => 'Logs', 'url' => ['index']]; 12 | $this->params['breadcrumbs'][] = ['label' => $model->log->name, 'url' => ['history', 'slug' => $model->log->slug]]; 13 | $this->params['breadcrumbs'][] = $this->title; 14 | 15 | $form = ActiveForm::begin(); 16 | 17 | echo $form->field($model, 'start')->input('date'); 18 | echo $form->field($model, 'end')->input('date'); 19 | echo $form->field($model, 'deleteAfterZip')->dropDownList([0 => 'keep', 1 => 'delete']); 20 | 21 | echo Html::submitInput('Submit', ['class' => 'btn btn-primary']); 22 | 23 | $form->end(); 24 | -------------------------------------------------------------------------------- /src/models/CleanForm.php: -------------------------------------------------------------------------------- 1 | 'Start Date', 30 | 'end' => 'End Date', 31 | ]; 32 | } 33 | 34 | public function init() 35 | { 36 | parent::init(); 37 | $this->start = date('Y-m-01', strtotime('-1 month')); 38 | $this->end = date('Y-m-01'); 39 | } 40 | 41 | public function attributeHints() 42 | { 43 | return [ 44 | 'start' => 'contain this', 45 | 'end' => 'not contain this', 46 | ]; 47 | } 48 | 49 | public function clean() 50 | { 51 | $log = $this->log; 52 | $startStamp = date('Ymd', strtotime($this->start)); 53 | $endStamp = date('Ymd', strtotime($this->end)); 54 | $logs = []; 55 | foreach (glob(Log::extractFileName($log->alias, '*')) as $fileName) { 56 | $logEnd = Log::extractFileStamp($log->alias, $fileName); 57 | // 被自动切割的log文件可能为:jd.log.20181109.1 58 | $arr = explode('.', $logEnd); 59 | if ($arr) { 60 | $logEnd = $arr[0]; 61 | } 62 | $stamp = date('Ymd', strtotime($logEnd)); 63 | if ($stamp >= $startStamp && $stamp < $endStamp) { 64 | $log = new Log($log->name, $log->alias, Log::extractFileStamp($log->alias, $fileName)); 65 | if (!$log->isZip) { 66 | $logs[] = $log; 67 | } 68 | } 69 | } 70 | 71 | foreach ($logs as $log) { 72 | unlink($log->fileName); 73 | } 74 | 75 | return true; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Yii2 Log Reader 2 | =============== 3 | Yii2 log reader 4 | 5 | > this project is extend from [zhuravljov/yii2-logreader](https://github.com/zhuravljov/yii2-logreader), and Add more operation like `delete` `download` `tail` and so on. 6 | 7 | > from 2.0. `history` can load file that Yii2 FileTarget rotated. See [History Usage](#history-usage) 8 | 9 | Preview 10 | ------------ 11 | 12 | Index 13 | 14 | ![preview1](https://github.com/krissss/yii2-log-reader/raw/master/screenshots/preview1.jpg) 15 | 16 | History 17 | 18 | ![preview2](https://github.com/krissss/yii2-log-reader/raw/master/screenshots/preview2.jpg) 19 | 20 | Installation 21 | ------------ 22 | 23 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 24 | 25 | Either run 26 | 27 | ``` 28 | php composer.phar require --prefer-dist kriss/yii2-log-reader "2.*" 29 | ``` 30 | 31 | or add 32 | 33 | ``` 34 | "kriss/yii2-log-reader": "2.*" 35 | ``` 36 | 37 | to the require section of your `composer.json` file. 38 | 39 | 40 | Usage 41 | ----- 42 | 43 | Once the extension is installed, simply modify your application configuration as follows: 44 | 45 | ```php 46 | return [ 47 | 'bootstrap' => ['log-reader'], 48 | 'modules' => [ 49 | 'log-reader' => [ 50 | 'class' => 'kriss\logReader\Module', 51 | //'as login_filter' => UserLoginFilter::class, // to use login filter 52 | 'aliases' => [ 53 | 'Frontend' => '@frontend/runtime/logs/app.log', 54 | 'Backend' => '@backend/runtime/logs/app.log', 55 | 'Console' => '@console/runtime/logs/app.log', 56 | ], 57 | //'defaultTailLine' => 200, 58 | ], 59 | ], 60 | ]; 61 | ``` 62 | 63 | You can then access Log Reader using the following URL: 64 | 65 | ```php 66 | http://localhost/path/to/index.php?r=log-reader 67 | ``` 68 | 69 | or if you have enabled pretty URLs, you may use the following URL: 70 | 71 | ```php 72 | http://localhost/path/to/log-reader 73 | ``` 74 | 75 | History Usage 76 | ----- 77 | 78 | For every day log view, you can config yii log like this: 79 | 80 | ```php 81 | [ 82 | 'class' => 'yii\log\FileTarget', 83 | 'categories' => ['test'], 84 | 'logVars' => [], 85 | 'logFile' => '@runtime/logs/test/test.log.' . date('Ymd'), // important 86 | 'maxLogFiles' => 31, 87 | 'dirMode' => 0777, 88 | 'fileMode' => 0777, 89 | ] 90 | ``` 91 | 92 | And config log-reader module `aliases` like: 93 | 94 | ```php 95 | 'test' => '@runtime/logs/test/test.log' 96 | ``` 97 | 98 | Then log with be save filename like `test.log.20190924`. This is log-reader `history` load filename. 99 | 100 | So you can view every day log in `history` action. 101 | -------------------------------------------------------------------------------- /src/models/ZipLogForm.php: -------------------------------------------------------------------------------- 1 | 'Start Date', 34 | 'end' => 'End Date', 35 | 'deleteAfterZip' => 'Is Delete After Zip', 36 | ]; 37 | } 38 | 39 | public function attributeHints() 40 | { 41 | return [ 42 | 'start' => 'contain this', 43 | 'end' => 'not contain this', 44 | ]; 45 | } 46 | 47 | public function init() 48 | { 49 | parent::init(); 50 | $this->start = date('Y-m-01', strtotime('-1 month')); 51 | $this->end = date('Y-m-01'); 52 | } 53 | 54 | public function zip() 55 | { 56 | $log = $this->log; 57 | $startStamp = date('Ymd', strtotime($this->start)); 58 | $endStamp = date('Ymd', strtotime($this->end)); 59 | $logs = []; 60 | foreach (glob(Log::extractFileName($log->alias, '*')) as $fileName) { 61 | $logEnd = Log::extractFileStamp($log->alias, $fileName); 62 | // 被自动切割的log文件可能为:jd.log.20181109.1 63 | $arr = explode('.', $logEnd); 64 | if ($arr) { 65 | $logEnd = $arr[0]; 66 | } 67 | $stamp = date('Ymd', strtotime($logEnd)); 68 | if ($stamp >= $startStamp && $stamp < $endStamp) { 69 | $log = new Log($log->name, $log->alias, Log::extractFileStamp($log->alias, $fileName)); 70 | if (!$log->isZip) { 71 | $logs[] = $log; 72 | } 73 | } 74 | } 75 | $current = date('YmdHis'); 76 | $fileName = Log::extractFileName($log->alias, "{$startStamp}-{$endStamp}-{$current}.zip"); 77 | $zip = new ZipArchive(); 78 | if ($zip->open($fileName, ZipArchive::CREATE) !== true) { 79 | $this->addError('log', 'cannot open zipFile, do you have permission?'); 80 | return false; 81 | } 82 | foreach ($logs as $log) { 83 | $zip->addFile($log->fileName, basename($log->fileName)); 84 | } 85 | $zip->close(); 86 | 87 | // 删除已打包的文件 88 | if ($this->deleteAfterZip) { 89 | foreach ($logs as $log) { 90 | unlink($log->fileName); 91 | } 92 | } 93 | 94 | return true; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Module.php: -------------------------------------------------------------------------------- 1 | 'label-default', 27 | 'info' => 'label-info', 28 | 'warning' => 'label-warning', 29 | 'error' => 'label-danger', 30 | ]; 31 | /** 32 | * @var string 33 | */ 34 | public $defaultLevelClass = 'label-default'; 35 | /** 36 | * @var int 37 | */ 38 | public $defaultTailLine = 100; 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | public function bootstrap($app) 44 | { 45 | if ($app instanceof Application) { 46 | $app->getUrlManager()->addRules([[ 47 | 'class' => GroupUrlRule::class, 48 | 'prefix' => $this->id, 49 | 'rules' => [ 50 | '' => 'default/index', 51 | '/' => 'default/', 52 | '' => 'default/', 53 | ], 54 | ]], false); 55 | } else { 56 | throw new InvalidConfigException('Can use for web application only.'); 57 | } 58 | } 59 | 60 | /** 61 | * @return Log[] 62 | */ 63 | public function getLogs() 64 | { 65 | $logs = []; 66 | foreach ($this->aliases as $name => $alias) { 67 | $logs[] = new Log($name, $alias); 68 | } 69 | 70 | return $logs; 71 | } 72 | 73 | /** 74 | * @param string $slug 75 | * @param null|string $stamp 76 | * @return null|Log 77 | */ 78 | public function findLog($slug, $stamp) 79 | { 80 | foreach ($this->aliases as $name => $alias) { 81 | if ($slug === Log::extractSlug($name)) { 82 | return new Log($name, $alias, $stamp); 83 | } 84 | } 85 | 86 | return null; 87 | } 88 | 89 | /** 90 | * @param Log $log 91 | * @return Log[] 92 | */ 93 | public function getHistory(Log $log) 94 | { 95 | $logs = []; 96 | foreach (glob(Log::extractFileName($log->alias, '*')) as $fileName) { 97 | $logs[] = new Log($log->name, $log->alias, Log::extractFileStamp($log->alias, $fileName)); 98 | } 99 | 100 | return $logs; 101 | } 102 | 103 | /** 104 | * @return integer 105 | */ 106 | public function getTotalCount() 107 | { 108 | $total = 0; 109 | foreach ($this->getLogs() as $log) { 110 | foreach ($log->getCounts() as $count) { 111 | $total += $count; 112 | } 113 | } 114 | 115 | return $total; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/views/default/history.php: -------------------------------------------------------------------------------- 1 | title = $name; 16 | $this->params['breadcrumbs'][] = ['label' => 'Logs', 'url' => ['index']]; 17 | $this->params['breadcrumbs'][] = $name; 18 | 19 | $formatter = new Formatter(); 20 | $fullSizeFormat = $formatter->format($fullSize, 'shortSize'); 21 | $captionBtn = []; 22 | if ($fullSize > 1) { 23 | $captionBtn[] = Html::a('zip', ['zip', 'slug' => Yii::$app->request->get('slug')], ['class' => 'btn btn-success btn-xs']); 24 | $captionBtn[] = Html::a('clean', ['clean', 'slug' => Yii::$app->request->get('slug')], ['class' => 'btn btn-danger btn-xs']); 25 | } 26 | $captionBtnStr = implode(' ', $captionBtn); 27 | ?> 28 |
29 | ['class' => 'table'], 31 | 'options' => ['class' => 'grid-view table-responsive'], 32 | 'dataProvider' => $dataProvider, 33 | 'caption' => "full size: {$fullSizeFormat}. {$captionBtnStr}", 34 | 'columns' => [ 35 | [ 36 | 'attribute' => 'fileName', 37 | 'format' => 'raw', 38 | 'value' => function (Log $log) { 39 | return pathinfo($log->fileName, PATHINFO_BASENAME); 40 | }, 41 | ], [ 42 | 'attribute' => 'counts', 43 | 'format' => 'raw', 44 | 'value' => function (Log $log) { 45 | return $this->render('_counts', ['log' => $log]); 46 | }, 47 | ], [ 48 | 'attribute' => 'size', 49 | 'format' => 'shortSize', 50 | 'headerOptions' => ['class' => 'sort-ordinal'], 51 | ], [ 52 | 'attribute' => 'updatedAt', 53 | 'format' => 'relativeTime', 54 | 'headerOptions' => ['class' => 'sort-numerical'], 55 | ], [ 56 | 'class' => '\yii\grid\ActionColumn', 57 | 'template' => '{view} {tail} {delete} {download}', 58 | 'urlCreator' => function ($action, Log $log) { 59 | return [$action, 'slug' => $log->slug, 'stamp' => $log->stamp]; 60 | }, 61 | 'buttons' => [ 62 | 'view' => function ($url, Log $log) { 63 | if ($log->isZip) { 64 | return ''; 65 | } 66 | return Html::a('View', $url, [ 67 | 'class' => 'btn btn-xs btn-primary', 68 | 'target' => '_blank', 69 | ]); 70 | }, 71 | 'tail' => function ($url, Log $log) use ($defaultTailLine) { 72 | if ($log->isZip) { 73 | return ''; 74 | } 75 | return Html::a('Tail', $url + ['line' => $defaultTailLine], [ 76 | 'class' => 'btn btn-xs btn-primary', 77 | 'target' => '_blank', 78 | ]); 79 | }, 80 | 'delete' => function ($url) { 81 | return Html::a('Delete', $url, [ 82 | 'class' => 'btn btn-xs btn-danger', 83 | 'data' => ['method' => 'post', 'confirm' => 'Are you sure?'], 84 | ]); 85 | }, 86 | 'download' => function ($url, Log $log) { 87 | return !$log->isExist ? '' : Html::a('Download', $url, [ 88 | 'class' => 'btn btn-xs btn-default', 89 | ]); 90 | }, 91 | ], 92 | ], 93 | ], 94 | ]) ?> 95 |
96 | registerCss(<<title = 'Logs'; 13 | $this->params['breadcrumbs'][] = 'Logs'; 14 | ?> 15 |
16 | '{items}', 18 | 'tableOptions' => ['class' => 'table'], 19 | 'options' => ['class' => 'grid-view table-responsive'], 20 | 'dataProvider' => $dataProvider, 21 | 'columns' => [ 22 | [ 23 | 'attribute' => 'name', 24 | 'format' => 'raw', 25 | 'value' => function (Log $log) { 26 | return Html::tag('h5', join("\n", [ 27 | Html::encode($log->name), 28 | '
', 29 | Html::tag('small', Html::encode($log->fileName)), 30 | ])); 31 | }, 32 | ], [ 33 | 'attribute' => 'counts', 34 | 'format' => 'raw', 35 | 'headerOptions' => ['class' => 'sort-ordinal'], 36 | 'value' => function (Log $log) { 37 | return $this->render('_counts', ['log' => $log]); 38 | }, 39 | ], [ 40 | 'attribute' => 'size', 41 | 'format' => 'shortSize', 42 | 'headerOptions' => ['class' => 'sort-ordinal'], 43 | ], [ 44 | 'attribute' => 'updatedAt', 45 | 'format' => 'relativeTime', 46 | 'headerOptions' => ['class' => 'sort-numerical'], 47 | ], [ 48 | 'class' => '\yii\grid\ActionColumn', 49 | 'template' => '{history} {view} {tail} {archive} {delete} {download}', 50 | 'urlCreator' => function ($action, Log $log) { 51 | return [$action, 'slug' => $log->slug]; 52 | }, 53 | 'buttons' => [ 54 | 'history' => function ($url) { 55 | return Html::a('History', $url, [ 56 | 'class' => 'btn btn-xs btn-default', 57 | ]); 58 | }, 59 | 'view' => function ($url, Log $log) { 60 | return !$log->isExist ? '' : Html::a('View', $url, [ 61 | 'class' => 'btn btn-xs btn-primary', 62 | 'target' => '_blank', 63 | ]); 64 | }, 65 | 'tail' => function ($url, Log $log) use ($defaultTailLine) { 66 | return !$log->isExist ? '' : Html::a('Tail', $url + ['line' => $defaultTailLine], [ 67 | 'class' => 'btn btn-xs btn-primary', 68 | 'target' => '_blank', 69 | ]); 70 | }, 71 | 'archive' => function ($url, Log $log) { 72 | return !$log->isExist ? '' : Html::a('Archive', $url, [ 73 | 'class' => 'btn btn-xs btn-success', 74 | 'data' => ['method' => 'post', 'confirm' => 'Are you sure?'], 75 | ]); 76 | }, 77 | 'delete' => function ($url, Log $log) { 78 | return !$log->isExist ? '' : Html::a('Delete', array_merge($url, ['since' => $log->updatedAt]), [ 79 | 'class' => 'btn btn-xs btn-danger', 80 | 'data' => ['method' => 'post', 'data-a' => 'aa', 'confirm' => 'Are you sure?'], 81 | ]); 82 | }, 83 | 'download' => function ($url, Log $log) { 84 | return !$log->isExist ? '' : Html::a('Download', $url, [ 85 | 'class' => 'btn btn-xs btn-default', 86 | ]); 87 | }, 88 | ], 89 | ], 90 | ], 91 | ]) ?> 92 |
93 | registerCss(<<_name = $name; 43 | $this->_alias = $alias; 44 | $this->_stamp = $stamp; 45 | $this->_fileName = static::extractFileName($alias, $stamp); 46 | parent::__construct($config); 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getName() 53 | { 54 | return $this->_name; 55 | } 56 | 57 | /** 58 | * @return string 59 | */ 60 | public function getAlias() 61 | { 62 | return $this->_alias; 63 | } 64 | 65 | /** 66 | * @return string 67 | */ 68 | public function getStamp() 69 | { 70 | return $this->_stamp; 71 | } 72 | 73 | /** 74 | * @return string 75 | */ 76 | public function getSlug() 77 | { 78 | return static::extractSlug($this->_name); 79 | } 80 | 81 | /** 82 | * @return string 83 | */ 84 | public function getFileName() 85 | { 86 | return $this->_fileName; 87 | } 88 | 89 | /** 90 | * @return boolean 91 | */ 92 | public function getIsExist() 93 | { 94 | return file_exists($this->getFileName()); 95 | } 96 | 97 | /** 98 | * @return bool 99 | */ 100 | public function getIsZip() 101 | { 102 | return $this->getIsExist() ? StringHelper::endsWith($this->getFileName(), '.zip') : false; 103 | } 104 | 105 | /** 106 | * @return integer|null 107 | */ 108 | public function getSize() 109 | { 110 | return $this->getIsExist() ? filesize($this->getFileName()) : null; 111 | } 112 | 113 | /** 114 | * @return integer|null 115 | */ 116 | public function getUpdatedAt() 117 | { 118 | return $this->getIsExist() ? filemtime($this->getFileName()) : null; 119 | } 120 | 121 | /** 122 | * @return string 123 | */ 124 | public function getDownloadName() 125 | { 126 | return $this->getSlug() . '.log'; 127 | } 128 | 129 | /** 130 | * @param bool $force 131 | * @return array 132 | */ 133 | public function getCounts($force = false) 134 | { 135 | if (!$this->getIsExist()) return []; 136 | 137 | $key = $this->getFileName() . '#counts'; 138 | if (!$force && ($counts = Yii::$app->cache->get($key)) !== false) { 139 | return $counts; 140 | } 141 | 142 | $counts = []; 143 | if ($h = fopen($this->getFileName(), 'r')) { 144 | while (($line = fgets($h)) !== false) { 145 | if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/', $line)) { 146 | if (preg_match('/^[\d\-\: ]+\[.*\]\[.*\]\[.*\]\[(.*)\]/U', $line, $m)) { 147 | $level = $m[1]; 148 | if (!isset($counts[$level])) $counts[$level] = 0; 149 | $counts[$level]++; 150 | } 151 | } 152 | } 153 | fclose($h); 154 | Yii::$app->cache->set($key, $counts, null, new FileDependency([ 155 | 'fileName' => $this->getFileName(), 156 | ])); 157 | } 158 | 159 | return $counts; 160 | } 161 | 162 | /** 163 | * @param string $stamp 164 | * @return boolean 165 | */ 166 | public function archive($stamp) 167 | { 168 | if ($this->getStamp() === null && $this->getIsExist()) { 169 | rename($this->getFileName(), static::extractFileName($this->getAlias(), $stamp)); 170 | return true; 171 | } else { 172 | return false; 173 | } 174 | } 175 | 176 | /** 177 | * @param string $name 178 | * @return string 179 | */ 180 | public static function extractSlug($name) 181 | { 182 | return Inflector::slug($name); 183 | } 184 | 185 | /** 186 | * @param string $alias 187 | * @param null|string $stamp 188 | * @return string 189 | */ 190 | public static function extractFileName($alias, $stamp = null) 191 | { 192 | $fileName = FileHelper::normalizePath(Yii::getAlias($alias, false)); 193 | if ($stamp === null) return $fileName; 194 | 195 | return $fileName . '.' . $stamp; 196 | } 197 | 198 | /** 199 | * @param string $alias 200 | * @param string $fileName 201 | * @return string|null 202 | */ 203 | public static function extractFileStamp($alias, $fileName) 204 | { 205 | $originName = FileHelper::normalizePath(Yii::getAlias($alias, false)); 206 | if (strpos($fileName, $originName) === 0) { 207 | return substr($fileName, strlen($originName) + 1); 208 | } else { 209 | return null; 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/controllers/DefaultController.php: -------------------------------------------------------------------------------- 1 | rememberUrl(); 27 | 28 | $dataProvider = new ArrayDataProvider([ 29 | 'allModels' => $this->module->getLogs(), 30 | 'sort' => [ 31 | 'attributes' => [ 32 | 'name', 33 | 'size' => ['default' => SORT_DESC], 34 | 'updatedAt' => ['default' => SORT_DESC], 35 | ], 36 | ], 37 | 'pagination' => ['pageSize' => 0], 38 | ]); 39 | return $this->render('index', [ 40 | 'dataProvider' => $dataProvider, 41 | 'defaultTailLine' => $this->module->defaultTailLine, 42 | ]); 43 | } 44 | 45 | public function actionView($slug, $stamp = null) 46 | { 47 | $log = $this->find($slug, $stamp); 48 | if ($log->isExist) { 49 | return Yii::$app->response->sendFile($log->fileName, basename($log->fileName), [ 50 | 'mimeType' => 'text/plain', 51 | 'inline' => true 52 | ]); 53 | } else { 54 | throw new NotFoundHttpException('Log not found.'); 55 | } 56 | } 57 | 58 | public function actionArchive($slug) 59 | { 60 | if ($this->find($slug, null)->archive(date('YmdHis'))) { 61 | Yii::$app->session->setFlash('success', 'archive success'); 62 | return $this->redirect(['history', 'slug' => $slug]); 63 | } else { 64 | throw new NotFoundHttpException('Log not found.'); 65 | } 66 | } 67 | 68 | public function actionHistory($slug) 69 | { 70 | $this->rememberUrl(); 71 | 72 | $log = $this->find($slug, null); 73 | $allLogs = $this->module->getHistory($log); 74 | 75 | $fullSize = array_sum(ArrayHelper::getColumn($allLogs, 'size')); 76 | 77 | $dataProvider = new ArrayDataProvider([ 78 | 'allModels' => $allLogs, 79 | 'sort' => [ 80 | 'attributes' => [ 81 | 'fileName', 82 | 'size' => ['default' => SORT_DESC], 83 | 'updatedAt' => ['default' => SORT_DESC], 84 | ], 85 | 'defaultOrder' => ['updatedAt' => SORT_DESC], 86 | ], 87 | ]); 88 | 89 | return $this->render('history', [ 90 | 'name' => $log->name, 91 | 'dataProvider' => $dataProvider, 92 | 'fullSize' => $fullSize, 93 | 'defaultTailLine' => $this->module->defaultTailLine, 94 | ]); 95 | } 96 | 97 | public function actionZip($slug) 98 | { 99 | $log = $this->find($slug, null); 100 | $model = new ZipLogForm(['log' => $log]); 101 | if ($model->load(Yii::$app->request->post()) && $model->validate()) { 102 | $result = $model->zip(); 103 | if ($result !== false) { 104 | Yii::$app->session->setFlash('success', 'zip success'); 105 | return $this->redirectPrevious(); 106 | } else { 107 | Yii::$app->session->setFlash('error', 'zip error: ', implode('
', $model->getFirstErrors())); 108 | } 109 | } 110 | return $this->render('zip', [ 111 | 'model' => $model, 112 | ]); 113 | } 114 | 115 | public function actionClean($slug) 116 | { 117 | $log = $this->find($slug, null); 118 | $model = new CleanForm(['log' => $log]); 119 | if ($model->load(Yii::$app->request->post()) && $model->validate()) { 120 | $result = $model->clean(); 121 | if ($result !== false) { 122 | Yii::$app->session->setFlash('success', 'clean success'); 123 | return $this->redirectPrevious(); 124 | } else { 125 | Yii::$app->session->setFlash('error', 'clean error: ', implode('
', $model->getFirstErrors())); 126 | } 127 | } 128 | return $this->render('clean', [ 129 | 'model' => $model, 130 | ]); 131 | } 132 | 133 | public function actionDelete($slug, $stamp = null, $since = null) 134 | { 135 | $log = $this->find($slug, $stamp); 136 | if ($since) { 137 | if ($log->updatedAt != $since) { 138 | Yii::$app->session->setFlash('error', 'delete error: file has updated'); 139 | return $this->redirectPrevious(); 140 | } 141 | } 142 | if (unlink($log->fileName)) { 143 | Yii::$app->session->setFlash('success', 'delete success'); 144 | } else { 145 | Yii::$app->session->setFlash('error', 'delete error'); 146 | } 147 | return $this->redirectPrevious(); 148 | } 149 | 150 | public function actionDownload($slug, $stamp = null) 151 | { 152 | $log = $this->find($slug, $stamp); 153 | if ($log->isExist) { 154 | Yii::$app->response->sendFile($log->fileName)->send(); 155 | } else { 156 | throw new NotFoundHttpException('Log not found.'); 157 | } 158 | } 159 | 160 | public function actionTail($slug, $line = 100, $stamp = null) 161 | { 162 | $log = $this->find($slug, $stamp); 163 | $line = intval($line); 164 | if ($log->isExist) { 165 | $result = shell_exec("tail -n {$line} {$log->fileName}"); 166 | 167 | Yii::$app->response->format = Response::FORMAT_RAW; 168 | Yii::$app->response->headers->set('Content-Type', 'text/event-stream'); 169 | return $result; 170 | } else { 171 | throw new NotFoundHttpException('Log not found.'); 172 | } 173 | } 174 | 175 | /** 176 | * @param string $slug 177 | * @param null|string $stamp 178 | * @return Log 179 | * @throws NotFoundHttpException 180 | */ 181 | protected function find($slug, $stamp) 182 | { 183 | if ($log = $this->module->findLog($slug, $stamp)) { 184 | return $log; 185 | } else { 186 | throw new NotFoundHttpException('Log not found.'); 187 | } 188 | } 189 | 190 | protected function rememberUrl($url = '') 191 | { 192 | Url::remember($url, '__logReaderReturnUrl'); 193 | } 194 | 195 | protected function redirectPrevious() 196 | { 197 | return $this->redirect(Url::previous('__logReaderReturnUrl')); 198 | } 199 | } 200 | --------------------------------------------------------------------------------