Skip to content

Commit a20e424

Browse files
authored
chore: enrich and improve telemetry events (#29)
- added `auth_token` to events after `auth_token_configured` (read it from .localstack/auth.json file when possible) - retry info endpoint [1] to improve `session_id` capture rate for `emulator.started` events - removed `expected_steps` from `setup_started` event - renamed `position` to `step_order` - changed `SCHEMA_VERSION`: 1 -> 2 - added `license_setup_ended` event - changed steps for "setup_ended" event - capture "setup_ended" when user cancels - capture "skipped" events better. ------ [1] We were not capturing the emulator session_id quite well in the emulator.started events. We rely on endpoint http://localhost:4566/_localstack/info to grab it, however the endpoint is not immediately available when we just start localstack (needs to be in running state). It needs a couple of seconds before it's accessible (some 4-6 seconds in my local tests). Now we retry up to 10x and wait 1s each time before sending the event to make sure we get the session_id as often as possible.
1 parent 6dd75db commit a20e424

File tree

6 files changed

+273
-96
lines changed

6 files changed

+273
-96
lines changed

src/plugins/setup.ts

Lines changed: 111 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
checkIsAuthenticated,
66
requestAuthentication,
77
saveAuthToken,
8+
readAuthToken,
89
} from "../utils/authenticate.ts";
910
import { configureAwsProfiles } from "../utils/configure-aws.ts";
1011
import { runInstallProcess } from "../utils/install.ts";
@@ -15,6 +16,7 @@ import {
1516
} from "../utils/license.ts";
1617
import { minDelay } from "../utils/promises.ts";
1718
import { updateDockerImage } from "../utils/setup.ts";
19+
import { get_setup_ended } from "../utils/telemetry.ts";
1820

1921
export default createPlugin(
2022
"setup",
@@ -38,26 +40,6 @@ export default createPlugin(
3840
payload: {
3941
namespace: "onboarding",
4042
origin: origin_trigger,
41-
expected_steps: [
42-
{
43-
name: "emulator_installed",
44-
is_first_step: true,
45-
is_last_step: false,
46-
position: 1,
47-
},
48-
{
49-
name: "auth_token_configured",
50-
is_first_step: false,
51-
is_last_step: false,
52-
position: 2,
53-
},
54-
{
55-
name: "aws_profile_configured",
56-
is_first_step: false,
57-
is_last_step: true,
58-
position: 3,
59-
},
60-
],
6143
},
6244
});
6345

