Skip to content

Commit 00a7af4

Browse files
authored
More logs (#154)
* More logs * Store deployment ID in context
1 parent a00a5ff commit 00a7af4

File tree

7 files changed

+132
-63
lines changed

7 files changed

+132
-63
lines changed

.changeset/social-houses-pull.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@seek/aws-codedeploy-infra': minor
3+
---
4+
5+
Enhance logs with additional context

packages/infra/src/handlers/framework/context.test.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import { setTimeout } from 'timers/promises';
22
import { inspect } from 'util';
33

4-
import {
5-
getAbortSignal,
6-
getContext,
7-
getRequestId,
8-
storage,
9-
withTimeout,
10-
} from './context.js';
4+
import { getAbortSignal, getContext, storage, withTimeout } from './context.js';
115

126
const context = {
137
abortSignal: new AbortController().signal,
148
requestId: 'mock-request-id',
9+
deploymentId: 'mock-deployment-id',
1510
};
1611

1712
describe('getAbortSignal', () => {
@@ -29,11 +24,6 @@ describe('getContext', () => {
2924
expect(getContext()).toStrictEqual({}));
3025
});
3126

32-
describe('getRequestId', () => {
33-
it('returns the current request ID where available', () =>
34-
storage.run(context, () => expect(getRequestId()).toBe(context.requestId)));
35-
});
36-
3727
describe('withTimeout', () => {
3828
it('returns task result on happy path', async () => {
3929
const result = 'mock-result';

packages/infra/src/handlers/framework/context.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { AsyncLocalStorage } from 'async_hooks';
33
type Context = {
44
abortSignal?: AbortSignal;
55
requestId?: string;
6+
deploymentId?: string;
67
};
78

89
export const storage = new AsyncLocalStorage<Context>();
@@ -11,8 +12,6 @@ export const getContext = (): Context => storage.getStore() ?? {};
1112

1213
export const getAbortSignal = () => storage.getStore()?.abortSignal;
1314

14-
export const getRequestId = () => storage.getStore()?.requestId;
15-
1615
export const withTimeout = async <T>(
1716
task: () => Promise<T>,
1817
timeoutMs: number,

packages/infra/src/handlers/framework/logging.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import createLogger from '@seek/logger';
22

33
import { config } from '../config.js';
44

5-
import { getRequestId } from './context.js';
5+
import { getContext } from './context.js';
66

77
export const testLogs: unknown[] = [];
88

@@ -14,7 +14,14 @@ export const logger = createLogger(
1414
{
1515
base: null,
1616

17-
mixin: () => ({ awsRequestId: getRequestId() }),
17+
mixin: () => {
18+
const context = getContext();
19+
20+
return {
21+
awsRequestId: context.requestId,
22+
deploymentId: context.deploymentId,
23+
};
24+
},
1825

1926
transport:
2027
config.environment === 'local' ? { target: 'pino-pretty' } : undefined,

packages/infra/src/handlers/index.test.ts

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,26 @@ import {
1010
import { mockClient } from 'aws-sdk-client-mock';
1111

1212
import { testLogs } from './framework/logging.js';
13-
import { processEvent } from './process/process.js';
13+
import { getDeploymentInfo, process } from './process/process.js';
1414

1515
import { handler } from './index.js';
1616

1717
const codeDeploy = mockClient(CodeDeployClient);
1818

19-
const processEventMock = jest.mocked(processEvent);
19+
const getDeploymentInfoMock = jest.mocked(getDeploymentInfo);
20+
const processMock = jest.mocked(process);
21+
22+
beforeEach(() => {
23+
getDeploymentInfoMock.mockResolvedValue({
24+
applicationName: 'beep',
25+
revision: { string: { content: 'stuff' } },
26+
});
27+
});
2028

2129
afterEach(() => {
2230
codeDeploy.reset();
23-
processEventMock.mockReset();
31+
getDeploymentInfoMock.mockReset();
32+
processMock.mockReset();
2433
testLogs.length = 0;
2534
});
2635

@@ -39,13 +48,13 @@ describe('handler', () => {
3948
};
4049

4150
it('reports a success back to CodeDeploy', async () => {
42-
processEventMock.mockResolvedValue(undefined);
51+
processMock.mockResolvedValue(undefined);
4352

4453
codeDeploy.on(PutLifecycleEventHookExecutionStatusCommand).resolves({});
4554

4655
await expect(handler(event, context)).resolves.toBeUndefined();
4756

48-
expect(processEventMock).toHaveBeenCalledTimes(1);
57+
expect(processMock).toHaveBeenCalledTimes(1);
4958

5059
expect(codeDeploy).toHaveReceivedNthCommandWith(
5160
1,
@@ -59,9 +68,14 @@ describe('handler', () => {
5968

6069
expect(testLogs).toStrictEqual([
6170
{
71+
applicationName: 'beep',
6272
awsRequestId: context.awsRequestId,
73+
deploymentId: 'mock-deployment-id',
6374
level: 30,
6475
msg: 'Reported lifecycle event status',
76+
revision: {
77+
string: { content: 'stuff' },
78+
},
6579
status: LifecycleEventStatus.SUCCEEDED,
6680
timestamp: expect.any(String),
6781
},
@@ -80,13 +94,13 @@ describe('handler', () => {
8094
},
8195
);
8296

83-
processEventMock.mockRejectedValue(err);
97+
processMock.mockRejectedValue(err);
8498

8599
codeDeploy.on(PutLifecycleEventHookExecutionStatusCommand).resolves({});
86100

87101
await expect(handler(event, context)).resolves.toBeUndefined();
88102

89-
expect(processEventMock).toHaveBeenCalledTimes(1);
103+
expect(processMock).toHaveBeenCalledTimes(1);
90104

91105
expect(codeDeploy).toHaveReceivedNthCommandWith(
92106
1,
@@ -100,7 +114,9 @@ describe('handler', () => {
100114

101115
expect(testLogs).toStrictEqual([
102116
{
117+
applicationName: 'beep',
103118
awsRequestId: context.awsRequestId,
119+
deploymentId: 'mock-deployment-id',
104120
err: {
105121
message: err.message,
106122
payload: err.payload,
@@ -109,12 +125,20 @@ describe('handler', () => {
109125
},
110126
level: 50,
111127
msg: 'Failed to process lifecycle event',
128+
revision: {
129+
string: { content: 'stuff' },
130+
},
112131
timestamp: expect.any(String),
113132
},
114133
{
134+
applicationName: 'beep',
115135
awsRequestId: context.awsRequestId,
136+
deploymentId: 'mock-deployment-id',
116137
level: 30,
117138
msg: 'Reported lifecycle event status',
139+
revision: {
140+
string: { content: 'stuff' },
141+
},
118142
status: LifecycleEventStatus.FAILED,
119143
timestamp: expect.any(String),
120144
},
@@ -124,7 +148,7 @@ describe('handler', () => {
124148
it('throws on failure to report a success', async () => {
125149
const err = new Error('mock-error');
126150

127-
processEventMock.mockResolvedValue(undefined);
151+
processMock.mockResolvedValue(undefined);
128152

129153
codeDeploy.on(PutLifecycleEventHookExecutionStatusCommand).rejects(err);
130154

@@ -134,7 +158,7 @@ describe('handler', () => {
134158
`"Failed to report lifecycle event status"`,
135159
);
136160

137-
expect(processEventMock).toHaveBeenCalledTimes(1);
161+
expect(processMock).toHaveBeenCalledTimes(1);
138162

139163
expect(codeDeploy).toHaveReceivedNthCommandWith(
140164
1,
@@ -148,11 +172,16 @@ describe('handler', () => {
148172

149173
expect(testLogs).toStrictEqual([
150174
{
175+
applicationName: 'beep',
151176
awsRequestId: context.awsRequestId,
177+
deploymentId: 'mock-deployment-id',
152178
err: expect.objectContaining({ message: err.message }),
153179
level: 50,
154180
msg: 'Failed to report lifecycle event status',
155181
status: LifecycleEventStatus.SUCCEEDED,
182+
revision: {
183+
string: { content: 'stuff' },
184+
},
156185
timestamp: expect.any(String),
157186
},
158187
]);
@@ -166,15 +195,15 @@ describe('handler', () => {
166195
.on(PutLifecycleEventHookExecutionStatusCommand)
167196
.rejects(reportError);
168197

169-
processEventMock.mockRejectedValue(processError);
198+
processMock.mockRejectedValue(processError);
170199

171200
await expect(
172201
handler(event, context),
173202
).rejects.toThrowErrorMatchingInlineSnapshot(
174203
`"Failed to report lifecycle event status"`,
175204
);
176205

177-
expect(processEventMock).toHaveBeenCalledTimes(1);
206+
expect(processMock).toHaveBeenCalledTimes(1);
178207

179208
expect(codeDeploy).toHaveReceivedNthCommandWith(
180209
1,
@@ -188,17 +217,27 @@ describe('handler', () => {
188217

189218
expect(testLogs).toStrictEqual([
190219
{
220+
applicationName: 'beep',
191221
awsRequestId: context.awsRequestId,
222+
deploymentId: 'mock-deployment-id',
192223
err: expect.objectContaining({ message: processError.message }),
193224
level: 50,
194225
msg: 'Failed to process lifecycle event',
226+
revision: {
227+
string: { content: 'stuff' },
228+
},
195229
timestamp: expect.any(String),
196230
},
197231
{
232+
applicationName: 'beep',
198233
awsRequestId: context.awsRequestId,
234+
deploymentId: 'mock-deployment-id',
199235
err: expect.objectContaining({ message: reportError.message }),
200236
level: 50,
201237
msg: 'Failed to report lifecycle event status',
238+
revision: {
239+
string: { content: 'stuff' },
240+
},
202241
status: LifecycleEventStatus.FAILED,
203242
timestamp: expect.any(String),
204243
},

packages/infra/src/handlers/index.ts

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,54 @@ import type { Context } from 'aws-lambda';
33

44
import { storage, withTimeout } from './framework/context.js';
55
import { logger } from './framework/logging.js';
6-
import { processEvent } from './process/process.js';
6+
import {
7+
type DeploymentInfo,
8+
getDeploymentInfo,
9+
process,
10+
} from './process/process.js';
711
import { reportEventStatus } from './report.js';
812
import type { CodeDeployLifecycleHookEvent } from './types.js';
913

1014
export const handler = (
1115
event: CodeDeployLifecycleHookEvent,
1216
context: Pick<Context, 'awsRequestId' | 'getRemainingTimeInMillis'>,
1317
): Promise<void> =>
14-
storage.run({ requestId: context.awsRequestId }, async () => {
15-
// Reserve a generous 30 seconds to report the status back to CodeDeploy.
16-
const timeoutMs = context.getRemainingTimeInMillis() - 30_000;
17-
18-
let status: LifecycleEventStatus = LifecycleEventStatus.FAILED;
19-
try {
20-
await withTimeout(() => processEvent(event), timeoutMs);
21-
22-
status = LifecycleEventStatus.SUCCEEDED;
23-
} catch (err) {
24-
logger.error({ err }, 'Failed to process lifecycle event');
25-
}
26-
27-
try {
28-
await reportEventStatus(event, status);
29-
30-
logger.info({ status }, 'Reported lifecycle event status');
31-
} catch (err) {
32-
const message = 'Failed to report lifecycle event status';
33-
34-
logger.error({ err, status }, message);
35-
36-
throw new Error(message);
37-
}
38-
});
18+
storage.run(
19+
{ requestId: context.awsRequestId, deploymentId: event.DeploymentId },
20+
async () => {
21+
// Reserve a generous 30 seconds to report the status back to CodeDeploy.
22+
const timeoutMs = context.getRemainingTimeInMillis() - 30_000;
23+
24+
let status: LifecycleEventStatus = LifecycleEventStatus.FAILED;
25+
let deploymentInfo: DeploymentInfo | undefined;
26+
27+
try {
28+
await withTimeout(async () => {
29+
deploymentInfo = await getDeploymentInfo(event);
30+
return process(deploymentInfo);
31+
}, timeoutMs);
32+
33+
status = LifecycleEventStatus.SUCCEEDED;
34+
} catch (err) {
35+
logger.error(
36+
{ err, ...deploymentInfo },
37+
'Failed to process lifecycle event',
38+
);
39+
}
40+
41+
try {
42+
await reportEventStatus(event, status);
43+
44+
logger.info(
45+
{ status, ...deploymentInfo },
46+
'Reported lifecycle event status',
47+
);
48+
} catch (err) {
49+
const message = 'Failed to report lifecycle event status';
50+
51+
logger.error({ err, status, ...deploymentInfo }, message);
52+
53+
throw new Error(message);
54+
}
55+
},
56+
);

0 commit comments

Comments
 (0)