当前位置: 首页 > news >正文

免费做网站哪家好常州百度推广代理

免费做网站哪家好,常州百度推广代理,什么系统做网站最安全,武汉前端网站开发公司问题背景 最近接手了一个骨灰级的项目,然而在项目中遇到了一个普遍的挑战:由于公司采用 ELK(Elasticsearch、Logstash、Kibana)作为日志收集和分析工具,追踪生产问题成为了一大难题。尽管 ELK 提供了强大的日志分析功…

问题背景

最近接手了一个骨灰级的项目,然而在项目中遇到了一个普遍的挑战:由于公司采用 ELK(Elasticsearch、Logstash、Kibana)作为日志收集和分析工具,追踪生产问题成为了一大难题。尽管 ELK 提供了强大的日志分析功能,但由于项目历史悠久,日志输出不规范,缺乏唯一标识,导致在海量日志中准确定位问题变得异常困难。为了提升生产环境下的问题排查和故障诊断效率,迫切需要在项目中引入一种机制,能够为每个请求生成唯一的标识符(traceId),并将其与 ELK 集成,以便在日志中准确追踪请求的全链路过程。

系统默认日志格式
在这里插入图片描述

elk 对这种格式采集并不太友好,所以打算重新写一个日志log类。每一条日志都非常清晰,区分 error、info、sql 级别 log,排查起来也非常直观。

先看效果:
在这里插入图片描述

查看application/config.php配置文件,第一反应就是这个File到底在哪?OK,我们直接全局搜索 File.php,最终锁定文件路径:source/thinkphp/library/think/log/driver/File.php

在这里插入图片描述
基于自身业务改造,时间比较短哈,改写了一个初版(简单粗暴就是日志单行展示),可以短时间适配业务,改造后的代码如下:

<?phpnamespace app\common\library;use think\App;
use think\Request;class YopLog
{protected $config = ['time_format' => ' c ','single'      => false,'file_size'   => 2097152,'path'        => LOG_PATH,'apart_level' => [],'max_files'   => 0,'json'        => true,];// 实例化并传入参数public function __construct($config = []){if (is_array($config)) {$this->config = array_merge($this->config, $config);}}/*** 日志写入接口* @access public* @param array $log 日志信息* @param bool $append 是否追加请求信息* @return bool*/public function save(array $log = [], $append = false){$destination = $this->getMasterLogFile();$path = dirname($destination);!is_dir($path) && mkdir($path, 0755, true);$info = [];foreach ($log as $type => $val) {foreach ($val as $msg) {if (!is_string($msg)) {if (!$this->config['json']) {$msg = var_export($msg, true);}}$info[$type][] = $this->config['json'] ? $msg : '[ ' . $type . ' ] ' . $msg;}if (!$this->config['json'] && (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level']))) {// 独立记录的日志级别$filename = $this->getApartLevelFile($path, $type);$this->write($info[$type], $filename, true, $append);unset($info[$type]);}}if ($info) {return $this->write($info, $destination, false, $append);}return true;}/*** 获取主日志文件名* @access public* @return string*/protected function getMasterLogFile(){if ($this->config['single']) {$name = is_string($this->config['single']) ? $this->config['single'] : 'single';$destination = $this->config['path'] . $name . '.log';} else {$cli = PHP_SAPI == 'cli' ? '_cli' : '';if ($this->config['max_files']) {$filename = date('Ymd') . $cli . '.log';$files    = glob($this->config['path'] . '*.log');try {if (count($files) > $this->config['max_files']) {unlink($files[0]);}} catch (\Exception $e) {}} else {$filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log';}$destination = $this->config['path'] . $filename;}return $destination;}/*** 获取独立日志文件名* @access public* @param string $path 日志目录* @param string $type 日志类型* @return string*/protected function getApartLevelFile($path, $type){$cli = PHP_SAPI == 'cli' ? '_cli' : '';if ($this->config['single']) {$name = is_string($this->config['single']) ? $this->config['single'] : 'single';$name .= '_' . $type;} elseif ($this->config['max_files']) {$name = date('Ymd') . '_' . $type . $cli;} else {$name = date('d') . '_' . $type . $cli;}return $path . DIRECTORY_SEPARATOR . $name . '.log';}/*** 日志写入* @access protected* @param array $message 日志信息* @param string $destination 日志文件* @param bool $apart 是否独立文件写入* @param bool $append 是否追加请求信息* @return bool*/protected function write($message, $destination, $apart = false, $append = false){// 检测日志文件大小,超过配置大小则备份日志文件重新生成$this->checkLogSize($destination);// 日志信息封装$info['timestamp'] = date($this->config['time_format']);if ($this->config['json']) {foreach ($message as $type => $msg) {if (is_array($msg)) {for ($i = 0; $i < count($msg); $i++) {$info[$type][$i] = $msg[$i] ?? "";}} else {$info[$type] = $msg;}}} else {foreach ($message as $type => $msg) {$info[$type] = is_array($msg) ? implode("\r\n", $msg) : $msg;}}if (PHP_SAPI == 'cli') {$message = $this->parseCliLog($info);} else {// 添加调试日志$this->getDebugLog($info, $append, $apart);$message = $this->parseLog($info);}return error_log(sprintf("%s %s", date("Y-m-d H:i:s"), $message), 3, $destination);}/*** 检查日志文件大小并自动生成备份文件* @access protected* @param string $destination 日志文件* @return void*/protected function checkLogSize($destination){if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) {try {rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination));} catch (\Exception $e) {}}}/*** CLI日志解析* @access protected* @param array $info 日志信息* @return string*/protected function parseCliLog($info){if ($this->config['json']) {$message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";} else {$now = $info['timestamp'];unset($info['timestamp']);$message = implode("\r\n", $info);$message = "[{$now}]" . $message . "\r\n";}return $message;}/*** 解析日志* @access protected* @param array $info 日志信息* @return string*/protected function parseLog($info){$request     = Request::instance();$requestInfo = ['traceId' => $_SERVER['traceId'] ?? "",'ip'      => $request->ip(),'method'  => $request->method(),'host'    => $request->host(),'uri'     => $request->url(),];if ($this->config['json']) {$info = $requestInfo + $info;return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";}array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");unset($info['timestamp']);return implode("\r\n", $info) . "\r\n";}protected function getDebugLog(&$info, $append, $apart){if (App::$debug && $append) {if ($this->config['json']) {// 获取基本信息$runtime = round(microtime(true) - THINK_START_TIME, 10);$reqs    = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);$info = ['runtime' => number_format($runtime, 6) . 's','reqs'    => $reqs . 'req/s','memory'  => $memory_use . 'kb','file'    => count(get_included_files()),] + $info;} elseif (!$apart) {// 增加额外的调试信息$runtime = round(microtime(true) - THINK_START_TIME, 10);$reqs    = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);$time_str   = '[运行时间:' . number_format($runtime, 6) . 's] [吞吐率:' . $reqs . 'req/s]';$memory_str = ' [内存消耗:' . $memory_use . 'kb]';$file_load  = ' [文件加载:' . count(get_included_files()) . ']';array_unshift($info, $time_str . $memory_str . $file_load);}}}
}

探索日志追踪解决方案

1. 生成 traceId: 需要一个能够生成唯一 traceId 的方法,确保每个请求都有一个唯一的标识符。
2. 存储 traceId: 将生成的 traceId 存储在 $_SERVER 中,以便在整个请求处理过程中都能够方便地访问到它。
3. 添加到响应头中: 在每次请求的响应中都添加 traceId 到响应头中,以便客户端收到响应后可以通过 traceId 与请求对应起来。
4. 处理异步请求: 对于异步请求,需要在发送请求时将 traceId 包含在请求头中,以便日志也能够与对应的原始请求进行关联。

解决方案

1. 生成 traceId: 在 Tags.php 中的 app_begin 钩子中,执行以下操作:

<?php
return [// 应用开始'app_begin' => ['app\\api\\behavior\\TraceId'],
];

2. 存储 traceId: 将生成的 traceId 存储在 $_SERVER 中(或者存储在header中)。

为了简化获取 traceId 的代码,我选择将其存储在 $_SERVER 中。这样,只需要通过 $_SERVER[‘traceId’] 就能够轻松获取到 traceId,而不需要编写繁琐的获取代码。相比之下,如果将 traceId 存储在请求体的 header 中,获取代码则需要写成 (Request::instance()->header())[‘traceId’] ?? “”。此外,如果系统中存在原生调用,需要获取所有的 header 头,就需要使用到 getallheaders() 函数。然而,getallheaders() 函数只能获取到最初请求打到服务上的所有 header 内容,而手动设置的 header 是无法被 getallheaders() 函数获取到的。因此,将 traceId 存储在 $_SERVER 中可以更加方便地获取,并且不受限于原生调用的影响

3. 添加到响应头中: 在响应头中添加 traceId。

<?phpnamespace app\api\behavior;/*** TraceId 行为类** 此行为类用于在 API 请求的上下文中自动注入一个唯一的 traceId 到 HTTP 响应头。* traceId 主要用于链路追踪,有助于在日志中跟踪请求的全链路过程,* 提升系统问题排查和诊断的效率。*/
class TraceId
{/*** 执行行为** @return void*/public function run(){// 使用generateTraceId()函数生成一个唯一的traceId值$traceId = generateTraceId();// 将生成的唯一traceId值存储在$_SERVER全局变量中$_SERVER['traceId'] = $traceId;// 设置响应头header("X-Trace-Id: {$traceId}");}
}

4. 处理异步请求: 在异步请求中,确保在发送请求时将 traceId 包含在请求头中。
发送请求

public function exec_bce($method, $post)
{$config = new \stdClass();$config->secret = 'dz_mufeng';$sign = $this->make_sign($post, $config);$traceId = $_SERVER['traceId'] ?? "";// 获取数据$content = http_build_query($post, '', '&');$header = ["Content-type:application/x-www-form-urlencoded","Content-length:" . strlen($content),"traceId: " . $traceId];$context['http'] = ['timeout' => 60,'method' => 'POST','header' => implode("\r\n", $header),'content' => $content,];$url = config('bce_url').'/code.php?method=' . $method . '&sign=' . $sign;log_write('code_exec_context:' . json_encode($context), 'info');$contextStream = stream_context_create($context);$res = file_get_contents($url, false, $contextStream);log_write("执行返回结果:" . $res, 'info');$res = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $res);$res = json_decode($res, true);if ($res['result'] == 0) {return $this->renderError($res['data']);} else {return $this->renderSuccess($res['data']);}}

本系统原生代码在接收请求时,可直接使用 $_SERVER[‘HTTP_TRACEID’] 获取 traceId。

<?php
public function log($params, $type = 'info')
{if (!is_string($params)) {$params = json_encode($params, 320);}$requestId = $_SERVER['traceId'] ?? '';$traceId = $_SERVER['HTTP_TRACEID'] ?? "";!is_dir($this->logPath) && mkdir($this->logPath, 0755, true);$requestInfo = ['[trace_id]' => empty($traceId) ? $requestId : $traceId,'[request_ip]' => $this->getIp(),'[method]'     => $_SERVER['REQUEST_METHOD'],'[domain]'     => sprintf('%s://%s', $_SERVER['REQUEST_SCHEME'], $_SERVER['HTTP_HOST']),'[uri]'        => sprintf('%s://%s%s', $_SERVER['REQUEST_SCHEME'], $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI']),'[param]'      => $params . "\r\n",'[trace]'      => (new \Exception)->getTraceAsString()];$println = "---------------------------------------------------------------\r\n";$msg     = sprintf("%s%s [%s] ", $println, date("Y-m-d H:i:s"), $type);foreach ($requestInfo as $key => $value) {$msg .= sprintf("%s: %s ", $key, $value);}file_put_contents(sprintf("%s/%s_%s",$this->logPath, date("d"), "api.log"), $msg . "\r\n", FILE_APPEND);
}

修改完之后日志中心检索,发现日志好多报错,发现不规范获取数据索引导致。

在这里插入图片描述
这类错误暂时不影响主流程,可以屏蔽掉,那么 [8] 代表什么?他代表错误级别,可以针对性屏蔽掉。

详见: Core_d.php

在这里插入图片描述
可以在框架对应模块的基类中过滤掉

error_reporting(E_ERROR | E_PARSE);

这段代码是用来设置 PHP 错误报告级别的。在 PHP 中,你可以通过修改 error_reporting 指令来控制哪些类型的错误将被报告。在这个例子中,error_reporting 被设置为报告四种类型的错误:

E_ERROR: 严重错误,可能会导致脚本终止执行。
E_WARNING: 警告,不会导致脚本终止执行,但可能会表明存在问题。
E_PARSE: 语法解析错误,通常是由于语法错误而导致的解析失败。
E_NOTICE: 提示,指出可能会导致问题的非致命性错误。
通过将这些错误类型组合起来,这段代码将导致 PHP 报告这四种类型的错误,而忽略其它类型的错误。这通常有助于在开发和调试阶段发现潜在的问题,但在生产环境中,通常会调整为仅报告严重错误(如 E_ERROR)。

结论

以上解决方案有效地为 ThinkPHP5 的日志添加了 traceId,实现了请求的全链路追踪(包括异步请求,确保请求连贯性),从而提高了系统问题排查和诊断的效率。

http://www.ritt.cn/news/15181.html

相关文章:

  • 彩票网站什么做5188关键词挖掘工具
  • 滕州哪里有做网站的成品网站1688入口网页版怎样
  • 潍坊网站建设官网如何做网络推广
  • 哪里的郑州网站建设互联网营销师培训教材
  • 网站权重优化方式建站平台
  • 个人网页制作教程与步骤seo云优化
  • 房地产开发商是干什么的宁波抖音seo搜索优化软件
  • 集团官方网站建设西安 做网站
  • 做好网站建设对企业有什么作用中国体育新闻
  • 中国人做跨电商有什么网站服装店营销策划方案
  • 苹果软件 做ppt模板下载网站有哪些内容挖掘关键词工具
  • 有没有学做ppt发网站或论坛福建企业seo推广
  • 北京海淀建筑行业培训中心大连百度网站排名优化
  • C 做的窗体怎么变成网站什么平台发广告最有效
  • 单页网站制作 在线 支付旅游app推广营销策略
  • 网站建设 微信微博外包怎么推广公司网站
  • 做网站页面遇到的问题黑帽seo技术有哪些
  • 有效的网站建设宣传网页制作
  • 旅游局网站建设方案网站seo公司哪家好
  • 基于用户体验的网站设计aso优化app推广
  • 阳江商城网站开发设计软文世界
  • pc做网站宁波seo哪家好快速推广
  • 网站服务空间产品网络营销
  • 服务外包平台百度seo推广软件
  • 网站服务器错误403买卖友情链接
  • 专门做饥饿营销的网站杭州百度快照
  • 陕西住房建设部网站接app推广
  • 如何做商城网站小程序百度快照推广一年要多少钱
  • 用啥网站做首页竞价什么意思
  • 做网站很赚钱吗深圳防疫措施优化