Skip to content

Commit b6cbfbd

Browse files
AnkcornThomas Ankcorn
and
Thomas Ankcorn
authored
WO-250 add workers-observability logs configuration (#7173)
* WO-250 add workers-observability logs configuration * WO-250 correctly validate nested observability configuration * run lint fix * WO-250 addressing PR feedback to avoid mutation of argument --------- Co-authored-by: Thomas Ankcorn <[email protected]>
1 parent b100713 commit b6cbfbd

File tree

6 files changed

+204
-8
lines changed

6 files changed

+204
-8
lines changed

.changeset/clever-grapes-sparkle.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Adds [observability.logs] settings to wrangler. This setting lets developers control the settings for logs as an independent dataset enabling more dataset types in the future. The most specific setting will win if any of the datasets are not enabled.
6+
7+
It also adds the following setting to the logs config
8+
9+
- `invocation_logs` - set to false to disable invocation logs. Defaults to true.
10+
11+
```toml
12+
[observability.logs]
13+
enabled = true
14+
invocation_logs = false
15+
```

packages/wrangler/src/__tests__/configuration.test.ts

+48-1
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,7 @@ describe("normalizeAndValidateConfig()", () => {
11671167
- Expected \\"logpush\\" to be of type boolean but got \\"INVALID\\".
11681168
- Expected \\"upload_source_maps\\" to be of type boolean but got \\"INVALID\\".
11691169
- Expected \\"observability.enabled\\" to be of type boolean but got \\"INVALID\\".
1170+
- Expected \\"observability.logs.enabled\\" to be of type boolean but got undefined.
11701171
- Expected \\"observability.head_sampling_rate\\" to be of type number but got \\"INVALID\\"."
11711172
`);
11721173
});
@@ -5485,11 +5486,57 @@ describe("normalizeAndValidateConfig()", () => {
54855486
expect(diagnostics.hasErrors()).toBe(true);
54865487
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
54875488
"Processing wrangler configuration:
5488-
- \\"observability.enabled\\" is a required field.
5489+
- \\"observability.enabled\\" or \\"observability.logs.enabled\\" is required.
54895490
- Expected \\"observability.head_sampling_rate\\" to be of type number but got true."
54905491
`);
54915492
});
54925493

5494+
it("should error on invalid observability.logs", () => {
5495+
const { diagnostics } = normalizeAndValidateConfig(
5496+
{
5497+
observability: {
5498+
enabled: true,
5499+
logs: "enabled",
5500+
},
5501+
} as unknown as RawConfig,
5502+
undefined,
5503+
{ env: undefined }
5504+
);
5505+
5506+
expect(diagnostics.hasWarnings()).toBe(false);
5507+
expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
5508+
"Processing wrangler configuration:
5509+
"
5510+
`);
5511+
5512+
expect(diagnostics.hasErrors()).toBe(true);
5513+
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
5514+
"Processing wrangler configuration:
5515+
- Expected \\"observability.logs\\" to be of type object but got \\"enabled\\"."
5516+
`);
5517+
});
5518+
5519+
it("should not error on nested [observability.logs] config only", () => {
5520+
const { diagnostics } = normalizeAndValidateConfig(
5521+
{
5522+
observability: {
5523+
logs: {
5524+
enabled: true,
5525+
},
5526+
},
5527+
} as unknown as RawConfig,
5528+
undefined,
5529+
{ env: undefined }
5530+
);
5531+
5532+
expect(diagnostics.hasWarnings()).toBe(false);
5533+
expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
5534+
"Processing wrangler configuration:
5535+
"
5536+
`);
5537+
5538+
expect(diagnostics.hasErrors()).toBe(false);
5539+
});
54935540
it("should error on a sampling rate out of range", () => {
54945541
const { diagnostics } = normalizeAndValidateConfig(
54955542
{

packages/wrangler/src/__tests__/deploy.test.ts

+37
Original file line numberDiff line numberDiff line change
@@ -11420,6 +11420,43 @@ export default{
1142011420
`);
1142111421
});
1142211422

