Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): print cause of uncaught errors in their stack trace #13870

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

thibseisel
Copy link

@thibseisel thibseisel commented Aug 8, 2024

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

Issue Number: #13550

When an uncaught error is thrown, NestJS handles it using the configured ExceptionFilter. If the default filter is not overridden, the error is logged under the ExceptionsHandler tag with its message and stack trace.

Since ECMAScript 2022, the Error constructor accepts a new cause parameter option. The cause of an Error is printed to the console when passed to console.error:

const cause = new Error("The real cause of our problem");
const error = new Error("Something went wrong here", { cause })
const caught = new Error("An unexpected error occurred", { cause: exception });
console.error(caught);

// prints stack trace, recursively specifying the cause
Error: An unexpected error occurred
    at REPL3:1:14
    at ContextifyScript.runInThisContext (node:vm:136:12)
    ... 7 lines matching cause stack trace ...
    at [_line] [as _line] (node:internal/readline/interface:888:18) {
  [cause]: Error: Something went wrong here
      at REPL2:1:13
      at ContextifyScript.runInThisContext (node:vm:136:12)
      ... 7 lines matching cause stack trace ...
      at [_line] [as _line] (node:internal/readline/interface:888:18) {
    [cause]: Error: The real cause of our problem
        at REPL1:1:13
        at ContextifyScript.runInThisContext (node:vm:136:12)
        at REPLServer.defaultEval (node:repl:598:22)
        at bound (node:domain:432:15)
        at REPLServer.runBound [as eval] (node:domain:443:12)
        at REPLServer.onLine (node:repl:927:10)
        at REPLServer.emit (node:events:531:35)
        at REPLServer.emit (node:domain:488:12)
        at [_onLine] [as _onLine] (node:internal/readline/interface:417:12)
        at [_line] [as _line] (node:internal/readline/interface:888:18)
  }
}

But since NestJS only considers the caught error's stack trace, it does not include the root causes, making it hard to diagnose issues. The above example would only print something like the following:

Error: An unexpected error occurred
    at REPL3:1:14
    at ContextifyScript.runInThisContext (node:vm:136:12)
    ... 7 lines matching cause stack trace ...
    at [_line] [as _line] (node:internal/readline/interface:888:18)

What is the new behavior?

When an unexpected error occurs, NestJS now recursively logs error causes. This only focuses on error causes and does not display any custom property error objects may have.

The above example would generate the following stack trace:

Error: An unexpected error occurred
    at REPL3:1:14
    at ContextifyScript.runInThisContext (node:vm:136:12)
Caused by Error: Something went wrong here
    at REPL2:1:13
    at ContextifyScript.runInThisContext (node:vm:136:12)
Caused by Error: The real cause of our problem
    at REPL1:1:13
    at ContextifyScript.runInThisContext (node:vm:136:12)

Does this PR introduce a breaking change?

  • Yes
  • No

This change makes sure that stack trace of errors without a cause are logged exactly the same way as before.
The only impact is that logged error stack traces may be longer. Deeply nested errors may cause logs to be less readable.

Other information

None.

@kamilmysliwiec
Copy link
Member

kamilmysliwiec commented Aug 9, 2024

There are 2 failing unit tests

Thanks for your contribution!

@thibseisel thibseisel force-pushed the feature/exception-handler-error-cause branch 2 times, most recently from e69f9ef to c3f54db Compare August 9, 2024 22:35
@thibseisel thibseisel force-pushed the feature/exception-handler-error-cause branch from c3f54db to 4d37a0b Compare August 9, 2024 22:40
@coveralls
Copy link

coveralls commented Aug 9, 2024

Pull Request Test Coverage Report for Build d4ce9edb-19ae-400b-bef2-ac4fb9aa2f87

Details

  • 12 of 12 (100.0%) changed or added relevant lines in 4 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.01%) to 92.219%

Totals Coverage Status
Change from base Build c9b17776-8993-4d3d-9083-ccfdef03782e: 0.01%
Covered Lines: 6756
Relevant Lines: 7326

💛 - Coveralls

@jochongs
Copy link

jochongs commented Aug 10, 2024

I've updated the test cases for the exception handler and submitted a PR for your review.

thibseisel#3


I noticed an issue in the existing test code, so I made the necessary adjustments and included them in this PR as well.

Currently, in the exception-handler.spec.ts file, the second test case seems to be designed for a scenario where the exception is not a RuntimeException.

it('when exception is not instanceof RuntimeException', () => {
const exception = new InvalidMiddlewareException('msg');
instance.handle(exception);
expect(errorSpy.calledWith(exception.what(), exception.stack)).to.be.true;
});

However, the InvalidMiddlewareException used in the test appears to inherit from RuntimeException.

export class InvalidMiddlewareException extends RuntimeException {
constructor(name: string) {
super(INVALID_MIDDLEWARE_MESSAGE`${name}`);
}
}

To better match the intended test scenario, I have modified the exception variable to be an instance of Error instead of InvalidMiddlewareException.

@thibseisel Please take a look at my PR as well. Thanks. :)

@thibseisel
Copy link
Author

@jochongs I just accepted your PR, thank you!

Also, I'd like to emphasize that unlike my initial implementation, this change no longer displays custom properties of an Error, only its chain of causes. This is because I decided to write a custom combineStackTrace function instead of relying on util.inspect, so that the generated stack trace is more compact.
This may no be what you initially wanted, please let me know if you'd like to make any change.

@jochongs
Copy link

jochongs commented Aug 12, 2024

@thibseisel

I’ve been thinking about the decision to print only the cause. I wonder if there might be value in printing all attributes of the Error object instead. I believe this could provide more comprehensive information for debugging. Was there a specific reason for choosing to print only the cause?

