Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@supertenant/core / src / tracing / index.js
Size: Mime:
// (c) Copyright 2023 Supertenant Ltd. - all rights reserved.
// See LICENSE file in project root for license terms.

'use strict';

const sdk = require('./sdk');
const constants = require('./constants');
const tracingMetrics = require('./metrics');
const opentracing = require('./opentracing');
const spanHandle = require('./spanHandle');
const tracingHeaders = require('./tracingHeaders');
const tracingUtil = require('./tracingUtil');
const spanBuffer = require('./spanBuffer');
const supportedVersion = require('./supportedVersion');

let tracingEnabled = false;
let tracingActivated = false;
let instrumenationsInitialized = false;
let automaticTracingEnabled = false;
/** @type {import('./cls')} */
let cls = null;
/** @type {import('../util/normalizeConfig').InstanaConfig} */
let config = null;

/** @typedef {import('../../../collector/src/pidStore')} CollectorPIDStore */

/**
 * @typedef {Object} TracingMetrics
 * @property {number} pid
 * @property {{opened: number, closed: number, dropped: number}} metrics
 */

/** @type {CollectorPIDStore} */
let processIdentityProvider = null;

// Note: Also update initializedTooLateHeuristic.js and the accompanying test when adding instrumentations.
const instrumentations = [
  // FIXME supertenant - all these will be added later.
  // './instrumentation/cloud/aws-sdk/v2/index',
  // './instrumentation/cloud/aws-sdk/v3/index',
  // './instrumentation/cloud/aws-sdk/v2/sdk',
  // './instrumentation/cloud/aws-sdk/v2/sqs',
  // './instrumentation/cloud/gcp/pubsub',
  // './instrumentation/cloud/gcp/storage',
  './instrumentation/control_flow/bluebird',
  './instrumentation/control_flow/clsHooked',
  // './instrumentation/control_flow/graphqlSubscriptions',
  // './instrumentation/database/elasticsearchLegacy',
  // './instrumentation/database/elasticsearchModern',
  // './instrumentation/database/ioredis',
  // './instrumentation/database/memcached',
  // './instrumentation/database/mongodb',
  // './instrumentation/database/mongoose',
  // './instrumentation/database/mssql',
  './instrumentation/database/mysql',
  // './instrumentation/database/pg',
  // './instrumentation/database/pgNative',
  // './instrumentation/database/prisma',
  // './instrumentation/database/redis',
  // './instrumentation/database/db2',
  './instrumentation/frameworks/express',
  // './instrumentation/frameworks/fastify',
  // './instrumentation/frameworks/hapi',
  // './instrumentation/frameworks/koa',
  // './instrumentation/loggers/bunyan',
  // './instrumentation/loggers/log4js',
  // './instrumentation/loggers/pino',
  // './instrumentation/loggers/winston',
  // './instrumentation/loggers/console',
  // './instrumentation/messaging/amqp',
  // './instrumentation/messaging/kafkaJs',
  // './instrumentation/messaging/kafkaNode',
  // './instrumentation/messaging/rdkafka',
  // './instrumentation/messaging/nats',
  // './instrumentation/messaging/natsStreaming',
  // './instrumentation/messaging/bull',
  // './instrumentation/process/memored',
  // './instrumentation/process/process',
  // './instrumentation/protocols/graphql',
  // './instrumentation/protocols/grpc',
  // './instrumentation/protocols/grpcJs',
  './instrumentation/protocols/http2Client',
  './instrumentation/protocols/http2Server',
  './instrumentation/protocols/httpClient',
  './instrumentation/protocols/httpServer',
  './instrumentation/protocols/nativeFetch',
  './instrumentation/protocols/superagent'
];

/**
 * This is a temporary type definition for instrumented modules until we get to add types to these modules.
 * For now it is safe to say that these modules are objects with the following methods:
 * @typedef {Object} InstanaInstrumentedModule
 * @property {Function} init
 * @property {Function} activate
 * @property {Function} deactivate
 * @property {Function} [updateConfig]
 * @property {boolean} [batchable]
 * @property {string} [spanName]
 */

/**
 * @typedef {Object} KafkaTracingConfig
 * @property {boolean} [traceCorrelation]
 * @property {string} [headerFormat]
 */

/** @type {Array.<InstanaInstrumentedModule>} */
let additionalInstrumentationModules = [];
/** @type {Object.<string, InstanaInstrumentedModule>} */
const instrumentationModules = {};

exports.constants = constants;
exports.tracingHeaders = tracingHeaders;
exports.opentracing = opentracing;
exports.sdk = sdk;
exports.spanBuffer = spanBuffer;
exports.supportedVersion = supportedVersion;
exports.util = tracingUtil;

/**
 * @param {Array.<InstanaInstrumentedModule>} _additionalInstrumentationModules
 */
