Description
Summary
In #3945 we added new zero-dependency functional utilities to read and parse environment variables. These new functions supersede the old class based environment service approach. We should refactor the Tracer package to use the new function based approach instead of the custom EnvironmentVariablesService
class.
Why is this needed?
The rationale for the introduction of these utilities as per #3945 is as follows:
[M]ost of the advantage to customers will come in the form of smaller utilities over time, since we'll be able to use these helpers across other Powertools for AWS utilities and replace the existing EnvironmentVariablesService class-based model which is extremely verbose and requires props drilling if you want to access env variables deep into an inheritance stack.
This change will help us to eventually remove the EnvironmentVariablesService
entirely from the code base. The Tracer package currently has its own extended version of the service that adds tracer-specific environment variable handling.
Which area does this relate to?
Tracer
Solution
Currently the Tracer package has its own EnvironmentVariablesService class that extends the common one:
import { EnvironmentVariablesService as CommonEnvironmentVariablesService } from '@aws-lambda-powertools/commons';
class EnvironmentVariablesService
extends CommonEnvironmentVariablesService
implements ConfigServiceInterface
{
private awsExecutionEnv = 'AWS_EXECUTION_ENV';
private samLocalVariable = 'AWS_SAM_LOCAL';
private tracerCaptureErrorVariable = 'POWERTOOLS_TRACER_CAPTURE_ERROR';
private tracerCaptureHTTPsRequestsVariable = 'POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS';
private tracerCaptureResponseVariable = 'POWERTOOLS_TRACER_CAPTURE_RESPONSE';
private tracingEnabledVariable = 'POWERTOOLS_TRACE_ENABLED';
public getAwsExecutionEnv(): string {
return this.get(this.awsExecutionEnv);
}
public getCaptureHTTPsRequests(): string {
return this.get(this.tracerCaptureHTTPsRequestsVariable);
}
public getSamLocal(): string {
return this.get(this.samLocalVariable);
}
public getTracingCaptureError(): string {
return this.get(this.tracerCaptureErrorVariable);
}
public getTracingCaptureResponse(): string {
return this.get(this.tracerCaptureResponseVariable);
}
public getTracingEnabled(): string {
return this.get(this.tracingEnabledVariable);
}
}
const environmentVariablesService = new EnvironmentVariablesService();
This service is used throughout the Tracer class and ProviderService for reading environment variables multiple times.
Proposed refactor using new functional utilities:
Replace the service with a private #envConfig
object that is populated once during the setOptions
method:
import {
getStringFromEnv,
getBooleanFromEnv,
isDevMode,
getServiceName,
getXRayTraceIdFromEnv
} from '@aws-lambda-powertools/commons/utils/env';
class Tracer {
// Replace envVarsService with #envConfig using hash notation
readonly #envConfig: {
awsExecutionEnv: string;
samLocal: string;
captureError: string;
captureHTTPsRequests: string;
captureResponse: string;
tracingEnabled: string;
serviceName: string;
xrayTraceId: string;
};
// In setOptions method, replace envVarsService assignment
private setOptions(options: TracerOptions): Tracer {
const { enabled, serviceName, captureHTTPsRequests, customConfigService } = options;
this.#setEnvConfig(); // Replace this.envVarsService = environmentVariablesService;
this.setCustomConfigService(customConfigService);
this.setTracingEnabled(enabled);
this.setCaptureResponse();
this.setCaptureError();
this.setServiceName(serviceName);
this.setCaptureHTTPsRequests(captureHTTPsRequests);
return this;
}
// Replace envVarsService initialization
private #setEnvConfig(): void {
this.#envConfig = {
awsExecutionEnv: getStringFromEnv({
key: 'AWS_EXECUTION_ENV',
defaultValue: '',
}),
samLocal: getStringFromEnv({
key: 'AWS_SAM_LOCAL',
defaultValue: '',
}),
captureError: getStringFromEnv({
key: 'POWERTOOLS_TRACER_CAPTURE_ERROR',
defaultValue: '',
}),
captureHTTPsRequests: getStringFromEnv({
key: 'POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS',
defaultValue: '',
}),
captureResponse: getStringFromEnv({
key: 'POWERTOOLS_TRACER_CAPTURE_RESPONSE',
defaultValue: '',
}),
tracingEnabled: getStringFromEnv({
key: 'POWERTOOLS_TRACE_ENABLED',
defaultValue: '',
}),
serviceName: getServiceName(),
xrayTraceId: getXRayTraceIdFromEnv() || '',
};
}
// Update methods to use #envConfig instead of service calls
private isAmplifyCli(): boolean {
return this.#envConfig.awsExecutionEnv === 'AWS_Lambda_amplify-mock';
}
private isLambdaExecutionEnv(): boolean {
return this.#envConfig.awsExecutionEnv !== '';
}
private isLambdaSamCli(): boolean {
return this.#envConfig.samLocal !== '';
}
private setCaptureError(): void {
if (this.captureError !== undefined) {
return;
}
if (this.#envConfig.captureError.toLowerCase() === 'false') {
this.captureError = false;
return;
}
this.captureError = true;
}
private setCaptureHTTPsRequests(captureHTTPsRequests?: boolean): void {
if (captureHTTPsRequests !== undefined) {
this.captureHTTPsRequests = captureHTTPsRequests;
return;
}
if (this.#envConfig.captureHTTPsRequests.toLowerCase() === 'false') {
this.captureHTTPsRequests = false;
return;
}
this.captureHTTPsRequests = true;
}
private setCaptureResponse(): void {
if (this.captureResponse !== undefined) {
return;
}
if (this.#envConfig.captureResponse.toLowerCase() === 'false') {
this.captureResponse = false;
return;
}
this.captureResponse = true;
}
private setServiceName(serviceName?: string): void {
if (serviceName !== undefined && this.isValidServiceName(serviceName)) {
this.serviceName = serviceName;
return;
}
if (this.#envConfig.serviceName !== undefined && this.isValidServiceName(this.#envConfig.serviceName)) {
this.serviceName = this.#envConfig.serviceName;
return;
}
this.serviceName = this.defaultServiceName;
}
private setTracingEnabled(enabled?: boolean): void {
if (enabled !== undefined) {
this.tracingEnabled = enabled;
return;
}
if (this.#envConfig.tracingEnabled.toLowerCase() === 'false') {
this.tracingEnabled = false;
return;
}
this.tracingEnabled = true;
}
}
For ProviderService, replace the import and usage:
// Replace import
import { getXRayTraceIdFromEnv } from '@aws-lambda-powertools/commons/utils/env';
// Replace usage in ProviderService
request.addHeader(
'X-Amzn-Trace-Id',
`Root=${getXRayTraceIdFromEnv()};Parent=${subsegment.id};Sampled=${subsegment.notTraced ? '0' : '1'}`
);
Files to be modified:
- Remove
packages/tracer/src/config/EnvironmentVariablesService.ts
entirely - Update
packages/tracer/src/Tracer.ts
to use the#envConfig
approach - Update
packages/tracer/src/provider/ProviderService.ts
to use functional utilities - Remove the
envVarsService
property and related methods (getEnvVarsService()
)
Benefits of this approach:
- Environment variables are read only once during initialization (better performance)
- No repeated environment variable access throughout the object lifecycle
- Cleaner, more functional approach
- Eliminates the verbose class-based service pattern
- Makes environment configuration explicit and cached
- Consistent with existing
setOptions
pattern
This refactor would eliminate approximately 75 lines of class-based code and improve performance by avoiding repeated environment variable reads.
Acknowledgment
- This request meets Powertools for AWS Lambda (TypeScript) Tenets
- Should this be considered in other Powertools for AWS Lambda languages? i.e. Python, Java, and .NET
Future readers
Please react with 👍 and your use case to help us understand customer demand.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status