Skip to content

Commit ff713f3

Browse files
Fabio.GomesMrFabio
authored andcommitted
feat(koa): Adds support to ignore a span by its layer name
1 parent 7cc26fb commit ff713f3

File tree

6 files changed

+211
-14
lines changed

6 files changed

+211
-14
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,15 @@ 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+
| `ignoreLayers` | `IgnoreMatcher[]` | `['logger', /router/]` | Ignore layers with specified names. |
5758
| `requestHook` | `KoaRequestCustomAttributeFunction` | `(span, info) => {}` | Function for adding custom attributes to Koa middleware layers. Receives params: `Span, KoaRequestInfo`. |
5859

60+
`ignoreLayers` accepts an array of elements of types:
61+
62+
- `string` for full match of the path,
63+
- `RegExp` for partial match of the path,
64+
- `function` in the form of `(path) => boolean` for custom logic.
65+
5966
`ignoreLayersType` accepts an array of `KoaLayerType` which can take the following string values:
6067

6168
- `router`,

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ import type * as koa from 'koa';
2626
import { KoaLayerType, KoaInstrumentationConfig } from './types';
2727
/** @knipignore */
2828
import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
29-
import { getMiddlewareMetadata, isLayerIgnored } from './utils';
29+
import {
30+
getMiddlewareMetadata,
31+
isLayerNameIgnored,
32+
isLayerTypeIgnored,
33+
} from './utils';
3034
import { getRPCMetadata, RPCType } from '@opentelemetry/core';
3135
import {
3236
kLayerPatched,
@@ -136,7 +140,7 @@ export class KoaInstrumentation extends InstrumentationBase<KoaInstrumentationCo
136140
// Skip patching layer if its ignored in the config
137141
if (
138142
middlewareLayer[kLayerPatched] === true ||
139-
isLayerIgnored(layerType, this.getConfig())
143+
isLayerTypeIgnored(layerType, this.getConfig())
140144
)
141145
return middlewareLayer;
142146

@@ -162,6 +166,11 @@ export class KoaInstrumentation extends InstrumentationBase<KoaInstrumentationCo
162166
isRouter,
163167
layerPath
164168
);
169+
170+
if (isLayerNameIgnored(metadata.name, this.getConfig())) {
171+
return middlewareLayer(context, next);
172+
}
173+
165174
const span = this.tracer.startSpan(metadata.name, {
166175
attributes: metadata.attributes,
167176
});

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,13 @@ export interface KoaInstrumentationConfig<
6969
> extends InstrumentationConfig {
7070
/** Ignore specific layers based on their type */
7171
ignoreLayersType?: KoaLayerType[];
72+
/** Ignore specific layers based on their name */
73+
ignoreLayers?: IgnoreMatcher[];
7274
/** Function for adding custom attributes to each middleware layer span */
7375
requestHook?: KoaRequestCustomAttributeFunction<
7476
KoaContextType,
7577
KoaMiddlewareType
7678
>;
7779
}
80+
81+
export type IgnoreMatcher = string | RegExp | ((name: string) => boolean);

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

Lines changed: 51 additions & 3 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';
@@ -49,12 +49,12 @@ export const getMiddlewareMetadata = (
4949
};
5050

