-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Changes from all commits
79b9772
6152009
bb94f6f
fcd00ba
b5d9794
e0fe675
4aa9cf0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; | ||
Comment on lines
+21
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. either move these constants as static properties in some class and it is better to be the classes where these values be used, or make them private. Do we need to add these values in CDK Docs, and let the customers use them ? |
||
|
||
/** | ||
* 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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see some difference between the implementation and the RFC. RFC suggested more values (RESOURCE, AWS::RESOURCE) |
||
/** | ||
* 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' | ||
} |
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make it |
||
} | ||
|
||
/** | ||
* 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, | ||
}, | ||
}; | ||
} | ||
Comment on lines
+80
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the returning type ? |
||
} | ||
|
||
/** | ||
* 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(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this is wrong, you defined in the interface as |
||
}; | ||
} | ||
} |
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, | ||
}); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add an index.ts in the slo dir, and expose whatever you want there. Here, add only the directory