@lensmcp/nest-instrumentation 1.25.44 → 1.25.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/context.d.ts +16 -0
- package/lib/context.js +1 -1
- package/lib/instrument.js +1 -1
- package/lib/trace-interceptor.js +2 -2
- package/package.json +4 -4
package/lib/context.d.ts
CHANGED
|
@@ -56,10 +56,26 @@ export interface LensmcpRequestContext {
|
|
|
56
56
|
loopDbCalls?: number;
|
|
57
57
|
loopRedisCalls?: number;
|
|
58
58
|
loopExternalCalls?: number;
|
|
59
|
+
/**
|
|
60
|
+
* The request's DB calls by the second they ran in (epoch second → calls), kept by the taps that count
|
|
61
|
+
* `dbCallCount` (`countDbSecond`): a loop that lived seconds says how many of its calls fell within two of them.
|
|
62
|
+
*/
|
|
63
|
+
dbSeconds?: Map<number, number>;
|
|
64
|
+
/**
|
|
65
|
+
* Whether the response is a stream (SSE). A stream's loop lives as long as it does — a pump reading every few seconds
|
|
66
|
+
* — and is judged by its bursts, not its life's total (issues/closed/foodguard-148).
|
|
67
|
+
*/
|
|
68
|
+
isStream?: () => boolean;
|
|
59
69
|
/** Outbound east-west calls made so far by this request; numbers the child ids. */
|
|
60
70
|
hopCount?: number;
|
|
61
71
|
}
|
|
62
72
|
export declare const LENSMCP_CONTEXT_STORAGE: AsyncLocalStorage<LensmcpRequestContext>;
|
|
73
|
+
/** The most seconds a request's `dbSeconds` keeps; the oldest go first. */
|
|
74
|
+
export declare const DB_SECONDS_MAX = 4096;
|
|
75
|
+
/** One DB call of the request, in its second (`dbSeconds`) — node-instrumentation's pg tap keeps the same. */
|
|
76
|
+
export declare function countDbSecond(ctx: {
|
|
77
|
+
dbSeconds?: Map<number, number>;
|
|
78
|
+
}, at: number): void;
|
|
63
79
|
export declare function currentLensmcpContext(): LensmcpRequestContext | undefined;
|
|
64
80
|
/**
|
|
65
81
|
* The traced call running now — its span id — so every span names the span it ran inside (`parentSpanId`). Time
|
package/lib/context.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var s=Object.defineProperty;var r=(e,n)=>s(e,"name",{value:n,configurable:!0});var S=Object.defineProperty,o=r((e,n)=>S(e,"name",{value:n,configurable:!0}),"o");import{AsyncLocalStorage as u}from"node:async_hooks";export const LENSMCP_CONTEXT_STORAGE=new u,DB_SECONDS_MAX=4096;export function countDbSecond(e,n){let t=e.dbSeconds;t||(e.dbSeconds=t=new Map);const c=Math.floor(n/1e3);t.set(c,(t.get(c)??0)+1),t.size>DB_SECONDS_MAX&&t.delete(t.keys().next().value)}r(countDbSecond,"countDbSecond"),o(countDbSecond,"countDbSecond");export function currentLensmcpContext(){return LENSMCP_CONTEXT_STORAGE.getStore()}r(currentLensmcpContext,"currentLensmcpContext"),o(currentLensmcpContext,"currentLensmcpContext");export const LENSMCP_SPAN_STORAGE=new u;export function currentSpanId(){return LENSMCP_SPAN_STORAGE.getStore()}r(currentSpanId,"currentSpanId"),o(currentSpanId,"currentSpanId");export function runInLensmcpContext(e,n){return LENSMCP_CONTEXT_STORAGE.run(e,n)}r(runInLensmcpContext,"runInLensmcpContext"),o(runInLensmcpContext,"runInLensmcpContext");
|
package/lib/instrument.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var K=Object.defineProperty;var m=(e,r)=>K(e,"name",{value:r,configurable:!0});var U=Object.defineProperty,c=m((e,r)=>U(e,"name",{value:r,configurable:!0}),"d");import{fingerprint as j,ulid as x}from"@lensmcp/core";import{redactText as A}from"@lensmcp/core/redact";import{captureFor as X,capturedValue as P}from"./capture.js";import{countDbSecond as Y,currentLensmcpContext as Z,currentSpanId as tt,LENSMCP_SPAN_STORAGE as et}from"./context.js";import{activeOptions as ot}from"./state.js";import{performance as B}from"node:perf_hooks";export const LENSMCP_TRACED=Symbol.for("@lensmcp/nest-instrumentation/traced"),LENSMCP_IGNORE=Symbol.for("@lensmcp/nest-instrumentation/ignore");const E=300,rt=2e3;function _(e,r,i,u){if(!e)return;const b=Math.floor(r/1e3),f=Math.floor(i/1e3);let n=0,d=0;for(const[I,y]of e)I<b||I>f||(n+=y,d=Math.max(d,y+(I<f?e.get(I+1)??0:0)));return n>=u?Math.min(u,d):void 0}m(_,"tt"),c(_,"dbBurstOf");function G(e){if(e instanceof Error){const i=e.code,u=A(e.message??"");return{name:e.name||e.constructor?.name||"Error",...u?{message:u.length>E?`${u.slice(0,E)}\u2026`:u}:{},...typeof i=="string"&&i.length<=64?{code:i}:{}}}const r=typeof e=="string"?A(e):void 0;return{name:e==null?"nothing":typeof e=="object"?"non-Error object":`non-Error ${typeof e}`,...r?{message:r.slice(0,E)}:{}}}m(G,"et"),c(G,"thrownOf");export function LensmcpIgnore(){return((e,r)=>{if(r===void 0)e[LENSMCP_IGNORE]=!0;else{const i=e[r];typeof i=="function"&&(i[LENSMCP_IGNORE]=!0)}})}m(LensmcpIgnore,"LensmcpIgnore"),c(LensmcpIgnore,"LensmcpIgnore");export function isTraced(e){return typeof e=="function"&&!!e[LENSMCP_TRACED]}m(isTraced,"isTraced"),c(isTraced,"isTraced");export function isIgnored(e){return!!e&&(typeof e=="function"||typeof e=="object")&&!!e[LENSMCP_IGNORE]}m(isIgnored,"isIgnored"),c(isIgnored,"isIgnored");export function traceWrap(e,r,i={}){if(isTraced(e))return e;const u=i.db??!1,b=c(function(...f){let n;try{n=ot()}catch{return e.apply(this,f)}if(!n.trace.services)return e.apply(this,f);const d=Date.now();let I=d,y=0;const t=Z(),v=x(),S=tt(),W=t?.dbCallCount??0,F=t?.redisCallCount??0,Q=t?.externalCallCount??0,V=t?.loopDbCalls??0,z=t?.loopRedisCalls??0,H=t?.loopExternalCalls??0,w=i.capture??X(r),$=w==="args"||w==="both"?P(f):void 0,q=c((o,a,h)=>{const s=Date.now()-d;if(o==="ok"&&!i.explicit&&w===void 0&&!t?.flowId&&!t?.requestId&&!t?.runId&&s<n.trace.flowlessMinMs)return;const l={id:x(),sessionId:n.sessionId,timestamp:Date.now(),source:"nestjs",category:"backend",severity:o==="error"?"error":"info",context:{sessionId:n.sessionId,flowId:t?.flowId,requestId:t?.requestId,...t?.runId?{runId:t.runId}:{}},fingerprint:j({kind:"method-call",identity:r}),title:`${r} (${s}ms)${o==="error"?" \u2717":""}`,message:a instanceof Error?a.message:void 0,raw:{kind:"span",...n.projectName?{project:n.projectName}:{},span:{name:r,spanId:v,...S?{parentSpanId:S}:{},flowId:t?.flowId,requestId:t?.requestId,startTime:d,endTime:Date.now(),durationMs:s,syncStartedAt:I,syncDurationMs:y,status:o,outcome:o==="error"?"threw":"returned",...o==="error"?{error:G(a)}:{},...$!==void 0?{args:$}:{},...o==="ok"&&(w==="result"||w==="both")?{result:P(h)}:{}}}};n.emit(l)},"emitSpan"),k=c(()=>{const o=Date.now();t&&(t.dbCallCount=(t.dbCallCount??0)+1,Y(t,o)),n.emit({id:x(),sessionId:n.sessionId,timestamp:Date.now(),source:"db",category:"db",severity:"info",context:{sessionId:n.sessionId,flowId:t?.flowId,requestId:t?.requestId,...t?.runId?{runId:t.runId}:{}},fingerprint:j({kind:"db-query",identity:r}),title:`query ${r}`,raw:{kind:"db-query",...n.projectName?{project:n.projectName}:{},query:{signature:r},startedAt:d,durationMs:o-d,span:{parentSpanId:v}}})},"emitDbQuery"),N=c(()=>{if(u||!t)return;const o=(t.dbCallCount??0)-W,a=(t.redisCallCount??0)-F,h=(t.externalCallCount??0)-Q,s=Math.max(0,o-((t.loopDbCalls??0)-V)),l=Math.max(0,a-((t.loopRedisCalls??0)-z)),p=Math.max(0,h-((t.loopExternalCalls??0)-H));if(s<3&&l<3&&p<3)return;const R=Date.now(),D=R-d,C=s>=3&&D>=rt?_(t.dbSeconds,d,R,s):void 0,M=t.isStream?.()===!0,J=M&&C!==void 0&&C<3&&l<3&&p<3,g=[];s>=3&&g.push(M&&C!==void 0?`${s} DB calls over ${Math.round(D/1e3)} s of a stream, at most ${C} within two seconds`:`${s} DB calls`),l>=3&&g.push(`${l} redis calls`),p>=3&&g.push(`${p} external calls`),s>=3&&(t.loopDbCalls=(t.loopDbCalls??0)+s),l>=3&&(t.loopRedisCalls=(t.loopRedisCalls??0)+l),p>=3&&(t.loopExternalCalls=(t.loopExternalCalls??0)+p);const T=o-s+(a-l)+(h-p);n.emit({id:x(),sessionId:n.sessionId,timestamp:Date.now(),source:"nestjs",category:"backend",severity:J?"info":"warning",context:{sessionId:n.sessionId,flowId:t.flowId,requestId:t.requestId,...t.runId?{runId:t.runId}:{}},fingerprint:j({kind:"loop",identity:r}),title:`loop in ${r} (${g.join(", ")})`,raw:{kind:"loop",...n.projectName?{project:n.projectName}:{},loop:{iterations:Math.max(s,l,p),durationMs:D,startedAt:d,dbCallsInsideLoop:s,redisCallsInsideLoop:l,externalCallsInsideLoop:p,awaitedOperationsInsideLoop:s+l+p,...C!==void 0?{dbBurst:C}:{},...M?{stream:!0}:{},...T>0?{reportedBeneath:T}:{}}}})},"emitLoopIfNeeded"),L=c(o=>{u&&k(),q("ok",void 0,o),N()},"onOk"),O=c(o=>{u&&k(),q("error",o),N()},"onErr");try{const o=et.run(v,()=>{I=Date.now();const a=B.now();try{return e.apply(this,f)}finally{y=Math.round((B.now()-a)*100)/100}});return o instanceof Promise?o.then(a=>(L(a),a),a=>{throw O(a),a}):(L(o),o)}catch(o){throw O(o),o}},"wrapped");return b[LENSMCP_TRACED]=!0,b}m(traceWrap,"traceWrap"),c(traceWrap,"traceWrap");
|
package/lib/trace-interceptor.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var oe=Object.defineProperty;var u=(t,e)=>oe(t,"name",{value:e,configurable:!0});var ne=Object.defineProperty,i=u((t,e)=>ne(t,"name",{value:e,configurable:!0}),"i");import{__decorate as ae}from"tslib";import{Injectable as ie}from"@nestjs/common";import{Observable as X}from"rxjs";import{catchError as de,tap as ue}from"rxjs/operators";import{fingerprint as E,headlessFlowId as J,headlessFlowsEnabled as K,isProbePath as V,LENS_REQUEST_ID_HEADER as ce,requestFacts as fe,responseFacts as le,shapeOf as W,ulid as v}from"@lensmcp/core";import{activeMemoryOptions as pe,onFlowStart as me,onFlowSettled as Ie}from"@lensmcp/memory-tracker";import{LENSMCP_CONTEXT_STORAGE as we}from"./context.js";import{attachContextEnd as ye}from"./context-end.js";import{attachFlowStart as ge}from"./flow-start.js";import{activeOptions as b}from"./state.js";const he=Symbol.for("lensmcp.exchangeId");let j=class{static{u(this,"O")}static{i(this,"TraceInterceptor")}intercept(t,e){const o=b();if(!o.trace.requests)return e.handle();const d=t.switchToHttp(),s=d.getRequest(),r=d.getResponse(),n=g(s.headers?.[ce])??g(s.headers?.["x-request-id"])??v(),c=g(s.headers?.["x-lensmcp-flow-id"]),f=g(s.headers?.["x-internal-token"])!==void 0,p=c??(!f&&K()&&!V(s.url)?J(n):void 0),l=t.getHandler?.(),I=!!l&&Reflect.getMetadata?.("__sse__",l)===!0,a={sessionId:o.sessionId,requestId:n,parentRequestId:g(s.headers?.["x-lensmcp-parent-request-id"]),flowId:p,originNodeId:g(s.headers?.["x-lensmcp-origin-node-id"]),traceparent:g(s.headers?.traceparent),runId:v(),dbCallCount:0,isStream:i(()=>I||x(r)==="text/event-stream","isStream")},m=s[he],q=(s.method??"GET").toUpperCase(),C=s.route?.path??s.url??"<unknown>",M=t.getClass?.()?.name,A=l?.name,h=Date.now();p&&!c&&_(a,q,C,h),!p&&f&&K()&&!V(s.url)&&ge(a,()=>(a.flowId||(a.flowId=J(n),_(a,q,C,h)),a.flowId));const H=o.memory.mode!=="off";H&&me(n);const P=i(()=>{if(!H)return;const w=pe()?.settleMs??1e3;setTimeout(()=>Ie(n,a.flowId),w).unref?.()},"settleAndCheck"),te=ye(a);let R;typeof r?.once=="function"&&qe()&&r.once("finish",()=>Y(o.projectName,a,q,C,s,r,R));let F=!1,T=typeof r?.once!="function";const B=i(()=>{F&&T&&te()},"endIfOver");if(!T){const w=i(()=>{T=!0,B()},"responseOut");r.once?.("finish",w),r.once?.("close",w)}return new X(w=>{let L;return we.run(a,()=>{L=e.handle().pipe(ue({next:i(y=>{R??={value:y},w.next(y)},"next"),complete:i(()=>{S(o.projectName,a,q,C,r.statusCode??200,h,void 0,m,x(r)),D(a,M,A,h,"ok"),P(),w.complete()},"complete")}),de(y=>{const U=y?.getResponse?.();U!==void 0&&(R??={value:U});const G=N(y);if(G!==void 0||typeof r.once!="function")S(o.projectName,a,q,C,G??O(r),h,y,m,x(r));else{let z=!1;const Q=i(se=>{if(z)return;z=!0;const re=se&&r.statusCode&&r.statusCode>=400?r.statusCode:O(r);S(o.projectName,a,q,C,re,h,y,m,x(r))},"settle");r.once("finish",()=>Q(!0)),r.once("close",()=>Q(!1))}return D(a,M,A,h,"error"),P(),w.error(y),new X})).subscribe()}),()=>{L?.unsubscribe(),F=!0,B()}})}};j=ae([ie()],j);export{j as TraceInterceptor};const k=new Map,ve=6e4,qe=i(()=>process.env.LENSMCP_HTTP_SHAPES!=="0","shapesOn");function Y(t,e,o,d,s,r,n){try{const c=r.statusCode??200,f=`${o} ${d} ${Math.floor(c/100)}`,p=Date.now();if(p-(k.get(f)??0)<ve)return;k.set(f,p),k.size>2e3&&k.delete(k.keys().next().value);const l=s.headers??{},I=s.body!==void 0&&s.body!==null&&typeof s.body=="object"&&!Buffer.isBuffer(s.body)?W(s.body):void 0,a=n&&n.value!==void 0&&typeof n.value=="object"&&n.value!==null&&!Buffer.isBuffer(n.value)&&typeof n.value.pipe!="function"?W(n.value):void 0,m={id:v(),sessionId:e.sessionId,timestamp:p,source:"nestjs",category:"backend",severity:"info",context:{sessionId:e.sessionId,flowId:e.flowId,requestId:e.requestId,...e.runId?{runId:e.runId}:{}},fingerprint:E({kind:"http-exchange",identity:f}),title:`${o} ${d} \u2192 ${c} (its shape)`,raw:{kind:"http-exchange",method:o,route:d,status:c,request:{...fe(l,s.query,s.params),...I?{body:I}:{}},response:{...le(r.getHeaders?.()??{},typeof l.origin=="string"?l.origin:void 0),...a?{body:a}:{}}}};$(m,t),b().emit(m)}catch{}}u(Y,"we"),i(Y,"recordExchange");function N(t){const e=t;if(typeof e?.getStatus=="function")return e.getStatus();if(typeof e?.status=="number"&&e.status>=100)return e.status;if(typeof e?.statusCode=="number"&&e.statusCode>=100)return e.statusCode}u(N,"Y"),i(N,"ownStatus");const Ce=new Set(["TypeError","ReferenceError","SyntaxError","RangeError","EvalError","URIError"]);export function requestOutcome(t,e){return e instanceof Error&&Ce.has(e.name)&&N(e)===void 0?"fault":t<400?"ok":t<500?"refused":t===500?"fault":"declared"}u(requestOutcome,"requestOutcome"),i(requestOutcome,"requestOutcome");function Z(t){return t==="ok"?"info":t==="refused"?"warning":"error"}u(Z,"Ce"),i(Z,"severityOf");function O(t){return t.statusCode&&t.statusCode>=400?t.statusCode:500}u(O,"J"),i(O,"inferStatus");function g(t){if(typeof t=="string")return t;if(Array.isArray(t)&&typeof t[0]=="string")return t[0]}u(g,"h"),i(g,"stringHeader");function _(t,e,o,d){const s=b(),r=`request:${e} ${o}`,n={id:v(),sessionId:t.sessionId,timestamp:d,source:"nestjs",category:"backend",severity:"info",context:{sessionId:t.sessionId,flowId:t.flowId,requestId:t.requestId,originType:"request",originNodeId:r},fingerprint:E({kind:"flow-origin",identity:`${e}:${o}`}),title:`${e} ${o}`,raw:{kind:"flow-origin",originNodeId:r,method:e,route:o}};$(n,s.projectName),s.emit(n)}u(_,"K"),i(_,"emitRequestOrigin");function D(t,e,o,d,s){if(!e||!o)return;const r=b(),n=Date.now()-d,c=`${e}.${o}`,f={id:v(),sessionId:t.sessionId,timestamp:Date.now(),source:"nestjs",category:"backend",severity:s==="error"?"error":"info",context:{sessionId:t.sessionId,flowId:t.flowId,requestId:t.requestId,...t.runId?{runId:t.runId}:{}},fingerprint:E({kind:"method-call",identity:c}),title:`${c} (${n}ms)${s==="error"?" \u2717":""}`,raw:{kind:"span",span:{name:c,role:"controller",flowId:t.flowId,requestId:t.requestId,startTime:d,endTime:Date.now(),durationMs:n,status:s}}};$(f,b().projectName),r.emit(f)}u(D,"W"),i(D,"emitControllerSpan");function ee(t){if(!(t instanceof Error))return{message:String(t)};const e=t.stack?t.stack.split(`
|
|
2
2
|
`).slice(0,30).join(`
|
|
3
|
-
`):void 0;return{...t.name?{name:t.name}:{},message:t.message,...e?{stack:e}:{}}}u(
|
|
3
|
+
`):void 0;return{...t.name?{name:t.name}:{},message:t.message,...e?{stack:e}:{}}}u(ee,"qe"),i(ee,"errorDetail");function $(t,e){if(!e)return;const o=t.raw;o&&o.project===void 0&&(o.project=e)}u($,"R"),i($,"stampProject");function x(t){try{const e=t.getHeaders?.()["content-type"];return typeof e=="string"?e.split(";")[0]?.trim().toLowerCase():void 0}catch{return}}u(x,"$"),i(x,"responseContentType");function S(t,e,o,d,s,r,n,c,f){const p=b(),l=Date.now()-r,I=requestOutcome(s,n),a={id:v(),sessionId:e.sessionId,timestamp:Date.now(),source:"nestjs",category:"backend",severity:Z(I),context:{sessionId:e.sessionId,flowId:e.flowId,requestId:e.requestId,...e.runId?{runId:e.runId}:{},...e.parentRequestId?{parentRequestId:e.parentRequestId}:{},originNodeId:e.originNodeId,traceparent:e.traceparent},fingerprint:E({kind:"server-request",identity:`${o}:${d}:${Math.floor(s/100)}xx`}),title:`${o} ${d} \u2192 ${s} (${l}ms)`,message:n instanceof Error?n.message:void 0,raw:{kind:"server-request",request:{method:o,route:d,status:s,outcome:I,durationMs:l,startedAt:r,...f?{contentType:f,streaming:f==="text/event-stream"}:{},endedAt:Date.now(),flowId:e.flowId,requestId:e.requestId,...e.parentRequestId?{parentRequestId:e.parentRequestId}:{},...typeof c=="string"?{exchangeId:c}:{},...e.runId?{runId:e.runId}:{},...e.dbCallCount||e.redisCallCount?{calls:{...e.dbCallCount?{db:e.dbCallCount}:{},...e.redisCallCount?{cache:e.redisCallCount}:{}}}:{},...n?{error:ee(n)}:{}}}},m={...a,id:v(),raw:{kind:"span",span:{name:`nest.request ${o} ${d}`,traceId:e.traceparent,flowId:e.flowId,requestId:e.requestId,startTime:r,endTime:Date.now(),durationMs:l,status:I==="ok"?"ok":"error"}}};$(a,t),$(m,t),p.emit(a),p.emit(m)}u(S,"T"),i(S,"emitRequest");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/nest-instrumentation",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.45",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@lensmcp/core": "1.25.
|
|
18
|
-
"@lensmcp/memory-tracker": "1.25.
|
|
19
|
-
"@lensmcp/protocol-types": "1.25.
|
|
17
|
+
"@lensmcp/core": "1.25.45",
|
|
18
|
+
"@lensmcp/memory-tracker": "1.25.45",
|
|
19
|
+
"@lensmcp/protocol-types": "1.25.45",
|
|
20
20
|
"tslib": "^2.3.0"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|