Skip to content

Commit

Permalink
fix(runtime): impv runtime logging struct; add func logs api (#538)
Browse files Browse the repository at this point in the history
Signed-off-by: maslow <wangfugen@126.com>
  • Loading branch information
maslow authored Dec 14, 2022
1 parent b08b4e2 commit c9d2292
Show file tree
Hide file tree
Showing 18 changed files with 302 additions and 191 deletions.
115 changes: 58 additions & 57 deletions runtimes/nodejs/src/cloud-sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,69 @@
import { AxiosStatic } from "axios";
import { Db, getDb } from "database-proxy";
import { CloudFunction, FunctionContext } from "../support/function-engine";
import * as mongodb from "mongodb";
import { DatabaseAgent } from "../db";
import request from "axios";
import { SchedulerInstance } from "../support/scheduler";
import { getToken, parseToken } from "../support/token";
import { WebSocket } from "ws";
import { WebSocketAgent } from "../support/ws";
import Config from "../config";
import { AxiosStatic } from 'axios'
import { Db, getDb } from 'database-proxy'
import { CloudFunction, FunctionContext } from '../support/function-engine'
import * as mongodb from 'mongodb'
import { DatabaseAgent } from '../db'
import request from 'axios'
import { SchedulerInstance } from '../support/scheduler'
import { getToken, parseToken } from '../support/token'
import { WebSocket } from 'ws'
import { WebSocketAgent } from '../support/ws'
import Config from '../config'

export type InvokeFunctionType = (
name: string,
param: FunctionContext
) => Promise<any>;
export type EmitFunctionType = (event: string, param: any) => void;
export type GetTokenFunctionType = (payload: any, secret?: string) => string;
param: FunctionContext,
) => Promise<any>
export type EmitFunctionType = (event: string, param: any) => void
export type GetTokenFunctionType = (payload: any, secret?: string) => string
export type ParseTokenFunctionType = (
token: string,
secret?: string
) => any | null;
secret?: string,
) => any | null

export interface MongoDriverObject {
client: mongodb.MongoClient;
db: mongodb.Db;
client: mongodb.MongoClient
db: mongodb.Db
}

export interface CloudSdkInterface {
/**
* Sending an HTTP request is actually an Axios instance. You can refer to the Axios documentation directly
*/
fetch: AxiosStatic;
fetch: AxiosStatic

/**
* Get a laf.js database-ql instance
*/
database(): Db;
database(): Db

/**
* Invoke cloud function
*/
invoke: InvokeFunctionType;
invoke: InvokeFunctionType

/**
* Emit a cloud function event that other cloud functions can set triggers to listen for
*/
emit: EmitFunctionType;
emit: EmitFunctionType

/**
* Cloud function global memory `shared` object, which can share data across multiple requests and different cloud functions
* 1. Some global configurations can be initialized into `shared`, such as 3rd-party API configuration
* 2. You can share some common methods, such as checkPermission(), to improve the performance of cloud functions
* 3. It can cache hot data and is recommended to use it in a small amount (this object is allocated in the node VM heap because of the memory limit of the node VM heap)
*/
shared: Map<string, any>;
shared: Map<string, any>

/**
* Generate a JWT Token, if don't provide `secret` fields, use current server secret key to do signature
*/
getToken: GetTokenFunctionType;
getToken: GetTokenFunctionType

/**
* Parse a JWT Token, if don't provide `secret` fields, use current server secret key to verify signature
*/
parseToken: ParseTokenFunctionType;
parseToken: ParseTokenFunctionType

/**
* The mongodb instance of MongoDB node.js native driver.
Expand Down Expand Up @@ -93,44 +93,44 @@ export interface CloudSdkInterface {
* .toArray()
* ```
*/
mongo: MongoDriverObject;
mongo: MongoDriverObject

/**
* Websocket connection list
*/
sockets: Set<WebSocket>;
sockets: Set<WebSocket>

/**
* Current app id
*/
appid: string;
appid: string

env: {
DB_URI?: string;
SERVER_SECRET?: string;
APP_ID?: string;
OSS_ACCESS_KEY?: string;
OSS_ACCESS_SECRET?: string;
OSS_REGION?: string;
OSS_INTERNAL_ENDPOINT?: string;
OSS_EXTERNAL_ENDPOINT?: string;
NPM_INSTALL_FLAGS?: string;
RUNTIME_IMAGE?: string;
};
DB_URI?: string
SERVER_SECRET?: string
APP_ID?: string
OSS_ACCESS_KEY?: string
OSS_ACCESS_SECRET?: string
OSS_REGION?: string
OSS_INTERNAL_ENDPOINT?: string
OSS_EXTERNAL_ENDPOINT?: string
NPM_INSTALL_FLAGS?: string
RUNTIME_IMAGE?: string
}
}

