LogViewerService.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <?php
  2. namespace App\Module\Dev\Services;
  3. use Illuminate\Support\Facades\File;
  4. use Illuminate\Support\Facades\Storage;
  5. /**
  6. * 日志查看服务类
  7. *
  8. * 提供日志查看相关的服务方法,供其他模块调用
  9. */
  10. class LogViewerService
  11. {
  12. /**
  13. * 获取日志文件列表
  14. *
  15. * @return array
  16. */
  17. public static function getLogFiles(): array
  18. {
  19. $path = storage_path('logs');
  20. $files = File::files($path);
  21. $logFiles = [];
  22. foreach ($files as $file) {
  23. if ($file->getExtension() === 'log') {
  24. $logFiles[] = [
  25. 'name' => $file->getFilename(),
  26. 'path' => $file->getPathname(),
  27. 'size' => self::formatFileSize($file->getSize()),
  28. 'modified' => date('Y-m-d H:i:s', $file->getMTime()),
  29. ];
  30. }
  31. }
  32. // 按修改时间排序
  33. usort($logFiles, function ($a, $b) {
  34. return strtotime($b['modified']) - strtotime($a['modified']);
  35. });
  36. return $logFiles;
  37. }
  38. /**
  39. * 获取日志文件内容
  40. *
  41. * @param string $filename 文件名
  42. * @param int $lines 行数
  43. * @return array
  44. */
  45. public static function getLogContent(string $filename, int $lines = 1000): array
  46. {
  47. $path = storage_path('logs/' . $filename);
  48. if (!File::exists($path)) {
  49. return [
  50. 'error' => '文件不存在',
  51. 'content' => [],
  52. ];
  53. }
  54. // 读取最后N行
  55. $content = self::tailFile($path, $lines);
  56. // 解析日志内容
  57. $parsedContent = self::parseLogContent($content);
  58. return [
  59. 'filename' => $filename,
  60. 'path' => $path,
  61. 'size' => self::formatFileSize(File::size($path)),
  62. 'modified' => date('Y-m-d H:i:s', File::lastModified($path)),
  63. 'content' => $parsedContent,
  64. ];
  65. }
  66. /**
  67. * 清空日志文件
  68. *
  69. * @param string $filename 文件名
  70. * @return bool
  71. */
  72. public static function clearLogFile(string $filename): bool
  73. {
  74. $path = storage_path('logs/' . $filename);
  75. if (!File::exists($path)) {
  76. return false;
  77. }
  78. // 清空文件内容
  79. file_put_contents($path, '');
  80. return true;
  81. }
  82. /**
  83. * 删除日志文件
  84. *
  85. * @param string $filename 文件名
  86. * @return bool
  87. */
  88. public static function deleteLogFile(string $filename): bool
  89. {
  90. $path = storage_path('logs/' . $filename);
  91. if (!File::exists($path)) {
  92. return false;
  93. }
  94. // 删除文件
  95. return File::delete($path);
  96. }
  97. /**
  98. * 格式化文件大小
  99. *
  100. * @param int $size 文件大小(字节)
  101. * @return string
  102. */
  103. protected static function formatFileSize(int $size): string
  104. {
  105. $units = ['B', 'KB', 'MB', 'GB', 'TB'];
  106. $i = 0;
  107. while ($size >= 1024 && $i < count($units) - 1) {
  108. $size /= 1024;
  109. $i++;
  110. }
  111. return round($size, 2) . ' ' . $units[$i];
  112. }
  113. /**
  114. * 读取文件最后N行
  115. *
  116. * @param string $path 文件路径
  117. * @param int $lines 行数
  118. * @return string
  119. */
  120. protected static function tailFile(string $path, int $lines): string
  121. {
  122. $file = fopen($path, 'r');
  123. $total_lines = count(file($path));
  124. if ($total_lines <= $lines) {
  125. $result = file_get_contents($path);
  126. fclose($file);
  127. return $result;
  128. }
  129. $line_counter = 0;
  130. $position = -1;
  131. $buffer = '';
  132. while ($line_counter < $lines && fseek($file, $position, SEEK_END) !== -1) {
  133. $char = fgetc($file);
  134. if ($char === "\n") {
  135. $line_counter++;
  136. }
  137. $buffer = $char . $buffer;
  138. $position--;
  139. }
  140. fclose($file);
  141. return $buffer;
  142. }
  143. /**
  144. * 解析日志内容
  145. *
  146. * @param string $content 日志内容
  147. * @return array
  148. */
  149. protected static function parseLogContent(string $content): array
  150. {
  151. $pattern = '/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+)\.(\w+): (.*?)(?=\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]|$)/s';
  152. preg_match_all($pattern, $content, $matches, PREG_SET_ORDER);
  153. $parsedContent = [];
  154. foreach ($matches as $match) {
  155. $parsedContent[] = [
  156. 'datetime' => $match[1],
  157. 'environment' => $match[2],
  158. 'level' => $match[3],
  159. 'message' => trim($match[4]),
  160. ];
  161. }
  162. return $parsedContent;
  163. }
  164. }