Skip to content

Commit b49bd40

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

File tree

5 files changed

+187
-46
lines changed

5 files changed

+187
-46
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: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@ import type * as koa from 'koa';
2626
import { KoaLayerType, KoaInstrumentationConfig } from './types';
2727
/** @knipignore */
2828
import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
29-
import {
30-
getMiddlewareMetadata,
31-
isLayerIgnored,
32-
isLayerNameIgnored,
33-
} from './utils';
29+
import { getMiddlewareMetadata, isLayerIgnored } from './utils';
3430
import { getRPCMetadata, RPCType } from '@opentelemetry/core';
3531
import {
3632
kLayerPatched,
@@ -167,7 +163,7 @@ export class KoaInstrumentation extends InstrumentationBase<KoaInstrumentationCo
167163
layerPath
168164
);
169165

170-
if (isLayerNameIgnored(metadata.layerName, this.getConfig())) {
166+
if (isLayerIgnored(layerType, this.getConfig(), metadata.name)) {
171167
return middlewareLayer(context, next);
172168
}
173169

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: 38 additions & 18 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 {
@@ -37,7 +36,6 @@ export const getMiddlewareMetadata = (
3736
[SEMATTRS_HTTP_ROUTE]: layerPath?.toString(),
3837
},
3938
name: context._matchedRouteName || `router - ${layerPath}`,
40-
layerName: context._matchedRouteName || layerPath?.toString() || '',
4139
};
4240
} else {
4341
return {
@@ -46,7 +44,6 @@ export const getMiddlewareMetadata = (
4644
[AttributeNames.KOA_TYPE]: KoaLayerType.MIDDLEWARE,
4745
},
4846
name: `middleware - ${layer.name}`,
49-
layerName: layer.name,
5047
};
5148
}
5249
};
@@ -59,26 +56,49 @@ 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)
69+
return false;
70+
try {
71+
for (const pattern of config.ignoreLayers) {
72+
if (satisfiesPattern(name!, pattern)) {
73+
return true;
74+
}
75+
}
76+
} catch (e) {
77+
/* catch block*/
78+
}
79+
80+
return false;
6881
};
6982

