axios-interceptor-logger 1.0.0 → 1.0.2
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/README.md +51 -28
- package/dist/index.js +3 -2
- package/dist/index.mjs +3 -2
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -13,56 +13,55 @@ This allows you to inject application-layer traffic directly into Elasticsearch/
|
|
|
13
13
|
- ⏱️ **Nanosecond Latency**: Tracks transaction duration down to the nanosecond, mapped to `event.duration`.
|
|
14
14
|
- 🔗 **Correlation IDs**: Generates (or passes through) UUID trace IDs for distributed tracing.
|
|
15
15
|
|
|
16
|
-
## Installation
|
|
16
|
+
## Installation
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Install the package directly from npm:
|
|
19
19
|
|
|
20
|
-
### 1. Push to GitHub
|
|
21
|
-
Create a new public repository on GitHub (e.g., named `axios-interceptor-logger`), and push this code:
|
|
22
20
|
```bash
|
|
23
|
-
|
|
24
|
-
git add .
|
|
25
|
-
git commit -m "Initial commit"
|
|
26
|
-
git branch -M main
|
|
27
|
-
git remote add origin https://github.com/YOUR_GITHUB_USERNAME/axios-interceptor-logger.git
|
|
28
|
-
git push -u origin main
|
|
21
|
+
npm install axios-interceptor-logger
|
|
29
22
|
```
|
|
30
23
|
|
|
31
|
-
### 2. Install in your Consumer App
|
|
32
|
-
Once published as a public repository, you or anyone else can install it directly via npm using the GitHub URL format:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npm install github:YOUR_GITHUB_USERNAME/axios-interceptor-logger
|
|
36
|
-
```
|
|
37
|
-
*(Replace `YOUR_GITHUB_USERNAME` with your actual GitHub username).*
|
|
38
|
-
|
|
39
24
|
## Usage
|
|
40
25
|
|
|
41
|
-
Import the `AxiosLoggerSingleton` and attach it to your Axios instance(s).
|
|
26
|
+
Import the `AxiosLoggerSingleton` and attach it to your Axios instance(s).
|
|
42
27
|
|
|
43
28
|
```typescript
|
|
44
|
-
import axios from
|
|
45
|
-
import { AxiosLoggerSingleton } from
|
|
29
|
+
import axios from "axios";
|
|
30
|
+
import { AxiosLoggerSingleton } from "axios-interceptor-logger";
|
|
46
31
|
|
|
47
32
|
// 1. Initialize the Logger Configuration
|
|
48
33
|
const logger = AxiosLoggerSingleton.getInstance({
|
|
49
34
|
verbose: true, // Prints ECS logs to the console
|
|
50
|
-
maxPayloadBytes: 5000, //
|
|
51
|
-
redactKeys: [
|
|
52
|
-
ignoreDomains: [
|
|
35
|
+
maxPayloadBytes: 5000, // Caps how many characters of each string field are logged (hard ceiling: 10000)
|
|
36
|
+
redactKeys: ["password", "secret", "token", "authorization", "apikey", "api_key"],
|
|
37
|
+
ignoreDomains: ["localhost"],
|
|
53
38
|
});
|
|
54
39
|
|
|
55
40
|
// 2. Create an Axios Instance
|
|
56
41
|
const apiClient = axios.create({
|
|
57
|
-
baseURL:
|
|
42
|
+
baseURL: "https://api.example.com",
|
|
58
43
|
});
|
|
59
44
|
|
|
60
45
|
// 3. Attach the Interceptor
|
|
61
46
|
logger.attach(apiClient);
|
|
62
47
|
|
|
63
48
|
// 4. Make requests (they will now be automatically logged in ECS format!)
|
|
64
|
-
await apiClient.post(
|
|
49
|
+
await apiClient.post("/login", {
|
|
50
|
+
username: "admin",
|
|
51
|
+
password: "supersecretpassword",
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Default Redacted Keys
|
|
56
|
+
|
|
57
|
+
If you don't pass `redactKeys`, any header/body key whose name *contains* one of the following (case-insensitive) is replaced with `[REDACTED]`:
|
|
58
|
+
|
|
65
59
|
```
|
|
60
|
+
authorization, password, token, secret, cookie, apikey, api_key, api-key,
|
|
61
|
+
privatekey, private_key, private-key
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This is a substring match against common secret names — it is **not** a guarantee that every secret-shaped field in your API traffic will be caught. Review the [Responsibility & Disclaimer](#responsibility--disclaimer) section below and pass your own `redactKeys` for anything app-specific (custom auth headers, PII fields, etc.).
|
|
66
65
|
|
|
67
66
|
## Environment Variables
|
|
68
67
|
|
|
@@ -78,8 +77,8 @@ The logger intercepts the traffic and outputs standard Packetbeat ECS JSON:
|
|
|
78
77
|
"ecs": { "version": "8.0.0" },
|
|
79
78
|
"agent": {
|
|
80
79
|
"name": "axios-interceptor-logger",
|
|
81
|
-
"type": "
|
|
82
|
-
"version": "1.0.
|
|
80
|
+
"type": "axios-logger",
|
|
81
|
+
"version": "1.0.2"
|
|
83
82
|
},
|
|
84
83
|
"event": {
|
|
85
84
|
"start": "2026-09-11T09:34:31.799Z",
|
|
@@ -109,3 +108,27 @@ The logger intercepts the traffic and outputs standard Packetbeat ECS JSON:
|
|
|
109
108
|
}
|
|
110
109
|
}
|
|
111
110
|
```
|
|
111
|
+
|
|
112
|
+
## Requirements
|
|
113
|
+
|
|
114
|
+
- Node.js >= 16 (uses `crypto.randomUUID` and Node's `fs`/streams APIs).
|
|
115
|
+
- **Server-side only.** This package imports Node core modules directly and is not meant to be bundled into a browser build — using it there will throw or fail to bundle.
|
|
116
|
+
|
|
117
|
+
## Responsibility & Disclaimer
|
|
118
|
+
|
|
119
|
+
This library sits between your code and the network, so "is my logging safe/reliable" depends on more than just this package. To be explicit about where each concern is handled:
|
|
120
|
+
|
|
121
|
+
**This library (`axios-interceptor-logger`) is responsible for:**
|
|
122
|
+
- Not crashing or leaking memory under normal or adversarial payload shapes (bounded recursion depth/keys/array items, backpressure-aware file writes, a circuit breaker on disk errors).
|
|
123
|
+
- Redacting the key names listed in [Default Redacted Keys](#default-redacted-keys), or any additional keys you pass via `redactKeys`.
|
|
124
|
+
- Falling back to `console` output if the configured log file becomes unwritable, so log entries aren't silently dropped.
|
|
125
|
+
|
|
126
|
+
**`axios` (the peer dependency) is responsible for:**
|
|
127
|
+
- Actually performing the HTTP request/response lifecycle this library hooks into — bugs in header normalization, redirects, or request/response shape are upstream of this package. Pin an `axios@^1.x` version you trust and keep it patched.
|
|
128
|
+
|
|
129
|
+
**Your application (the dependent) is responsible for:**
|
|
130
|
+
- **Log rotation and disk space.** This library appends to a single file forever; it does not rotate or cap file size. Point `filePath` at a location managed by `logrotate`, your process manager, or a log-shipping sidecar — otherwise the log file will grow until the disk fills up.
|
|
131
|
+
- **Writable storage.** On read-only filesystems (some serverless/container platforms), file writes will fail and the logger falls back to `console`; set `filePath` to a writable path (e.g. `/tmp`) explicitly rather than relying on the fallback.
|
|
132
|
+
- **What actually counts as sensitive for your API.** The default `redactKeys` only catches common secret *names*. Fields like SSNs, card numbers, or app-specific PII in the body will be logged in cleartext (subject to the per-string truncation cap) unless you add them to `redactKeys` or route those endpoints through `ignoreDomains`.
|
|
133
|
+
- **Compliance requirements** (GDPR, PCI-DSS, etc.) for what you're allowed to persist to disk — this library logs request/response bodies by design; it's your call which traffic should go through it.
|
|
134
|
+
- **Throughput at scale.** Sanitizing and serializing very large request/response bodies is synchronous CPU work (deferred one tick via `setImmediate`, not offloaded to a worker). For endpoints with large payloads, consider `ignoreDomains` or trimming what you send/receive.
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
|
|
1
|
+
"use strict";var M=Object.create;var A=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var b=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var q=(i,e)=>{for(var t in e)A(i,t,{get:e[t],enumerable:!0})},I=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of U(e))!P.call(i,n)&&n!==t&&A(i,n,{get:()=>e[n],enumerable:!(r=B(e,n))||r.enumerable});return i};var _=(i,e,t)=>(t=i!=null?M(b(i)):{},I(e||!i||!i.__esModule?A(t,"default",{value:i,enumerable:!0}):t,i)),O=i=>I(A({},"__esModule",{value:!0}),i);var Y={};q(Y,{AxiosLoggerSingleton:()=>S});module.exports=O(Y);var v=_(require("crypto"));var X=["authorization","password","token","secret","cookie","apikey","api_key","api-key","privatekey","private_key","private-key"];function m(i,e=X,t=10240){let r=Math.min(Math.max(0,t||0),1e4),n=new WeakSet;function o(s,l=0){if(s==null)return s;if(l>=10)return"[MAX_DEPTH_EXCEEDED]";if(typeof s!="object"&&typeof s!="function")return typeof s=="string"?s.length>r?s.substring(0,r)+`... [TRUNCATED ${s.length-r} CHARACTERS]`:s:typeof s=="bigint"?s.toString():s;if(Buffer.isBuffer(s)||s instanceof ArrayBuffer||typeof Blob<"u"&&s instanceof Blob){let f=Buffer.isBuffer(s)?"Buffer":s instanceof ArrayBuffer?"ArrayBuffer":"Blob",c=s.byteLength||s.size||0;return{"[BINARY_DATA]":`<Type: ${f}, Size: ${c} bytes>`}}if(n.has(s))return"[CIRCULAR_REFERENCE]";n.add(s);let a;if(Array.isArray(s)){let f=s.length>100;a=s.slice(0,100).map(c=>o(c,l+1)),f&&a.push(`[... ${s.length-100} MORE ITEMS TRUNCATED]`)}else{a={};let f=Object.keys(s),c=f.length>50;for(let u of f.slice(0,50))if(e.some(g=>u.toLowerCase().includes(g.toLowerCase())))a[u]="[REDACTED]";else try{a[u]=o(s[u],l+1)}catch{a[u]="[UNREADABLE_PROPERTY]"}c&&(a["[TRUNCATED_KEYS]"]=`${f.length-50} more keys omitted`)}return n.delete(s),a}return o(i)}var h=class{log(e){console.log(JSON.stringify(e,null,2))}};var E=_(require("fs")),T=_(require("path"));var d=class{filePath;stream=null;fallbackTransport=null;initializing=!1;backpressured=!1;queue=[];queueBytes=0;MAX_QUEUE_BYTES=20*1024*1024;lastErrorTime=0;COOLDOWN_MS=6e4;constructor(e){this.filePath=e||T.join(process.cwd(),"logs","http-transactions.log")}async initStream(){if(!(this.initializing||this.stream)&&!(Date.now()-this.lastErrorTime<this.COOLDOWN_MS)){this.initializing=!0;try{let e=T.dirname(this.filePath);await E.promises.mkdir(e,{recursive:!0}),this.stream=E.createWriteStream(this.filePath,{flags:"a"}),this.stream.on("error",t=>{this.handleStreamError(t)}),this.stream.on("drain",()=>{this.backpressured=!1,this.flushQueue()}),this.flushQueue()}catch(e){this.handleStreamError(e)}finally{this.initializing=!1}}}handleStreamError(e){let t=!this.fallbackTransport;this.stream&&(this.stream.destroy(),this.stream=null),this.lastErrorTime=Date.now(),t&&(this.fallbackTransport=new h,console.error(`[Axios Logger] Critical failure creating file transport at ${this.filePath}. Falling back to console. Error:`,e)),this.flushQueue()}flushQueue(){if(this.queue.length!==0){if(!this.stream||this.backpressured){if(this.fallbackTransport)for(;this.queue.length>0;){let e=this.queue.shift();e&&(this.queueBytes-=e.length*2,process.stdout.write(e))}return}for(;this.queue.length>0;){let e=this.queue.shift();if(e&&(this.queueBytes-=e.length*2,!this.stream.write(e))){this.backpressured=!0;break}}}}log(e){if(!this.stream&&this.lastErrorTime>0){if(Date.now()-this.lastErrorTime>this.COOLDOWN_MS)this.initStream();else if(this.fallbackTransport){this.fallbackTransport.log(e);return}}!this.stream&&!this.initializing&&this.lastErrorTime===0&&this.initStream();let t;try{t=JSON.stringify(e)+`
|
|
2
|
+
`}catch(r){t=JSON.stringify({error:"Failed to stringify log payload",message:r.message})+`
|
|
3
|
+
`}if(this.stream&&!this.backpressured)this.stream.write(t)||(this.backpressured=!0);else{let r=t.length*2,n=e.status==="Error"||!!e.error,o=this.MAX_QUEUE_BYTES*.8;if(this.queueBytes+r<(n?this.MAX_QUEUE_BYTES:o))this.queue.push(t),this.queueBytes+=r;else{let s=Date.now();(s-this.lastDropWarningTime>1e4||!this.lastDropWarningTime)&&(this.lastDropWarningTime=s,console.warn(`[Axios Logger] Dropping ${n?"ERROR":"INFO"} logs due to disk backpressure. Queue memory limit reached.`))}}}};var k="1.0.2";var R=Symbol("axios_logging_metadata");function C(i){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(i)}function N(){if(!(typeof process<"u"&&!!process.versions&&!!process.versions.node))throw new Error("[axios-interceptor-logger] This library uses Node.js core modules (fs, crypto) and is not supported outside a Node.js runtime (e.g. browser bundles).")}var S=class i{static instance;config;transport;constructor(e){N(),this.config=e||{},this.config.customLogger?this.transport=this.config.customLogger:this.config.transportMode==="otlp"?(console.warn("OTLP transport is marked as future scope. Falling back to FileTransport."),this.transport=new d(this.config.filePath)):this.config.transportMode==="file"||!this.config.transportMode?this.transport=new d(this.config.filePath):this.transport=new d(this.config.filePath)}static getInstance(e){return i.instance||(i.instance=new i(e)),i.instance}attach(e){let t=e.interceptors.request.use(n=>this.handleRequest(n),n=>Promise.reject(n)),r=e.interceptors.response.use(n=>this.handleResponse(n),n=>this.handleError(n));return{eject:()=>{e.interceptors.request.eject(t),e.interceptors.response.eject(r)}}}isDomainIgnored(e){if(!e||!this.config.ignoreDomains)return!1;let t=e.url||"";e.baseURL&&!t.startsWith("http")&&(t=e.baseURL.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""));try{let r=new URL(t,t.startsWith("/")?"http://localhost":void 0);return this.config.ignoreDomains.includes(r.hostname)}catch{return!1}}handleRequest(e){if(this.isDomainIgnored(e))return e;let t=e.headers["X-Correlation-ID"];return(!t||!C(t))&&(t=v.default.randomUUID()),e.headers["X-Correlation-ID"]=t,e[R]={id:t,startTime:Date.now(),logged:!1},e}handleResponse(e){return this.isDomainIgnored(e.config)||setImmediate(()=>this.logTransaction(e.config,e,null)),e}handleError(e){if(e.config&&this.isDomainIgnored(e.config))return Promise.reject(e);let t=e.config,r,n=!1;if(t&&(r=t[R]),!r){let o;t&&t.headers&&t.headers["X-Correlation-ID"]?o=t.headers["X-Correlation-ID"]:e.response&&e.response.config&&e.response.config.headers["X-Correlation-ID"]?o=e.response.config.headers["X-Correlation-ID"]:e.request&&typeof e.request.getHeader=="function"&&(o=e.request.getHeader("X-Correlation-ID")),(!o||!C(o))&&(o=v.default.randomUUID(),n=!0),r={id:o,startTime:Date.now(),logged:!1},t&&(t[R]=r)}return setImmediate(()=>this.logTransaction(t,e.response,e,n)),Promise.reject(e)}logTransaction(e,t,r,n=!1){if(!e)return;let o=e[R];if(!o||o.logged)return;o.logged=!0;let s=Date.now()-o.startTime,l=e.url||"";e.baseURL&&!l.startsWith("http")&&(l=e.baseURL.replace(/\/+$/,"")+"/"+l.replace(/^\/+/,""));let a,f;try{a=new URL(l,l.startsWith("/")?"http://localhost":void 0),a.port&&(f=parseInt(a.port,10))}catch{}let c=e.headers?m({...e.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},u;c["content-length"]?u=parseInt(c["content-length"],10):e.data&&typeof e.data=="string"&&(u=Buffer.byteLength(e.data,"utf8"));let g=new Date().toISOString(),w=new Date(o.startTime).toISOString(),x=s*1e6,L=a?a.hostname:"unknown",y={"@timestamp":g,ecs:{version:"8.0.0"},agent:{name:"axios-interceptor-logger",type:"axios-logger",version:k},event:{start:w,end:g,duration:x,kind:"event",category:["network"],type:["connection","protocol"],dataset:"http"},http:{request:{method:(e.method||"GET").toUpperCase(),bytes:u,headers:c,body:e.data?{content:m(e.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0},version:"1.1"},url:{full:l,path:a?a.pathname:l,query:a&&a.search?a.search.replace("?",""):void 0,scheme:a?a.protocol.replace(":",""):"unknown",domain:L,port:f},source:{domain:typeof process<"u"&&process.env&&process.env.APP_HOST?process.env.APP_HOST:"localhost"},destination:{domain:L},server:{domain:L},network:{protocol:"http"},status:r?"Error":"OK","trace.id":o.id};if(t){let p=t.headers?m({...t.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},D;p["content-length"]?D=parseInt(p["content-length"],10):t.data&&typeof t.data=="string"&&(D=Buffer.byteLength(t.data,"utf8")),y.http.response={status_code:t.status,headers:p,body:t.data?{content:m(t.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0,bytes:D}}r&&(y.error={message:r.message?String(r.message):"Unknown error",code:r.code||r.code?String(r.code||r.code):void 0}),this.config.verbose&&new h().log(y),this.transport.log(y)}};0&&(module.exports={AxiosLoggerSingleton});
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import _ from"crypto";var C=["authorization","password","token","secret","cookie","apikey","api_key","api-key","privatekey","private_key","private-key"];function m(o,e=C,t=10240){let r=Math.min(Math.max(0,t||0),1e4),a=new WeakSet;function n(s,l=0){if(s==null)return s;if(l>=10)return"[MAX_DEPTH_EXCEEDED]";if(typeof s!="object"&&typeof s!="function")return typeof s=="string"?s.length>r?s.substring(0,r)+`... [TRUNCATED ${s.length-r} CHARACTERS]`:s:typeof s=="bigint"?s.toString():s;if(Buffer.isBuffer(s)||s instanceof ArrayBuffer||typeof Blob<"u"&&s instanceof Blob){let f=Buffer.isBuffer(s)?"Buffer":s instanceof ArrayBuffer?"ArrayBuffer":"Blob",c=s.byteLength||s.size||0;return{"[BINARY_DATA]":`<Type: ${f}, Size: ${c} bytes>`}}if(a.has(s))return"[CIRCULAR_REFERENCE]";a.add(s);let i;if(Array.isArray(s)){let f=s.length>100;i=s.slice(0,100).map(c=>n(c,l+1)),f&&i.push(`[... ${s.length-100} MORE ITEMS TRUNCATED]`)}else{i={};let f=Object.keys(s),c=f.length>50;for(let u of f.slice(0,50))if(e.some(g=>u.toLowerCase().includes(g.toLowerCase())))i[u]="[REDACTED]";else try{i[u]=n(s[u],l+1)}catch{i[u]="[UNREADABLE_PROPERTY]"}c&&(i["[TRUNCATED_KEYS]"]=`${f.length-50} more keys omitted`)}return a.delete(s),i}return n(o)}var h=class{log(e){console.log(JSON.stringify(e,null,2))}};import*as A from"fs";import*as E from"path";var d=class{filePath;stream=null;fallbackTransport=null;initializing=!1;backpressured=!1;queue=[];queueBytes=0;MAX_QUEUE_BYTES=20*1024*1024;lastErrorTime=0;COOLDOWN_MS=6e4;constructor(e){this.filePath=e||E.join(process.cwd(),"logs","http-transactions.log")}async initStream(){if(!(this.initializing||this.stream)&&!(Date.now()-this.lastErrorTime<this.COOLDOWN_MS)){this.initializing=!0;try{let e=E.dirname(this.filePath);await A.promises.mkdir(e,{recursive:!0}),this.stream=A.createWriteStream(this.filePath,{flags:"a"}),this.stream.on("error",t=>{this.handleStreamError(t)}),this.stream.on("drain",()=>{this.backpressured=!1,this.flushQueue()}),this.flushQueue()}catch(e){this.handleStreamError(e)}finally{this.initializing=!1}}}handleStreamError(e){let t=!this.fallbackTransport;this.stream&&(this.stream.destroy(),this.stream=null),this.lastErrorTime=Date.now(),t&&(this.fallbackTransport=new h,console.error(`[Axios Logger] Critical failure creating file transport at ${this.filePath}. Falling back to console. Error:`,e)),this.flushQueue()}flushQueue(){if(this.queue.length!==0){if(!this.stream||this.backpressured){if(this.fallbackTransport)for(;this.queue.length>0;){let e=this.queue.shift();e&&(this.queueBytes-=e.length*2,process.stdout.write(e))}return}for(;this.queue.length>0;){let e=this.queue.shift();if(e&&(this.queueBytes-=e.length*2,!this.stream.write(e))){this.backpressured=!0;break}}}}log(e){if(!this.stream&&this.lastErrorTime>0){if(Date.now()-this.lastErrorTime>this.COOLDOWN_MS)this.initStream();else if(this.fallbackTransport){this.fallbackTransport.log(e);return}}!this.stream&&!this.initializing&&this.lastErrorTime===0&&this.initStream();let t;try{t=JSON.stringify(e)+`
|
|
2
|
+
`}catch(r){t=JSON.stringify({error:"Failed to stringify log payload",message:r.message})+`
|
|
3
|
+
`}if(this.stream&&!this.backpressured)this.stream.write(t)||(this.backpressured=!0);else{let r=t.length*2,a=e.status==="Error"||!!e.error,n=this.MAX_QUEUE_BYTES*.8;if(this.queueBytes+r<(a?this.MAX_QUEUE_BYTES:n))this.queue.push(t),this.queueBytes+=r;else{let s=Date.now();(s-this.lastDropWarningTime>1e4||!this.lastDropWarningTime)&&(this.lastDropWarningTime=s,console.warn(`[Axios Logger] Dropping ${a?"ERROR":"INFO"} logs due to disk backpressure. Queue memory limit reached.`))}}}};var D="1.0.2";var T=Symbol("axios_logging_metadata");function v(o){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(o)}function w(){if(!(typeof process<"u"&&!!process.versions&&!!process.versions.node))throw new Error("[axios-interceptor-logger] This library uses Node.js core modules (fs, crypto) and is not supported outside a Node.js runtime (e.g. browser bundles).")}var L=class o{static instance;config;transport;constructor(e){w(),this.config=e||{},this.config.customLogger?this.transport=this.config.customLogger:this.config.transportMode==="otlp"?(console.warn("OTLP transport is marked as future scope. Falling back to FileTransport."),this.transport=new d(this.config.filePath)):this.config.transportMode==="file"||!this.config.transportMode?this.transport=new d(this.config.filePath):this.transport=new d(this.config.filePath)}static getInstance(e){return o.instance||(o.instance=new o(e)),o.instance}attach(e){let t=e.interceptors.request.use(a=>this.handleRequest(a),a=>Promise.reject(a)),r=e.interceptors.response.use(a=>this.handleResponse(a),a=>this.handleError(a));return{eject:()=>{e.interceptors.request.eject(t),e.interceptors.response.eject(r)}}}isDomainIgnored(e){if(!e||!this.config.ignoreDomains)return!1;let t=e.url||"";e.baseURL&&!t.startsWith("http")&&(t=e.baseURL.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""));try{let r=new URL(t,t.startsWith("/")?"http://localhost":void 0);return this.config.ignoreDomains.includes(r.hostname)}catch{return!1}}handleRequest(e){if(this.isDomainIgnored(e))return e;let t=e.headers["X-Correlation-ID"];return(!t||!v(t))&&(t=_.randomUUID()),e.headers["X-Correlation-ID"]=t,e[T]={id:t,startTime:Date.now(),logged:!1},e}handleResponse(e){return this.isDomainIgnored(e.config)||setImmediate(()=>this.logTransaction(e.config,e,null)),e}handleError(e){if(e.config&&this.isDomainIgnored(e.config))return Promise.reject(e);let t=e.config,r,a=!1;if(t&&(r=t[T]),!r){let n;t&&t.headers&&t.headers["X-Correlation-ID"]?n=t.headers["X-Correlation-ID"]:e.response&&e.response.config&&e.response.config.headers["X-Correlation-ID"]?n=e.response.config.headers["X-Correlation-ID"]:e.request&&typeof e.request.getHeader=="function"&&(n=e.request.getHeader("X-Correlation-ID")),(!n||!v(n))&&(n=_.randomUUID(),a=!0),r={id:n,startTime:Date.now(),logged:!1},t&&(t[T]=r)}return setImmediate(()=>this.logTransaction(t,e.response,e,a)),Promise.reject(e)}logTransaction(e,t,r,a=!1){if(!e)return;let n=e[T];if(!n||n.logged)return;n.logged=!0;let s=Date.now()-n.startTime,l=e.url||"";e.baseURL&&!l.startsWith("http")&&(l=e.baseURL.replace(/\/+$/,"")+"/"+l.replace(/^\/+/,""));let i,f;try{i=new URL(l,l.startsWith("/")?"http://localhost":void 0),i.port&&(f=parseInt(i.port,10))}catch{}let c=e.headers?m({...e.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},u;c["content-length"]?u=parseInt(c["content-length"],10):e.data&&typeof e.data=="string"&&(u=Buffer.byteLength(e.data,"utf8"));let g=new Date().toISOString(),I=new Date(n.startTime).toISOString(),k=s*1e6,R=i?i.hostname:"unknown",y={"@timestamp":g,ecs:{version:"8.0.0"},agent:{name:"axios-interceptor-logger",type:"axios-logger",version:D},event:{start:I,end:g,duration:k,kind:"event",category:["network"],type:["connection","protocol"],dataset:"http"},http:{request:{method:(e.method||"GET").toUpperCase(),bytes:u,headers:c,body:e.data?{content:m(e.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0},version:"1.1"},url:{full:l,path:i?i.pathname:l,query:i&&i.search?i.search.replace("?",""):void 0,scheme:i?i.protocol.replace(":",""):"unknown",domain:R,port:f},source:{domain:typeof process<"u"&&process.env&&process.env.APP_HOST?process.env.APP_HOST:"localhost"},destination:{domain:R},server:{domain:R},network:{protocol:"http"},status:r?"Error":"OK","trace.id":n.id};if(t){let p=t.headers?m({...t.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},S;p["content-length"]?S=parseInt(p["content-length"],10):t.data&&typeof t.data=="string"&&(S=Buffer.byteLength(t.data,"utf8")),y.http.response={status_code:t.status,headers:p,body:t.data?{content:m(t.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0,bytes:S}}r&&(y.error={message:r.message?String(r.message):"Unknown error",code:r.code||r.code?String(r.code||r.code):void 0}),this.config.verbose&&new h().log(y),this.transport.log(y)}};export{L as AxiosLoggerSingleton};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "axios-interceptor-logger",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Axios Unified HTTP Transaction Logger",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean --minify",
|
|
20
|
-
"test": "
|
|
20
|
+
"test": "NODE_OPTIONS='--max-old-space-size=16' node --import tsx --test tests/**/*.test.ts"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"axios",
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
],
|
|
31
31
|
"author": "Kamalyesh Kannadkar <k.kamalyesh@gmail.com>",
|
|
32
32
|
"license": "MIT",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=16.0.0"
|
|
35
|
+
},
|
|
33
36
|
"repository": {
|
|
34
37
|
"type": "git",
|
|
35
38
|
"url": "git+https://github.com/kkamalyesh-ops/axios-interceptor-logger.git"
|
|
@@ -38,7 +41,6 @@
|
|
|
38
41
|
"url": "https://github.com/kkamalyesh-ops/axios-interceptor-logger/issues"
|
|
39
42
|
},
|
|
40
43
|
"homepage": "https://github.com/kkamalyesh-ops/axios-interceptor-logger#readme",
|
|
41
|
-
"dependencies": {},
|
|
42
44
|
"peerDependencies": {
|
|
43
45
|
"axios": "^1.0.0"
|
|
44
46
|
},
|
|
@@ -46,6 +48,7 @@
|
|
|
46
48
|
"@types/node": "^20.0.0",
|
|
47
49
|
"axios": "^1.6.0",
|
|
48
50
|
"tsup": "^8.0.0",
|
|
51
|
+
"tsx": "^4.23.13",
|
|
49
52
|
"typescript": "^5.0.0"
|
|
50
53
|
}
|
|
51
54
|
}
|