/**
* Cloud SDK instance
*/
const cloud: CloudSdkInterface = create();
const cloud: CloudSdkInterface = create()

/**
* After the database connection is successful, update its Mongo object, otherwise it is null
*/
DatabaseAgent.accessor.ready.then(() => {
cloud.mongo.client = DatabaseAgent.accessor.conn;
cloud.mongo.db = DatabaseAgent.accessor.db;
});
cloud.mongo.client = DatabaseAgent.accessor.conn
cloud.mongo.db = DatabaseAgent.accessor.db
})

/**
* Create a new Cloud SDK instance
Expand Down Expand Up @@ -161,14 +161,14 @@ export function create() {
OSS_REGION: process.env.OSS_REGION,
OSS_INTERNAL_ENDPOINT: process.env.OSS_INTERNAL_ENDPOINT,
OSS_EXTERNAL_ENDPOINT: process.env.OSS_EXTERNAL_ENDPOINT,
NPM_INSTALL_FLAGS: process.env.NPM_INSTALL_FLAGS || "",
NPM_INSTALL_FLAGS: process.env.NPM_INSTALL_FLAGS || '',
RUNTIME_IMAGE: process.env.RUNTIME_IMAGE,
},
};
return cloud;
}
return cloud
}

export default cloud;
export default cloud

/**
* The cloud function is invoked in the cloud function, which runs in the cloud function.
Expand All @@ -178,24 +178,25 @@ export default cloud;
* @returns
*/
async function invokeInFunction(name: string, param?: FunctionContext) {
const data = await CloudFunction.getFunctionByName(name);
const func = new CloudFunction(data);
const data = await CloudFunction.getFunctionByName(name)
const func = new CloudFunction(data)

if (!func) {
throw new Error(`invoke() failed to get function: ${name}`);
throw new Error(`invoke() failed to get function: ${name}`)
}

param = param ?? {};
param = param ?? {} as any
param.__function_name = name

param.requestId = param.requestId ?? "invoke";
param.requestId = param.requestId ?? 'invoke'

param.method = param.method ?? "call";
param.method = param.method ?? 'call'

const result = await func.invoke(param);
const result = await func.invoke(param)

if (result.error) {
throw result.error;
throw result.error
}

return result.data;
return result.data
}
7 changes: 4 additions & 3 deletions runtimes/nodejs/src/handler/db-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
* @LastEditTime: 2022-02-03 00:39:18
* @Description:
*/
import { Request, Response } from 'express'
import { Response } from 'express'
import { Proxy } from 'database-proxy'
import Config from '../config'
import { DatabaseAgent } from '../db'
import { logger } from '../support/logger'
import { PolicyAgent } from '../support/policy'
import { IRequest } from '../support/types'


