You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2020/07/26 03:51:29 UTC
[skywalking-nodejs] 04/23: Add plugin for built-in http.server
module
This is an automated email from the ASF dual-hosted git repository.
kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-nodejs.git
commit aed0136bdb24a5ca784ef91ed1cbdfffcd03d925
Author: kezhenxu94 <ke...@163.com>
AuthorDate: Sun Jun 21 21:35:57 2020 +0800
Add plugin for built-in http.server module
---
src/index.ts | 5 ++
src/plugins/HttpPlugin.ts | 57 ++++++++++++++++++--
src/trace/Component.ts | 1 +
src/trace/context/{Carrier.ts => CarrierItem.ts} | 19 ++-----
src/trace/context/Context.ts | 2 +-
src/trace/context/ContextCarrier.ts | 69 ++++++++++++++++++++++++
src/trace/context/DummyContext.ts | 2 +-
src/trace/context/SegmentRef.ts | 16 +++---
src/trace/context/SpanContext.ts | 12 +++--
src/trace/span/EntrySpan.ts | 6 +--
src/trace/span/ExitSpan.ts | 23 ++++----
src/trace/span/Span.ts | 8 +--
12 files changed, 170 insertions(+), 50 deletions(-)
diff --git a/src/index.ts b/src/index.ts
index 6a5acb0..260331f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -30,6 +30,11 @@ class Agent {
protocol: Protocol = new GrpcProtocol();
start(options: AgentConfig = {}): void {
+ if (process.env.SW_DISABLE === 'true') {
+ logger.info('SkyWalking agent is disabled by `SW_DISABLE=true`');
+ return;
+ }
+
Object.assign(config, options);
if (this.started) {
diff --git a/src/plugins/HttpPlugin.ts b/src/plugins/HttpPlugin.ts
index 0993029..f552e6f 100644
--- a/src/plugins/HttpPlugin.ts
+++ b/src/plugins/HttpPlugin.ts
@@ -19,11 +19,12 @@
import SwPlugin from '@/core/SwPlugin';
import { URL } from 'url';
-import { ClientRequest, IncomingMessage, RequestOptions } from 'http';
+import { ClientRequest, IncomingMessage, RequestOptions, ServerResponse } from 'http';
import ContextManager from '@/trace/context/ContextManager';
import { Component } from '@/trace/Component';
import Tag from '@/Tag';
import { SpanLayer } from '@/proto/language-agent/Tracing_pb';
+import { ContextCarrier } from '@/trace/context/ContextCarrier';
type RequestFunctionType = (
url: string | URL,
@@ -37,19 +38,60 @@ class HttpPlugin implements SwPlugin {
install(): void {
this.interceptClientRequest();
+
+ const http = require('http');
+
+ (original => {
+ http.Server.prototype.emit = function(event: string | symbol, ...args: any[]): boolean {
+ if (event === 'request') {
+ if (!(args[0] instanceof IncomingMessage) && !(args[1] instanceof ServerResponse)) {
+ return original.apply(this, arguments);
+ }
+ const req = args[0] as IncomingMessage;
+ const res = args[1] as ServerResponse;
+
+ const headers = req.rawHeaders;
+ const headersMap: { [key: string]: string } = {};
+
+ for (let i = 0; i < headers.length / 2; i += 2) {
+ headersMap[headers[i]] = headers[i + 1];
+ }
+
+ const carrier = new ContextCarrier();
+ carrier.items.forEach(item => {
+ if (headersMap.hasOwnProperty(item.key)) {
+ item.value = headersMap[item.key];
+ }
+ });
+
+ const span = ContextManager.current.newEntrySpan('/', carrier).start();
+ span.operation = req.url || '/';
+ span.component = Component.HTTP_SERVER;
+ span.layer = SpanLayer.HTTP;
+
+ res.on('finish', () => {
+ span
+ .tag(Tag.httpStatusCode(res.statusCode))
+ .tag(Tag.httpStatusMsg(res.statusMessage))
+ .stop();
+ });
+ }
+ return original.apply(this, arguments);
+ };
+ })(http.Server.prototype.emit);
}
private interceptClientRequest() {
const http = require('http');
- if (http.request === this.wrapHttpRequest) {
+ if (http.request === this.wrapHttpClientRequest) {
return;
}
- http.request = this.wrapHttpRequest(http.request);
+ http.request = this.wrapHttpClientRequest(http.request);
}
- private wrapHttpRequest(originalRequest: RequestFunctionType): RequestFunctionType {
+ private wrapHttpClientRequest(originalRequest: RequestFunctionType): RequestFunctionType {
return (
url: string | URL,
options: RequestOptions,
@@ -84,6 +126,13 @@ class HttpPlugin implements SwPlugin {
callbackSpan.stop();
});
+ span
+ .extract()
+ .items
+ .forEach(item => {
+ request.setHeader(item.key, item.value);
+ });
+
span.stop();
return request;
diff --git a/src/trace/Component.ts b/src/trace/Component.ts
index 4fd62f9..30d0ee4 100644
--- a/src/trace/Component.ts
+++ b/src/trace/Component.ts
@@ -20,6 +20,7 @@
export class Component {
static UNKNOWN = new Component(0);
static HTTP = new Component(2);
+ static HTTP_SERVER = new Component(49);
constructor(public id: number) {
}
diff --git a/src/trace/context/Carrier.ts b/src/trace/context/CarrierItem.ts
similarity index 72%
rename from src/trace/context/Carrier.ts
rename to src/trace/context/CarrierItem.ts
index 1bcfe06..2d58a9f 100644
--- a/src/trace/context/Carrier.ts
+++ b/src/trace/context/CarrierItem.ts
@@ -17,20 +17,9 @@
*
*/
-import ID from '@/trace/ID';
+export class CarrierItem {
+ value!: string;
-export interface ContextCarrier extends CarrierItem {
- traceId: ID;
- segmentId: ID;
- spanId: number;
- service: string;
- serviceInstance: string;
- endpoint: string;
- clientAddress: string;
- items: CarrierItem[];
-}
-
-export interface CarrierItem {
- key: string;
- value: string;
+ constructor(public key: string) {
+ }
}
diff --git a/src/trace/context/Context.ts b/src/trace/context/Context.ts
index 23a21f5..590e0af 100644
--- a/src/trace/context/Context.ts
+++ b/src/trace/context/Context.ts
@@ -18,9 +18,9 @@
*/
import Span from '@/trace/span/Span';
-import { ContextCarrier } from '@/trace/context/Carrier';
import Segment from '@/trace/context/Segment';
import Snapshot from '@/trace/context/Snapshot';
+import { ContextCarrier } from '@/trace/context/ContextCarrier';
export default interface Context {
segment: Segment;
diff --git a/src/trace/context/ContextCarrier.ts b/src/trace/context/ContextCarrier.ts
new file mode 100644
index 0000000..36a037a
--- /dev/null
+++ b/src/trace/context/ContextCarrier.ts
@@ -0,0 +1,69 @@
+/*!
+ *
+ * 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 ID from '@/trace/ID';
+import { CarrierItem } from '@/trace/context/CarrierItem';
+
+export class ContextCarrier extends CarrierItem {
+ constructor(
+ public traceId?: ID,
+ public segmentId?: ID,
+ public spanId?: number,
+ public service?: string,
+ public serviceInstance?: string,
+ public endpoint?: string,
+ public clientAddress?: string,
+ public items: CarrierItem[] = [],
+ ) {
+ super('sw8');
+ this.items.push(this);
+ }
+
+ private encode(s: string): string {
+ return Buffer.from(s).toString('base64');
+ }
+
+ private decode(s: string): string {
+ return Buffer.from(s, 'base64').toString();
+ }
+
+ get value(): string {
+ return [
+ '1',
+ this.encode(this.traceId!.toString()),
+ this.encode(this.segmentId!.toString()),
+ this.spanId!.toString(),
+ this.encode(this.service!),
+ this.encode(this.serviceInstance!),
+ this.encode(this.endpoint!),
+ this.encode(this.clientAddress!),
+ ].join('-');
+ }
+
+ set value(val) {
+ const parts = val.split('-');
+ this.traceId = new ID(this.decode(parts[1]));
+ this.segmentId = new ID(this.decode(parts[2]));
+ this.spanId = Number.parseInt(parts[3], 10);
+ this.service = this.decode(parts[4]);
+ this.serviceInstance = this.decode(parts[5]);
+ this.endpoint = this.decode(parts[6]);
+ this.clientAddress = this.decode(parts[7]);
+ }
+}
diff --git a/src/trace/context/DummyContext.ts b/src/trace/context/DummyContext.ts
index 820fd81..a86b4ee 100644
--- a/src/trace/context/DummyContext.ts
+++ b/src/trace/context/DummyContext.ts
@@ -18,13 +18,13 @@
*/
import Context from '@/trace/context/Context';
-import { ContextCarrier } from '@/trace/context/Carrier';
import Span from '@/trace/span/Span';
import DummySpan from '@/trace/span/DummySpan';
import Segment from '@/trace/context/Segment';
import { SpanType } from '@/proto/language-agent/Tracing_pb';
import Snapshot from '@/trace/context/Snapshot';
import ID from '@/trace/ID';
+import { ContextCarrier } from '@/trace/context/ContextCarrier';
export default class DummyContext implements Context {
span: Span = new DummySpan({
diff --git a/src/trace/context/SegmentRef.ts b/src/trace/context/SegmentRef.ts
index bedef5b..9c07abc 100644
--- a/src/trace/context/SegmentRef.ts
+++ b/src/trace/context/SegmentRef.ts
@@ -17,10 +17,10 @@
*
*/
-import { ContextCarrier } from '@/trace/context/Carrier';
import Snapshot from '@/trace/context/Snapshot';
import ID from '@/trace/ID';
import config from '@/config/AgentConfig';
+import { ContextCarrier } from '@/trace/context/ContextCarrier';
export default class SegmentRef {
private constructor(
@@ -45,13 +45,13 @@ export default class SegmentRef {
static fromCarrier(carrier: ContextCarrier): SegmentRef {
return new SegmentRef(
'CrossProcess',
- carrier.traceId,
- carrier.segmentId,
- carrier.spanId,
- carrier.service,
- carrier.serviceInstance,
- carrier.endpoint,
- carrier.clientAddress,
+ carrier.traceId!,
+ carrier.segmentId!,
+ carrier.spanId!,
+ carrier.service!,
+ carrier.serviceInstance!,
+ carrier.endpoint!,
+ carrier.clientAddress!,
);
}
diff --git a/src/trace/context/SpanContext.ts b/src/trace/context/SpanContext.ts
index 793a9b5..821dc93 100644
--- a/src/trace/context/SpanContext.ts
+++ b/src/trace/context/SpanContext.ts
@@ -18,7 +18,6 @@
*/
import Context from '@/trace/context/Context';
-import { ContextCarrier } from '@/trace/context/Carrier';
import Span from '@/trace/span/Span';
import Segment from '@/trace/context/Segment';
import EntrySpan from '@/trace/span/EntrySpan';
@@ -30,6 +29,7 @@ import { createLogger } from '@/logging';
import { executionAsyncId } from 'async_hooks';
import Snapshot from '@/trace/context/Snapshot';
import SegmentRef from '@/trace/context/SegmentRef';
+import { ContextCarrier } from '@/trace/context/ContextCarrier';
const logger = createLogger(__filename);
@@ -60,15 +60,21 @@ export default class SpanContext implements Context {
});
}
- return new EntrySpan({
+ const span = new EntrySpan({
id: this.spanId++,
parentId: this.parentId,
context: this,
operation,
});
+
+ if (carrier) {
+ span.inject(carrier);
+ }
+
+ return span;
}
- newExitSpan(operation: string, peer: string, carrier?: ContextCarrier): Span {
+ newExitSpan(operation: string, peer: string): Span {
if (logger.isDebugEnabled()) {
logger.debug('Creating exit span', {
parentId: this.parentId,
diff --git a/src/trace/span/EntrySpan.ts b/src/trace/span/EntrySpan.ts
index a04149b..d87be6e 100644
--- a/src/trace/span/EntrySpan.ts
+++ b/src/trace/span/EntrySpan.ts
@@ -20,9 +20,9 @@
import StackedSpan from '@/trace/span/StackedSpan';
import { Component } from '@/trace/Component';
import { SpanCtorOptions } from '@/trace/span/Span';
-import { ContextCarrier } from '@/trace/context/Carrier';
import SegmentRef from '@/trace/context/SegmentRef';
import { SpanLayer, SpanType } from '@/proto/language-agent/Tracing_pb';
+import { ContextCarrier } from '@/trace/context/ContextCarrier';
export default class EntrySpan extends StackedSpan {
maxDepth = 0;
@@ -45,8 +45,8 @@ export default class EntrySpan extends StackedSpan {
return this;
}
- extract(carrier: ContextCarrier): this {
- super.extract(carrier);
+ inject(carrier: ContextCarrier): this {
+ super.inject(carrier);
const ref = SegmentRef.fromCarrier(carrier);
diff --git a/src/trace/span/ExitSpan.ts b/src/trace/span/ExitSpan.ts
index 1e2e63d..fd0f516 100644
--- a/src/trace/span/ExitSpan.ts
+++ b/src/trace/span/ExitSpan.ts
@@ -19,9 +19,9 @@
import StackedSpan from '@/trace/span/StackedSpan';
import { SpanCtorOptions } from '@/trace/span/Span';
-import { ContextCarrier } from '@/trace/context/Carrier';
import config from '@/config/AgentConfig';
import { SpanType } from '@/proto/language-agent/Tracing_pb';
+import { ContextCarrier } from '@/trace/context/ContextCarrier';
export default class ExitSpan extends StackedSpan {
constructor(options: SpanCtorOptions) {
@@ -35,15 +35,16 @@ export default class ExitSpan extends StackedSpan {
return super.start();
}
- inject(carrier: ContextCarrier): this {
- carrier.traceId = this.context.segment.relatedTraces[0];
- carrier.segmentId = this.context.segment.segmentId;
- carrier.spanId = this.id;
- carrier.service = config.serviceName;
- carrier.serviceInstance = config.serviceInstance;
- carrier.endpoint = this.context.spans[0].operation;
- carrier.clientAddress = this.peer;
-
- return this;
+ extract(): ContextCarrier {
+ return new ContextCarrier(
+ this.context.segment.relatedTraces[0],
+ this.context.segment.segmentId,
+ this.id,
+ config.serviceName,
+ config.serviceInstance,
+ this.context.spans[0].operation,
+ this.peer,
+ [],
+ );
}
}
diff --git a/src/trace/span/Span.ts b/src/trace/span/Span.ts
index 75d0d85..e893fd4 100644
--- a/src/trace/span/Span.ts
+++ b/src/trace/span/Span.ts
@@ -22,11 +22,11 @@ import { Component } from '@/trace/Component';
import { Tag } from '@/Tag';
import Log, { LogItem } from '@/Log';
import Segment from '@/trace/context/Segment';
-import { ContextCarrier } from '@/trace/context/Carrier';
import SegmentRef from '@/trace/context/SegmentRef';
import { SpanLayer, SpanType } from '@/proto/language-agent/Tracing_pb';
import { createLogger } from '@/logging';
import * as packageInfo from 'package.json';
+import { ContextCarrier } from '@/trace/context/ContextCarrier';
export type SpanCtorOptions = {
context: Context;
@@ -92,15 +92,15 @@ export default abstract class Span {
}
// noinspection JSUnusedLocalSymbols
- inject(carrier: ContextCarrier): this {
+ extract(): ContextCarrier {
throw new Error(`
can only inject context carrier into ExitSpan, this may be a potential bug in the agent,
please report this in ${packageInfo.bugs.url} if you encounter this.
`);
}
- extract(carrier: ContextCarrier): this {
- this.context.segment.relate(carrier.traceId);
+ inject(carrier: ContextCarrier): this {
+ this.context.segment.relate(carrier.traceId!);
return this;
}