feat(plugin-http): add/modify attributes (#643)
* feat(plugin-http): add/modify attributes closes #373, #394 Signed-off-by: Olivier Albertini <olivier.albertini@montreal.ca> * fix: change remotePort to localPort refactor: remove useless checks test: add assertions Signed-off-by: Olivier Albertini <olivier.albertini@montreal.ca> * test(plugin-https): sync with http plugin Signed-off-by: Olivier Albertini <olivier.albertini@montreal.ca> Co-authored-by: Mayur Kale <mayurkale@google.com>
This commit is contained in:
parent
e859b8e930
commit
3d9b8228b5
|
@ -54,7 +54,7 @@ Http plugin has few options available to choose from. You can set the following:
|
||||||
| [`applyCustomAttributesOnSpan`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L52) | `HttpCustomAttributeFunction` | Function for adding custom attributes |
|
| [`applyCustomAttributesOnSpan`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L52) | `HttpCustomAttributeFunction` | Function for adding custom attributes |
|
||||||
| [`ignoreIncomingPaths`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all incoming requests that match paths |
|
| [`ignoreIncomingPaths`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all incoming requests that match paths |
|
||||||
| [`ignoreOutgoingUrls`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all outgoing requests that match urls |
|
| [`ignoreOutgoingUrls`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all outgoing requests that match urls |
|
||||||
|
| [`serverName`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `string` | The primary server name of the matched virtual host. |
|
||||||
## Useful links
|
## Useful links
|
||||||
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
|
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
|
||||||
- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
|
- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
|
||||||
|
|
|
@ -15,17 +15,29 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attributes Names according to [OpenTelemetry attributes specs](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md#semantic-conventions)
|
* Attributes Names according to [OpenTelemetry attributes specs](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md#common-attributes)
|
||||||
*/
|
*/
|
||||||
export enum AttributeNames {
|
export enum AttributeNames {
|
||||||
HTTP_HOSTNAME = 'http.hostname',
|
HTTP_HOST = 'http.host',
|
||||||
COMPONENT = 'component',
|
COMPONENT = 'component',
|
||||||
HTTP_METHOD = 'http.method',
|
HTTP_METHOD = 'http.method',
|
||||||
HTTP_PATH = 'http.path',
|
HTTP_TARGET = 'http.target',
|
||||||
HTTP_ROUTE = 'http.route',
|
HTTP_ROUTE = 'http.route',
|
||||||
HTTP_URL = 'http.url',
|
HTTP_URL = 'http.url',
|
||||||
HTTP_STATUS_CODE = 'http.status_code',
|
HTTP_STATUS_CODE = 'http.status_code',
|
||||||
HTTP_STATUS_TEXT = 'http.status_text',
|
HTTP_STATUS_TEXT = 'http.status_text',
|
||||||
|
HTTP_FLAVOR = 'http.flavor',
|
||||||
|
NET_PEER_IP = 'net.peer.ip',
|
||||||
|
NET_PEER_PORT = 'net.peer.port',
|
||||||
|
NET_PEER_NAME = 'net.peer.name',
|
||||||
|
NET_HOST_IP = 'net.host.ip',
|
||||||
|
NET_HOST_PORT = 'net.host.port',
|
||||||
|
NET_HOST_NAME = 'net.host.name',
|
||||||
|
NET_TRANSPORT = 'net.transport',
|
||||||
|
IP_TCP = 'IP.TCP',
|
||||||
|
IP_UDP = 'IP.UDP',
|
||||||
|
HTTP_SERVER_NAME = 'http.server_name',
|
||||||
|
HTTP_CLIENT_IP = 'http.client_ip',
|
||||||
// NOT ON OFFICIAL SPEC
|
// NOT ON OFFICIAL SPEC
|
||||||
HTTP_ERROR_NAME = 'http.error_name',
|
HTTP_ERROR_NAME = 'http.error_name',
|
||||||
HTTP_ERROR_MESSAGE = 'http.error_message',
|
HTTP_ERROR_MESSAGE = 'http.error_message',
|
||||||
|
|
|
@ -19,7 +19,6 @@ import {
|
||||||
Span,
|
Span,
|
||||||
SpanKind,
|
SpanKind,
|
||||||
SpanOptions,
|
SpanOptions,
|
||||||
Attributes,
|
|
||||||
CanonicalCode,
|
CanonicalCode,
|
||||||
Status,
|
Status,
|
||||||
} from '@opentelemetry/types';
|
} from '@opentelemetry/types';
|
||||||
|
@ -45,6 +44,7 @@ import {
|
||||||
import { Format } from './enums/Format';
|
import { Format } from './enums/Format';
|
||||||
import { AttributeNames } from './enums/AttributeNames';
|
import { AttributeNames } from './enums/AttributeNames';
|
||||||
import * as utils from './utils';
|
import * as utils from './utils';
|
||||||
|
import { Socket } from 'net';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Http instrumentation plugin for Opentelemetry
|
* Http instrumentation plugin for Opentelemetry
|
||||||
|
@ -183,37 +183,24 @@ export class HttpPlugin extends BasePlugin<Http> {
|
||||||
return (): ClientRequest => {
|
return (): ClientRequest => {
|
||||||
this._logger.debug('makeRequestTrace by injecting context into header');
|
this._logger.debug('makeRequestTrace by injecting context into header');
|
||||||
|
|
||||||
const host = options.hostname || options.host || 'localhost';
|
const hostname =
|
||||||
const method = options.method ? options.method.toUpperCase() : 'GET';
|
options.hostname ||
|
||||||
const headers = options.headers || {};
|
options.host?.replace(/^(.*)(\:[0-9]{1,5})/, '$1') ||
|
||||||
const userAgent = headers['user-agent'];
|
'localhost';
|
||||||
|
const attributes = utils.getOutgoingRequestAttributes(options, {
|
||||||
span.setAttributes({
|
component: this.component,
|
||||||
[AttributeNames.HTTP_URL]: utils.getAbsoluteUrl(
|
hostname,
|
||||||
options,
|
|
||||||
headers,
|
|
||||||
`${this.component}:`
|
|
||||||
),
|
|
||||||
[AttributeNames.HTTP_HOSTNAME]: host,
|
|
||||||
[AttributeNames.HTTP_METHOD]: method,
|
|
||||||
[AttributeNames.HTTP_PATH]: options.path || '/',
|
|
||||||
});
|
});
|
||||||
|
span.setAttributes(attributes);
|
||||||
if (userAgent !== undefined) {
|
|
||||||
span.setAttribute(AttributeNames.HTTP_USER_AGENT, userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
request.on(
|
request.on(
|
||||||
'response',
|
'response',
|
||||||
(
|
(response: IncomingMessage & { aborted?: boolean }) => {
|
||||||
response: IncomingMessage & { aborted?: boolean; req: ClientRequest }
|
const attributes = utils.getOutgoingRequestAttributesOnResponse(
|
||||||
) => {
|
response,
|
||||||
if (response.statusCode) {
|
{ hostname }
|
||||||
span.setAttributes({
|
);
|
||||||
[AttributeNames.HTTP_STATUS_CODE]: response.statusCode,
|
span.setAttributes(attributes);
|
||||||
[AttributeNames.HTTP_STATUS_TEXT]: response.statusMessage,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._tracer.bind(response);
|
this._tracer.bind(response);
|
||||||
this._logger.debug('outgoingRequest on response()');
|
this._logger.debug('outgoingRequest on response()');
|
||||||
|
@ -280,7 +267,7 @@ export class HttpPlugin extends BasePlugin<Http> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const request = args[0] as IncomingMessage;
|
const request = args[0] as IncomingMessage;
|
||||||
const response = args[1] as ServerResponse;
|
const response = args[1] as ServerResponse & { socket: Socket };
|
||||||
const pathname = request.url
|
const pathname = request.url
|
||||||
? url.parse(request.url).pathname || '/'
|
? url.parse(request.url).pathname || '/'
|
||||||
: '/';
|
: '/';
|
||||||
|
@ -301,8 +288,13 @@ export class HttpPlugin extends BasePlugin<Http> {
|
||||||
|
|
||||||
const propagation = plugin._tracer.getHttpTextFormat();
|
const propagation = plugin._tracer.getHttpTextFormat();
|
||||||
const headers = request.headers;
|
const headers = request.headers;
|
||||||
|
|
||||||
const spanOptions: SpanOptions = {
|
const spanOptions: SpanOptions = {
|
||||||
kind: SpanKind.SERVER,
|
kind: SpanKind.SERVER,
|
||||||
|
attributes: utils.getIncomingRequestAttributes(request, {
|
||||||
|
component: plugin.component,
|
||||||
|
serverName: plugin._config.serverName,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const spanContext = propagation.extract(Format.HTTP, headers);
|
const spanContext = propagation.extract(Format.HTTP, headers);
|
||||||
|
@ -332,32 +324,10 @@ export class HttpPlugin extends BasePlugin<Http> {
|
||||||
() => response.end.apply(this, arguments as any),
|
() => response.end.apply(this, arguments as any),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
const requestUrl = request.url ? url.parse(request.url) : null;
|
|
||||||
const hostname = headers.host
|
|
||||||
? headers.host.replace(/^(.*)(\:[0-9]{1,5})/, '$1')
|
|
||||||
: 'localhost';
|
|
||||||
const userAgent = headers['user-agent'];
|
|
||||||
|
|
||||||
const attributes: Attributes = {
|
const attributes = utils.getIncomingRequestAttributesOnResponse(
|
||||||
[AttributeNames.HTTP_URL]: utils.getAbsoluteUrl(
|
response
|
||||||
requestUrl,
|
);
|
||||||
headers,
|
|
||||||
`${plugin.component}:`
|
|
||||||
),
|
|
||||||
[AttributeNames.HTTP_HOSTNAME]: hostname,
|
|
||||||
[AttributeNames.HTTP_METHOD]: method,
|
|
||||||
[AttributeNames.HTTP_STATUS_CODE]: response.statusCode,
|
|
||||||
[AttributeNames.HTTP_STATUS_TEXT]: response.statusMessage,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (requestUrl) {
|
|
||||||
attributes[AttributeNames.HTTP_PATH] = requestUrl.path || '/';
|
|
||||||
attributes[AttributeNames.HTTP_ROUTE] = requestUrl.pathname || '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userAgent !== undefined) {
|
|
||||||
attributes[AttributeNames.HTTP_USER_AGENT] = userAgent;
|
|
||||||
}
|
|
||||||
|
|
||||||
span
|
span
|
||||||
.setAttributes(attributes)
|
.setAttributes(attributes)
|
||||||
|
|
|
@ -57,10 +57,18 @@ export interface HttpCustomAttributeFunction {
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options available for the HTTP Plugin (see [documentation](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-http#http-plugin-options))
|
||||||
|
*/
|
||||||
export interface HttpPluginConfig extends PluginConfig {
|
export interface HttpPluginConfig extends PluginConfig {
|
||||||
|
/** Not trace all incoming requests that match paths */
|
||||||
ignoreIncomingPaths?: IgnoreMatcher[];
|
ignoreIncomingPaths?: IgnoreMatcher[];
|
||||||
|
/** Not trace all outgoing requests that match urls */
|
||||||
ignoreOutgoingUrls?: IgnoreMatcher[];
|
ignoreOutgoingUrls?: IgnoreMatcher[];
|
||||||
|
/** Function for adding custom attributes */
|
||||||
applyCustomAttributesOnSpan?: HttpCustomAttributeFunction;
|
applyCustomAttributesOnSpan?: HttpCustomAttributeFunction;
|
||||||
|
/** The primary server name of the matched virtual host. */
|
||||||
|
serverName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Err extends Error {
|
export interface Err extends Error {
|
||||||
|
|
|
@ -14,17 +14,19 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Status, CanonicalCode, Span } from '@opentelemetry/types';
|
import { Status, CanonicalCode, Span, Attributes } from '@opentelemetry/types';
|
||||||
import {
|
import {
|
||||||
RequestOptions,
|
RequestOptions,
|
||||||
IncomingMessage,
|
IncomingMessage,
|
||||||
ClientRequest,
|
ClientRequest,
|
||||||
IncomingHttpHeaders,
|
IncomingHttpHeaders,
|
||||||
OutgoingHttpHeaders,
|
OutgoingHttpHeaders,
|
||||||
|
ServerResponse,
|
||||||
} from 'http';
|
} from 'http';
|
||||||
import { IgnoreMatcher, Err, ParsedRequestOptions } from './types';
|
import { IgnoreMatcher, Err, ParsedRequestOptions } from './types';
|
||||||
import { AttributeNames } from './enums/AttributeNames';
|
import { AttributeNames } from './enums/AttributeNames';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
|
import { Socket } from 'net';
|
||||||
|
|
||||||
export const OT_REQUEST_HEADER = 'x-opentelemetry-outgoing-request';
|
export const OT_REQUEST_HEADER = 'x-opentelemetry-outgoing-request';
|
||||||
/**
|
/**
|
||||||
|
@ -280,3 +282,157 @@ export const isValidOptionsType = (options: unknown): boolean => {
|
||||||
export const isOpenTelemetryRequest = (options: RequestOptions) => {
|
export const isOpenTelemetryRequest = (options: RequestOptions) => {
|
||||||
return !!(options && options.headers && options.headers[OT_REQUEST_HEADER]);
|
return !!(options && options.headers && options.headers[OT_REQUEST_HEADER]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns outgoing request attributes scoped to the options passed to the request
|
||||||
|
* @param {ParsedRequestOptions} requestOptions the same options used to make the request
|
||||||
|
* @param {{ component: string, hostname: string }} options used to pass data needed to create attributes
|
||||||
|
*/
|
||||||
|
export const getOutgoingRequestAttributes = (
|
||||||
|
requestOptions: ParsedRequestOptions,
|
||||||
|
options: { component: string; hostname: string }
|
||||||
|
): Attributes => {
|
||||||
|
const host = requestOptions.host;
|
||||||
|
const hostname =
|
||||||
|
requestOptions.hostname ||
|
||||||
|
host?.replace(/^(.*)(\:[0-9]{1,5})/, '$1') ||
|
||||||
|
'localhost';
|
||||||
|
const requestMethod = requestOptions.method;
|
||||||
|
const method = requestMethod ? requestMethod.toUpperCase() : 'GET';
|
||||||
|
const headers = requestOptions.headers || {};
|
||||||
|
const userAgent = headers['user-agent'];
|
||||||
|
|
||||||
|
const attributes: Attributes = {
|
||||||
|
[AttributeNames.HTTP_URL]: getAbsoluteUrl(
|
||||||
|
requestOptions,
|
||||||
|
headers,
|
||||||
|
`${options.component}:`
|
||||||
|
),
|
||||||
|
[AttributeNames.HTTP_METHOD]: method,
|
||||||
|
[AttributeNames.HTTP_TARGET]: requestOptions.path || '/',
|
||||||
|
[AttributeNames.NET_PEER_NAME]: hostname,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (userAgent !== undefined) {
|
||||||
|
attributes[AttributeNames.HTTP_USER_AGENT] = userAgent;
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns attributes related to the kind of HTTP protocol used
|
||||||
|
* @param {string} [kind] Kind of HTTP protocol used: "1.0", "1.1", "2", "SPDY" or "QUIC".
|
||||||
|
*/
|
||||||
|
export const getAttributesFromHttpKind = (kind?: string): Attributes => {
|
||||||
|
const attributes: Attributes = {};
|
||||||
|
if (kind) {
|
||||||
|
attributes[AttributeNames.HTTP_FLAVOR] = kind;
|
||||||
|
if (kind.toUpperCase() !== 'QUIC') {
|
||||||
|
attributes[AttributeNames.NET_TRANSPORT] = AttributeNames.IP_TCP;
|
||||||
|
} else {
|
||||||
|
attributes[AttributeNames.NET_TRANSPORT] = AttributeNames.IP_UDP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns outgoing request attributes scoped to the response data
|
||||||
|
* @param {IncomingMessage} response the response object
|
||||||
|
* @param {{ hostname: string }} options used to pass data needed to create attributes
|
||||||
|
*/
|
||||||
|
export const getOutgoingRequestAttributesOnResponse = (
|
||||||
|
response: IncomingMessage,
|
||||||
|
options: { hostname: string }
|
||||||
|
): Attributes => {
|
||||||
|
const { statusCode, statusMessage, httpVersion, socket } = response;
|
||||||
|
const { remoteAddress, remotePort } = socket;
|
||||||
|
const attributes: Attributes = {
|
||||||
|
[AttributeNames.NET_PEER_IP]: remoteAddress,
|
||||||
|
[AttributeNames.NET_PEER_PORT]: remotePort,
|
||||||
|
[AttributeNames.HTTP_HOST]: `${options.hostname}:${remotePort}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (statusCode) {
|
||||||
|
attributes[AttributeNames.HTTP_STATUS_CODE] = statusCode;
|
||||||
|
attributes[AttributeNames.HTTP_STATUS_TEXT] = (
|
||||||
|
statusMessage || ''
|
||||||
|
).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
const httpKindAttributes = getAttributesFromHttpKind(httpVersion);
|
||||||
|
return Object.assign(attributes, httpKindAttributes);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns incoming request attributes scoped to the request data
|
||||||
|
* @param {IncomingMessage} request the request object
|
||||||
|
* @param {{ component: string, serverName?: string }} options used to pass data needed to create attributes
|
||||||
|
*/
|
||||||
|
export const getIncomingRequestAttributes = (
|
||||||
|
request: IncomingMessage,
|
||||||
|
options: { component: string; serverName?: string }
|
||||||
|
): Attributes => {
|
||||||
|
const headers = request.headers;
|
||||||
|
const userAgent = headers['user-agent'];
|
||||||
|
const ips = headers['x-forwarded-for'];
|
||||||
|
const method = request.method || 'GET';
|
||||||
|
const httpVersion = request.httpVersion;
|
||||||
|
const requestUrl = request.url ? url.parse(request.url) : null;
|
||||||
|
const host = requestUrl?.host || headers.host;
|
||||||
|
const hostname =
|
||||||
|
requestUrl?.hostname ||
|
||||||
|
host?.replace(/^(.*)(\:[0-9]{1,5})/, '$1') ||
|
||||||
|
'localhost';
|
||||||
|
const serverName = options.serverName;
|
||||||
|
const attributes: Attributes = {
|
||||||
|
[AttributeNames.HTTP_URL]: getAbsoluteUrl(
|
||||||
|
requestUrl,
|
||||||
|
headers,
|
||||||
|
`${options.component}:`
|
||||||
|
),
|
||||||
|
[AttributeNames.HTTP_HOST]: host,
|
||||||
|
[AttributeNames.NET_HOST_NAME]: hostname,
|
||||||
|
[AttributeNames.HTTP_METHOD]: method,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof ips === 'string') {
|
||||||
|
attributes[AttributeNames.HTTP_CLIENT_IP] = ips.split(',')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof serverName === 'string') {
|
||||||
|
attributes[AttributeNames.HTTP_SERVER_NAME] = serverName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestUrl) {
|
||||||
|
attributes[AttributeNames.HTTP_TARGET] = requestUrl.path || '/';
|
||||||
|
attributes[AttributeNames.HTTP_ROUTE] = requestUrl.pathname || '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userAgent !== undefined) {
|
||||||
|
attributes[AttributeNames.HTTP_USER_AGENT] = userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const httpKindAttributes = getAttributesFromHttpKind(httpVersion);
|
||||||
|
return Object.assign(attributes, httpKindAttributes);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns incoming request attributes scoped to the response data
|
||||||
|
* @param {(ServerResponse & { socket: Socket; })} response the response object
|
||||||
|
*/
|
||||||
|
export const getIncomingRequestAttributesOnResponse = (
|
||||||
|
response: ServerResponse & { socket: Socket }
|
||||||
|
): Attributes => {
|
||||||
|
const { statusCode, statusMessage, socket } = response;
|
||||||
|
const { localAddress, localPort, remoteAddress, remotePort } = socket;
|
||||||
|
|
||||||
|
return {
|
||||||
|
[AttributeNames.NET_HOST_IP]: localAddress,
|
||||||
|
[AttributeNames.NET_HOST_PORT]: localPort,
|
||||||
|
[AttributeNames.NET_PEER_IP]: remoteAddress,
|
||||||
|
[AttributeNames.NET_PEER_PORT]: remotePort,
|
||||||
|
[AttributeNames.HTTP_STATUS_CODE]: statusCode,
|
||||||
|
[AttributeNames.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -41,6 +41,7 @@ const serverPort = 22345;
|
||||||
const protocol = 'http';
|
const protocol = 'http';
|
||||||
const hostname = 'localhost';
|
const hostname = 'localhost';
|
||||||
const pathname = '/test';
|
const pathname = '/test';
|
||||||
|
const serverName = 'my.server.name';
|
||||||
const memoryExporter = new InMemorySpanExporter();
|
const memoryExporter = new InMemorySpanExporter();
|
||||||
const httpTextFormat = new DummyPropagation();
|
const httpTextFormat = new DummyPropagation();
|
||||||
const logger = new NoopLogger();
|
const logger = new NoopLogger();
|
||||||
|
@ -140,6 +141,14 @@ describe('HttpPlugin', () => {
|
||||||
assert.strictEqual(spans.length, 2);
|
assert.strictEqual(spans.length, 2);
|
||||||
assertSpan(incomingSpan, SpanKind.SERVER, validations);
|
assertSpan(incomingSpan, SpanKind.SERVER, validations);
|
||||||
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
|
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
|
||||||
|
assert.strictEqual(
|
||||||
|
incomingSpan.attributes[AttributeNames.NET_HOST_PORT],
|
||||||
|
serverPort
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
outgoingSpan.attributes[AttributeNames.NET_PEER_PORT],
|
||||||
|
serverPort
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
|
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
|
||||||
|
@ -176,6 +185,7 @@ describe('HttpPlugin', () => {
|
||||||
(url: string) => url.endsWith(`/ignored/function`),
|
(url: string) => url.endsWith(`/ignored/function`),
|
||||||
],
|
],
|
||||||
applyCustomAttributesOnSpan: customAttributeFunction,
|
applyCustomAttributesOnSpan: customAttributeFunction,
|
||||||
|
serverName,
|
||||||
};
|
};
|
||||||
plugin.enable(http, tracer, tracer.logger, config);
|
plugin.enable(http, tracer, tracer.logger, config);
|
||||||
server = http.createServer((request, response) => {
|
server = http.createServer((request, response) => {
|
||||||
|
@ -204,7 +214,13 @@ describe('HttpPlugin', () => {
|
||||||
|
|
||||||
it('should generate valid spans (client side and server side)', async () => {
|
it('should generate valid spans (client side and server side)', async () => {
|
||||||
const result = await httpRequest.get(
|
const result = await httpRequest.get(
|
||||||
`${protocol}://${hostname}:${serverPort}${pathname}`
|
`${protocol}://${hostname}:${serverPort}${pathname}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'x-forwarded-for': '<client>, <proxy1>, <proxy2>',
|
||||||
|
'user-agent': 'chrome',
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
const spans = memoryExporter.getFinishedSpans();
|
const spans = memoryExporter.getFinishedSpans();
|
||||||
const [incomingSpan, outgoingSpan] = spans;
|
const [incomingSpan, outgoingSpan] = spans;
|
||||||
|
@ -216,11 +232,36 @@ describe('HttpPlugin', () => {
|
||||||
resHeaders: result.resHeaders,
|
resHeaders: result.resHeaders,
|
||||||
reqHeaders: result.reqHeaders,
|
reqHeaders: result.reqHeaders,
|
||||||
component: plugin.component,
|
component: plugin.component,
|
||||||
|
serverName,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert.strictEqual(spans.length, 2);
|
assert.strictEqual(spans.length, 2);
|
||||||
assertSpan(incomingSpan, SpanKind.SERVER, validations);
|
assert.strictEqual(
|
||||||
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
|
incomingSpan.attributes[AttributeNames.HTTP_CLIENT_IP],
|
||||||
|
'<client>'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
incomingSpan.attributes[AttributeNames.NET_HOST_PORT],
|
||||||
|
serverPort
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
outgoingSpan.attributes[AttributeNames.NET_PEER_PORT],
|
||||||
|
serverPort
|
||||||
|
);
|
||||||
|
[
|
||||||
|
{ span: incomingSpan, kind: SpanKind.SERVER },
|
||||||
|
{ span: outgoingSpan, kind: SpanKind.CLIENT },
|
||||||
|
].forEach(({ span, kind }) => {
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.HTTP_FLAVOR],
|
||||||
|
'1.1'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.NET_TRANSPORT],
|
||||||
|
AttributeNames.IP_TCP
|
||||||
|
);
|
||||||
|
assertSpan(span, kind, validations);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
|
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
|
||||||
|
@ -545,7 +586,7 @@ describe('HttpPlugin', () => {
|
||||||
const [span] = spans;
|
const [span] = spans;
|
||||||
assert.strictEqual(spans.length, 1);
|
assert.strictEqual(spans.length, 1);
|
||||||
assert.strictEqual(span.status.code, CanonicalCode.ABORTED);
|
assert.strictEqual(span.status.code, CanonicalCode.ABORTED);
|
||||||
assert.ok(Object.keys(span.attributes).length > 6);
|
assert.ok(Object.keys(span.attributes).length >= 6);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import {
|
||||||
SimpleSpanProcessor,
|
SimpleSpanProcessor,
|
||||||
} from '@opentelemetry/tracing';
|
} from '@opentelemetry/tracing';
|
||||||
import { HttpPluginConfig } from '../../src/types';
|
import { HttpPluginConfig } from '../../src/types';
|
||||||
|
import { AttributeNames } from '../../src/enums/AttributeNames';
|
||||||
const protocol = 'http';
|
const protocol = 'http';
|
||||||
const serverPort = 32345;
|
const serverPort = 32345;
|
||||||
const hostname = 'localhost';
|
const hostname = 'localhost';
|
||||||
|
@ -141,7 +142,7 @@ describe('HttpPlugin Integration tests', () => {
|
||||||
assertSpan(span, SpanKind.CLIENT, validations);
|
assertSpan(span, SpanKind.CLIENT, validations);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a rootSpan for GET requests and add propagation headers if URL and options are used', async () => {
|
it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => {
|
||||||
let spans = memoryExporter.getFinishedSpans();
|
let spans = memoryExporter.getFinishedSpans();
|
||||||
assert.strictEqual(spans.length, 0);
|
assert.strictEqual(spans.length, 0);
|
||||||
|
|
||||||
|
@ -166,6 +167,11 @@ describe('HttpPlugin Integration tests', () => {
|
||||||
assert.strictEqual(spans.length, 1);
|
assert.strictEqual(spans.length, 1);
|
||||||
assert.ok(span.name.indexOf('GET /') >= 0);
|
assert.ok(span.name.indexOf('GET /') >= 0);
|
||||||
assert.strictEqual(result.reqHeaders['x-foo'], 'foo');
|
assert.strictEqual(result.reqHeaders['x-foo'], 'foo');
|
||||||
|
assert.strictEqual(span.attributes[AttributeNames.HTTP_FLAVOR], '1.1');
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.NET_TRANSPORT],
|
||||||
|
AttributeNames.IP_TCP
|
||||||
|
);
|
||||||
assertSpan(span, SpanKind.CLIENT, validations);
|
assertSpan(span, SpanKind.CLIENT, validations);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ export const assertSpan = (
|
||||||
reqHeaders?: http.OutgoingHttpHeaders;
|
reqHeaders?: http.OutgoingHttpHeaders;
|
||||||
path?: string | null;
|
path?: string | null;
|
||||||
forceStatus?: Status;
|
forceStatus?: Status;
|
||||||
|
serverName?: string;
|
||||||
component: string;
|
component: string;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
|
@ -53,16 +54,12 @@ export const assertSpan = (
|
||||||
span.attributes[AttributeNames.HTTP_ERROR_MESSAGE],
|
span.attributes[AttributeNames.HTTP_ERROR_MESSAGE],
|
||||||
span.status.message
|
span.status.message
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
|
||||||
span.attributes[AttributeNames.HTTP_HOSTNAME],
|
|
||||||
validations.hostname
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
span.attributes[AttributeNames.HTTP_METHOD],
|
span.attributes[AttributeNames.HTTP_METHOD],
|
||||||
validations.httpMethod
|
validations.httpMethod
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
span.attributes[AttributeNames.HTTP_PATH],
|
span.attributes[AttributeNames.HTTP_TARGET],
|
||||||
validations.path || validations.pathname
|
validations.path || validations.pathname
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
|
@ -79,12 +76,6 @@ export const assertSpan = (
|
||||||
utils.parseResponseStatus(validations.httpStatusCode)
|
utils.parseResponseStatus(validations.httpStatusCode)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
(span.attributes[AttributeNames.HTTP_URL] as string).indexOf(
|
|
||||||
span.attributes[AttributeNames.HTTP_HOSTNAME] as string
|
|
||||||
) > -1,
|
|
||||||
'should be consistent'
|
|
||||||
);
|
|
||||||
assert.ok(span.endTime, 'must be finished');
|
assert.ok(span.endTime, 'must be finished');
|
||||||
assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration');
|
assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration');
|
||||||
|
|
||||||
|
@ -97,8 +88,40 @@ export const assertSpan = (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (span.kind === SpanKind.CLIENT) {
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.NET_PEER_NAME],
|
||||||
|
validations.hostname,
|
||||||
|
'must be consistent (PEER_NAME and hostname)'
|
||||||
|
);
|
||||||
|
assert.ok(span.attributes[AttributeNames.NET_PEER_IP], 'must have PEER_IP');
|
||||||
|
assert.ok(
|
||||||
|
span.attributes[AttributeNames.NET_PEER_PORT],
|
||||||
|
'must have PEER_PORT'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
(span.attributes[AttributeNames.HTTP_URL] as string).indexOf(
|
||||||
|
span.attributes[AttributeNames.NET_PEER_NAME] as string
|
||||||
|
) > -1,
|
||||||
|
'must be consistent'
|
||||||
|
);
|
||||||
|
}
|
||||||
if (span.kind === SpanKind.SERVER) {
|
if (span.kind === SpanKind.SERVER) {
|
||||||
|
if (validations.serverName) {
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.HTTP_SERVER_NAME],
|
||||||
|
validations.serverName,
|
||||||
|
' must have serverName attribute'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
span.attributes[AttributeNames.NET_HOST_PORT],
|
||||||
|
'must have HOST_PORT'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
span.attributes[AttributeNames.NET_HOST_IP],
|
||||||
|
'must have HOST_IP'
|
||||||
|
);
|
||||||
|
}
|
||||||
assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY);
|
assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY);
|
||||||
} else if (validations.reqHeaders) {
|
} else if (validations.reqHeaders) {
|
||||||
assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]);
|
assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]);
|
||||||
|
|
|
@ -45,6 +45,7 @@ let server: https.Server;
|
||||||
const serverPort = 32345;
|
const serverPort = 32345;
|
||||||
const protocol = 'https';
|
const protocol = 'https';
|
||||||
const hostname = 'localhost';
|
const hostname = 'localhost';
|
||||||
|
const serverName = 'my.server.name';
|
||||||
const pathname = '/test';
|
const pathname = '/test';
|
||||||
const memoryExporter = new InMemorySpanExporter();
|
const memoryExporter = new InMemorySpanExporter();
|
||||||
const httpTextFormat = new DummyPropagation();
|
const httpTextFormat = new DummyPropagation();
|
||||||
|
@ -153,6 +154,14 @@ describe('HttpsPlugin', () => {
|
||||||
assert.strictEqual(spans.length, 2);
|
assert.strictEqual(spans.length, 2);
|
||||||
assertSpan(incomingSpan, SpanKind.SERVER, validations);
|
assertSpan(incomingSpan, SpanKind.SERVER, validations);
|
||||||
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
|
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
|
||||||
|
assert.strictEqual(
|
||||||
|
incomingSpan.attributes[AttributeNames.NET_HOST_PORT],
|
||||||
|
serverPort
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
outgoingSpan.attributes[AttributeNames.NET_PEER_PORT],
|
||||||
|
serverPort
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
|
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
|
||||||
|
@ -189,6 +198,7 @@ describe('HttpsPlugin', () => {
|
||||||
(url: string) => url.endsWith(`/ignored/function`),
|
(url: string) => url.endsWith(`/ignored/function`),
|
||||||
],
|
],
|
||||||
applyCustomAttributesOnSpan: customAttributeFunction,
|
applyCustomAttributesOnSpan: customAttributeFunction,
|
||||||
|
serverName,
|
||||||
};
|
};
|
||||||
plugin.enable(
|
plugin.enable(
|
||||||
(https as unknown) as Http,
|
(https as unknown) as Http,
|
||||||
|
@ -230,7 +240,13 @@ describe('HttpsPlugin', () => {
|
||||||
|
|
||||||
it('should generate valid spans (client side and server side)', async () => {
|
it('should generate valid spans (client side and server side)', async () => {
|
||||||
const result = await httpsRequest.get(
|
const result = await httpsRequest.get(
|
||||||
`${protocol}://${hostname}:${serverPort}${pathname}`
|
`${protocol}://${hostname}:${serverPort}${pathname}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'x-forwarded-for': '<client>, <proxy1>, <proxy2>',
|
||||||
|
'user-agent': 'chrome',
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
const spans = memoryExporter.getFinishedSpans();
|
const spans = memoryExporter.getFinishedSpans();
|
||||||
const [incomingSpan, outgoingSpan] = spans;
|
const [incomingSpan, outgoingSpan] = spans;
|
||||||
|
@ -242,11 +258,37 @@ describe('HttpsPlugin', () => {
|
||||||
resHeaders: result.resHeaders,
|
resHeaders: result.resHeaders,
|
||||||
reqHeaders: result.reqHeaders,
|
reqHeaders: result.reqHeaders,
|
||||||
component: plugin.component,
|
component: plugin.component,
|
||||||
|
serverName,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert.strictEqual(spans.length, 2);
|
assert.strictEqual(spans.length, 2);
|
||||||
assertSpan(incomingSpan, SpanKind.SERVER, validations);
|
assert.strictEqual(
|
||||||
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
|
incomingSpan.attributes[AttributeNames.HTTP_CLIENT_IP],
|
||||||
|
'<client>'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
incomingSpan.attributes[AttributeNames.NET_HOST_PORT],
|
||||||
|
serverPort
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
outgoingSpan.attributes[AttributeNames.NET_PEER_PORT],
|
||||||
|
serverPort
|
||||||
|
);
|
||||||
|
|
||||||
|
[
|
||||||
|
{ span: incomingSpan, kind: SpanKind.SERVER },
|
||||||
|
{ span: outgoingSpan, kind: SpanKind.CLIENT },
|
||||||
|
].forEach(({ span, kind }) => {
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.HTTP_FLAVOR],
|
||||||
|
'1.1'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.NET_TRANSPORT],
|
||||||
|
AttributeNames.IP_TCP
|
||||||
|
);
|
||||||
|
assertSpan(span, kind, validations);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
|
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
|
||||||
|
@ -571,7 +613,7 @@ describe('HttpsPlugin', () => {
|
||||||
const [span] = spans;
|
const [span] = spans;
|
||||||
assert.strictEqual(spans.length, 1);
|
assert.strictEqual(spans.length, 1);
|
||||||
assert.strictEqual(span.status.code, CanonicalCode.ABORTED);
|
assert.strictEqual(span.status.code, CanonicalCode.ABORTED);
|
||||||
assert.ok(Object.keys(span.attributes).length > 6);
|
assert.ok(Object.keys(span.attributes).length >= 6);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NoopLogger } from '@opentelemetry/core';
|
import { NoopLogger } from '@opentelemetry/core';
|
||||||
import { HttpPluginConfig, Http } from '@opentelemetry/plugin-http';
|
import {
|
||||||
|
HttpPluginConfig,
|
||||||
|
Http,
|
||||||
|
AttributeNames,
|
||||||
|
} from '@opentelemetry/plugin-http';
|
||||||
import { SpanKind, Span } from '@opentelemetry/types';
|
import { SpanKind, Span } from '@opentelemetry/types';
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
|
@ -143,7 +147,7 @@ describe('HttpsPlugin Integration tests', () => {
|
||||||
assertSpan(span, SpanKind.CLIENT, validations);
|
assertSpan(span, SpanKind.CLIENT, validations);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a rootSpan for GET requests and add propagation headers if URL and options are used', async () => {
|
it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => {
|
||||||
let spans = memoryExporter.getFinishedSpans();
|
let spans = memoryExporter.getFinishedSpans();
|
||||||
assert.strictEqual(spans.length, 0);
|
assert.strictEqual(spans.length, 0);
|
||||||
|
|
||||||
|
@ -168,6 +172,11 @@ describe('HttpsPlugin Integration tests', () => {
|
||||||
assert.strictEqual(spans.length, 1);
|
assert.strictEqual(spans.length, 1);
|
||||||
assert.ok(span.name.indexOf('GET /') >= 0);
|
assert.ok(span.name.indexOf('GET /') >= 0);
|
||||||
assert.strictEqual(result.reqHeaders['x-foo'], 'foo');
|
assert.strictEqual(result.reqHeaders['x-foo'], 'foo');
|
||||||
|
assert.strictEqual(span.attributes[AttributeNames.HTTP_FLAVOR], '1.1');
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.NET_TRANSPORT],
|
||||||
|
AttributeNames.IP_TCP
|
||||||
|
);
|
||||||
assertSpan(span, SpanKind.CLIENT, validations);
|
assertSpan(span, SpanKind.CLIENT, validations);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,12 @@ import { SpanKind, Status } from '@opentelemetry/types';
|
||||||
import { hrTimeToNanoseconds } from '@opentelemetry/core';
|
import { hrTimeToNanoseconds } from '@opentelemetry/core';
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
|
import { DummyPropagation } from './DummyPropagation';
|
||||||
|
import { ReadableSpan } from '@opentelemetry/tracing';
|
||||||
import {
|
import {
|
||||||
AttributeNames,
|
AttributeNames,
|
||||||
parseResponseStatus,
|
parseResponseStatus,
|
||||||
} from '@opentelemetry/plugin-http';
|
} from '@opentelemetry/plugin-http';
|
||||||
import { DummyPropagation } from './DummyPropagation';
|
|
||||||
import { ReadableSpan } from '@opentelemetry/tracing';
|
|
||||||
|
|
||||||
export const assertSpan = (
|
export const assertSpan = (
|
||||||
span: ReadableSpan,
|
span: ReadableSpan,
|
||||||
|
@ -37,6 +37,7 @@ export const assertSpan = (
|
||||||
reqHeaders?: http.OutgoingHttpHeaders;
|
reqHeaders?: http.OutgoingHttpHeaders;
|
||||||
path?: string | null;
|
path?: string | null;
|
||||||
forceStatus?: Status;
|
forceStatus?: Status;
|
||||||
|
serverName?: string;
|
||||||
component: string;
|
component: string;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
|
@ -55,16 +56,12 @@ export const assertSpan = (
|
||||||
span.attributes[AttributeNames.HTTP_ERROR_MESSAGE],
|
span.attributes[AttributeNames.HTTP_ERROR_MESSAGE],
|
||||||
span.status.message
|
span.status.message
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
|
||||||
span.attributes[AttributeNames.HTTP_HOSTNAME],
|
|
||||||
validations.hostname
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
span.attributes[AttributeNames.HTTP_METHOD],
|
span.attributes[AttributeNames.HTTP_METHOD],
|
||||||
validations.httpMethod
|
validations.httpMethod
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
span.attributes[AttributeNames.HTTP_PATH],
|
span.attributes[AttributeNames.HTTP_TARGET],
|
||||||
validations.path || validations.pathname
|
validations.path || validations.pathname
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
|
@ -80,12 +77,6 @@ export const assertSpan = (
|
||||||
validations.forceStatus || parseResponseStatus(validations.httpStatusCode)
|
validations.forceStatus || parseResponseStatus(validations.httpStatusCode)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
(span.attributes[AttributeNames.HTTP_URL] as string).indexOf(
|
|
||||||
span.attributes[AttributeNames.HTTP_HOSTNAME] as string
|
|
||||||
) > -1,
|
|
||||||
'should be consistent'
|
|
||||||
);
|
|
||||||
assert.ok(span.endTime, 'must be finished');
|
assert.ok(span.endTime, 'must be finished');
|
||||||
assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration');
|
assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration');
|
||||||
|
|
||||||
|
@ -98,8 +89,37 @@ export const assertSpan = (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (span.kind === SpanKind.CLIENT) {
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.NET_PEER_NAME],
|
||||||
|
validations.hostname,
|
||||||
|
'must be consistent (PEER_NAME and hostname)'
|
||||||
|
);
|
||||||
|
assert.ok(span.attributes[AttributeNames.NET_PEER_IP], 'must have PEER_IP');
|
||||||
|
assert.ok(
|
||||||
|
span.attributes[AttributeNames.NET_PEER_PORT],
|
||||||
|
'must have PEER_PORT'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
(span.attributes[AttributeNames.HTTP_URL] as string).indexOf(
|
||||||
|
span.attributes[AttributeNames.NET_PEER_NAME] as string
|
||||||
|
) > -1,
|
||||||
|
'must be consistent'
|
||||||
|
);
|
||||||
|
}
|
||||||
if (span.kind === SpanKind.SERVER) {
|
if (span.kind === SpanKind.SERVER) {
|
||||||
|
if (validations.serverName) {
|
||||||
|
assert.strictEqual(
|
||||||
|
span.attributes[AttributeNames.HTTP_SERVER_NAME],
|
||||||
|
validations.serverName,
|
||||||
|
' must have serverName attribute'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert.ok(
|
||||||
|
span.attributes[AttributeNames.NET_HOST_PORT],
|
||||||
|
'must have HOST_PORT'
|
||||||
|
);
|
||||||
|
assert.ok(span.attributes[AttributeNames.NET_HOST_IP], 'must have HOST_IP');
|
||||||
assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY);
|
assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY);
|
||||||
} else if (validations.reqHeaders) {
|
} else if (validations.reqHeaders) {
|
||||||
assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]);
|
assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]);
|
||||||
|
|
Loading…
Reference in New Issue