11423+
it("should allow uploading workers with nested observability logs setting", async () => {
11424+
writeWranglerToml({
11425+
observability: {
11426+
enabled: true,
11427+
head_sampling_rate: 0.5,
11428+
logs: {
11429+
enabled: true,
11430+
head_sampling_rate: 0.3,
11431+
invocation_logs: false,
11432+
},
11433+
},
11434+
});
11435+
await fs.promises.writeFile("index.js", `export default {};`);
11436+
mockSubDomainRequest();
11437+
mockUploadWorkerRequest({
11438+
expectedObservability: {
11439+
enabled: true,
11440+
head_sampling_rate: 0.5,
11441+
logs: {
11442+
enabled: true,
11443+
head_sampling_rate: 0.3,
11444+
invocation_logs: false,
11445+
},
11446+
},
11447+
});
11448+
11449+
await runWrangler("publish index.js");
11450+
expect(std.out).toMatchInlineSnapshot(`
11451+
"Total Upload: xx KiB / gzip: xx KiB
11452+
Worker Startup Time: 100 ms
11453+
Uploaded test-name (TIMINGS)
11454+
Deployed test-name triggers (TIMINGS)
11455+
https://test-name.test-sub-domain.workers.dev
11456+
Current Version ID: Galaxy-Class"
11457+
`);
11458+
});
11459+
1142311460
it("should disable observability if not explicitly defined", async () => {
1142411461
writeWranglerToml({});
1142511462
await fs.promises.writeFile("index.js", `export default {};`);

packages/wrangler/src/config/environment.ts

+7
Original file line numberDiff line numberDiff line change
@@ -946,4 +946,11 @@ export interface Observability {
946946
enabled: boolean;
947947
/** The sampling rate */
948948
head_sampling_rate?: number;
949+
logs?: {
950+
enabled: boolean;
951+
/** The sampling rate */
952+
head_sampling_rate?: number;
953+
/** Set to false to disable invocation logs */
954+
invocation_logs?: boolean;
955+
};
949956
}

packages/wrangler/src/config/validation-helpers.ts

+37
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,43 @@ export const validateRequiredProperty = (
434434
return true;
435435
};
436436

437+
/**
438+
* Validate that at least one of the properties in the list is required.
439+
*/
440+
export const validateAtLeastOnePropertyRequired = (
441+
diagnostics: Diagnostics,
442+
container: string,
443+
properties: {
444+
key: string;
445+
value: unknown;
446+
type: TypeofType;
447+
}[]
448+
): boolean => {
449+
const containerPath = container ? `${container}.` : "";
450+
451+
if (properties.every((property) => property.value === undefined)) {
452+
diagnostics.errors.push(
453+
`${properties.map(({ key }) => `"${containerPath}${key}"`).join(" or ")} is required.`
454+
);
455+
return false;
456+
}
457+
458+
const errors = [];
459+
for (const prop of properties) {
460+
if (typeof prop.value === prop.type) {
461+
return true;
462+
}
463+
errors.push(
464+
`Expected "${containerPath}${prop.key}" to be of type ${prop.type} but got ${JSON.stringify(
465+
prop.value
466+
)}.`
467+
);
468+
}
469+
470+
diagnostics.errors.push(...errors);
471+
return false;
472+
};
473+
437474
/**
438475
* Validate that, if the optional field exists, then it has the expected type.
439476
*/

packages/wrangler/src/config/validation.ts

+60-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
isValidName,
2525
notInheritable,
2626
validateAdditionalProperties,
27+
validateAtLeastOnePropertyRequired,
2728
validateOptionalProperty,
2829
validateOptionalTypedArray,
2930
validateRequiredProperty,
@@ -3341,14 +3342,22 @@ const validateObservability: ValidatorFn = (diagnostics, field, value) => {
33413342
const val = value as Observability;
33423343
let isValid = true;
33433344

3345+
/**
3346+
* One of observability.enabled or observability.logs.enabled must be defined
3347+
*/
33443348
isValid =
3345-
validateRequiredProperty(
3346-
diagnostics,
3347-
field,
3348-
"enabled",
3349-
val.enabled,
3350-
"boolean"
3351-
) && isValid;
3349+
validateAtLeastOnePropertyRequired(diagnostics, field, [
3350+
{
3351+
key: "enabled",
3352+
value: val.enabled,
3353+
type: "boolean",
3354+
},
3355+
{
3356+
key: "logs.enabled",
3357+
value: val.logs?.enabled,
3358+
type: "boolean",
3359+
},
3360+
]) && isValid;
33523361

33533362
isValid =
33543363
validateOptionalProperty(
@@ -3359,12 +3368,56 @@ const validateObservability: ValidatorFn = (diagnostics, field, value) => {
33593368
"number"
33603369
) && isValid;
33613370

3371+
isValid =
3372+
validateOptionalProperty(diagnostics, field, "logs", val.logs, "object") &&
3373+
isValid;
3374+
33623375
isValid =
33633376
validateAdditionalProperties(diagnostics, field, Object.keys(val), [
33643377
"enabled",
33653378
"head_sampling_rate",
3379+
"logs",
33663380
]) && isValid;
33673381

3382+
/**
3383+
* Validate the optional nested logs configuration
3384+
*/
3385+
if (typeof val.logs === "object") {
3386+
isValid =
3387+
validateRequiredProperty(
3388+
diagnostics,
3389+
field,
3390+
"logs.enabled",
3391+
val.logs.enabled,
3392+
"boolean"
3393+
) && isValid;
3394+
3395+
isValid =
3396+
validateOptionalProperty(
3397+
diagnostics,
3398+
field,
3399+
"logs.head_sampling_rate",
3400+
val.logs.head_sampling_rate,
3401+
"number"
3402+
) && isValid;
3403+
3404+
isValid =
3405+
validateOptionalProperty(
3406+
diagnostics,
3407+
field,
3408+
"logs.invocation_logs",
3409+
val.logs.invocation_logs,
3410+
"boolean"
3411+
) && isValid;
3412+
3413+
isValid =
3414+
validateAdditionalProperties(diagnostics, field, Object.keys(val.logs), [
3415+
"enabled",
3416+
"head_sampling_rate",
3417+
"invocation_logs",
3418+
]) && isValid;
3419+
}
3420+
33683421
const samplingRate = val?.head_sampling_rate;
33693422

33703423
if (samplingRate && (samplingRate < 0 || samplingRate > 1)) {

0 commit comments

Comments
 (0)