(I couldn’t find the original commit where inspect method was used, so I created this PR to compare the code. #13875)

@kamilmysliwiec
Copy link
Member

I'm somewhat leaning toward printing all attributes too. Is there any specific reason we should consider printing only the cause prop?

@thibseisel
Copy link
Author

@jochongs @kamilmysliwiec I omitted custom properties essentially to reduce verbosity. String properties are rather short, but some deep objects could make the stack-trace unreasonably long (I'm pointing at you, Node.js streams!).

I'm not against the idea though. Some custom errors have useful properties, for example AxiosError exposes the HTTP status code and response body.

@jochongs
Copy link

jochongs commented Nov 2, 2024

@thibseisel @kamilmysliwiec

Hello, I've been developing with Nest.js recently and had some thoughts that I wanted to share here.

For smaller-scale servers, I think the default logger could work well even in production. Since it only logs unknown errors, it prevents excessive log accumulation and reduces development effort.

Considering issue #13841, it seems reasonable to use the default logger in production environments as well.

If the above premise holds, one potential concern is that the default logger might log all properties of error objects, which could be overwhelming for some errors with numerous properties, as shown in the example below.

Example of an error log with all properties included

This error occurred in the httpService that we frequently use.

await this.httpService.axiosRef.post('https://axios-error.test/not-found');
[Nest] 16623  - 2024. 11. 02. 오전 10:23:00   ERROR [ExceptionsHandler] getaddrinfo ENOTFOUND axios-error.test
AxiosError: getaddrinfo ENOTFOUND axios-error.test
    at Function.AxiosError.from (/Users/jochong/dev/opensource/nest/test/node_modules/axios/lib/core/AxiosError.js:92:14)
    at RedirectableRequest.handleRequestError (/Users/jochong/dev/opensource/nest/test/node_modules/axios/lib/adapters/http.js:620:25)
    at RedirectableRequest.emit (node:events:520:28)
    at RedirectableRequest.emit (node:domain:488:12)
    at ClientRequest.eventHandlers.<computed> (/Users/jochong/dev/opensource/nest/test/node_modules/follow-redirects/index.js:49:24)
    at ClientRequest.emit (node:events:520:28)
    at ClientRequest.emit (node:domain:488:12)
    at TLSSocket.socketErrorListener (node:_http_client:502:9)
    at TLSSocket.emit (node:events:520:28)
    at TLSSocket.emit (node:domain:488:12)
    at Axios.request (/Users/jochong/dev/opensource/nest/test/node_modules/axios/lib/core/Axios.js:45:41)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async AppController.helloWorld (/Users/jochong/dev/opensource/nest/test/src/app.controller.ts:15:5)
    at async /Users/jochong/dev/opensource/nest/node_modules/@nestjs/core/router/router-execution-context.js:46:28
    at async /Users/jochong/dev/opensource/nest/node_modules/@nestjs/core/router/router-proxy.js:9:17 {
  hostname: 'axios-error.test',
  syscall: 'getaddrinfo',
  code: 'ENOTFOUND',
  errno: -3008,
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [ 'xhr', 'http', 'fetch' ],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: {
      FormData: [Function: FormData] {
        LINE_BREAK: '\r\n',
        DEFAULT_CONTENT_TYPE: 'application/octet-stream'
      },
      Blob: [class Blob]
    },
    validateStatus: [Function: validateStatus],
    headers: Object [AxiosHeaders] {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'application/x-www-form-urlencoded',
      'User-Agent': 'axios/1.7.7',
      'Accept-Encoding': 'gzip, compress, deflate, br'
    },
    method: 'post',
    url: 'https://axios-error.test/not-found',
    data: undefined
  },
  request: <ref *2> Writable {
    _events: {
      close: undefined,
      error: [Function: handleRequestError],
      prefinish: undefined,
      finish: undefined,
      drain: undefined,
      response: [Function: handleResponse],
      socket: [Function: handleRequestSocket]
    },
    _writableState: WritableState {
      highWaterMark: 65536,
      length: 0,
      corked: 0,
      onwrite: [Function: bound onwrite],
      writelen: 0,
      bufferedIndex: 0,
      pendingcb: 0,
      [Symbol(kState)]: 17580812,
      [Symbol(kBufferedValue)]: null
    },
    _maxListeners: undefined,
    _options: {
      maxRedirects: 21,
      maxBodyLength: Infinity,
      protocol: 'https:',
      path: '/not-found',
      method: 'POST',
      headers: [Object: null prototype] {
        Accept: 'application/json, text/plain, */*',
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': 'axios/1.7.7',
        'Accept-Encoding': 'gzip, compress, deflate, br'
      },
      agents: { http: undefined, https: undefined },
      auth: undefined,
      family: undefined,
      beforeRedirect: [Function: dispatchBeforeRedirect],
      beforeRedirects: { proxy: [Function: beforeRedirect] },
      hostname: 'axios-error.test',
      port: '',
      agent: undefined,
      nativeProtocols: <ref *4> {
        'http:': {
          _connectionListener: [Function: connectionListener],
          METHODS: [
            'ACL',        'BIND',        'CHECKOUT',
            'CONNECT',    'COPY',        'DELETE',
            'GET',        'HEAD',        'LINK',
            'LOCK',       'M-SEARCH',    'MERGE',
            'MKACTIVITY', 'MKCALENDAR',  'MKCOL',
            'MOVE',       'NOTIFY',      'OPTIONS',
            'PATCH',      'POST',        'PROPFIND',
            'PROPPATCH',  'PURGE',       'PUT',
            'QUERY',      'REBIND',      'REPORT',
            'SEARCH',     'SOURCE',      'SUBSCRIBE',
            'TRACE',      'UNBIND',      'UNLINK',
            'UNLOCK',     'UNSUBSCRIBE'
          ],
          STATUS_CODES: {
            '100': 'Continue',
            '101': 'Switching Protocols',
            '102': 'Processing',
            '103': 'Early Hints',
            '200': 'OK',
            '201': 'Created',
            '202': 'Accepted',
            '203': 'Non-Authoritative Information',
            '204': 'No Content',
            '205': 'Reset Content',
            '206': 'Partial Content',
            '207': 'Multi-Status',
            '208': 'Already Reported',
            '226': 'IM Used',
            '300': 'Multiple Choices',
            '301': 'Moved Permanently',
            '302': 'Found',
            '303': 'See Other',
            '304': 'Not Modified',
            '305': 'Use Proxy',
            '307': 'Temporary Redirect',
            '308': 'Permanent Redirect',
            '400': 'Bad Request',
            '401': 'Unauthorized',
            '402': 'Payment Required',
            '403': 'Forbidden',
            '404': 'Not Found',
            '405': 'Method Not Allowed',
            '406': 'Not Acceptable',
            '407': 'Proxy Authentication Required',
            '408': 'Request Timeout',
            '409': 'Conflict',
            '410': 'Gone',
            '411': 'Length Required',
            '412': 'Precondition Failed',
            '413': 'Payload Too Large',
            '414': 'URI Too Long',
            '415': 'Unsupported Media Type',
            '416': 'Range Not Satisfiable',
            '417': 'Expectation Failed',
            '418': "I'm a Teapot",
            '421': 'Misdirected Request',
            '422': 'Unprocessable Entity',
            '423': 'Locked',
            '424': 'Failed Dependency',
            '425': 'Too Early',
            '426': 'Upgrade Required',
            '428': 'Precondition Required',
            '429': 'Too Many Requests',
            '431': 'Request Header Fields Too Large',
            '451': 'Unavailable For Legal Reasons',
            '500': 'Internal Server Error',
            '501': 'Not Implemented',
            '502': 'Bad Gateway',
            '503': 'Service Unavailable',
            '504': 'Gateway Timeout',
            '505': 'HTTP Version Not Supported',
            '506': 'Variant Also Negotiates',
            '507': 'Insufficient Storage',
            '508': 'Loop Detected',
            '509': 'Bandwidth Limit Exceeded',
            '510': 'Not Extended',
            '511': 'Network Authentication Required'
          },
          Agent: [Function: Agent] { defaultMaxSockets: Infinity },
          ClientRequest: [Function: ClientRequest],
          IncomingMessage: [Function: IncomingMessage],
          OutgoingMessage: [Function: OutgoingMessage],
          Server: [Function: Server],
          ServerResponse: [Function: ServerResponse],
          createServer: [Function: createServer],
          validateHeaderName: [Function: wrappedFn] {
            withoutStackTrace: [Function (anonymous)]
          },
          validateHeaderValue: [Function: wrappedFn] {
            withoutStackTrace: [Function (anonymous)]
          },
          get: [Function: get],
          request: [Function: request],
          setMaxIdleHTTPParsers: [Function: setMaxIdleHTTPParsers],
          maxHeaderSize: [Getter],
          globalAgent: [Getter/Setter]
        },
        'https:': {
          Agent: [Function: Agent],
          globalAgent: <ref *1> Agent {
            _events: [Object: null prototype] {
              free: [Function (anonymous)],
              newListener: [Function: maybeEnableKeylog]
            },
            _eventsCount: 2,
            _maxListeners: undefined,
            defaultPort: 443,
            protocol: 'https:',
            options: [Object: null prototype] {
              keepAlive: true,
              scheduling: 'lifo',
              timeout: 5000,
              noDelay: true,
              path: null
            },
            requests: [Object: null prototype] {},
            sockets: [Object: null prototype] {
              'axios-error.test:443:::::::::::::::::::::': [
                <ref *3> TLSSocket {
                  _tlsOptions: {
                    allowHalfOpen: undefined,
                    pipe: false,
                    secureContext: SecureContext { context: SecureContext {} },
                    isServer: false,
                    requestCert: true,
                    rejectUnauthorized: true,
                    session: undefined,
                    ALPNProtocols: undefined,
                    requestOCSP: undefined,
                    enableTrace: undefined,
                    pskCallback: undefined,
                    highWaterMark: undefined,
                    onread: undefined,
                    signal: undefined
                  },
                  _secureEstablished: false,
                  _securePending: false,
                  _newSessionPending: false,
                  _controlReleased: true,
                  secureConnecting: true,
                  _SNICallback: null,
                  servername: null,
                  alpnProtocol: null,
                  authorized: false,
                  authorizationError: null,
                  encrypted: true,
                  _events: [Object: null prototype] {
                    close: [
                      [Function: onSocketCloseDestroySSL],
                      [Function: bound onceWrapper] {
                        listener: [Function (anonymous)]
                      },
                      [Function: onClose],
                      [Function: socketCloseListener],
                      [Function: bound onceWrapper] {
                        listener: [Function: onClose]
                      }
                    ],
                    end: [
                      [Function: onConnectEnd],
                      [Function: onReadableStreamEnd]
                    ],
                    error: [Function: socketErrorListener],
                    newListener: [Function: keylogNewListener],
                    connect: [
                      [Function: bound onceWrapper] {
                        listener: [Function (anonymous)]
                      },
                      [Function: bound onceWrapper] {
                        listener: [Function (anonymous)]
                      },
                      [Function: bound onceWrapper] {
                        listener: [Function: connect]
                      }
                    ],
                    secure: [Function: onConnectSecure],
                    session: [Function (anonymous)],
                    free: [Function: onFree],
                    timeout: [
                      [Function: onTimeout],
                      [Function: bound onceWrapper] {
                        listener: [Function: emitRequestTimeout]
                      }
                    ],
                    agentRemove: [Function: onRemove],
                    data: undefined,
                    drain: [Function: ondrain]
                  },
                  _eventsCount: 11,
                  connecting: false,
                  _hadError: true,
                  _parent: null,
                  _host: 'axios-error.test',
                  _closeAfterHandlingError: false,
                  _readableState: ReadableState {
                    highWaterMark: 65536,
                    buffer: [],
                    bufferIndex: 0,
                    length: 0,
                    pipes: [],
                    awaitDrainWriters: null,
                    [Symbol(kState)]: 59779574,
                    [Symbol(kErroredValue)]: Error: getaddrinfo ENOTFOUND axios-error.test
                        at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26) {
                      errno: -3008,
                      code: 'ENOTFOUND',
                      syscall: 'getaddrinfo',
                      hostname: 'axios-error.test'
                    }
                  },
                  _writableState: WritableState {
                    highWaterMark: 65536,
                    length: 258,
                    corked: 0,
                    onwrite: [Function: bound onwrite],
                    writelen: 258,
                    bufferedIndex: 0,
                    pendingcb: 1,
                    [Symbol(kState)]: 118260214,
                    [Symbol(kBufferedValue)]: null,
                    [Symbol(kWriteCbValue)]: [Function: bound onFinish],
                    [Symbol(kErroredValue)]: Error: getaddrinfo ENOTFOUND axios-error.test
                        at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26) {
                      errno: -3008,
                      code: 'ENOTFOUND',
                      syscall: 'getaddrinfo',
                      hostname: 'axios-error.test'
                    }
                  },
                  allowHalfOpen: false,
                  _maxListeners: undefined,
                  _sockname: null,
                  _pendingData: 'POST /not-found HTTP/1.1\r\n' +
                    'Accept: application/json, text/plain, */*\r\n' +
                    'Content-Type: application/x-www-form-urlencoded\r\n' +
                    'User-Agent: axios/1.7.7\r\n' +
                    'Accept-Encoding: gzip, compress, deflate, br\r\n' +
                    'Host: axios-error.test\r\n' +
                    'Connection: keep-alive\r\n' +
                    'Content-Length: 0\r\n' +
                    '\r\n',
                  _pendingEncoding: 'latin1',
                  server: undefined,
                  _server: null,
                  ssl: null,
                  _requestCert: true,
                  _rejectUnauthorized: true,
                  timeout: 5000,
                  parser: null,
                  _httpMessage: ClientRequest {
                    _events: [Object: null prototype] {
                      response: [Function: bound onceWrapper] {
                        listener: [Function (anonymous)]
                      },
                      abort: [Function (anonymous)],
                      aborted: [Function (anonymous)],
                      connect: [Function (anonymous)],
                      error: [Function (anonymous)],
                      socket: [Function (anonymous)],
                      timeout: [Function (anonymous)]
                    },
                    _eventsCount: 7,
                    _maxListeners: undefined,
                    outputData: [],
                    outputSize: 0,
                    writable: true,
                    destroyed: false,
                    _last: false,
                    chunkedEncoding: false,
                    shouldKeepAlive: true,
                    maxRequestsOnConnectionReached: false,
                    _defaultKeepAlive: true,
                    useChunkedEncodingByDefault: true,
                    sendDate: false,
                    _removedConnection: false,
                    _removedContLen: false,
                    _removedTE: false,
                    strictContentLength: false,
                    _contentLength: 0,
                    _hasBody: true,
                    _trailer: '',
                    finished: true,
                    _headerSent: true,
                    _closed: false,
                    _header: 'POST /not-found HTTP/1.1\r\n' +
                      'Accept: application/json, text/plain, */*\r\n' +
                      'Content-Type: application/x-www-form-urlencoded\r\n' +
                      'User-Agent: axios/1.7.7\r\n' +
                      'Accept-Encoding: gzip, compress, deflate, br\r\n' +
                      'Host: axios-error.test\r\n' +
                      'Connection: keep-alive\r\n' +
                      'Content-Length: 0\r\n' +
                      '\r\n',
                    _keepAliveTimeout: 0,
                    _onPendingData: [Function: nop],
                    agent: [Circular *1],
                    socketPath: undefined,
                    method: 'POST',
                    maxHeaderSize: undefined,
                    insecureHTTPParser: undefined,
                    joinDuplicateHeaders: undefined,
                    path: '/not-found',
                    _ended: false,
                    res: null,
                    aborted: false,
                    timeoutCb: [Function: emitRequestTimeout],
                    upgradeOrConnect: false,
                    parser: null,
                    maxHeadersCount: null,
                    reusedSocket: false,
                    host: 'axios-error.test',
                    protocol: 'https:',
                    _redirectable: [Circular *2],
                    [Symbol(shapeMode)]: false,
                    [Symbol(kCapture)]: false,
                    [Symbol(kBytesWritten)]: 0,
                    [Symbol(kNeedDrain)]: false,
                    [Symbol(corked)]: 0,
                    [Symbol(kChunkedBuffer)]: [],
                    [Symbol(kChunkedLength)]: 0,
                    [Symbol(kSocket)]: [Circular *3],
                    [Symbol(kOutHeaders)]: [Object: null prototype] {
                      accept: [ 'Accept', 'application/json, text/plain, */*' ],
                      'content-type': [
                        'Content-Type',
                        'application/x-www-form-urlencoded'
                      ],
                      'user-agent': [ 'User-Agent', 'axios/1.7.7' ],
                      'accept-encoding': [
                        'Accept-Encoding',
                        'gzip, compress, deflate, br'
                      ],
                      host: [ 'Host', 'axios-error.test' ]
                    },
                    [Symbol(errored)]: null,
                    [Symbol(kHighWaterMark)]: 65536,
                    [Symbol(kRejectNonStandardBodyWrites)]: false,
                    [Symbol(kUniqueHeaders)]: null
                  },
                  [Symbol(alpncallback)]: null,
                  [Symbol(res)]: TLSWrap {
                    _parent: TCP {
                      reading: [Getter/Setter],
                      onconnection: null,
                      [Symbol(owner_symbol)]: [Circular *3],
                      [Symbol(handle_onclose)]: [Function: done]
                    },
                    _parentWrap: null,
                    _secureContext: SecureContext { context: SecureContext {} },
                    reading: false,
                    onkeylog: [Function: onkeylog],
                    onhandshakestart: {},
                    onhandshakedone: [Function (anonymous)],
                    onocspresponse: [Function: onocspresponse],
                    onnewsession: [Function: onnewsessionclient],
                    onerror: [Function: onerror],
                    [Symbol(owner_symbol)]: [Circular *3]
                  },
                  [Symbol(verified)]: false,
                  [Symbol(pendingSession)]: null,
                  [Symbol(async_id_symbol)]: 12,
                  [Symbol(kHandle)]: null,
                  [Symbol(lastWriteQueueSize)]: 0,
                  [Symbol(timeout)]: Timeout {
                    _idleTimeout: -1,
                    _idlePrev: null,
                    _idleNext: null,
                    _idleStart: 1820,
                    _onTimeout: null,
                    _timerArgs: undefined,
                    _repeat: null,
                    _destroyed: true,
                    [Symbol(refed)]: false,
                    [Symbol(kHasPrimitive)]: false,
                    [Symbol(asyncId)]: 14,
                    [Symbol(triggerId)]: 9
                  },
                  [Symbol(kBuffer)]: null,
                  [Symbol(kBufferCb)]: null,
                  [Symbol(kBufferGen)]: null,
                  [Symbol(shapeMode)]: true,
                  [Symbol(kCapture)]: false,
                  [Symbol(kSetNoDelay)]: false,
                  [Symbol(kSetKeepAlive)]: true,
                  [Symbol(kSetKeepAliveInitialDelay)]: 60,
                  [Symbol(kBytesRead)]: 0,
                  [Symbol(kBytesWritten)]: 0,
                  [Symbol(connect-options)]: {
                    rejectUnauthorized: true,
                    ciphers: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA',
                    checkServerIdentity: [Function: checkServerIdentity],
                    minDHSize: 1024,
                    maxRedirects: 21,
                    maxBodyLength: Infinity,
                    protocol: 'https:',
                    path: null,
                    method: 'POST',
                    headers: [Object: null prototype] {
                      Accept: 'application/json, text/plain, */*',
                      'Content-Type': 'application/x-www-form-urlencoded',
                      'User-Agent': 'axios/1.7.7',
                      'Accept-Encoding': 'gzip, compress, deflate, br'
                    },
                    agents: { http: undefined, https: undefined },
                    auth: undefined,
                    family: undefined,
                    beforeRedirect: [Function: dispatchBeforeRedirect],
                    beforeRedirects: { proxy: [Function: beforeRedirect] },
                    hostname: 'axios-error.test',
                    port: 443,
                    agent: undefined,
                    nativeProtocols: [Circular *4],
                    pathname: '/not-found',
                    _defaultAgent: [Circular *1],
                    host: 'axios-error.test',
                    keepAlive: true,
                    scheduling: 'lifo',
                    timeout: 5000,
                    noDelay: true,
                    servername: 'axios-error.test',
                    _agentKey: 'axios-error.test:443:::::::::::::::::::::',
                    encoding: null,
                    keepAliveInitialDelay: 1000
                  }
                }
              ]
            },
            freeSockets: [Object: null prototype] {},
            keepAliveMsecs: 1000,
            keepAlive: true,
            maxSockets: Infinity,
            maxFreeSockets: 256,
            scheduling: 'lifo',
            maxTotalSockets: Infinity,
            totalSocketCount: 1,
            maxCachedSessions: 100,
            _sessionCache: { map: {}, list: [] },
            [Symbol(shapeMode)]: false,
            [Symbol(kCapture)]: false
          },
          Server: [Function: Server],
          createServer: [Function: createServer],
          get: [Function: get],
          request: [Function: request]
        }
      },
      pathname: '/not-found'
    },
    _ended: true,
    _ending: true,
    _redirectCount: 0,
    _redirects: [],
    _requestBodyLength: 0,
    _requestBodyBuffers: [],
    _eventsCount: 3,
    _onNativeResponse: [Function (anonymous)],
    _currentRequest: <ref *5> ClientRequest {
      _events: [Object: null prototype] {
        response: [Function: bound onceWrapper] {
          listener: [Function (anonymous)]
        },
        abort: [Function (anonymous)],
        aborted: [Function (anonymous)],
        connect: [Function (anonymous)],
        error: [Function (anonymous)],
        socket: [Function (anonymous)],
        timeout: [Function (anonymous)]
      },
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: false,
      chunkedEncoding: false,
      shouldKeepAlive: true,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      _header: 'POST /not-found HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: application/x-www-form-urlencoded\r\n' +
        'User-Agent: axios/1.7.7\r\n' +
        'Accept-Encoding: gzip, compress, deflate, br\r\n' +
        'Host: axios-error.test\r\n' +
        'Connection: keep-alive\r\n' +
        'Content-Length: 0\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: <ref *1> Agent {
        _events: [Object: null prototype] {
          free: [Function (anonymous)],
          newListener: [Function: maybeEnableKeylog]
        },
        _eventsCount: 2,
        _maxListeners: undefined,
        defaultPort: 443,
        protocol: 'https:',
        options: [Object: null prototype] {
          keepAlive: true,
          scheduling: 'lifo',
          timeout: 5000,
          noDelay: true,
          path: null
        },
        requests: [Object: null prototype] {},
        sockets: [Object: null prototype] {
          'axios-error.test:443:::::::::::::::::::::': [
            <ref *3> TLSSocket {
              _tlsOptions: {
                allowHalfOpen: undefined,
                pipe: false,
                secureContext: SecureContext { context: SecureContext {} },
                isServer: false,
                requestCert: true,
                rejectUnauthorized: true,
                session: undefined,
                ALPNProtocols: undefined,
                requestOCSP: undefined,
                enableTrace: undefined,
                pskCallback: undefined,
                highWaterMark: undefined,
                onread: undefined,
                signal: undefined
              },
              _secureEstablished: false,
              _securePending: false,
              _newSessionPending: false,
              _controlReleased: true,
              secureConnecting: true,
              _SNICallback: null,
              servername: null,
              alpnProtocol: null,
              authorized: false,
              authorizationError: null,
              encrypted: true,
              _events: [Object: null prototype] {
                close: [
                  [Function: onSocketCloseDestroySSL],
                  [Function: bound onceWrapper] {
                    listener: [Function (anonymous)]
                  },
                  [Function: onClose],
                  [Function: socketCloseListener],
                  [Function: bound onceWrapper] {
                    listener: [Function: onClose]
                  }
                ],
                end: [
                  [Function: onConnectEnd],
                  [Function: onReadableStreamEnd]
                ],
                error: [Function: socketErrorListener],
                newListener: [Function: keylogNewListener],
                connect: [
                  [Function: bound onceWrapper] {
                    listener: [Function (anonymous)]
                  },
                  [Function: bound onceWrapper] {
                    listener: [Function (anonymous)]
                  },
                  [Function: bound onceWrapper] {
                    listener: [Function: connect]
                  }
                ],
                secure: [Function: onConnectSecure],
                session: [Function (anonymous)],
                free: [Function: onFree],
                timeout: [
                  [Function: onTimeout],
                  [Function: bound onceWrapper] {
                    listener: [Function: emitRequestTimeout]
                  }
                ],
                agentRemove: [Function: onRemove],
                data: undefined,
                drain: [Function: ondrain]
              },
              _eventsCount: 11,
              connecting: false,
              _hadError: true,
              _parent: null,
              _host: 'axios-error.test',
              _closeAfterHandlingError: false,
              _readableState: ReadableState {
                highWaterMark: 65536,
                buffer: [],
                bufferIndex: 0,
                length: 0,
                pipes: [],
                awaitDrainWriters: null,
                [Symbol(kState)]: 59779574,
                [Symbol(kErroredValue)]: Error: getaddrinfo ENOTFOUND axios-error.test
                    at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26) {
                  errno: -3008,
                  code: 'ENOTFOUND',
                  syscall: 'getaddrinfo',
                  hostname: 'axios-error.test'
                }
              },
              _writableState: WritableState {
                highWaterMark: 65536,
                length: 258,
                corked: 0,
                onwrite: [Function: bound onwrite],
                writelen: 258,
                bufferedIndex: 0,
                pendingcb: 1,
                [Symbol(kState)]: 118260214,
                [Symbol(kBufferedValue)]: null,
                [Symbol(kWriteCbValue)]: [Function: bound onFinish],
                [Symbol(kErroredValue)]: Error: getaddrinfo ENOTFOUND axios-error.test
                    at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26) {
                  errno: -3008,
                  code: 'ENOTFOUND',
                  syscall: 'getaddrinfo',
                  hostname: 'axios-error.test'
                }
              },
              allowHalfOpen: false,
              _maxListeners: undefined,
              _sockname: null,
              _pendingData: 'POST /not-found HTTP/1.1\r\n' +
                'Accept: application/json, text/plain, */*\r\n' +
                'Content-Type: application/x-www-form-urlencoded\r\n' +
                'User-Agent: axios/1.7.7\r\n' +
                'Accept-Encoding: gzip, compress, deflate, br\r\n' +
                'Host: axios-error.test\r\n' +
                'Connection: keep-alive\r\n' +
                'Content-Length: 0\r\n' +
                '\r\n',
              _pendingEncoding: 'latin1',
              server: undefined,
              _server: null,
              ssl: null,
              _requestCert: true,
              _rejectUnauthorized: true,
              timeout: 5000,
              parser: null,
              _httpMessage: [Circular *5],
              [Symbol(alpncallback)]: null,
              [Symbol(res)]: TLSWrap {
                _parent: TCP {
                  reading: [Getter/Setter],
                  onconnection: null,
                  [Symbol(owner_symbol)]: [Circular *3],
                  [Symbol(handle_onclose)]: [Function: done]
                },
                _parentWrap: null,
                _secureContext: SecureContext { context: SecureContext {} },
                reading: false,
                onkeylog: [Function: onkeylog],
                onhandshakestart: {},
                onhandshakedone: [Function (anonymous)],
                onocspresponse: [Function: onocspresponse],
                onnewsession: [Function: onnewsessionclient],
                onerror: [Function: onerror],
                [Symbol(owner_symbol)]: [Circular *3]
              },
              [Symbol(verified)]: false,
              [Symbol(pendingSession)]: null,
              [Symbol(async_id_symbol)]: 12,
              [Symbol(kHandle)]: null,
              [Symbol(lastWriteQueueSize)]: 0,
              [Symbol(timeout)]: Timeout {
                _idleTimeout: -1,
                _idlePrev: null,
                _idleNext: null,
                _idleStart: 1820,
                _onTimeout: null,
                _timerArgs: undefined,
                _repeat: null,
                _destroyed: true,
                [Symbol(refed)]: false,
                [Symbol(kHasPrimitive)]: false,
                [Symbol(asyncId)]: 14,
                [Symbol(triggerId)]: 9
              },
              [Symbol(kBuffer)]: null,
              [Symbol(kBufferCb)]: null,
              [Symbol(kBufferGen)]: null,
              [Symbol(shapeMode)]: true,
              [Symbol(kCapture)]: false,
              [Symbol(kSetNoDelay)]: false,
              [Symbol(kSetKeepAlive)]: true,
              [Symbol(kSetKeepAliveInitialDelay)]: 60,
              [Symbol(kBytesRead)]: 0,
              [Symbol(kBytesWritten)]: 0,
              [Symbol(connect-options)]: {
                rejectUnauthorized: true,
                ciphers: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA',
                checkServerIdentity: [Function: checkServerIdentity],
                minDHSize: 1024,
                maxRedirects: 21,
                maxBodyLength: Infinity,
                protocol: 'https:',
                path: null,
                method: 'POST',
                headers: [Object: null prototype] {
                  Accept: 'application/json, text/plain, */*',
                  'Content-Type': 'application/x-www-form-urlencoded',
                  'User-Agent': 'axios/1.7.7',
                  'Accept-Encoding': 'gzip, compress, deflate, br'
                },
                agents: { http: undefined, https: undefined },
                auth: undefined,
                family: undefined,
                beforeRedirect: [Function: dispatchBeforeRedirect],
                beforeRedirects: { proxy: [Function: beforeRedirect] },
                hostname: 'axios-error.test',
                port: 443,
                agent: undefined,
                nativeProtocols: <ref *4> {
                  'http:': {
                    _connectionListener: [Function: connectionListener],
                    METHODS: [
                      'ACL',        'BIND',        'CHECKOUT',
                      'CONNECT',    'COPY',        'DELETE',
                      'GET',        'HEAD',        'LINK',
                      'LOCK',       'M-SEARCH',    'MERGE',
                      'MKACTIVITY', 'MKCALENDAR',  'MKCOL',
                      'MOVE',       'NOTIFY',      'OPTIONS',
                      'PATCH',      'POST',        'PROPFIND',
                      'PROPPATCH',  'PURGE',       'PUT',
                      'QUERY',      'REBIND',      'REPORT',
                      'SEARCH',     'SOURCE',      'SUBSCRIBE',
                      'TRACE',      'UNBIND',      'UNLINK',
                      'UNLOCK',     'UNSUBSCRIBE'
                    ],
                    STATUS_CODES: {
                      '100': 'Continue',
                      '101': 'Switching Protocols',
                      '102': 'Processing',
                      '103': 'Early Hints',
                      '200': 'OK',
                      '201': 'Created',
                      '202': 'Accepted',
                      '203': 'Non-Authoritative Information',
                      '204': 'No Content',
                      '205': 'Reset Content',
                      '206': 'Partial Content',
                      '207': 'Multi-Status',
                      '208': 'Already Reported',
                      '226': 'IM Used',
                      '300': 'Multiple Choices',
                      '301': 'Moved Permanently',
                      '302': 'Found',
                      '303': 'See Other',
                      '304': 'Not Modified',
                      '305': 'Use Proxy',
                      '307': 'Temporary Redirect',
                      '308': 'Permanent Redirect',
                      '400': 'Bad Request',
                      '401': 'Unauthorized',
                      '402': 'Payment Required',
                      '403': 'Forbidden',
                      '404': 'Not Found',
                      '405': 'Method Not Allowed',
                      '406': 'Not Acceptable',
                      '407': 'Proxy Authentication Required',
                      '408': 'Request Timeout',
                      '409': 'Conflict',
                      '410': 'Gone',
                      '411': 'Length Required',
                      '412': 'Precondition Failed',
                      '413': 'Payload Too Large',
                      '414': 'URI Too Long',
                      '415': 'Unsupported Media Type',
                      '416': 'Range Not Satisfiable',
                      '417': 'Expectation Failed',
                      '418': "I'm a Teapot",
                      '421': 'Misdirected Request',
                      '422': 'Unprocessable Entity',
                      '423': 'Locked',
                      '424': 'Failed Dependency',
                      '425': 'Too Early',
                      '426': 'Upgrade Required',
                      '428': 'Precondition Required',
                      '429': 'Too Many Requests',
                      '431': 'Request Header Fields Too Large',
                      '451': 'Unavailable For Legal Reasons',
                      '500': 'Internal Server Error',
                      '501': 'Not Implemented',
                      '502': 'Bad Gateway',
                      '503': 'Service Unavailable',
                      '504': 'Gateway Timeout',
                      '505': 'HTTP Version Not Supported',
                      '506': 'Variant Also Negotiates',
                      '507': 'Insufficient Storage',
                      '508': 'Loop Detected',
                      '509': 'Bandwidth Limit Exceeded',
                      '510': 'Not Extended',
                      '511': 'Network Authentication Required'
                    },
                    Agent: [Function: Agent] { defaultMaxSockets: Infinity },
                    ClientRequest: [Function: ClientRequest],
                    IncomingMessage: [Function: IncomingMessage],
                    OutgoingMessage: [Function: OutgoingMessage],
                    Server: [Function: Server],
                    ServerResponse: [Function: ServerResponse],
                    createServer: [Function: createServer],
                    validateHeaderName: [Function: wrappedFn] {
                      withoutStackTrace: [Function (anonymous)]
                    },
                    validateHeaderValue: [Function: wrappedFn] {
                      withoutStackTrace: [Function (anonymous)]
                    },
                    get: [Function: get],
                    request: [Function: request],
                    setMaxIdleHTTPParsers: [Function: setMaxIdleHTTPParsers],
                    maxHeaderSize: [Getter],
                    globalAgent: [Getter/Setter]
                  },
                  'https:': {
                    Agent: [Function: Agent],
                    globalAgent: [Circular *1],
                    Server: [Function: Server],
                    createServer: [Function: createServer],
                    get: [Function: get],
                    request: [Function: request]
                  }
                },
                pathname: '/not-found',
                _defaultAgent: [Circular *1],
                host: 'axios-error.test',
                keepAlive: true,
                scheduling: 'lifo',
                timeout: 5000,
                noDelay: true,
                servername: 'axios-error.test',
                _agentKey: 'axios-error.test:443:::::::::::::::::::::',
                encoding: null,
                keepAliveInitialDelay: 1000
              }
            }
          ]
        },
        freeSockets: [Object: null prototype] {},
        keepAliveMsecs: 1000,
        keepAlive: true,
        maxSockets: Infinity,
        maxFreeSockets: 256,
        scheduling: 'lifo',
        maxTotalSockets: Infinity,
        totalSocketCount: 1,
        maxCachedSessions: 100,
        _sessionCache: { map: {}, list: [] },
        [Symbol(shapeMode)]: false,
        [Symbol(kCapture)]: false
      },
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      joinDuplicateHeaders: undefined,
      path: '/not-found',
      _ended: false,
      res: null,
      aborted: false,
      timeoutCb: [Function: emitRequestTimeout],
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'axios-error.test',
      protocol: 'https:',
      _redirectable: [Circular *2],
      [Symbol(shapeMode)]: false,
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kChunkedBuffer)]: [],
      [Symbol(kChunkedLength)]: 0,
      [Symbol(kSocket)]: <ref *3> TLSSocket {
        _tlsOptions: {
          allowHalfOpen: undefined,
          pipe: false,
          secureContext: SecureContext { context: SecureContext {} },
          isServer: false,
          requestCert: true,
          rejectUnauthorized: true,
          session: undefined,
          ALPNProtocols: undefined,
          requestOCSP: undefined,
          enableTrace: undefined,
          pskCallback: undefined,
          highWaterMark: undefined,
          onread: undefined,
          signal: undefined
        },
        _secureEstablished: false,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        secureConnecting: true,
        _SNICallback: null,
        servername: null,
        alpnProtocol: null,
        authorized: false,
        authorizationError: null,
        encrypted: true,
        _events: [Object: null prototype] {
          close: [
            [Function: onSocketCloseDestroySSL],
            [Function: bound onceWrapper] {
              listener: [Function (anonymous)]
            },
            [Function: onClose],
            [Function: socketCloseListener],
            [Function: bound onceWrapper] {
              listener: [Function: onClose]
            }
          ],
          end: [ [Function: onConnectEnd], [Function: onReadableStreamEnd] ],
          error: [Function: socketErrorListener],
          newListener: [Function: keylogNewListener],
          connect: [
            [Function: bound onceWrapper] {
              listener: [Function (anonymous)]
            },
            [Function: bound onceWrapper] {
              listener: [Function (anonymous)]
            },
            [Function: bound onceWrapper] {
              listener: [Function: connect]
            }
          ],
          secure: [Function: onConnectSecure],
          session: [Function (anonymous)],
          free: [Function: onFree],
          timeout: [
            [Function: onTimeout],
            [Function: bound onceWrapper] {
              listener: [Function: emitRequestTimeout]
            }
          ],
          agentRemove: [Function: onRemove],
          data: undefined,
          drain: [Function: ondrain]
        },
        _eventsCount: 11,
        connecting: false,
        _hadError: true,
        _parent: null,
        _host: 'axios-error.test',
        _closeAfterHandlingError: false,
        _readableState: ReadableState {
          highWaterMark: 65536,
          buffer: [],
          bufferIndex: 0,
          length: 0,
          pipes: [],
          awaitDrainWriters: null,
          [Symbol(kState)]: 59779574,
          [Symbol(kErroredValue)]: Error: getaddrinfo ENOTFOUND axios-error.test
              at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26) {
            errno: -3008,
            code: 'ENOTFOUND',
            syscall: 'getaddrinfo',
            hostname: 'axios-error.test'
          }
        },
        _writableState: WritableState {
          highWaterMark: 65536,
          length: 258,
          corked: 0,
          onwrite: [Function: bound onwrite],
          writelen: 258,
          bufferedIndex: 0,
          pendingcb: 1,
          [Symbol(kState)]: 118260214,
          [Symbol(kBufferedValue)]: null,
          [Symbol(kWriteCbValue)]: [Function: bound onFinish],
          [Symbol(kErroredValue)]: Error: getaddrinfo ENOTFOUND axios-error.test
              at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26) {
            errno: -3008,
            code: 'ENOTFOUND',
            syscall: 'getaddrinfo',
            hostname: 'axios-error.test'
          }
        },
        allowHalfOpen: false,
        _maxListeners: undefined,
        _sockname: null,
        _pendingData: 'POST /not-found HTTP/1.1\r\n' +
          'Accept: application/json, text/plain, */*\r\n' +
          'Content-Type: application/x-www-form-urlencoded\r\n' +
          'User-Agent: axios/1.7.7\r\n' +
          'Accept-Encoding: gzip, compress, deflate, br\r\n' +
          'Host: axios-error.test\r\n' +
          'Connection: keep-alive\r\n' +
          'Content-Length: 0\r\n' +
          '\r\n',
        _pendingEncoding: 'latin1',
        server: undefined,
        _server: null,
        ssl: null,
        _requestCert: true,
        _rejectUnauthorized: true,
        timeout: 5000,
        parser: null,
        _httpMessage: [Circular *5],
        [Symbol(alpncallback)]: null,
        [Symbol(res)]: TLSWrap {
          _parent: TCP {
            reading: [Getter/Setter],
            onconnection: null,
            [Symbol(owner_symbol)]: [Circular *3],
            [Symbol(handle_onclose)]: [Function: done]
          },
          _parentWrap: null,
          _secureContext: SecureContext { context: SecureContext {} },
          reading: false,
          onkeylog: [Function: onkeylog],
          onhandshakestart: {},
          onhandshakedone: [Function (anonymous)],
          onocspresponse: [Function: onocspresponse],
          onnewsession: [Function: onnewsessionclient],
          onerror: [Function: onerror],
          [Symbol(owner_symbol)]: [Circular *3]
        },
        [Symbol(verified)]: false,
        [Symbol(pendingSession)]: null,
        [Symbol(async_id_symbol)]: 12,
        [Symbol(kHandle)]: null,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(timeout)]: Timeout {
          _idleTimeout: -1,
          _idlePrev: null,
          _idleNext: null,
          _idleStart: 1820,
          _onTimeout: null,
          _timerArgs: undefined,
          _repeat: null,
          _destroyed: true,
          [Symbol(refed)]: false,
          [Symbol(kHasPrimitive)]: false,
          [Symbol(asyncId)]: 14,
          [Symbol(triggerId)]: 9
        },
        [Symbol(kBuffer)]: null,
        [Symbol(kBufferCb)]: null,
        [Symbol(kBufferGen)]: null,
        [Symbol(shapeMode)]: true,
        [Symbol(kCapture)]: false,
        [Symbol(kSetNoDelay)]: false,
        [Symbol(kSetKeepAlive)]: true,
        [Symbol(kSetKeepAliveInitialDelay)]: 60,
        [Symbol(kBytesRead)]: 0,
        [Symbol(kBytesWritten)]: 0,
        [Symbol(connect-options)]: {
          rejectUnauthorized: true,
          ciphers: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA',
          checkServerIdentity: [Function: checkServerIdentity],
          minDHSize: 1024,
          maxRedirects: 21,
          maxBodyLength: Infinity,
          protocol: 'https:',
          path: null,
          method: 'POST',
          headers: [Object: null prototype] {
            Accept: 'application/json, text/plain, */*',
            'Content-Type': 'application/x-www-form-urlencoded',
            'User-Agent': 'axios/1.7.7',
            'Accept-Encoding': 'gzip, compress, deflate, br'
          },
          agents: { http: undefined, https: undefined },
          auth: undefined,
          family: undefined,
          beforeRedirect: [Function: dispatchBeforeRedirect],
          beforeRedirects: { proxy: [Function: beforeRedirect] },
          hostname: 'axios-error.test',
          port: 443,
          agent: undefined,
          nativeProtocols: <ref *4> {
            'http:': {
              _connectionListener: [Function: connectionListener],
              METHODS: [
                'ACL',        'BIND',        'CHECKOUT',
                'CONNECT',    'COPY',        'DELETE',
                'GET',        'HEAD',        'LINK',
                'LOCK',       'M-SEARCH',    'MERGE',
                'MKACTIVITY', 'MKCALENDAR',  'MKCOL',
                'MOVE',       'NOTIFY',      'OPTIONS',
                'PATCH',      'POST',        'PROPFIND',
                'PROPPATCH',  'PURGE',       'PUT',
                'QUERY',      'REBIND',      'REPORT',
                'SEARCH',     'SOURCE',      'SUBSCRIBE',
                'TRACE',      'UNBIND',      'UNLINK',
                'UNLOCK',     'UNSUBSCRIBE'
              ],
              STATUS_CODES: {
                '100': 'Continue',
                '101': 'Switching Protocols',
                '102': 'Processing',
                '103': 'Early Hints',
                '200': 'OK',
                '201': 'Created',
                '202': 'Accepted',
                '203': 'Non-Authoritative Information',
                '204': 'No Content',
                '205': 'Reset Content',
                '206': 'Partial Content',
                '207': 'Multi-Status',
                '208': 'Already Reported',
                '226': 'IM Used',
                '300': 'Multiple Choices',
                '301': 'Moved Permanently',
                '302': 'Found',
                '303': 'See Other',
                '304': 'Not Modified',
                '305': 'Use Proxy',
                '307': 'Temporary Redirect',
                '308': 'Permanent Redirect',
                '400': 'Bad Request',
                '401': 'Unauthorized',
                '402': 'Payment Required',
                '403': 'Forbidden',
                '404': 'Not Found',
                '405': 'Method Not Allowed',
                '406': 'Not Acceptable',
                '407': 'Proxy Authentication Required',
                '408': 'Request Timeout',
                '409': 'Conflict',
                '410': 'Gone',
                '411': 'Length Required',
                '412': 'Precondition Failed',
                '413': 'Payload Too Large',
                '414': 'URI Too Long',
                '415': 'Unsupported Media Type',
                '416': 'Range Not Satisfiable',
                '417': 'Expectation Failed',
                '418': "I'm a Teapot",
                '421': 'Misdirected Request',
                '422': 'Unprocessable Entity',
                '423': 'Locked',
                '424': 'Failed Dependency',
                '425': 'Too Early',
                '426': 'Upgrade Required',
                '428': 'Precondition Required',
                '429': 'Too Many Requests',
                '431': 'Request Header Fields Too Large',
                '451': 'Unavailable For Legal Reasons',
                '500': 'Internal Server Error',
                '501': 'Not Implemented',
                '502': 'Bad Gateway',
                '503': 'Service Unavailable',
                '504': 'Gateway Timeout',
                '505': 'HTTP Version Not Supported',
                '506': 'Variant Also Negotiates',
                '507': 'Insufficient Storage',
                '508': 'Loop Detected',
                '509': 'Bandwidth Limit Exceeded',
                '510': 'Not Extended',
                '511': 'Network Authentication Required'
              },
              Agent: [Function: Agent] { defaultMaxSockets: Infinity },
              ClientRequest: [Function: ClientRequest],
              IncomingMessage: [Function: IncomingMessage],
              OutgoingMessage: [Function: OutgoingMessage],
              Server: [Function: Server],
              ServerResponse: [Function: ServerResponse],
              createServer: [Function: createServer],
              validateHeaderName: [Function: wrappedFn] {
                withoutStackTrace: [Function (anonymous)]
              },
              validateHeaderValue: [Function: wrappedFn] {
                withoutStackTrace: [Function (anonymous)]
              },
              get: [Function: get],
              request: [Function: request],
              setMaxIdleHTTPParsers: [Function: setMaxIdleHTTPParsers],
              maxHeaderSize: [Getter],
              globalAgent: [Getter/Setter]
            },
            'https:': {
              Agent: [Function: Agent],
              globalAgent: <ref *1> Agent {
                _events: [Object: null prototype] {
                  free: [Function (anonymous)],
                  newListener: [Function: maybeEnableKeylog]
                },
                _eventsCount: 2,
                _maxListeners: undefined,
                defaultPort: 443,
                protocol: 'https:',
                options: [Object: null prototype] {
                  keepAlive: true,
                  scheduling: 'lifo',
                  timeout: 5000,
                  noDelay: true,
                  path: null
                },
                requests: [Object: null prototype] {},
                sockets: [Object: null prototype] {
                  'axios-error.test:443:::::::::::::::::::::': [ [Circular *3] ]
                },
                freeSockets: [Object: null prototype] {},
                keepAliveMsecs: 1000,
                keepAlive: true,
                maxSockets: Infinity,
                maxFreeSockets: 256,
                scheduling: 'lifo',
                maxTotalSockets: Infinity,
                totalSocketCount: 1,
                maxCachedSessions: 100,
                _sessionCache: { map: {}, list: [] },
                [Symbol(shapeMode)]: false,
                [Symbol(kCapture)]: false
              },
              Server: [Function: Server],
              createServer: [Function: createServer],
              get: [Function: get],
              request: [Function: request]
            }
          },
          pathname: '/not-found',
          _defaultAgent: <ref *1> Agent {
            _events: [Object: null prototype] {
              free: [Function (anonymous)],
              newListener: [Function: maybeEnableKeylog]
            },
            _eventsCount: 2,
            _maxListeners: undefined,
            defaultPort: 443,
            protocol: 'https:',
            options: [Object: null prototype] {
              keepAlive: true,
              scheduling: 'lifo',
              timeout: 5000,
              noDelay: true,
              path: null
            },
            requests: [Object: null prototype] {},
            sockets: [Object: null prototype] {
              'axios-error.test:443:::::::::::::::::::::': [ [Circular *3] ]
            },
            freeSockets: [Object: null prototype] {},
            keepAliveMsecs: 1000,
            keepAlive: true,
            maxSockets: Infinity,
            maxFreeSockets: 256,
            scheduling: 'lifo',
            maxTotalSockets: Infinity,
            totalSocketCount: 1,
            maxCachedSessions: 100,
            _sessionCache: { map: {}, list: [] },
            [Symbol(shapeMode)]: false,
            [Symbol(kCapture)]: false
          },
          host: 'axios-error.test',
          keepAlive: true,
          scheduling: 'lifo',
          timeout: 5000,
          noDelay: true,
          servername: 'axios-error.test',
          _agentKey: 'axios-error.test:443:::::::::::::::::::::',
          encoding: null,
          keepAliveInitialDelay: 1000
        }
      },
      [Symbol(kOutHeaders)]: [Object: null prototype] {
        accept: [ 'Accept', 'application/json, text/plain, */*' ],
        'content-type': [ 'Content-Type', 'application/x-www-form-urlencoded' ],
        'user-agent': [ 'User-Agent', 'axios/1.7.7' ],
        'accept-encoding': [ 'Accept-Encoding', 'gzip, compress, deflate, br' ],
        host: [ 'Host', 'axios-error.test' ]
      },
      [Symbol(errored)]: null,
      [Symbol(kHighWaterMark)]: 65536,
      [Symbol(kRejectNonStandardBodyWrites)]: false,
      [Symbol(kUniqueHeaders)]: null
    },
    _currentUrl: 'https://axios-error.test/not-found',
    [Symbol(shapeMode)]: true,
    [Symbol(kCapture)]: false
  },
  cause: Error: getaddrinfo ENOTFOUND axios-error.test
      at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26) {
    errno: -3008,
    code: 'ENOTFOUND',
    syscall: 'getaddrinfo',
    hostname: 'axios-error.test'
  }
}

However, in development, logging all properties is highly convenient. How about using an environment variable like NODE_ENV to control this behavior? For instance, we could log all properties in development but limit it to just the stack trace in production.

How about modifying the code in this link so it logs only in development mode?

if (this.isExceptionObject(exception)) {
  // Display all error properties only in development mode
  if (process.env.NODE_ENV === 'development') {
    return BaseExceptionFilter.logger.error(
      exception.message,
      inspect(exception, false, null),
    );
  }

  return BaseExceptionFilter.logger.error(
    exception.message,
    combineStackTrace(exception),
  );
}

May I ask your thoughts on this?


I primarily run Nest applications in container environments like ECS, where I review error logs directly in the terminal or via ECS Service log tab. In cases where a dedicated log management infrastructure like ELK or PLG isn't set up, I found the default logger to be quite sufficient for production as well. It would be great to have the option to use the default logger effectively even in production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants