Skip to content

Commit 2ec5140

Browse files
authored
Add additional Open Telemetry attributes (and adhere to canonical attribute naming) (#9700)
Co-authored-by: Daniel Cousens <[email protected]>
1 parent a4a5da6 commit 2ec5140

File tree

4 files changed

+52
-28
lines changed

4 files changed

+52
-28
lines changed

examples/logging-opentelemetry/keystone.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@ export default config<TypeInfo>({
2828

2929
return {
3030
async willSendResponse({ operation, request }) {
31-
span.setAttribute('type', operation?.operation || 'unknown')
32-
span.setAttribute('name', request.operationName || 'unknown')
33-
span.setAttribute('hash', request.query ? sha256(request.query) : 'empty')
34-
// span.setAttribute('query', request.query?.replaceAll(/\s+/g, ' ') || '') // WARNING: verbose
31+
span.setAttribute('graphql.operation.name', operation?.operation || 'unknown')
32+
span.setAttribute('graphql.operation.type', request.operationName || 'unknown')
33+
span.setAttribute(
34+
'graphql.document.sha256',
35+
request.query ? sha256(request.query) : 'empty'
36+
)
37+
// span.setAttribute('graphql.document', request.query?.replaceAll(/\s+/g, ' ') || '') // WARNING: verbose
3538
span.end()
3639
},
3740
}
@@ -47,9 +50,9 @@ export default config<TypeInfo>({
4750
'http request',
4851
{
4952
attributes: {
50-
method: req.method,
51-
path: req.path,
52-
userAgent: req.headers['user-agent'] || '',
53+
'http.request.method': req.method,
54+
'http.request.path': req.path,
55+
'user_agent.original': req.headers['user-agent'] || '',
5356
},
5457
},
5558
span => {

packages/core/src/lib/core/mutations/index.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ async function createSingle__(
7979
) {
8080
return await withSpan(
8181
`create ${list.graphql.names.outputTypeNameLower}`,
82-
async () => {
82+
async span => {
8383
// throw an accessDeniedError if not allowed
8484
await enforceListLevelAccessControl(context, 'create', list, inputData, undefined)
8585
await enforceFieldLevelAccessControl(context, 'create', list, inputData, undefined)
@@ -94,13 +94,17 @@ async function createSingle__(
9494
await beforeOperation()
9595

9696
// operation
97-
const item = await context.prisma[list.listKey].create({
97+
const result = await context.prisma[list.listKey].create({
9898
data: list.isSingleton ? { ...data, id: 1 } : data,
9999
})
100100

101-
return { item, afterOperation }
101+
span.setAttribute('keystone.result.id', result?.id ?? '')
102+
return { item: result, afterOperation }
102103
},
103-
{ 'keystone.list': list.listKey, 'keystone.operation': 'create' }
104+
{
105+
'keystone.list': list.listKey,
106+
'keystone.operation': 'create',
107+
}
104108
)
105109
}
106110

@@ -143,7 +147,7 @@ async function updateSingle__(
143147
) {
144148
return await withSpan(
145149
`update ${list.graphql.names.outputTypeNameLower}`,
146-
async () => {
150+
async span => {
147151
// validate and resolve the input filter
148152
const uniqueWhere = await resolveUniqueWhereInput(where, list, context)
149153

@@ -164,15 +168,16 @@ async function updateSingle__(
164168
await beforeOperation()
165169

166170
// operation
167-
const updatedItem = await context.prisma[list.listKey].update({
171+
const result = await context.prisma[list.listKey].update({
168172
where: { id: item.id },
169173
data,
170174
})
175+
span.setAttribute('keystone.result.id', result?.id ?? '')
171176

172177
// after operation
173-
await afterOperation(updatedItem)
178+
await afterOperation(result)
174179

175-
return updatedItem
180+
return result
176181
},
177182
{ 'keystone.list': list.listKey, 'keystone.operation': 'update' }
178183
)
@@ -186,7 +191,7 @@ async function deleteSingle__(
186191
) {
187192
return await withSpan(
188193
`delete ${list.graphql.names.outputTypeNameLower}`,
189-
async () => {
194+
async span => {
190195
// validate and resolve the input filter
191196
const uniqueWhere = await resolveUniqueWhereInput(where, list, context)
192197

@@ -214,6 +219,7 @@ async function deleteSingle__(
214219

215220
// operation
216221
const result = await context.prisma[list.listKey].delete({ where: { id: item.id } })
222+
span.setAttribute('keystone.result.id', result?.id ?? '')
217223

218224
// after operation
219225
await runSideEffectOnlyHook(list, 'afterOperation', {
@@ -236,7 +242,7 @@ async function actionSingle__(
236242
) {
237243
return await withSpan(
238244
action.otel,
239-
async () => {
245+
async span => {
240246
// no before operation hook for actions
241247

242248
// operation
@@ -248,6 +254,7 @@ async function actionSingle__(
248254
},
249255
context
250256
)
257+
span.setAttribute('keystone.result.id', (result?.id as string) ?? '')
251258

252259
// no after operation hook for actions
253260
return result

packages/core/src/lib/utils.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
/**
2-
* Turns a passed in string into a human readable label
3-
* @param {String} str The string to convert.
4-
* @returns The new string
5-
*/
6-
export function humanize(str: string, capitalize: boolean = true) {
7-
str = str.replace(/[^a-zA-Z0-9]+/g, ' ').replace(/([a-z0-9])([A-Z]+)/g, '$1 $2')
1+
// WARNING: this is an opinionated subject, with too many ways to do this
2+
// we went with a subset that we are happy with
3+
// see tests2/utils.test.ts for examples
4+
export function humanize(s: string, capitalize: boolean = true) {
5+
// drop non-alphanumeric
6+
s = s.replace(/[^a-zA-Z0-9]+/g, ' ')
87

9-
if (!capitalize) return str
10-
return str
8+
// insert spaces before camels of length > 1
9+
for (let i = 0; i < 24; i++) {
10+
// not unbounded, shouldnt happen
11+
const next = s.replace(/([a-z0-9])([A-Z][A-Za-z0-9])/, '$1 $2')
12+
if (next === s) break
13+
s = next
14+
}
15+
16+
if (!capitalize) return s
17+
return s
1118
.split(' ')
1219
.map(x => x.charAt(0).toUpperCase() + x.slice(1))
1320
.join(' ')

tests2/utils.test.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,19 @@ describe('utils', () => {
1111
assert.equal(humanize('kebab-case'), 'Kebab Case')
1212
assert.equal(humanize('multiple words here'), 'Multiple Words Here')
1313
assert.equal(humanize('Multiple Words Here'), 'Multiple Words Here')
14-
assert.equal(humanize('Thing42WithOther43'), 'Thing42 With Other43')
15-
assert.equal(humanize('Thing42_withOther43', false), 'Thing42 with Other43')
1614
assert.equal(humanize('foo'), 'Foo')
1715
assert.equal(humanize('Foo'), 'Foo')
1816
assert.equal(humanize('fooBar'), 'Foo Bar')
1917
assert.equal(humanize('FooBar'), 'Foo Bar')
2018
assert.equal(humanize('Foo Bar'), 'Foo Bar')
19+
assert.equal(humanize('Foo11WithBar11'), 'Foo11 With Bar11')
20+
assert.equal(humanize('Foo1A_WithBar11'), 'Foo1A With Bar11')
21+
assert.equal(humanize('Foo1AA_WithBar11'), 'Foo1 AA With Bar11')
22+
assert.equal(humanize('Foo11_WithBar11'), 'Foo11 With Bar11')
23+
assert.equal(humanize('Foo11_WithBar11A'), 'Foo11 With Bar11A')
24+
assert.equal(humanize('Foo11_WithBar11AA'), 'Foo11 With Bar11 AA')
25+
assert.equal(humanize('Foo11_withBar11', false), 'Foo11 with Bar11')
26+
assert.equal(humanize('FOO1A_BAR11'), 'FOO1A BAR11')
27+
assert.equal(humanize('FOO1A1_BAR11'), 'FOO1 A1 BAR11')
2128
})
2229
})

0 commit comments

Comments
 (0)