diff --git a/packages/opentelemetry-plugin-http/src/utils.ts b/packages/opentelemetry-plugin-http/src/utils.ts index 1fb1cbdc99..a523909604 100644 --- a/packages/opentelemetry-plugin-http/src/utils.ts +++ b/packages/opentelemetry-plugin-http/src/utils.ts @@ -218,33 +218,38 @@ export const setSpanWithError = ( }; /** - * - * @param headers - * @param Attributes + * Adds attributes for content-length and content-encoding HTTP headers + * @param { OutgoingHttpHeaders | IncomingHttpHeaders } headers http headers + * @param { Attributes } Attributes span attributes + * @param { boolean } isRequest set true for setting request content-header */ -export const setRequestContentLengthAttributes = ( +export const setContentLengthAttributes = ( headers: OutgoingHttpHeaders | IncomingHttpHeaders, - attributes: Attributes + attributes: Attributes, + isRequest: boolean ) => { - let isCompressed = false + let isCompressed = false; + + if (headers['content-length'] === undefined) + return; if (headers['content-encoding'] && headers['content-encoding'] !== 'identity') { isCompressed = true - } + }; if (isCompressed) { - if (headers as OutgoingHttpHeaders) { + if (isRequest) { attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH] = headers['content-length'] } else { attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH] = headers['content-length'] - } + }; } else { - if (headers as OutgoingHttpHeaders) { + if (isRequest) { attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED] = headers['content-length'] } else { attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED] = headers['content-length'] - } - } + }; + }; }; /** @@ -396,7 +401,7 @@ export const getOutgoingRequestAttributesOnResponse = ( [HttpAttribute.HTTP_HOST]: `${options.hostname}:${remotePort}`, }; - // Place content-length func here? + setContentLengthAttributes(headers, attributes, false) if (statusCode) { attributes[HttpAttribute.HTTP_STATUS_CODE] = statusCode; @@ -458,7 +463,7 @@ export const getIncomingRequestAttributes = ( attributes[HttpAttribute.HTTP_USER_AGENT] = userAgent; } - // Place content-length func here? + setContentLengthAttributes(headers, attributes, true) const httpKindAttributes = getAttributesFromHttpKind(httpVersion); return Object.assign(attributes, httpKindAttributes); diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts index 9a290e551a..01f8744e38 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts @@ -310,16 +310,114 @@ describe('Utility', () => { }); }); - describe('setRequestContentLengthAttributes', () => { - it('should set attributes', () => { - const attributes: Attributes = {} - let headers: http.OutgoingHttpHeaders = { - 'content-length': 1200 - } + describe('setContentLengthAttributes', () => { + it('should set response content-length uncompressed attribute with no content-encoding header', () => { + const attributes: Attributes = {}; + + let headers: http.IncomingHttpHeaders = {}; + + headers["content-length"] = '1200'; + + utils.setContentLengthAttributes(headers, attributes, false); + + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED], '1200'); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], undefined); + }); + + it('should set response content-length uncompressed attribute with "identity" content-encoding header', () => { + const attributes: Attributes = {}; + + let headers: http.IncomingHttpHeaders = {}; + + headers["content-length"] = '1200'; + headers["content-encoding"] = 'identity'; + + utils.setContentLengthAttributes(headers, attributes, false); + + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED], '1200'); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], undefined); + }); + + it('should set response content-length compressed attribute with "gzip" content-encoding header', () => { + const attributes: Attributes = {}; + + let headers: http.IncomingHttpHeaders = {}; + + headers["content-length"] = '1200'; + headers["content-encoding"] = 'gzip'; + + utils.setContentLengthAttributes(headers, attributes, false); + + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], '1200'); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], undefined); + }); + + it('should set request content-length uncompressed attribute with no content-encoding header', () => { + const attributes: Attributes = {}; + + let headers: http.OutgoingHttpHeaders = {}; + + headers["content-length"] = '1200'; - utils.setRequestContentLengthAttributes(headers, attributes) + utils.setContentLengthAttributes(headers, attributes, true); - assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED], 1200) - }) - }) + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED], '1200'); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], undefined); + }); + + it('should set request content-length uncompressed attribute with "identity" content-encoding header', () => { + const attributes: Attributes = {}; + + let headers: http.OutgoingHttpHeaders = {}; + + headers["content-length"] = '1200'; + headers["content-encoding"] = 'identity'; + + utils.setContentLengthAttributes(headers, attributes, true); + + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED], '1200'); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], undefined); + }); + + it('should set request content-length compressed attribute with "gzip" content-encoding header', () => { + const attributes: Attributes = {}; + + let headers: http.OutgoingHttpHeaders = {}; + + headers["content-length"] = '1200'; + headers["content-encoding"] = 'gzip'; + + utils.setContentLengthAttributes(headers, attributes, true); + + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], '1200'); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], undefined); + }); + + it('should set no attributes with no content-length header', () => { + const attributes: Attributes = {}; + + let headers: http.OutgoingHttpHeaders = {}; + + headers["content-encoding"] = 'gzip'; + + utils.setContentLengthAttributes(headers, attributes, true); + + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED], undefined); + assert.strictEqual(attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], undefined); + }); + }); }); diff --git a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts index f9bda54dd0..e92dcd7020 100644 --- a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts +++ b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts @@ -83,7 +83,24 @@ export const assertSpan = ( ); } } + if (span.kind === SpanKind.CLIENT) { + if (validations.resHeaders['content-length']) { + const contentLength = validations.resHeaders['content-length'] + + if (validations.resHeaders['content-encoding'] && validations.resHeaders['content-encoding'] != 'identity') { + assert.strictEqual( + span.attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], + contentLength + ); + } else { + assert.strictEqual( + span.attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED], + contentLength + ); + } + } + assert.strictEqual( span.attributes[GeneralAttribute.NET_PEER_NAME], validations.hostname, @@ -105,6 +122,22 @@ export const assertSpan = ( ); } if (span.kind === SpanKind.SERVER) { + if (validations.reqHeaders && validations.reqHeaders['content-length']) { + const contentLength = validations.reqHeaders['content-length'] + + if (validations.reqHeaders['content-encoding'] && validations.reqHeaders['content-encoding'] != 'identity') { + assert.strictEqual( + span.attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], + contentLength + ); + } else { + assert.strictEqual( + span.attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED], + contentLength + ); + } + } + if (validations.serverName) { assert.strictEqual( span.attributes[HttpAttribute.HTTP_SERVER_NAME], diff --git a/packages/opentelemetry-semantic-conventions/src/trace/http.ts b/packages/opentelemetry-semantic-conventions/src/trace/http.ts index ae8e7e657b..5193fd17ec 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/http.ts +++ b/packages/opentelemetry-semantic-conventions/src/trace/http.ts @@ -26,8 +26,8 @@ export const HttpAttribute = { HTTP_CLIENT_IP: 'http.client_ip', HTTP_SCHEME: 'http.scheme', HTTP_RESPONSE_CONTENT_LENGTH: 'http.response_content_length', - HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED: 'http.request_content_length_uncompressed', - HTTP_REQUEST_CONTENT_LENGTH: 'http.response_content_length', + HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED: 'http.response_content_length_uncompressed', + HTTP_REQUEST_CONTENT_LENGTH: 'http.request_content_length', HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED: 'http.request_content_length_uncompressed', // NOT ON OFFICIAL SPEC