Skip to content

Commit eb08494

Browse files
authored
Merge branch 'dev' into fix/lingering-list-references
2 parents 75813dc + dd8c424 commit eb08494

3 files changed

Lines changed: 65 additions & 1 deletion

File tree

packages/opencode/src/control-plane/workspace.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export const create = fn(CreateInput, async (input) => {
117117
OPENCODE_EXPERIMENTAL_WORKSPACES: "true",
118118
OTEL_EXPORTER_OTLP_HEADERS: process.env.OTEL_EXPORTER_OTLP_HEADERS,
119119
OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
120+
OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES,
120121
}
121122
await adaptor.create(config, env)
122123

packages/opencode/src/effect/observability.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,29 @@ const headers = Flag.OTEL_EXPORTER_OTLP_HEADERS
2121
)
2222
: undefined
2323

24-
function resource() {
24+
export function resource(): { serviceName: string; serviceVersion: string; attributes: Record<string, string> } {
2525
const processMetadata = ensureProcessMetadata("main")
26+
const attributes: Record<string, string> = (() => {
27+
const value = process.env.OTEL_RESOURCE_ATTRIBUTES
28+
if (!value) return {}
29+
try {
30+
return Object.fromEntries(
31+
value.split(",").map((entry) => {
32+
const index = entry.indexOf("=")
33+
if (index < 1) throw new Error("Invalid OTEL_RESOURCE_ATTRIBUTES entry")
34+
return [decodeURIComponent(entry.slice(0, index)), decodeURIComponent(entry.slice(index + 1))]
35+
}),
36+
)
37+
} catch {
38+
return {}
39+
}
40+
})()
41+
2642
return {
2743
serviceName: "opencode",
2844
serviceVersion: InstallationVersion,
2945
attributes: {
46+
...attributes,
3047
"deployment.environment.name": InstallationChannel,
3148
"opencode.client": Flag.OPENCODE_CLIENT,
3249
"opencode.process_role": processMetadata.processRole,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { afterEach, describe, expect, test } from "bun:test"
2+
import { resource } from "../../src/effect/observability"
3+
4+
const otelResourceAttributes = process.env.OTEL_RESOURCE_ATTRIBUTES
5+
const opencodeClient = process.env.OPENCODE_CLIENT
6+
7+
afterEach(() => {
8+
if (otelResourceAttributes === undefined) delete process.env.OTEL_RESOURCE_ATTRIBUTES
9+
else process.env.OTEL_RESOURCE_ATTRIBUTES = otelResourceAttributes
10+
11+
if (opencodeClient === undefined) delete process.env.OPENCODE_CLIENT
12+
else process.env.OPENCODE_CLIENT = opencodeClient
13+
})
14+
15+
describe("resource", () => {
16+
test("parses and decodes OTEL resource attributes", () => {
17+
process.env.OTEL_RESOURCE_ATTRIBUTES =
18+
"service.namespace=anomalyco,team=platform%2Cobservability,label=hello%3Dworld,key%2Fname=value%20here"
19+
20+
expect(resource().attributes).toMatchObject({
21+
"service.namespace": "anomalyco",
22+
team: "platform,observability",
23+
label: "hello=world",
24+
"key/name": "value here",
25+
})
26+
})
27+
28+
test("drops OTEL resource attributes when any entry is invalid", () => {
29+
process.env.OTEL_RESOURCE_ATTRIBUTES = "service.namespace=anomalyco,broken"
30+
31+
expect(resource().attributes["service.namespace"]).toBeUndefined()
32+
expect(resource().attributes["opencode.client"]).toBeDefined()
33+
})
34+
35+
test("keeps built-in attributes when env values conflict", () => {
36+
process.env.OPENCODE_CLIENT = "cli"
37+
process.env.OTEL_RESOURCE_ATTRIBUTES =
38+
"opencode.client=web,service.instance.id=override,service.namespace=anomalyco"
39+
40+
expect(resource().attributes).toMatchObject({
41+
"opencode.client": "cli",
42+
"service.namespace": "anomalyco",
43+
})
44+
expect(resource().attributes["service.instance.id"]).not.toBe("override")
45+
})
46+
})

0 commit comments

Comments
 (0)