Skip to content

Commit 55b2fa9

Browse files
ejsmithclaude
andcommitted
Clarify environment overrides and cover their edge cases
Document that an invalid per-event name does not fall back to the default, move the README guidance into the configuration section, test clearing an override, trimming control whitespace, and omitting an unset environment, and apply the lint rules from main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent b6a699a commit 55b2fa9

6 files changed

Lines changed: 51 additions & 14 deletions

File tree

‎.agents/skills/exceptionless-javascript/references/configuration.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ await Exceptionless.startup((config) => {
3131

3232
## Deployment environment
3333

34-
Set `config.environment = "production"` or call `config.setEnvironment("production")`. Per-event `setEnvironment("staging")` overrides the default. Names are trimmed and retain their supplied casing; empty names, names longer than 64 characters, and control characters are ignored. Missing values remain unspecified. The server filters case-insensitively and normalizes aggregation keys. This property is independent of `data.@environment` runtime metadata and of the application version. It does not change server stack grouping or create per-environment status.
34+
Set `config.environment = "production"` or call `config.setEnvironment("production")`. Per-event `setEnvironment("staging")` overrides the default. Names are trimmed and retain their supplied casing; empty names, names longer than 64 characters, and names with control characters are unspecified. An invalid per-event name stays unspecified instead of using the default; pass `null` or `undefined` to `setEnvironment` to use the default. Missing values remain unspecified. The server filters case-insensitively and normalizes aggregation keys. This property is independent of `data.@environment` runtime metadata and of the application version. It does not change server stack grouping or create per-environment status.
3535

3636
## Privacy
3737

‎README.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ The definition of the word exceptionless is: to be without exception. Exceptionl
88

99
## Browser
1010

11-
Set a deployment environment in startup configuration with `config.environment = "production"` (or `config.setEnvironment("production")`). Override it on an event with `Exceptionless.createLog("Example").setEnvironment("staging").submit()`. Names are trimmed and limited to 64 characters, preserving the supplied casing. The server filters case-insensitively and normalizes aggregation keys. Missing or invalid names remain unspecified. The top-level `environment` is separate from machine/runtime diagnostics in `data.@environment`; stacks and fixed versions remain shared across environments.
12-
1311
You can install the npm package via `npm install @exceptionless/browser --save`
1412
or via cdn [`https://unpkg.com/@exceptionless/browser`](https://unpkg.com/@exceptionless/browser).
1513
Next, you just need to call startup during your app's startup to automatically
@@ -158,6 +156,10 @@ await Exceptionless.startup((c) => {
158156
Please see the [docs](https://exceptionless.com/docs/clients/javascript/) for
159157
more information on configuring the client.
160158

159+
#### Deployment environments
160+
161+
Set a deployment environment in startup configuration with `config.environment = "production"` (or `config.setEnvironment("production")`). Override it on an event with `Exceptionless.createLog("Example").setEnvironment("staging").submit()`. Names are trimmed and limited to 64 characters, preserving the supplied casing. The server filters case-insensitively and normalizes aggregation keys. Missing or invalid names remain unspecified, and an invalid per-event name does not fall back to the configured default; pass `null` or `undefined` to `setEnvironment` to use the default. The top-level `environment` is separate from machine/runtime diagnostics in `data.@environment`; stacks and fixed versions remain shared across environments.
162+
161163
### Submitting Events and Errors
162164

163165
Once configured, Exceptionless will automatically submit any unhandled exceptions

‎packages/core/src/EventBuilder.ts‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,6 @@ import { isEmpty, stringify } from "./Utils.js";
77
import { EventPluginContext } from "./plugins/EventPluginContext.js";
88

99
export class EventBuilder {
10-
public setEnvironment(value: string | null | undefined): EventBuilder {
11-
if (value != null) {
12-
this.target.environment = value;
13-
} else {
14-
delete this.target.environment;
15-
}
16-
return this;
17-
}
18-
1910
public target: Event;
2011
public client: ExceptionlessClient;
2112
public context: EventContext;
@@ -71,6 +62,15 @@ export class EventBuilder {
7162
return this;
7263
}
7364

65+
public setEnvironment(value: string | null | undefined): EventBuilder {
66+
if (value != null) {
67+
this.target.environment = value;
68+
} else {
69+
delete this.target.environment;
70+
}
71+
return this;
72+
}
73+
7474
public setMessage(message: string | null | undefined): EventBuilder {
7575
if (message) {
7676
this.target.message = message;

‎packages/core/test/configuration/Configuration.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ describe("Configuration", () => {
1818
expect(config.environment).toBeUndefined();
1919
config.setEnvironment("prod\ninvalid");
2020
expect(config.environment).toBeUndefined();
21+
config.setEnvironment("\tProduction\n");
22+
expect(config.environment).toBe("Production");
2123
});
2224

2325
test("should override configuration defaults", () => {

‎packages/core/test/plugins/default/ConfigurationDefaultsPlugin.test.ts‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@ describe("ConfigurationDefaultsPlugin", () => {
1212
client.config.setEnvironment(" Production ");
1313
const plugin = new ConfigurationDefaultsPlugin();
1414
for (const type of ["error", "log", "usage", "session"]) {
15-
const event: Event = { type, data: { "@environment": { machine_name: "worker-1" } } };
15+
const event: Event = {
16+
type,
17+
data: {
18+
"@environment": {
19+
machine_name: "worker-1"
20+
}
21+
}
22+
};
1623
await plugin.run(new EventPluginContext(client, event, new EventContext()));
1724
expect(event.environment).toBe("Production");
1825
expect(event.data?.["@environment"]?.machine_name).toBe("worker-1");
@@ -32,6 +39,30 @@ describe("ConfigurationDefaultsPlugin", () => {
3239
expect(builder.target.environment).toBeUndefined();
3340
});
3441

42+
test.each([null, undefined])("should apply the default when the override is cleared with %j", async (environment) => {
43+
const client = new ExceptionlessClient();
44+
client.config.environment = "production";
45+
const builder = client.createLog("test", "message").setEnvironment("staging").setEnvironment(environment);
46+
await new ConfigurationDefaultsPlugin().run(new EventPluginContext(client, builder.target, new EventContext()));
47+
expect(builder.target.environment).toBe("production");
48+
});
49+
50+
test("should trim surrounding control whitespace from an override", async () => {
51+
const client = new ExceptionlessClient();
52+
client.config.environment = "production";
53+
const builder = client.createLog("test", "message").setEnvironment("\tStaging\n");
54+
await new ConfigurationDefaultsPlugin().run(new EventPluginContext(client, builder.target, new EventContext()));
55+
expect(builder.target.environment).toBe("Staging");
56+
});
57+
58+
test("should omit an unspecified environment from the serialized event", async () => {
59+
const client = new ExceptionlessClient();
60+
const builder = client.createLog("test", "message");
61+
await new ConfigurationDefaultsPlugin().run(new EventPluginContext(client, builder.target, new EventContext()));
62+
const serialized = JSON.parse(JSON.stringify(builder.target)) as Record<string, unknown>;
63+
expect(Object.keys(serialized)).not.toContain("environment");
64+
});
65+
3566
describe("should add default", () => {
3667
const userDataKey: string = "user";
3768
const user = {

‎packages/core/test/plugins/default/DuplicateCheckerPlugin.test.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ describe("DuplicateCheckerPlugin", () => {
6868
new EventContext()
6969
);
7070

71-
if (environment) context.event.environment = environment;
71+
if (environment) {
72+
context.event.environment = environment;
73+
}
7274
await plugin.run(context);
7375
return context;
7476
};

0 commit comments

Comments
 (0)