函数日志相关
- 如何监听云函数运行日志
开发环境不支持日志订阅,生产环境才支持
(1)创建飞书群
- 创建任意一个飞书群,也可以使用现在的飞书群。
- 在群里点击「设置」->「群机器人」->「添加机器人」,选择自定义机器人,记下 Webhook 地址。
(2)创建日志订阅
- 进入应用管理页,点击「①应用监控」->「②函数日志告警配置.去配置」->「③填写记下 Webhook 地址」。
- 日志等级按需选择,可多选。
250px|700px|reset
250px|700px|reset
- 函数日志告警是否支持屏蔽/降频
(1)场景及问题
250px|700px|reset
开发者想了解函数日志告警是否支持屏蔽重复报警一段时间(比如屏蔽 15min/30min),即告警降频。
(2)解决方案
(3)关于告警降频
告警降频能力针对如下场景比较有用:
比如A函数报警生效,我们排查出A函数有问题,但是解决A函数问题的期间,A函数还在持续报警,并且短时间就报警了几百条。但此时B函数也突然出现问题报警,因为A报警没办法屏蔽,B问题就被淹没了。
- 函数查询不到日志
(1)场景及问题
用户在可观测模块,查询不到函数执行日志
(2)自查思路
函数可观测分为新、旧两个版本(@byted-apaas/server-common-node v2.0.9 以及 @byted-apaas/server-common-go v0.0.29 版本之后,用的是新版可观测日志)。两个版本下查询不到日志的原因不一样,需要分开来看:
- 旧版
- 函数分为同步和异步两种类型:
250px|700px|reset
- 同步函数和异步函数在执行时可能会面临如下日志相关情况:
- 执行过程中看不到日志,正常执行完成后才看到日志。
- 同步函数执行超时(>15min)后,查询不到函数执行日志。
- 异步函数执行超时(>3h)后,查询不到函数执行日志。
- 函数未超时,但异常退出,查不到日志。
- 一次函数执行的日志总大小超过 7MB。
- 原因如下:
- 执行过程中看不到日志,正常执行完成后才看到日志。——这是正常现象,云函数设计之初就是在执行完成后才提交日志,未来云函数日志会以流式日志的方式接入新版可观测,到时可以实时看到程序运行过程中产生的日志,未来用户只需要升级到最新版 SDK 即可。
- 同步/异步函数执行超时后, 后端 FaaS 平台会回收函数实例资源,这种异常情况会导致函数运行过程中产生的日志无法上报到 aPaaS,所以也看不到日志。
- 函数未超时,但异常退出,查不到日志。——可能是函数代码中操作了大文件上传/下载、存在死循环等情况导致函数运行过程中 OOM 了,这种实例异常退出的情况也会导致函数运行过程中产生的日志无法上报到 aPaaS,所以也看不到日志。
- 新版
- 函数打印日志较大,超出了平台限制,导致日志无法展示。
(3)解决方案
- 旧版:
- 如果是同步函数超时,那么开发者可以将同步任务函数改为异步任务函数,异步任务最长执行时间为 3h。但无论是同步函数还是异步函数,用户都需要优化代码,确保业务逻辑在对应的超时时间内完成。
- 同步函数执行超时时间是15min
- 异步函数执行超时时间是3h
- 开发者检查代码,确保不存在大文件上传/下载、死循环等问题导致函数 OOM。
- 大文件上传可以参考:“案例合集”部分“multipart/form-data 上传大文件”介绍。
- 新版:
- 调整打印的日志大小。
- 函数收到日志告警 Error: You may create or update up to 500 records at once [k_mt_ec_900148]
(1)场景及问题
开发者配置了函数日志告警,在函数执行完成后收到如下 Error 日志告警:
250px|700px|reset
(2)自查思路
这个报错一般是由于代码中使用了如下批量接口且一次性创建/更新了超过 500 条记录:
- application.data.object().batchCreate
- application.data.object().batchUpdate
比如如下代码:
try { // 当 i=1 时,就会出现一次性更新超 500 条记录的情况 for (let i = 0; i * 500 < uniqWith.length; i++) { await context.db.object(xxx).batchUpdate(uniqWith.slice(i, (i + 1) * 500)} catch (err){ logger.info(`去重失败,全部更新:${err}`)}}
- 函数日志可以查询 7 天以前的吗
(1)场景及问题
开发者想查询 7 天以前的函数日志。
(2)解决方案
@byted-apaas/server-common-node v2.0.9 以及 @byted-apaas/server-common-go v0.0.29 版本之后,用的是新版可观测日志
- 旧版:目前飞书低代码平台的云函数日志只支持存储 7 天,所以 7 天以前的日志无法查询。
- 新版:接入可观测后,可以保留 一个月。引导用户升级 sdk 即可接入新版可观测日志。
- 流程执行日志中,查询不到函数日志
(1)场景及问题
用户流程调用函数,流程执行日志中,无云函数日志
250px|700px|reset
(2)解决方案
出现此类问题的根因是,应用使用的流程是 1.0 版本,日志也上报到了旧版可观测日志。云函数日志上报到了新版本可观测。
碰到此类问题,目前有两个方法来应对:
- 把应用内的流程升级到 2.0 版本,就可以看到所有日志了
- 如果短期无法升级,用户可以去函数执行的日志里,去看函数执行日志,路径如下:
250px|700px|reset
SDK 相关
- 如何正确使用 Go SDK 筛选日期类型字段
(1)场景及问题
- Go SDK 的数据查询场景,筛选日期类型字段;
⚠️ 这里需要注意,该例子针对的是「日期」类型,对于「日期时间」类型右值应为时间戳
250px|700px|reset
- 用 cond.Gte 条件筛选时报错:日期类型字段不支持 Gte 条件;
(2)自查思路
- 日期字段的比较用 cond.IsOnOrAfter 或 cond.IsOnOrBefore;
- 右值用格式为 yyyy-MM-dd 的字符串日期表达;
(3)Bad Case
- 用 cond.Gte 条件筛选日期类型字段,导致报错:日期类型字段不支持 Gte 条件
250px|700px|reset
(4)Good Case
- 日期字段的比较用 cond.IsOnOrAfter 或 cond.IsOnOrBefore;
- 右值用格式为 yyyy-MM-dd 的字符串日期表达;
250px|700px|reset
- 如何正确使用 Node.js SDK 的 BaaS 能力
(1)场景及问题
Node.js SDK 的 baas 使用场景;
(2)自查思路
- 依赖列表中是否安装了 BaaS SDK:@byted-apaas/baas-sdk-node
- 有此依赖才可以使用 baas 的能力。
- 如未安装,可以手动安装一下。
- 是否主动导入 BaaS SDK
- BaaS SDK 是开箱即用的,不需要主动导入。
- 如何正确使用 Go SDK 查询数据
(1)场景及问题
使用 Go SDK 查询数据时,一直报错:反序列化失败;
(2)自查思路
查询数据的第二个入参变量
- 前面是否添加取地址符 &;
- 类型是否正确;
(3)Bad Case
- 未加取地址符报错
250px|700px|reset
- 类型不匹配报错:应该为 slice,却传成了 struct
250px|700px|reset
(4)Good Case
250px|700px|reset
- 如何正确使用 Node.js SDK 触发其它函数
(1)场景及问题
- 用 faas.function(funcName).invoke() 报错;
- 用 baas.tasks.createDistributedTask([1, 2, 3], 'increasesComputeDistributedTaskTaiZhangFunc', undefined, undefined, {}) 报错;
250px|700px|reset
(2)自查思路
- 判断该函数是否存在且已部署;
- 判断该函数是否存在语法错误;
- Node.js 的触发原理是 require('./{funcName}');
- 当 {funcName} 中存在语法错误时,便会抛出异常;
- 存量 SDK 会统一将该异常转为 The function ({apiName}) does not exist. 不合理;
- 最新版的 SDK 已将语法类的原始错误吐出;
- SDK 查询条件支持 like 的功能吗?
部分支持,即仅支持 %..% 的格式。
- OQL 的查询,例如:fieldAPIName LIKE '%ab%';
- API 的查询,可以利用 cond.Contain,达到 %..% 的效果,例如:Where(cond.Contain("fieldAPIName", "ab"))
- SDK API 查询的过滤条件,支持下钻吗?
支持,用点 . 实现,最多5层。
例如:查询部门名称为 aPaaS Engineering 的员工 Where(cond.Eq("_department._name", "aPaaS Engineering"))
- SDK NotIn 报错「请求参数不合法:unknown operator type isNoneOf」
notIn 仅支持 ID、选项和关联对象(多值)类型的字段。当 notIn 的左值为其它数据类型时,会报错。
- SDK 怎么连表查询
(1)场景及问题
SDK 怎么实现连表查询
(2)解决方案
连表查询可以考虑 application.data.oql 接口(链接文档有使用样例),对于大表连表可能存在性能问题,一般使用「多次查询」 + 「结果过滤」的方式替代连表查询。详见 oql 使用说明。
- SDK 使用 oql 访问外部数据源报错 object "xxx"dosen't exist
(1)场景及问题
开发者使用 NodeJS SDK application.data.oql().execute() 方法进行 oql 查询时,报错 object "xxx"dosen't exist [k_ec_000060] (logID=undefined) at Object.doRequest
(2)自查思路
- oql 语句中查询的表名称是否正确,应为对象的 API 名称,不是对象的名称;
- oql 语句中查询的表字段是否正确,应为字段的 API 名称,不是字段的名称;
举例,假如有如下对象信息:
250px|700px|reset
250px|700px|reset
然后执行如下 oql 语句那么就会报错:
250px|700px|reset
这里 page_enter_from_info 和 page_name 都是对象和字段的名称,需要改为对应的 API 名称 object_122 和 text_a7bef1bb306,即 SELECT text_a7bef1bb306 FORM object_122。
- SDK application.Data.Object 报错提示超时 12000
(1)场景及问题(用户侧)
使用 SDK application.Data.Object 接口查询超时报错,提示超时 12000
(2)解决方案
- 查询耗时超过 12s 导致的报错,12s 为系统设置不可调整,需用户侧优化查询语句。
- 如果确认耗时很短,且属于偶发现象,可能是 faas 云平台到下游网络链路出现的偶发超时,此类问题可以在代码逻辑中进行重试(比如使用 catch 捕获错误,根据错误信息进行重试)解决。
- SDK 提交事务报错 Invalid Request
(1)场景及问题
开发者使用 sdk 提交事务时,返回报错:Invalid Request
250px|700px|reset
(2)自查思路
确认事务提交参数是否正确(一般情况下都是开发者设置的参数不正确或者类型错误)
- 开发者可通过加日志的方式,打印事务中的每个操作的参数,确保类型正确
- SDK 调用 aPaaS 函数报错 connect ECONNREFUSED
(1)场景及问题
开发者在云函数中使用 node sdk 调用 apaas 其它函数:、
context.function(`${function_name}`.invoke(params))
在调试运行时报错:connect ECONNREFUSED {IP} at ClientRequest.
250px|700px|reset
(2)自查思路
ECONNREFUSED 表示服务端拒绝连接,可以按照以下步骤来排查:
- 查看调用的目标函数名称是否正确
- 查看调用的目标函数是否部署或者发布
- 开发环境:查看是否部署过
- 线上环境:查看是否发布过
- 目标函数逻辑排查
- 函数有调用外部服务接口:
- 自测外部接口是否正常
- 外部接口是否有权限限制
- 自身代码 bug
- 目标函数没有调用外部服务接口:
- 自身代码 bug
- 使用 Go SDK 出现告警:[调用下游-严格授权灰度放行]
(1)场景及问题
字节内部开发者(外部开发者不涉及)在低代码函数中使用 Go 版本 SDK 访问一些接口时出现如下告警:
250px|700px|reset
(2)根本原因
(3)解决方案
- SDK 查询子对象报错: 请求参数不合法 objectApiAlias[subObject_aa] fieldApiAlias[_id] not exist
(1)场景及问题
开发者在对象定义中使用了子对象:
250px|700px|reset
然后在函数代码中使用 application.data.object(子对象apiName) 进行查询,报如下错误:
[k_ec_000015] 请求参数不合法:fieldLocator objectApiAlias[subObject_6a6250733e1] fieldApiAlias[_id] not exist! (logID: 2024052121554xxxx))
(2)自查思路
飞书低代码平台当前不支持对子对象的查询,因此需要开发者修改代码,通过查询主对象就能够拿到该主对象的子对象信息。
- SDK application.flow 输出 undefined
(1)场景及问题
开发者在函数代码中使用 application.flow.execution 打印数据:
250px|700px|reset
然后使用流程调用函数,在运行过程中发现函数输出日志为 undefined:
250px|700px|reset
(2)自查思路
server-sdk-node SDK 低版本(<1.1.16)存在 bug,开发者可以在 WEBIDE 页面点击“依赖管理”检查依赖版本,然后升级到最新版即可:
250px|700px|reset
注:
- 该 bug 修复版本为 1.1.16,所以至少要升级到该版本,但建议升级到最新版,都是经过充分测试的,且最新版一般更稳定、性能更好、支持的特性也更丰富。
- 若非流程调用函数场景,比如函数调试场景,那么 application.flow.execution 输出 undefined 符合预期。
- SDK 查询对象记录后发现字段类型对不上,对象中字段为整数,sdk 拿到的是字符串
(1)场景及问题
开发者在对象中将某个字段类型设置为了整数:
250px|700px|reset
然后通过 SDK 的 application.data.object 获取记录数据的时候发现类型为整数的字段返回的是字符串类型:
250px|700px|reset
(2)解决方案
开发者自己在代码中做一下转换即可:
import ( "strconv")func test() { num, err := strconv.ParseInt(str, 10, 64) if err!= nil { // 处理转换错误 return }}}
- SDK 查询对象超时 Error: the data query timed out
(1)场景及问题
开发者使用SDK查询对象报错超时:the data query timed out
250px|700px|reset
(2)自查思路
其实报错日志中已经提供了解决方案:The data query timed out, please try to change the query coonditions or contact the app admin.
- 报错日志中有 k_mt_ec_query_timeout 错误码,查阅应用内的错误码-metadata 类型可知 k_mt_ec_query_timeout 错误码代表如下含义:
- 数据查询超时,请尝试更换查询条件或联系应用管理员。(和报错日志中的提示信息一致)
- 对于这个查询超时可以这样理解:
- 就像 MySQL 查询一样,当表数据量很大时,如果想加速查询,我们需要对常用查询字段建立索引(比如where name = 'xxx'需要对 name 字段建立索引),否则使用没有索引的字段查询就可能很慢甚至导致超时。
- 所以当开发者遇到查询对象超时,可以检查自己的对象有哪些字段建立了索引,更换查询条件使用有索引的字段查询即可,如果更换不了查询字段,那么可以联系应用管理员对查询字段添加索引。
- SDK 查询对象,pageLimit 参数的含义是什么
(1)场景及问题
用户在使用 application.data.object("xx").select("xx").findStream(handler, pageLimit),不太理解 pageLimit 的含义。甚至有些用户误以为是查询的总数。
(2)自查思路
SDK 文件操作相关
- 如何正确设置上传文件的类型
(1)场景及问题
- 使用 application.resources.file.upload() 上传文件后,绑定到记录中;
- 开发者常常碰到文件预览不了的问题;
250px|700px|reset
(2)原因及解决方案
- 上传时未指定文件名或指定的文件名未带扩展名;
- 在上传的第三个参数中正确设置带文件类型的文件名解决;
250px|700px|reset
- 如果 upload 函数中智能提示中只有两个参数,并未找到 fileName 参数时,请升级如下 SDK 到最新正式版本
- @byted-apaas/server-sdk-node
- 如果是本地开发,升级 SDK 后还需要执行一下 ae project repair 或 ae function repair 生效
- 云函数上传附件失败 Invalid URL:undefined/auth/v1/appToken
(1)场景及问题
云函数低代码中上传附件失败,提示:Invalid URL: undefined/auth/v1/appToken
250px|700px|reset
(2)自查思路
- 开发者可先排查是否使用了 @byted-apaas/server-common-node@1.0.24 版本
- @byted-apaas/server-common-node@1.0.24 存在 bug 导致调用失败,升级版本即可解决问题。
- SDK 下载附件报错 File upload failed: Error: 权限不足[k_ec_000009]
(1)场景及问题
开发者使用 NodeJS SDK context.resources.file.download() 接口进行文件下载,查看函数日志报错 File upload failed: Error: 权限不足[k_ec_000009]
(2)自查思路
context.resources.file.download() 接口需要将整个 file_info 作为参数传递:
// file_info 参考样例{ id: 'xx', mime_type: 'jpg', name: '328648-xx.jpg', size: 143592, token: 'xxx'}}
比如:
250px|700px|reset
开发者需要排查在调用 context.resources.file.download() 接口时是否仅传入了文件 token 之类的部分信息。
(3)最终解决方案
升级过程如下:
- 在前端 WEBIDE 中的“依赖管理”模块进行查看,重点关注 @byted-apaas/server-sdk-node 依赖版本:
250px|700px|reset
- 点击依赖下方的刷新按钮即可自动升级到最新版,最新版都是经过充分测试且稳定的版本,可放心升级。最好是将 @byted-apaas/xxx-node 相关依赖都升级到最新版 :
250px|700px|reset
- 升级依赖后,点击右上角的“部署”,等部署成功后在开发环境进行相关测试(比如页面、流程等和调用函数相关的操作),测试没问题后发布上线即可。
- SDK 下载附件报错 Permission denied
(1)场景及问题
开发者先通过表单页面进行文件上传,之后在其它页面通过 JS 代码调用 ServerlessSDK 接口下载之前上传成功的附件,提示权限不足:
250px|700px|reset
(2)自查思路
下载附件,需要结合附件上传的途径来分析。目前有两种上传附件的方式:
- 通过低代码上传
- 此时触发操作的身份是 apaas 系统;所以在低代码中下载,也是使用系统身份下载,这种方式不会有问题。
- 通过页面上传
- 通过页面上传的附件,触发操作的身份是用户自己;
- 然后通过低码下载的时候,身份是系统,所以会出现身份不一致报错权限的校验问题;
- 解决方案:下载的时候用文件 id 下载,此时不会校验权限,更多可参考 application.resources.download()接口
- SDK 如何根据附件的文件 id 查询到具体对象的记录
(1)场景及问题
将附件(可能多个)上传到对象的一条记录后,希望通过附件 id 查询到目标记录;
这里指的是定义的对象中有一个文件列表的字段:
250px|700px|reset
上传了文件(下图举例为两个文件)后,得到了文件的 id 等信息:
250px|700px|reset
接着新增/更新一条该对象的记录,其中文件列表字段的值为上述文件信息,本问题则是问是否能够通过文件的id查询到对应记录的 id。
(2)解决方案
- 对象数据量少时,可遍历查询出对象记录,在查询结果匹配附件 id 找到目标记录;
- 对象数据量多时,开发者可建设一个附件 id 到对象记录 id 的关系表,通过附件 id 查询到记录 id 后再查询记录;
- 传入云函数的变量类型为 arrayBuffer 类型时,云函数 params 中接收到的数据为空
(1)场景及问题
在自定义组件中上传文件,拿到文件的二进制数据,然后将该二进制数据传给云函数,期望云函数将该文件的二进制数据上传。
(2)根本原因
由于云函数不支持接收二进制数据,因此导致数据在传递过程中,无法对二进制数据进行解析,导致云函数接收到的数据为空。
(3)解决方案
用户修改处理方式,可通过如下步骤进行数据上传:
a. 在「数据」中新建一个「对象」用来存放待上传的文件
b. 自定义组件中将文件上传到对象中,触发函数
c. 函数获取对象记录中的文件,上传文件
- application.resources.file.upload 上传报 k_op_ec_10101 或 400 (Bad Response)
(1)场景及问题
用户通过 application.resources.file.upload 方式上传文件流报错
- 示例代码:
// 通过 NPM dependencies 成功安装 NPM 包后此处可引入使用// 如安装 linq 包后就可以引入并使用这个包// const linq = require("linq");/** * @param {Params} params 自定义参数 * @param {Context} context 上下文参数,可通过此参数下钻获取上下文变量信息等 * @param {Logger} logger 日志记录器 * * @return 函数的返回数据 */module.exports = async function (params, context, logger) { // 日志功能 logger.info(`${new Date()} 函数开始执行`); // 获取网络文件流 let resp; try { resp = await axios({ url: '', // url: 'https://sf1-scmcdn-cn.feishucdn.com/obj/feishu-static/apaas/kunlun/app_dev_main/production/assets/3e119123c66b317a5da8f994079cca9e.png', method: 'get', responseType: 'stream' }); } catch(err) { logger.error(err) } // 上传文件获取文件 token try { const file_info = await application.resources.file.upload(resp.data); logger.info(file_info); } catch (err){ logger.error(err) } }}
250px|700px|reset
(2)自查思路
可能是上传的文件类型不支持导致的,用户可以通过 curl -i 查看当前上传文件的类型。
支持的类型包括:image/*,video/mp4,video/quicktime,video/x-msvideo,video/x-ms-wmv,video/x-flv,video/mpeg,audio/mpeg,audio/x-ms-wma,audio/wav
*
250px|700px|reset
250px|700px|reset
编译部署相关
- CLI 本地编译报错 ERROR DX_CLI_41030 构建 golang 函数失败
(1)场景及问题
开发者本地执行 ae project build 或 ae source push 命令时报错提示:“Unsupported Go version. Supported versions are: 1.15, 1.16, 1.17, 1.18”
250px|700px|reset
(2)根本原因
ae project build 或 ae source push 命令会触发 golang 代码本地编译,云函数 golang 框架依赖了一些基础库,但开发者本地安装了高版本 golang 环境(比如 1.19/1.20),高版本 golang 不兼容这些基础库的语法特性, 所以编译时报错。
(3)解决方案
开发者安装 go 1.16 或者其他支持的版本(报错提示中有支持的版本列表)。
- 部署代码失败,提示必填参数 xxx 为空
(1)场景及问题
开发者在本地 IDE 通过 ae source push 命令部署云函数,提示: 必填参数 chat.output.resps.fieldApiNames 为空,报错如下所示:
250px|700px|reset
(2)自查思路
检查函数实际返回和函数在 index.meta.json 文件中声明的返回(返回字段、返回类型)是否一致,如果不一致,需要修复使两者保持一致后再部署。
- 函数部署失败报错 Functions can no longer be published as Public APIs
(1)场景及问题
开发者误删除了自己应用的 public API 云函数,想再部署该云函数,提示:“Functions can no longer be published as Public APIs. Please update the "isPublicAPI" property of the following functions to "false" and try again:onboarding_search”
(2)自查思路
- public API 特性是已下线功能,不再支持新的 public API。
- 开发者可以通过改为 openapi 的方式接入。
- 函数部署失败报错 the referenced function cannot be offline or deleted
(1)场景及问题
部署云函数失败,提示 the referenced function cannot be offline or deleted.
250px|700px|reset
(2)自查思路
“the referenced function(xxx) cannot be offline or deleted” 表示“函数 xxx 正在被其它地方使用(比如流程),所以函数 xxx 当前不能进行删除/下线/重命名操作”,可以引导开发者排查所有引用该函数 xxx 的地方,包括但不限于流程、其它函数等,找到后解除对该函数 xxx 的引用关系,比如:发现流程中有对函数 xxx 的引用,那么可以通过删除函数节点重新配置下对最新函数的引用保存流程即可。
- 部署函数失败报错 ERROR SystemError: 请求失败: 服务内部错误, 日志 ID: xxx
(1)场景及问题
开发者通过 CLI 命令 ae source push 或 WEBIDE 部署函数时常会遇到如下系统错误:
250px|700px|reset
这类错误没有更详细的错误日志,因此将常见错误场景进行总结,开发者可以参考先进行自主排查。
(2)自查思路
- 场景一:函数名包含中文字符
比如如下场景在部署函数时就会报错:
250px|700px|reset
排查函数命名中是否有中文字符,比如中文、中文标点符号等,当前平台不支持中文函数名,需要开发者统一改成英文字符。
- 场景二:npm 依赖未找到
在 npm 依赖版本找不到的情况下也会报错:
250px|700px|reset
开发者可以排查下 package.json 中新引入了哪些自定义依赖,然后在“依赖管理”中进行 check 依赖是否正常,正常情况下会展示出该依赖的所有可用版本:
250px|700px|reset
选择可用版本使用即可。
(3)最终解决方案
飞书低代码平台正在尝试将部署错误详情进行归类并最终露出给开发者,以方便开发者自行进行问题定位。
- WEBIDE 函数调试时报错 [ERR_INVALID_URL]: Invalid URL: undefined/auth/v1/appToken
(1)场景及问题
开发者在 WEBIDE 中调试函数时报错:TypeError [ERR_INVALID_URL]: Invalid URL: undefined/auth/v1/appToken(logID=undefined)
250px|700px|reset
(2)根本原因
由报错堆栈可以看出 server-common-node SDK 使用的还是kunlun/**工具包,这是低版本 SDK 才会有的工具包,而低版本存在 bug 会导致出现该问题。
(3)解决方案
- 开发者在 WEBIDE 中点击“依赖管理”check 下依赖版本:
250px|700px|reset
- 升级 @byted-apaas/**-node 相关 SDK 版本到最新版即可:
250px|700px|reset
- Nodejs 版本低 The engine "node" is incompatible with this module. Expected version "^16.20.0 || ^18.16.0 || >=20.0.0". Got "16.19.1"
(1)场景及问题
用户使用的 npm 包需要更高版本的 nodejs
250px|700px|reset
(2)自查思路
核心问题是用户使用的 npm 包要求更高的 nodejs 版本;目前 aPaaS 云函数正在支持高版本的 nodejs 中。
需要用户把要用的 npm 包降低下版本,或者使用一个别的包。
- 云函数调试/部署时报错:Error: Cannot find module 'xxx'
(1)问题描述
用户在自己的代码内引入了一个依赖包,然后报 Error: Cannot find module
250px|700px|reset
(2)根本原因
用户没有安装该依赖包,可以检查一下依赖管理 /package.json 中是否有上面报错的依赖包
(3)解决方案
安装该依赖包,如果在依赖管理搜索不到,而 npm 托管平台上存在,可以手动在 package.json 中添加
- 在 npm 上有某个依赖包,但是在 webide 上搜索不到
可以手动在 package.json 中添加所需的依赖包即可。
函数引用相关
- 修改云函数出入参后,云函数列表页提示函数引用异常
(1)场景及问题
- 开发者修改被流程、JS 事件等引用的云函数出入参数;
- 在云函数列表页面提示函数引用异常:
250px|700px|reset
(2)自查思路
开发者在引用详情页面点击「前往查看」跳转到引用页面,手动更新引用函数,对于流程引用的云函数,需要将流程节点的云函数删除之后再重新添加云函数引用。
对云函数的出入参数进行修改,会触发云函数的引用异常检查,如下四种操作可能会提示函数引用异常:
- 函数减少已被流程使用的入参;
- 函数增加必填入参;
- 函数出入参对象的 ObjectAPIName 修改;
- 函数出入参对象列表入参中对象增、减字段。
- 云函数参数的引用对象不存在,函数列表显示不出来
(1)场景及问题
云函数参数引用的对象不存在,页面提示如下错误。
250px|700px|reset
云函数列表显示不出来,但可通过 WebIDE 进行编辑云函数。
250px|700px|reset
(2)自查思路
进入 WebIDE 删除错误提示中不存在的参数引用后重新部署,函数列表能正常显示。
函数运行相关
- 云函数请求报错:lookup is invalid apiName = xx
(1)场景和问题
云函数执行报错 Error: lookup is invalid apiName = xx
250px|700px|reset
(2)排查思路
确认自己的入参有没有空对象,比如:[{name: 1},{}],如果有,请移除空对象,改为:[{name: 1}]
- 云函数如何请求 HTTP 接口
(1)场景及问题
开发者希望在云函数中发起 HTTP 请求,并获取该 HTTP 请求的返回结果。
(2)解决方案
在云函数中可以参考如下代码发起 HTTP 请求:
const res = await faas.tool.retry( async () => { const res = await faas.tool.http({ url: `https://activity.feishu.cn/api/lark_ug/entity/UG/ug_site_leads/${primaryKey}`, method: 'GET', query: {}, headers: { ...ugServiceHeader, }, responseType: 'json', timeout: 10000, }); return getCheckHttpRetry()(res, logger); }, { retryCount: 3, retryDelay: 500, }, ); const { data, code, msg } = res?.body || {}; if (code !== 0) { return { code: 500, message: `HTTP 请求失败: ${msg}`, }; } // 获取到 HTTP 返回的数据 data,继续后续处理 ....
- 云函数访问第三方域名或调用其它函数时报错 4xx/5xx
(1)场景及问题
在函数代码中使用 FaaS SDK 提供的 http 工具或三方包 axios 请求第三方域名,HTTP 响应码是400/408/429/500等错误,比如用户在代码中访问如下域名:
250px|700px|reset
运行后报错 400:
250px|700px|reset
(2)自查思路
400/500等错误码是常见的 HTTP 标准错误码,一般引导用户自行查阅网络资料分析解决即可。比如:
- 4xx:代表客户端请求错误,妨碍了服务器的处理。
- 400:错误请求, 一般是请求参数有问题,比如少了必填参数、参数类型错误等。
- 403 :服务器拒绝请求。
- 404 :服务器找不到请求的网页,一般是负载均衡 path 路由配置不正确等。
- 408:请求超时,服务器等候请求时发生超时。
- 429:指服务器拒绝响应客户端的请求,因为客户端发送的请求次数过于频繁,触发了服务端的限流,开发者可以降低调用频率后再次尝试。
- 5xx:代表服务端错误,表示服务器在尝试处理请求时发生内部错误,这些错误可能是服务器本身的错误,而不是请求出错。
- 500:服务器内部错误,服务器遇到错误,无法完成请求。
- 排查思路:根据目标域名找到对应的后端第三方服务,查看第三方服务的日志进行解决。
- 504:网关超时。
- 排查思路:函数运行环境与目标域名是否存在网络隔离。
- 发现有些函数代码语句未执行
(1)场景及问题
开发者使用低代码进行开发,并且在代码中对某个 async 函数进行了调用,但发现被调用函数中的某些代码语句未执行,比如如下代码:
250px|700px|reset
(2)根本原因
async 定义的函数是异步函数,顾名思义是异步执行的函数,因此在其它地方调用该异步函数时不会等待异步函数执行完成,即异步函数中的代码逻辑还没执行完可能程序就结束退出了。
(3)自查思路
排查代码中异步函数调用处有没有 await 关键词,如果没有的话加上 await 即可:
250px|700px|reset
- 函数运行报错: invalid memory address or nil pointer
(1)场景及问题
云函数执行失败,查看函数日志,有 invalid memory address or nil pointer 相关报错:
250px|700px|reset
(2)自查思路
这是空指针异常,是开发者代码有问题,开发者排查代码自行修复即可。
- 云函数访问第三方域名报错 Error: connect ETIMEDOUT
(1)场景及问题
开发者使用 nodejs 开发云函数,在函数代码中使用 FaaS SDK 提供的 http 工具或三方包 axios 请求第三方域名时报错:Error: connect ETIMEDOUT {IP:PORT} at TCPConnectWrap.after Connect
250px|700px|reset
(2)自查思路
ETIMEDOUT 为链接超时,是指的在客户端与远程服务器建立链接发生的超时,开发者可以检查域名对应的后端服务器端口是否有正确监听或者防火墙是否屏蔽了该端口。
如用户在云函数中访问的目标地址有白名单限制,也会报 ETIMEDOUT 错误,这种情况下,需要用户获取其应用云函数的出口 ip 列表并添加到白名单才能够正常访问。请提供租户 ID 和 namespace,联系研发同学获取云函数的出口 ip 列表,注意:开发环境和正式环境的出口 ip 列表不一样,需要分别获取。
更多 ETIMEDOUT 相关资料可自行网络搜索。
- 云函数访问第三方域名报错 Error: getaddrinfo ENOTFOUND
(1)场景及问题
开发者使用 nodejs 开发云函数,在函数代码中使用 FaaS SDK 提供的 http 工具或三方包 axios 请求第三方域名时报错:Error: getaddrinfo ENOTFOUND
250px|700px|reset
(2)自查思路
在 nodejs 中通过 HTTP 或 HTTPS 请求访问某个地址时,如果 nodejs 无法解析该地址,它将抛出一个 getaddrinfo ENOTFOUND 错误。这通常发生在以下情况:
- API 地址错误:提供的 API 地址可能不正确,或者存在拼写错误。
- 网络问题:域名/API 地址属于内网地址,nodejs 程序无法访问该地址。
开发者可以检查 api 地址是否存在错误,或者检查目标域名是否是内网域名导致外网无法访问。
常见 case:客户在 aPaaS 低代码函数(公网环境)中访问客户自己公司的内网域名不通,可以考虑换成公网域名访问或者在中间加个代理来解决。
- 通过 HTTP 触发函数运行报错:504 Gateway Time-out
(1)场景及问题
开发者通过 HTTP 接口触发函数运行报错:
250px|700px|reset
目前自定义 JS 事件、PublicAPI 等方式触发函数运行走的都是 HTTP 请求,都有可能出现 504 问题。
(2)根本原因
HTTP 请求当前设置的统一超时时间为一分钟,如果函数中的业务逻辑执行超过 1min,那么请求会自动断开,返回 504 超时。
(3)解决方案
开发者可以按照如下思路进行整改:任选其一即可
- 简化函数处理逻辑,保证函数在一分钟内处理完成。
- 采用流程调用函数,此时函数最大超时 15 分钟。
- 采用“长任务函数”(异步函数)的方式,最大超时 3 小时。
- 函数运行报错: Cannot find module '/xxx/functions/node_modules/nodejieba/build/Release/nodejieba.node'
(1)场景及问题
开发者在低代码中引入了一些“特殊”的 nodejs 依赖:这里以 nodejieba 依赖为例
250px|700px|reset
同时在代码中通过 require 的方式使用了该“特殊”依赖:
250px|700px|reset
然后部署、发布到线上环境后,流程调用函数或者直接运行函数时报错:
250px|700px|reset
(2)根本原因
查看 nodejieba 的 package.json,其配置了 install script 命令:
250px|700px|reset
scripts install 指示在 nodejieba 依赖成功下载后,会执行 scripts 中配置的 install 命令,该命令会对 nodejieba 目录进行编译,在nodejieba目录下生成 build/Release 目录,该目录也就是 module_path 配置的目录。当开发者编写云函数代码并 require('nodejieba') 时 nodejs 会按照 module_path 指示的路径进行加载文件。
而飞书低代码平台在发布云函数的过程中会执行 npm install xxx --ignore-scripts 命令进行安装用户指定的依赖,--ignore-scripts 参数会忽略 scripts install 配置的命令,因此 nodejieba 此类依赖就变成了“无效依赖”,因为跳过后置脚本后就不能生成正确的目录,进一步在代码中 require 使用时就会报错 “Cannot find module”。
(3)解决方案
--ignore-scripts 是飞书低代码平台出于安全起见携带的参数(可以避免一个恶意包里的病毒,更多可参考:what does ignore-scripts mean),因此开发者不要选择 nodejieba 之类的“特殊”依赖,这些依赖有个特点:
- 在下载安装后还需要依赖一些后置脚本编译后才能正常工作。
开发者可以通过查看这些依赖的 package.json 中有无配置 scripts install 即可确认,当发现这些“特殊依赖”后,可以寻找有相同能力的其它依赖来代替。
- 云函数运行报错: ENOENT: no such file or directory
(1)场景及问题
流程触发云函数执行,在流程页面提示云函数执行失败: statusCode=412, respBody={"ErrorCode":"CAExited","ErrorMessage":"Function instance exited unexpectedly(code 1, message:operation not permitted) with start command 'npm run start ...,示例如下图所示。
250px|700px|reset
(2)自查思路
错误信息包含 no such file or directory,意思是系统找不到对应的文件,找到错误信息中不存在的文件路径,根据文件路径自查该文件是否存在,文件命名是否合法。在示例错误信息中,看到提示不存在的文件路径为 lstat '/code/functions/_commonUtils/msgTemplates/����������.json,找不到的文件路径中出现乱码,是因为用户使用了中文作为文件名,将文件路径的中文改为英文后,重新部署函数再执行即可。
- 云函数线上报错 Call InnerAPI failed
(1)场景及问题
用户使用 sdk 时,遇到:
- Call InnerAPI failed 错误,比如使用 sdk 获取环境变量时,提示 Field "UsedBy" is required [k_cf_ec_200001]
- 非 Call InnerAPI failed 错误,但是错误堆栈中有 innerapi 字样的。
250px|700px|reset
250px|700px|reset
(2)根本原因
用户使用的 sdk 版本较低
所有使用 innerAPI 的接口报的错都是因为 SDK 版本较低,innerAPI 已经不再使用,需要将 serverless sdk 升级到最新版本
(3)解决方案
升级最新版本即可解决,需要升级的 sdk 及其版本号
- Nodejs
{
"dependencies": {
"@byted-apaas/baas-sdk-node": "1.0.16",
"@byted-apaas/faas-sdk-node": "1.0.11",
"@byted-apaas/server-common-node": "2.0.13",
"@byted-apaas/server-sdk-node": "1.1.19"
}
}
- Golang
// go.modrequire ( github.com/byted-apaas/server-common-go v0.0.34 github.com/byted-apaas/server-sdk-go v0.0.35 github.com/byted-apaas/baas-sdk-go v0.0.13 github.com/byted-apaas/faas-sdk-go v0.0.11))
- 云函数线上报错 Call InnerAPI failed: Failed to connect server "tcp-mesh"
(1)场景及问题
开发者使用 sdk 查询下游 data 对象时,提示超时问题
250px|700px|reset
(2)自查思路
开发者使用的 sdk 较低
所有使用 innerAPI 的接口报的错都是因为 SDK 版本较低,innerAPI 已经不再使用,需要将 serverless sdk 升级到最新版本
(3)解决方案
升级最新版本即可解决,需要升级的 sdk 及其版本号
- nodejs
{ "dependencies": { "@byted-apaas/baas-sdk-node": "1.0.16", "@byted-apaas/faas-sdk-node": "1.0.11", "@byted-apaas/server-common-node": "2.0.13", "@byted-apaas/server-sdk-node": "1.1.18" }}}
- Golang
// go.modrequire ( github.com/byted-apaas/server-common-go v0.0.34 github.com/byted-apaas/server-sdk-go v0.0.35 github.com/byted-apaas/baas-sdk-go v0.0.13 github.com/byted-apaas/faas-sdk-go v0.0.11))
- 调用云函数,入参没有传进去
(1)场景及问题
250px|700px|reset
250px|700px|reset
(2)自查思路
用户自定义的 type 结构需要设置正确,否则反序列化会有问题。
250px|700px|reset
- golang 云函数配置入参,debug 函数获取不到入参
(1)场景及问题
用户按照文档内容写 index.meta.json 配置入参后,在 WebIDE debug 云函数获取不到入参值,错误示例如下所示:
250px|700px|reset
(2)自查思路
golang 云函数参数结构体首字母需要大写,首字母小写获取不到入参值。Params 正确示例如下所示:
type Params struct { HardwareID int `json:"hardware_id"` // 首字母大写,json 对应 index.meta.json 配置字段。 SoftwareID int `json:"software_id"`}}
- 函数调用,遇到 overload 142902 错误
(1)场景及问题
用户函数调用遇到 overload 错误:
250px|700px|reset
(2)自查思路
碰到该问题,表示用户云函数调用频率过高,命中了 bytefaas 的限流。
可以通过调整最大并发数来实现,详细如下:
- 进入 bytefaas:
250px|700px|reset
- 调整并发数
250px|700px|reset
- Nodejs 引入了依赖包,但是运行时报错:xxx is not a function
(1)问题描述
在函数中通过 require 引入了依赖包,但是报错 xxx is not a function,错误的代码参考:
// A 文件中的代码B = require("B")B()// B 文件中的代码C = require("C")C()// C 文件中的代码A = require("A")A())
(2)排查思路
通过上面的代码可以看出代码中出现了循环引用的关系:A -> B -> C -> A,此时会导致依赖包在加载的时候存在问题,因此会报 xxx is not a function
(3)解决方案
- 新建一个函数用来解循环
- 使用 faas.function 的方式
- 使用延迟加载的方式,把模块之间的相互引用放在函数内部,而不是在模块的顶层。这样可以延迟加载依赖的模块,直到函数被调用
- Request exceeded the upper limit of <no value> attempt(s) in <nvalue>s. Please try again later.
(1)问题描述
调用函数遇到 Request exceeded the upper limit of <no value> attempt(s) in <nvalue>s. Please try again later. 错误
250px|700px|reset
(2)排查思路
以上问题是因为业务调用函数并发较大,被网关限流拦截导致的。
(3)解决方案
- 处理方式:用户调整业务逻辑,降低函数并发量。
- 是否可调整限流:目前不支持。
流程调用函数相关
- 在开发环境正常执行的流程,在线上环境执行时流程节点查询不到任何数据
(1)场景及问题
开发者在开发环境测试流程,流程执行正常,流程节点能查询到对应的测试数据。相同的流程在线上环境执行时,流程节点查询不到任何数据。
(2)自查思路
开发环境数据和线上环境数据是隔离的,开发环境可以在如下页面添加测试数据:
250px|700px|reset
开发者新建的对象在线上环境初始状态是没有任何数据的,需要开发者增加数据后才能查询到数据,例如先通过表单增加对象数据。
- 流程运行日志提示函数调用超出最大内存限制
(1)场景及问题
流程执行失败;
查看流程日志,在流程日志中的调用函数节点,有「超过最大内存限制」的错误日志:
250px|700px|reset
(2)自查思路
开发者优化代码:
- 对于附件上传场景需要限制附件的最大上限,大文件上传可以参考:“案例合集”部分“multipart/form-data 上传大文件”介绍。
- 对于附件下载场景避免并发一次性下载过多附件超过内存限制。
- 对于日志场景,尽量不要将大量日志打印出来,日志会暂存到内存中,如果打印的日志量过大,可能会触发内存限制。
- 流程调用函数超过最大执行时间
(1)场景及问题
流程执行中调用函数出现错误:超出最大执行时间
250px|700px|reset
(2)自查思路
函数分为同步和异步两种类型:
- 同步函数执行超时时间是15min
- 异步函数执行超时时间是 3h
250px|700px|reset
如果是同步函数超时,那么开发者可以将同步任务函数改为异步任务函数,异步任务最长执行时间为 3h。但无论是同步函数还是异步函数,用户都需要优化代码,确保业务逻辑在对应的超时时间内完成。开发者可以添加日志展示耗时,找出耗时较长的代码逻辑,进行分析优化即可。
- 流程中「函数节点」报错 Error: TimeoutError: Timeout awaiting 'request' for 25000ms
(1)场景及问题
开发者使用 nodejs 进行开发函数,在函数中通过循环的方式并发调用 application.flow.execute 或者其它方法,然后开发者在流程中调用该函数,查看函数节点日志,出现如下错误:
250px|700px|reset
(2)根本原因
application.flow.execute 等接口方法底层是通过 http 请求进行的,为了提高性能,nodejs 使用连接池进行发送 http 请求,当 QPS 超过一定限制(当前是<=20)后后续请求会排队,QPS <=20 这是 SDK 当前的硬性限制(也是出于保护 faas 实例的目的)。
(3)解决方案
- 开发者先优化代码,不要在循环中短时间(比如 1s)内连续并发触发流程,假设流程运行比较耗时,前续流程还没处理完, 那后续请求可能都要排队了,最后达到 25s (当前 HTTP请求超时时间)后就都超时了。如果一定要通过 for 循环触发,建议两次之间预留一些时间间隔(比如 2s)。
- 用户在 nodejs 云函数代码升级 server-common-sdk 到 2.0.9 或以上版本,高版本的 common-sdk 优化了 http 请求排队机制,可以部分缓解 http 请求排队问题,升级 server-common-sdk 示例如下所示:
250px|700px|reset
- 函数调用返回为空
(1)场景及问题
用户反馈调用接口返回为空,无报错
250px|700px|reset
250px|700px|reset
(2)根本原因
出现此类问题,是因为用户调用的接口需要配置鉴权。没有配置鉴权,报错信息会出现在 res header 中。
(3)解决方案
引导用户自行打印返回 body 和 header,可自行发现问题。如果是鉴权问题,比如缺少 token,可以自行增加鉴权,如下图,即可获取 token
250px|700px|reset
- 可以在云函数里直连redis么?
(1)场景及问题
开发者需要在云函数中直连自己的 redis,想了解云函数是否支持
(2)解答
可以的,云函数没有做任何限制,和用户在本地使用是一样的,云函数只是帮用户启了一个远端实例。用户本地能连使用云函数也能连。
- 云函数面板已经没有这个函数了,但是进去代码编辑页(WEBIDE)去搜,代码还在。
(1)场景及问题
用户在函数列表删除函数后,返回 WebIDE 还有该函数代码;
(2)根本原因
Webide 容器需要重新拉取远端代码才能更新
(3)解决方案
用户可重新拉取最新代码,即可自行解决。
- 在云函数访问下游 RPC 服务报错: service discovery failed
(1)场景及问题
在云函数访问下游 RPC 服务报错,详细错误为 service discovery failed。
(2)根本原因
这类问题比较明显,就是服务没有实例或者实例没有注册到服务发现。可能的情况为:
- 在云函数没有指定下游 RPC 服务集群时,请求打到下游 default 集群;如果下游服务没有 default 集群,会导致服务发现失败。
- 下游服务实例故障,确实找不到
(3)解决方案
- 用户在云函数指定下游 RPC 服务集群后,即可访问成功。
- 修复下游实例逻辑,使得服务实例正常上线并能注册到服务发现中心。
其他
- 错误码的含义
(1)场景及问题
接口报错了,但是不了解错误码的含义
(2)根本原因