函数计算目前支持Node.js 6.10(runtime=nodejs6)、Node.js 8.9.0(runtime=nodejs8)、Node.js 10.15.3(runtime=nodejs10)和Node.js 12.16.1(runtime=nodejs12)运行环境。本文介绍了Node.js运行环境的日志、函数、错误、模块使用、外部命令调用等内容。

打印日志

您的函数通过console.log打印的内容会被收集到创建服务时指定的Logstore中。函数日志的更多信息,请参见配置并查看函数日志

执行以下命令获取日志。

exports.handler = function (event, context, callback) {
    console.info('hello world');
    callback(null, 'hello world');
};            

输出的日志内容如下。

message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO] hello world           
您还可以通过接口setLogLevel来指定打印日志的级别,其中日志级别从高到低如下所示。
日志级别 对应接口
error console.error
warn console.warn
info console.info
verbose console.log
debug console.debug

执行以下代码,打印WARN、ERROR级别的日志。

'use strict';
exports.handler = function(evt, ctx, cb) {
    console.setLogLevel("error");
    console.error("console error 1");
    console.info("console info 1");
    console.warn("console warn 1");
    console.log("console log 1");

    console.setLogLevel("warn");
    console.error("console error 2");
    console.info("console info 2");
    console.warn("console warn 2");
    console.log("console log 2");

    console.setLogLevel("info");
    cb(null, evt);
};            

输出的日志内容如下。

message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [ERROR] console error 1
message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [ERROR] console error 2
message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [WARN] console warn 2          

返回信息

Node.js采用异步编程的模型,您的函数需要使用callback入参返回信息。callback的语法如下所示。

callback(Error error, Object result);           
其中:
  • error:可选参数,在函数执行内部失败时使用此参数返回错误内容,成功情况下设置为null。
  • result:可选参数,使用此参数返回函数成功的执行结果。result可以是任意类型,函数计算会将其序列化成字节流,放到响应体中返回给调用方。
说明 根据调用函数时的调用类型不同,返回值会有不同的处理方式。同步调用的返回值将会序列化字节流返回给调用方,异步调用的返回值将会被抛弃,需要您将重要信息记录到日志中。
  • callback被调用后则函数结束

    callback被调用后函数就会停止运行,重复调用callback只接受第一次调用的结果。需要确保所有任务在callback调用之前完成,否则有些任务可能不会被运行。例如调用下面的函数,将返回hello world,并且message不会被打印。

    exports.handler = function(event, context, callback) {
      callback(null, 'hello world');
      callback(null, 'done');
      setTimeout(function() {
        console.log('message');
      }, 1000);
    };          
  • callback未被调用则函数超时

    如果在函数中没有调用callback,则系统将认为函数没有结束,会等待函数结果直到超时。例如调用下面的函数时将收到超时错误。

    exports.handler = function(event, context, callback) {
      console.log('hello world');
    };            

    调用结果如下。

    {"errorMessage":"Function timed out after 3 seconds"}           

错误处理

对于Node.js运行环境的函数,您可能收到以下两种错误,错误类型记录在返回的HTTP Header字段中的X-Fc-Error-Type。

  • HandledInvocationError:通过callback的第一个参数返回的错误。

    示例:

    执行以下命令调用callback命令。

    exports.handler = function(event, context, callback) {
      callback(new Error('oops'));
    };

    收到的响应如下所示。

    {
      "errorMessage": "oops",
      "errorType": "Error",
      "stackTrace": [
        "at exports.handler (/code/index.js:2:12)"
      ]
    }
  • UnhandledInvocationError:其他错误,包括未捕获异常、超时错误和OOM(Out of memory)错误等。

    当您的函数逻辑未捕获到错误时,函数计算系统会尽可能捕获错误,并返回具体的信息。当遇到系统无法捕获的错误时,例如您的函数在运行过程中突然崩溃退出,系统会返回一个通用的错误信息。

    exports.handler = function(event, context, callback) {
      throw new Error('oops');
    };

    收到的响应如下所示。

    {"errorMessage":"Process exited unexpectedly before completing request"}

更多错误类型相关信息,请参见错误处理

使用内置模块

除了Node.js的标准模块,函数计算的Node.js运行环境中还包含了一些常用模块,您可以直接引用这些常用模块,目前函数计算包含的常见模块如下所示。

模块名称 版本 模块介绍
co 4.6.0 控制流
gm 1.23.0 图片处理库
ali-oss 4.10.1 OSS SDK
ali-mns 2.6.5 MNS SDK
tablestore 4.2.0 OTS SDK
aliyun-sdk 1.10.12 Aliyun SDK
@alicloud/fc2 2.1.0 函数计算SDK
opencv 6.0.0 视觉算法库
https://help.aliyun.com/document_detail/body 5.1.0 HTTP https://help.aliyun.com/document_detail/body解析库
raw-https://help.aliyun.com/document_detail/body 2.3.2 HTTP https://help.aliyun.com/document_detail/body解析库

访问OSS的示例代码如下所示。

var gm = require('gm').subClass({ imageMagick: true });
exports.handler = function (event, context, callback) {
    gm(event)
        .flip()
        .toBuffer('PNG', function (err, buffer) {
            if (err) return callback(err);
            callback(null, buffer); 
       });
};
说明 上面的函数直接使用event作为图片的二进制数据,并且直接把生成的图片作为二进制数据返回。

使用自定义模块

如果您需要使用自定义模块,则需要将您的自定义模块与代码一起打包上传。您可以通过以下方式进行依赖管理。