export async function handleDatabaseProxy(req: Request, res: Response) {
export async function handleDatabaseProxy(req: IRequest, res: Response) {
const accessor = DatabaseAgent.accessor

const requestId = req['requestId']
const auth = req['auth'] ?? {}
const auth = req.user || {}
const policy_name = req.params?.policy

// get corresponding policy
Expand Down
13 changes: 8 additions & 5 deletions runtimes/nodejs/src/handler/debug-func.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
* @Description:
*/

import { Request, Response } from 'express'
import { Response } from 'express'
import { FunctionContext } from '../support/function-engine'
import { logger } from '../support/logger'
import { CloudFunction } from '../support/function-engine'
import { IRequest } from '../support/types'

/**
* Handler of debugging cloud function
*/
export async function handleDebugFunction(req: Request, res: Response) {
export async function handleDebugFunction(req: IRequest, res: Response) {

const requestId = req['requestId']
const func_name = req.params?.name
Expand All @@ -25,7 +26,7 @@ export async function handleDebugFunction(req: Request, res: Response) {
}

// verify the debug token
const auth = req['auth']
const auth = req.user
if (!auth || auth.type !== 'debug') {
return res.status(403).send('permission denied: invalid debug token')
}
Expand All @@ -40,9 +41,11 @@ export async function handleDebugFunction(req: Request, res: Response) {
body: param,
headers: req.headers,
method: req.method,
auth: req['auth'],
auth: req.user,
user: req.user,
requestId,
response: res
response: res,
__function_name: func.name,
}
const result = await func.invoke(ctx)

Expand Down
57 changes: 30 additions & 27 deletions runtimes/nodejs/src/handler/invoke-func.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,40 @@
* @Description:
*/

import { Request, Response } from "express";
import { FunctionContext } from "../support/function-engine";
import { logger } from "../support/logger";
import { CloudFunction } from "../support/function-engine";
import { Response } from 'express'
import { FunctionContext } from '../support/function-engine'
import { logger } from '../support/logger'
import { CloudFunction } from '../support/function-engine'
import { IRequest } from '../support/types'

const DEFAULT_FUNCTION_NAME = "__default__";
const DEFAULT_FUNCTION_NAME = '__default__'

/**
* Handler of invoking cloud function
*/
export async function handleInvokeFunction(req: Request, res: Response) {
const requestId = req["requestId"];
const func_name = req.params?.name;
export async function handleInvokeFunction(req: IRequest, res: Response) {
const requestId = req['requestId']
const func_name = req.params?.name

// load function data from db
let funcData = await CloudFunction.getFunctionByName(func_name);
let funcData = await CloudFunction.getFunctionByName(func_name)
if (!funcData) {
if (func_name === "healthz") {
return res.status(200).send("ok");
if (func_name === 'healthz') {
return res.status(200).send('ok')
}

// load default function from db
funcData = await CloudFunction.getFunctionByName(DEFAULT_FUNCTION_NAME);
funcData = await CloudFunction.getFunctionByName(DEFAULT_FUNCTION_NAME)
if (!funcData) {
return res.status(404).send("Function Not Found");
return res.status(404).send('Function Not Found')
}
}

const func = new CloudFunction(funcData);
const func = new CloudFunction(funcData)

// reject while no HTTP enabled
if (!func.methods.includes(req.method.toUpperCase())) {
return res.status(405).send("Method Not Allowed");
return res.status(405).send('Method Not Allowed')
}

try {
Expand All @@ -48,38 +49,40 @@ export async function handleInvokeFunction(req: Request, res: Response) {
body: req.body,
headers: req.headers,
method: req.method,
auth: req["auth"],
auth: req['auth'],
user: req.user,
requestId,
request: req,
response: res,
};
const result = await func.invoke(ctx);
__function_name: func.name,
}
const result = await func.invoke(ctx)

if (result.error) {
logger.error(
requestId,
`invoke function ${func_name} invoke error: `,
result
);
result,
)

return res.status(400).send({
error:
"invoke cloud function got error, please check the function logs",
'invoke cloud function got error, please check the function logs',
requestId,
});
})
}

logger.trace(
requestId,
`invoke function ${func_name} invoke success: `,
result
);
result,
)

if (res.writableEnded === false) {
return res.send(result.data);
return res.send(result.data)
}
} catch (error) {
logger.error(requestId, "failed to invoke error", error);
return res.status(500).send("Internal Server Error");
logger.error(requestId, 'failed to invoke error', error)
return res.status(500).send('Internal Server Error')
}
}
Loading

0 comments on commit c9d2292

Please sign in to comment.