Преглед изворни кода

Merge pull request #32217 from overleaf/ar-log-long-fetches

[fetch-utils] allow long running requests to be logged

GitOrigin-RevId: 19688937b8aefe314b6bcde98c61badf4f250472
Andrew Rumble пре 6 месеци
родитељ
комит
6f8a2ea9e3

+ 26 - 7
libraries/fetch-utils/index.js

@@ -5,6 +5,12 @@ const fetch = require('node-fetch')
 const http = require('node:http')
 const https = require('node:https')
 
+let logger
+
+function setLogger(loggerInstance) {
+  logger = loggerInstance
+}
+
 /**
  * @import { Response } from 'node-fetch'
  */
@@ -23,7 +29,7 @@ async function fetchJson(url, opts = {}) {
 }
 
 async function fetchJsonWithResponse(url, opts = {}) {
-  const { fetchOpts, detachSignal } = parseOpts(opts)
+  const { fetchOpts, detachSignal } = parseOpts(opts, url)
   fetchOpts.headers = fetchOpts.headers ?? {}
   fetchOpts.headers.Accept = fetchOpts.headers.Accept ?? 'application/json'
 
@@ -53,7 +59,7 @@ async function fetchStream(url, opts = {}) {
 }
 
 async function fetchStreamWithResponse(url, opts = {}) {
-  const { fetchOpts, abortController, detachSignal } = parseOpts(opts)
+  const { fetchOpts, abortController, detachSignal } = parseOpts(opts, url)
   const response = await performRequest(url, fetchOpts, detachSignal)
 
   if (!response.ok) {
@@ -76,7 +82,7 @@ async function fetchStreamWithResponse(url, opts = {}) {
  * @throws {RequestFailedError} if the response has a failure status code
  */
 async function fetchNothing(url, opts = {}) {
-  const { fetchOpts, detachSignal } = parseOpts(opts)
+  const { fetchOpts, detachSignal } = parseOpts(opts, url)
   const response = await performRequest(url, fetchOpts, detachSignal)
   if (!response.ok) {
     const body = await maybeGetResponseBody(response)
@@ -108,7 +114,7 @@ async function fetchRedirect(url, opts = {}) {
  * @throws {RequestFailedError} if the response has a non redirect status code or missing Location header
  */
 async function fetchRedirectWithResponse(url, opts = {}) {
-  const { fetchOpts, detachSignal } = parseOpts(opts)
+  const { fetchOpts, detachSignal } = parseOpts(opts, url)
   fetchOpts.redirect = 'manual'
   const response = await performRequest(url, fetchOpts, detachSignal)
   if (response.status < 300 || response.status >= 400) {
@@ -142,7 +148,7 @@ async function fetchString(url, opts = {}) {
 }
 
 async function fetchStringWithResponse(url, opts = {}) {
-  const { fetchOpts, detachSignal } = parseOpts(opts)
+  const { fetchOpts, detachSignal } = parseOpts(opts, url)
   const response = await performRequest(url, fetchOpts, detachSignal)
   if (!response.ok) {
     const body = await maybeGetResponseBody(response)
@@ -167,7 +173,7 @@ class RequestFailedError extends OError {
   }
 }
 
-function parseOpts(opts) {
+function parseOpts(opts, url) {
   const fetchOpts = _.omit(opts, ['json', 'signal', 'basicAuth'])
   if (opts.json) {
     setupJsonBody(fetchOpts, opts.json)
@@ -178,9 +184,21 @@ function parseOpts(opts) {
 
   const abortController = new AbortController()
   fetchOpts.signal = abortController.signal
-  let detachSignal = () => {}
+  let detachSignal
   if (opts.signal) {
     detachSignal = abortOnSignal(abortController, opts.signal)
+  } else {
+    const timeout = setTimeout(() => {
+      if (logger) {
+        logger.warn(
+          { url, method: opts.method ?? 'GET' },
+          'Fetch request did not complete within 120 seconds'
+        )
+      }
+    }, 120000)
+    detachSignal = () => {
+      clearTimeout(timeout)
+    }
   }
   if (opts.body instanceof Readable) {
     abortOnDestroyedRequest(abortController, fetchOpts.body)
@@ -323,4 +341,5 @@ module.exports = {
   ConnectTimeoutError,
   CustomHttpAgent,
   CustomHttpsAgent,
+  setLogger,
 }

+ 2 - 0
libraries/logger/logging-manager.js

@@ -6,6 +6,7 @@ const {
   FileLogLevelChecker,
   GCEMetadataLogLevelChecker,
 } = require('./log-level-checker')
+const { setLogger } = require('@overleaf/fetch-utils')
 
 const LoggingManager = {
   /**
@@ -31,6 +32,7 @@ const LoggingManager = {
     })
     this._setupRingBuffer()
     this._setupLogLevelChecker()
+    setLogger(this)
     return this
   },
 

+ 4 - 0
libraries/logger/test/unit/logging-manager-tests.js

@@ -43,9 +43,13 @@ describe('LoggingManager', function () {
         .stub()
         .returns(this.GCEMetadataLogLevelChecker),
     }
+    this.fetchUtils = {
+      setLogger: sinon.stub(),
+    }
     this.LoggingManager = SandboxedModule.require(MODULE_PATH, {
       requires: {
         bunyan: this.Bunyan,
+        '@overleaf/fetch-utils': this.fetchUtils,
         './log-level-checker': this.LogLevelChecker,
       },
     })