注意 如果您是在本地打包自定义模块,需要上传node_modules。
  • 方法一:使用npm包管理器进行依赖管理。

    本文以安装MySQL数据库为例进行详细介绍。

    1. 执行以下命令建立一个目录,用于存放代码和依赖模块。
      mkdir /tmp/code
    2. 执行以下命令在/tmp/code目录下安装依赖。
      cd /tmp/code
      npm install mysql
    3. 新建代码文件,例如/tmp/code/index.js,在代码中使用mysql。
      var mysql = require('mysql');
      var connection;
      // exports.initializer: initializer function
      exports.initializer = function (context, callback) {
        connection = mysql.createConnection({
          host: 'localhost',
          user: '***',
          password: '*******',
          database: 'my_db'
        });
        connection.connect();
      }
      
      exports.handler = function (event, context, callback) {
        connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
          if (error) return callback(error);
          console.log('The solution is: ', results[0].solution);
          callback(null, results[0].solution);
        });
        connection.end();
      };
    4. 打包上传。

      打包时,需要针对文件进行打包,而不是针对代码整体目录进行打包。打包完成后,入口函数文件需要位于包内的根目录。

      • 在Windows下打包时,可以进入函数代码目录,全选所有文件以后,单击鼠标右键,选择压缩为zip包,生成代码包。
      • 在Linux下打包时,通过调用zip命令时,将源文件指定为代码目录下的所有文件,实现生成部署代码包,例如zip code.zip /home/code/*

      打包后,在函数计算控制台指定函数中的代码执行页面,您可以选择OSS上传或者代码包上传方式上传代码包。

  • 方法二:使用fun install安装依赖。

    如果您使用Funcraft部署应用,可以使用fun install命令来安装依赖。更多信息,请参见使用fun install安装第三方依赖。本文以安装mysql库为例进行详细介绍。

    1. 执行以下命令在函数根目录下初始化Funfile文件。
      fun install init
      ? Select a runtime
        nodejs6 
      ? nodejs8
        nodejs10
        nodejs12
        python2.7
        python3
        java8
        php7.2
       (Move up and down to reveal more choices)
    2. 选择语言nodejs8,在当前目录生成一个名为Funfile的文件,编辑文件内容。
      RUNTIME nodejs8
      RUN npm install mysql
    3. 新建template.yml文件。更多信息,请参见template.yml。例如/tmp/code/template.yml,示例如下。
      ROSTemplateFormatVersion: '2015-09-01'
      Transform: 'Aliyun::Serverless-2018-04-03'
      Resources:
        FunDemo: 
          Type: 'Aliyun::Serverless::Service'
          nodejsdemo: 
            Type: 'Aliyun::Serverless::Function'
            Properties:
              Handler: index.handler
              Runtime: nodejs8
              Initializer: index.initializer
              CodeUri: './'           

      该template.yml文件含义如下:声明名为FunDemo的服务,在这个服务下,声明一个名为nodejsdemo的函数,配置函数入口为index.handler,Initializer为index.initializer函数的runtime为nodejs8。并指定CodeUri为当前目录。更多信息,请参见CodeUri。在部署时,Funcraft会将CodeUri指定目录下的内容打包上传。

      更多的配置规则,请参见Serverless Application Model

    4. 在函数根目录下执行fun install命令安装依赖。
      fun install

      返回结果如下。

      using template: template.yml
      start installing function dependencies without docker
      
      building FunDemo/nodejsdemo
      Funfile exist, Fun will use container to build forcely
      Step 1/2 : FROM registry.cn-beijing.aliyuncs.com/aliyunfc/runtime-nodejs8:build-1.9.4
      ---> 8c6c75614064
      Step 2/2 : RUN npm install mysql
      ---> Using cache
      ---> 715f4427faf9
      sha256:715f4427faf9b66238328e44736078f2ac4bbc0d403e441a6c460b6cc2f405ae
      Successfully built 715f4427faf9
      Successfully tagged fun-cache-58fa61db-dd07-416b-aa47-a6ee752ec6d7:latest
      copying function artifact to /Users/txd123/Desktop/express
      
      Install Success
      
      
      Tips for next step
      ======================
      * Invoke Event Function: fun local invoke
      * Invoke Http Function: fun local start
      * Build Http Function: fun build
      * Deploy Resources: fun deploy
                                  
    5. 使用Funcraft工具部署。
      fun deploy           

      返回结果如下。

      using region: cn-hangzhou
      using accountId: ***********3557
      using accessKeyId: ***********r3Ra
      using timeout: 300
      
      Waiting for service FunDemo to be deployed...
          Waiting for function nodejsdemo to be deployed...
              Waiting for packaging function nodejs code...
              package function nodejs code done
          function nodejsdemo deploy success
      service FunDemo deploy success
                                  

    登录函数计算控制台,即可看到相关的服务、函数被创建成功,且触发执行可以返回正确的结果。

调用外部命令

您的函数可能会用到一些工具,而这些工具并不是用Node.js写的。例如,Shell、C++或者Go编译出来的可执行文件。您仍然可以将这些工具与代码一起打包,然后在函数中通过运行外部命令的方法来使用它们。以下代码演示了如何运行一个Shell脚本。

var exec = require('child_process');

exports.handler = function(event, context, callback) {
  var scriptPath = process.env['FC_FUNC_CODE_PATH'] + '/script.sh';
  exec.exec('bash '+scriptPath, {}, function(err, stdout, stderr) {
    if (err) return callback(err);
    console.log(stdout, stderr);
    callback(null, stdout);
  });
};
            
说明 使用C、C++、Go编译出来的可执行文件,需要与函数计算的运行环境兼容。函数计算的Node.js运行环境如下所示。
  • Linux内核版本:Linux 4.4.24-2.al7.x86_64。
  • Docker基础镜像:docker pull node:6.10。