Skip to content

Commit 7576742

Browse files
committed
feat(koa): Adds support to ignore a span by its layer name
1 parent 6e1cd53 commit 7576742

File tree

5 files changed

+134
-44
lines changed

5 files changed

+134
-44
lines changed

plugins/node/opentelemetry-instrumentation-koa/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Note that generator-based middleware are deprecated and won't be instrumented.
5454
| Options | Type | Example | Description |
5555
| ------------------ | ----------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------- |
5656
| `ignoreLayersType` | `KoaLayerType[]` | `['middleware']` | Ignore layers of specified type. |
57-
| `ignoreLayersName` | `string[]` | `['logger']` | Ignore layers with specified names. |
57+
| `ignoreLayers` | `IgnoreMatcher[]` | `['logger', /router/]` | Ignore layers with specified names. |
5858
| `requestHook` | `KoaRequestCustomAttributeFunction` | `(span, info) => {}` | Function for adding custom attributes to Koa middleware layers. Receives params: `Span, KoaRequestInfo`. |
5959

6060
`ignoreLayersType` accepts an array of `KoaLayerType` which can take the following string values:

plugins/node/opentelemetry-instrumentation-koa/src/instrumentation.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
2929
import {
3030
getMiddlewareMetadata,
3131
isLayerIgnored,
32-
isLayerNameIgnored,
3332
} from './utils';
3433
import { getRPCMetadata, RPCType } from '@opentelemetry/core';
3534
import {
@@ -167,7 +166,7 @@ export class KoaInstrumentation extends InstrumentationBase<KoaInstrumentationCo
167166
layerPath
168167
);
169168

170-
if (isLayerNameIgnored(metadata.layerName, this.getConfig())) {
169+
if (isLayerIgnored(layerType, this.getConfig(), metadata.name)) {
171170
return middlewareLayer(context, next);
172171
}
173172

plugins/node/opentelemetry-instrumentation-koa/src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@ export interface KoaInstrumentationConfig<
7070
/** Ignore specific layers based on their type */
7171
ignoreLayersType?: KoaLayerType[];
7272
/** Ignore specific layers based on their name */
73-
ignoreLayersName?: string[];
73+
ignoreLayers?: IgnoreMatcher[];
7474
/** Function for adding custom attributes to each middleware layer span */
7575
requestHook?: KoaRequestCustomAttributeFunction<
7676
KoaContextType,
7777
KoaMiddlewareType
7878
>;
7979
}
80+
81+
export type IgnoreMatcher = string | RegExp | ((url: string) => boolean);

plugins/node/opentelemetry-instrumentation-koa/src/utils.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { KoaLayerType, KoaInstrumentationConfig } from './types';
16+
import { KoaLayerType, KoaInstrumentationConfig, IgnoreMatcher } from './types';
1717
import { KoaContext, KoaMiddleware } from './internal-types';
1818
import { AttributeNames } from './enums/AttributeNames';
1919
import { Attributes } from '@opentelemetry/api';
@@ -27,7 +27,6 @@ export const getMiddlewareMetadata = (
2727
): {
2828
attributes: Attributes;
2929
name: string;
30-
layerName: string;
3130
} => {
3231
if (isRouter) {
3332
return {
@@ -36,17 +35,15 @@ export const getMiddlewareMetadata = (
3635
[AttributeNames.KOA_TYPE]: KoaLayerType.ROUTER,
3736
[SEMATTRS_HTTP_ROUTE]: layerPath?.toString(),
3837
},
39-
name: context._matchedRouteName || `router - ${layerPath}`,
40-
layerName: context._matchedRouteName || layerPath?.toString() || '',
38+
name: context._matchedRouteName || `router - ${layerPath}`
4139
};
4240
} else {
4341
return {
4442
attributes: {
4543
[AttributeNames.KOA_NAME]: layer.name ?? 'middleware',
4644
[AttributeNames.KOA_TYPE]: KoaLayerType.MIDDLEWARE,
4745
},
48-
name: `middleware - ${layer.name}`,
49-
layerName: layer.name,
46+
name: `middleware - ${layer.name}`
5047
};
5148
}
5249
};
@@ -59,26 +56,48 @@ export const getMiddlewareMetadata = (
5956
*/
6057
export const isLayerIgnored = (
6158
type: KoaLayerType,
62-
config?: KoaInstrumentationConfig
59+
config?: KoaInstrumentationConfig,
60+
name?: string
6361
): boolean => {
64-
return !!(
62+
if (
6563
Array.isArray(config?.ignoreLayersType) &&
6664
config?.ignoreLayersType?.includes(type)
67-
);
65+
) {
66+
return true;
67+
}
68+
if (Array.isArray(config?.ignoreLayers) === false || !config?.ignoreLayers ) return false;
69+
try {
70+
for (const pattern of config.ignoreLayers) {
71+
if (satisfiesPattern(name!, pattern)) {
72+
return true;
73+
}
74+
}
75+
} catch (e) {
76+
/* catch block*/
77+
}
78+
79+
return false;
6880
};
6981

7082
/**
71-
* Check whether the given request name is ignored by configuration
72-
* @param [list] List of ignore name patterns
73-
* @param [onException] callback for doing something when an exception has
74-
* occurred
83+
* Check whether the given obj match pattern
84+
* @param constant e.g URL of request
85+
* @param obj obj to inspect
86+
* @param pattern Match pattern
7587
*/
76-
export const isLayerNameIgnored = (
77-
layerName: string,
78-
config?: KoaInstrumentationConfig
88+
export const satisfiesPattern = (
89+
constant: string,
90+
pattern: IgnoreMatcher
7991
): boolean => {
80-
return !!(
81-
Array.isArray(config?.ignoreLayersName) &&
82-
config?.ignoreLayersName?.includes(layerName)
83-
);
92+
console.warn(`constant: ${constant}`);
93+
console.warn(`pattern: ${pattern}`);
94+
if (typeof pattern === 'string') {
95+
return pattern === constant;
96+
} else if (pattern instanceof RegExp) {
97+
return pattern.test(constant);
98+
} else if (typeof pattern === 'function') {
99+
return pattern(constant);
100+
} else {
101+
throw new TypeError('Pattern is in unsupported datatype');
102+
}
84103
};

plugins/node/opentelemetry-instrumentation-koa/test/utils.test.ts

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import * as utils from '../src/utils';
1818
import * as assert from 'assert';
19-
import { KoaInstrumentationConfig, KoaLayerType } from '../src/types';
19+
import { IgnoreMatcher, KoaInstrumentationConfig, KoaLayerType } from '../src/types';
2020

2121
describe('Utils', () => {
2222
describe('isLayerIgnored()', () => {
@@ -60,45 +60,115 @@ describe('Utils', () => {
6060
});
6161
describe('isLayerNameIgnored()', () => {
6262
it('should not fail with invalid config', () => {
63-
assert.strictEqual(utils.isLayerNameIgnored(''), false);
63+
assert.strictEqual(utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {}), false);
6464
assert.strictEqual(
65-
utils.isLayerNameIgnored('', {} as KoaInstrumentationConfig),
65+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {} as KoaInstrumentationConfig),
6666
false
6767
);
6868
assert.strictEqual(
69-
utils.isLayerNameIgnored('', {
70-
ignoreLayersName: {},
71-
} as KoaInstrumentationConfig),
69+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
70+
ignoreLayers: {},
71+
} as KoaInstrumentationConfig, ''),
7272
false
7373
);
7474
assert.strictEqual(
75-
utils.isLayerNameIgnored('logger', {
76-
ignoreLayersName: {},
77-
} as KoaInstrumentationConfig),
75+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
76+
ignoreLayers: {},
77+
} as KoaInstrumentationConfig, 'logger'),
7878
false
7979
);
80-
assert.strictEqual(utils.isLayerNameIgnored('logger'), false);
80+
assert.strictEqual(utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {}, 'logger'), false);
8181
assert.strictEqual(
82-
utils.isLayerNameIgnored('', {
83-
ignoreLayersName: [],
84-
} as KoaInstrumentationConfig),
82+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
83+
ignoreLayers: [],
84+
} as KoaInstrumentationConfig, ''),
8585
false
8686
);
8787
});
8888

