Skip to content

feat(applicationsignals-alpha): introduce Application Signals SLO L2 constructs #33987

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-applicationsignals-alpha/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export * from './enablement/constants';
export * from './enablement/instrumentation-versions';
export * from './enablement/ecs-cloudwatch-agent';
export * from './enablement/ecs-sdk-instrumentation';
export * from './slo/slo';
export * from './slo/slo-types';
149 changes: 149 additions & 0 deletions packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* Types of metrics that can be used for SLIs
*/
export enum MetricType {
/**
* Latency-based metric type
* Used for measuring response time or duration
*/
LATENCY = 'LATENCY',

/**
* Availability-based metric type
* Used for measuring uptime or success rate
*/
AVAILABILITY = 'AVAILABILITY'
}

/**
* Default values for goal configuration
*/
export const DEFAULT_GOAL_CONFIG = {
ATTAINMENT_GOAL: 99.9,
WARNING_THRESHOLD: 30,
} as const;

/**
* Comparison operators for metric thresholds
*/
export enum ComparisonOperator {
/**
* Greater than operator
* True if metric value is strictly greater than threshold
*/
GREATER_THAN = 'GREATER_THAN',

/**
* Less than operator
* True if metric value is strictly less than threshold
*/
LESS_THAN = 'LESS_THAN',

/**
* Greater than or equal operator
* True if metric value is greater than or equal to threshold
*/
GREATER_THAN_OR_EQUAL = 'GREATER_THAN_OR_EQUAL',

/**
* Less than or equal operator
* True if metric value is less than or equal to threshold
*/
LESS_THAN_OR_EQUAL = 'LESS_THAN_OR_EQUAL'
}

/**
* Statistical methods for aggregating metric values
*/
export enum MetricStatistic {
/**
* Average of all values in the period
*/
AVERAGE = 'Average',

/**
* Sum of all values in the period
*/
SUM = 'Sum',

/**
* Minimum value in the period
*/
MINIMUM = 'Minimum',

/**
* Maximum value in the period
*/
MAXIMUM = 'Maximum',

/**
* Count of samples in the period
*/
SAMPLE_COUNT = 'SampleCount',

/**
* 99th percentile of values in the period
*/
P99 = 'p99',

/**
* 95th percentile of values in the period
*/
P95 = 'p95',

/**
* 90th percentile of values in the period
*/
P90 = 'p90',

/**
* 50th percentile (median) of values in the period
*/
P50 = 'p50'
}

/**
* Types of services that can be monitored
*/
export enum KeyAttributeType {
/**
* Service running in your account
*/
SERVICE = 'SERVICE',

/**
* AWS managed service
*/
AWS_SERVICE = 'AWS_SERVICE',

/**
* External service
*/
REMOTE_SERVICE = 'REMOTE_SERVICE'
}

/**
* Units for duration measurement in SLO intervals
*/
export enum DurationUnit {
/**
* Minute unit for fine-grained intervals
*/
MINUTE = 'MINUTE',

/**
* Hour unit for medium-term intervals
*/
HOUR = 'HOUR',

/**
* Day unit for daily intervals
*/
DAY = 'DAY',

/**
* Month unit for long-term intervals
* Used for calendar-aligned monitoring
*/
MONTH = 'MONTH'
}
152 changes: 152 additions & 0 deletions packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/interval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { CalendarIntervalProps, GoalConfig, IInterval, IntervalProps } from './slo';
import { DEFAULT_GOAL_CONFIG, DurationUnit } from './constants';

/**
* Base class for Interval implementations
*/
export abstract class Interval implements IInterval {
/**
* Creates a calendar interval
*/
public static calendar(props: CalendarIntervalProps): IInterval {
return new CalendarInterval(props);
}

/**
* Creates a rolling interval
*/
public static rolling(props: IntervalProps): IInterval {
return new RollingInterval(props);
}

abstract bind(): any;
}

