Skip to content
This repository was archived by the owner on Jan 16, 2025. It is now read-only.

Commit 0ba0930

Browse files
authored
fix: add logging context to runner lambda (#1399)
* fix(logging): Add context to scale logs Signed-off-by: Nathaniel McAuliffe <[email protected]> * Remove testing Signed-off-by: Nathaniel McAuliffe <[email protected]> * Remove unnecessary import Signed-off-by: Nathaniel McAuliffe <[email protected]> * Moving log fields to end, adjusting format
1 parent 8094576 commit 0ba0930

File tree

5 files changed

+73
-40
lines changed

5 files changed

+73
-40
lines changed

Diff for: modules/runners/lambdas/runners/src/scale-runners/gh-auth.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import {
1111
} from '@octokit/auth-app/dist-types/types';
1212
import { OctokitOptions } from '@octokit/core/dist-types/types';
1313
import { getParameterValue } from './ssm';
14-
import { logger as rootLogger } from './logger';
14+
import { logger as rootLogger, LogFields } from './logger';
1515

16-
const logger = rootLogger.getChildLogger();
16+
const logger = rootLogger.getChildLogger({ name: 'gh-auth' });
1717

1818
export async function createOctoClient(token: string, ghesApiUrl = ''): Promise<Octokit> {
1919
const ocktokitOptions: OctokitOptions = {
@@ -60,7 +60,7 @@ async function createAuth(installationId: number | undefined, ghesApiUrl: string
6060
};
6161
if (installationId) authOptions = { ...authOptions, installationId };
6262

63-
logger.debug(`GHES API URL: ${ghesApiUrl}`);
63+
logger.debug(`GHES API URL: ${ghesApiUrl}`, LogFields.print());
6464
if (ghesApiUrl) {
6565
authOptions.request = request.defaults({
6666
baseUrl: ghesApiUrl,

Diff for: modules/runners/lambdas/runners/src/scale-runners/logger.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ export const logger = new Logger({
55
displayInstanceName: false,
66
maskAnyRegEx: ['--token [A-Z0-9]*'],
77
minLevel: process.env.LOG_LEVEL || 'info',
8-
name: 'scale-up',
8+
name: 'scale-runners',
99
overwriteConsole: true,
1010
type: process.env.LOG_TYPE || 'pretty',
1111
});
12+
export class LogFields {
13+
static fields: { [key: string]: string } = {};
14+
15+
public static print(): string {
16+
return JSON.stringify(LogFields.fields);
17+
}
18+
}

Diff for: modules/runners/lambdas/runners/src/scale-runners/runners.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EC2, SSM } from 'aws-sdk';
2-
import { logger as rootLogger } from './logger';
2+
import { logger as rootLogger, LogFields } from './logger';
33

4-
const logger = rootLogger.getChildLogger();
4+
const logger = rootLogger.getChildLogger({ name: 'runners' });
55

66
export interface RunnerList {
77
instanceId: string;
@@ -75,16 +75,20 @@ export async function terminateRunner(instanceId: string): Promise<void> {
7575
InstanceIds: [instanceId],
7676
})
7777
.promise();
78-
logger.info(`Runner ${instanceId} has been terminated.`);
78+
logger.info(`Runner ${instanceId} has been terminated.`, LogFields.print());
7979
}
8080

8181
export async function createRunner(runnerParameters: RunnerInputParameters, launchTemplateName: string): Promise<void> {
82-
logger.debug('Runner configuration: ' + JSON.stringify(runnerParameters));
82+
logger.debug('Runner configuration: ' + JSON.stringify(runnerParameters), LogFields.print());
8383
const ec2 = new EC2();
8484
const runInstancesResponse = await ec2
8585
.runInstances(getInstanceParams(launchTemplateName, runnerParameters))
8686
.promise();
87-
logger.info('Created instance(s): ', runInstancesResponse.Instances?.map((i) => i.InstanceId).join(','));
87+
logger.info(
88+
'Created instance(s): ',
89+
runInstancesResponse.Instances?.map((i) => i.InstanceId).join(','),
90+
LogFields.print(),
91+
);
8892
const ssm = new SSM();
8993
runInstancesResponse.Instances?.forEach(async (i: EC2.Instance) => {
9094
await ssm

Diff for: modules/runners/lambdas/runners/src/scale-runners/scale-down.ts

+34-19
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ import { listEC2Runners, RunnerInfo, RunnerList, terminateRunner } from './runne
44
import { getIdleRunnerCount, ScalingDownConfig } from './scale-down-config';
55
import { createOctoClient, createGithubAppAuth, createGithubInstallationAuth } from './gh-auth';
66
import { githubCache, GhRunners } from './cache';
7-
import { logger as rootLogger } from './logger';
7+
import { logger as rootLogger, LogFields } from './logger';
88

9-
const logger = rootLogger.getChildLogger();
9+
const logger = rootLogger.getChildLogger({ name: 'scale-down' });
1010

1111
async function getOrCreateOctokit(runner: RunnerInfo): Promise<Octokit> {
1212
const key = runner.owner;
1313
const cachedOctokit = githubCache.clients.get(key);
1414

1515
if (cachedOctokit) {
16-
logger.debug(`[createGitHubClientForRunner] Cache hit for ${key}`);
16+
logger.debug(`[createGitHubClientForRunner] Cache hit for ${key}`, LogFields.print());
1717
return cachedOctokit;
1818
}
1919

20-
logger.debug(`[createGitHubClientForRunner] Cache miss for ${key}`);
20+
logger.debug(`[createGitHubClientForRunner] Cache miss for ${key}`, LogFields.print());
2121
const ghesBaseUrl = process.env.GHES_URL;
2222
let ghesApiUrl = '';
2323
if (ghesBaseUrl) {
@@ -50,11 +50,11 @@ async function listGitHubRunners(runner: RunnerInfo): Promise<GhRunners> {
5050
const key = runner.owner as string;
5151
const cachedRunners = githubCache.runners.get(key);
5252
if (cachedRunners) {
53-
logger.debug(`[listGithubRunners] Cache hit for ${key}`);
53+
logger.debug(`[listGithubRunners] Cache hit for ${key}`, LogFields.print());
5454
return cachedRunners;
5555
}
5656

57-
logger.debug(`[listGithubRunners] Cache miss for ${key}`);
57+
logger.debug(`[listGithubRunners] Cache miss for ${key}`, LogFields.print());
5858
const client = await getOrCreateOctokit(runner);
5959
const runners =
6060
runner.type === 'Org'
@@ -102,12 +102,18 @@ async function removeRunner(ec2runner: RunnerInfo, ghRunnerId: number): Promise<
102102

103103
if (result.status == 204) {
104104
await terminateRunner(ec2runner.instanceId);
105-
logger.info(`AWS runner instance '${ec2runner.instanceId}' is terminated and GitHub runner is de-registered.`);
105+
logger.info(
106+
`AWS runner instance '${ec2runner.instanceId}' is terminated and GitHub runner is de-registered.`,
107+
LogFields.print(),
108+
);
106109
} else {
107-
logger.error(`Failed to de-register GitHub runner: ${result.status}`);
110+
logger.error(`Failed to de-register GitHub runner: ${result.status}`, LogFields.print());
108111
}
109112
} catch (e) {
110-
logger.info(`Runner '${ec2runner.instanceId}' cannot be de-registered, most likely the runner is active.`);
113+
logger.info(
114+
`Runner '${ec2runner.instanceId}' cannot be de-registered, most likely the runner is active.`,
115+
LogFields.print(),
116+
);
111117
}
112118
}
113119

@@ -120,26 +126,29 @@ async function evaluateAndRemoveRunners(
120126

121127
for (const ownerTag of ownerTags) {
122128
const ec2RunnersFiltered = ec2Runners.filter((runner) => runner.owner === ownerTag);
123-
logger.debug(`Found: '${ec2RunnersFiltered.length}' active GitHub runners with owner tag: '${ownerTag}'`);
129+
logger.debug(
130+
`Found: '${ec2RunnersFiltered.length}' active GitHub runners with owner tag: '${ownerTag}'`,
131+
LogFields.print(),
132+
);
124133
for (const ec2Runner of ec2RunnersFiltered) {
125134
const ghRunners = await listGitHubRunners(ec2Runner);
126135
const ghRunner = ghRunners.find((runner) => runner.name === ec2Runner.instanceId);
127136
if (ghRunner) {
128137
if (runnerMinimumTimeExceeded(ec2Runner)) {
129138
if (idleCounter > 0) {
130139
idleCounter--;
131-
logger.info(`Runner '${ec2Runner.instanceId}' will kept idle.`);
140+
logger.info(`Runner '${ec2Runner.instanceId}' will kept idle.`, LogFields.print());
132141
} else {
133-
logger.info(`Runner '${ec2Runner.instanceId}' will be terminated.`);
142+
logger.info(`Runner '${ec2Runner.instanceId}' will be terminated.`, LogFields.print());
134143
await removeRunner(ec2Runner, ghRunner.id);
135144
}
136145
}
137146
} else {
138147
if (bootTimeExceeded(ec2Runner)) {
139-
logger.info(`Runner '${ec2Runner.instanceId}' is orphaned and will be removed.`);
148+
logger.info(`Runner '${ec2Runner.instanceId}' is orphaned and will be removed.`, LogFields.print());
140149
terminateOrphan(ec2Runner.instanceId);
141150
} else {
142-
logger.debug(`Runner ${ec2Runner.instanceId} has not yet booted.`);
151+
logger.debug(`Runner ${ec2Runner.instanceId} has not yet booted.`, LogFields.print());
143152
}
144153
}
145154
}
@@ -150,7 +159,7 @@ async function terminateOrphan(instanceId: string): Promise<void> {
150159
try {
151160
await terminateRunner(instanceId);
152161
} catch (e) {
153-
logger.debug(`Orphan runner '${instanceId}' cannot be removed.`);
162+
logger.debug(`Orphan runner '${instanceId}' cannot be removed.`, LogFields.print());
154163
}
155164
}
156165

@@ -195,19 +204,25 @@ export async function scaleDown(): Promise<void> {
195204
// list and sort runners, newest first. This ensure we keep the newest runners longer.
196205
const ec2Runners = await listAndSortRunners(environment);
197206
const activeEc2RunnersCount = ec2Runners.length;
198-
logger.info(`Found: '${activeEc2RunnersCount}' active GitHub EC2 runner instances before clean-up.`);
207+
logger.info(
208+
`Found: '${activeEc2RunnersCount}' active GitHub EC2 runner instances before clean-up.`,
209+
LogFields.print(),
210+
);
199211

200212
if (activeEc2RunnersCount === 0) {
201-
logger.debug(`No active runners found for environment: '${environment}'`);
213+
logger.debug(`No active runners found for environment: '${environment}'`, LogFields.print());
202214
return;
203215
}
204216
const legacyRunners = filterLegacyRunners(ec2Runners);
205-
logger.debug(JSON.stringify(legacyRunners));
217+
logger.debug(JSON.stringify(legacyRunners), LogFields.print());
206218
const runners = filterRunners(ec2Runners);
207219

208220
await evaluateAndRemoveRunners(runners, scaleDownConfigs);
209221
await evaluateAndRemoveRunners(legacyRunners, scaleDownConfigs);
210222

211223
const activeEc2RunnersCountAfter = (await listAndSortRunners(environment)).length;
212-
logger.info(`Found: '${activeEc2RunnersCountAfter}' active GitHub EC2 runners instances after clean-up.`);
224+
logger.info(
225+
`Found: '${activeEc2RunnersCountAfter}' active GitHub EC2 runners instances after clean-up.`,
226+
LogFields.print(),
227+
);
213228
}

Diff for: modules/runners/lambdas/runners/src/scale-runners/scale-up.ts

+19-12
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { listEC2Runners, createRunner, RunnerInputParameters } from './runners';
22
import { createOctoClient, createGithubAppAuth, createGithubInstallationAuth } from './gh-auth';
33
import yn from 'yn';
44
import { Octokit } from '@octokit/rest';
5-
import { logger as rootLogger } from './logger';
5+
import { logger as rootLogger, LogFields } from './logger';
66

7-
const logger = rootLogger.getChildLogger();
7+
const logger = rootLogger.getChildLogger({ name: 'scale-up' });
88

99
export interface ActionRequestMessage {
1010
id: number;
@@ -23,7 +23,16 @@ export async function scaleUp(eventSource: string, payload: ActionRequestMessage
2323
const environment = process.env.ENVIRONMENT;
2424
const ghesBaseUrl = process.env.GHES_URL;
2525

26-
console.info(`Received ${payload.eventType} from ${payload.repositoryOwner}/${payload.repositoryName}`);
26+
const runnerType = enableOrgLevel ? 'Org' : 'Repo';
27+
const runnerOwner = enableOrgLevel ? payload.repositoryOwner : `${payload.repositoryOwner}/${payload.repositoryName}`;
28+
29+
LogFields.fields = {};
30+
LogFields.fields.runnerType = runnerType;
31+
LogFields.fields.runnerOwner = runnerOwner;
32+
LogFields.fields.event = payload.eventType;
33+
LogFields.fields.id = payload.id.toString();
34+
35+
logger.info(`Received event`, LogFields.print());
2736

2837
let ghesApiUrl = '';
2938
if (ghesBaseUrl) {
@@ -50,8 +59,6 @@ export async function scaleUp(eventSource: string, payload: ActionRequestMessage
5059

5160
const ghAuth = await createGithubInstallationAuth(installationId, ghesApiUrl);
5261
const githubInstallationClient = await createOctoClient(ghAuth.token, ghesApiUrl);
53-
const runnerType = enableOrgLevel ? 'Org' : 'Repo';
54-
const runnerOwner = enableOrgLevel ? payload.repositoryOwner : `${payload.repositoryOwner}/${payload.repositoryName}`;
5562

5663
const isQueued = await getJobStatus(githubInstallationClient, payload);
5764
if (isQueued) {
@@ -60,10 +67,10 @@ export async function scaleUp(eventSource: string, payload: ActionRequestMessage
6067
runnerType,
6168
runnerOwner,
6269
});
63-
logger.info(`${runnerType} ${runnerOwner} has ${currentRunners.length}/${maximumRunners} runners`);
70+
logger.info(`Current runners: ${currentRunners.length} of ${maximumRunners}`, LogFields.print());
6471

6572
if (currentRunners.length < maximumRunners) {
66-
console.info(`Attempting to launch a new runner`);
73+
logger.info(`Attempting to launch a new runner`, LogFields.print());
6774
// create token
6875
const registrationToken = enableOrgLevel
6976
? await githubInstallationClient.actions.createRegistrationTokenForOrg({ org: payload.repositoryOwner })
@@ -87,7 +94,7 @@ export async function scaleUp(eventSource: string, payload: ActionRequestMessage
8794
runnerType,
8895
});
8996
} else {
90-
logger.info('No runner will be created, maximum number of runners reached.');
97+
logger.info('No runner will be created, maximum number of runners reached.', LogFields.print());
9198
}
9299
}
93100
}
@@ -112,7 +119,7 @@ async function getJobStatus(githubInstallationClient: Octokit, payload: ActionRe
112119
throw Error(`Event ${payload.eventType} is not supported`);
113120
}
114121
if (!isQueued) {
115-
logger.info(`Job ${payload.id} is not queued`);
122+
logger.info(`Job not queued`, LogFields.print());
116123
}
117124
return isQueued;
118125
}
@@ -121,14 +128,14 @@ export async function createRunnerLoop(runnerParameters: RunnerInputParameters):
121128
const launchTemplateNames = process.env.LAUNCH_TEMPLATE_NAME?.split(',') as string[];
122129
let launched = false;
123130
for (let i = 0; i < launchTemplateNames.length; i++) {
124-
logger.info(`Attempt '${i}' to launch instance using ${launchTemplateNames[i]}.`);
131+
logger.info(`Attempt '${i}' to launch instance using ${launchTemplateNames[i]}.`, LogFields.print());
125132
try {
126133
await createRunner(runnerParameters, launchTemplateNames[i]);
127134
launched = true;
128135
break;
129136
} catch (error) {
130-
logger.debug(`Attempt '${i}' to launch instance using ${launchTemplateNames[i]} FAILED.`);
131-
logger.error(error);
137+
logger.debug(`Attempt '${i}' to launch instance using ${launchTemplateNames[i]} FAILED.`, LogFields.print());
138+
logger.error(error, LogFields.print());
132139
}
133140
}
134141
if (launched == false) {

0 commit comments

Comments
 (0)