7083
/**
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
84+
* Check whether the given obj match pattern
85+
* @param constant e.g URL of request
86+
* @param obj obj to inspect
87+
* @param pattern Match pattern
7588
*/
76-
export const isLayerNameIgnored = (
77-
layerName: string,
78-
config?: KoaInstrumentationConfig
89+
export const satisfiesPattern = (
90+
constant: string,
91+
pattern: IgnoreMatcher
7992
): boolean => {
80-
return !!(
81-
Array.isArray(config?.ignoreLayersName) &&
82-
config?.ignoreLayersName?.includes(layerName)
83-
);
93+
console.warn(`constant: ${constant}`);
94+
console.warn(`pattern: ${pattern}`);
95+
if (typeof pattern === 'string') {
96+
return pattern === constant;
97+
} else if (pattern instanceof RegExp) {
98+
return pattern.test(constant);
99+
} else if (typeof pattern === 'function') {
100+
return pattern(constant);
101+
} else {
102+
throw new TypeError('Pattern is in unsupported datatype');
103+
}
84104
};

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

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

1717
import * as utils from '../src/utils';
1818
import * as assert from 'assert';
19-
import { KoaInstrumentationConfig, KoaLayerType } from '../src/types';
19+
import {
20+
IgnoreMatcher,
21+
KoaInstrumentationConfig,
22+
KoaLayerType,
23+
} from '../src/types';
2024

2125
describe('Utils', () => {
2226
describe('isLayerIgnored()', () => {
@@ -60,45 +64,164 @@ describe('Utils', () => {
6064
});
6165
describe('isLayerNameIgnored()', () => {
6266
it('should not fail with invalid config', () => {
63-
assert.strictEqual(utils.isLayerNameIgnored(''), false);
6467
assert.strictEqual(
65-
utils.isLayerNameIgnored('', {} as KoaInstrumentationConfig),
68+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {}),
6669
false
6770
);
6871
assert.strictEqual(
69-
utils.isLayerNameIgnored('', {
70-
ignoreLayersName: {},
71-
} as KoaInstrumentationConfig),
72+
utils.isLayerIgnored(
73+
KoaLayerType.MIDDLEWARE,
74+
{} as KoaInstrumentationConfig
75+
),
7276
false
7377
);
7478
assert.strictEqual(
75-
utils.isLayerNameIgnored('logger', {
76-
ignoreLayersName: {},
77-
} as KoaInstrumentationConfig),
79+
utils.isLayerIgnored(
80+
KoaLayerType.MIDDLEWARE,
81+
{
82+
ignoreLayers: {},
83+
} as KoaInstrumentationConfig,
84+
''
85+
),
7886
false
7987
);
80-
assert.strictEqual(utils.isLayerNameIgnored('logger'), false);
8188
assert.strictEqual(
82-
utils.isLayerNameIgnored('', {
83-
ignoreLayersName: [],
84-
} as KoaInstrumentationConfig),
89+
utils.isLayerIgnored(
90+
KoaLayerType.MIDDLEWARE,
91+
{
92+
ignoreLayers: {},
93+
} as KoaInstrumentationConfig,
94+
'logger'
95+
),
96+
false
97+
);
98+
assert.strictEqual(
99+
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {}, 'logger'),
100+
false
101+
);
102+
assert.strictEqual(
103+
utils.isLayerIgnored(
104+
KoaLayerType.MIDDLEWARE,
105+
{
106+
ignoreLayers: [],
107+
} as KoaInstrumentationConfig,
108+
''
109+
),
85110
false
86111
);
87112
});
88113

89-
it('should ignore based on type', () => {
114+
it('should ignore based on name', () => {
90115
assert.strictEqual(
91-
utils.isLayerNameIgnored('logger', {
92-
ignoreLayersName: ['logger'],
93-
}),
116+
utils.isLayerIgnored(
117+
KoaLayerType.MIDDLEWARE,
118+
{
119+
ignoreLayers: ['logger'],
120+
},
121+
'logger'
122+
),
94123
true
95124
);
96125
assert.strictEqual(
97-
utils.isLayerNameIgnored('', {
98-
ignoreLayersName: ['logger'],
99-
}),
126+
utils.isLayerIgnored(
127+
KoaLayerType.MIDDLEWARE,
128+
{
129+
ignoreLayers: ['logger'],
130+
},
131+
'logger'
132+
),
133+
true
134+
);
135+
assert.strictEqual(
136+
utils.isLayerIgnored(
137+
KoaLayerType.MIDDLEWARE,
138+
{
139+
ignoreLayers: ['logger'],
140+
},
141+
''
142+
),
100143
false
101144
);
145+
assert.strictEqual(
146+
utils.isLayerIgnored(
147+
KoaLayerType.MIDDLEWARE,
148+
{
149+
ignoreLayers: [/logger/],
150+
},
151+
'logger - test'
152+
),
153+
true
154+
);
155+
assert.strictEqual(
156+
utils.isLayerIgnored(
157+
KoaLayerType.MIDDLEWARE,
158+
{
159+
ignoreLayers: [/logger/],
160+
},
161+
'router - test'
162+
),
163+
false
164+
);
165+
assert.strictEqual(
166+
utils.isLayerIgnored(
167+
KoaLayerType.MIDDLEWARE,
168+
{
169+
ignoreLayers: [(name: string) => name === 'test'],
170+
},
171+
'test'
172+
),
173+
true
174+
);
175+
assert.strictEqual(
176+
utils.isLayerIgnored(
177+
KoaLayerType.MIDDLEWARE,
178+
{
179+
ignoreLayers: [(name: string) => name === 'router'],
180+
},
181+
'test'
182+
),
183+
false
184+
);
185+
});
186+
});
187+
});
188+
189+
describe('Utility', () => {
190+
describe('satisfiesPattern()', () => {
191+
it('string pattern', () => {
192+
const answer1 = utils.satisfiesPattern('localhost', 'localhost');
193+
assert.strictEqual(answer1, true);
194+
const answer2 = utils.satisfiesPattern('hostname', 'localhost');
195+
assert.strictEqual(answer2, false);
196+
});
197+
198+
it('regex pattern', () => {
199+
const answer1 = utils.satisfiesPattern('LocalHost', /localhost/i);
200+
assert.strictEqual(answer1, true);
201+
const answer2 = utils.satisfiesPattern('Montreal.ca', /montreal.ca/);
202+
assert.strictEqual(answer2, false);
203+
});
204+
205+
it('should throw if type is unknown', () => {
206+
try {
207+
utils.satisfiesPattern('google.com', true as unknown as IgnoreMatcher);
208+
assert.fail();
209+
} catch (error) {
210+
assert.strictEqual(error instanceof TypeError, true);
211+
}
212+
});
213+
214+
it('function pattern', () => {
215+
const answer1 = utils.satisfiesPattern(
216+
'montreal.ca',
217+
(url: string) => url === 'montreal.ca'
218+
);
219+
assert.strictEqual(answer1, true);
220+
const answer2 = utils.satisfiesPattern(
221+
'montreal.ca',
222+
(url: string) => url !== 'montreal.ca'
223+
);
224+
assert.strictEqual(answer2, false);
102225
});
103226
});
104227
});

0 commit comments

Comments
 (0)