UNPKG

@microsoft/applicationinsights-core-js

Version:

Microsoft Application Insights Core Javascript SDK

488 lines (486 loc) • 24.1 kB
/* * Application Insights JavaScript SDK - Core, 3.3.9 * Copyright (c) Microsoft and contributors. All rights reserved. */ "use strict"; import { arrForEach, dumpObj, isArray, isFunction, isNullOrUndefined, isUndefined, objForEachKey, objFreeze, objKeys } from "@nevware21/ts-utils"; import { _applyDefaultValue } from "../Config/ConfigDefaults"; import { createDynamicConfig } from "../Config/DynamicConfig"; import { _DYN_CREATE_NEW, _DYN_DIAG_LOG, _DYN_GET_NEXT, _DYN_GET_PLUGIN, _DYN_IDENTIFIER, _DYN_IS_INITIALIZED, _DYN_LENGTH, _DYN_LOGGER, _DYN_PROCESS_NEXT, _DYN_PUSH, _DYN_SET_NEXT_PLUGIN, _DYN_TEARDOWN, _DYN_UNLOAD, _DYN_UPDATE } from "../__DynamicConstants"; import { _throwInternal, safeGetLogger } from "./DiagnosticLogger"; import { proxyFunctions } from "./HelperFuncs"; import { STR_CORE, STR_DISABLED, STR_EMPTY, STR_EXTENSION_CONFIG, STR_PRIORITY, STR_PROCESS_TELEMETRY } from "./InternalConstants"; import { doPerf } from "./PerfManager"; import { _getPluginState } from "./TelemetryHelpers"; var strTelemetryPluginChain = "TelemetryPluginChain"; var strHasRunFlags = "_hasRun"; var strGetTelCtx = "_getTelCtx"; var _chainId = 0; function _getNextProxyStart(proxy, core, startAt) { while (proxy) { if (proxy[_DYN_GET_PLUGIN /* @min:%2egetPlugin */]() === startAt) { return proxy; } proxy = proxy[_DYN_GET_NEXT /* @min:%2egetNext */](); } // This wasn't found in the existing chain so create an isolated one with just this plugin return createTelemetryProxyChain([startAt], core.config || {}, core); } /** * @ignore * @param telemetryChain * @param dynamicHandler * @param core * @param startAt - Identifies the next plugin to execute, if null there is no "next" plugin and if undefined it should assume the start of the chain * @returns */ function _createInternalContext(telemetryChain, dynamicHandler, core, startAt) { // We have a special case where we want to start execution from this specific plugin // or we simply reuse the existing telemetry plugin chain (normal execution case) var _nextProxy = null; // By Default set as no next plugin var _onComplete = []; if (!dynamicHandler) { dynamicHandler = createDynamicConfig({}, null, core[_DYN_LOGGER /* @min:%2elogger */]); } if (startAt !== null) { // There is no next element (null) vs not defined (undefined) so use the full chain _nextProxy = startAt ? _getNextProxyStart(telemetryChain, core, startAt) : telemetryChain; } var context = { _next: _moveNext, ctx: { core: function () { return core; }, diagLog: function () { return safeGetLogger(core, dynamicHandler.cfg); }, getCfg: function () { return dynamicHandler.cfg; }, getExtCfg: _resolveExtCfg, getConfig: _getConfig, hasNext: function () { return !!_nextProxy; }, getNext: function () { return _nextProxy; }, setNext: function (nextPlugin) { _nextProxy = nextPlugin; }, iterate: _iterateChain, onComplete: _addOnComplete } }; function _addOnComplete(onComplete, that) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } if (onComplete) { _onComplete[_DYN_PUSH /* @min:%2epush */]({ func: onComplete, self: !isUndefined(that) ? that : context.ctx, args: args }); } } function _moveNext() { var nextProxy = _nextProxy; // Automatically move to the next plugin _nextProxy = nextProxy ? nextProxy[_DYN_GET_NEXT /* @min:%2egetNext */]() : null; if (!nextProxy) { var onComplete = _onComplete; if (onComplete && onComplete[_DYN_LENGTH /* @min:%2elength */] > 0) { arrForEach(onComplete, function (completeDetails) { try { completeDetails.func.call(completeDetails.self, completeDetails.args); } catch (e) { _throwInternal(core[_DYN_LOGGER /* @min:%2elogger */], 2 /* eLoggingSeverity.WARNING */, 73 /* _eInternalMessageId.PluginException */, "Unexpected Exception during onComplete - " + dumpObj(e)); } }); _onComplete = []; } } return nextProxy; } function _getExtCfg(identifier, createIfMissing) { var idCfg = null; var cfg = dynamicHandler.cfg; if (cfg && identifier) { var extCfg = cfg[STR_EXTENSION_CONFIG /* @min:%2eextensionConfig */]; if (!extCfg && createIfMissing) { extCfg = {}; } // Always set the value so that the property always exists cfg[STR_EXTENSION_CONFIG] = extCfg; // Note: it is valid for the "value" to be undefined // Calling `ref()` has a side effect of causing the referenced property to become dynamic (if not already) extCfg = dynamicHandler.ref(cfg, STR_EXTENSION_CONFIG); if (extCfg) { idCfg = extCfg[identifier]; if (!idCfg && createIfMissing) { idCfg = {}; } // Always set the value so that the property always exists extCfg[identifier] = idCfg; // Note: it is valid for the "value" to be undefined // Calling `ref()` has a side effect of causing the referenced property to become dynamic (if not already) idCfg = dynamicHandler.ref(extCfg, identifier); } } return idCfg; } function _resolveExtCfg(identifier, defaultValues) { var newConfig = _getExtCfg(identifier, true); if (defaultValues) { // Enumerate over the defaultValues and if not already populated attempt to // find a value from the root config or use the default value objForEachKey(defaultValues, function (field, defaultValue) { // for each unspecified field, set the default value if (isNullOrUndefined(newConfig[field])) { var cfgValue = dynamicHandler.cfg[field]; if (cfgValue || !isNullOrUndefined(cfgValue)) { newConfig[field] = cfgValue; } } _applyDefaultValue(dynamicHandler, newConfig, field, defaultValue); }); } return dynamicHandler.setDf(newConfig, defaultValues); } function _getConfig(identifier, field, defaultValue) { if (defaultValue === void 0) { defaultValue = false; } var theValue; var extConfig = _getExtCfg(identifier, false); var rootConfig = dynamicHandler.cfg; if (extConfig && (extConfig[field] || !isNullOrUndefined(extConfig[field]))) { theValue = extConfig[field]; } else if (rootConfig[field] || !isNullOrUndefined(rootConfig[field])) { theValue = rootConfig[field]; } return (theValue || !isNullOrUndefined(theValue)) ? theValue : defaultValue; } function _iterateChain(cb) { // Keep processing until we reach the end of the chain var nextPlugin; while (!!(nextPlugin = context._next())) { var plugin = nextPlugin[_DYN_GET_PLUGIN /* @min:%2egetPlugin */](); if (plugin) { // callback with the current on cb(plugin); } } } return context; } /** * Creates a new Telemetry Item context with the current config, core and plugin execution chain * @param plugins - The plugin instances that will be executed * @param config - The current config * @param core - The current core instance * @param startAt - Identifies the next plugin to execute, if null there is no "next" plugin and if undefined it should assume the start of the chain */ export function createProcessTelemetryContext(telemetryChain, cfg, core, startAt) { var config = createDynamicConfig(cfg); var internalContext = _createInternalContext(telemetryChain, config, core, startAt); var context = internalContext.ctx; function _processNext(env) { var nextPlugin = internalContext._next(); if (nextPlugin) { // Run the next plugin which will call "processNext()" nextPlugin[STR_PROCESS_TELEMETRY /* @min:%2eprocessTelemetry */](env, context); } return !nextPlugin; } function _createNew(plugins, startAt) { if (plugins === void 0) { plugins = null; } if (isArray(plugins)) { plugins = createTelemetryProxyChain(plugins, config.cfg, core, startAt); } return createProcessTelemetryContext(plugins || context[_DYN_GET_NEXT /* @min:%2egetNext */](), config.cfg, core, startAt); } context[_DYN_PROCESS_NEXT /* @min:%2eprocessNext */] = _processNext; context[_DYN_CREATE_NEW /* @min:%2ecreateNew */] = _createNew; return context; } /** * Creates a new Telemetry Item context with the current config, core and plugin execution chain for handling the unloading of the chain * @param plugins - The plugin instances that will be executed * @param config - The current config * @param core - The current core instance * @param startAt - Identifies the next plugin to execute, if null there is no "next" plugin and if undefined it should assume the start of the chain */ export function createProcessTelemetryUnloadContext(telemetryChain, core, startAt) { var config = createDynamicConfig(core.config); var internalContext = _createInternalContext(telemetryChain, config, core, startAt); var context = internalContext.ctx; function _processNext(unloadState) { var nextPlugin = internalContext._next(); nextPlugin && nextPlugin[_DYN_UNLOAD /* @min:%2eunload */](context, unloadState); return !nextPlugin; } function _createNew(plugins, startAt) { if (plugins === void 0) { plugins = null; } if (isArray(plugins)) { plugins = createTelemetryProxyChain(plugins, config.cfg, core, startAt); } return createProcessTelemetryUnloadContext(plugins || context[_DYN_GET_NEXT /* @min:%2egetNext */](), core, startAt); } context[_DYN_PROCESS_NEXT /* @min:%2eprocessNext */] = _processNext; context[_DYN_CREATE_NEW /* @min:%2ecreateNew */] = _createNew; return context; } /** * Creates a new Telemetry Item context with the current config, core and plugin execution chain for updating the configuration * @param plugins - The plugin instances that will be executed * @param config - The current config * @param core - The current core instance * @param startAt - Identifies the next plugin to execute, if null there is no "next" plugin and if undefined it should assume the start of the chain */ export function createProcessTelemetryUpdateContext(telemetryChain, core, startAt) { var config = createDynamicConfig(core.config); var internalContext = _createInternalContext(telemetryChain, config, core, startAt); var context = internalContext.ctx; function _processNext(updateState) { return context.iterate(function (plugin) { if (isFunction(plugin[_DYN_UPDATE /* @min:%2eupdate */])) { plugin[_DYN_UPDATE /* @min:%2eupdate */](context, updateState); } }); } function _createNew(plugins, startAt) { if (plugins === void 0) { plugins = null; } if (isArray(plugins)) { plugins = createTelemetryProxyChain(plugins, config.cfg, core, startAt); } return createProcessTelemetryUpdateContext(plugins || context[_DYN_GET_NEXT /* @min:%2egetNext */](), core, startAt); } context[_DYN_PROCESS_NEXT /* @min:%2eprocessNext */] = _processNext; context[_DYN_CREATE_NEW /* @min:%2ecreateNew */] = _createNew; return context; } /** * Creates an execution chain from the array of plugins * @param plugins - The array of plugins that will be executed in this order * @param defItemCtx - The default execution context to use when no telemetry context is passed to processTelemetry(), this * should be for legacy plugins only. Currently, only used for passing the current core instance and to provide better error * reporting (hasRun) when errors occur. */ export function createTelemetryProxyChain(plugins, config, core, startAt) { var firstProxy = null; var add = startAt ? false : true; if (isArray(plugins) && plugins[_DYN_LENGTH /* @min:%2elength */] > 0) { // Create the proxies and wire up the next plugin chain var lastProxy_1 = null; arrForEach(plugins, function (thePlugin) { if (!add && startAt === thePlugin) { add = true; } if (add && thePlugin && isFunction(thePlugin[STR_PROCESS_TELEMETRY /* @min:%2eprocessTelemetry */])) { // Only add plugins that are processors var newProxy = createTelemetryPluginProxy(thePlugin, config, core); if (!firstProxy) { firstProxy = newProxy; } if (lastProxy_1) { // Set this new proxy as the next for the previous one lastProxy_1._setNext(newProxy); } lastProxy_1 = newProxy; } }); } if (startAt && !firstProxy) { // Special case where the "startAt" was not in the original list of plugins return createTelemetryProxyChain([startAt], config, core); } return firstProxy; } /** * Create the processing telemetry proxy instance, the proxy is used to abstract the current plugin to allow monitoring and * execution plugins while passing around the dynamic execution state (IProcessTelemetryContext), the proxy instance no longer * contains any execution state and can be reused between requests (this was not the case for 2.7.2 and earlier with the * TelemetryPluginChain class). * @param plugin - The plugin instance to proxy * @param config - The default execution context to use when no telemetry context is passed to processTelemetry(), this * should be for legacy plugins only. Currently, only used for passing the current core instance and to provide better error * reporting (hasRun) when errors occur. * @returns */ export function createTelemetryPluginProxy(plugin, config, core) { var nextProxy = null; var hasProcessTelemetry = isFunction(plugin[STR_PROCESS_TELEMETRY /* @min:%2eprocessTelemetry */]); var hasSetNext = isFunction(plugin[_DYN_SET_NEXT_PLUGIN /* @min:%2esetNextPlugin */]); var chainId; if (plugin) { chainId = plugin[_DYN_IDENTIFIER /* @min:%2eidentifier */] + "-" + plugin[STR_PRIORITY /* @min:%2epriority */] + "-" + _chainId++; } else { chainId = "Unknown-0-" + _chainId++; } var proxyChain = { getPlugin: function () { return plugin; }, getNext: function () { return nextProxy; }, processTelemetry: _processTelemetry, unload: _unloadPlugin, update: _updatePlugin, _id: chainId, _setNext: function (nextPlugin) { nextProxy = nextPlugin; } }; function _getTelCtx() { var itemCtx; // Looks like a plugin didn't pass the (optional) context, so create a new one if (plugin && isFunction(plugin[strGetTelCtx])) { // This plugin extends from the BaseTelemetryPlugin so lets use it itemCtx = plugin[strGetTelCtx](); } if (!itemCtx) { // Create a temporary one itemCtx = createProcessTelemetryContext(proxyChain, config, core); } return itemCtx; } function _processChain(itemCtx, processPluginFn, name, details, isAsync) { var hasRun = false; var identifier = plugin ? plugin[_DYN_IDENTIFIER /* @min:%2eidentifier */] : strTelemetryPluginChain; var hasRunContext = itemCtx[strHasRunFlags]; if (!hasRunContext) { // Assign and populate hasRunContext = itemCtx[strHasRunFlags] = {}; } // Ensure that we keep the context in sync itemCtx.setNext(nextProxy); if (plugin) { doPerf(itemCtx[STR_CORE /* @min:%2ecore */](), function () { return identifier + ":" + name; }, function () { // Mark this component as having run hasRunContext[chainId] = true; try { // Set a flag on the next plugin so we know if it was attempted to be executed var nextId = nextProxy ? nextProxy._id : STR_EMPTY; if (nextId) { hasRunContext[nextId] = false; } hasRun = processPluginFn(itemCtx); } catch (error) { var hasNextRun = nextProxy ? hasRunContext[nextProxy._id] : true; if (hasNextRun) { // The next plugin after us has already run so set this one as complete hasRun = true; } if (!nextProxy || !hasNextRun) { // Either we have no next plugin or the current one did not attempt to call the next plugin // Which means the current one is the root of the failure so log/report this failure _throwInternal(itemCtx[_DYN_DIAG_LOG /* @min:%2ediagLog */](), 1 /* eLoggingSeverity.CRITICAL */, 73 /* _eInternalMessageId.PluginException */, "Plugin [" + identifier + "] failed during " + name + " - " + dumpObj(error) + ", run flags: " + dumpObj(hasRunContext)); } } }, details, isAsync); } return hasRun; } function _processTelemetry(env, itemCtx) { itemCtx = itemCtx || _getTelCtx(); function _callProcessTelemetry(itemCtx) { if (!plugin || !hasProcessTelemetry) { return false; } var pluginState = _getPluginState(plugin); if (pluginState[_DYN_TEARDOWN /* @min:%2eteardown */] || pluginState[STR_DISABLED]) { return false; } // Ensure that we keep the context in sync (for processNext()), just in case a plugin // doesn't calls processTelemetry() instead of itemContext.processNext() or some // other form of error occurred if (hasSetNext) { // Backward compatibility setting the next plugin on the instance plugin[_DYN_SET_NEXT_PLUGIN /* @min:%2esetNextPlugin */](nextProxy); } plugin[STR_PROCESS_TELEMETRY /* @min:%2eprocessTelemetry */](env, itemCtx); // Process Telemetry is expected to call itemCtx.processNext() or nextPlugin.processTelemetry() return true; } if (!_processChain(itemCtx, _callProcessTelemetry, "processTelemetry", function () { return ({ item: env }); }, !(env.sync))) { // The underlying plugin is either not defined, not enabled or does not have a processTelemetry implementation // so we still want the next plugin to be executed. itemCtx[_DYN_PROCESS_NEXT /* @min:%2eprocessNext */](env); } } function _unloadPlugin(unloadCtx, unloadState) { function _callTeardown() { // Setting default of hasRun as false so the proxyProcessFn() is called as teardown() doesn't have to exist or call unloadNext(). var hasRun = false; if (plugin) { var pluginState = _getPluginState(plugin); var pluginCore = plugin[STR_CORE] || pluginState[STR_CORE /* @min:%2ecore */]; // Only teardown the plugin if it was initialized by the current core (i.e. It's not a shared plugin) if (plugin && (!pluginCore || pluginCore === unloadCtx.core()) && !pluginState[_DYN_TEARDOWN /* @min:%2eteardown */]) { // Handle plugins that don't extend from the BaseTelemetryPlugin pluginState[STR_CORE /* @min:%2ecore */] = null; pluginState[_DYN_TEARDOWN /* @min:%2eteardown */] = true; pluginState[_DYN_IS_INITIALIZED /* @min:%2eisInitialized */] = false; if (plugin[_DYN_TEARDOWN /* @min:%2eteardown */] && plugin[_DYN_TEARDOWN /* @min:%2eteardown */](unloadCtx, unloadState) === true) { // plugin told us that it was going to (or has) call unloadCtx.processNext() hasRun = true; } } } return hasRun; } if (!_processChain(unloadCtx, _callTeardown, "unload", function () { }, unloadState.isAsync)) { // Only called if we hasRun was not true unloadCtx[_DYN_PROCESS_NEXT /* @min:%2eprocessNext */](unloadState); } } function _updatePlugin(updateCtx, updateState) { function _callUpdate() { // Setting default of hasRun as false so the proxyProcessFn() is called as teardown() doesn't have to exist or call unloadNext(). var hasRun = false; if (plugin) { var pluginState = _getPluginState(plugin); var pluginCore = plugin[STR_CORE] || pluginState[STR_CORE /* @min:%2ecore */]; // Only update the plugin if it was initialized by the current core (i.e. It's not a shared plugin) if (plugin && (!pluginCore || pluginCore === updateCtx.core()) && !pluginState[_DYN_TEARDOWN /* @min:%2eteardown */]) { if (plugin[_DYN_UPDATE /* @min:%2eupdate */] && plugin[_DYN_UPDATE /* @min:%2eupdate */](updateCtx, updateState) === true) { // plugin told us that it was going to (or has) call unloadCtx.processNext() hasRun = true; } } } return hasRun; } if (!_processChain(updateCtx, _callUpdate, "update", function () { }, false)) { // Only called if we hasRun was not true updateCtx[_DYN_PROCESS_NEXT /* @min:%2eprocessNext */](updateState); } } return objFreeze(proxyChain); } /** * This class will be removed! * @deprecated use createProcessTelemetryContext() instead */ var ProcessTelemetryContext = /** @class */ (function () { /** * Creates a new Telemetry Item context with the current config, core and plugin execution chain * @param plugins - The plugin instances that will be executed * @param config - The current config * @param core - The current core instance */ function ProcessTelemetryContext(pluginChain, config, core, startAt) { var _self = this; var context = createProcessTelemetryContext(pluginChain, config, core, startAt); // Proxy all functions of the context to this object proxyFunctions(_self, context, objKeys(context)); } return ProcessTelemetryContext; }()); export { ProcessTelemetryContext }; //# sourceMappingURL=ProcessTelemetryContext.js.map