@microsoft/applicationinsights-core-js
Version:
Microsoft Application Insights Core Javascript SDK
488 lines (486 loc) • 24.1 kB
JavaScript
/*
* Application Insights JavaScript SDK - Core, 3.3.9
* Copyright (c) Microsoft and contributors. All rights reserved.
*/
;
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