function Client()

in lib/apm-client/http-apm-client/index.js [96:226]


function Client(opts) {
  if (!(this instanceof Client)) return new Client(opts);

  Writable.call(this, { objectMode: true });

  this._corkTimer = null;
  this._agent = null;
  this._activeIntakeReq = false;
  this._onIntakeReqConcluded = null;
  this._transport = null;
  this._configTimer = null;
  this._backoffReconnectCount = 0;
  this._intakeRequestGracefulExitFn = null; // set in makeIntakeRequest
  this._encodedMetadata = null;
  this._cloudMetadata = null;
  this._extraMetadata = null;
  this._metadataFilters = new Filters();
  // _lambdaActive indicates if a Lambda function invocation is active. It is
  // only meaningful if `isLambdaExecutionEnvironment`.
  this._lambdaActive = false;
  // Whether to forward `.lambdaRegisterTransaction()` calls to the Lambda
  // extension. This will be set false if a previous attempt failed.
  this._lambdaShouldRegisterTransactions = true;

  // Internal runtime stats for developer debugging/tuning.
  this._numEvents = 0; // number of events given to the client
  this._numEventsDropped = 0; // number of events dropped because overloaded
  this._numEventsEnqueued = 0; // number of events written through to chopper
  this.sent = 0; // number of events sent to APM server (not necessarily accepted)
  this._slowWriteBatch = {
    // data on slow or the slowest _writeBatch
    numOver10Ms: 0,
    // Data for the slowest _writeBatch:
    encodeTimeMs: 0,
    fullTimeMs: 0,
    numEvents: 0,
    numBytes: 0,
  };

  this.config(opts);
  this._log = this._conf.logger || new NoopLogger();

  // `_apmServerVersion` is one of:
  // - `undefined`: the version has not yet been fetched
  // - `null`: the APM server version is unknown, could not be determined
  // - a semver.SemVer instance
  this._apmServerVersion = this._conf.apmServerVersion
    ? new semver.SemVer(this._conf.apmServerVersion)
    : undefined;
  if (!this._apmServerVersion) {
    this._fetchApmServerVersion();
  }

  const numExtraMdOpts = [
    this._conf.cloudMetadataFetcher,
    this._conf.expectExtraMetadata,
    this._conf.extraMetadata,
  ].reduce((accum, curr) => (curr ? accum + 1 : accum), 0);
  if (numExtraMdOpts > 1) {
    throw new Error(
      'it is an error to configure a Client with more than one of "cloudMetadataFetcher", "expectExtraMetadata", or "extraMetadata"',
    );
  } else if (this._conf.cloudMetadataFetcher) {
    // Start stream in corked mode, uncork when cloud metadata is fetched and
    // assigned.  Also, the _maybeUncork will not uncork until _encodedMetadata
    // is set.
    this._log.trace('corking (cloudMetadataFetcher)');
    this.cork();
    this._fetchAndEncodeMetadata(() => {
      // _fetchAndEncodeMetadata will have set/memoized the encoded
      // metadata to the _encodedMetadata property.

      // This reverses the cork() call in the constructor above. "Maybe" uncork,
      // in case the client has been destroyed before this callback is called.
      this._maybeUncork();
      this._log.trace('uncorked (cloudMetadataFetcher)');

      // the `cloud-metadata` event allows listeners to know when the
      // agent has finished fetching and encoding its metadata for the
      // first time
      this.emit('cloud-metadata', this._encodedMetadata);
    });
  } else if (this._conf.expectExtraMetadata) {
    // Uncorking will happen in the expected `.setExtraMetadata()` call.
    this._log.trace('corking (expectExtraMetadata)');
    this.cork();
  } else if (this._conf.extraMetadata) {
    this.setExtraMetadata(this._conf.extraMetadata);
  } else {
    this._resetEncodedMetadata();
  }

  this._chopper = new StreamChopper({
    size: this._conf.size,
    time: this._conf.time,
    type: StreamChopper.overflow,
    transform() {
      return zlib.createGzip({
        level: zlib.constants.Z_BEST_SPEED,
      });
    },
  });
  const onIntakeError = (err) => {
    if (this.destroyed === false) {
      this.emit('request-error', err);
    }
  };
  this._chopper.on('stream', getChoppedStreamHandler(this, onIntakeError));

  // We don't expect the chopper stream to end until the client is ending.
  // Make sure to clean up if this does happen unexpectedly.
  const fail = () => {
    if (this._writableState.ending === false) this.destroy();
  };
  eos(this._chopper, fail);

  this._index = clientsToAutoEnd.length;
  clientsToAutoEnd.push(this);

  // The 'beforeExit' event is significant in Lambda invocation completion
  // handling, so we log it for debugging.
  if (isLambdaExecutionEnvironment && this._log.isLevelEnabled('trace')) {
    process.prependListener('beforeExit', () => {
      this._log.trace('process "beforeExit"');
    });
  }

  if (this._conf.centralConfig) {
    this._pollConfig();
  }
}