Added http ignore by method (#49)
diff --git a/README.md b/README.md
index 1ff9800..de6acf0 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@
| `SW_AGENT_DISABLE_PLUGINS` | Comma-delimited list of plugins to disable in the plugins directory (e.g. "mysql", "express"). | `` |
| `SW_IGNORE_SUFFIX` | The suffices of endpoints that will be ignored (not traced), comma separated | `.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg` |
| `SW_TRACE_IGNORE_PATH` | The paths of endpoints that will be ignored (not traced), comma separated | `` |
+| `SW_HTTP_IGNORE_METHOD` | Comma-delimited list of http methods to ignore (GET, POST, HEAD, OPTIONS, etc...) | `` |
| `SW_SQL_TRACE_PARAMETERS` | If set to 'true' then SQL query parameters will be included | `false` |
| `SW_SQL_PARAMETERS_MAX_LENGTH` | The maximum string length of SQL parameters to log | `512` |
| `SW_MONGO_TRACE_PARAMETERS` | If set to 'true' then mongodb query parameters will be included | `false` |
diff --git a/src/config/AgentConfig.ts b/src/config/AgentConfig.ts
index eb74b0d..59c2c04 100644
--- a/src/config/AgentConfig.ts
+++ b/src/config/AgentConfig.ts
@@ -29,6 +29,7 @@
disablePlugins?: string;
ignoreSuffix?: string;
traceIgnorePath?: string;
+ httpIgnoreMethod?: string;
sqlTraceParameters?: boolean;
sqlParametersMaxLength?: number;
mongoTraceParameters?: boolean;
@@ -36,6 +37,7 @@
// the following is internal state computed from config values
reDisablePlugins?: RegExp;
reIgnoreOperation?: RegExp;
+ reHttpIgnoreMethod?: RegExp;
};
export function finalizeConfig(config: AgentConfig): void {
@@ -53,9 +55,10 @@
).join('|') + ')$'; // replaces ","
config.reIgnoreOperation = RegExp(`${ignoreSuffix}|${ignorePath}`);
+ config.reHttpIgnoreMethod = RegExp(`^(?:${config.httpIgnoreMethod!.split(',').map((s) => s.trim()).join('|')})$`, 'i');
}
-export default {
+const _config = {
serviceName: process.env.SW_AGENT_NAME || 'your-nodejs-service',
serviceInstance:
process.env.SW_AGENT_INSTANCE ||
@@ -70,10 +73,18 @@
disablePlugins: process.env.SW_AGENT_DISABLE_PLUGINS || '',
ignoreSuffix: process.env.SW_IGNORE_SUFFIX ?? '.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg',
traceIgnorePath: process.env.SW_TRACE_IGNORE_PATH || '',
+ httpIgnoreMethod: process.env.SW_HTTP_IGNORE_METHOD || '',
sqlTraceParameters: (process.env.SW_SQL_TRACE_PARAMETERS || '').toLowerCase() === 'true',
sqlParametersMaxLength: Math.trunc(Math.max(0, Number(process.env.SW_SQL_PARAMETERS_MAX_LENGTH))) || 512,
mongoTraceParameters: (process.env.SW_MONGO_TRACE_PARAMETERS || '').toLowerCase() === 'true',
mongoParametersMaxLength: Math.trunc(Math.max(0, Number(process.env.SW_MONGO_PARAMETERS_MAX_LENGTH))) || 512,
reDisablePlugins: RegExp(''), // temporary placeholder so Typescript doesn't throw a fit
reIgnoreOperation: RegExp(''),
+ reHttpIgnoreMethod: RegExp(''),
};
+
+export default _config;
+
+export function ignoreHttpMethodCheck(method: string): boolean {
+ return Boolean(method.match(_config.reHttpIgnoreMethod));
+}
diff --git a/src/index.ts b/src/index.ts
index 1dfae71..3f455b8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -33,12 +33,14 @@
return;
}
+ if (this.started) {
+ logger.warn('SkyWalking agent started more than once, subsequent options to start ignored.');
+ return;
+ }
+
Object.assign(config, options);
finalizeConfig(config);
- if (this.started) {
- throw new Error('SkyWalking agent is already started and can only be started once.');
- }
logger.debug('Starting SkyWalking agent');
this.started = true;
diff --git a/src/plugins/AxiosPlugin.ts b/src/plugins/AxiosPlugin.ts
index 1684c99..1eedd67 100644
--- a/src/plugins/AxiosPlugin.ts
+++ b/src/plugins/AxiosPlugin.ts
@@ -17,12 +17,14 @@
*
*/
-import SwPlugin, {wrapPromise} from '../core/SwPlugin';
+import SwPlugin from '../core/SwPlugin';
import { URL } from 'url';
import ContextManager from '../trace/context/ContextManager';
import { Component } from '../trace/Component';
import Tag from '../Tag';
import { SpanLayer } from '../proto/language-agent/Tracing_pb';
+import DummySpan from '../trace/span/DummySpan';
+import { ignoreHttpMethodCheck } from '../config/AgentConfig';
import PluginInstaller from '../core/PluginInstaller';
class AxiosPlugin implements SwPlugin {
@@ -44,7 +46,10 @@
config = url ? {...url} : {};
const {origin, host, pathname: operation} = new URL(config.url); // TODO: this may throw invalid URL
- const span = ContextManager.current.newExitSpan(operation, host, Component.AXIOS, Component.HTTP);
+ const method = (config.method || 'GET').toUpperCase();
+ const span = ignoreHttpMethodCheck(method)
+ ? DummySpan.create()
+ : ContextManager.current.newExitSpan(operation, host, Component.AXIOS, Component.HTTP);
span.start();
@@ -56,7 +61,7 @@
span.peer = host;
span.tag(Tag.httpURL(origin + operation));
- span.tag(Tag.httpMethod((config.method || 'GET').toUpperCase()));
+ span.tag(Tag.httpMethod(method));
span.inject().items.forEach((item) => config.headers[item.key] = item.value);
diff --git a/src/plugins/ExpressPlugin.ts b/src/plugins/ExpressPlugin.ts
index 10ae4f9..d9b25f0 100644
--- a/src/plugins/ExpressPlugin.ts
+++ b/src/plugins/ExpressPlugin.ts
@@ -22,8 +22,9 @@
import ContextManager from '../trace/context/ContextManager';
import { Component } from '../trace/Component';
import Tag from '../Tag';
-import EntrySpan from '../trace/span/EntrySpan';
import { ContextCarrier } from '../trace/context/ContextCarrier';
+import DummySpan from '../trace/span/DummySpan';
+import { ignoreHttpMethodCheck } from '../config/AgentConfig';
import PluginInstaller from '../core/PluginInstaller';
import HttpPlugin from './HttpPlugin';
@@ -42,7 +43,9 @@
router.handle = function(req: IncomingMessage, res: ServerResponse, next: any) {
const carrier = ContextCarrier.from((req as any).headers || {});
const operation = (req.url || '/').replace(/\?.*/g, '');
- const span: EntrySpan = ContextManager.current.newEntrySpan(operation, carrier, Component.HTTP_SERVER) as EntrySpan;
+ const span = ignoreHttpMethodCheck(req.method ?? 'GET')
+ ? DummySpan.create()
+ : ContextManager.current.newEntrySpan(operation, carrier, Component.HTTP_SERVER);
span.component = Component.EXPRESS;
diff --git a/src/plugins/HttpPlugin.ts b/src/plugins/HttpPlugin.ts
index c9936e3..22c37f4 100644
--- a/src/plugins/HttpPlugin.ts
+++ b/src/plugins/HttpPlugin.ts
@@ -24,9 +24,10 @@
import { Component } from '../trace/Component';
import Tag from '../Tag';
import Span from '../trace/span/Span';
-import ExitSpan from '../trace/span/ExitSpan';
import { SpanLayer } from '../proto/language-agent/Tracing_pb';
import { ContextCarrier } from '../trace/context/ContextCarrier';
+import DummySpan from '../trace/span/DummySpan';
+import { ignoreHttpMethodCheck } from '../config/AgentConfig';
class HttpPlugin implements SwPlugin {
readonly module = 'http';
@@ -59,7 +60,10 @@
};
const operation = pathname.replace(/\?.*$/g, '');
- const span: ExitSpan = ContextManager.current.newExitSpan(operation, host, Component.HTTP) as ExitSpan;
+ const method = arguments[url instanceof URL || typeof url === 'string' ? 1 : 0]?.method || 'GET';
+ const span = ignoreHttpMethodCheck(method)
+ ? DummySpan.create()
+ : ContextManager.current.newExitSpan(operation, host, Component.HTTP);
if (span.depth) // if we inherited from a higher level plugin then do nothing, higher level should do all the work and we don't duplicate here
return _request.apply(this, arguments);
@@ -72,7 +76,7 @@
span.peer = host;
span.tag(Tag.httpURL(protocol + '://' + host + pathname));
- span.tag(Tag.httpMethod(arguments[url instanceof URL || typeof url === 'string' ? 1 : 0]?.method || 'GET'));
+ span.tag(Tag.httpMethod(method));
const copyStatusAndWrapEmit = (res: any) => {
span.tag(Tag.httpStatusCode(res.statusCode));
@@ -146,7 +150,9 @@
function _sw_request(this: any, req: IncomingMessage, res: ServerResponse, ...reqArgs: any[]) {
const carrier = ContextCarrier.from((req as any).headers || {});
const operation = (req.url || '/').replace(/\?.*/g, '');
- const span = ContextManager.current.newEntrySpan(operation, carrier);
+ const span = ignoreHttpMethodCheck(req.method ?? 'GET')
+ ? DummySpan.create()
+ : ContextManager.current.newEntrySpan(operation, carrier);
span.component = Component.HTTP_SERVER;
@@ -168,7 +174,7 @@
? `[${req.connection.remoteAddress}]:${req.connection.remotePort}`
: `${req.connection.remoteAddress}:${req.connection.remotePort}`);
- span.tag(Tag.httpMethod(req.method));
+ span.tag(Tag.httpMethod(req.method ?? 'GET'));
const ret = handler();
diff --git a/src/trace/context/DummyContext.ts b/src/trace/context/DummyContext.ts
index f08c93d..2d5c4fb 100644
--- a/src/trace/context/DummyContext.ts
+++ b/src/trace/context/DummyContext.ts
@@ -22,15 +22,10 @@
import DummySpan from '../../trace/span/DummySpan';
import Segment from '../../trace/context/Segment';
import { Component } from '../../trace/Component';
-import { SpanType } from '../../proto/language-agent/Tracing_pb';
import { ContextCarrier } from './ContextCarrier';
export default class DummyContext implements Context {
- span: Span = new DummySpan({
- context: this,
- operation: '',
- type: SpanType.LOCAL,
- });
+ span: Span = DummySpan.create(this);
segment: Segment = new Segment();
nSpans = 0;
diff --git a/src/trace/context/SpanContext.ts b/src/trace/context/SpanContext.ts
index 29241cd..2436bd0 100644
--- a/src/trace/context/SpanContext.ts
+++ b/src/trace/context/SpanContext.ts
@@ -19,7 +19,6 @@
import config from '../../config/AgentConfig';
import Context from '../../trace/context/Context';
-import DummyContext from '../../trace/context/DummyContext';
import Span from '../../trace/span/Span';
import DummySpan from '../../trace/span/DummySpan';
import Segment from '../../trace/context/Segment';
@@ -53,13 +52,8 @@
}
ignoreCheck(operation: string, type: SpanType): Span | undefined {
- if (operation.match(config.reIgnoreOperation)) {
- return new DummySpan({
- context: new DummyContext(),
- operation: '',
- type,
- });
- }
+ if (operation.match(config.reIgnoreOperation))
+ return DummySpan.create();
return undefined;
}
diff --git a/src/trace/span/DummySpan.ts b/src/trace/span/DummySpan.ts
index 8657bfb..c2b1571 100644
--- a/src/trace/span/DummySpan.ts
+++ b/src/trace/span/DummySpan.ts
@@ -19,8 +19,19 @@
import Span from '../../trace/span/Span';
import { ContextCarrier } from '../context/ContextCarrier';
+import Context from '../context/Context';
+import { SpanType } from '../../proto/language-agent/Tracing_pb';
+import DummyContext from '../context/DummyContext';
export default class DummySpan extends Span {
+ static create(context?: Context): DummySpan {
+ return new DummySpan({
+ context: context ?? new DummyContext(),
+ operation: '',
+ type: SpanType.LOCAL,
+ });
+ }
+
inject(): ContextCarrier {
return new ContextCarrier();
}
diff --git a/src/trace/span/EntrySpan.ts b/src/trace/span/EntrySpan.ts
index 511b0a5..3532c75 100644
--- a/src/trace/span/EntrySpan.ts
+++ b/src/trace/span/EntrySpan.ts
@@ -17,13 +17,13 @@
*
*/
-import StackedSpan from '../../trace/span/StackedSpan';
+import Span from '../../trace/span/Span';
import { SpanCtorOptions } from './Span';
import SegmentRef from '../../trace/context/SegmentRef';
import { SpanType } from '../../proto/language-agent/Tracing_pb';
import { ContextCarrier } from '../context/ContextCarrier';
-export default class EntrySpan extends StackedSpan {
+export default class EntrySpan extends Span {
constructor(options: SpanCtorOptions) {
super(
Object.assign(options, {
diff --git a/src/trace/span/ExitSpan.ts b/src/trace/span/ExitSpan.ts
index 745e4c4..a120366 100644
--- a/src/trace/span/ExitSpan.ts
+++ b/src/trace/span/ExitSpan.ts
@@ -17,14 +17,14 @@
*
*/
-import StackedSpan from '../../trace/span/StackedSpan';
+import Span from '../../trace/span/Span';
import { SpanCtorOptions } from './Span';
import config from '../../config/AgentConfig';
import { SpanType } from '../../proto/language-agent/Tracing_pb';
import { ContextCarrier } from '../context/ContextCarrier';
import ContextManager from '../context/ContextManager';
-export default class ExitSpan extends StackedSpan {
+export default class ExitSpan extends Span {
constructor(options: SpanCtorOptions) {
super(
Object.assign(options, {
diff --git a/src/trace/span/Span.ts b/src/trace/span/Span.ts
index 2899a45..a240c8a 100644
--- a/src/trace/span/Span.ts
+++ b/src/trace/span/Span.ts
@@ -50,6 +50,7 @@
operation: string;
layer = SpanLayer.UNKNOWN;
component = Component.UNKNOWN;
+ depth = 0;
inherit?: Component;
readonly tags: Tag[] = [];
@@ -74,12 +75,15 @@
}
start(): void {
- this.startTime = new Date().getTime();
- this.context.start(this);
+ if (++this.depth === 1) {
+ this.startTime = new Date().getTime();
+ this.context.start(this);
+ }
}
stop(): void {
- this.context.stop(this);
+ if (--this.depth === 0)
+ this.context.stop(this);
}
async(): void {
diff --git a/src/trace/span/StackedSpan.ts b/src/trace/span/StackedSpan.ts
deleted file mode 100644
index 77e3243..0000000
--- a/src/trace/span/StackedSpan.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*!
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-import Span, { SpanCtorOptions } from '../../trace/span/Span';
-import { SpanType } from '../../proto/language-agent/Tracing_pb';
-
-export default class StackedSpan extends Span {
- depth = 0;
-
- constructor(options: SpanCtorOptions & { type: SpanType }) {
- super(options);
- }
-
- start(): void {
- if (++this.depth === 1) {
- super.start();
- }
- }
-
- stop(): void {
- if (--this.depth === 0) {
- super.stop();
- }
- }
-}