89-
it('should ignore based on type', () => {
89+
it('should ignore based on name', () => {
9090
assert.strictEqual(
91-
utils.isLayerNameIgnored('logger', {
92-
ignoreLayersName: ['logger'],
93-
}),
91+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
92+
ignoreLayers: ['logger'],
93+
}, 'logger'),
9494
true
9595
);
9696
assert.strictEqual(
97-
utils.isLayerNameIgnored('', {
98-
ignoreLayersName: ['logger'],
99-
}),
97+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
98+
ignoreLayers: ['logger'],
99+
}, 'logger'),
100+
true
101+
);
102+
assert.strictEqual(
103+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
104+
ignoreLayers: ['logger'],
105+
}, ''),
100106
false
101107
);
108+
assert.strictEqual(
109+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
110+
ignoreLayers: [/logger/],
111+
}, 'logger - test'),
112+
true
113+
);
114+
assert.strictEqual(
115+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
116+
ignoreLayers: [/logger/],
117+
}, 'router - test'),
118+
false
119+
);
120+
assert.strictEqual(
121+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
122+
ignoreLayers: [(name: string) => name === 'test'],
123+
}, 'test'),
124+
true
125+
);
126+
assert.strictEqual(
127+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
128+
ignoreLayers: [(name: string) => name === 'router'],
129+
}, 'test'),
130+
false
131+
);
132+
});
133+
});
134+
});
135+
136+
describe('Utility', () => {
137+
describe('satisfiesPattern()', () => {
138+
it('string pattern', () => {
139+
const answer1 = utils.satisfiesPattern('localhost', 'localhost');
140+
assert.strictEqual(answer1, true);
141+
const answer2 = utils.satisfiesPattern('hostname', 'localhost');
142+
assert.strictEqual(answer2, false);
143+
});
144+
145+
it('regex pattern', () => {
146+
const answer1 = utils.satisfiesPattern('LocalHost', /localhost/i);
147+
assert.strictEqual(answer1, true);
148+
const answer2 = utils.satisfiesPattern('Montreal.ca', /montreal.ca/);
149+
assert.strictEqual(answer2, false);
150+
});
151+
152+
it('should throw if type is unknown', () => {
153+
try {
154+
utils.satisfiesPattern('google.com', true as unknown as IgnoreMatcher);
155+
assert.fail();
156+
} catch (error) {
157+
assert.strictEqual(error instanceof TypeError, true);
158+
}
159+
});
160+
161+
it('function pattern', () => {
162+
const answer1 = utils.satisfiesPattern(
163+
'montreal.ca',
164+
(url: string) => url === 'montreal.ca'
165+
);
166+
assert.strictEqual(answer1, true);
167+
const answer2 = utils.satisfiesPattern(
168+
'montreal.ca',
169+
(url: string) => url !== 'montreal.ca'
170+
);
171+
assert.strictEqual(answer2, false);
102172
});
103173
});
104174
});

0 commit comments

Comments
 (0)