Skip to content

Commit

Permalink
feat: use LoggingConfig.LogGroupName when lambda is configured with c…
Browse files Browse the repository at this point in the history
…ustom log groups
  • Loading branch information
onhate committed Mar 11, 2024
1 parent bf33efd commit 589e2d8
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 29 deletions.
4 changes: 4 additions & 0 deletions packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ export class EvaluateCloudFormationTemplate {
return cfnExpression;
}

public getResourceProperty(logicalId: string, propertyName: string) {
return this.template.Resources?.[logicalId]?.Properties?.[propertyName];
}

private references(logicalId: string, templateElement: any): boolean {
if (typeof templateElement === 'string') {
return logicalId === templateElement;
Expand Down
71 changes: 42 additions & 29 deletions packages/aws-cdk/lib/api/logs/find-cloudwatch-logs.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
import * as cxapi from '@aws-cdk/cx-api';
import { CloudFormation } from 'aws-sdk';
import { Mode, SdkProvider, ISDK } from '../aws-auth';
import { ISDK, Mode, SdkProvider } from '../aws-auth';
import { Deployments } from '../deployments';
import { EvaluateCloudFormationTemplate, LazyListStackResources } from '../evaluate-cloudformation-template';

// resource types that have associated CloudWatch Log Groups that should _not_ be monitored
const IGNORE_LOGS_RESOURCE_TYPES = ['AWS::EC2::FlowLog', 'AWS::CloudTrail::Trail', 'AWS::CodeBuild::Project'];

// Resource types that will create a CloudWatch log group with a specific name if one is not provided.
// The keys are CFN resource types, and the values are the name of the physical name property of that resource
// and the service name that is used in the automatically created CloudWatch log group.
const RESOURCE_TYPES_WITH_IMPLICIT_LOGS: { [cfnResourceType: string]: { [key: string]: string } } = {
'AWS::Lambda::Function': {
PhysicalNamePropertyName: 'FunctionName',
LogGroupServiceName: 'lambda',
},
};

/**
* Configuration needed to monitor CloudWatch Log Groups
* found in a given CloudFormation Stack
Expand Down Expand Up @@ -84,38 +74,61 @@ function isReferencedFromIgnoredResource(
logGroupResource: CloudFormation.StackResourceSummary,
evaluateCfnTemplate: EvaluateCloudFormationTemplate,
): boolean {
let foundReference = false;
const resourcesReferencingLogGroup = evaluateCfnTemplate.findReferencesTo(logGroupResource.LogicalResourceId);
for (const reference of resourcesReferencingLogGroup) {
if (IGNORE_LOGS_RESOURCE_TYPES.includes(reference.Type)) {
foundReference = true;
return true;
}
}
return foundReference;
return false;
}

type CloudWatchLogsResolver = (
resource: CloudFormation.StackResourceSummary,
evaluateCfnTemplate: EvaluateCloudFormationTemplate
) => string | undefined;

const cloudWatchLogsResolvers: Record<string, CloudWatchLogsResolver> = {
'AWS::Logs::LogGroup': (resource, evaluateCfnTemplate) => {
if (isReferencedFromIgnoredResource(resource, evaluateCfnTemplate)) {
return undefined;
}
return resource.PhysicalResourceId?.toString();
},

// Resource types that will create a CloudWatch log group with a specific name if one is not provided.
// The keys are CFN resource types, and the values are the name of the physical name property of that resource
// and the service name that is used in the automatically created CloudWatch log group.
'AWS::Lambda::Function': (resource, evaluateCfnTemplate) => {
const loggingConfig = evaluateCfnTemplate.getResourceProperty(resource.LogicalResourceId, 'LoggingConfig');
if (loggingConfig?.LogGroupName) {
return loggingConfig.LogGroupName;
}

return `/aws/lambda/${resource.PhysicalResourceId}`;
},
};

/**
* Find all CloudWatch Log Groups in the deployed template.
* This will find both explicitely created Log Groups (excluding those associated with ignored resources)
* as well as Log Groups created implicitely (i.e. Lambda Functions)
* This will find both explicitly created Log Groups (excluding those associated with ignored resources)
* and Log Groups created implicitly (i.e. Lambda Functions)
*/
function findAllLogGroupNames(
stackResources: CloudFormation.StackResourceSummary[],
evaluateCfnTemplate: EvaluateCloudFormationTemplate,
): string[] {
return stackResources.reduce((logGroupNames: string[], resource) => {
let logGroupName;
if (resource.ResourceType === 'AWS::Logs::LogGroup') {
if (!isReferencedFromIgnoredResource(resource, evaluateCfnTemplate)) {
logGroupName = resource.PhysicalResourceId;
const logGroupNames: string[] = [];

for (const resource of stackResources) {
const logGroupResolver = cloudWatchLogsResolvers[resource.ResourceType];
if (logGroupResolver) {
const logGroupName = logGroupResolver(resource, evaluateCfnTemplate);
if (logGroupName) {
logGroupNames.push(logGroupName);
}
} else if (RESOURCE_TYPES_WITH_IMPLICIT_LOGS[resource.ResourceType]) {
const servicePart = RESOURCE_TYPES_WITH_IMPLICIT_LOGS[resource.ResourceType].LogGroupServiceName;
logGroupName = `/aws/${servicePart}/${resource.PhysicalResourceId}`;
}
if (logGroupName) {
logGroupNames.push(logGroupName);
}
return logGroupNames;
}, []);
}

return logGroupNames;
}

0 comments on commit 589e2d8

Please sign in to comment.