// +---------------------------------------------------------------------- namespace app\driver\log; use think\App; use think\Request; /** * 本地化调试输出到文件 */ class File { protected static $prefix = null; protected $config = [ 'time_format' => ' c ', 'single' => false, 'file_size' => 2097152, 'path' => LOG_PATH, 'apart_level' => [], ]; protected $writed = []; // 实例化并传入参数 public function __construct($config = []) { if (is_null(self::$prefix)) { self::$prefix = substr(md5(getmypid() . uniqid()), 0, 13) . ' '; } if (is_array($config)) { $this->config = array_merge($this->config, $config); } } /** * 日志写入接口 * @access public * @param array $log 日志信息 * @return bool */ public function save(array $log = []) { if ($this->config['single']) { $destination = $this->config['path'] . 'single.log'; } else { $cli = IS_CLI ? '_cli' : ''; $destination = $this->config['path'] . date('Ym') . DS . date('d_H') . $cli . '.log'; } $path = dirname($destination); if (!is_dir($path)) { @mkdir($path, 0755, true); clearstatcache(); } $error_count = 0; $write_data = [ $destination => '', ]; foreach ($log as $message) { // foreach ($log as $type => $val) { $type = $message['type']; $msg = $message['msg']; if (!is_string($msg)) { $msg = json_encode($msg, JSON_UNESCAPED_UNICODE); } $level = self::$prefix . '[ ' . $type . ' ] ' . $msg . "\r\n"; $filename = ''; if (in_array($type, $this->config['apart_level'])) { // 独立记录的日志级别 if ($this->config['single']) { $filename = $path . DS . $type . '.log'; } else { $filename = $path . DS . date('d_H') . '_' . $type . $cli . '.log'; } } if ($filename) { if (!array_key_exists($filename, $write_data)) { $write_data[$filename] = $level; } else { $write_data[$filename] .= $level; } } $write_data[$destination] .= $level; if ($type == 'error') { $error_count++; } } foreach ($write_data as $file => $content) { if ($content) { if ($file == $destination) { $this->write($content, $file); } else { $this->write($content,$file, true); } } } if ($error_count) { file_put_contents(LOG_PATH . date('Ym') . DS . date('d') . '_error_count.log', str_repeat('1', $error_count), FILE_APPEND); } return true; } protected function write($message, $destination, $apart = false) { //检测日志文件大小,超过配置大小则备份日志文件重新生成 // if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { // rename($destination, dirname($destination) . DS . time() . '-' . basename($destination)); // $this->writed[$destination] = false; // } if (empty($this->writed[$destination]) && !IS_CLI) { if (isset($_SERVER['HTTP_HOST'])) { $current_uri = isset($_SERVER['REQUEST_SCHEME']) ? ($_SERVER['REQUEST_SCHEME'] . '://') : ''; $current_uri .= $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; } else { $current_uri = "cmd:" . implode(' ', $_SERVER['argv']); } if (App::$debug && !$apart) { // 获取基本信息 $runtime = round(microtime(true) - THINK_START_TIME, 10); $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; $time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]'; $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; $file_load = ' [文件加载:' . count(get_included_files()) . ']'; $message = self::$prefix . '[ info ] ' . $time_str . $memory_str . $file_load . "\r\n" . $message; } $now = date($this->config['time_format']); $ip = Request::instance()->ip(); $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI'; $message = "---------------------------------------------------------------\r\n" . self::$prefix . "[{$now}] {$ip} {$method} {$current_uri}\r\n" . $message; $this->writed[$destination] = true; } if (IS_CLI) { $now = date($this->config['time_format']); $message = "[{$now}]" . $message; } return error_log($message, 3, $destination); } }