Skip to content

Commit

Permalink
WO-250 add workers-observability logs configuration (#7173)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
Ankcorn and Thomas Ankcorn authored Nov 14, 2024
1 parent b100713 commit b6cbfbd
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 8 deletions.
15 changes: 15 additions & 0 deletions .changeset/clever-grapes-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"wrangler": minor
---

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.

It also adds the following setting to the logs config

- `invocation_logs` - set to false to disable invocation logs. Defaults to true.

```toml
[observability.logs]
enabled = true
invocation_logs = false
```
49 changes: 48 additions & 1 deletion packages/wrangler/src/__tests__/configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,7 @@ describe("normalizeAndValidateConfig()", () => {
- Expected \\"logpush\\" to be of type boolean but got \\"INVALID\\".
- Expected \\"upload_source_maps\\" to be of type boolean but got \\"INVALID\\".
- Expected \\"observability.enabled\\" to be of type boolean but got \\"INVALID\\".
- Expected \\"observability.logs.enabled\\" to be of type boolean but got undefined.
- Expected \\"observability.head_sampling_rate\\" to be of type number but got \\"INVALID\\"."
`);
});
Expand Down Expand Up @@ -5485,11 +5486,57 @@ describe("normalizeAndValidateConfig()", () => {
expect(diagnostics.hasErrors()).toBe(true);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- \\"observability.enabled\\" is a required field.
- \\"observability.enabled\\" or \\"observability.logs.enabled\\" is required.
- Expected \\"observability.head_sampling_rate\\" to be of type number but got true."
`);
});

it("should error on invalid observability.logs", () => {
const { diagnostics } = normalizeAndValidateConfig(
{
observability: {
enabled: true,
logs: "enabled",
},
} as unknown as RawConfig,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
"
`);

expect(diagnostics.hasErrors()).toBe(true);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- Expected \\"observability.logs\\" to be of type object but got \\"enabled\\"."
`);
});

it("should not error on nested [observability.logs] config only", () => {
const { diagnostics } = normalizeAndValidateConfig(
{
observability: {
logs: {
enabled: true,
},
},
} as unknown as RawConfig,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
"
`);

expect(diagnostics.hasErrors()).toBe(false);
});
it("should error on a sampling rate out of range", () => {
const { diagnostics } = normalizeAndValidateConfig(
{
Expand Down
37 changes: 37 additions & 0 deletions packages/wrangler/src/__tests__/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11420,6 +11420,43 @@ export default{
`);
});

it("should allow uploading workers with nested observability logs setting", async () => {
writeWranglerToml({
observability: {
enabled: true,
head_sampling_rate: 0.5,
logs: {
enabled: true,
head_sampling_rate: 0.3,
invocation_logs: false,
},
},
});
await fs.promises.writeFile("index.js", `export default {};`);
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedObservability: {
enabled: true,
head_sampling_rate: 0.5,
logs: {
enabled: true,
head_sampling_rate: 0.3,
invocation_logs: false,
},
},
});

await runWrangler("publish index.js");
expect(std.out).toMatchInlineSnapshot(`
"Total Upload: xx KiB / gzip: xx KiB
Worker Startup Time: 100 ms
Uploaded test-name (TIMINGS)
Deployed test-name triggers (TIMINGS)
https://test-name.test-sub-domain.workers.dev
Current Version ID: Galaxy-Class"
`);
});

it("should disable observability if not explicitly defined", async () => {
writeWranglerToml({});
await fs.promises.writeFile("index.js", `export default {};`);
Expand Down
7 changes: 7 additions & 0 deletions packages/wrangler/src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -946,4 +946,11 @@ export interface Observability {
enabled: boolean;
/** The sampling rate */
head_sampling_rate?: number;
logs?: {
enabled: boolean;
/** The sampling rate */
head_sampling_rate?: number;
/** Set to false to disable invocation logs */
invocation_logs?: boolean;
};
}
37 changes: 37 additions & 0 deletions packages/wrangler/src/config/validation-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,43 @@ export const validateRequiredProperty = (
return true;
};

/**
* Validate that at least one of the properties in the list is required.
*/
export const validateAtLeastOnePropertyRequired = (
diagnostics: Diagnostics,
container: string,
properties: {
key: string;
value: unknown;
type: TypeofType;
}[]
): boolean => {
const containerPath = container ? `${container}.` : "";

if (properties.every((property) => property.value === undefined)) {
diagnostics.errors.push(
`${properties.map(({ key }) => `"${containerPath}${key}"`).join(" or ")} is required.`
);
return false;
}

const errors = [];
for (const prop of properties) {
if (typeof prop.value === prop.type) {
return true;
}
errors.push(
`Expected "${containerPath}${prop.key}" to be of type ${prop.type} but got ${JSON.stringify(
prop.value
)}.`
);
}

diagnostics.errors.push(...errors);
return false;
};

/**
* Validate that, if the optional field exists, then it has the expected type.
*/
Expand Down
67 changes: 60 additions & 7 deletions packages/wrangler/src/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
isValidName,
notInheritable,
validateAdditionalProperties,
validateAtLeastOnePropertyRequired,
validateOptionalProperty,
validateOptionalTypedArray,
validateRequiredProperty,
Expand Down Expand Up @@ -3341,14 +3342,22 @@ const validateObservability: ValidatorFn = (diagnostics, field, value) => {
const val = value as Observability;
let isValid = true;

/**
* One of observability.enabled or observability.logs.enabled must be defined
*/
isValid =
validateRequiredProperty(
diagnostics,
field,
"enabled",
val.enabled,
"boolean"
) && isValid;
validateAtLeastOnePropertyRequired(diagnostics, field, [
{
key: "enabled",
value: val.enabled,
type: "boolean",
},
{
key: "logs.enabled",
value: val.logs?.enabled,
type: "boolean",
},
]) && isValid;

isValid =
validateOptionalProperty(
Expand All @@ -3359,12 +3368,56 @@ const validateObservability: ValidatorFn = (diagnostics, field, value) => {
"number"
) && isValid;

isValid =
validateOptionalProperty(diagnostics, field, "logs", val.logs, "object") &&
isValid;

isValid =
validateAdditionalProperties(diagnostics, field, Object.keys(val), [
"enabled",
"head_sampling_rate",
"logs",
]) && isValid;

/**
* Validate the optional nested logs configuration
*/
if (typeof val.logs === "object") {
isValid =
validateRequiredProperty(
diagnostics,
field,
"logs.enabled",
val.logs.enabled,
"boolean"
) && isValid;

isValid =
validateOptionalProperty(
diagnostics,
field,
"logs.head_sampling_rate",
val.logs.head_sampling_rate,
"number"
) && isValid;

isValid =
validateOptionalProperty(
diagnostics,
field,
"logs.invocation_logs",
val.logs.invocation_logs,
"boolean"
) && isValid;

isValid =
validateAdditionalProperties(diagnostics, field, Object.keys(val.logs), [
"enabled",
"head_sampling_rate",
"invocation_logs",
]) && isValid;
}

const samplingRate = val?.head_sampling_rate;

if (samplingRate && (samplingRate < 0 || samplingRate > 1)) {
Expand Down

0 comments on commit b6cbfbd

Please sign in to comment.