Skip to content
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

feat(ecs-service-extensions): Enable default logging to CloudWatch for extensions (under feature flag) #17817

Merged
merged 17 commits into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from 15 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
33 changes: 32 additions & 1 deletion packages/@aws-cdk-containers/ecs-service-extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,38 @@ nameDescription.add(new Container({
Every `ServiceDescription` requires at minimum that you add a `Container` extension
which defines the main application (essential) container to run for the service.

After that, you can optionally enable additional features for the service using the `ServiceDescription.add()` method:
### Logging using `awslogs` log driver

If no observability extensions have been configured for a service, the ECS Service Extensions configure an `awslogs` log driver for the application container of the service to send the container logs to CloudWatch Logs. You can either provide a log group to the `Container` extension or one will be created for you by the CDK.
madeline-k marked this conversation as resolved.
Show resolved Hide resolved

Following is an example of an application with an `awslogs` log driver configured for the application container:

```ts
const environment = new Environment(stack, 'production');

const nameDescription = new ServiceDescription();
nameDescription.add(new Container({
cpu: 1024,
memoryMiB: 2048,
trafficPort: 80,
image: ContainerImage.fromRegistry('nathanpeck/name'),
environment: {
PORT: '80',
},
// Optionally provide a log group to send the application container logs to (If not provided, a log group will be created on your behalf)
madeline-k marked this conversation as resolved.
Show resolved Hide resolved
logGroup: new awslogs.LogGroup(stack, 'MyLogGroup'),
}));
```

This logging behavior is supported by default if using CDK v1.140.0 or v2.6.0 and later. For enabling this behavior for previous versions, ensure that the `ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER` flag within the application stack context is set to true, like so:
madeline-k marked this conversation as resolved.
Show resolved Hide resolved

```ts
stack.node.setContext(cxapi.ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER, true);
```

Alternatively, you can also set the feature flag in the `cdk.json` file. For more information, refer the [docs](https://docs.aws.amazon.com/cdk/v2/guide/featureflags.html).

After adding the `Container` extension, you can optionally enable additional features for the service using the `ServiceDescription.add()` method:

```ts
nameDescription.add(new AppMeshExtension({ mesh }));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import * as ecs from '@aws-cdk/aws-ecs';
import * as awslogs from '@aws-cdk/aws-logs';
import * as cdk from '@aws-cdk/core';
import * as cxapi from '@aws-cdk/cx-api';
import { Service } from '../service';
import { ServiceExtension } from './extension-interfaces';

Expand Down Expand Up @@ -38,6 +41,13 @@ export interface ContainerExtensionProps {
readonly environment?: {
[key: string]: string,
}

/**
* The log group into which application container logs should be routed.
*
* @default - A log group is automatically created for you if the `ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER` feature flag is set.
*/
readonly logGroup?: awslogs.ILogGroup;
}

/**
Expand All @@ -51,6 +61,11 @@ export class Container extends ServiceExtension {
*/
public readonly trafficPort: number;

/**
* The log group into which application container logs should be routed.
*/
public logGroup?: awslogs.ILogGroup;

/**
* The settings for the container.
*/
Expand All @@ -60,11 +75,12 @@ export class Container extends ServiceExtension {
super('service-container');
this.props = props;
this.trafficPort = props.trafficPort;
this.logGroup = props.logGroup;
}

// @ts-ignore - Ignore unused params that are required for abstract class extend
public prehook(service: Service, scope: Construct) {
this.parentService = service;
this.scope = scope;
}

// This hook sets the overall task resource requirements to the
Expand Down Expand Up @@ -93,6 +109,31 @@ export class Container extends ServiceExtension {
containerProps = hookProvider.mutateContainerDefinition(containerProps);
});

// If no observability extensions have been added to the service description then we can configure the `awslogs` log driver
if (!containerProps.logging) {
// Create a log group for the service if one is not provided by the user (only if feature flag is set)
if (!this.logGroup && this.parentService.node.tryGetContext(cxapi.ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER)) {
this.logGroup = new awslogs.LogGroup(this.scope, `${this.parentService.id}-logs`, {
upparekh marked this conversation as resolved.
Show resolved Hide resolved
logGroupName: `${this.parentService.id}-logs`,
removalPolicy: cdk.RemovalPolicy.DESTROY,
retention: awslogs.RetentionDays.ONE_MONTH,
});
}

if (this.logGroup) {
containerProps = {
...containerProps,
logging: new ecs.AwsLogDriver({
streamPrefix: this.parentService.id,
logGroup: this.logGroup,
}),
};
}
} else {
if (this.logGroup) {
throw Error(`Log configuration already specified. You cannot provide a log group for the application container of service '${this.parentService.id}' while also adding log configuration separately using service extensions.`);
}
}
this.container = taskDefinition.addContainer('app', containerProps);

// Create a port mapping for the container
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export interface ServiceProps {

/**
* The options for configuring the auto scaling target.
*
* @default none
*/
readonly autoScaleTaskCount?: AutoScalingOptions;
}
Expand Down Expand Up @@ -196,7 +198,6 @@ export class Service extends Construct {
// Ensure that the task definition supports both EC2 and Fargate
compatibility: ecs.Compatibility.EC2_AND_FARGATE,
} as ecs.TaskDefinitionProps;

for (const extensions in this.serviceDescription.extensions) {
if (this.serviceDescription.extensions[extensions]) {
taskDefProps = this.serviceDescription.extensions[extensions].modifyTaskDefinitionProps(taskDefProps);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"@aws-cdk/aws-sqs": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"@aws-cdk/cx-api": "0.0.0",
"@aws-cdk/region-info": "0.0.0",
"constructs": "^3.3.69"
},
Expand Down Expand Up @@ -98,6 +99,7 @@
"@aws-cdk/aws-sqs": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"@aws-cdk/cx-api": "0.0.0",
"@aws-cdk/region-info": "0.0.0",
"constructs": "^3.3.69"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ describe('appmesh', () => {
});

// THEN

// Ensure that task has an App Mesh sidecar
expect(stack).toHaveResource('AWS::ECS::TaskDefinition', {
ContainerDefinitions: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ describe('cloudwatch agent', () => {
});

// THEN

expect(stack).toHaveResource('AWS::ECS::TaskDefinition', {
ContainerDefinitions: [
{
Expand Down
Loading