日志服务(SLS) 的桌面端 Node.js SDK 封装

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 日志服务(SLS) 的桌面端 Node.js SDK 封装

项目需求

项目上为了更好地掌握产品的使用数据,综合对比各种埋点框架之后决定基于自由度更高的日志服务(以下简称“SLS”)实现数据埋点。

SLS 官方提供的 SDK 非常丰富,包含十几种语言,而 JavaScript 更是受到了优厚待遇,相关 SDK 已经达到了 3 种:浏览器 JavaScript SDK、小程序 JavaScript SDK、Node.js SDK。

目前我们项目没有小程序,只有 Node.js 的桌面端以及 JavaScript 的浏览器端,所以不考虑小程序 JavaScript SDK。

浏览器 JavaScript SDK

浏览器 JavaScript SDK,可以通过 npm 方便地引入,在 web 页面上进行数据上报。

importSlsTrackerfrom'@aliyun-sls/web-track-browser';
constopts= {
host: '${host}', // 所在地域的服务入口。例如cn-hangzhou.log.aliyuncs.comproject: '${project}', // Project名称。logstore: '${logstore}', // Logstore名称。}

API 也相当简单,4个函数实现了同步和异步、单条和多条的数据上报。

image.png

符合项目需求,很顺利地就被引用了。

Node.js SDK

Node.js SDK 和浏览器 JavaScript SDK 提供的 API 相比,则提供了更为丰富的功能,不仅有日志数据上报,还加入了对日志库的管理操作。

image.png

不仅如此,使用的时候增加必传参数 AccessKeyID 和 AccessKeySecret。

constALY=require('aliyun-sdk')
varsls=newALY.SLS({
accessKeyId: "11****ut",                         //阿里云访问密钥AccessKey ID。更多信息,请参见访问密钥。阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维。 secretAccessKey: "TS****7Y",                     //阿里云访问密钥AccessKey Secret。 endpoint: 'http://cn-hangzhou.log.aliyuncs.com', //日志服务的域名。更多信息,请参见服务入口。此处以杭州为例,其它地域请根据实际情况填写。apiVersion: '2015-06-01'//SDK版本号,固定值。  })
constprojectName="you_project_name"// 必选,Project名称。constlogstoreName="your_logstore_name"// 必选,Project描述。// 创建Project。functioncreateProject () {
constparam= {
projectDetail: {
projectName,                                  
description: "description about project"    }
  }
sls.createProject(param, function(err, data) {
if (err) {
console.error('error:', err)
    } else {
console.log('创建project', data)
    }
  })
}
// 运行function。createProject()

这两个特点就和桌面端的使用场景有些相悖了。

一方面桌面端也只需要进行数据上报,并不需要对日志库管理,丰富的功能有些冗余。另一方面作为支持公共写的日志库实际上并不需要这些配置信息,而且这些信息放在端上也存在安全风险。

那在桌面端上该怎么方便地上报日志数据呢?

解决思路

借用浏览器 JavaScript SDK

既然桌面端和浏览器一样都是用 JavaScript 写的,那是否可以直接在桌面端用浏览器 JavaScript SDK 呢?

很遗憾并不行,由于浏览器 JavaScript SDK 依赖了浏览器一些全局方法,会导致在 Node.js 中直接报错。

如果要强行在 Node.js 中运行,对其进行改造也是可以。

但是改造流程可能会比较长:

  • 拉取代码仓库。
  • 修改代码。
  • 提交 PR 合并后等新版本发布或者自行再建一个仓库发布到 (T)NPM。

这一番操作下来已是远水救不了近火,所以换个思路自行封装一个轻量的用于客户端的 Node.js SDK

基于官方 API 进行封装

首先考虑的是基于官方的 API 进行封装。

关于日志数据上报,官方提供了2个的方法: PostLogStoreLogsPutLogs 。这两个方法各有一些限制:

  • PostLogStoreLogs 只支持 PB格式的日志压缩数据。
  • PutLogs 需要传入 LogGroup、LogTag 这些参数。

除此之外,它们都有一个共同点:需要 AK 授权。

这和浏览器 JavaScript SDK 相比明显麻烦很多,既然这样,我们只能往更底层去挖掘可能性了~

基于官方 SDK 逆向研发

既然我们想要在桌面端实现一个功能和浏览器 JavaScript SDK 一样的 SDK,那么不妨先来了解它做了什么。

当我们调用 send 函数的时候,实际上并没有立即发送日志,而是在随后的轮询间隔中,以数组的形式将日志数据进行上报。

image.png

所以这里推测采用了队列来缓存日志数据,减少网络请求次数。

同时仔细观察还可以发现为了不占用网络线程,这里采用了浏览器 sendBeacon 方法而不是 XMLHTTPRequest 或者 fetch 的方式,如果浏览器不支持这个方法则降级为 POST 请求。但由于这个 API 是浏览器提供的,所以桌面端只考虑采用 POST 请求方式。

image.png

通过浏览器调试工具观察请求网址可以发现其规律

https://{project}.{host}/logstores/{logstore}/track?APIVersion=0.6.0

image.png

总结一下:

  1. 1.建立日志队列,延迟、定时发送。
  2. 2.按照固定格式发送 POST 请求。
  3. 3.支持立即发送,对应 SDK 的 sendImmediate 方法。(这一点是后面开发中才发现的需求,比如页面离开时需要立即发送日志,避免缓存的日志数据丢失)

具体实现

一般而言,对于无状态的封装可以考虑用(纯)函数。

而 SDK 不仅要传入 project、host、logstore 这些配置数据,还需要建立内部的缓存队列,所以对于这种有状态的场景考虑使用类 Class。

对应代码如下:

classSlsTracker {
_timeout=null; // 定时器实现延迟发送__logs__= []; // 缓存队列constructor({
host,
project,
logstore  }) {
// 配置信息this._host=host;
this._project=project;
this._logstore=logstore  }
}

