文档管理中心
我的

Custom Runtime

入口方法

入口方法定义如下所示:

  • Node.js 20.x
    收起
    自动换行
    深色代码主题
    复制
    1. app.post('/invoke', function (req, res)

    完整的云函数示例代码请参考函数示例-Node.js 20.x。

  • Java 1.8
    收起
    自动换行
    深色代码主题
    复制
    1. @RequestMapping(path = "/invoke", method = RequestMethod.POST)

    完整的云函数示例代码请参考函数示例-Java 1.8。

  • Python 3.11
    收起
    自动换行
    深色代码主题
    复制
    1. @app.route("/invoke", methods=["POST"])

    完整的云函数示例代码请参考函数示例-Python 3.11。

说明

函数收到的所有触发事件均会作为请求体被转发到/invoke路径下。请求体的格式请参考HTTP触发器的event对象。

日志记录

  • 日志输出路径:/opt/huawei/logs/
  • 日志固定输出格式:
    <pattern>{"M":"%replace(%t [%c] - %m){'\"','\\\"'}","T":"%d{yyyy-MM-dd HH:mm:ss.SSS,GMT}","L":"%level","LINE":"%file:%L","TENANTID":"${FAAS_FUNCTION_TENANTID}","FUNCTION":"${FAAS_FUNCTION_NAME}","VERSION":"${FAAS_FUNCTION_VERSION}","BUSINESSID":"${FAAS_FUNCTION_BUSINESS}","X-Trace-ID":"%X{x-trace-id}"}%n</pattern>;
    说明

    其中“x-trace-id”可由用户根据实际情况填写,作为记录一条请求执行的标记,比如:MDC.put("x-trace-id", "xxx");

  • 日志输出大小:<2GB
  • 日志输出内容:平台无限制,但请注意敏感信息的处理。输出内容建议为标志性信息,方便筛选。
  • 日志配置指导:
    • Python项目
      配置日志工具类RuntimeRunLog,详情请参考函数示例-Python 3.11。
      收起
      自动换行
      深色代码主题
      复制
      1. import logging
      2. import logging.handlers
      3. LOG_DEFAULT_MAX_BYTES = 50 * 1024 * 1024
      4. LOG_DEFAULT_BACKUP_COUNT = 3
      5. from _rotating_file_handler import RotatingFileHandler
      6. class RuntimeRunLog:
      7. def __init__(self):
      8. self.runtime_run_log = logging.getLogger('runtime-python-run')
      9. formatter = logging.Formatter(fmt='{"T": "%(asctime)s.%(msecs)03d","L": "%(levelname)s","M": "%(message)s"}',
      10. datefmt='%Y-%m-%d %H:%M:%S')
      11. log_file_name = '/opt/huawei/logs/runtime-python-run.log'
      12. handler = RotatingFileHandler(file_name=log_file_name, mode='a', max_bytes=LOG_DEFAULT_MAX_BYTES,
      13. backup_count=LOG_DEFAULT_BACKUP_COUNT, log_name="runtime-python-run")
      14. handler.setLevel(logging.INFO)
      15. handler.setFormatter(formatter)
      16. self.runtime_run_log.addHandler(handler)
      17. self.runtime_run_log.setLevel(logging.INFO)
      18. runtime_run_log = RuntimeRunLog().runtime_run_log
    • Node.js项目

      配置日志工具函数logConfig,详情请参考函数示例-Node.js 20.x。

    • Java项目

      引入slf4j依赖,并在项目“/resources”目录下添加slf4j的配置文件logback.xml,详情请参考函数示例-Java 1.8。

      说明

      若您已引用云函数Server SDK,则无需再引入slf4j依赖。若未引用,需要您手动引入slf4j依赖,示例如下:

      收起
      自动换行
      深色代码主题
      复制
      1. <dependency>
      2. <groupId>org.slf4j</groupId>
      3. <artifactId>slf4j-api</artifactId>
      4. <version>2.0.7</version>
      5. </dependency>

      logback.xml文件示例:

      收起
      自动换行
      深色代码主题
      复制
      1. <?xml version="1.0" encoding="UTF-8"?>
      2. <!-- Please refer to : https://logback.qos.ch/manual/configuration.html -->
      3. <!-- The 'configuration' parameter is as follows: -->
      4. <!-- scan: true means automatically reloading configuration file upon modification -->
      5. <!-- scanPeriod: automatically reloading check period -->
      6. <!-- debug: print the debug info or not, type: boolean (true/false) -->
      7. <configuration scan="true" scanPeriod="120 seconds" debug="false">
      8. <!-- The scope of property can be "context, local, system" -->
      9. <property scope="context" name="LOG_HOME" value="../log"/>
      10. <property scope="context" name="CONTEXT_NAME" value="CloudSOALoggingContext"/>
      11. <contextName>${CONTEXT_NAME}</contextName>
      12. <!-- Run log -->
      13. <appender name="run_rolling_appender" class="ch.qos.logback.core.rolling.RollingFileAppender">
      14. <encoder>
      15. <pattern>{"M":"%m","T":"%d{yyyy-MM-dd HH:mm:ss.SSS}","L":"%level","LINE":"%file:%L","TENANTID":"${FAAS_FUNCTION_TENANTID}","FUNCTION":"${FAAS_FUNCTION_NAME}","VERSION":"${FAAS_FUNCTION_VERSION}","BUSINESSID":"${FAAS_FUNCTION_BUSINESS}","X-Trace-ID":"%X{x-trace-id}"}%n</pattern>
      16. </encoder>
      17. <file>/opt/huawei/logs/json-user-function.log</file>
      18. <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      19. <FileNamePattern>/opt/huawei/logs/json-user-function.log_%i.log</FileNamePattern>
      20. <minIndex>1</minIndex>
      21. <maxIndex>50</maxIndex>
      22. </rollingPolicy>
      23. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      24. <maxFileSize>50MB</maxFileSize>
      25. </triggeringPolicy>
      26. </appender>
      27. <appender name="run_appender_thread" class="ch.qos.logback.classic.AsyncAppender">
      28. <queueSize>1024</queueSize>
      29. <discardingThreshold>10</discardingThreshold>
      30. <appender-ref ref="run_rolling_appender"/>
      31. <includeCallerData>true</includeCallerData>
      32. </appender>
      33. <!-- Log configuration specification -->
      34. <!-- level can be: OFF ERROR WARN INFO DEBUG TRACE ALL -->
      35. <!-- additivity: should be false as we will not use this feature of LOGBACK -->
      36. <logger name="com.huawei.faas" level="INFO" additivity="false">
      37. <appender-ref ref="run_appender_thread"/>
      38. </logger>
      39. <root level="INFO">
      40. <appender-ref ref="run_appender_thread"/>
      41. </root>
      42. </configuration>
  • 日志查看:云监控提供的日志服务功能可实时呈现和查询云函数的业务日志(即run日志)和接入日志(即access日志)两种日志类型,当函数日志输出到固定路径时,您即可在云监控的日志服务页面查看云函数日志,详情请参考日志服务。
    说明

    云函数接入日志由系统自动生成,输出路径为:/home/wisefunction/runtime-log/runtime-interface.log。

获取环境变量

Custom Runtime运行环境下,获取环境变量的方法,与开发者上传的工程项目里使用的编程语言所对应的获取环境变量的方法有关。

示例如下:

  • Node.js 20.x
    收起
    自动换行
    深色代码主题
    复制
    1. // 解析环境变量env1
    2. let testEnv = process.env.env1;
  • Java 1.8
    收起
    自动换行
    深色代码主题
    复制
    1. // 解析环境变量env1
    2. String env1 = System.getenv("env1");
  • Python 3.11
    收起
    自动换行
    深色代码主题
    复制
    1. # 解析环境变量env1
    2. env = os.environ['env1']

异常处理

根据自定义运行时的内置运行环境,使用对应语言的异常处理方式即可。

函数示例

Node.js 20.x

收起
自动换行
深色代码主题
复制
  1. /**
  2. * Describe the basic method of Cloud Functions
  3. */
  4. const express = require('express');
  5. const log4js = require("log4js");
  6. const app = express();
  7. let runJsonLogger = logEvent => {
  8. let logInfo = {
  9. M: logEvent.data[0] || "",
  10. T: logEvent.startTime,
  11. L: logEvent.level.levelStr,
  12. TRACE: logEvent.data[1], // logger若有第二个参数,需要设置为traceid
  13. };
  14. return JSON.stringify(logInfo);
  15. };
  16. log4js.addLayout("runjson", () => {
  17. return runJsonLogger;
  18. });
  19. let logConfig = {
  20. replaceConsole: true,
  21. appenders: {
  22. 'out': {
  23. type: 'stdout',
  24. layout: {
  25. type: "colored"
  26. }
  27. },
  28. 'files': {
  29. type: 'file',
  30. filename: '/opt/huawei/logs/logs.log',
  31. layout: {
  32. "type": "runjson",
  33. "pattern": "%x{showDate}|%m"
  34. }
  35. }
  36. },
  37. categories: {
  38. default: {
  39. appenders: ['out', 'files'],
  40. level: "INFO"
  41. }
  42. },
  43. disableClustering: true
  44. }
  45. log4js.configure(logConfig);
  46. const logger = log4js.getLogger('CUSTOM-RUNTIME-NODEJS-DEMO-LOG');
  47. app.post('/invoke', function (req, res) {
  48. // example of display environment variables
  49. let env1 = process.env.env1;
  50. // example of display logs
  51. logger.info("Test info log");
  52. logger.warn("Test warn log");
  53. logger.debug("Test debug log");
  54. logger.error("Test error log");
  55. logger.info("--------Start-------");
  56. try {
  57. let startTime = new Date().getTime();
  58. let endTime = startTime;
  59. let interval = 0;
  60. startTime = process.uptime() * 1000;
  61. // print input parameters and environment variables
  62. logger.info("req: " + req.query);
  63. logger.info("env1: " + env1);
  64. endTime = process.uptime() * 1000;
  65. interval = endTime - startTime;
  66. logger.info("intervalTime: " + interval);
  67. logger.info("--------Finished-------");
  68. let result = {"intervalTime": interval};
  69. res.send(JSON.stringify(result));
  70. } catch (error) {
  71. logger.error("--------Error-------");
  72. logger.error("error: " + error);
  73. res.send(error);
  74. }
  75. });
  76. app.listen(9000, function () {
  77. logger.info("---------server start now!-------");
  78. });

Java 1.8

收起
自动换行
深色代码主题
复制
  1. package com.example.demo;
  2. import com.alibaba.fastjson.JSONObject;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import org.springframework.boot.SpringApplication;
  6. import org.springframework.boot.autoconfigure.SpringBootApplication;
  7. import org.springframework.web.bind.annotation.RequestBody;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RequestMethod;
  10. import org.springframework.web.bind.annotation.RestController;
  11. import javax.servlet.http.HttpServletRequest;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. @SpringBootApplication
  15. @RestController
  16. public class Application {
  17. private static final Logger LOG = LoggerFactory.getLogger("com.huawei.customedemo");
  18. public static void main(String[] args) {
  19. LOG.info("start spring server now!");
  20. try {
  21. ClasspathLoader.loadClassPath();
  22. } catch (MyException e) {
  23. LOG.error("failed to load layer " + e.getMessage());
  24. }
  25. SpringApplication.run(Application.class, args);
  26. }
  27. @RequestMapping(path = "/invoke", method = RequestMethod.POST)
  28. public Object postSimple(HttpServletRequest req, @RequestBody JSONObject body) {
  29. // example of display environment variables
  30. String env = System.getenv("env1");
  31. // example of display logs
  32. LOG.info("Test info log");
  33. LOG.warn("Test warn log");
  34. LOG.debug("Test debug log");
  35. LOG.error("Test error log");
  36. LOG.info("----------Start-----------");
  37. JSONObject result = new JSONObject();
  38. result.put("code", 0);
  39. try {
  40. long interval;
  41. long startTime = System.currentTimeMillis();
  42. LOG.info("receive post request");
  43. String query=req.getQueryString();
  44. String hwm=req.getHeader("hwm");
  45. Map<String,String[]> params =req.getParameterMap();
  46. String config=PropertyUtil.getProperty("key.rootkey.path");
  47. LOG.info("query :"+query);
  48. LOG.info("headers :"+hwm);
  49. LOG.info("params :"+JSONObject.toJSONString(params));
  50. LOG.info("body :"+body.toJSONString());
  51. LOG.info("config :"+config);
  52. long endTime = System.currentTimeMillis();
  53. interval = endTime - startTime;
  54. LOG.info("intervalTime: " + interval + "(ms)");
  55. result.put("intervalTime: ", interval + "(ms)");
  56. LOG.info("--------Finished---------");
  57. } catch (Exception e) {
  58. LOG.error("the ex is " + e.getMessage());
  59. result.put("msg", e.getMessage());
  60. }
  61. return result ;
  62. }
  63. }

Python 3.11

收起
自动换行
深色代码主题
复制
  1. """
  2. Describe the basic method of Cloud Functions
  3. """
  4. import os
  5. import sys
  6. import time
  7. from run import runtime_run_log as run_log
  8. sys.path.append('python_modules')
  9. from flask import Flask, request
  10. app = Flask(__name__)
  11. @app.route("/invoke", methods=["POST"])
  12. def simple_fn():
  13. try:
  14. # example of display environment variables
  15. env1 = os.environ.get("env1")
  16. # example of display logs
  17. run_log.info("Test info log")
  18. run_log.debug("Test debug log")
  19. run_log.warning("Test warning log")
  20. run_log.error("Test error log")
  21. run_log.info("-------------Start--------------")
  22. startTime = int(time.time())
  23. # print input parameters and environment variables
  24. query = request.query_string.decode('utf-8')
  25. run_log.info("request: " + repr(query))
  26. headers = request.headers
  27. run_log.info("headers: " + str(headers))
  28. body = request.data.decode('utf-8')
  29. run_log.info("body: " + str(body))
  30. run_log.info("env1: " + env1)
  31. endTime = int(time.time())
  32. interval = endTime - startTime
  33. run_log.info("intervalTime: " + str(interval))
  34. run_log.info("-------------Finished--------------")
  35. return request.method + " success"
  36. except Exception as error:
  37. run_log.error("error: " + format(str(error)))
  38. return request.method + format(str(error))
  39. if __name__ == '__main__':
  40. app.run(port=9000)

准备函数部署包

Custom Runtime函数部署包结构如下所示,bootstrap可执行文件必须在zip包根目录下,其他相关代码和依赖项可以自由放置。

收起
自动换行
深色代码主题
复制
  1. function.zip
  2. |---bootstrap //可执行文件,启动Server
  3. |---WiseFunctionCustomDemo.jar
  4. |---其他相关代码和依赖等

bootstrap文件内容示例:

收起
自动换行
深色代码主题
复制
  1. #!/bin/bash
  2. java -jar WiseFunctionCustomDemo.jar

Custom Runtime自定义运行环境支持Node.js、Java、Python三种语言类型,不同语言的版本约束和函数部署包示例如下:

Node.js 20.x:custom-runtime(nodejs).zip

Java 1.8:custom-runtime(java).zip

Python 3.11:custom-runtime(python).zip