diff --git a/packages/opentelemetry-plugin-http/README.md b/packages/opentelemetry-plugin-http/README.md index 69d196b5cdd..59c7fa31e52 100644 --- a/packages/opentelemetry-plugin-http/README.md +++ b/packages/opentelemetry-plugin-http/README.md @@ -55,6 +55,7 @@ Http plugin has few options available to choose from. You can set the following: | [`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 | | [`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 - For more information on OpenTelemetry, visit: - For more about OpenTelemetry JavaScript: diff --git a/packages/opentelemetry-plugin-http/src/types.ts b/packages/opentelemetry-plugin-http/src/types.ts index 432235a05a1..bfd75fefd85 100644 --- a/packages/opentelemetry-plugin-http/src/types.ts +++ b/packages/opentelemetry-plugin-http/src/types.ts @@ -78,3 +78,7 @@ export interface Err extends Error { syscall?: string; stack?: string; } + +export interface SpecialHttpStatusCodeMapping { + [custom: number]: number; +} diff --git a/packages/opentelemetry-plugin-http/src/utils.ts b/packages/opentelemetry-plugin-http/src/utils.ts index f49a9978d4b..2f8c02d8b8d 100644 --- a/packages/opentelemetry-plugin-http/src/utils.ts +++ b/packages/opentelemetry-plugin-http/src/utils.ts @@ -23,12 +23,30 @@ import { OutgoingHttpHeaders, ServerResponse, } from 'http'; -import { IgnoreMatcher, Err, ParsedRequestOptions } from './types'; +import { + IgnoreMatcher, + Err, + ParsedRequestOptions, + SpecialHttpStatusCodeMapping, +} from './types'; import { AttributeNames } from './enums/AttributeNames'; import * as url from 'url'; import { Socket } from 'net'; export const OT_REQUEST_HEADER = 'x-opentelemetry-outgoing-request'; + +export const HTTP_STATUS_SPECIAL_CASES: SpecialHttpStatusCodeMapping = { + 401: CanonicalCode.UNAUTHENTICATED, + 403: CanonicalCode.PERMISSION_DENIED, + 404: CanonicalCode.NOT_FOUND, + 429: CanonicalCode.RESOURCE_EXHAUSTED, + 501: CanonicalCode.UNIMPLEMENTED, + 503: CanonicalCode.UNAVAILABLE, + 504: CanonicalCode.DEADLINE_EXCEEDED, + 598: CanonicalCode.INTERNAL, + 599: CanonicalCode.INTERNAL, +}; + /** * Get an absolute url */ @@ -58,37 +76,40 @@ export const getAbsoluteUrl = ( return `${protocol}//${host}${path}`; }; /** - * Parse status code from HTTP response. + * Parse status code from HTTP response. [More details](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md#status) */ export const parseResponseStatus = ( statusCode: number ): Omit => { - if (statusCode < 200 || statusCode > 504) { + // search for special case + const code: number | undefined = HTTP_STATUS_SPECIAL_CASES[statusCode]; + + if (code !== undefined) { + return { code }; + } + + // 0xx are unknown + if (statusCode < 100) { return { code: CanonicalCode.UNKNOWN }; - } else if (statusCode >= 200 && statusCode < 400) { + } + + // 1xx, 2xx, 3xx are OK + if (statusCode < 400) { return { code: CanonicalCode.OK }; - } else { - switch (statusCode) { - case 400: - return { code: CanonicalCode.INVALID_ARGUMENT }; - case 504: - return { code: CanonicalCode.DEADLINE_EXCEEDED }; - case 404: - return { code: CanonicalCode.NOT_FOUND }; - case 403: - return { code: CanonicalCode.PERMISSION_DENIED }; - case 401: - return { code: CanonicalCode.UNAUTHENTICATED }; - case 429: - return { code: CanonicalCode.RESOURCE_EXHAUSTED }; - case 501: - return { code: CanonicalCode.UNIMPLEMENTED }; - case 503: - return { code: CanonicalCode.UNAVAILABLE }; - default: - return { code: CanonicalCode.UNKNOWN }; - } } + + // 4xx are client errors + if (statusCode < 500) { + return { code: CanonicalCode.INVALID_ARGUMENT }; + } + + // 5xx are internal errors + if (statusCode < 512) { + return { code: CanonicalCode.INTERNAL }; + } + + // All other codes are unknown + return { code: CanonicalCode.UNKNOWN }; }; /** diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts index 4efaa6920d1..75353e2c212 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts @@ -280,7 +280,19 @@ describe('HttpPlugin', () => { assert.strictEqual(spans.length, 0); }); - const httpErrorCodes = [400, 401, 403, 404, 429, 501, 503, 504, 500, 505]; + const httpErrorCodes = [ + 400, + 401, + 403, + 404, + 429, + 501, + 503, + 504, + 500, + 505, + 597, + ]; for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts index 32372c8cbed..c8bcddd957f 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts @@ -14,17 +14,17 @@ * limitations under the License. */ +import { NoopLogger } from '@opentelemetry/core'; +import { NoopScopeManager } from '@opentelemetry/scope-base'; +import { BasicTracerRegistry, Span } from '@opentelemetry/tracing'; +import { CanonicalCode, SpanKind } from '@opentelemetry/types'; import * as assert from 'assert'; +import * as http from 'http'; import * as sinon from 'sinon'; import * as url from 'url'; -import { CanonicalCode, SpanKind } from '@opentelemetry/types'; -import { NoopScopeManager } from '@opentelemetry/scope-base'; +import { AttributeNames } from '../../src'; import { IgnoreMatcher } from '../../src/types'; import * as utils from '../../src/utils'; -import * as http from 'http'; -import { Span, BasicTracerRegistry } from '@opentelemetry/tracing'; -import { AttributeNames } from '../../src'; -import { NoopLogger } from '@opentelemetry/core'; describe('Utility', () => { describe('parseResponseStatus()', () => { @@ -36,18 +36,25 @@ describe('Utility', () => { }); it('should return OK for Success HTTP status code', () => { - for (let index = 200; index < 400; index++) { + for (let index = 100; index < 400; index++) { const status = utils.parseResponseStatus(index); assert.deepStrictEqual(status, { code: CanonicalCode.OK }); } }); it('should not return OK for Bad HTTP status code', () => { - for (let index = 400; index <= 504; index++) { + for (let index = 400; index <= 600; index++) { const status = utils.parseResponseStatus(index); assert.notStrictEqual(status.code, CanonicalCode.OK); } }); + it('should handle special HTTP status codes', () => { + for (const key in utils.HTTP_STATUS_SPECIAL_CASES) { + const status = utils.parseResponseStatus(key as any); + const canonicalCode = utils.HTTP_STATUS_SPECIAL_CASES[key]; + assert.deepStrictEqual(status.code, canonicalCode); + } + }); }); describe('hasExpectHeader()', () => { it('should throw if no option', () => {