aws-embedded-metrics
Advanced tools
Comparing version 1.0.1 to 1.1.0-rc1
import { IConfiguration } from './IConfiguration'; | ||
import Environments from '../environment/Environments'; | ||
export declare class EnvironmentConfigurationProvider { | ||
@@ -7,2 +8,3 @@ getConfiguration(): IConfiguration; | ||
private tryGetEnvVariableAsBoolean; | ||
getEnvironmentOverride(): Environments; | ||
} |
@@ -17,2 +17,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Environments_1 = require("../environment/Environments"); | ||
const ENV_VAR_PREFIX = 'AWS_EMF'; | ||
@@ -27,2 +28,3 @@ var ConfigKeys; | ||
ConfigKeys["AGENT_ENDPOINT"] = "AGENT_ENDPOINT"; | ||
ConfigKeys["ENVIRONMENT_OVERRIDE"] = "ENVIRONMENT"; | ||
})(ConfigKeys || (ConfigKeys = {})); | ||
@@ -38,2 +40,3 @@ class EnvironmentConfigurationProvider { | ||
serviceType: this.getEnvVariable(ConfigKeys.SERVICE_TYPE) || this.getEnvVariableWithoutPrefix(ConfigKeys.SERVICE_TYPE), | ||
environmentOverride: this.getEnvironmentOverride(), | ||
}; | ||
@@ -51,3 +54,11 @@ } | ||
} | ||
getEnvironmentOverride() { | ||
const overrideValue = this.getEnvVariable(ConfigKeys.ENVIRONMENT_OVERRIDE); | ||
const environment = Environments_1.default[overrideValue]; | ||
if (environment === undefined) { | ||
return Environments_1.default.Unknown; | ||
} | ||
return environment; | ||
} | ||
} | ||
exports.EnvironmentConfigurationProvider = EnvironmentConfigurationProvider; |
@@ -0,1 +1,2 @@ | ||
import Environments from '../environment/Environments'; | ||
export interface IConfiguration { | ||
@@ -28,2 +29,11 @@ /** | ||
agentEndpoint: string | undefined; | ||
/** | ||
* Environment override. This will short circuit auto-environment detection. | ||
* Valid values include: | ||
* - Local: no decoration and sends over stdout | ||
* - Lambda: decorates logs with Lambda metadata and sends over stdout | ||
* - Agent: no decoration and sends over TCP | ||
* - EC2: decorates logs with EC2 metadata and sends over TCP | ||
*/ | ||
environmentOverride: Environments | undefined; | ||
} |
export declare enum Constants { | ||
MAX_DIMENSIONS = 10, | ||
DEFAULT_NAMESPACE = "aws-embedded-metrics" | ||
MAX_DIMENSIONS = 9, | ||
DEFAULT_NAMESPACE = "aws-embedded-metrics", | ||
MAX_METRICS_PER_EVENT = 100 | ||
} |
@@ -19,4 +19,5 @@ "use strict"; | ||
(function (Constants) { | ||
Constants[Constants["MAX_DIMENSIONS"] = 10] = "MAX_DIMENSIONS"; | ||
Constants[Constants["MAX_DIMENSIONS"] = 9] = "MAX_DIMENSIONS"; | ||
Constants["DEFAULT_NAMESPACE"] = "aws-embedded-metrics"; | ||
Constants[Constants["MAX_METRICS_PER_EVENT"] = 100] = "MAX_METRICS_PER_EVENT"; | ||
})(Constants = exports.Constants || (exports.Constants = {})); |
@@ -39,2 +39,8 @@ "use strict"; | ||
getLogGroupName() { | ||
// if the caller explicitly overrides logGroupName to | ||
// be empty, we should honor that rather than providing | ||
// the default behavior. | ||
if (Configuration_1.default.logGroupName === '') { | ||
return ''; | ||
} | ||
return Configuration_1.default.logGroupName ? Configuration_1.default.logGroupName : `${this.getName()}-metrics`; | ||
@@ -41,0 +47,0 @@ } |
import { IEnvironment } from './IEnvironment'; | ||
declare type EnvironmentProvider = () => Promise<IEnvironment>; | ||
declare const resolveEnvironment: EnvironmentProvider; | ||
declare const resetEnvironment: () => void; | ||
export { EnvironmentProvider, resolveEnvironment, resetEnvironment }; | ||
declare const cleanResolveEnvironment: () => Promise<IEnvironment>; | ||
export { EnvironmentProvider, resolveEnvironment, cleanResolveEnvironment }; |
@@ -29,14 +29,34 @@ "use strict"; | ||
const LambdaEnvironment_1 = require("./LambdaEnvironment"); | ||
const environments = [new LambdaEnvironment_1.LambdaEnvironment(), new EC2Environment_1.EC2Environment()]; | ||
const Configuration_1 = require("../config/Configuration"); | ||
const Environments_1 = require("./Environments"); | ||
const LocalEnvironment_1 = require("./LocalEnvironment"); | ||
const lambdaEnvironment = new LambdaEnvironment_1.LambdaEnvironment(); | ||
const ec2Environment = new EC2Environment_1.EC2Environment(); | ||
const defaultEnvironment = new DefaultEnvironment_1.DefaultEnvironment(); | ||
let environment; | ||
const resolveEnvironment = () => __awaiter(this, void 0, void 0, function* () { | ||
if (environment) { | ||
return environment; | ||
const environments = [lambdaEnvironment, ec2Environment]; | ||
let environment = undefined; | ||
const getEnvironmentFromOverride = () => { | ||
// short-circuit environment detection and use override | ||
switch (Configuration_1.default.environmentOverride) { | ||
case Environments_1.default.Agent: | ||
return defaultEnvironment; | ||
case Environments_1.default.EC2: | ||
return ec2Environment; | ||
case Environments_1.default.Lambda: | ||
return lambdaEnvironment; | ||
case Environments_1.default.Local: | ||
return new LocalEnvironment_1.LocalEnvironment(); | ||
case Environments_1.default.Unknown: | ||
default: | ||
return undefined; | ||
} | ||
}; | ||
const discoverEnvironment = () => __awaiter(this, void 0, void 0, function* () { | ||
Logger_1.LOG(`Discovering environment`); | ||
for (const envUnderTest of environments) { | ||
Logger_1.LOG(`Testing: ${envUnderTest.constructor.name}`); | ||
let isEnvironment = false; | ||
try { | ||
isEnvironment = yield envUnderTest.probe(); | ||
if (yield envUnderTest.probe()) { | ||
return envUnderTest; | ||
} | ||
} | ||
@@ -46,16 +66,24 @@ catch (e) { | ||
} | ||
if (isEnvironment) { | ||
environment = envUnderTest; | ||
break; | ||
} | ||
return defaultEnvironment; | ||
}); | ||
const _resolveEnvironment = () => __awaiter(this, void 0, void 0, function* () { | ||
Logger_1.LOG('Resolving environment'); | ||
if (environment) { | ||
return environment; | ||
} | ||
if (Configuration_1.default.environmentOverride) { | ||
Logger_1.LOG('Environment override supplied', Configuration_1.default.environmentOverride); | ||
// this will be falsy if an invalid configuration value is provided | ||
environment = getEnvironmentFromOverride(); | ||
if (environment) { | ||
return environment; | ||
} | ||
else { | ||
Logger_1.LOG('Invalid environment provided. Falling back to auto-discovery.', Configuration_1.default.environmentOverride); | ||
} | ||
} | ||
if (!environment) { | ||
environment = defaultEnvironment; | ||
} | ||
Logger_1.LOG(`Using Environment: ${environment.constructor.name}`); | ||
environment = yield discoverEnvironment(); // eslint-disable-line require-atomic-updates | ||
return environment; | ||
}); | ||
exports.resolveEnvironment = resolveEnvironment; | ||
const resetEnvironment = () => (environment = undefined); | ||
exports.resetEnvironment = resetEnvironment; | ||
// pro-actively begin resolving the environment | ||
@@ -65,2 +93,11 @@ // this will allow us to kick off any async tasks | ||
// may occur on the initial flush() | ||
resolveEnvironment(); | ||
const environmentPromise = _resolveEnvironment(); | ||
const resolveEnvironment = () => __awaiter(this, void 0, void 0, function* () { | ||
return environmentPromise; | ||
}); | ||
exports.resolveEnvironment = resolveEnvironment; | ||
const cleanResolveEnvironment = () => __awaiter(this, void 0, void 0, function* () { | ||
environment = undefined; | ||
return yield _resolveEnvironment(); | ||
}); | ||
exports.cleanResolveEnvironment = cleanResolveEnvironment; |
@@ -17,3 +17,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const LambdaSink_1 = require("../sinks/LambdaSink"); | ||
const ConsoleSink_1 = require("../sinks/ConsoleSink"); | ||
class LambdaEnvironment { | ||
@@ -44,3 +44,3 @@ probe() { | ||
if (!this.sink) { | ||
this.sink = new LambdaSink_1.LambdaSink(); | ||
this.sink = new ConsoleSink_1.ConsoleSink(); | ||
} | ||
@@ -47,0 +47,0 @@ return this.sink; |
export { MetricsLogger } from './logger/MetricsLogger'; | ||
export { LambdaSink } from './sinks/LambdaSink'; | ||
export { ConsoleSink as LocalSink } from './sinks/ConsoleSink'; | ||
export { AgentSink } from './sinks/AgentSink'; | ||
@@ -4,0 +4,0 @@ export { metricScope } from './logger/MetricScope'; |
@@ -19,4 +19,4 @@ "use strict"; | ||
exports.MetricsLogger = MetricsLogger_1.MetricsLogger; | ||
var LambdaSink_1 = require("./sinks/LambdaSink"); | ||
exports.LambdaSink = LambdaSink_1.LambdaSink; | ||
var ConsoleSink_1 = require("./sinks/ConsoleSink"); | ||
exports.LocalSink = ConsoleSink_1.ConsoleSink; | ||
var AgentSink_1 = require("./sinks/AgentSink"); | ||
@@ -23,0 +23,0 @@ exports.AgentSink = AgentSink_1.AgentSink; |
@@ -45,3 +45,3 @@ import { MetricValues } from './MetricValues'; | ||
*/ | ||
putDimensions(dimensions: Record<string, string>): void; | ||
putDimensions(incomingDimensionSet: Record<string, string>): void; | ||
/** | ||
@@ -48,0 +48,0 @@ * Overwrite all dimensions. |
@@ -69,4 +69,27 @@ "use strict"; | ||
*/ | ||
putDimensions(dimensions) { | ||
this.dimensions.push(dimensions); | ||
putDimensions(incomingDimensionSet) { | ||
if (this.dimensions.length === 0) { | ||
this.dimensions.push(incomingDimensionSet); | ||
return; | ||
} | ||
for (let i = 0; i < this.dimensions.length; i++) { | ||
const existingDimensionSet = this.dimensions[i]; | ||
// check for duplicate dimensions when putting | ||
// this is an O(n^2) operation, but since we never expect to have more than | ||
// 10 dimensions, this is acceptable for almost all cases. | ||
// This makes re-using loggers much easier. | ||
const existingDimensionSetKeys = Object.keys(existingDimensionSet); | ||
const incomingDimensionSetKeys = Object.keys(incomingDimensionSet); | ||
if (existingDimensionSetKeys.length !== incomingDimensionSetKeys.length) { | ||
this.dimensions.push(incomingDimensionSet); | ||
return; | ||
} | ||
for (let j = 0; j < existingDimensionSetKeys.length; j++) { | ||
if (!incomingDimensionSetKeys.includes(existingDimensionSetKeys[j])) { | ||
// we're done now because we know that the dimensions keys are not identical | ||
this.dimensions.push(incomingDimensionSet); | ||
return; | ||
} | ||
} | ||
} | ||
} | ||
@@ -73,0 +96,0 @@ /** |
@@ -62,3 +62,3 @@ "use strict"; | ||
sink.accept(this.context); | ||
this.context = MetricsContext_1.MetricsContext.empty(); | ||
this.context = this.context.createCopyWithContext(); | ||
}); | ||
@@ -65,0 +65,0 @@ } |
@@ -11,3 +11,3 @@ import { MetricsContext } from '../logger/MetricsContext'; | ||
*/ | ||
serialize(context: MetricsContext): string; | ||
serialize(context: MetricsContext): string[]; | ||
} |
@@ -30,3 +30,3 @@ "use strict"; | ||
context.getDimensions().forEach(d => { | ||
// we can only take the first 10 defined dimensions | ||
// we can only take the first 9 defined dimensions | ||
// the reason we do this in the serializer is because | ||
@@ -42,18 +42,35 @@ // it is possible that other sinks or formats can | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const body = Object.assign({}, dimensionProperties, context.properties, { _aws: Object.assign({}, context.meta, { CloudWatchMetrics: [ | ||
{ | ||
Dimensions: dimensionKeys, | ||
Metrics: [], | ||
Namespace: context.namespace, | ||
}, | ||
] }) }); | ||
const createBody = () => { | ||
return Object.assign({}, dimensionProperties, context.properties, { _aws: Object.assign({}, context.meta, { CloudWatchMetrics: [ | ||
{ | ||
Dimensions: dimensionKeys, | ||
Metrics: [], | ||
Namespace: context.namespace, | ||
}, | ||
] }) }); | ||
}; | ||
const eventBatches = []; | ||
let currentBody = createBody(); | ||
const currentMetricsInBody = () => currentBody._aws.CloudWatchMetrics[0].Metrics.length; | ||
const shouldSerialize = () => currentMetricsInBody() === Constants_1.Constants.MAX_METRICS_PER_EVENT; | ||
// converts the body to JSON and pushes it into the batches | ||
const serializeCurrentBody = () => { | ||
eventBatches.push(JSON.stringify(currentBody)); | ||
currentBody = createBody(); | ||
}; | ||
for (const [key, metric] of context.metrics) { | ||
// if there is only one metric value, unwrap it to make querying easier | ||
const metricValue = metric.values.length === 1 ? metric.values[0] : metric.values; | ||
body[key] = metricValue; | ||
body._aws.CloudWatchMetrics[0].Metrics.push({ Name: key, Unit: metric.unit }); | ||
currentBody[key] = metricValue; | ||
currentBody._aws.CloudWatchMetrics[0].Metrics.push({ Name: key, Unit: metric.unit }); | ||
if (shouldSerialize()) { | ||
serializeCurrentBody(); | ||
} | ||
} | ||
return JSON.stringify(body); | ||
if (eventBatches.length === 0 || currentMetricsInBody() > 0) { | ||
serializeCurrentBody(); | ||
} | ||
return eventBatches; | ||
} | ||
} | ||
exports.LogSerializer = LogSerializer; |
@@ -8,3 +8,3 @@ import { MetricsContext } from '../logger/MetricsContext'; | ||
*/ | ||
serialize(context: MetricsContext): string; | ||
serialize(context: MetricsContext): string[]; | ||
} |
@@ -80,9 +80,15 @@ "use strict"; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
context.meta.LogGroupName = this.logGroupName; | ||
if (this.logGroupName) { | ||
context.meta.LogGroupName = this.logGroupName; | ||
} | ||
if (this.logStreamName) { | ||
context.meta.LogStreamName = this.logStreamName; | ||
} | ||
const message = this.serializer.serialize(context) + '\n'; | ||
const bytes = Buffer.from(message); | ||
yield this.socketClient.sendMessage(bytes); | ||
const events = this.serializer.serialize(context); | ||
for (let index = 0; index < events.length; index++) { | ||
const event = events[index]; | ||
const message = event + '\n'; | ||
const bytes = Buffer.from(message); | ||
yield this.socketClient.sendMessage(bytes); | ||
} | ||
}); | ||
@@ -89,0 +95,0 @@ } |
@@ -54,3 +54,2 @@ "use strict"; | ||
Logger_1.LOG('Failed to write', err); | ||
Logger_1.LOG('Socket', this.socket); | ||
reject(err); | ||
@@ -57,0 +56,0 @@ }; |
{ | ||
"name": "aws-embedded-metrics", | ||
"version": "1.0.1", | ||
"version": "1.1.0-rc1", | ||
"description": "AWS Embedded Metrics Client Library", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -12,2 +12,3 @@ # aws-embedded-metrics | ||
* [API](#api) | ||
* [Configuration](#configuration) | ||
* [Examples](#examples) | ||
@@ -197,3 +198,3 @@ * [Development](#development) | ||
### Configuration | ||
## Configuration | ||
@@ -273,2 +274,48 @@ All configuration values can be set using environment variables with the prefix (`AWS_EMF_`). Configuration should be performed as close to application start up as possible. | ||
**AgentEndpoint**: For agent-based platforms, you may optionally configure the endpoint to reach the agent on. | ||
Example: | ||
```js | ||
// in process | ||
const { Configuration } = require("aws-embedded-metrics"); | ||
Configuration.agentEndpoint = "udp://127.0.0.1:1000"; | ||
// environment | ||
AWS_EMF_AGENT_ENDPOINT="udp://127.0.0.1:1000" | ||
``` | ||
**EnvironmentOverride**: Short circuit auto-environment detection by explicitly defining how events should be sent. | ||
Valid values include: | ||
- Local: no decoration and sends over stdout | ||
- Lambda: decorates logs with Lambda metadata and sends over stdout | ||
- Agent: no decoration and sends over TCP | ||
- EC2: decorates logs with EC2 metadata and sends over TCP | ||
Example: | ||
```js | ||
// in process | ||
const { Configuration } = require("aws-embedded-metrics"); | ||
Configuration.environmentOverride = "Local"; | ||
// environment | ||
AWS_EMF_ENVIRONMENT=Local | ||
``` | ||
**EnableDebugLogging**: Enable debug logging for the library. If the library is not behaving as expected, you can set this to true to log to console. | ||
Example: | ||
```js | ||
// in process | ||
const { Configuration } = require("aws-embedded-metrics"); | ||
Configuration.debuggingLoggingEnabled = true; | ||
// environment | ||
AWS_EMF_ENABLE_DEBUG_LOGGING=true | ||
``` | ||
## Examples | ||
@@ -275,0 +322,0 @@ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
108441
72
2152
372
2
12