diagnostics.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. 'use strict'
  2. const diagnosticsChannel = require('node:diagnostics_channel')
  3. const util = require('node:util')
  4. const undiciDebugLog = util.debuglog('undici')
  5. const fetchDebuglog = util.debuglog('fetch')
  6. const websocketDebuglog = util.debuglog('websocket')
  7. const channels = {
  8. // Client
  9. beforeConnect: diagnosticsChannel.channel('undici:client:beforeConnect'),
  10. connected: diagnosticsChannel.channel('undici:client:connected'),
  11. connectError: diagnosticsChannel.channel('undici:client:connectError'),
  12. sendHeaders: diagnosticsChannel.channel('undici:client:sendHeaders'),
  13. // Request
  14. create: diagnosticsChannel.channel('undici:request:create'),
  15. bodySent: diagnosticsChannel.channel('undici:request:bodySent'),
  16. bodyChunkSent: diagnosticsChannel.channel('undici:request:bodyChunkSent'),
  17. bodyChunkReceived: diagnosticsChannel.channel('undici:request:bodyChunkReceived'),
  18. headers: diagnosticsChannel.channel('undici:request:headers'),
  19. trailers: diagnosticsChannel.channel('undici:request:trailers'),
  20. error: diagnosticsChannel.channel('undici:request:error'),
  21. // WebSocket
  22. open: diagnosticsChannel.channel('undici:websocket:open'),
  23. close: diagnosticsChannel.channel('undici:websocket:close'),
  24. socketError: diagnosticsChannel.channel('undici:websocket:socket_error'),
  25. ping: diagnosticsChannel.channel('undici:websocket:ping'),
  26. pong: diagnosticsChannel.channel('undici:websocket:pong'),
  27. // ProxyAgent
  28. proxyConnected: diagnosticsChannel.channel('undici:proxy:connected')
  29. }
  30. let isTrackingClientEvents = false
  31. function trackClientEvents (debugLog = undiciDebugLog) {
  32. if (isTrackingClientEvents) {
  33. return
  34. }
  35. // Check if any of the channels already have subscribers to prevent duplicate subscriptions
  36. // This can happen when both Node.js built-in undici and undici as a dependency are present
  37. if (channels.beforeConnect.hasSubscribers || channels.connected.hasSubscribers ||
  38. channels.connectError.hasSubscribers || channels.sendHeaders.hasSubscribers) {
  39. isTrackingClientEvents = true
  40. return
  41. }
  42. isTrackingClientEvents = true
  43. diagnosticsChannel.subscribe('undici:client:beforeConnect',
  44. evt => {
  45. const {
  46. connectParams: { version, protocol, port, host }
  47. } = evt
  48. debugLog(
  49. 'connecting to %s%s using %s%s',
  50. host,
  51. port ? `:${port}` : '',
  52. protocol,
  53. version
  54. )
  55. })
  56. diagnosticsChannel.subscribe('undici:client:connected',
  57. evt => {
  58. const {
  59. connectParams: { version, protocol, port, host }
  60. } = evt
  61. debugLog(
  62. 'connected to %s%s using %s%s',
  63. host,
  64. port ? `:${port}` : '',
  65. protocol,
  66. version
  67. )
  68. })
  69. diagnosticsChannel.subscribe('undici:client:connectError',
  70. evt => {
  71. const {
  72. connectParams: { version, protocol, port, host },
  73. error
  74. } = evt
  75. debugLog(
  76. 'connection to %s%s using %s%s errored - %s',
  77. host,
  78. port ? `:${port}` : '',
  79. protocol,
  80. version,
  81. error.message
  82. )
  83. })
  84. diagnosticsChannel.subscribe('undici:client:sendHeaders',
  85. evt => {
  86. const {
  87. request: { method, path, origin }
  88. } = evt
  89. debugLog('sending request to %s %s%s', method, origin, path)
  90. })
  91. }
  92. let isTrackingRequestEvents = false
  93. function trackRequestEvents (debugLog = undiciDebugLog) {
  94. if (isTrackingRequestEvents) {
  95. return
  96. }
  97. // Check if any of the channels already have subscribers to prevent duplicate subscriptions
  98. // This can happen when both Node.js built-in undici and undici as a dependency are present
  99. if (channels.headers.hasSubscribers || channels.trailers.hasSubscribers ||
  100. channels.error.hasSubscribers) {
  101. isTrackingRequestEvents = true
  102. return
  103. }
  104. isTrackingRequestEvents = true
  105. diagnosticsChannel.subscribe('undici:request:headers',
  106. evt => {
  107. const {
  108. request: { method, path, origin },
  109. response: { statusCode }
  110. } = evt
  111. debugLog(
  112. 'received response to %s %s%s - HTTP %d',
  113. method,
  114. origin,
  115. path,
  116. statusCode
  117. )
  118. })
  119. diagnosticsChannel.subscribe('undici:request:trailers',
  120. evt => {
  121. const {
  122. request: { method, path, origin }
  123. } = evt
  124. debugLog('trailers received from %s %s%s', method, origin, path)
  125. })
  126. diagnosticsChannel.subscribe('undici:request:error',
  127. evt => {
  128. const {
  129. request: { method, path, origin },
  130. error
  131. } = evt
  132. debugLog(
  133. 'request to %s %s%s errored - %s',
  134. method,
  135. origin,
  136. path,
  137. error.message
  138. )
  139. })
  140. }
  141. let isTrackingWebSocketEvents = false
  142. function trackWebSocketEvents (debugLog = websocketDebuglog) {
  143. if (isTrackingWebSocketEvents) {
  144. return
  145. }
  146. // Check if any of the channels already have subscribers to prevent duplicate subscriptions
  147. // This can happen when both Node.js built-in undici and undici as a dependency are present
  148. if (channels.open.hasSubscribers || channels.close.hasSubscribers ||
  149. channels.socketError.hasSubscribers || channels.ping.hasSubscribers ||
  150. channels.pong.hasSubscribers) {
  151. isTrackingWebSocketEvents = true
  152. return
  153. }
  154. isTrackingWebSocketEvents = true
  155. diagnosticsChannel.subscribe('undici:websocket:open',
  156. evt => {
  157. const {
  158. address: { address, port }
  159. } = evt
  160. debugLog('connection opened %s%s', address, port ? `:${port}` : '')
  161. })
  162. diagnosticsChannel.subscribe('undici:websocket:close',
  163. evt => {
  164. const { websocket, code, reason } = evt
  165. debugLog(
  166. 'closed connection to %s - %s %s',
  167. websocket.url,
  168. code,
  169. reason
  170. )
  171. })
  172. diagnosticsChannel.subscribe('undici:websocket:socket_error',
  173. err => {
  174. debugLog('connection errored - %s', err.message)
  175. })
  176. diagnosticsChannel.subscribe('undici:websocket:ping',
  177. evt => {
  178. debugLog('ping received')
  179. })
  180. diagnosticsChannel.subscribe('undici:websocket:pong',
  181. evt => {
  182. debugLog('pong received')
  183. })
  184. }
  185. if (undiciDebugLog.enabled || fetchDebuglog.enabled) {
  186. trackClientEvents(fetchDebuglog.enabled ? fetchDebuglog : undiciDebugLog)
  187. trackRequestEvents(fetchDebuglog.enabled ? fetchDebuglog : undiciDebugLog)
  188. }
  189. if (websocketDebuglog.enabled) {
  190. trackClientEvents(undiciDebugLog.enabled ? undiciDebugLog : websocketDebuglog)
  191. trackWebSocketEvents(websocketDebuglog)
  192. }
  193. module.exports = {
  194. channels
  195. }