Skip to content

Commit

Permalink
feat(runtime): refactor log console & enable module cache (#1618)
Browse files Browse the repository at this point in the history
* feat: refactor log console

* feat(runtime): opt console

* fix: fix some problem

* fix: opt cache remove

* fix: default enable module cache

* feat: console add function name

* fix: fix runtime log format & websocket cache

---------

Co-authored-by: maslow <[email protected]>
  • Loading branch information
skyoct and maslow authored Nov 1, 2023
1 parent 779fae9 commit 8d95fff
Show file tree
Hide file tree
Showing 16 changed files with 116 additions and 119 deletions.
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
},
"dependencies": {
"commander": "^9.2.0",
"dayjs": "^1.11.10",
"dotenv": "^10.0.0"
}
}
8 changes: 4 additions & 4 deletions runtimes/nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ cd runtimes/nodejs
# connect the cluster if not connected
telepresence connect

export APPID=your-app-id
export appid=your-app-id

# proxy app cluster traffic to local, replace `APPID` with your prepared appid
telepresence intercept $APPID -n $APPID -p 8000:8000 -e $(pwd)/.env
telepresence intercept $appid -n laf-runtime -p 8000:8000 -e $(pwd)/.env

# after intercept command, you can use following command to check if intercept active
telepresence list -n $APPID
telepresence list -n $appid

# Start local service first, required nodejs version >= 18.0.0
npm install
Expand All @@ -50,5 +50,5 @@ npm start
> Clean up
```bash
telepresence leave $APPID-$APPID
telepresence leave $appid-laf-runtime
```
2 changes: 1 addition & 1 deletion runtimes/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,4 @@
],
"delay": 1000
}
}
}
4 changes: 2 additions & 2 deletions runtimes/nodejs/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export default class Config {
return process.env.OSS_EXTERNAL_ENDPOINT
}

static get ENABLE_MODULE_CACHE(): boolean {
return process.env.ENABLE_MODULE_CACHE === 'true' || false
static get DISABLE_MODULE_CACHE(): boolean {
return process.env.DISABLE_MODULE_CACHE === 'true'
}
}
19 changes: 12 additions & 7 deletions runtimes/nodejs/src/handler/invoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { parseToken } from '../support/token'
import { logger } from '../support/logger'
import {
CloudFunction,
DebugConsole,
FunctionCache,
FunctionContext,
ICloudFunctionData,
Expand Down Expand Up @@ -84,12 +85,10 @@ async function invokeFunction(
result,
)

ctx.response
.status(400)
.send({
error: `invoke ${ctx.__function_name} function got error, please check the function logs`,
requestId,
})
ctx.response.status(400).send({
error: `invoke ${ctx.__function_name} function got error, please check the function logs`,
requestId,
})
return false
}

