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 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').Config} */
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;

// IMPORTANT: Update initializedTooLateHeuristic.js and its test when adding more instrumentations.
const instrumentations = [
  './instrumentation/control_flow/bluebird',
  './instrumentation/control_flow/clsHooked',
  './instrumentation/database/ioredis',
  './instrumentation/database/mongodb',
  './instrumentation/database/mysql',
  './instrumentation/database/redis',
  './instrumentation/frameworks/express',
  './instrumentation/protocols/http2Client',
  './instrumentation/protocols/http2Server',
  './instrumentation/protocols/httpClient',
  './instrumentation/protocols/httpServer'
];

/**
 * 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} InstrumentedModule
 * @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.<InstrumentedModule>} */
let additionalInstrumentationModules = [];
/** @type {Object.<string, InstrumentedModule>} */
const instrumentationModules = {};

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

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

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

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

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

  if (tracingEnabled) {
    tracingHeaders.init(config);
    cls = require('./cls');
    cls.init(config, processIdentityProvider);
    sdk.init(cls);

    if (automaticTracingEnabled) {
      initInstrumentations(config);
      exports.activate(config);
    }
  }
};

/**
 * @param {import('../util/normalizeConfig').Config} _config
 */
function initInstrumentations(_config) {
  // initialize all instrumentations
  if (!instrumenationsInitialized) {
    instrumentations.forEach(instrumentationKey => {
      instrumentationModules[instrumentationKey] = require(instrumentationKey);
      instrumentationModules[instrumentationKey].init(_config);
    });
    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') {
      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();
      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()
  };
};

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}`;
};