{"id":16368690,"url":"https://github.com/aigoncharov/cls-proxify","last_synced_at":"2025-04-05T21:07:44.248Z","repository":{"id":34221767,"uuid":"171887113","full_name":"aigoncharov/cls-proxify","owner":"aigoncharov","description":"Logging on steroids with CLS and Proxy. Integrated with express, koa, fastify.","archived":false,"fork":false,"pushed_at":"2022-02-11T15:54:27.000Z","size":768,"stargazers_count":160,"open_issues_count":2,"forks_count":7,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-29T20:05:19.778Z","etag":null,"topics":["cls","cls-hooked","hacktoberfest","logging","nodejs","proxy","traceid","tracing"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aigoncharov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-21T14:31:11.000Z","updated_at":"2024-10-04T23:20:54.000Z","dependencies_parsed_at":"2022-08-08T00:02:06.784Z","dependency_job_id":null,"html_url":"https://github.com/aigoncharov/cls-proxify","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aigoncharov%2Fcls-proxify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aigoncharov%2Fcls-proxify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aigoncharov%2Fcls-proxify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aigoncharov%2Fcls-proxify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aigoncharov","download_url":"https://codeload.github.com/aigoncharov/cls-proxify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247399877,"owners_count":20932876,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cls","cls-hooked","hacktoberfest","logging","nodejs","proxy","traceid","tracing"],"created_at":"2024-10-11T02:53:32.444Z","updated_at":"2025-04-05T21:07:44.228Z","avatar_url":"https://github.com/aigoncharov.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cls-proxify [![Build Status](https://travis-ci.org/aigoncharov/cls-proxify.svg?branch=master)](https://travis-ci.org/aigoncharov/cls-proxify) [![Coverage Status](https://coveralls.io/repos/github/aigoncharov/cls-proxify/badge.svg?branch=master)](https://coveralls.io/github/aigoncharov/cls-proxify?branch=master) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=A%20small%20library%20that%20proxies%20any%20arbitrary%20object%20with%20a%20proxy%20from%20CLS.%20Super-useful%20for%20logging.\u0026url=https://github.com/aigoncharov/cls-proxify\u0026hashtags=javascript,library,nodejs,cls,proxy,logging)\n\nLogging 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](https://github.com/jeff-lewis/cls-hooked) 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](https://github.com/expressjs/express), [koa](https://github.com/koajs/koa), [fastify](https://github.com/fastify/fastify) out-of-the-box.\n\nMany thanks to [@mcollina](https://github.com/mcollina) for the idea of combining [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) and [CLS](https://github.com/jeff-lewis/cls-hooked).\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Installation](#installation)\n- [Quick start](#quick-start)\n  - [Express](#express)\n  - [Koa](#koa)\n  - [Fastify](#fastify)\n  - [Any other framework or library](#any-other-framework-or-library)\n  - [Set custom CLS storage](#set-custom-cls-storage)\n- [In depth](#in-depth)\n  - [How it works](#how-it-works)\n  - [Does it work only for loggers?](#does-it-work-only-for-loggers)\n- [Live demos](#live-demos)\n  - [Usage with pino and fastify](#usage-with-pino-and-fastify)\n  - [Usage with pino and express](#usage-with-pino-and-express)\n- [Troubleshooting](#troubleshooting)\n  - [My context got lost](#my-context-got-lost)\n  - [I'm experiencing a memory leak](#im-experiencing-a-memory-leak)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Installation\n\n```\nnpm i cls-proxify cls-hooked\n```\n\n## Quick start\n\n### Express\n\n\u003e TypeScript users, `clsProxifyExpressMiddleware` uses typings from `@types/express`. Please, run `npm i -D @types/express`\n\n```ts\nimport { clsProxify } from 'cls-proxify'\nimport { clsProxifyExpressMiddleware } from 'cls-proxify/integration/express'\nimport * as express from 'express'\n\nconst logger = {\n  info: (msg: string) =\u003e console.log(msg),\n}\nconst loggerCls = clsProxify(logger)\n\nconst app = express()\napp.use(\n  clsProxifyExpressMiddleware((req) =\u003e {\n    const headerRequestID = req.headers.Traceparent\n    const loggerProxy = {\n      info: (msg: string) =\u003e `${headerRequestID}: ${msg}`,\n    }\n    // this value will be accesible in CLS by key 'cls-proxify'\n    // it will be used as a proxy for `loggerCls`\n    return loggerProxy\n  }),\n)\n\napp.get('/test', (req, res) =\u003e {\n  loggerCls.info('My message!')\n  // Logs `${headerRequestID}: My message!` into the console\n  // Say, we send GET /test with header 'Traceparent' set to 12345\n  // It's going to log '12345: My message!'\n  // If it doesn't find anything in CLS by key 'cls-proxify' it uses the original `logger` and logs 'My message!'\n})\n```\n\n### Koa\n\n\u003e TypeScript users, `clsProxifyKoaMiddleware` uses typings from `@types/koa`. Please, run `npm i -D @types/koa`\n\n```ts\nimport { clsProxify } from 'cls-proxify'\nimport { clsProxifyKoaMiddleware } from 'cls-proxify/integration/koa'\nimport * as Koa from 'koa'\n\nconst logger = {\n  info: (msg: string) =\u003e console.log(msg),\n}\nconst loggerCls = clsProxify(logger)\n\nconst app = new Koa()\napp.use(\n  clsProxifyKoaMiddleware((ctx) =\u003e {\n    const headerRequestID = ctx.req.headers.Traceparent\n    const loggerProxy = {\n      info: (msg: string) =\u003e `${headerRequestID}: ${msg}`,\n    }\n    // this value will be accesible in CLS by key 'cls-proxify'\n    // it will be used as a proxy for `loggerCls`\n    return loggerProxy\n  }),\n)\n\napp.use((ctx) =\u003e {\n  loggerCls.info('My message!')\n  // Logs `${headerRequestID}: My message!` into the console\n  // Say, we send GET / with header 'Traceparent' set to 12345\n  // It's going to log '12345: My message!'\n  // If it doesn't find anything in CLS by key 'cls-proxify' it uses the original `logger` and logs 'My message!'\n})\n```\n\n### Fastify\n\n```ts\nimport { clsProxify } from 'cls-proxify'\nimport { clsProxifyFastifyPlugin } from 'cls-proxify/integration/fastify'\nimport * as fastify from 'fastify'\n\nconst logger = {\n  info: (msg: string) =\u003e console.log(msg),\n}\nconst loggerCls = clsProxify(logger)\n\nconst app = fastify()\napp.register(clsProxifyFastifyPlugin, {\n  proxify: (req) =\u003e {\n    const headerRequestID = ctx.req.headers.Traceparent\n    const loggerProxy = {\n      info: (msg: string) =\u003e `${headerRequestID}: ${msg}`,\n    }\n    // this value will be accesible in CLS by key 'cls-proxify'\n    // it will be used as a proxy for `loggerCls`\n    return loggerProxy\n  },\n})\n\napp.get('/test', (req, res) =\u003e {\n  loggerCls.info('My message!')\n  // Logs `${headerRequestID}: My message!` into the console\n  // Say, we send GET /test with header 'Traceparent' set to 12345\n  // It's going to log '12345: My message!'\n  // If it doesn't find anything in CLS by key 'cls-proxify' it uses the original `logger` and logs 'My message!'\n})\n```\n\n### Any other framework or library\n\n```ts\nimport { clsProxify, getClsHookedStorage } from 'cls-proxify'\nimport AbstractWebServer from 'abstract-web-server'\n\nconst logger = {\n  info: (msg: string) =\u003e console.log(msg),\n}\nconst loggerCls = clsProxify(logger)\n\nconst app = new AbstractWebServer()\n// Assuming this AbstractWebServer supports some form of middlewares\napp.use((request, response, next) =\u003e {\n  // Assuming your request and response are event emitters\n  getClsHookedStorage().namespace.bindEmitter(request)\n  getClsHookedStorage().namespace.bindEmitter(response)\n\n  getClsHookedStorage().namespace.run(() =\u003e {\n    const headerRequestID = request.headers.Traceparent\n    // this value will be accesible in CLS by key 'cls-proxify'\n    // it will be used as a proxy for `loggerCls`\n    const loggerProxy = {\n      info: (msg: string) =\u003e `${headerRequestID}: ${msg}`,\n    }\n    getClsHookedStorage().set(loggerProxy)\n\n    next()\n  })\n})\n\napp.get('/test', (req, res) =\u003e {\n  loggerCls.info('My message!')\n  // Logs `${headerRequestID}: My message!` into the console\n  // Say, we send GET /test with header 'Traceparent' set to 12345\n  // It's going to log '12345: My message!'\n  // If it doesn't find anything in CLS by key 'cls-proxify' it uses the original `logger` and logs 'My message!'\n})\n```\n\n### Set custom CLS storage\n\n```ts\nimport { clsProxify, setClsHookedStorage, ClsHookedStorage, ClsProxifyStorage } from 'cls-proxify'\nimport AbstractWebServer from 'abstract-web-server'\n\n// You can subclass existing ClsHookedStorage\nclass CustomClsStorage extends ClsHookedStorage {\n  // Override namespace\n  public readonly namespace = createNamespace('myNamespace')\n  // Or override CLS key\n  protected readonly key = 'yoda'\n}\nsetClsHookedStorage(new CustomClsStorage())\n\n// Or you can implement your own storage from scratch.\n// Just make sure it conforms to `ClsProxifyStorage` interface.\nclass SecretStorage\u003cT\u003e implements ClsProxifyStorage\u003cT\u003e {\n  set(proxy: T): void {}\n  get(): T | undefined {}\n}\nsetClsHookedStorage(new SecretStorage())\n```\n\n## In depth\n\n### How it works\n\n\u003e If you're struggling to grasp the idea behind CLS at these two articles: [Request Id Tracing in Node.js Applications](https://itnext.io/request-id-tracing-in-node-js-applications-c517c7dab62d), [A Pragmatic Overview of Async Hooks API in Node.js](https://itnext.io/a-pragmatic-overview-of-async-hooks-api-in-node-js-e514b31460e9).\n\n\u003e Take a look at [this article which overviews how CLS works and covers the idea behind this library](https://itnext.io/nodejs-logging-made-right-117a19e8b4ce).\n\nWe wrap our original logger in a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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 `run` and `bindEmitter`. Once the context is created we enhance our original logger with some extra data and put the updated logger in the context. Once we try to call any method of our logger we'll actually call the same method on our logger in CLS.\n\n![](docs/how-cls-proxify-works.jpg)\n\n### Does it work only for loggers?\n\nNo. You can proxify any object you want. Moreover you can even proxify functions and class constructors.\n\nHere's a list of [traps](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Terminology) cls-proxify provides:\n\n- [get](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/get)\n- [apply](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/apply)\n- [construct](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/construct)\n- [ownKeys](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/ownKeys)\n- [has](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/has)\n- [getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor)\n\nTake a look at [the tests](https://github.com/aigoncharov/cls-proxify/blob/master/src/core.test.ts#L29) to get an idea of how you can utilize them.\n\n## Live demos\n\n#### [Usage with pino and fastify](https://repl.it/@aigoncharov/cls-proxify-pino-fastify)\n\n#### [Usage with pino and express](https://repl.it/@aigoncharov/cls-proxify-pino-express)\n\n## Troubleshooting\n\n### My context got lost\n\nNote 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.\n\n### I'm experiencing a memory leak\n\nMake sure you don't keep any external references to the objects inside of CLS. It may prevent them from being collected by GC.\nTake a look at this issues: [#21](https://github.com/Jeff-Lewis/cls-hooked/issues/21), [#11](https://github.com/Jeff-Lewis/cls-hooked/issues/11).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faigoncharov%2Fcls-proxify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faigoncharov%2Fcls-proxify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faigoncharov%2Fcls-proxify/lists"}