exports.registerAdditionalInstrumentations = function registerAdditionalInstrumentations(
  _additionalInstrumentationModules
) {
  additionalInstrumentationModules = additionalInstrumentationModules.concat(_additionalInstrumentationModules);
};

/**
 * @param {import('../util/normalizeConfig').InstanaConfig} preliminaryConfig
 */
exports.preInit = function preInit(preliminaryConfig) {
  initInstrumenations(preliminaryConfig);
};

/**
 * @param {import('../util/normalizeConfig').InstanaConfig} _config
 * @param {import('..').DownstreamConnection} downstreamConnection
 * @param {CollectorPIDStore} _processIdentityProvider
 */
exports.init = function init(_config, downstreamConnection, _processIdentityProvider) {
  config = _config;
  processIdentityProvider = _processIdentityProvider;

  tracingEnabled = config.tracing.enabled;
  automaticTracingEnabled = config.tracing.automaticTracingEnabled;

  if (tracingEnabled) {
    tracingUtil.init(config);
    tracingHeaders.init(config);
    if (process.env.ST_INSTANA_ENABLED === "true") {
      spanBuffer.init(config, downstreamConnection);
      opentracing.init(config, automaticTracingEnabled, processIdentityProvider);
    }
    cls = require('./cls');
    cls.init(config, processIdentityProvider);
    sdk.init(cls);

    if (automaticTracingEnabled) {
      initInstrumenations(config);
    }
  }

  if (config.tracing.activateImmediately) {
    exports.activate();
  }
};

/**
 * @param {import('../util/normalizeConfig').InstanaConfig} _config
 */
function initInstrumenations(_config) {
  // initialize all instrumentations
  if (!instrumenationsInitialized) {
    instrumentations.forEach(instrumentationKey => {
      instrumentationModules[instrumentationKey] = require(instrumentationKey);
      instrumentationModules[instrumentationKey].init(_config);
      if (instrumentationModules[instrumentationKey].batchable && instrumentationModules[instrumentationKey].spanName) {
        spanBuffer.addBatchableSpanName(instrumentationModules[instrumentationKey].spanName);
      }
    });
    additionalInstrumentationModules.forEach(instrumentationModule => {
      instrumentationModule.init(_config);
    });
    instrumenationsInitialized = true;
  } else {
    instrumentations.forEach(instrumentationKey => {
      if (instrumentationModules[instrumentationKey].updateConfig) {
        instrumentationModules[instrumentationKey].updateConfig(_config);
      }
    });
  }
}

exports.activate = function activate(extraConfig = {}) {
  if (tracingEnabled && !tracingActivated) {
    tracingActivated = true;
    if (process.env.ST_INSTANA_ENABLED === "true") {
      spanBuffer.activate(extraConfig);
      opentracing.activate();
      sdk.activate();
    }
    if (automaticTracingEnabled) {
      instrumentations.forEach(instrumentationKey => {
        const instrumentationName = /.\/instrumentation\/[^/]*\/(.*)/.exec(instrumentationKey)[1];

        const isDisabled =
          config.tracing.disabledTracers.findIndex(disabledKey => instrumentationName.toLowerCase() === disabledKey) !==
          -1;

        if (!isDisabled) {
          instrumentationModules[instrumentationKey].activate(extraConfig);
        }
      });
    }
  }
};

exports.deactivate = function deactivate() {
  if (tracingEnabled && tracingActivated) {
    tracingActivated = false;

    if (automaticTracingEnabled) {
      instrumentations.forEach(instrumentationKey => {
        instrumentationModules[instrumentationKey].deactivate();
      });
    }

    if (process.env.ST_INSTANA_ENABLED === "true") {
      opentracing.deactivate();
      spanBuffer.deactivate();
      sdk.deactivate();
    }
  }
};

exports.getHandleForCurrentSpan = function getHandleForCurrentSpan() {
  return spanHandle.getHandleForCurrentSpan(cls);
};

exports.getCls = function getCls() {
  // This only provides a value if tracing is enabled, otherwise cls will not be required and is null.
  return cls;
};

/**
 * @returns {TracingMetrics}
 */
exports._getAndResetTracingMetrics = function _getAndResetTracingMetrics() {
  return {
    pid:
      processIdentityProvider && typeof processIdentityProvider.getEntityId === 'function'
        ? processIdentityProvider.getEntityId()
        : undefined,
    metrics: tracingMetrics.getAndReset()
  };
};

/**
 * @param {string} name
 * @param {*} module_
 */
exports._instrument = function _instrument(name, module_) {
  if (name === 'superagent') {
    require('./instrumentation/protocols/superagent').instrument(module_);
  } else {
    throw new Error(`An unknown or unsupported instrumentation has been requested: ${name}`);
  }
};

exports._debugCurrentSpanName = function _debugCurrentSpanName() {
  if (!cls) {
    return 'current: no cls';
  }
  const s = cls.ns.get('com.supertenant.span');
  if (!s) {
    return 'current: no span';
  }
  return `current: ${s.n}`;
};