Expand Down Expand Up @@ -168,10 +167,16 @@ async function invokeDebug(

const func = new CloudFunction(funcData)

const debugConsole = new DebugConsole(funcName)

try {
// execute the func
ctx.__function_name = funcName
const result = await func.execute(ctx, useInterceptor)
const result = await func.execute(ctx, useInterceptor, debugConsole)

// set logs to response header
ctx.response.set('x-laf-func-logs', debugConsole.getLogs())
ctx.response.set('x-laf-func-time-usage', result.time_usage.toString())

if (result.error) {
logger.error(requestId, `debug function ${funcName} error: `, result)
Expand Down
13 changes: 0 additions & 13 deletions runtimes/nodejs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,6 @@ app.use(function (req, res, next) {

const requestId = (req['requestId'] =
req.headers['x-request-id'] || generateUUID())
if (req.url !== '/_/healthz') {
logger.info(
requestId,
`${req.method} "${req.url}" - referer: ${
req.get('referer') || '-'
} ${req.get('user-agent')}`,
)
logger.trace(requestId, `${req.method} ${req.url}`, {
body: req.body,
headers: req.headers,
auth,
})
}
res.set('request-id', requestId)
next()
})
Expand Down
14 changes: 8 additions & 6 deletions runtimes/nodejs/src/storage-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import cors from 'cors'

const app = express()

app.use(cors({
origin: true,
methods: '*',
exposedHeaders: '*',
credentials: true,
}))
app.use(
cors({
origin: true,
methods: '*',
exposedHeaders: '*',
credentials: true,
}),
)

const tryPath = (bucket: string, path: string) => {
const testPaths = path.endsWith('/')
Expand Down
5 changes: 3 additions & 2 deletions runtimes/nodejs/src/support/engine/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CLOUD_FUNCTION_COLLECTION } from '../../constants'
import { InitHook } from '../init-hook'
import { DatabaseChangeStream } from '../database-change-stream'
import { FunctionModule } from './module'
import { ChangeStreamDocument } from 'mongodb'

export class FunctionCache {
private static cache: Map<string, ICloudFunctionData> = new Map()
Expand Down Expand Up @@ -36,7 +37,7 @@ export class FunctionCache {
* @param change
* @returns
*/
private static async streamChange(change): Promise<void> {
private static async streamChange(change: ChangeStreamDocument<ICloudFunctionData>): Promise<void> {
if (change.operationType === 'insert') {
const func = await DatabaseAgent.db
.collection<ICloudFunctionData>(CLOUD_FUNCTION_COLLECTION)
Expand All @@ -45,11 +46,11 @@ export class FunctionCache {
// add func in map
FunctionCache.cache.set(func.name, func)
} else if (change.operationType == 'delete') {
FunctionModule.deleteAllCache()
// remove this func
for (const [funcName, func] of this.cache) {
if (change.documentKey._id.equals(func._id)) {
FunctionCache.cache.delete(funcName)
FunctionModule.deleteCache(funcName)
}
}
}
Expand Down
72 changes: 34 additions & 38 deletions runtimes/nodejs/src/support/engine/console.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,23 @@
import * as util from 'util'
import { FunctionContext } from './types'
import Config from '../../config'
import axios from 'axios'
import dayjs from 'dayjs'

export class FunctionConsole {
ctx: FunctionContext
export class Console {
functionName: string

static write(message: string, ctx: FunctionContext) {
if (!Config.LOG_SERVER_URL || !Config.LOG_SERVER_TOKEN) return

const doc = {
request_id: ctx.requestId || '',
func: ctx.__function_name,
is_required: ctx.__is_required || false,
data: message,
created_at: new Date(),
}

axios.post(
`${Config.LOG_SERVER_URL}/function/log`,
{
appid: Config.APPID,
log: doc,
},
{
headers: {
'x-token': Config.LOG_SERVER_TOKEN,
},
},
)
constructor(functionName: string) {
this.functionName = functionName
}

constructor(ctx: FunctionContext) {
this.ctx = ctx
}

private _log(...params: any[]) {
_log(...params: any[]): void {
const now = dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')
const content = params
.map((param) => {
return util.inspect(param, { depth: 30 })
return util.inspect(param, { depth: 1 })
})
.join(' ')

FunctionConsole.write(content, this.ctx)

const data = `[${now}] [${this.functionName}] ${content}`
console.log(data)
}

debug(...params: any[]) {
Expand All @@ -60,8 +35,29 @@ export class FunctionConsole {
warn(...params: any[]) {
this._log(...params)
}
}

error(...params: any[]) {
this._log(...params)
export class DebugConsole extends Console {
constructor(functionName: string) {
super(functionName)
}

private _logs: string[] = []

_log(...params: any[]): void {
const now = dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')
const content = params
.map((param) => {
return util.inspect(param, { depth: 1 })
})
.join(' ')

const data = `[${now}] [${this.functionName}] ${content}`
this._logs.push(data)
console.log(data)
}

getLogs() {
return JSON.stringify(this._logs)
}
}
7 changes: 4 additions & 3 deletions runtimes/nodejs/src/support/engine/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ export class CloudFunction {
async execute(
param: FunctionContext,
useInterceptor: boolean = false,
debugConsole: any = null,
): Promise<FunctionResult> {
const sandbox = buildSandbox(param, [])
const sandbox = buildSandbox(param, [], debugConsole)
let code = ``
if (useInterceptor) {
const interceptorFunc = FunctionCache.get(INTERCEPTOR_FUNCTION_NAME)
Expand Down Expand Up @@ -102,7 +103,7 @@ export class CloudFunction {
const wrapped = `
const require = (module) => {
fromModule.push(__filename)
return requireFunc(module, fromModule, __context__)
return requireFunc(module, fromModule)
}
${code};
const __main__ = exports.main || exports.default
Expand All @@ -122,7 +123,7 @@ export class CloudFunction {
const wrapped = `
const require = (module) => {
fromModule.push(__filename)
return requireFunc(module, fromModule, __context__)
return requireFunc(module, fromModule)
}
function __next__() {
Expand Down
23 changes: 14 additions & 9 deletions runtimes/nodejs/src/support/engine/module.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import { FunctionCache, FunctionContext } from '.'
import Config from '../../config'
import { FunctionCache } from './cache'
import { FunctionContext } from './types'
import { buildSandbox, createScript } from './utils'

export class FunctionModule {
private static cache: Map<string, any> = new Map()

static require(
name: string,
fromModule: string[],
functionContext: FunctionContext,
): any {
static require(name: string, fromModule: string[]): any {
if (name === '@/cloud-sdk') {
return require('@lafjs/cloud')
} else if (name.startsWith('@/')) {
Expand All @@ -28,6 +23,12 @@ export class FunctionModule {
)
}

// build function context
const functionContext: FunctionContext = {
requestId: '',
__function_name: name,
}

// build function module
const data = FunctionCache.get(name)
const functionModule = FunctionModule.build(
Expand All @@ -37,7 +38,7 @@ export class FunctionModule {
)

// cache module
if (Config.ENABLE_MODULE_CACHE) {
if (Config.DISABLE_MODULE_CACHE) {
FunctionModule.cache.set(name, functionModule)
}
return functionModule
Expand Down Expand Up @@ -67,11 +68,15 @@ export class FunctionModule {
FunctionModule.cache.delete(name)
}

static deleteAllCache(): void {
FunctionModule.cache.clear()
}

private static wrap(code: string): string {
return `
const require = (name) => {
fromModule.push(__filename)
return requireFunc(name, fromModule, __context__)
return requireFunc(name, fromModule)
}
const exports = {};
${code}
Expand Down
3 changes: 1 addition & 2 deletions runtimes/nodejs/src/support/engine/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { IncomingHttpHeaders } from 'http'
import { Request, Response } from 'express'
import { ObjectId } from 'mongodb'
import WebSocket = require('ws')
import { FunctionConsole } from './console'

export type RequireFuncType = (
module: string,
Expand All @@ -17,7 +16,7 @@ export interface RuntimeContext {
__context__: FunctionContext
module: { exports: Object }
exports: Object
console: FunctionConsole
console: any
requireFunc: RequireFuncType
Buffer: typeof Buffer
setTimeout: typeof setTimeout
Expand Down
Loading

0 comments on commit 8d95fff

Please sign in to comment.