至于延迟、定时发送的实现可以依赖 setTimeout 或 setInterval,由于发送应该是队列有值的时候才进行,所以 setInterval 轮询这种性能消耗较大的方式并不适用。

下面来考虑核心函数 send 的实现。

send 接收一个对象参数作为日志数据。所以现将它推送到队列,然后创立一个延迟的 POST 请求,但很可能之前已经创建了一个 POST 请求,所以通过 this._timeout 来判断,如果存在则不再创建。定到定时器触发时将队列所有数据发送。

这里需要注意的是,上报的数据值需要转换为字符串。

考虑到程序的鲁棒性,可以考虑限制队列长度和字符长度。

classSlsTracker {
......send(info){
this.__logs__.push(this._transString(info));    
constoptions= {
hostname: `${this._project}.${this._host}`,
port: 443,
path: `/logstores/${this._logstore}/track?APIVersion=0.6.0`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
      }
    }
if (this._timeout) return;
this._timeout=setTimeout((o) => {
constpayload=JSON.stringify({ __logs__: this.__logs__ });
o.headers['Content-Length'] =Buffer.byteLength(payload)
constreq=https.request(o)
req.write(payload);
req.end();
this.__logs__= [];
this._timeout=null;
    }, this._time, options);
  }
.......
}

至于立即发送日志数据实现就相对容易了,取消之前定时器直接发送请求即可。

classSlsTracker {
......sendImmediate() {
constpayload=JSON.stringify({ __logs__: this.__logs__ });
consto= {
hostname: `${this._project}.${this._host}`,
port: 443,
path: `/logstores/${this._logstore}/track?APIVersion=0.6.0`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload)
      }
    }
constreq=https.request(o)
req.write(payload);
req.end();
this.__logs__= [];
clearTimeout(this._timeout);
this._timeout=null;
  }
}

总结

在依赖的代码库或平台不能满足要求时,首先考虑基于已有的信息自行进行研发,实在不行逆向研发也未尝不可。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
11天前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
116 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
1月前
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
216 3
|
11天前
|
JSON 监控 JavaScript
Node.js-API 限流与日志优化
Node.js-API 限流与日志优化
|
1月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1625 14
|
29天前
|
JavaScript 前端开发 开发工具
【Azure Developer】使用JavaScript通过SDK进行monitor-query的client认证报错问题
AADSTS90002: Tenant 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' not found. Check to make sure you have the correct tenant ID and are signing into the correct cloud. Check with your subscription administrator, this may happen if there are no active subscriptions for the tenant.
|
1月前
|
Python
log日志学习
【10月更文挑战第9天】 python处理log打印模块log的使用和介绍
30 0
|
1月前
|
数据可视化
Tensorboard可视化学习笔记(一):如何可视化通过网页查看log日志
关于如何使用TensorBoard进行数据可视化的教程,包括TensorBoard的安装、配置环境变量、将数据写入TensorBoard、启动TensorBoard以及如何通过网页查看日志文件。
193 0
|
3月前
|
SQL 运维 监控
Nest.js 实战 (十):使用 winston 打印和收集日志记录
这篇文章介绍了在Nest服务中如何使用Winston记录日志。文章首先强调了日志记录在后台服务中的重要性,接着提到Nest默认的内部日志记录器,并指出可以通过@nestjs/common包中的Logger类来全面控制日志系统的行为。文章还提到,为了在生产环境中实现更高级的日志功能,可以使用如Winston之类的Node.js日志包。接下来,文章介绍了如何在Nest服务中使用Winston记录日志,包括安装相关依赖、创建winston配置文件以及实现简单的日志记录示例。最后,文章指出更高级的自定义日志功能需要读者自己去探索。
118 2
Nest.js 实战 (十):使用 winston 打印和收集日志记录
|
1月前
|
存储 分布式计算 NoSQL
大数据-136 - ClickHouse 集群 表引擎详解1 - 日志、Log、Memory、Merge
大数据-136 - ClickHouse 集群 表引擎详解1 - 日志、Log、Memory、Merge
40 0
|
1月前
|
缓存 Linux 编译器
【C++】CentOS环境搭建-安装log4cplus日志组件包及报错解决方案
通过上述步骤,您应该能够在CentOS环境中成功安装并使用log4cplus日志组件。面对任何安装或使用过程中出现的问题,仔细检查错误信息,对照提供的解决方案进行调整,通常都能找到合适的解决之道。log4cplus的强大功能将为您的项目提供灵活、高效的日志管理方案,助力软件开发与维护。
54 0

相关产品

  • 日志服务