public async exportEnvelopes()

in sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/baseSender.ts [71:216]


  public async exportEnvelopes(envelopes: Envelope[]): Promise<ExportResult> {
    diag.info(`Exporting ${envelopes.length} envelope(s)`);

    if (envelopes.length < 1) {
      return { code: ExportResultCode.SUCCESS };
    }

    try {
      const startTime = new Date().getTime();
      const { result, statusCode } = await this.send(envelopes);
      const endTime = new Date().getTime();
      const duration = endTime - startTime;
      this.numConsecutiveRedirects = 0;

      if (statusCode === 200) {
        // Success -- @todo: start retry timer
        if (!this.retryTimer) {
          this.retryTimer = setTimeout(() => {
            this.retryTimer = null;
            this.sendFirstPersistedFile();
          }, this.batchSendRetryIntervalMs);
          this.retryTimer.unref();
        }
        // If we are not exportings statsbeat and statsbeat is not disabled -- count success
        this.networkStatsbeatMetrics?.countSuccess(duration);
        return { code: ExportResultCode.SUCCESS };
      } else if (statusCode && isRetriable(statusCode)) {
        // Failed -- persist failed data
        if (statusCode === 429 || statusCode === 439) {
          this.networkStatsbeatMetrics?.countThrottle(statusCode);
        }
        if (result) {
          diag.info(result);
          const breezeResponse = JSON.parse(result) as BreezeResponse;
          const filteredEnvelopes: Envelope[] = [];
          // If we have a partial success, count the succeeded envelopes
          if (breezeResponse.itemsReceived > 0) {
            this.networkStatsbeatMetrics?.countSuccess(duration);
          }
          // Figure out if we need to either retry or count failures
          if (breezeResponse.errors) {
            breezeResponse.errors.forEach((error) => {
              if (error.statusCode && isRetriable(error.statusCode)) {
                filteredEnvelopes.push(envelopes[error.index]);
              }
            });
          }
          if (filteredEnvelopes.length > 0) {
            this.networkStatsbeatMetrics?.countRetry(statusCode);
            // calls resultCallback(ExportResult) based on result of persister.push
            return await this.persist(filteredEnvelopes);
          }
          // Failed -- not retriable
          this.networkStatsbeatMetrics?.countFailure(duration, statusCode);
          return {
            code: ExportResultCode.FAILED,
          };
        } else {
          // calls resultCallback(ExportResult) based on result of persister.push
          this.networkStatsbeatMetrics?.countRetry(statusCode);
          return await this.persist(envelopes);
        }
      } else {
        // Failed -- not retriable
        if (this.networkStatsbeatMetrics) {
          if (statusCode) {
            this.networkStatsbeatMetrics.countFailure(duration, statusCode);
          }
        } else {
          // Handles all other status codes or client exceptions for Statsbeat
          this.incrementStatsbeatFailure();
        }
        return {
          code: ExportResultCode.FAILED,
        };
      }
    } catch (error: any) {
      const restError = error as RestError;
      if (
        restError.statusCode &&
        (restError.statusCode === 307 || // Temporary redirect
          restError.statusCode === 308)
      ) {
        // Permanent redirect
        this.numConsecutiveRedirects++;
        // To prevent circular redirects
        if (this.numConsecutiveRedirects < 10) {
          if (restError.response && restError.response.headers) {
            const location = restError.response.headers.get("location");
            if (location) {
              // Update sender URL
              this.handlePermanentRedirect(location);
              // Send to redirect endpoint as HTTPs library doesn't handle redirect automatically
              return this.exportEnvelopes(envelopes);
            }
          }
        } else {
          const redirectError = new Error("Circular redirect");
          this.networkStatsbeatMetrics?.countException(redirectError);
          return { code: ExportResultCode.FAILED, error: redirectError };
        }
      } else if (
        restError.statusCode &&
        isRetriable(restError.statusCode) &&
        !this.isStatsbeatSender
      ) {
        this.networkStatsbeatMetrics?.countRetry(restError.statusCode);
        return this.persist(envelopes);
      } else if (
        restError.statusCode === 400 &&
        restError.message.includes("Invalid instrumentation key")
      ) {
        // Invalid instrumentation key, shutdown statsbeat, fail silently
        this.shutdownStatsbeat();
        return { code: ExportResultCode.SUCCESS };
      } else if (
        restError.statusCode &&
        this.isStatsbeatSender &&
        isStatsbeatShutdownStatus(restError.statusCode)
      ) {
        // If the status code is a shutdown status code for statsbeat, shutdown statsbeat and fail silently
        this.incrementStatsbeatFailure();
        return { code: ExportResultCode.SUCCESS };
      }
      if (this.isRetriableRestError(restError)) {
        if (restError.statusCode) {
          this.networkStatsbeatMetrics?.countRetry(restError.statusCode);
        }
        if (!this.isStatsbeatSender) {
          diag.error(
            "Retrying due to transient client side error. Error message:",
            restError.message,
          );
        }
        return this.persist(envelopes);
      }
      this.networkStatsbeatMetrics?.countException(restError);
      if (!this.isStatsbeatSender) {
        diag.error(
          "Envelopes could not be exported and are not retriable. Error message:",
          restError.message,
        );
      }
      return { code: ExportResultCode.FAILED, error: restError };
    }
  }