Response content must be a string or object ?(上)lumen日志格式修改

如题所述

第1个回答  2022-06-06

  如果laravel和lumen框架使用的多,一定会碰到这个异常: The Response content must be a string or object implementing __toString(), "boolean" given ,也正是由这个异常,我开始决定写相关的笔记。

  公司项目使用的lumen框架,前负责人还是很良心的,对lumen框架也是十分精通。通过框架依赖注入和服务注册的能力,添加了一个异常处理的类,在任何异常发生时捕获并把错误信息记录到日志文件,向前端返回一个json数据。之所以这么做,我猜是因为lumen自身默认的异常处理类会返回一个状态吗为500且渲染过的页面,不适用于前后端分离的项目使用;同时状态码为500更容易看出是服务挂掉了,显得有点low。最近我在重构此项目时,有一个需求是要修改日志的格式。
  使用过的同学应该知道lumen的 日志格式 很友好,message+array的格式,并会通过formater注入日志时间。

  为什么修改呢,因为响应平台化运维要求,把日志文件接入到 ELK ,要求上报的数据每条都是json格式,实际上这个修改也比较简单,异常处理的日志格式修改稍微复杂一点,不过也能处理,下面记录一下实现过程。

日志格式修改
lumen5.5版本及之前, bootstrap/app.php 文件开启门面加载配置 $app->withFacades() ,使用的日志类facade(门面类) Illuminate\Support\Facades\Log 很容易找到。

可以看出日志写入时使用的对象是 Psr\Log\LoggerInterface ,但很明显它只是一个接口。深入了解可以看到 lumen 在 laravel\lumen\Application 核心类文件中使用IOC容器注册服务绑定了 log 与 Psr\Log\LoggerInterface 这两个服务别名。

服务容器 的具体实现过程我就不赘述了, 可以点击查看这里 。总之如果使用log门面,那么最终处理日志记录的是 registerLogBindings 函数中返回的 Logger 对象,那么改写日志格式有两种方式:

综上选择第二种实现方法
   步骤一 :在 /app/Providers 目录下创建一个服务

可以看到在register方法里,先定义日志格式 $format 字符串,模拟了一个json字符串。 LineFormatter 是 lumen 用于日志格式化的类, $format 就是其格式化所使用的的格式。
写入日志的调用如 Log::info($mesage, $content) 正常会传入两个参数,上面说了 Log 服务别名对应的日志服务是 Monolog/Logger ;它须有一个日志处理者 $monologHandler 。
$monologHandler 提供日志文件创建,日志写入等功能。方法中使用的 \Monolog\Handler\RotatingFileHandler 是 lumen 提供的按天生成日志文件写、入日志,并删除超过30天的日志处理服务。

   步骤二 :改写LineFormatter类
在正常的lumen日志中,其每行展示均是字符串,如果修改了 $format 就要兼容数组的展示。所以需要把 $record['formatted'] 的每个 json 对象的 value 转为 json 格式,再使用 $format 格式化才可以变成完整的 json 格式。于是我复制了 MonoLog/Formatter/LineFormatter.php 文件,改写后注册到 $monologHandler 对象中。

对比原文件,我修改了 format 方法与 stringify 方法;前者是为了在 extra 、 context 和其他日志数组元素为空时赋值为空数组,后者是将这些元素的值转为 json 格式。

   步骤三 :在 bootstrap/app.php 注册一个 服务提供者

可以看到无论是用户主动记录的日志还是抛出的异常,以及系统异常都会被记录,格式也都符合json格式

异常的错误日志

测试抛出了一个关键异常
  改完了日志格式后就是测试阶段,我在写测试用例时,模拟了http,system,MySQL链接超时中断异常等等。
上面的最终效果展示中有这样一段,实际上这是修改了报错处理类修改之后的日志展示

之所以修改,是因为当测试到pdo异常时突然发现多了一个异常。这个相信也有不少人见到过:

The Response content must be a string or object implementing __toString(), "boolean" given
错误的意思很明显,response只能是字符串。这就很奇怪了,明明抛出了异常,返回的必定是一个数组,为什么底层异常处理类会说是一个bool类型呢?具体修复过程,下篇文章: Response content must be a string or object ? (中) lumen的异常处理 我再说明

相似回答