LogRock:通过记录进行测试
两年多来,我们一直
在进行Cleverbrush项目。 这是用于处理矢量图形的软件。 使用图形编辑器意味着大量的应用程序用例。 我们试图节省金钱和时间,因此我们优化了包括测试在内的所有内容。 用每个选项覆盖测试用例过于昂贵且不合理,尤其是因为不可能覆盖所有选项。
在开发过程中,创建了一个用于React JS应用程序的模块
-LogRock(github) 。
该模块允许您组织现代日志记录应用程序。 根据日志,我们进行测试。 在本文中,我将向您介绍使用此模块的复杂性以及如何通过日志记录组织测试。
怎么了
如果将程序与活生物体进行比较,则其中的错误是一种疾病。 该“疾病”的原因可能是多种因素,包括特定用户的环境。 如果我们正在考虑使用Web平台,则尤其如此。 有时因果关系非常复杂,并且在测试过程中发现的错误是许多事件的结果。
与人类疾病一样,没有人会比患者更好地解释其症状,没有测试者能够比程序本身更好地说明发生了什么。
怎么办
要了解正在发生的事情,我们需要用户在我们的应用程序中执行的操作的列表。
为了使我们的程序本身可以告诉我们它的“
缺点 ”,我们将使用
LogRock模块
(github)并将其与ElasticSearch,LogStash和Kibana关联。
ElasticSearch是功能强大的全文本搜索引擎。 您可以
在此处观看ElasticSearch教程。
LogStash是一个用于从各种来源收集日志的系统,该系统可以将日志发送到ElasticSearch。
Kibana是带有许多附加组件的
ElasticSearch的Web界面。
如何运作?
如果发生错误(或仅按需提供),则应用程序会将日志发送到服务器,然后将日志保存到文件中。 Logstash将ElasticSearch中的数据逐步保存到数据库中。 用户登录到Kibana并查看保存的日志。
看起来像是经过精心调校的Kibana。 它显示来自ElasticSearch的数据。 Kibana可以表格,图表,地图等形式显示数据,这对于分析和了解我们的应用程序正在发生的事情非常方便。
在本文中,我不会讨论设置ElasticStack!创建一个日志系统
作为示例,我们将日志记录系统集成到用React编写的一页JS应用程序中。 您的应用程序将在哪种框架上编写都没有关系。 我将尝试描述构建日志系统本身的方法。
1.客户
1.0 LogRock。 安装方式
链接到LogRock要安装,您必须执行:
npm install logrock yarn add logrock
1.1 LogRock。 应用设置
首先,将我们的应用程序包装在一个组件中
import { LoggerContainer } from "logrock"; <LoggerContainer> <App /> </LoggerContainer>
LoggerContainer是响应您的应用程序错误并形成堆栈的组件。
堆栈是一个对象,其中包含有关用户的操作系统,浏览器,按下的鼠标或键盘按钮以及操作子数组的信息,其中记录了他在我们的系统中执行的所有用户操作。
LoggerContainer有许多设置,请考虑其中的一些
<LoggerContainer active={true|false} limit={20} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer>
active-启用或禁用记录器
限制-设置用户最近保存的操作数量的限制。 如果用户执行21个动作,则该数组中的第一个动作将被自动删除。 因此,我们将获得错误之前的最后20个动作。
onError-发生错误时调用的回调。 Stack对象进入其中,其中存储了有关环境,用户操作等的所有信息。 正是从此回调中,我们需要将此数据发送到ElasticSearch或后端,或将其保存到文件中以进行进一步的分析和监视。
1.2 LogRock。 记录中
为了对用户操作进行高质量的日志记录,我们将不得不通过日志调用覆盖我们的代码。
LogRock模块随附与LoggerContainer关联的记录器
假设我们有一个组件
import React, { useState } from "react"; export default function Toggle(props) { const [toggleState, setToggleState] = useState("off"); function toggle() { setToggleState(toggleState === "off" ? "on" : "off"); } return <div className={`switch ${toggleState}`} onClick={toggle} />; }
为了用日志正确覆盖它,我们需要修改toggle方法
function toggle() { let state = toggleState === "off" ? "on" : "off"; logger.info(`React.Toggle|Toggle component changed state ${state}`); setToggleState(state); }
我们添加了一个记录器,其中信息分为两部分。 React.Toggle向我们展示了该动作发生在React的Toggle组件级别,然后我们对该动作和该组件的当前状态进行了口头解释。 这样的分层是没有必要的,但是通过这种方法,将更清楚地知道我们的代码在何处执行。
如果发生错误,我们还可以使用React版本16中引入的“ componentDidCatch”方法。
2.服务器交互
考虑以下示例。
假设我们有一个从后端收集用户数据的方法。 该方法是异步的,部分逻辑隐藏在后端中。 如何正确记录此代码?
首先,由于我们有一个客户端应用程序,所有发送到服务器的请求都将通过一个用户会话,而无需重新加载页面。 为了将客户端上的操作与服务器上的操作相关联,我们必须创建一个全局SessionID并将其添加到对服务器的每个请求的标头中。 在服务器上,我们可以使用任何记录器来记录我们的逻辑,例如从前端获取示例,并且在发生错误的情况下,将带有Elastic中附加的sessionID的该数据发送到后端板。
1.我们在客户端上生成SessionID
window.SESSION_ID = `sessionid-${Math.random().toString(36).substr(3, 9)}`;
2.我们必须为对服务器的所有请求设置SessionID。 如果我们使用库进行查询,则通过为所有查询声明一个SessionID非常简单。
let fetch = axios.create({...}); fetch.defaults.headers.common.sessionId = window.SESSION_ID;
3.在LoggerContainer中,SessionID有一个特殊字段
<LoggerContainer active={true|false} sessionID={window.SESSION_ID} limit={20} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer>
4.请求本身(在客户端上)将如下所示:
logger.info(`store.getData|User is ready for loading... User ID is ${id}`); getData('/api/v1/user', { id }) .then(userData => { logger.info(`store.getData|User have already loaded. User count is ${JSON.stringify(userData)}`); }) .catch(err => { logger.error(`store.getData|User loaded fail ${err.message}`); });
一切如何工作:我们在客户端请求之前记录日志。 根据我们的代码,我们看到现在将开始从服务器加载数据。 我们已将SessionID附加到请求。 如果我们的后端被添加了该SessionID的日志覆盖,并且请求失败,那么我们可以看到后端发生了什么。
因此,我们不仅在客户端而且还在后端监视应用程序的整个周期。
3.测试人员
与测试人员一起工作应该对过程进行单独描述。
由于我们有一家初创公司,所以我们没有正式的要求,有时工作中的一切都不合逻辑。
如果测试人员不了解该行为,则至少需要考虑这种情况。 同样,测试人员经常无法重复一种情况两次。 由于导致不正确行为的步骤可能很繁琐,而且很简单。 而且,并非所有错误都会导致严重后果,例如Exception。 其中一些只能更改应用程序的行为,而不能被系统解释为错误。 为此,在登台时,您可以在应用程序标题中添加一个按钮以强制发送日志。 测试人员发现有些东西无法正常工作,单击按钮,然后将包含操作的堆栈发送到ElasticSearch。
但是,如果发生了严重错误,我们必须阻塞该接口,以使测试仪不会进一步单击并且不会陷入死胡同。
为此,我们显示死亡的蓝屏。
我们在顶部看到带有此严重错误的堆栈的文本,在下面看到的是其之前的操作。 我们还会获得错误ID,测试人员只需选择它并将其附加到故障单上即可。 稍后,可以使用此ID在Kibana中轻松找到此错误。
为此,LoggerContainer具有自己的属性。
<LoggerContainer active={true|false} limit={20} bsodActive={true} bsod={BSOD} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer>
bsodActive-启用/禁用BSOD(禁用BSOD适用于生产代码)
bsod是一个组件。 默认情况下,它看起来像上面的屏幕截图。
要在UI LoggerContainer中显示按钮,我们可以在上下文中使用
context.logger.onError(context.logger.getStackData());
4. LogRock。 用户互动
您可以将日志输出到控制台或将其显示给用户,为此,您需要使用stdout方法:
<LoggerContainer active={true|false} limit={20} bsodActive={true} bsod={BSOD} onError={stack => { sendToServer(stack); }} stdout={(level, message, important) => { console[level](message); if (important) { alert(message); } }} > <App /> </LoggerContainer>
stdout是负责显示消息的方法。
为了使消息变得重要,只需将第二个参数true传递给记录器就足够了。 因此,该消息可以在弹出窗口中显示给用户,例如,如果数据加载失败,我们可以显示错误消息。
logger.log('Something was wrong', true);
进阶记录
如果您在一个商店中使用Redux或类似的解决方案,则可以将记录器放入处理您的操作的中间件中,从而所有重要操作都将通过我们的系统。
为了有效地进行日志记录,您可以将数据包装在代理对象中,并使记录器执行与该对象有关的所有操作。
要使用日志记录覆盖第三方方法(库方法,旧版代码方法),可以使用修饰符-“ @”。
小费
记录包括生产在内的应用程序,因为比真实用户要好,因此没有测试者会发现任何瓶颈。
不要忘记在许可协议中指明日志的收集。
请勿记录密码,银行详细信息和其他个人信息!日志的冗余性也很差,请使签名尽可能清晰。
替代品
作为替代方法,我强调:
- Rollbar是高度可定制的。 每月只需$ 150,即可记录500,000个错误。 如果您是从头开始开发应用程序,则建议使用它。
- Sentry易于集成,但可定制性较低。 允许您以每月$ 200的价格记录100万个事件。
两种服务都使您可以做几乎相同的事情并集成到后端中。
接下来是什么
日志记录不仅是查找错误的信息,还监视用户的操作,数据收集。 日志记录可以很好地补充Google Analytics(分析)和用户体验测试。
结论
当您发布该应用程序时,他的生活才刚刚开始。 对自己的想法负责,获取反馈,监控日志并加以改进。 编写高质量的软件并繁荣发展:)
PS如果您想为Angular,Vue等模块的开发提供帮助 我很高兴在
这里提出请求。