5151
/**
52-
* Check whether the given request is ignored by configuration
52+
* Check whether the given request layer type is ignored by configuration
5353
* @param [list] List of ignore patterns
5454
* @param [onException] callback for doing something when an exception has
5555
* occurred
5656
*/
57-
export const isLayerIgnored = (
57+
export const isLayerTypeIgnored = (
5858
type: KoaLayerType,
5959
config?: KoaInstrumentationConfig
6060
): boolean => {
@@ -63,3 +63,51 @@ export const isLayerIgnored = (
6363
config?.ignoreLayersType?.includes(type)
6464
);
6565
};
66+
67+
/**
68+
* Check whether the given request layer name is ignored by configuration
69+
* @param [list] List of ignore patterns
70+
* @param [onException] callback for doing something when an exception has
71+
* occurred
72+
*/
73+
export const isLayerNameIgnored = (
74+
name: string,
75+
config?: KoaInstrumentationConfig
76+
): boolean => {
77+
if (Array.isArray(config?.ignoreLayers) === false || !config?.ignoreLayers)
78+
return false;
79+
try {
80+
for (const pattern of config.ignoreLayers) {
81+
if (satisfiesPattern(name!, pattern)) {
82+
return true;
83+
}
84+
}
85+
} catch (e) {
86+
/* catch block*/
87+
}
88+
89+
return false;
90+
};
91+
92+
/**
93+
* Check whether the given obj match pattern
94+
* @param constant e.g URL of request
95+
* @param obj obj to inspect
96+
* @param pattern Match pattern
97+
*/
98+
export const satisfiesPattern = (
99+
constant: string,
100+
pattern: IgnoreMatcher
101+
): boolean => {
102+
console.warn(`constant: ${constant}`);
103+
console.warn(`pattern: ${pattern}`);
104+
if (typeof pattern === 'string') {
105+
return pattern === constant;
106+
} else if (pattern instanceof RegExp) {
107+
return pattern.test(constant);
108+
} else if (typeof pattern === 'function') {
109+
return pattern(constant);
110+
} else {
111+
throw new TypeError('Pattern is in unsupported datatype');
112+
}
113+
};

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ describe('Koa Instrumentation', () => {
730730
'--experimental-loader=@opentelemetry/instrumentation/hook.mjs',
731731
NODE_NO_WARNINGS: '1',
732732
},
733-
checkResult: (err, stdout, stderr) => {
733+
checkResult: (err: any, stdout: any, stderr: any) => {
734734
assert.ifError(err);
735735
},
736736
checkCollector: (collector: testUtils.TestCollector) => {

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

Lines changed: 137 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,34 @@
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', () => {
22-
describe('isLayerIgnored()', () => {
26+
describe('isLayerTypeIgnored()', () => {
2327
it('should not fail with invalid config', () => {
24-
assert.strictEqual(utils.isLayerIgnored(KoaLayerType.MIDDLEWARE), false);
2528
assert.strictEqual(
26-
utils.isLayerIgnored(
29+
utils.isLayerTypeIgnored(KoaLayerType.MIDDLEWARE),
30+
false
31+
);
32+
assert.strictEqual(
33+
utils.isLayerTypeIgnored(
2734
KoaLayerType.MIDDLEWARE,
2835
{} as KoaInstrumentationConfig
2936
),
3037
false
3138
);
3239
assert.strictEqual(
33-
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
40+
utils.isLayerTypeIgnored(KoaLayerType.MIDDLEWARE, {
3441
ignoreLayersType: {},
3542
} as KoaInstrumentationConfig),
3643
false
3744
);
3845
assert.strictEqual(
39-
utils.isLayerIgnored(KoaLayerType.ROUTER, {
46+
utils.isLayerTypeIgnored(KoaLayerType.ROUTER, {
4047
ignoreLayersType: {},
4148
} as KoaInstrumentationConfig),
4249
false
@@ -45,17 +52,139 @@ describe('Utils', () => {
4552

4653
it('should ignore based on type', () => {
4754
assert.strictEqual(
48-
utils.isLayerIgnored(KoaLayerType.MIDDLEWARE, {
55+
utils.isLayerTypeIgnored(KoaLayerType.MIDDLEWARE, {
4956
ignoreLayersType: [KoaLayerType.MIDDLEWARE],
5057
}),
5158
true
5259
);
5360
assert.strictEqual(
54-
utils.isLayerIgnored(KoaLayerType.ROUTER, {
61+
utils.isLayerTypeIgnored(KoaLayerType.ROUTER, {
5562
ignoreLayersType: [KoaLayerType.MIDDLEWARE],
5663
}),
5764
false
5865
);
5966
});
6067
});
68+
describe('isLayerTypeIgnored()', () => {
69+
it('should not fail with invalid config', () => {
70+
assert.strictEqual(
71+
utils.isLayerTypeIgnored(KoaLayerType.MIDDLEWARE, {}),
72+
false
73+
);
74+
assert.strictEqual(
75+
utils.isLayerTypeIgnored(
76+
KoaLayerType.MIDDLEWARE,
77+
{} as KoaInstrumentationConfig
78+
),
79+
false
80+
);
81+
assert.strictEqual(
82+
utils.isLayerTypeIgnored(KoaLayerType.MIDDLEWARE, {
83+
ignoreLayers: {},
84+
} as KoaInstrumentationConfig),
85+
false
86+
);
87+
assert.strictEqual(
88+
utils.isLayerTypeIgnored(KoaLayerType.MIDDLEWARE, {
89+
ignoreLayers: {},
90+
} as KoaInstrumentationConfig),
91+
false
92+
);
93+
assert.strictEqual(
94+
utils.isLayerTypeIgnored(KoaLayerType.MIDDLEWARE, {}),
95+
false
96+
);
97+
assert.strictEqual(
98+
utils.isLayerTypeIgnored(KoaLayerType.MIDDLEWARE, {
99+
ignoreLayers: [],
100+
} as KoaInstrumentationConfig),
101+
false
102+
);
103+
});
104+
105+
it('should ignore based on name', () => {
106+
assert.strictEqual(
107+
utils.isLayerNameIgnored('logger', {
108+
ignoreLayers: ['logger'],
109+
}),
110+
true
111+
);
112+
assert.strictEqual(
113+
utils.isLayerNameIgnored('logger', {
114+
ignoreLayers: ['logger'],
115+
}),
116+
true
117+
);
118+
assert.strictEqual(
119+
utils.isLayerNameIgnored('', {
120+
ignoreLayers: ['logger'],
121+
}),
122+
false
123+
);
124+
assert.strictEqual(
125+
utils.isLayerNameIgnored('logger - test', {
126+
ignoreLayers: [/logger/],
127+
}),
128+
true
129+
);
130+
assert.strictEqual(
131+
utils.isLayerNameIgnored('router - test', {
132+
ignoreLayers: [/logger/],
133+
}),
134+
false
135+
);
136+
assert.strictEqual(
137+
utils.isLayerNameIgnored('test', {
138+
ignoreLayers: [(name: string) => name === 'test'],
139+
}),
140+
true
141+
);
142+
assert.strictEqual(
143+
utils.isLayerNameIgnored('test', {
144+
ignoreLayers: [(name: string) => name === 'router'],
145+
}),
146+
false
147+
);
148+
});
149+
});
150+
});
151+
152+
describe('Utility', () => {
153+
describe('satisfiesPattern()', () => {
154+
it('string pattern', () => {
155+
const answer1 = utils.satisfiesPattern('localhost', 'localhost');
156+
assert.strictEqual(answer1, true);
157+
const answer2 = utils.satisfiesPattern('hostname', 'localhost');
158+
assert.strictEqual(answer2, false);
159+
});
160+
161+
it('regex pattern', () => {
162+
const answer1 = utils.satisfiesPattern('LocalHost', /localhost/i);
163+
assert.strictEqual(answer1, true);
164+
const answer2 = utils.satisfiesPattern('Montreal.ca', /montreal.ca/);
165+
assert.strictEqual(answer2, false);
166+
});
167+
168+
it('should throw if type is unknown', () => {
169+
try {
170+
utils.satisfiesPattern('google.com', true as unknown as IgnoreMatcher);
171+
assert.fail();
172+
} catch (error) {
173+
assert.strictEqual(error instanceof TypeError, true);
174+
}
175+
});
176+
177+
it('function pattern', () => {
178+
const answer1 = utils.satisfiesPattern(
179+
'montreal.ca',
180+
(url: string) => url === 'montreal.ca'
181+
);
182+
assert.strictEqual(answer1, true);
183+
const answer2 = utils.satisfiesPattern(
184+
'montreal.ca',
185+
(url: string) => url !== 'montreal.ca'
186+
);
187+
assert.strictEqual(answer2, false);
188+
});
189+
});
61190
});

0 commit comments

Comments
 (0)