在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:aigoncharov/cls-proxify开源软件地址:https://github.com/aigoncharov/cls-proxify开源编程语言:TypeScript 95.5%开源软件介绍:cls-proxifyLogging on steroids with CLS and Proxy. A small library that proxies any arbitrary object with a proxy from Continuation-Local Storage a.k.a. CLS if found one. Super-useful for creating child loggers per each request with dynamic context from the request itself (e.g. adding request trace ID, adding request payload). Integrated with express, koa, fastify out-of-the-box. Many thanks to @mcollina for the idea of combining Proxy and CLS. Installation
Quick startExpress
import { clsProxify } from 'cls-proxify'
import { clsProxifyExpressMiddleware } from 'cls-proxify/integration/express'
import * as express from 'express'
const logger = {
info: (msg: string) => console.log(msg),
}
const loggerCls = clsProxify(logger)
const app = express()
app.use(
clsProxifyExpressMiddleware((req) => {
const headerRequestID = req.headers.Traceparent
const loggerProxy = {
info: (msg: string) => `${headerRequestID}: ${msg}`,
}
// this value will be accesible in CLS by key 'cls-proxify'
// it will be used as a proxy for `loggerCls`
return loggerProxy
}),
)
app.get('/test', (req, res) => {
loggerCls.info('My message!')
// Logs `${headerRequestID}: My message!` into the console
// Say, we send GET /test with header 'Traceparent' set to 12345
// It's going to log '12345: My message!'
// If it doesn't find anything in CLS by key 'cls-proxify' it uses the original `logger` and logs 'My message!'
}) Koa
import { clsProxify } from 'cls-proxify'
import { clsProxifyKoaMiddleware } from 'cls-proxify/integration/koa'
import * as Koa from 'koa'
const logger = {
info: (msg: string) => console.log(msg),
}
const loggerCls = clsProxify(logger)
const app = new Koa()
app.use(
clsProxifyKoaMiddleware((ctx) => {
const headerRequestID = ctx.req.headers.Traceparent
const loggerProxy = {
info: (msg: string) => `${headerRequestID}: ${msg}`,
}
// this value will be accesible in CLS by key 'cls-proxify'
// it will be used as a proxy for `loggerCls`
return loggerProxy
}),
)
app.use((ctx) => {
loggerCls.info('My message!')
// Logs `${headerRequestID}: My message!` into the console
// Say, we send GET / with header 'Traceparent' set to 12345
// It's going to log '12345: My message!'
// If it doesn't find anything in CLS by key 'cls-proxify' it uses the original `logger` and logs 'My message!'
}) Fastifyimport { clsProxify } from 'cls-proxify'
import { clsProxifyFastifyPlugin } from 'cls-proxify/integration/fastify'
import * as fastify from 'fastify'
const logger = {
info: (msg: string) => console.log(msg),
}
const loggerCls = clsProxify(logger)
const app = fastify()
app.register(clsProxifyFastifyPlugin, {
proxify: (req) => {
const headerRequestID = ctx.req.headers.Traceparent
const loggerProxy = {
info: (msg: string) => `${headerRequestID}: ${msg}`,
}
// this value will be accesible in CLS by key 'cls-proxify'
// it will be used as a proxy for `loggerCls`
return loggerProxy
},
})
app.get('/test', (req, res) => {
loggerCls.info('My message!')
// Logs `${headerRequestID}: My message!` into the console
// Say, we send GET /test with header 'Traceparent' set to 12345
// It's going to log '12345: My message!'
// If it doesn't find anything in CLS by key 'cls-proxify' it uses the original `logger` and logs 'My message!'
}) Any other framework or libraryimport { clsProxify, getClsHookedStorage } from 'cls-proxify'
import AbstractWebServer from 'abstract-web-server'
const logger = {
info: (msg: string) => console.log(msg),
}
const loggerCls = clsProxify(logger)
const app = new AbstractWebServer()
// Assuming this AbstractWebServer supports some form of middlewares
app.use((request, response, next) => {
// Assuming your request and response are event emitters
getClsHookedStorage().namespace.bindEmitter(request)
getClsHookedStorage().namespace.bindEmitter(response)
getClsHookedStorage().namespace.run(() => {
const headerRequestID = request.headers.Traceparent
// this value will be accesible in CLS by key 'cls-proxify'
// it will be used as a proxy for `loggerCls`
const loggerProxy = {
info: (msg: string) => `${headerRequestID}: ${msg}`,
}
getClsHookedStorage().set(loggerProxy)
next()
})
})
app.get('/test', (req, res) => {
loggerCls.info('My message!')
// Logs `${headerRequestID}: My message!` into the console
// Say, we send GET /test with header 'Traceparent' set to 12345
// It's going to log '12345: My message!'
// If it doesn't find anything in CLS by key 'cls-proxify' it uses the original `logger` and logs 'My message!'
}) Set custom CLS storageimport { clsProxify, setClsHookedStorage, ClsHookedStorage, ClsProxifyStorage } from 'cls-proxify'
import AbstractWebServer from 'abstract-web-server'
// You can subclass existing ClsHookedStorage
class CustomClsStorage extends ClsHookedStorage {
// Override namespace
public readonly namespace = createNamespace('myNamespace')
// Or override CLS key
protected readonly key = 'yoda'
}
setClsHookedStorage(new CustomClsStorage())
// Or you can implement your own storage from scratch.
// Just make sure it conforms to `ClsProxifyStorage` interface.
class SecretStorage<T> implements ClsProxifyStorage<T> {
set(proxy: T): void {}
get(): T | undefined {}
}
setClsHookedStorage(new SecretStorage()) In depthHow it works
We wrap our original logger in a Proxy. Every time we try to access any property of that object we first check if there's an updated logger in CLS available. If it's there we take the property from it. If it's not we take the property from the original logger. Then for every request we create a CLS context using Does it work only for loggers?No. You can proxify any object you want. Moreover you can even proxify functions and class constructors. Here's a list of traps cls-proxify provides: Take a look at the tests to get an idea of how you can utilize them. Live demosUsage with pino and fastifyUsage with pino and expressTroubleshootingMy context got lostNote that some middlewares may cause CLS context to get lost. To avoid it use any third party middleware that does not need access to request ids before you use this middleware. I'm experiencing a memory leakMake sure you don't keep any external references to the objects inside of CLS. It may prevent them from being collected by GC. Take a look at this issues: #21, #11. |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论