@@ -69,27 +51,39 @@ export default createPlugin(
6951
},
7052
async (progress, cancellationToken) => {
7153
/////////////////////////////////////////////////////////////////////
54+
let cliStatus: "COMPLETED" | "SKIPPED" = "COMPLETED";
55+
let authenticationStatus: "COMPLETED" | "SKIPPED" = "COMPLETED";
7256
{
7357
const installationStartedAt = new Date().toISOString();
74-
const { cancelled } = await runInstallProcess(
58+
const { cancelled, skipped } = await runInstallProcess(
7559
progress,
7660
cancellationToken,
7761
outputChannel,
7862
telemetry,
7963
origin_trigger,
8064
);
65+
cliStatus = skipped === true ? "SKIPPED" : "COMPLETED";
8166
if (cancelled || cancellationToken.isCancellationRequested) {
8267
telemetry.track({
8368
name: "emulator_installed",
8469
payload: {
8570
namespace: "onboarding",
8671
origin: origin_trigger,
87-
position: 1,
72+
step_order: 1,
8873
started_at: installationStartedAt,
8974
ended_at: new Date().toISOString(),
9075
status: "CANCELLED",
9176
},
9277
});
78+
telemetry.track(
79+
get_setup_ended(
80+
cliStatus,
81+
"SKIPPED",
82+
"SKIPPED",
83+
"SKIPPED",
84+
"CANCELLED",
85+
),
86+
);
9387
return;
9488
}
9589
}
@@ -110,30 +104,45 @@ export default createPlugin(
110104
const authenticated = await minDelay(checkIsAuthenticated());
111105
if (cancellationToken.isCancellationRequested) {
112106
telemetry.track({
113-
name: "setup_ended",
107+
name: "auth_token_configured",
114108
payload: {
115109
namespace: "onboarding",
116-
steps: [1, 2, 3],
110+
origin: origin_trigger,
111+
step_order: 2,
112+
started_at: authStartedAuthAt,
113+
ended_at: new Date().toISOString(),
117114
status: "CANCELLED",
118115
},
119116
});
117+
telemetry.track(
118+
get_setup_ended(
119+
cliStatus,
120+
"CANCELLED",
121+
"SKIPPED",
122+
"SKIPPED",
123+
"CANCELLED",
124+
await readAuthToken(),
125+
),
126+
);
120127
return;
121128
}
122129
if (authenticated) {
123130
progress.report({
124131
message: "Skipping authentication...",
125132
});
133+
authenticationStatus = "SKIPPED";
126134
telemetry.track({
127135
name: "auth_token_configured",
128136
payload: {
129137
namespace: "onboarding",
130138
origin: origin_trigger,
131-
position: 2,
139+
step_order: 2,
132140
started_at: authStartedAuthAt,
133141
ended_at: new Date().toISOString(),
134142
status: "SKIPPED",
135143
},
136144
});
145+
137146
await minDelay(Promise.resolve());
138147
} else {
139148
/////////////////////////////////////////////////////////////////////
@@ -153,13 +162,23 @@ export default createPlugin(
153162
payload: {
154163
namespace: "onboarding",
155164
origin: origin_trigger,
156-
position: 2,
165+
step_order: 2,
157166
auth_token: authToken,
158167
started_at: authStartedAuthAt,
159168
ended_at: new Date().toISOString(),
160169
status: "CANCELLED",
161170
},
162171
});
172+
telemetry.track(
173+
get_setup_ended(
174+
cliStatus,
175+
"CANCELLED",
176+
"SKIPPED",
177+
"SKIPPED",
178+
"CANCELLED",
179+
await readAuthToken(),
180+
),
181+
);
163182
return;
164183
}
165184

@@ -174,14 +193,23 @@ export default createPlugin(
174193
payload: {
175194
namespace: "onboarding",
176195
origin: origin_trigger,
177-
position: 2,
196+
step_order: 2,
178197
auth_token: authToken,
179198
started_at: authStartedAuthAt,
180199
ended_at: new Date().toISOString(),
181200
status: "CANCELLED",
182201
},
183202
});
184-
203+
telemetry.track(
204+
get_setup_ended(
205+
cliStatus,
206+
"CANCELLED",
207+
"SKIPPED",
208+
"SKIPPED",
209+
"CANCELLED",
210+
authToken,
211+
),
212+
);
185213
return;
186214
}
187215
}
@@ -193,6 +221,7 @@ export default createPlugin(
193221
// then there will be no license info to be reported by `localstack license info`.
194222
// Also, an expired license could be cached.
195223
// Activating the license pre-emptively to know its state during the setup process.
224+
const licenseCheckStartedAt = new Date().toISOString();
196225
const licenseIsValid = await minDelay(
197226
activateLicense(outputChannel).then(() =>
198227
checkIsLicenseValid(outputChannel),
@@ -213,10 +242,43 @@ export default createPlugin(
213242
}
214243

215244
if (cancellationToken.isCancellationRequested) {
245+
telemetry.track({
246+
name: "license_setup_ended",
247+
payload: {
248+
namespace: "onboarding",
249+
step_order: 3,
250+
origin: origin_trigger,
251+
auth_token: await readAuthToken(),
252+
started_at: licenseCheckStartedAt,
253+
ended_at: new Date().toISOString(),
254+
status: "CANCELLED",
255+
},
256+
});
257+
telemetry.track(
258+
get_setup_ended(
259+
cliStatus,
260+
authenticationStatus,
261+
"CANCELLED",
262+
"SKIPPED",
263+
"CANCELLED",
264+
await readAuthToken(),
265+
),
266+
);
216267
return;
217268
}
218269

219-
//TODO add telemetry
270+
telemetry.track({
271+
name: "license_setup_ended",
272+
payload: {
273+
namespace: "onboarding",
274+
step_order: 3,
275+
origin: origin_trigger,
276+
auth_token: await readAuthToken(),
277+
started_at: licenseCheckStartedAt,
278+
ended_at: new Date().toISOString(),
279+
status: "COMPLETED",
280+
},
281+
});
220282

221283
/////////////////////////////////////////////////////////////////////
222284
progress.report({
@@ -245,14 +307,16 @@ export default createPlugin(
245307
}
246308

247309
if (cancellationToken.isCancellationRequested) {
248-
telemetry.track({
249-
name: "setup_ended",
250-
payload: {
251-
namespace: "onboarding",
252-
steps: [1, 2, 3],
253-
status: "CANCELLED",
254-
},
255-
});
310+
telemetry.track(
311+
get_setup_ended(
312+
cliStatus,
313+
authenticationStatus,
314+
"COMPLETED",
315+
"COMPLETED",
316+
"CANCELLED",
317+
await readAuthToken(),
318+
),
319+
);
256320
return;
257321
}
258322

@@ -281,14 +345,16 @@ export default createPlugin(
281345
});
282346
}
283347

284-
telemetry.track({
285-
name: "setup_ended",
286-
payload: {
287-
namespace: "onboarding",
288-
steps: [1, 2, 3],
289-
status: "COMPLETED",
290-
},
291-
});
348+
telemetry.track(
349+
get_setup_ended(
350+
cliStatus,
351+
authenticationStatus,
352+
"COMPLETED",
353+
"COMPLETED",
354+
"COMPLETED",
355+
await readAuthToken(),
356+
),
357+
);
292358
},
293359
);
294360
},

