From 0b9dff671412116ecf7258556009aecb80769316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=BCnyamin=20Sar=C4=B1g=C3=BCl?= Date: Thu, 4 Jun 2020 17:34:44 +0300 Subject: [PATCH] separate lambda related application info logic from index --- src/ThundraWrapper.ts | 4 +- src/application/ApplicationInfo.ts | 11 +++ src/application/ApplicationInfoProvider.ts | 5 ++ src/application/ApplicationManager.ts | 20 +++++ .../LambdaApplicationInfoProvider.ts | 32 ++++++++ src/application/LambdaContextProvider.ts | 11 +++ src/index.ts | 44 +++++----- src/plugins/Invocation.ts | 3 +- src/plugins/support/ApplicationSupport.ts | 32 -------- src/plugins/utils/LambdaUtils.ts | 60 ++++++++++++++ src/plugins/utils/Utils.ts | 81 ++++++------------- 11 files changed, 190 insertions(+), 113 deletions(-) create mode 100644 src/application/ApplicationInfo.ts create mode 100644 src/application/ApplicationInfoProvider.ts create mode 100644 src/application/ApplicationManager.ts create mode 100644 src/application/LambdaApplicationInfoProvider.ts create mode 100644 src/application/LambdaContextProvider.ts delete mode 100644 src/plugins/support/ApplicationSupport.ts create mode 100644 src/plugins/utils/LambdaUtils.ts diff --git a/src/ThundraWrapper.ts b/src/ThundraWrapper.ts index 9a9e82bc..b8ee852c 100644 --- a/src/ThundraWrapper.ts +++ b/src/ThundraWrapper.ts @@ -404,7 +404,7 @@ class ThundraWrapper { ); } - async executeAfteInvocationAndReport(afterInvocationData: any) { + async executeAfterInvocationAndReport(afterInvocationData: any) { if (this.monitoringDisabled) { return; } @@ -442,7 +442,7 @@ class ThundraWrapper { }; } - await this.executeAfteInvocationAndReport(afterInvocationData); + await this.executeAfterInvocationAndReport(afterInvocationData); if (this.timeout) { clearTimeout(this.timeout); diff --git a/src/application/ApplicationInfo.ts b/src/application/ApplicationInfo.ts new file mode 100644 index 00000000..6b09f57c --- /dev/null +++ b/src/application/ApplicationInfo.ts @@ -0,0 +1,11 @@ +import ConfigProvider from '../config/ConfigProvider'; +import ConfigNames from '../config/ConfigNames'; +import ThundraLogger from '../ThundraLogger'; + +export interface ApplicationInfo { + applicationId: string; + applicationInstanceId: string; + applicationRegion: string; + applicationVersion: string; + applicationTags: any; +} diff --git a/src/application/ApplicationInfoProvider.ts b/src/application/ApplicationInfoProvider.ts new file mode 100644 index 00000000..974173a0 --- /dev/null +++ b/src/application/ApplicationInfoProvider.ts @@ -0,0 +1,5 @@ +import {ApplicationInfo} from './ApplicationInfo'; + +export interface ApplicationInfoProvider { + getApplicationInfo: () => ApplicationInfo; +} diff --git a/src/application/ApplicationManager.ts b/src/application/ApplicationManager.ts new file mode 100644 index 00000000..b406c18f --- /dev/null +++ b/src/application/ApplicationManager.ts @@ -0,0 +1,20 @@ +import {ApplicationInfoProvider} from './ApplicationInfoProvider'; +import {LambdaApplicationInfoProvider} from './LambdaApplicationInfoProvider'; +import {ApplicationInfo} from './ApplicationInfo'; + +export class ApplicationManager { + + static applicationInfoProvider: ApplicationInfoProvider; + + static setApplicationInfoProvider(applicationInfoProvider: ApplicationInfoProvider) { + ApplicationManager.applicationInfoProvider = applicationInfoProvider; + } + + static getApplicationInfoProvider(): ApplicationInfoProvider { + return ApplicationManager.applicationInfoProvider; + } + + static getApplicationInfo(): ApplicationInfo { + return ApplicationManager.applicationInfoProvider.getApplicationInfo(); + } +} diff --git a/src/application/LambdaApplicationInfoProvider.ts b/src/application/LambdaApplicationInfoProvider.ts new file mode 100644 index 00000000..fc614753 --- /dev/null +++ b/src/application/LambdaApplicationInfoProvider.ts @@ -0,0 +1,32 @@ +import Utils from '../plugins/utils/Utils'; +import {EnvVariableKeys} from '../Constants'; +import {ApplicationInfoProvider} from './ApplicationInfoProvider'; +import {ApplicationInfo} from './ApplicationInfo'; +import {LambdaContextProvider} from './LambdaContextProvider'; +import {LambdaUtils} from '../plugins/utils/LambdaUtils'; + +export class LambdaApplicationInfoProvider implements ApplicationInfoProvider { + + private applicationInfo: ApplicationInfo; + + constructor() { + const logStreamName = Utils.getEnvVar(EnvVariableKeys.AWS_LAMBDA_LOG_STREAM_NAME); + const region = Utils.getEnvVar(EnvVariableKeys.AWS_REGION); + const functionVersion = Utils.getEnvVar(EnvVariableKeys.AWS_LAMBDA_FUNCTION_VERSION); + this.applicationInfo = { + applicationId: undefined, + applicationInstanceId: logStreamName ? logStreamName.split(']').pop() : Utils.generateId(), + applicationRegion: region ? region : '', + applicationVersion: functionVersion ? functionVersion : '', + applicationTags: Utils.getApplicationTags(), + }; + } + + getApplicationInfo(): ApplicationInfo { + const lambdaContext = LambdaContextProvider.getContext(); + if (!this.applicationInfo.applicationId && lambdaContext) { + this.applicationInfo.applicationId = LambdaUtils.getApplicationId(lambdaContext); + } + return this.applicationInfo; + } +} diff --git a/src/application/LambdaContextProvider.ts b/src/application/LambdaContextProvider.ts new file mode 100644 index 00000000..9326e0ec --- /dev/null +++ b/src/application/LambdaContextProvider.ts @@ -0,0 +1,11 @@ +export class LambdaContextProvider { + static context: any; + + static setContext(context: any) { + LambdaContextProvider.context = context; + } + + static getContext() { + return LambdaContextProvider.context; + } +} diff --git a/src/index.ts b/src/index.ts index 30dcdf7c..15b87cbd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,6 @@ import Logger from './plugins/Logger'; import Log from './plugins/Log'; import InvocationSupport from './plugins/support/InvocationSupport'; import InvocationTraceSupport from './plugins/support/InvocationTraceSupport'; -import ApplicationSupport from './plugins/support/ApplicationSupport'; import ErrorInjectorSpanListener from './plugins/listeners/ErrorInjectorSpanListener'; import FilteringSpanListener from './plugins/listeners/FilteringSpanListener'; import LatencyInjectorSpanListener from './plugins/listeners/LatencyInjectorSpanListener'; @@ -33,6 +32,9 @@ import ErrorAwareSampler from './opentracing/sampler/ErrorAwareSampler'; import TimeAwareSampler from './opentracing/sampler/TimeAwareSampler'; import { SamplerCompositionOperator } from './opentracing/sampler/CompositeSampler'; import ConfigNames from './config/ConfigNames'; +import {ApplicationManager} from './application/ApplicationManager'; +import {LambdaContextProvider} from './application/LambdaContextProvider'; +import {LambdaApplicationInfoProvider} from './application/LambdaApplicationInfoProvider'; const ThundraWarmup = require('@thundra/warmup'); const get = require('lodash.get'); @@ -90,36 +92,32 @@ module.exports = (options?: any) => { process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; } - ApplicationSupport.parseApplicationTags(); - - const logStreamName = Utils.getEnvVar(EnvVariableKeys.AWS_LAMBDA_LOG_STREAM_NAME); - const region = Utils.getEnvVar(EnvVariableKeys.AWS_REGION); - const functionVersion = Utils.getEnvVar(EnvVariableKeys.AWS_LAMBDA_FUNCTION_VERSION); - const applicationInstanceId = logStreamName ? logStreamName.split(']').pop() : Utils.generateId(); - - const pluginContext: PluginContext = new PluginContext({ - applicationInstanceId, - applicationRegion: region ? region : '', - applicationVersion: functionVersion ? functionVersion : '', - requestCount: 0, - apiKey: config.apiKey, - timeoutMargin: config.timeoutMargin, - transactionId: null, - config, - }); - - const increaseRequestCount = () => pluginContext.requestCount += 1; - return (originalFunc: any) => { // Check if already wrapped if (get(originalFunc, 'thundraWrapped', false)) { return originalFunc; } + ApplicationManager.setApplicationInfoProvider(new LambdaApplicationInfoProvider()); + const applicationInfoProvider = ApplicationManager.getApplicationInfoProvider(); + const applicationInfo = applicationInfoProvider.getApplicationInfo(); + const pluginContext: PluginContext = new PluginContext({ + applicationInstanceId: applicationInfo.applicationInstanceId, + applicationRegion: applicationInfo.applicationRegion, + applicationVersion: applicationInfo.applicationVersion, + requestCount: 0, + apiKey: config.apiKey, + timeoutMargin: config.timeoutMargin, + transactionId: null, + config, + }); + + const increaseRequestCount = () => pluginContext.requestCount += 1; + const thundraWrappedHandler: any = async (originalEvent: any, originalContext: any, originalCallback: any) => { + LambdaContextProvider.setContext(originalContext); // Creating applicationId here, since we need the information in context - const applicationId = Utils.getApplicationId(originalContext, pluginContext); - pluginContext.applicationId = applicationId; + pluginContext.applicationId = ApplicationManager.getApplicationInfo().applicationId; plugins.forEach((plugin: any) => { plugin.setPluginContext(pluginContext); diff --git a/src/plugins/Invocation.ts b/src/plugins/Invocation.ts index 7898a536..cb030aed 100644 --- a/src/plugins/Invocation.ts +++ b/src/plugins/Invocation.ts @@ -8,6 +8,7 @@ import MonitoringDataType from './data/base/MonitoringDataType'; import PluginContext from './PluginContext'; import InvocationSupport from './support/InvocationSupport'; import InvocationTraceSupport from './support/InvocationTraceSupport'; +import {LambdaUtils} from './utils/LambdaUtils'; class Invocation { hooks: { 'before-invocation': (data: any) => void; 'after-invocation': (data: any) => void; }; @@ -78,7 +79,7 @@ class Invocation { this.invocationData.tags['aws.lambda.memory_limit'] = this.pluginContext.maxMemory; this.invocationData.tags['aws.lambda.arn'] = originalContext.invokedFunctionArn; - this.invocationData.tags['aws.account_no'] = Utils.getAWSAccountNo(originalContext.invokedFunctionArn); + this.invocationData.tags['aws.account_no'] = LambdaUtils.getAWSAccountNo(originalContext.invokedFunctionArn); this.invocationData.tags['aws.lambda.invocation.coldstart'] = this.pluginContext.requestCount === 0; this.invocationData.tags['aws.region'] = this.pluginContext.applicationRegion; this.invocationData.tags['aws.lambda.log_group_name'] = originalContext ? originalContext.logGroupName : ''; diff --git a/src/plugins/support/ApplicationSupport.ts b/src/plugins/support/ApplicationSupport.ts deleted file mode 100644 index 50f84ed0..00000000 --- a/src/plugins/support/ApplicationSupport.ts +++ /dev/null @@ -1,32 +0,0 @@ -import ThundraLogger from '../../ThundraLogger'; -import ConfigNames from '../../config/ConfigNames'; -import ConfigProvider from '../../config/ConfigProvider'; - -class ApplicationSupport { - static applicationTags: any = {}; - - static parseApplicationTags(): void { - ApplicationSupport.applicationTags = {}; - for (const key of ConfigProvider.names()) { - if (key.startsWith(ConfigNames.THUNDRA_APPLICATION_TAG_PREFIX)) { - try { - const propsKey = key.substring(ConfigNames.THUNDRA_APPLICATION_TAG_PREFIX.length); - const propsValue = ConfigProvider.get(key); - if (isNaN(parseFloat(propsValue))) { - if (propsValue === 'true' || propsValue === 'false') { - ApplicationSupport.applicationTags[propsKey] = propsValue === 'true' ? true : false; - } else { - ApplicationSupport.applicationTags[propsKey] = propsValue; - } - } else { - ApplicationSupport.applicationTags[propsKey] = parseFloat(propsValue); - } - } catch (ex) { - ThundraLogger.getInstance().error(`Cannot parse application tag ${key}`); - } - } - } - } -} - -export default ApplicationSupport; diff --git a/src/plugins/utils/LambdaUtils.ts b/src/plugins/utils/LambdaUtils.ts new file mode 100644 index 00000000..401ac305 --- /dev/null +++ b/src/plugins/utils/LambdaUtils.ts @@ -0,0 +1,60 @@ +import {EnvVariableKeys} from '../../Constants'; +import ConfigProvider from '../../config/ConfigProvider'; +import ConfigNames from '../../config/ConfigNames'; +import Utils from './Utils'; + +export class LambdaUtils { + + static getApplicationId(originalContext: any) { + const arn = originalContext.invokedFunctionArn; + const region = Utils.getEnvVar(EnvVariableKeys.AWS_REGION) + || 'local'; + const accountNo = LambdaUtils.getAccountNo(arn, ConfigProvider.get(ConfigNames.THUNDRA_APIKEY)); + const functionName = LambdaUtils.getApplicationName(originalContext); + + return `aws:lambda:${region}:${accountNo}:${functionName}`; + } + + static getApplicationName(originalContext: any) { + return ConfigProvider.get(ConfigNames.THUNDRA_APPLICATION_NAME, + originalContext.functionName + || Utils.getEnvVar(EnvVariableKeys.AWS_LAMBDA_FUNCTION_NAME) + || 'lambda-app'); + } + + static getAccountNo(arn: string, apiKey: string) { + if (LambdaUtils.getIfSAMLocalDebugging()) { + return 'sam_local'; + } else if (LambdaUtils.getIfSLSLocalDebugging()) { + return 'sls_local'; + } else { + return (LambdaUtils.getAWSAccountNo(arn) + || apiKey + || 'guest'); + } + } + + static getAWSAccountNo(arn: string) { + return LambdaUtils.getARNPart(arn, 4); + } + + static getAWSRegion(arn: string) { + return LambdaUtils.getARNPart(arn, 3); + } + + static getARNPart(arn: string, index: number) { + try { + return arn.split(':')[index]; + } catch (error) { + return ''; + } + } + + static getIfSAMLocalDebugging() { + return Utils.getEnvVar(EnvVariableKeys.AWS_SAM_LOCAL) === 'true'; + } + + static getIfSLSLocalDebugging() { + return Utils.getEnvVar(EnvVariableKeys.SLS_LOCAL) === 'true'; + } +} diff --git a/src/plugins/utils/Utils.ts b/src/plugins/utils/Utils.ts index 4fb59ae3..a2517cdb 100644 --- a/src/plugins/utils/Utils.ts +++ b/src/plugins/utils/Utils.ts @@ -18,11 +18,11 @@ import MetricData from '../data/metric/MetricData'; import SpanData from '../data/trace/SpanData'; import LogData from '../data/log/LogData'; import ThundraLogger from '../../ThundraLogger'; -import ApplicationSupport from '../support/ApplicationSupport'; import ThundraTracer from '../../opentracing/Tracer'; import CompositeMonitoringData from '../data/composite/CompositeMonitoringData'; import InvocationSupport from '../support/InvocationSupport'; import ModuleVersionValidator from '../integrations/ModuleVersionValidator'; +import {ApplicationManager} from '../../application/ApplicationManager'; const parse = require('module-details-from-path'); const uuidv4 = require('uuid/v4'); @@ -381,7 +381,8 @@ class Utils { monitoringData.applicationVersion = applicationVersion; monitoringData.applicationRuntimeVersion = process.version; - monitoringData.applicationTags = {...monitoringData.applicationTags, ...ApplicationSupport.applicationTags}; + monitoringData.applicationTags = {...monitoringData.applicationTags, + ...ApplicationManager.getApplicationInfo().applicationTags}; return monitoringData; } @@ -489,59 +490,6 @@ class Utils { return monitoringData; } - static getAWSAccountNo(arn: string) { - return Utils.getARNPart(arn, 4); - } - - static getAWSRegion(arn: string) { - return Utils.getARNPart(arn, 3); - } - - static getAccountNo(arn: string, pluginContext: any) { - if (Utils.getIfSAMLocalDebugging()) { - return 'sam_local'; - } else if (Utils.getIfSLSLocalDebugging()) { - return 'sls_local'; - } else { - return (Utils.getAWSAccountNo(arn) - || pluginContext.apiKey - || 'guest'); - } - } - - static getApplicationId(originalContext: any, pluginContext: any) { - const arn = originalContext.invokedFunctionArn; - const region = Utils.getEnvVar(EnvVariableKeys.AWS_REGION) - || 'local'; - const accountNo = Utils.getAccountNo(arn, pluginContext); - const functionName = Utils.getApplicationName(originalContext); - - return `aws:lambda:${region}:${accountNo}:${functionName}`; - } - - static getApplicationName(originalContext: any) { - return ConfigProvider.get(ConfigNames.THUNDRA_APPLICATION_NAME, - originalContext.functionName - || Utils.getEnvVar(EnvVariableKeys.AWS_LAMBDA_FUNCTION_NAME) - || 'lambda-app'); - } - - static getARNPart(arn: string, index: number) { - try { - return arn.split(':')[index]; - } catch (error) { - return ''; - } - } - - static getIfSAMLocalDebugging() { - return Utils.getEnvVar(EnvVariableKeys.AWS_SAM_LOCAL) === 'true'; - } - - static getIfSLSLocalDebugging() { - return Utils.getEnvVar(EnvVariableKeys.SLS_LOCAL) === 'true'; - } - static getXRayTraceInfo() { let traceID: string = ''; let segmentID: string = ''; @@ -593,6 +541,29 @@ class Utils { return response.statusCode && typeof response.statusCode === 'number'; } + static getApplicationTags(): any { + const applicationTags: any = {}; + for (const key of ConfigProvider.names()) { + if (key.startsWith(ConfigNames.THUNDRA_APPLICATION_TAG_PREFIX)) { + try { + const propsKey = key.substring(ConfigNames.THUNDRA_APPLICATION_TAG_PREFIX.length); + const propsValue = ConfigProvider.get(key); + if (isNaN(parseFloat(propsValue))) { + if (propsValue === 'true' || propsValue === 'false') { + applicationTags[propsKey] = propsValue === 'true' ? true : false; + } else { + applicationTags[propsKey] = propsValue; + } + } else { + applicationTags[propsKey] = parseFloat(propsValue); + } + } catch (ex) { + ThundraLogger.getInstance().error(`Cannot parse application tag ${key}`); + } + } + } + return applicationTags; + } } export default Utils;