@@ -2,10 +2,12 @@ import { Context, Hono } from "hono";
2
2
import { MiddlewareHandler } from "hono/types" ;
3
3
import { performance } from "perf_hooks" ;
4
4
5
+ import { AsyncLocalStorage } from "async_hooks" ;
5
6
import { ApitallyClient } from "../common/client.js" ;
7
+ import { patchConsole } from "../common/consoleCapture.js" ;
6
8
import { consumerFromStringOrObject } from "../common/consumerRegistry.js" ;
7
9
import { parseContentLength } from "../common/headers.js" ;
8
- import { convertHeaders } from "../common/requestLogger.js" ;
10
+ import { convertHeaders , LogRecord } from "../common/requestLogger.js" ;
9
11
import {
10
12
getResponseBody ,
11
13
getResponseJson ,
@@ -37,103 +39,113 @@ export function useApitally(app: Hono, config: ApitallyConfig) {
37
39
}
38
40
39
41
function getMiddleware ( client : ApitallyClient ) : MiddlewareHandler {
42
+ const logsContext = new AsyncLocalStorage < LogRecord [ ] > ( ) ;
43
+
44
+ if ( client . requestLogger . config . captureLogs ) {
45
+ patchConsole ( logsContext ) ;
46
+ }
47
+
40
48
return async ( c , next ) => {
41
49
if ( ! client . isEnabled ( ) || c . req . method . toUpperCase ( ) === "OPTIONS" ) {
42
50
await next ( ) ;
43
51
return ;
44
52
}
45
53
46
- const timestamp = Date . now ( ) / 1000 ;
47
- const startTime = performance . now ( ) ;
48
-
49
- await next ( ) ;
54
+ await logsContext . run ( [ ] , async ( ) => {
55
+ const timestamp = Date . now ( ) / 1000 ;
56
+ const startTime = performance . now ( ) ;
50
57
51
- let response ;
52
- const responseTime = performance . now ( ) - startTime ;
53
- const [ responseSize , newResponse ] = await measureResponseSize ( c . res ) ;
54
- const requestSize = parseContentLength ( c . req . header ( "content-length" ) ) ;
55
-
56
- const consumer = getConsumer ( c ) ;
57
- client . consumerRegistry . addOrUpdateConsumer ( consumer ) ;
58
-
59
- client . requestCounter . addRequest ( {
60
- consumer : consumer ?. identifier ,
61
- method : c . req . method ,
62
- path : c . req . routePath ,
63
- statusCode : c . res . status ,
64
- responseTime,
65
- requestSize,
66
- responseSize,
67
- } ) ;
58
+ await next ( ) ;
68
59
69
- response = newResponse ;
60
+ let response ;
61
+ const responseTime = performance . now ( ) - startTime ;
62
+ const [ responseSize , newResponse ] = await measureResponseSize ( c . res ) ;
63
+ const requestSize = parseContentLength ( c . req . header ( "content-length" ) ) ;
70
64
71
- if ( c . res . status === 400 ) {
72
- const [ responseJson , newResponse ] = await getResponseJson ( response ) ;
73
- const validationErrors = extractZodErrors ( responseJson ) ;
74
- validationErrors . forEach ( ( error ) => {
75
- client . validationErrorCounter . addValidationError ( {
76
- consumer : consumer ?. identifier ,
77
- method : c . req . method ,
78
- path : c . req . routePath ,
79
- ...error ,
80
- } ) ;
81
- } ) ;
82
- response = newResponse ;
83
- }
65
+ const consumer = getConsumer ( c ) ;
66
+ client . consumerRegistry . addOrUpdateConsumer ( consumer ) ;
84
67
85
- if ( c . error ) {
86
- client . serverErrorCounter . addServerError ( {
68
+ client . requestCounter . addRequest ( {
87
69
consumer : consumer ?. identifier ,
88
70
method : c . req . method ,
89
71
path : c . req . routePath ,
90
- type : c . error . name ,
91
- msg : c . error . message ,
92
- traceback : c . error . stack || "" ,
72
+ statusCode : c . res . status ,
73
+ responseTime,
74
+ requestSize,
75
+ responseSize,
93
76
} ) ;
94
- }
95
77
96
- if ( client . requestLogger . enabled ) {
97
- let requestBody ;
98
- let responseBody ;
99
- let newResponse = response ;
100
- const requestContentType = c . req . header ( "content-type" ) ;
101
- const responseContentType = c . res . headers . get ( "content-type" ) ;
102
- if (
103
- client . requestLogger . config . logRequestBody &&
104
- client . requestLogger . isSupportedContentType ( requestContentType )
105
- ) {
106
- requestBody = Buffer . from ( await c . req . arrayBuffer ( ) ) ;
107
- }
108
- if (
109
- client . requestLogger . config . logResponseBody &&
110
- client . requestLogger . isSupportedContentType ( responseContentType )
111
- ) {
112
- [ responseBody , newResponse ] = await getResponseBody ( response ) ;
78
+ response = newResponse ;
79
+
80
+ if ( c . res . status === 400 ) {
81
+ const [ responseJson , newResponse ] = await getResponseJson ( response ) ;
82
+ const validationErrors = extractZodErrors ( responseJson ) ;
83
+ validationErrors . forEach ( ( error ) => {
84
+ client . validationErrorCounter . addValidationError ( {
85
+ consumer : consumer ?. identifier ,
86
+ method : c . req . method ,
87
+ path : c . req . routePath ,
88
+ ...error ,
89
+ } ) ;
90
+ } ) ;
113
91
response = newResponse ;
114
92
}
115
- client . requestLogger . logRequest (
116
- {
117
- timestamp,
93
+
94
+ if ( c . error ) {
95
+ client . serverErrorCounter . addServerError ( {
96
+ consumer : consumer ?. identifier ,
118
97
method : c . req . method ,
119
98
path : c . req . routePath ,
120
- url : c . req . url ,
121
- headers : convertHeaders ( c . req . header ( ) ) ,
122
- size : Number ( requestSize ) ,
123
- consumer : consumer ?. identifier ,
124
- body : requestBody ,
125
- } ,
126
- {
127
- statusCode : c . res . status ,
128
- responseTime : responseTime / 1000 ,
129
- headers : convertHeaders ( c . res . headers ) ,
130
- size : responseSize ,
131
- body : responseBody ,
132
- } ,
133
- c . error ,
134
- ) ;
135
- }
136
- c . res = response ;
99
+ type : c . error . name ,
100
+ msg : c . error . message ,
101
+ traceback : c . error . stack || "" ,
102
+ } ) ;
103
+ }
104
+
105
+ if ( client . requestLogger . enabled ) {
106
+ let requestBody ;
107
+ let responseBody ;
108
+ let newResponse = response ;
109
+ const requestContentType = c . req . header ( "content-type" ) ;
110
+ const responseContentType = c . res . headers . get ( "content-type" ) ;
111
+ if (
112
+ client . requestLogger . config . logRequestBody &&
113
+ client . requestLogger . isSupportedContentType ( requestContentType )
114
+ ) {
115
+ requestBody = Buffer . from ( await c . req . arrayBuffer ( ) ) ;
116
+ }
117
+ if (
118
+ client . requestLogger . config . logResponseBody &&
119
+ client . requestLogger . isSupportedContentType ( responseContentType )
120
+ ) {
121
+ [ responseBody , newResponse ] = await getResponseBody ( response ) ;
122
+ response = newResponse ;
123
+ }
124
+ const logs = logsContext . getStore ( ) ;
125
+ client . requestLogger . logRequest (
126
+ {
127
+ timestamp,
128
+ method : c . req . method ,
129
+ path : c . req . routePath ,
130
+ url : c . req . url ,
131
+ headers : convertHeaders ( c . req . header ( ) ) ,
132
+ size : Number ( requestSize ) ,
133
+ consumer : consumer ?. identifier ,
134
+ body : requestBody ,
135
+ } ,
136
+ {
137
+ statusCode : c . res . status ,
138
+ responseTime : responseTime / 1000 ,
139
+ headers : convertHeaders ( c . res . headers ) ,
140
+ size : responseSize ,
141
+ body : responseBody ,
142
+ } ,
143
+ c . error ,
144
+ logs ,
145
+ ) ;
146
+ }
147
+ c . res = response ;
148
+ } ) ;
137
149
} ;
138
150
}
139
151
0 commit comments