src/utils/authenticate.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -124,34 +124,39 @@ export async function saveAuthToken(
124124
}
125125
}
126126

127-
/**
128-
* Checks if the user is authenticated by validating the stored auth token.
129-
*
130-
* License is validated separately
131-
*
132-
* @returns boolean indicating if the authentication is valid
133-
*/
134-
export async function checkIsAuthenticated() {
127+
function isAuthTokenPresent(authObject: unknown) {
128+
return (
129+
typeof authObject === "object" &&
130+
authObject !== null &&
131+
AUTH_TOKEN_KEY in authObject
132+
);
133+
}
134+
135+
// Reads the auth token from the auth.json file for logging in the user
136+
export async function readAuthToken(): Promise<string> {
135137
try {
136138
const authJson = await fs.readFile(LOCALSTACK_AUTH_FILENAME, "utf-8");
137139
const authObject = JSON.parse(authJson) as unknown;
138140
if (!isAuthTokenPresent(authObject)) {
139-
return false;
141+
return "";
140142
}
141143
const authToken = authObject[AUTH_TOKEN_KEY];
142144
if (typeof authToken !== "string") {
143-
return false;
145+
return "";
144146
}
145-
return true;
146-
} catch (error) {
147-
return false;
147+
return authToken;
148+
} catch {
149+
return "";
148150
}
149151
}
150152

151-
function isAuthTokenPresent(authObject: unknown) {
152-
return (
153-
typeof authObject === "object" &&
154-
authObject !== null &&
155-
AUTH_TOKEN_KEY in authObject
156-
);
153+
/**
154+
* Checks if the user is authenticated by validating the stored auth token.
155+
*
156+
* License is validated separately
157+
*
158+
* @returns boolean indicating if the authentication is valid
159+
*/
160+
export async function checkIsAuthenticated() {
161+
return (await readAuthToken()) !== "";
157162
}

0 commit comments

Comments
 (0)