UNPKG

apminsight

Version:

monitor nodejs applications

466 lines (400 loc) 14.1 kB
var tracker = require("./tracker"); var utils = require("./../util/utils"); var logger = require("./../util/logger"); var metricstore = require("./metricstore"); var errorInfo = require("./error"); function Transaction(reqInfo) { this._startTime = new Date().getTime(); (this._responseTime = 0), (this._minRt = 0), (this._maxRt = 0); (this._errorRt = 0), (this._count = 0), (this._endTime = 0); (this._errorCount = 0), (this._completed = false); (this._dbCalls = []), (this._intCompInfo = {}); (this._extCompInfo = {}), (this._extCallCount = 0), (this._errInfo = {}); (this._ocurrence = -1), (this._traceReason = 0); this._byApi = reqInfo.api ? true : false; this._ignore = false; this._rootTracker = tracker.createRootTracker(reqInfo.rootListner); (this._trackersCount = 0), (this._sqlTrackersCount = 0); this._customParams = {}; this._error = null; } Transaction.prototype.updateRtMetric = function () { this._endTime = new Date().getTime(); this._responseTime = this._endTime - this._startTime; this._minRt = this._responseTime; this._maxRt = this._responseTime; }; Transaction.prototype.updateReqCountByErr = function (err) { if (err instanceof Error) { this._errorCount = 1; this._error = new errorInfo(err); this.getRootTracker().setError(err, this); } else { this._count = 1; } }; Transaction.prototype.aggregateNonErrorTxn = function (txn) { this._responseTime += txn.getRt(); this._count += txn.getCount(); if (this._minRt === 0 || txn.getMinRt() < this._minRt) { this._minRt = txn.getMinRt(); } if (this._maxRt === 0 || this._maxRt < txn.getMaxRt()) { this._maxRt = txn.getMaxRt(); } }; Transaction.prototype.aggregateTxnSubResources = function (txn) { this._dbCalls = this._dbCalls.concat(txn.getDbCalls()); aggregateComponents(this._intCompInfo, txn.getInternalCompInfo()); aggregateComponents(this._extCompInfo, txn.getExtCompInfo()); this.aggregateErrors(txn.getErrorsInfo()); }; Transaction.prototype.aggregateComponent = function (component) { if (!component) { return; } if (component.isExt() > 0) { var compNameInfo = component.getName() + component.getHost() + component.getPort(); checkAndAggregateComp(this._extCompInfo, component, compNameInfo); this._extCallCount += component.getCount() + component.getErrorCount(); } else { checkAndAggregateComp( this._intCompInfo, component, component.getName() ); } }; function checkAndAggregateComp(collectedComp, newComp, compNameIndex) { var compInfo = collectedComp[compNameIndex]; if (compInfo) { collectedComp[compNameIndex].aggregate(newComp); } else { collectedComp[compNameIndex] = newComp; } } Transaction.prototype.checkAndAddError = function (errName) { if (utils.isEmpty(errName)) { return; } if (this._errInfo[errName]) { this._errInfo[errName] += 1; } else { this._errInfo[errName] = 1; } }; function aggregateComponents(baseCompInfo, newCompInfo) { var allUniqueComp = Object.keys(newCompInfo); if (allUniqueComp.length == 0) { return baseCompInfo; } allUniqueComp.forEach(function (eachUniqueComp) { var comp = baseCompInfo[eachUniqueComp]; if (comp) { var combineComp = newCompInfo[eachUniqueComp]; baseCompInfo[eachUniqueComp] = comp.aggregateAsNewComponent(combineComp); return; } baseCompInfo[eachUniqueComp] = newCompInfo[eachUniqueComp]; }); } Transaction.prototype.aggregateErrors = function (errorInfo) { var allUniqueError = Object.keys(errorInfo); var curTxn = this; allUniqueError.forEach(function (eachUniqueError) { if (curTxn._errInfo[eachUniqueError]) { curTxn._errInfo[eachUniqueError] += errorInfo[eachUniqueError]; return; } curTxn._errInfo[eachUniqueError] = errorInfo[eachUniqueError]; }); }; Transaction.prototype.getComponentDetails = function () { var components = []; var intCompInfo = this._intCompInfo; var extCompInfo = this._extCompInfo; Object.keys(intCompInfo).forEach(function (eachComp) { components.push(intCompInfo[eachComp].getInfoAsObj()); }); Object.keys(extCompInfo).forEach(function (eachComp) { components.push(extCompInfo[eachComp].getInfoAsObj()); }); return components; }; Transaction.prototype.getErrorDetails = function () { var logmetric = {}; var warningCount = 0, fatalCount = 0; var errorInfo = this._errInfo; Object.keys(errorInfo).forEach(function (eachError) { var errorCount = errorInfo[eachError]; logmetric[eachError] = errorCount; fatalCount += errorCount; }); logmetric.warning = warningCount; logmetric.fatal = fatalCount; return logmetric; }; Transaction.prototype.appendCompInfoInTrace = function (traceInfo) { traceInfo.ext_components = extractCompDetailsForTrace(this._extCompInfo); traceInfo.int_components = extractCompDetailsForTrace(this._intCompInfo); }; function extractCompDetailsForTrace(allCompInfo) { var allComp = Object.keys(allCompInfo); var compDetails = { success: {}, fail: {} }; if (allComp.length <= 0) { return compDetails; } allComp.forEach(function (eachComp) { var compInfo = allCompInfo[eachComp]; if ( compDetails.success[compInfo.getName()] || compDetails.fail[compInfo.getName()] ) { compDetails.success[compInfo.getName()] += allCompInfo[eachComp].getCount(); compDetails.fail[compInfo.getName()] += allCompInfo[eachComp].getErrorCount(); } else { compDetails.success[compInfo.getName()] = allCompInfo[eachComp].getCount(); compDetails.fail[compInfo.getName()] = allCompInfo[eachComp].getErrorCount(); } }); return compDetails; } Transaction.prototype.checkAndupdateInDataStore = function ( txnMetric, txnType, samplingFac, metricLimit ) { var txnExist = false; var matchedTxn; if (txnMetric[this.getUrl()]) { txnExist = true; matchedTxn = txnMetric[this.getUrl()]; } else { matchedTxn = new txnType({}); } if (matchedTxn.incrementAndGetOcurrence() % samplingFac !== 0) { logger.critical("txn dropped due to sampling factor"); return; } if (txnExist || Object.keys(txnMetric).length <= metricLimit) { var dbCallCount = metricstore.updateAndGetDbCount( this.getDbCalls().length ); if ( dbCallCount > utils.getGenericThreshold(this.getUrl()).getDbMetricSize() ) { this.clearDbCalls(); } matchedTxn.aggregate(this); txnMetric[this.getUrl()] = matchedTxn; } metricstore.checkAndIncludeInTrace(this); }; Transaction.prototype.isTxnIgnored = function (resCode) { let shouldIgnore = this._ignore; // Default to current ignore status try { if (this.isCompleted()) { logger.critical("End txn called again", this.getUrl()); return true; } // If no response code is provided, return the current ignore status or if already ignored if (shouldIgnore || utils.isEmpty(resCode)) { return shouldIgnore; } // Convert resCode to string safely const resCodeStr = String(resCode); // Get include list let httpErrorCodesIncludeList = apmInsightAgentInstance .getThreshold() .getHttpErrorCodesTrackList(); if (httpErrorCodesIncludeList.length == 0) { httpErrorCodesIncludeList = apmInsightAgentInstance.getConfig().getHttpErrorCodesTrack(); } // Check include list logic if (httpErrorCodesIncludeList.length > 0) { const isValidResponse = httpErrorCodesIncludeList.includes(resCodeStr) || (resCode >= 200 && resCode < 300); this._ignore = shouldIgnore = !isValidResponse; // Update shouldIgnore based on validation return shouldIgnore; } // Check ignore list if include list approach isn't used const httpErrorCodesIgnoreList = apmInsightAgentInstance .getThreshold() .getHttpErrorCodesIgnoreList(); if (httpErrorCodesIgnoreList.includes(resCodeStr)) { logger.critical( `${this.getUrl()} (${resCode}) transaction ignored. This response code is configured in ignore http error code list: ${httpErrorCodesIgnoreList}` ); shouldIgnore = true; } } catch (err) { logger.error( `Error while evaluating transaction ignore status for ${this.getUrl() || 'unknown'}: ${err}` ); // We'll return the current value of shouldIgnore via the finally block } finally { return shouldIgnore; } }; Transaction.prototype.setTransactionName = function (name) { this._url = name; }; Transaction.prototype.setRumTraceId = function (traceId) { this._rumTraceId = traceId; }; Transaction.prototype.getRumTraceId = function () { return this._rumTraceId; }; Transaction.prototype.setCustomParams = function (key, value) { var cusParams = this._customParams; if (cusParams) { var values = cusParams[key]; if (utils.isEmpty(values)) { if (Object.keys(cusParams).length < 10) { values = []; values.push(value); cusParams[key] = values; } } else if (values.length < 10) { values.push(value); } } }; Transaction.prototype.endRootTracker = function (err) { this._rootTracker.endTracker(this, err); }; Transaction.prototype.getRootTracker = function () { return this._rootTracker; }; Transaction.prototype.getDbCalls = function () { return this._dbCalls; }; Transaction.prototype.getInternalCompInfo = function () { return this._intCompInfo; }; Transaction.prototype.getExtCompInfo = function () { return this._extCompInfo; }; Transaction.prototype.getErrorsInfo = function () { return this._errInfo; }; Transaction.prototype.addDbCall = function (dbTracker) { this._dbCalls.push(dbTracker); this.incrSqlTrackersCount(); }; Transaction.prototype.clearDbCalls = function () { this._dbCalls = []; }; Transaction.prototype.isCompleted = function () { return this._completed; }; Transaction.prototype.isErrorTxn = function () { return this._errorCount > 0; }; Transaction.prototype.markCompleted = function () { this._completed = true; }; Transaction.prototype.getUrl = function () { return this._url; }; Transaction.prototype.getStartTime = function () { return this._startTime; }; Transaction.prototype.getRt = function () { return this._responseTime; }; Transaction.prototype.getErrorRt = function () { return this._errorRt; }; Transaction.prototype.getError = function () { return this._error; }; Transaction.prototype.getMinRt = function () { return this._minRt; }; Transaction.prototype.getMaxRt = function () { return this._maxRt; }; Transaction.prototype.getCount = function () { return this._count; }; Transaction.prototype.getErrorCount = function () { return this._errorCount; }; Transaction.prototype.getExtCallCount = function () { return this._extCallCount; }; Transaction.prototype.getCustomParams = function () { return this._customParams; }; Transaction.prototype.getCustomParamsAsStr = function () { var cusParams = this.getCustomParams(); Object.keys(cusParams).forEach(function (eachKey) { var eachValue = cusParams[eachKey]; for (var index = 0; index < eachValue.length; index++) { var valueAtIndex = eachValue[index]; try { if (utils.isObject(valueAtIndex)) { eachValue[index] = JSON.stringify(valueAtIndex); } else { eachValue[index] = String(valueAtIndex); } } catch (err) { logger.error( "error while converting the value " + valueAtIndex + " to string" ); } } }); return cusParams; }; Transaction.prototype.incrementAndGetOcurrence = function () { this.incrementOcurrence(); return this._ocurrence; }; Transaction.prototype.incrementOcurrence = function () { this._ocurrence += 1; }; Transaction.prototype.isCreatedByApi = function () { return this._byApi; }; Transaction.prototype.setIgnore = function () { this._ignore = true; logger.info(this.getUrl() + " txn ignored by api"); }; Transaction.prototype.getTrackerCountInfo = function () { return { method_trackers_count: this.getMethdCallsCount(), sql_trackers_count: this.getSqlTrackersCount(), ext_trackers_count: this.getExtTrackersCountExcludingSql() }; }; Transaction.prototype.getMethdCallsCount = function () { var internalCount = this._trackersCount - this._extCallCount; return internalCount < 0 ? 0 : internalCount; }; Transaction.prototype.getExtTrackersCountExcludingSql = function () { var count = this._extCallCount - this._sqlTrackersCount; return count < 0 ? 0 : count; }; Transaction.prototype.getSqlTrackersCount = function () { return this._sqlTrackersCount; }; Transaction.prototype.incrSqlTrackersCount = function () { this._sqlTrackersCount++; }; Transaction.prototype.incrTrackersCount = function () { this._trackersCount++; }; module.exports = Transaction;