/**
* Implementation of Calendar Interval
*/
export class CalendarInterval extends Interval {
/**
* The duration value for the interval
* Must be greater than 0
*
* @private
*/
private readonly duration: number;

/**
* The unit of duration measurement
* Can be MINUTE, HOUR, DAY, or MONTH
*
* @private
*/
private readonly unit: DurationUnit;

/**
* The start time of the interval
* Specified as Unix timestamp in milliseconds
* Default starts from now
*
* @private
*/
private readonly startTime: number;

constructor(props: CalendarIntervalProps) {
super();
this.duration = props.duration;
this.unit = props.unit;
this.startTime = props.startTime?? Date.now();
this.validate();
}

private validate() {
if (this.duration <= 0) {
throw new Error('Duration must be greater than 0');
}

if (this.unit === DurationUnit.MONTH && this.duration > 12) {
throw new Error('Month duration cannot exceed 12');
}

if (this.unit === DurationUnit.DAY && this.duration > 31) {
throw new Error('Day duration cannot exceed 31');
}

if (this.startTime <= 0) {
throw new Error('Start time must be greater than 0');
}
}

bind() {
return {
calendarInterval: {
duration: this.duration,
durationUnit: this.unit,
startTime: this.startTime,
},
};
}
}

/**
* Implementation of Rolling Interval
*/
export class RollingInterval extends Interval {
private readonly duration: number;
private readonly unit: DurationUnit;

constructor(props: IntervalProps) {
super();
this.duration = props.duration;
this.unit = props.unit;
this.validate();
}

private validate() {
if (this.duration <= 0) {
throw new Error('Duration must be greater than 0');
}

if (this.unit === DurationUnit.MONTH && this.duration > 12) {
throw new Error('Month duration cannot exceed 12');
}

if (this.unit === DurationUnit.DAY && this.duration > 31) {
throw new Error('Day duration cannot exceed 31');
}
}

bind() {
return {
rollingInterval: {
duration: this.duration,
durationUnit: this.unit,
},
};
}
}

/**
* Implementation of goal configuration
*/
export class Goal {
/**
* Creates a new goal configuration
*/
public static of(props: GoalConfig): Goal {
return new Goal(props);
}

private constructor(private readonly props: GoalConfig) {}

/**
* Binds the goal configuration to L1 construct properties
*/
public _bind(): applicationsignals.CfnServiceLevelObjective.GoalProperty {
return {
attainmentGoal: this.props.attainmentGoal ?? DEFAULT_GOAL_CONFIG.ATTAINMENT_GOAL,
warningThreshold: this.props.warningThreshold ?? DEFAULT_GOAL_CONFIG.WARNING_THRESHOLD,
interval: this.props.interval._bind(),
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { KeyAttributeType } from './constants';
import { KeyAttributesProps } from './slo';

/**
* Class representing Application Signals key attributes with validation
*/
export class KeyAttributes {
private readonly props: KeyAttributesProps;

constructor(props: KeyAttributesProps) {
this.validateProps(props);
this.props = props;
}

private validateProps(props: KeyAttributesProps) {
if (!props.name && !props.environment && !props.type) {
throw new Error('name or type or environment is required for Application Signals service');
}
}

public bind(): { [key: string]: string } {
return {
Type: this.props.type,
Name: this.props.name,
Environment: this.props.environment,
...(this.props.identifier && { Identifier: this.props.identifier }),
};
}

/**
* Creates key attributes for an AWS service
*/
public static awsService(props: Omit<KeyAttributesProps, 'type'>): KeyAttributes {
return new KeyAttributes({
type: KeyAttributeType.AWS_SERVICE,
...props,
});
}

/**
* Creates key attributes for a custom service
*/
public static service(props: Omit<KeyAttributesProps, 'type'>): KeyAttributes {
return new KeyAttributes({
type: KeyAttributeType.SERVICE,
...props,
});
}

/**
* Creates key attributes for a remote service
*/
public static remoteService(props: Omit<KeyAttributesProps, 'type'>): KeyAttributes {
return new KeyAttributes({
type: KeyAttributeType.REMOTE_SERVICE,
...props,
});
}
}
Loading