From b7a15ca84ba608150a6cb3b88c74ccc7e21a6f99 Mon Sep 17 00:00:00 2001 From: Steve Faulkner Date: Mon, 23 Aug 2021 11:51:53 -0500 Subject: [PATCH 01/15] Fix retry handling for Session Not Found errors --- sdk/cosmosdb/cosmos/CHANGELOG.md | 11 +- sdk/cosmosdb/cosmos/src/retry/RetryContext.ts | 2 +- sdk/cosmosdb/cosmos/src/retry/retryUtility.ts | 3 +- .../cosmos/src/retry/sessionRetryPolicy.ts | 6 +- .../cosmos/test/internal/session.spec.ts | 404 +++--------------- 5 files changed, 65 insertions(+), 361 deletions(-) diff --git a/sdk/cosmosdb/cosmos/CHANGELOG.md b/sdk/cosmosdb/cosmos/CHANGELOG.md index 13dfc48d24e0..a958196088eb 100644 --- a/sdk/cosmosdb/cosmos/CHANGELOG.md +++ b/sdk/cosmosdb/cosmos/CHANGELOG.md @@ -8,6 +8,8 @@ ### Bugs Fixed +- Fixed bugs in session token clearing logic. Session Not found (404, substatus 1002) was not being handled correctly by the session retry policy and would mistakenly retry the request with the same session token. + ### Other Changes ## 3.13.0 (2021-08-10) @@ -41,8 +43,10 @@ ## 3.12.0 (2021-07-06) ### Features Added + - With the dropping of support for Node.js versions that are no longer in LTS, the dependency on `@types/node` has been updated to version 12. Read our [support policy](https://github.com/Azure/azure-sdk-for-js/blob/main/SUPPORT.md) for more details. - Added background refresher for endpoints, and new `ConnectionPolicy` options. Refreshing defaults to true, and the default refresh rate is every 5 minutes. + ```js const client = new CosmosClient({ endpoint, @@ -52,13 +56,14 @@ const client = new CosmosClient({ endpointRefreshRateInMs: 700, enableBackgroundEndpointRefreshing: true } -}) +}); ``` - Added `client.dispose()` for closing the endpoint refresher verbosely. Necessary when destroying the CosmosClient inside existing processes like an express web server, or when you want to destroy the client and create a new one in the same process. + ```js -const client = new CosmosClient() -client.dispose() // cancels background endpoint refreshing +const client = new CosmosClient(); +client.dispose(); // cancels background endpoint refreshing ``` ## 3.11.5 (2021-06-10) diff --git a/sdk/cosmosdb/cosmos/src/retry/RetryContext.ts b/sdk/cosmosdb/cosmos/src/retry/RetryContext.ts index 9b5a067cb3cc..f8a225afad3c 100644 --- a/sdk/cosmosdb/cosmos/src/retry/RetryContext.ts +++ b/sdk/cosmosdb/cosmos/src/retry/RetryContext.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. export interface RetryContext { - retryCount?: number; + retryCount: number; retryRequestOnPreferredLocations?: boolean; clearSessionTokenNotAvailable?: boolean; } diff --git a/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts b/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts index 883d7deaf3fc..87ee429c5de7 100644 --- a/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts +++ b/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts @@ -36,7 +36,7 @@ interface RetryPolicies { * @hidden */ export async function execute({ - retryContext = {}, + retryContext = { retryCount: 0 }, retryPolicies, requestContext, executeRequest @@ -64,6 +64,7 @@ export async function execute({ } if (retryContext && retryContext.clearSessionTokenNotAvailable) { requestContext.client.clearSessionToken(requestContext.path); + delete requestContext.headers["x-ms-session-token"]; } requestContext.endpoint = await requestContext.globalEndpointManager.resolveServiceEndpoint( requestContext.resourceType, diff --git a/sdk/cosmosdb/cosmos/src/retry/sessionRetryPolicy.ts b/sdk/cosmosdb/cosmos/src/retry/sessionRetryPolicy.ts index 3a0ad5a09bfe..6358b5af23cc 100644 --- a/sdk/cosmosdb/cosmos/src/retry/sessionRetryPolicy.ts +++ b/sdk/cosmosdb/cosmos/src/retry/sessionRetryPolicy.ts @@ -56,7 +56,8 @@ export class SessionRetryPolicy implements RetryPolicy { if (this.currentRetryAttemptCount > endpoints.length) { return false; } else { - retryContext.retryCount = ++this.currentRetryAttemptCount - 1; + this.currentRetryAttemptCount++; + retryContext.retryCount++; retryContext.retryRequestOnPreferredLocations = this.currentRetryAttemptCount > 1; retryContext.clearSessionTokenNotAvailable = this.currentRetryAttemptCount === endpoints.length; @@ -66,7 +67,8 @@ export class SessionRetryPolicy implements RetryPolicy { if (this.currentRetryAttemptCount > 1) { return false; } else { - retryContext.retryCount = ++this.currentRetryAttemptCount - 1; + this.currentRetryAttemptCount++; + retryContext.retryCount++; retryContext.retryRequestOnPreferredLocations = false; // Forces all operations to primary write endpoint retryContext.clearSessionTokenNotAvailable = true; return true; diff --git a/sdk/cosmosdb/cosmos/test/internal/session.spec.ts b/sdk/cosmosdb/cosmos/test/internal/session.spec.ts index ff6885fc4c2d..3f251dd68e8d 100644 --- a/sdk/cosmosdb/cosmos/test/internal/session.spec.ts +++ b/sdk/cosmosdb/cosmos/test/internal/session.spec.ts @@ -1,37 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. import assert from "assert"; -import { Context } from "mocha"; import { Suite } from "mocha"; -import * as sinon from "sinon"; -import { ClientContext, PluginConfig, PluginOn } from "../../src"; -import { OperationType, ResourceType, trimSlashes } from "../../src/common"; +import { ClientContext, Container, PluginConfig, PluginOn } from "../../src"; +import { OperationType, ResourceType } from "../../src/common"; import { ConsistencyLevel } from "../../src"; -import { Constants, CosmosClient } from "../../src"; +import { CosmosClient } from "../../src"; import { SessionContainer } from "../../src/session/sessionContainer"; -import { VectorSessionToken } from "../../src/session/VectorSessionToken"; import { endpoint, masterKey } from "../public/common/_testConfig"; import { getTestDatabase, removeAllDatabases } from "../public/common/TestHelpers"; -import * as RequestHandler from "../../src/request/RequestHandler"; import { RequestContext } from "../../src"; import { Response } from "../../src/request/Response"; -// TODO: there is alot of "any" types for tokens here -// TODO: there is alot of leaky document client stuff here that will make removing document client hard - -const client = new CosmosClient({ - endpoint, - key: masterKey, - consistencyLevel: ConsistencyLevel.Session, - connectionPolicy: { enableBackgroundEndpointRefreshing: false } -}); - -function getCollection2TokenMap( - sessionContainer: SessionContainer -): Map> { - return (sessionContainer as any).collectionResourceIdToSessionTokens; -} - describe("New session token", function() { it("preserves tokens", async function() { let response: Response; @@ -88,348 +68,64 @@ describe("New session token", function() { }); }); -describe.skip("Session Token", function(this: Suite) { - this.timeout(process.env.MOCHA_TIMEOUT || 20000); - - const containerId = "sessionTestColl"; - - const containerDefinition = { - id: containerId, - partitionKey: { paths: ["/id"] } - }; - const containerOptions = { offerThroughput: 25100 }; - - const clientContext: ClientContext = (client as any).clientContext; - const sessionContainer: SessionContainer = (clientContext as any).sessionContainer; - - const spy = sinon.spy(RequestHandler, "request"); - +describe("Session Token", function(this: Suite) { beforeEach(async function() { await removeAllDatabases(); }); - it("validate session tokens for sequence of operations", async function() { - const database = await getTestDatabase("session test", client); - - const { resource: createdContainerDef } = await database.containers.create( - containerDefinition, - containerOptions - ); - const container = database.container(createdContainerDef.id); - assert.equal(spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], undefined); - // TODO: testing implementation detail by looking at containerResourceIdToSesssionTokens - let collRid2SessionToken: Map< - string, - Map - > = (sessionContainer as any).collectionResourceIdToSessionTokens; - assert.equal(collRid2SessionToken.size, 0, "Should have no tokens in container"); - - const { resource: document1 } = await container.items.create({ id: "1" }); - assert.equal( - spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], - undefined, - "Initial create token should be qual" - ); - - collRid2SessionToken = getCollection2TokenMap(sessionContainer); - assert.equal( - collRid2SessionToken.size, - 1, - "Should only have one container in the sessioncontainer" - ); - const containerRid = collRid2SessionToken.keys().next().value; - let containerTokens = collRid2SessionToken.get(containerRid); - assert.equal(containerTokens.size, 1, "Should only have one partition in container"); - const firstPartition = containerTokens.keys().next().value; - let firstPartitionToken = containerTokens.get(firstPartition); - assert.notEqual(firstPartitionToken, "Should have a token for first partition"); - - const token = sessionContainer.get({ - isNameBased: true, - operationType: OperationType.Create, - resourceAddress: container.url, - resourceType: ResourceType.item, - resourceId: "2" - }); - const { resource: document2 } = await container.items.create({ id: "2" }); - assert.equal( - spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], - token, - "create token should be equal" - ); - - collRid2SessionToken = getCollection2TokenMap(sessionContainer); - assert.equal( - collRid2SessionToken.size, - 1, - "Should only have one container in the sessioncontainer" - ); - containerTokens = collRid2SessionToken.get(containerRid); - assert.equal(containerTokens.size, 2, "Should have two partitions in container"); - const keysIterator = containerTokens.keys(); - keysIterator.next(); // partition 1 - const secondPartition = keysIterator.next().value; - assert.equal( - containerTokens.get(firstPartition).toString(), - firstPartitionToken.toString(), - "First partition token should still match after create" - ); - let secondPartitionToken = containerTokens.get(secondPartition); - assert(secondPartitionToken, "Should have a LSN for second partition"); - - const readToken = sessionContainer.get({ - isNameBased: true, - operationType: OperationType.Read, - resourceAddress: container.url, - resourceType: ResourceType.item, - resourceId: "1" - }); - await container.item(document1.id, "1").read(); - assert.equal( - spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], - readToken, - "read token should be equal" - ); - - collRid2SessionToken = getCollection2TokenMap(sessionContainer); - assert.equal( - collRid2SessionToken.size, - 1, - "Should only have one container in the sessioncontainer" - ); - containerTokens = collRid2SessionToken.get(containerRid); - assert.equal(containerTokens.size, 2, "Should have two partitions in container"); - assert.equal( - containerTokens.get(firstPartition).toString(), - firstPartitionToken.toString(), - "First partition token should still match after read" - ); - assert.equal( - containerTokens.get(secondPartition).toString(), - secondPartitionToken.toString(), - "Second partition token should still match after read" - ); - - const upsertToken = sessionContainer.get({ - isNameBased: true, - operationType: OperationType.Upsert, - resourceAddress: container.url, - resourceType: ResourceType.item, - resourceId: "1" - }); - const { resource: document13 } = await container.items.upsert({ id: "1", operation: "upsert" }); - assert.equal( - spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], - upsertToken, - "upsert token should be equal" - ); - - collRid2SessionToken = getCollection2TokenMap(sessionContainer); - assert.equal( - collRid2SessionToken.size, - 1, - "Should only have one container in the sessioncontainer" - ); - containerTokens = collRid2SessionToken.get(containerRid); - assert.equal(containerTokens.size, 2, "Should have two partitions in container"); - // TODO: should validate the LSN only increased by 1... - assert.notEqual( - containerTokens.get(firstPartition).toString(), - firstPartitionToken.toString(), - "First partition token should no longer match after upsert" - ); - assert.equal( - containerTokens.get(secondPartition).toString(), - secondPartitionToken.toString(), - "Second partition token should still match after upsert" - ); - firstPartitionToken = containerTokens.get(firstPartition); - - const deleteToken = sessionContainer.get({ - isNameBased: true, - operationType: OperationType.Delete, - resourceAddress: container.url, - resourceType: ResourceType.item, - resourceId: "2" - }); - await container.item(document2.id, "2").delete(); - assert.equal( - spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], - deleteToken, - "delete token should be equal" - ); - - collRid2SessionToken = getCollection2TokenMap(sessionContainer); - assert.equal( - collRid2SessionToken.size, - 1, - "Should only have one container in the sessioncontainer" - ); - containerTokens = collRid2SessionToken.get(containerRid); - assert.equal(containerTokens.size, 2, "Should have two partitions in container"); - assert.equal( - containerTokens.get(firstPartition).toString(), - firstPartitionToken.toString(), - "First partition token should still match delete" - ); - // TODO: should validate the LSN only increased by 1... - assert.notEqual( - containerTokens.get(secondPartition).toString(), - secondPartitionToken.toString(), - "Second partition token should not match after delete" - ); - secondPartitionToken = containerTokens.get(secondPartition); - - const replaceToken = sessionContainer.get({ - isNameBased: true, - operationType: OperationType.Replace, - resourceAddress: container.url, - resourceType: ResourceType.item, - resourceId: "1" - }); - await container.item(document13.id, "1").replace({ id: "1", operation: "replace" }); - assert.equal( - spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], - replaceToken, - "replace token should be equal" - ); - collRid2SessionToken = getCollection2TokenMap(sessionContainer); - assert.equal( - collRid2SessionToken.size, - 1, - "Should only have one container in the sessioncontainer" - ); - containerTokens = collRid2SessionToken.get(containerRid); - assert.equal(containerTokens.size, 2, "Should have two partitions in container"); - // TODO: should validate the LSN only increased by 1... - assert.notEqual( - containerTokens.get(firstPartition).toString(), - firstPartitionToken.toString(), - "First partition token should no longer match after replace" - ); - assert.equal( - containerTokens.get(secondPartition).toString(), - secondPartitionToken.toString(), - "Second partition token should still match after replace" - ); - firstPartitionToken = containerTokens.get(firstPartition); - - const query = `SELECT * from c WHERE c.id = "1"`; - const queryIterator = container.items.query(query); - - const queryToken = sessionContainer.get({ - isNameBased: true, - operationType: OperationType.Query, - resourceAddress: container.url, - resourceType: ResourceType.item - }); - await queryIterator.fetchAll(); - assert.equal(spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], queryToken); - - collRid2SessionToken = getCollection2TokenMap(sessionContainer); - assert.equal( - collRid2SessionToken.size, - 1, - "Should only have one container in the sessioncontainer" - ); - containerTokens = collRid2SessionToken.get(containerRid); - assert.equal(containerTokens.size, 2, "Should have two partitions in container"); - assert.equal( - containerTokens.get(firstPartition).toString(), - firstPartitionToken.toString(), - "First partition token should still match after query" - ); - assert.equal( - containerTokens.get(secondPartition).toString(), - secondPartitionToken.toString(), - "Second partition token should still match after query" - ); - - const deleteContainerToken = sessionContainer.get({ - isNameBased: true, - operationType: OperationType.Delete, - resourceAddress: container.url, - resourceType: ResourceType.container, - resourceId: container.id - }); - await container.delete(); - assert.equal( - spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], - deleteContainerToken, - "delete container token should match" - ); - collRid2SessionToken = getCollection2TokenMap(sessionContainer); - assert.equal(collRid2SessionToken.size, 0, "collRid map should be empty on container delete"); - - spy.restore(); - }); - - it("validate 'lsn not caught up' error for higher lsn and clearing session token", async function(this: Context) { - this.retries(2); - const database = await getTestDatabase("session test", client); - - const containerLink = "dbs/" + database.id + "/colls/" + containerId; - const increaseLSN = function(oldTokens: Map>): string { - for (const [, tokens] of oldTokens.entries()) { - for (const [pk, token] of tokens.entries()) { - (token as any).globalLsn = (token as any).globalLsn + 200; - const newToken = token.merge(token); - return `${pk}:${newToken.toString()}`; - } - } - throw new Error("No valid token found to increase"); - }; - - await database.containers.create(containerDefinition, containerOptions); - const container = database.container(containerDefinition.id); - await container.items.create({ id: "1" }); - const callbackSpy = sinon.spy(function(requestContext: RequestContext) { - const oldTokens = getCollection2TokenMap(sessionContainer); - requestContext.headers[Constants.HttpHeaders.SessionToken] = increaseLSN(oldTokens); - }); - const applySessionTokenStub = sinon - .stub(clientContext as any, "applySessionToken") - .callsFake(callbackSpy as any); - const resp = await container.item("1", "1").read(); - assert.equal(resp.resource, undefined); - assert.equal(resp.substatus, 1002, "Substatus should indicate the LSN didn't catchup."); - assert.equal(callbackSpy.callCount, 1); - assert.equal(trimSlashes(callbackSpy.lastCall.args[0].path), containerLink + "/docs/1"); - applySessionTokenStub.restore(); - await container.item("1", "1").read(); - }); - - it("validate session container update on 'Not found' with 'undefined' status code for non master resource", async function() { - const client2 = new CosmosClient({ + it("retries session not found successfully", async function() { + const clientA = new CosmosClient({ endpoint, key: masterKey, consistencyLevel: ConsistencyLevel.Session, connectionPolicy: { enableBackgroundEndpointRefreshing: false } }); + // Create a second client with a plugin that simulates "Session Not Found" error + const clientB = new CosmosClient({ + endpoint, + key: masterKey, + consistencyLevel: ConsistencyLevel.Session, + connectionPolicy: { enableBackgroundEndpointRefreshing: false }, + plugins: [ + { + on: "request", + plugin: async (context, next) => { + // Simulate a "Session Not Found" error by manually making the client session token *way* ahead of any available session on the server + // This is just a way to simulate the error. Getting this to happen in practice is difficult and only usually occurs cross region where there is significant replication lag + if (context.headers["x-ms-session-token"]) { + context.headers["x-ms-session-token"] = "0:0#900000#3=8600000#10=-1"; + } + return next(context); + } + } + ] + }); - const db = await getTestDatabase("session test", client); - - const { resource: createdContainerDef } = await db.containers.create( - containerDefinition, - containerOptions - ); - const createdContainer = db.container(createdContainerDef.id); - - const { resource: createdDocument } = await createdContainer.items.create({ - id: "1" + // Create Database and Container + const { database } = await clientA.databases.createIfNotExists({ + id: "sessionTest" }); - await client2 - .database(db.id) - .container(createdContainerDef.id) - .item(createdDocument.id, "1") - .delete(); - const setSessionTokenSpy = sinon.spy(sessionContainer, "set"); + const { container } = await database.containers.createIfNotExists({ + id: "sessionTest" + }); + + // Create items using both clients so they each establishes a session with the backend + const container2 = await clientB.database("sessionTest").container("sessionTest"); + await Promise.all([createItem(container), createItem(container2)]); - const resp = await createdContainer.item(createdDocument.id, "1").read(); - assert.equal(resp.resource, undefined); - assert.equal(resp.statusCode, 404, "expecting 404 (Not found)"); - assert.equal(resp.substatus, undefined, "expecting substatus code to be undefined"); - assert.equal(setSessionTokenSpy.callCount, 1, "unexpected number of calls to sesSessionToken"); - setSessionTokenSpy.restore(); + // Create an item using client + const id = await createItem(container); + const { resource, statusCode } = await container2.item(id).read(); + assert(resource); + assert.strictEqual(statusCode, 200); }); }); + +async function createItem(container: Container) { + const { + resource: { id } + } = await container.items.create({ + id: (Math.random() + 1).toString(36).substring(7) + }); + return id; +} From ca193b813e861baf3f55f6ee60abf1b5ecf174ad Mon Sep 17 00:00:00 2001 From: Steve Faulkner Date: Mon, 23 Aug 2021 10:02:23 -0700 Subject: [PATCH 02/15] Update sdk/cosmosdb/cosmos/test/internal/session.spec.ts Co-authored-by: Zachary Foster --- sdk/cosmosdb/cosmos/test/internal/session.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/cosmosdb/cosmos/test/internal/session.spec.ts b/sdk/cosmosdb/cosmos/test/internal/session.spec.ts index 3f251dd68e8d..1d62d66d5a63 100644 --- a/sdk/cosmosdb/cosmos/test/internal/session.spec.ts +++ b/sdk/cosmosdb/cosmos/test/internal/session.spec.ts @@ -109,7 +109,7 @@ describe("Session Token", function(this: Suite) { id: "sessionTest" }); - // Create items using both clients so they each establishes a session with the backend + // Create items using both clients so they each establish a session with the backend const container2 = await clientB.database("sessionTest").container("sessionTest"); await Promise.all([createItem(container), createItem(container2)]); From 4b1a28d5212fb21504c693d3612296b9f5396488 Mon Sep 17 00:00:00 2001 From: omziv <80668253+omziv@users.noreply.github.com> Date: Wed, 25 Aug 2021 19:35:55 +0300 Subject: [PATCH 03/15] Update CODEOWNERS for Application Insights (#17055) --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0a5014d0cb83..7e45e0c47d67 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -108,8 +108,8 @@ # PRLabel: %Search /sdk/search/ @xirzec @sarangan12 -/sdk/applicationinsights/applicationinsights-query/ @divyajay @alongafni -/sdk/operationalinsights/loganalytics/ @divyajay @alongafni +/sdk/applicationinsights/applicationinsights-query/ @omziv @anatse @raronen @ischrei @danhadari @azmonapplicationinsights +/sdk/operationalinsights/loganalytics/ @omziv @anatse @raronen @ischrei @danhadari @azmonapplicationinsights /sdk/cognitiveservices/cognitiveservices-qnamaker-runtime/ @bingisbestest @nerajput1607 /sdk/cognitiveservices/cognitiveservices-qnamaker/ @bingisbestest @nerajput1607 From 3eca3884b8accf3d0d0fd031ea078a5a0c803620 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Tue, 31 Aug 2021 16:41:38 -0700 Subject: [PATCH 04/15] Updating Azure Monitor Exporter to align with latest schema spec --- common/config/rush/pnpm-lock.yaml | 10 +- .../package.json | 2 +- .../samples-dev/basicTracerNode.ts | 4 +- .../src/platform/nodejs/context/context.ts | 14 +- .../utils/constants/applicationinsights.ts | 38 +- .../src/utils/constants/span/azAttributes.ts | 18 - .../src/utils/constants/span/dbAttributes.ts | 15 - .../utils/constants/span/grpcAttributes.ts | 15 - .../utils/constants/span/httpAttributes.ts | 25 -- .../src/utils/eventhub.ts | 70 ---- .../src/utils/spanUtils.ts | 325 ++++++++++-------- .../test/internal/eventhub.test.ts | 110 ------ .../test/internal/spanUtils.test.ts | 116 +++---- .../test/utils/assert.ts | 15 +- .../test/utils/basic.ts | 12 +- 15 files changed, 277 insertions(+), 512 deletions(-) delete mode 100644 sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/azAttributes.ts delete mode 100644 sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/dbAttributes.ts delete mode 100644 sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/grpcAttributes.ts delete mode 100644 sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/httpAttributes.ts delete mode 100644 sdk/monitor/monitor-opentelemetry-exporter/src/utils/eventhub.ts delete mode 100644 sdk/monitor/monitor-opentelemetry-exporter/test/internal/eventhub.test.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index f61e1bf62beb..fb5da1576a96 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -1258,6 +1258,12 @@ packages: node: '>=8.0.0' resolution: integrity: sha512-t4fKikazahwNKmwD+CE/icHyuZldWvNMupJhjxdk9T/KxHFx3zCGjHT3MKavwYP6abzgAAm5WwzD1oHlmj7dyg== + /@opentelemetry/semantic-conventions/0.24.0: + dev: false + engines: + node: '>=8.0.0' + resolution: + integrity: sha512-a/szuMQV0Quy0/M7kKdglcbRSoorleyyOwbTNNJ32O+RBN766wbQlMTvdimImTmwYWGr+NJOni1EcC242WlRcA== /@opentelemetry/tracing/0.22.0_@opentelemetry+api@1.0.2: dependencies: '@opentelemetry/api': 1.0.2 @@ -11009,7 +11015,7 @@ packages: '@opentelemetry/instrumentation-http': 0.22.0_@opentelemetry+api@1.0.2 '@opentelemetry/node': 0.22.0_@opentelemetry+api@1.0.2 '@opentelemetry/resources': 0.22.0_@opentelemetry+api@1.0.2 - '@opentelemetry/semantic-conventions': 0.22.0 + '@opentelemetry/semantic-conventions': 0.24.0 '@opentelemetry/tracing': 0.22.0_@opentelemetry+api@1.0.2 '@types/mocha': 7.0.2 '@types/node': 12.20.19 @@ -11031,7 +11037,7 @@ packages: dev: false name: '@rush-temp/monitor-opentelemetry-exporter' resolution: - integrity: sha512-WYH8CaZOliclSLRlTl5xrJfy5dZtOvEc1ypw2W6qwQABZOeeF8geFXO6glkcxMCrEKIIeAjN14yyDXDQMsF3fg== + integrity: sha512-6x73k57tzSG7uXsMYo/N19DtEBt+PgxbV54C8le9hNOFRxpi6okX3gvZS+9QF7JmxKrXa7ohxH9331gOVmcnEQ== tarball: file:projects/monitor-opentelemetry-exporter.tgz version: 0.0.0 file:projects/monitor-query.tgz: diff --git a/sdk/monitor/monitor-opentelemetry-exporter/package.json b/sdk/monitor/monitor-opentelemetry-exporter/package.json index f48383888f48..bfd57b7141c8 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/package.json +++ b/sdk/monitor/monitor-opentelemetry-exporter/package.json @@ -113,7 +113,7 @@ "@opentelemetry/api": "^1.0.1", "@opentelemetry/core": "^0.22.0", "@opentelemetry/resources": "^0.22.0", - "@opentelemetry/semantic-conventions": "^0.22.0", + "@opentelemetry/semantic-conventions": "^0.24.0", "@opentelemetry/tracing": "^0.22.0", "tslib": "^2.2.0" }, diff --git a/sdk/monitor/monitor-opentelemetry-exporter/samples-dev/basicTracerNode.ts b/sdk/monitor/monitor-opentelemetry-exporter/samples-dev/basicTracerNode.ts index 14758d13b5ca..8f8708548cb3 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/samples-dev/basicTracerNode.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/samples-dev/basicTracerNode.ts @@ -11,7 +11,7 @@ import * as opentelemetry from "@opentelemetry/api"; import { Resource } from "@opentelemetry/resources"; -import { ResourceAttributes } from "@opentelemetry/semantic-conventions"; +import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; import { BasicTracerProvider, SimpleSpanProcessor } from "@opentelemetry/tracing"; import { AzureMonitorTraceExporter } from "@azure/monitor-opentelemetry-exporter"; @@ -21,7 +21,7 @@ dotenv.config(); const provider = new BasicTracerProvider({ resource: new Resource({ - [ResourceAttributes.SERVICE_NAME]: "basic-service" + [SemanticResourceAttributes.SERVICE_NAME]: "basic-service" }) }); diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts index ac21d2499193..f634e9f90c6a 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts @@ -7,7 +7,7 @@ import * as path from "path"; import { diag } from "@opentelemetry/api"; import { SDK_INFO } from "@opentelemetry/core"; -import { ContextTagKeys } from "../../../generated"; +import { KnownContextTagKeys } from "../../../generated"; import { Tags } from "../../../types"; type PackageJson = { version: string }; @@ -92,17 +92,13 @@ export class Context { Context.appVersion[packageJsonPath] = packageJson.version; } - this.tags["ai.application.ver"] = Context.appVersion[packageJsonPath]; + this.tags[KnownContextTagKeys.AiApplicationVer] = Context.appVersion[packageJsonPath]; } } private _loadDeviceContext(): void { - this.tags["ai.device.id"] = ""; - this.tags["ai.device.osVersion"] = os && `${os.type()} ${os.release()}`; - - // not yet supported tags - this.tags["ai.device.osArchitecture" as ContextTagKeys] = os && os.arch(); - this.tags["ai.device.osPlatform" as ContextTagKeys] = os && os.platform(); + this.tags[KnownContextTagKeys.AiDeviceId] = ""; + this.tags[KnownContextTagKeys.AiDeviceOsVersion] = os && `${os.type()} ${os.release()}`; } private _loadInternalContext(): void { @@ -144,7 +140,7 @@ export class Context { } this.tags[ - "ai.internal.sdkVersion" + KnownContextTagKeys.AiInternalSdkVersion ] = `node${Context.nodeVersion}:ot${SDK_INFO.VERSION}:ext${Context.sdkVersion}`; } } diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts index c999af8825e9..ca34b4476dfa 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts @@ -1,41 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -/** - * AI cloud role tag name. - * @internal - */ -export const AI_CLOUD_ROLE = "ai.cloud.role"; -/** - * AI cloud role isntance tag name. - * @internal - */ -export const AI_CLOUD_ROLE_INSTACE = "ai.cloud.roleInstance"; -/** - * AI operation Id tag name. - * @internal - */ -export const AI_OPERATION_ID = "ai.operation.id"; -/** - * AI operation parent Id tag name. - * @internal - */ -export const AI_OPERATION_PARENT_ID = "ai.operation.parentId"; -/** - * AI operation tag name. - * @internal - */ -export const AI_OPERATION_NAME = "ai.operation.name"; /** * AI MS Links. * @internal */ export const MS_LINKS = "_MS.links"; -/** - * AI InProc attribute. - * @internal - */ -export const INPROC = "InProc"; /** * AI enqueued time attribute. * @internal @@ -51,3 +21,11 @@ export const TIME_SINCE_ENQUEUED = "timeSinceEnqueued"; * @internal */ export const packageVersion = "1.0.0-beta.4"; + + +export enum DependencyTypes { + InProc = "InProc", + QueueMessage = "Queue Message", + Sql = "SQL", + Http = "Http", +} diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/azAttributes.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/azAttributes.ts deleted file mode 100644 index 439894fee189..000000000000 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/azAttributes.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -/** - * Azure SDK namespace. - * @internal - */ -export const AzNamespace = "az.namespace"; -/** - * Azure SDK Eventhub. - * @internal - */ -export const MicrosoftEventHub = "Microsoft.EventHub"; -/** - * Azure SDK message bus destination. - * @internal - */ -export const MessageBusDestination = "message_bus.destination"; diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/dbAttributes.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/dbAttributes.ts deleted file mode 100644 index 88bf583a5345..000000000000 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/dbAttributes.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import * as conventions from "@opentelemetry/semantic-conventions"; - -/** - * OpenTelemetry DB name attribute. - * @internal - */ -export const DB_NAME = conventions.SemanticAttributes.DB_NAME; -/** - * OpenTelemetry DB statement attribute. - * @internal - */ -export const DB_STATEMENT = conventions.SemanticAttributes.DB_STATEMENT; diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/grpcAttributes.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/grpcAttributes.ts deleted file mode 100644 index 900fe04fdfb0..000000000000 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/grpcAttributes.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import * as conventions from "@opentelemetry/semantic-conventions"; - -/** - * OpenTelemetry GRPC method attribute. - * @internal - */ -export const GRPC_METHOD = conventions.SemanticAttributes.RPC_METHOD; -/** - * OpenTelemetry GRPC status code attribute. - * @internal - */ -export const GRPC_STATUS_CODE = conventions.SemanticAttributes.RPC_GRPC_STATUS_CODE; diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/httpAttributes.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/httpAttributes.ts deleted file mode 100644 index d742d21ce6ab..000000000000 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/httpAttributes.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import * as conventions from "@opentelemetry/semantic-conventions"; - -/** - * OpenTelemetry HTTP method attribute. - * @internal - */ -export const HTTP_METHOD = conventions.SemanticAttributes.HTTP_METHOD; -/** - * OpenTelemetry HTTP URL attribute. - * @internal - */ -export const HTTP_URL = conventions.SemanticAttributes.HTTP_URL; -/** - * OpenTelemetry HTTP route attribute. - * @internal - */ -export const HTTP_ROUTE = conventions.SemanticAttributes.HTTP_ROUTE; -/** - * OpenTelemetry HTTP status code attribute. - * @internal - */ -export const HTTP_STATUS_CODE = conventions.SemanticAttributes.HTTP_STATUS_CODE; diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/eventhub.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/eventhub.ts deleted file mode 100644 index c6750362efb6..000000000000 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/eventhub.ts +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { SpanKind } from "@opentelemetry/api"; -import { hrTimeToMilliseconds } from "@opentelemetry/core"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import { ReadableSpan } from "@opentelemetry/tracing"; -import { RemoteDependencyData, RequestData } from "../generated"; -import { TIME_SINCE_ENQUEUED, ENQUEUED_TIME } from "./constants/applicationinsights"; -import { - AzNamespace, - MessageBusDestination, - MicrosoftEventHub -} from "./constants/span/azAttributes"; - -/** - * Average span.links[].attributes.enqueuedTime - */ -const getTimeSinceEnqueued = (span: ReadableSpan) => { - let countEnqueueDiffs = 0; - let sumEnqueueDiffs = 0; - const startTimeMs = hrTimeToMilliseconds(span.startTime); - - span.links.forEach(({ attributes }) => { - const enqueuedTime = attributes?.[ENQUEUED_TIME] as string | number; - if (enqueuedTime) { - countEnqueueDiffs += 1; - sumEnqueueDiffs += startTimeMs - (parseFloat(enqueuedTime.toString()) || 0); - } - }); - - return Math.max(sumEnqueueDiffs / (countEnqueueDiffs || 1), 0); -}; - -/** - * Implementation of Mapping to Azure Monitor - * - * https://gist.github.com/lmolkova/e4215c0f44a49ef824983382762e6b92#mapping-to-azure-monitor-application-insights-telemetry - * @internal - */ -export const parseEventHubSpan = ( - span: ReadableSpan, - baseData: RequestData | RemoteDependencyData -): void => { - const namespace = span.attributes[AzNamespace] as typeof MicrosoftEventHub; - const peerAddress = ((span.attributes[SemanticAttributes.NET_PEER_NAME] || - span.attributes["peer.address"] || - "unknown") as string).replace(/\/$/g, ""); // remove trailing "/" - const messageBusDestination = (span.attributes[MessageBusDestination] || "unknown") as string; - - switch (span.kind) { - case SpanKind.CLIENT: - baseData.type = namespace; - baseData.target = `${peerAddress}/${messageBusDestination}`; - break; - case SpanKind.PRODUCER: - baseData.type = `Queue Message | ${namespace}`; - baseData.target = `${peerAddress}/${messageBusDestination}`; - break; - case SpanKind.CONSUMER: - baseData.type = `Queue Message | ${namespace}`; - (baseData as any).source = `${peerAddress}/${messageBusDestination}`; - baseData.measurements = { - ...baseData.measurements, - [TIME_SINCE_ENQUEUED]: getTimeSinceEnqueued(span) - }; - break; - default: // no op - } -}; diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts index b99b0a0a37cf..5ae894d87c61 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts @@ -1,75 +1,81 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import os from "os"; import { URL } from "url"; import { ReadableSpan } from "@opentelemetry/tracing"; import { hrTimeToMilliseconds } from "@opentelemetry/core"; import { diag, SpanKind, SpanStatusCode, Link } from "@opentelemetry/api"; -import { ResourceAttributes } from "@opentelemetry/semantic-conventions"; +import { SemanticResourceAttributes, SemanticAttributes } from "@opentelemetry/semantic-conventions"; + import { Tags, Properties, MSLink, Measurements } from "../types"; -import { - HTTP_METHOD, - HTTP_ROUTE, - HTTP_URL, - HTTP_STATUS_CODE -} from "./constants/span/httpAttributes"; -import { - AI_CLOUD_ROLE, - AI_CLOUD_ROLE_INSTACE, - AI_OPERATION_ID, - AI_OPERATION_PARENT_ID, - AI_OPERATION_NAME, - MS_LINKS, - INPROC -} from "./constants/applicationinsights"; -import { GRPC_METHOD, GRPC_STATUS_CODE } from "./constants/span/grpcAttributes"; import { msToTimeSpan } from "./breezeUtils"; import { getInstance } from "../platform"; -import { DB_NAME, DB_STATEMENT } from "./constants/span/dbAttributes"; -import { parseEventHubSpan } from "./eventhub"; -import { AzNamespace, MicrosoftEventHub } from "./constants/span/azAttributes"; -import { RemoteDependencyData, RequestData, TelemetryItem as Envelope } from "../generated"; +import { DependencyTypes, MS_LINKS } from "./constants/applicationinsights"; +import { RemoteDependencyData, RequestData, TelemetryItem as Envelope, KnownContextTagKeys } from "../generated"; + function createTagsFromSpan(span: ReadableSpan): Tags { const context = getInstance(); const tags: Tags = { ...context.tags }; - tags[AI_OPERATION_ID] = span.spanContext().traceId; + tags[KnownContextTagKeys.AiOperationId] = span.spanContext().traceId; if (span.parentSpanId) { - tags[AI_OPERATION_PARENT_ID] = span.parentSpanId; + tags[KnownContextTagKeys.AiOperationParentId] = span.parentSpanId; } if (span.resource && span.resource.attributes) { - const serviceName = span.resource.attributes[ResourceAttributes.SERVICE_NAME]; - const serviceNamespace = span.resource.attributes[ResourceAttributes.SERVICE_NAMESPACE]; - const serviceInstanceId = span.resource.attributes[ResourceAttributes.SERVICE_INSTANCE_ID]; + const serviceName = span.resource.attributes[SemanticResourceAttributes.SERVICE_NAME]; + const serviceNamespace = span.resource.attributes[SemanticResourceAttributes.SERVICE_NAMESPACE]; if (serviceName) { if (serviceNamespace) { - tags[AI_CLOUD_ROLE] = `${serviceNamespace}.${serviceName}`; + tags[KnownContextTagKeys.AiCloudRole] = `${serviceNamespace}.${serviceName}`; } else { - tags[AI_CLOUD_ROLE] = String(serviceName); + tags[KnownContextTagKeys.AiCloudRole] = String(serviceName); } } + const serviceInstanceId = span.resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID]; if (serviceInstanceId) { - tags[AI_CLOUD_ROLE_INSTACE] = String(serviceInstanceId); + tags[KnownContextTagKeys.AiCloudRoleInstance] = String(serviceInstanceId); + } else { + tags[KnownContextTagKeys.AiCloudRoleInstance] = os && os.hostname(); + } + const endUserId = span.resource.attributes[SemanticAttributes.ENDUSER_ID]; + if (endUserId) { + tags[KnownContextTagKeys.AiUserId] = String(endUserId); } } - - // @todo: is this for RequestData only? - if ( - (span.kind === SpanKind.SERVER || span.kind === SpanKind.CONSUMER) && - span.attributes[GRPC_METHOD] - ) { - tags[AI_OPERATION_NAME] = String(span.attributes[GRPC_METHOD]); - } - if ( - (span.kind === SpanKind.SERVER || span.kind === SpanKind.CONSUMER) && - span.attributes[HTTP_METHOD] && - span.attributes[HTTP_ROUTE] - ) { - tags[AI_OPERATION_NAME] = `${span.attributes[HTTP_METHOD] as string} ${span.attributes[ - HTTP_ROUTE - ] as string}`; + // HTTP Spans + const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; + if (httpMethod) { + let httpClientIp = null; + let netPeerIp = null; + if (span.resource && span.resource.attributes) { + httpClientIp = span.resource.attributes[SemanticAttributes.HTTP_CLIENT_IP]; + netPeerIp = span.resource.attributes[SemanticAttributes.NET_PEER_IP]; + } + if (span.kind === SpanKind.SERVER) { + tags[KnownContextTagKeys.AiOperationName] = `${httpMethod as string} ${span.name as string}`; + if (httpClientIp) { + tags[KnownContextTagKeys.AiLocationIp] = String(httpClientIp); + } + else if (netPeerIp) { + tags[KnownContextTagKeys.AiLocationIp] = String(netPeerIp); + } + } + else { + tags[KnownContextTagKeys.AiOperationName] = span.name; + if (netPeerIp) { + tags[KnownContextTagKeys.AiLocationIp] = String(netPeerIp); + } + } + const httpUserAgent = span.resource.attributes[SemanticAttributes.HTTP_USER_AGENT]; + if (httpUserAgent) { + // TODO: Not exposed in Swagger, need to update def + tags["ai.user.userAgent"] = String(httpUserAgent); + } } + // TODO: Operation Name TBD for non HTTP + return tags; } @@ -78,7 +84,8 @@ function createPropertiesFromSpan(span: ReadableSpan): [Properties, Measurements const measurements: Measurements = {}; for (const key of Object.keys(span.attributes)) { - if (!(key.startsWith("http.") || key.startsWith("rpc.") || key.startsWith("db."))) { + if (!(key.startsWith("http.") || key.startsWith("rpc.") || key.startsWith("db.") + || key.startsWith("peer.") || key.startsWith("net."))) { properties[key] = span.attributes[key] as string; } } @@ -95,102 +102,157 @@ function createPropertiesFromSpan(span: ReadableSpan): [Properties, Measurements return [properties, measurements]; } +function getUrl(span: ReadableSpan): string { + const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; + if (httpMethod) { + const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; + if (httpUrl) { + return String(httpUrl); + } + else { + const httpScheme = span.attributes[SemanticAttributes.HTTP_SCHEME]; + if (httpScheme) { + const httpTarget = span.attributes[SemanticAttributes.HTTP_TARGET]; + if (httpTarget) { + const httpHost = span.attributes[SemanticAttributes.HTTP_HOST]; + if (httpHost) { + return `${httpScheme}://${httpHost}${httpTarget}`; + } + else { + const netPeerPort = span.attributes[SemanticAttributes.NET_PEER_PORT]; + if (netPeerPort) { + const netPeerName = span.attributes[SemanticAttributes.NET_PEER_NAME]; + if (netPeerName) { + return `${httpScheme}://${netPeerName}:${netPeerPort}`; + } + else { + const netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; + if (netPeerIp) { + return `${httpScheme}://${netPeerIp}:${netPeerPort}`; + } + } + } + } + } + } + } + } + return ""; +} + +function getTarget(span: ReadableSpan): string { + const peerService = span.attributes[SemanticAttributes.PEER_SERVICE]; + const httpHost = span.attributes[SemanticAttributes.HTTP_HOST]; + const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; + const netPeerName = span.attributes[SemanticAttributes.NET_PEER_NAME]; + const netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; + if (peerService) { + return String(peerService); + } + else if (httpHost) { + return String(httpHost); + } + else if (httpUrl) { + return String(httpUrl); + } + else if (netPeerName) { + return String(netPeerName); + } + else if (netPeerIp) { + return String(netPeerIp); + } + return ""; +} + function createDependencyData(span: ReadableSpan): RemoteDependencyData { - const data: RemoteDependencyData = { + const remoteDependencyData: RemoteDependencyData = { name: span.name, id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, - success: span.status.code === SpanStatusCode.OK, - resultCode: String(span.status.code), - target: span.attributes[HTTP_URL] as string | undefined, + success: span.status.code != SpanStatusCode.ERROR, + resultCode: "0", type: "Dependency", duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), - version: 1 + version: 2 }; - - if (span.attributes[HTTP_STATUS_CODE]) { - data.type = "HTTP"; - data.resultCode = String(span.attributes[HTTP_STATUS_CODE]); - } - - if (span.attributes[GRPC_STATUS_CODE] !== undefined) { - data.type = "GRPC"; - data.resultCode = String(span.attributes[GRPC_STATUS_CODE]); - } - - if (span.attributes[GRPC_METHOD]) { - data.target = String(span.attributes[GRPC_METHOD]); - data.data = String(span.attributes[GRPC_METHOD]); + const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; + const dbSystem = span.attributes[SemanticAttributes.DB_SYSTEM]; + const rpcMethod = span.attributes[SemanticAttributes.RPC_METHOD]; + // HTTP Dependency + if (httpMethod) { + remoteDependencyData.type = DependencyTypes.Http; + remoteDependencyData.data = getUrl(span); + const httpStatusCode = span.attributes[SemanticAttributes.HTTP_STATUS_CODE]; + if (httpStatusCode) { + remoteDependencyData.resultCode = String(httpStatusCode); + } + let target = getTarget(span); + if (target) { + let url = new URL(target); + // Ignore port if it is a default port + if (url.port === "80" || url.port === "443") { + url.port = ""; + } + remoteDependencyData.target = `${url}`; + } } - - if (span.attributes[HTTP_URL]) { - const url = new URL(span.attributes[HTTP_URL] as string); - data.target = url.hostname; - data.data = url.href; - - if (span.attributes[HTTP_METHOD]) { - data.name = `${span.attributes[HTTP_METHOD] as string} ${url.pathname}`; + // DB Dependency + else if (dbSystem) { + remoteDependencyData.type = String(dbSystem); + const dbStatement = span.attributes[SemanticAttributes.DB_STATEMENT]; + if (dbStatement) { + remoteDependencyData.data = String(dbStatement); + } + let target = getTarget(span); + const dbName = span.attributes[SemanticAttributes.DB_NAME]; + if (target) { + remoteDependencyData.target = dbName ? `${target}/${dbName}` : `${target}`; + } + else { + remoteDependencyData.target = dbName ? `${dbName}` : `${dbSystem}`; } } - - if (span.attributes[DB_STATEMENT]) { - data.name = String(span.attributes[DB_STATEMENT]); - data.data = String(span.attributes[DB_STATEMENT]); - data.type = "DB"; - if (span.attributes[DB_NAME]) { - data.target = String(span.attributes[DB_NAME]); + // grpc Dependency + else if (rpcMethod) { + const grpcStatusCode = span.attributes[SemanticAttributes.RPC_GRPC_STATUS_CODE]; + if (grpcStatusCode) { + remoteDependencyData.resultCode = String(grpcStatusCode); + } + let target = getTarget(span); + if (target) { + remoteDependencyData.target = `${target}`; + } + else { + const rpcSystem = span.attributes[SemanticAttributes.RPC_SYSTEM]; + if (rpcSystem) { + remoteDependencyData.target = String(rpcSystem); + } } } - - return data; + return remoteDependencyData; } function createRequestData(span: ReadableSpan): RequestData { - const data: RequestData = { + const requestData: RequestData = { name: span.name, id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, - success: span.status.code === SpanStatusCode.OK, - responseCode: String(span.status.code), + success: span.status.code != SpanStatusCode.ERROR, + responseCode: "0", duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), - version: 1, + version: 2, source: undefined }; - - if (span.attributes[HTTP_METHOD]) { - data.name = span.attributes[HTTP_METHOD] as string; - - if (span.attributes[HTTP_STATUS_CODE]) { - data.responseCode = String(span.attributes[HTTP_STATUS_CODE]); - } - - if (span.attributes[HTTP_URL]) { - data.url = span.attributes[HTTP_URL] as string; - } - - if (span.attributes[HTTP_ROUTE]) { - data.name = `${span.attributes[HTTP_METHOD] as string} ${span.attributes[ - HTTP_ROUTE - ] as string}`; - } else if (span.attributes[HTTP_URL]) { - const url = new URL(span.attributes[HTTP_URL] as string); - data.name = `${span.attributes[HTTP_METHOD] as string} ${url.pathname}`; - } - } - - if (span.attributes[GRPC_STATUS_CODE]) { - data.responseCode = String(span.attributes[GRPC_STATUS_CODE]); + const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; + if (httpUrl) { + requestData.url = String(httpUrl); } - if (span.attributes[GRPC_METHOD]) { - data.url = String(span.attributes[GRPC_METHOD]); + const httpStatusCode = span.attributes[SemanticAttributes.HTTP_STATUS_CODE]; + const grpcStatusCode = span.attributes[SemanticAttributes.RPC_GRPC_STATUS_CODE]; + if (httpStatusCode) { + requestData.responseCode = String(httpStatusCode); + } else if (grpcStatusCode) { + requestData.responseCode = String(grpcStatusCode); } - - return data; -} - -function createInProcData(span: ReadableSpan): RemoteDependencyData { - const data = createDependencyData(span); - data.type = INPROC; - data.success = true; - return data; + return requestData; } /** @@ -210,6 +272,7 @@ export function readableSpanToEnvelope(span: ReadableSpan, ikey: string): Envelo switch (span.kind) { case SpanKind.CLIENT: case SpanKind.PRODUCER: + case SpanKind.INTERNAL: name = "Microsoft.ApplicationInsights.RemoteDependency"; baseType = "RemoteDependencyData"; baseData = createDependencyData(span); @@ -220,28 +283,18 @@ export function readableSpanToEnvelope(span: ReadableSpan, ikey: string): Envelo baseType = "RequestData"; baseData = createRequestData(span); break; - case SpanKind.INTERNAL: - baseType = "RemoteDependencyData"; - name = "Microsoft.ApplicationInsights.RemoteDependency"; - baseData = createInProcData(span); - break; default: // never diag.error(`Unsupported span kind ${span.kind}`); throw new Error(`Unsupported span kind ${span.kind}`); } - if (span.attributes[AzNamespace] === MicrosoftEventHub) { - parseEventHubSpan(span, baseData); - } else if (span.attributes[AzNamespace]) { - switch (span.kind) { - case SpanKind.INTERNAL: - (baseData as RemoteDependencyData).type = `${INPROC} | ${span.attributes[AzNamespace]}`; - break; - default: // no op - } + if (span.kind === SpanKind.PRODUCER) { + baseData.type = DependencyTypes.QueueMessage; + } + if (span.kind === SpanKind.INTERNAL && span.parentSpanId) { + baseData.type = DependencyTypes.InProc; } - return { name, sampleRate, diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/eventhub.test.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/eventhub.test.ts deleted file mode 100644 index 7303ed4a393a..000000000000 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/eventhub.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { SpanAttributes, HrTime, SpanContext, SpanKind, ROOT_CONTEXT } from "@opentelemetry/api"; -import { timeInputToHrTime } from "@opentelemetry/core"; -import { BasicTracerProvider, Span } from "@opentelemetry/tracing"; -import * as assert from "assert"; -import { ENQUEUED_TIME, TIME_SINCE_ENQUEUED } from "../../src/utils/constants/applicationinsights"; -import { - AzNamespace, - MessageBusDestination, - MicrosoftEventHub -} from "../../src/utils/constants/span/azAttributes"; -import { parseEventHubSpan } from "../../src/utils/eventhub"; -import { RemoteDependencyData, TelemetryItem as Envelope } from "../../src/generated"; - -const tracer = new BasicTracerProvider().getTracer("default"); - -describe("#parseEventHubSpan(...)", () => { - const peerAddress = "example.servicebus.windows.net"; - const destination = "test123"; - const attributes: SpanAttributes = { - [AzNamespace]: MicrosoftEventHub, - ["peer.address"]: peerAddress, - [MessageBusDestination]: destination - }; - - it("should correctly parse SpanKind.CLIENT", () => { - const envelope = { data: { baseData: {} } } as Envelope; - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CLIENT - ); - span.setAttributes(attributes); - - const baseData = envelope.data?.baseData as RemoteDependencyData; - parseEventHubSpan(span, baseData); - - assert.strictEqual(baseData.type, attributes[AzNamespace]); - assert.strictEqual(baseData.target, `${peerAddress}/${destination}`); - - assert.strictEqual((baseData as any).source, undefined); - assert.strictEqual(baseData.measurements, undefined); - }); - - it("should correctly parse SpanKind.PRODUCER", () => { - const envelope = { data: { baseData: {} } } as Envelope; - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.PRODUCER - ); - span.setAttributes(attributes); - - const baseData = envelope.data?.baseData as RemoteDependencyData; - parseEventHubSpan(span, baseData); - - assert.strictEqual(baseData.type, `Queue Message | ${attributes[AzNamespace]}`); - assert.strictEqual(baseData.target, `${peerAddress}/${destination}`); - - assert.strictEqual((baseData as any).source, undefined); - assert.strictEqual(baseData.measurements, undefined); - }); - - it("should correctly parse SpanKind.CONSUMER", () => { - const startTime = Date.now(); - const envelope = { data: { baseData: {} } } as Envelope; - const span = new Span( - tracer, - ROOT_CONTEXT, - "test span", - { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, - SpanKind.CONSUMER, - undefined, - [ - { - context: (null as unknown) as SpanContext, - attributes: { [ENQUEUED_TIME]: startTime - 111 } - }, - { - context: (null as unknown) as SpanContext, - attributes: { [ENQUEUED_TIME]: startTime - 222 } - }, - { - context: (null as unknown) as SpanContext, - attributes: { [ENQUEUED_TIME]: startTime - 111 } - } - ] - ); - - // cast since startTime is readonly - (span as { startTime: HrTime }).startTime = timeInputToHrTime(startTime); - span.setAttributes(attributes); - - const baseData = envelope.data?.baseData as RemoteDependencyData; - parseEventHubSpan(span, baseData); - assert.strictEqual(baseData.type, `Queue Message | ${attributes[AzNamespace]}`); - assert.strictEqual((baseData as any).source, `${peerAddress}/${destination}`); - assert.deepStrictEqual(baseData.measurements, { - [TIME_SINCE_ENQUEUED]: 148 - }); - - assert.strictEqual(baseData.target, undefined); - }); -}); diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts index f16fe42b9bdf..c55866b0afe6 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts @@ -6,29 +6,23 @@ import { SpanKind, SpanStatusCode, ROOT_CONTEXT } from "@opentelemetry/api"; import * as assert from "assert"; import { hrTimeToMilliseconds } from "@opentelemetry/core"; import { Resource } from "@opentelemetry/resources"; -import { ResourceAttributes } from "@opentelemetry/semantic-conventions"; +import { SemanticAttributes, SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; import { Tags, Properties, Measurements } from "../../src/types"; -import { - AI_CLOUD_ROLE, - AI_CLOUD_ROLE_INSTACE -} from "../../src/utils/constants/applicationinsights"; -import * as http from "../../src/utils/constants/span/httpAttributes"; -import * as grpc from "../../src/utils/constants/span/grpcAttributes"; import * as ai from "../../src/utils/constants/applicationinsights"; import { Context, getInstance } from "../../src/platform"; import { msToTimeSpan } from "../../src/utils/breezeUtils"; import { readableSpanToEnvelope } from "../../src/utils/spanUtils"; -import { RemoteDependencyData, RequestData } from "../../src/generated"; +import { RemoteDependencyData, RequestData, KnownContextTagKeys } from "../../src/generated"; import { TelemetryItem as Envelope } from "../../src/generated"; const context = getInstance(undefined, "./"); const tracerProviderConfig: TracerConfig = { resource: new Resource({ - [ResourceAttributes.SERVICE_INSTANCE_ID]: "testServiceInstanceID", - [ResourceAttributes.SERVICE_NAME]: "testServiceName", - [ResourceAttributes.SERVICE_NAMESPACE]: "testServiceNamespace" + [SemanticResourceAttributes.SERVICE_INSTANCE_ID]: "testServiceInstanceID", + [SemanticResourceAttributes.SERVICE_NAME]: "testServiceName", + [SemanticResourceAttributes.SERVICE_NAMESPACE]: "testServiceNamespace" }) }; @@ -62,8 +56,8 @@ function assertEnvelope( } const expectedServiceTags: Tags = { - [AI_CLOUD_ROLE]: "testServiceNamespace.testServiceName", - [AI_CLOUD_ROLE_INSTACE]: "testServiceInstanceID" + [KnownContextTagKeys.AiCloudRole]: "testServiceNamespace.testServiceName", + [KnownContextTagKeys.AiCloudRoleInstance]: "testServiceInstanceID" }; assert.deepStrictEqual(envelope.tags, { ...context.tags, @@ -94,17 +88,16 @@ describe("spanUtils.ts", () => { ); span.setAttributes({ "extra.attribute": "foo", - [grpc.GRPC_STATUS_CODE]: SpanStatusCode.OK, - [grpc.GRPC_METHOD]: "/foo.Example/Foo" + [SemanticAttributes.RPC_GRPC_STATUS_CODE]: 123, + [SemanticAttributes.RPC_METHOD]: "/foo.Example/Foo" }); span.setStatus({ code: SpanStatusCode.OK }); span.end(); const expectedTags: Tags = { - [ai.AI_OPERATION_ID]: "traceid", - [ai.AI_OPERATION_PARENT_ID]: "parentSpanId", - [ai.AI_OPERATION_NAME]: "/foo.Example/Foo" + [KnownContextTagKeys.AiOperationId]: "traceid", + [KnownContextTagKeys.AiOperationParentId]: "parentSpanId", }; const expectedProperties = { "extra.attribute": "foo" @@ -115,10 +108,9 @@ describe("spanUtils.ts", () => { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, success: true, - responseCode: "1", - url: "/foo.Example/Foo", + responseCode: "123", name: `parent span`, - version: 1, + version: 2, properties: expectedProperties, measurements: {} }; @@ -145,16 +137,17 @@ describe("spanUtils.ts", () => { ); span.setAttributes({ "extra.attribute": "foo", - [grpc.GRPC_STATUS_CODE]: SpanStatusCode.OK, - [grpc.GRPC_METHOD]: "/foo.Example/Foo" + [SemanticAttributes.RPC_GRPC_STATUS_CODE]: 123, + [SemanticAttributes.RPC_METHOD]: "/foo.Example/Foo", + [SemanticAttributes.RPC_SYSTEM]: "test rpc system" }); span.setStatus({ code: SpanStatusCode.OK }); span.end(); const expectedTags: Tags = { - [ai.AI_OPERATION_ID]: "traceid", - [ai.AI_OPERATION_PARENT_ID]: "parentSpanId" + [KnownContextTagKeys.AiOperationId]: "traceid", + [KnownContextTagKeys.AiOperationParentId]: "parentSpanId" }; const expectedProperties = { "extra.attribute": "foo" @@ -164,12 +157,11 @@ describe("spanUtils.ts", () => { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, success: true, - resultCode: "1", - target: "/foo.Example/Foo", - data: "/foo.Example/Foo", - type: "GRPC", + resultCode: "123", + target: "test rpc system", + type: "Dependency", name: `parent span`, - version: 1, + version: 2, properties: expectedProperties, measurements: {} }; @@ -205,8 +197,8 @@ describe("spanUtils.ts", () => { span.end(); const expectedTime = new Date(hrTimeToMilliseconds(span.startTime)); const expectedTags: Tags = { - [ai.AI_OPERATION_ID]: "traceid", - [ai.AI_OPERATION_PARENT_ID]: "parentSpanId" + [KnownContextTagKeys.AiOperationId]: "traceid", + [KnownContextTagKeys.AiOperationParentId]: "parentSpanId" }; const expectedProperties = { "extra.attribute": "foo" @@ -216,9 +208,9 @@ describe("spanUtils.ts", () => { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, success: true, - responseCode: "1", + responseCode: "0", name: `parent span`, - version: 1, + version: 2, source: undefined, properties: expectedProperties, measurements: {} @@ -254,8 +246,8 @@ describe("spanUtils.ts", () => { }); span.end(); const expectedTags: Tags = { - [ai.AI_OPERATION_ID]: "traceid", - [ai.AI_OPERATION_PARENT_ID]: "parentSpanId" + [KnownContextTagKeys.AiOperationId]: "traceid", + [KnownContextTagKeys.AiOperationParentId]: "parentSpanId" }; const expectedProperties = { "extra.attribute": "foo" @@ -265,11 +257,10 @@ describe("spanUtils.ts", () => { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, success: true, - resultCode: "1", - target: undefined, + resultCode: "0", type: "Dependency", name: `parent span`, - version: 1, + version: 2, properties: expectedProperties, measurements: {} }; @@ -298,23 +289,21 @@ describe("spanUtils.ts", () => { "parentSpanId" ); span.setAttributes({ - [http.HTTP_METHOD]: "GET", - [http.HTTP_ROUTE]: "/api/example", - [http.HTTP_URL]: "https://example.com/api/example", - [http.HTTP_STATUS_CODE]: 200, + [SemanticAttributes.HTTP_METHOD]: "GET", + [SemanticAttributes.HTTP_ROUTE]: "/api/example", + [SemanticAttributes.HTTP_URL]: "https://example.com/api/example", + [SemanticAttributes.HTTP_STATUS_CODE]: 200, "extra.attribute": "foo" }); span.setStatus({ code: SpanStatusCode.OK }); span.end(); - const expectedTags: Tags = { - [ai.AI_OPERATION_ID]: "traceid", - [ai.AI_OPERATION_PARENT_ID]: "parentSpanId", - [ai.AI_OPERATION_NAME]: `${span.attributes[http.HTTP_METHOD] as string} ${span.attributes[ - http.HTTP_ROUTE - ] as string}` - }; + const expectedTags: Tags = {}; + expectedTags[KnownContextTagKeys.AiOperationId] = "traceid"; + expectedTags[KnownContextTagKeys.AiOperationParentId] = "parentSpanId"; + expectedTags[KnownContextTagKeys.AiOperationName] = "GET parent span"; + const expectedProperties = { "extra.attribute": "foo" }; @@ -325,8 +314,8 @@ describe("spanUtils.ts", () => { success: true, responseCode: "200", url: "https://example.com/api/example", - name: `GET /api/example`, - version: 1, + name: `parent span`, + version: 2, source: undefined, properties: expectedProperties, measurements: {} @@ -353,19 +342,20 @@ describe("spanUtils.ts", () => { "parentSpanId" ); span.setAttributes({ - [http.HTTP_METHOD]: "GET", - [http.HTTP_URL]: "https://example.com/api/example", - [http.HTTP_STATUS_CODE]: 200, + [SemanticAttributes.HTTP_METHOD]: "GET", + [SemanticAttributes.HTTP_URL]: "https://example.com/api/example", + [SemanticAttributes.PEER_SERVICE]: "https://someotherexample.com/api/example", + [SemanticAttributes.HTTP_STATUS_CODE]: 200, "extra.attribute": "foo" }); span.setStatus({ code: SpanStatusCode.OK }); span.end(); - const expectedTags: Tags = { - [ai.AI_OPERATION_ID]: span.spanContext().traceId, - [ai.AI_OPERATION_PARENT_ID]: "parentSpanId" - }; + const expectedTags: Tags = {}; + expectedTags[KnownContextTagKeys.AiOperationId] = span.spanContext().traceId; + expectedTags[KnownContextTagKeys.AiOperationParentId] = "parentSpanId"; + expectedTags[KnownContextTagKeys.AiOperationName] = "parent span"; const expectedProperties = { "extra.attribute": "foo" }; @@ -375,11 +365,11 @@ describe("spanUtils.ts", () => { id: `|traceid.spanId.`, success: true, resultCode: "200", - type: "HTTP", - target: "example.com", + type: "Http", + target: "https://someotherexample.com/api/example", data: "https://example.com/api/example", - name: `GET /api/example`, - version: 1, + name: `parent span`, + version: 2, properties: expectedProperties, measurements: {} }; diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts index 03873ab3bb1e..2a00a2bebb26 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts @@ -2,12 +2,8 @@ // Licensed under the MIT license. import * as assert from "assert"; -import { - AI_OPERATION_ID, - AI_OPERATION_PARENT_ID -} from "../../src/utils/constants/applicationinsights"; import { Expectation } from "./types"; -import { MonitorBase, RequestData, TelemetryItem as Envelope } from "../../src/generated"; +import { MonitorBase, RequestData, TelemetryItem as Envelope, KnownContextTagKeys } from "../../src/generated"; import { TelemetryItem as EnvelopeMapper } from "../../src/generated/models/mappers"; export const assertData = (actual: MonitorBase, expected: MonitorBase): void => { @@ -35,7 +31,7 @@ export const assertTrace = (actual: Envelope[], expectation: Expectation): void if (envelope.length !== 1) { assert.ok(false, `assertTrace: could not find exported envelope: ${expectation.name}`); } - const operationId = envelope[0].tags![AI_OPERATION_ID]; + const operationId = envelope[0].tags![KnownContextTagKeys.AiOperationId]; const parseId = (id: string): { traceId: string; spanId: string } => { const parts = id.replace("|", "").split("."); @@ -50,8 +46,8 @@ export const assertTrace = (actual: Envelope[], expectation: Expectation): void const { spanId } = parseId((envelope[0].data!.baseData as RequestData).id); return ( - e.tags![AI_OPERATION_ID] === operationId && - e.tags![AI_OPERATION_PARENT_ID] === spanId && + e.tags![KnownContextTagKeys.AiOperationId] === operationId && + e.tags![KnownContextTagKeys.AiOperationParentId] === spanId && (e.data!.baseData as RequestData).name === (child.data!.baseData as RequestData).name ); }); @@ -83,8 +79,7 @@ export const assertExpectation = (actual: Envelope[], expectations: Expectation[ if (envelope.length !== 1) { assert.ok( false, - `assertExpectation: could not find exported envelope: ${ - (expectation.data?.baseData as RequestData).name + `assertExpectation: could not find exported envelope: ${(expectation.data?.baseData as RequestData).name }` ); } diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/utils/basic.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/utils/basic.ts index 692c0a2c4ba3..929721c6cf4d 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/utils/basic.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/utils/basic.ts @@ -87,10 +87,10 @@ export class BasicScenario implements Scenario { data: { baseType: "RequestData", baseData: { - version: 1, + version: 2, name: "BasicScenario.Root", duration: msToTimeSpan(600), - responseCode: SpanStatusCode.OK.toString(), + responseCode: "0", success: true, properties: { foo: "bar" @@ -103,11 +103,11 @@ export class BasicScenario implements Scenario { data: { baseType: "RemoteDependencyData", baseData: { - version: 1, + version: 2, name: "BasicScenario.Child.1", duration: msToTimeSpan(100), success: true, - resultCode: SpanStatusCode.OK.toString(), + resultCode: "0", properties: { numbers: "123" } @@ -120,11 +120,11 @@ export class BasicScenario implements Scenario { data: { baseType: "RemoteDependencyData", baseData: { - version: 1, + version: 2, name: "BasicScenario.Child.2", duration: msToTimeSpan(100), success: true, - resultCode: SpanStatusCode.OK.toString(), + resultCode: "0", properties: { numbers: "1234" } From 0ce5d5cc97a817cad7b59bccc36c1182300547c3 Mon Sep 17 00:00:00 2001 From: Hector Hernandez Guzman Date: Tue, 31 Aug 2021 16:50:00 -0700 Subject: [PATCH 05/15] Revert unwanted changes --- .github/CODEOWNERS | 4 ++-- sdk/cosmosdb/cosmos/CHANGELOG.md | 11 +++-------- sdk/cosmosdb/cosmos/src/retry/RetryContext.ts | 2 +- sdk/cosmosdb/cosmos/src/retry/retryUtility.ts | 3 +-- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7e45e0c47d67..0a5014d0cb83 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -108,8 +108,8 @@ # PRLabel: %Search /sdk/search/ @xirzec @sarangan12 -/sdk/applicationinsights/applicationinsights-query/ @omziv @anatse @raronen @ischrei @danhadari @azmonapplicationinsights -/sdk/operationalinsights/loganalytics/ @omziv @anatse @raronen @ischrei @danhadari @azmonapplicationinsights +/sdk/applicationinsights/applicationinsights-query/ @divyajay @alongafni +/sdk/operationalinsights/loganalytics/ @divyajay @alongafni /sdk/cognitiveservices/cognitiveservices-qnamaker-runtime/ @bingisbestest @nerajput1607 /sdk/cognitiveservices/cognitiveservices-qnamaker/ @bingisbestest @nerajput1607 diff --git a/sdk/cosmosdb/cosmos/CHANGELOG.md b/sdk/cosmosdb/cosmos/CHANGELOG.md index a958196088eb..13dfc48d24e0 100644 --- a/sdk/cosmosdb/cosmos/CHANGELOG.md +++ b/sdk/cosmosdb/cosmos/CHANGELOG.md @@ -8,8 +8,6 @@ ### Bugs Fixed -- Fixed bugs in session token clearing logic. Session Not found (404, substatus 1002) was not being handled correctly by the session retry policy and would mistakenly retry the request with the same session token. - ### Other Changes ## 3.13.0 (2021-08-10) @@ -43,10 +41,8 @@ ## 3.12.0 (2021-07-06) ### Features Added - - With the dropping of support for Node.js versions that are no longer in LTS, the dependency on `@types/node` has been updated to version 12. Read our [support policy](https://github.com/Azure/azure-sdk-for-js/blob/main/SUPPORT.md) for more details. - Added background refresher for endpoints, and new `ConnectionPolicy` options. Refreshing defaults to true, and the default refresh rate is every 5 minutes. - ```js const client = new CosmosClient({ endpoint, @@ -56,14 +52,13 @@ const client = new CosmosClient({ endpointRefreshRateInMs: 700, enableBackgroundEndpointRefreshing: true } -}); +}) ``` - Added `client.dispose()` for closing the endpoint refresher verbosely. Necessary when destroying the CosmosClient inside existing processes like an express web server, or when you want to destroy the client and create a new one in the same process. - ```js -const client = new CosmosClient(); -client.dispose(); // cancels background endpoint refreshing +const client = new CosmosClient() +client.dispose() // cancels background endpoint refreshing ``` ## 3.11.5 (2021-06-10) diff --git a/sdk/cosmosdb/cosmos/src/retry/RetryContext.ts b/sdk/cosmosdb/cosmos/src/retry/RetryContext.ts index f8a225afad3c..9b5a067cb3cc 100644 --- a/sdk/cosmosdb/cosmos/src/retry/RetryContext.ts +++ b/sdk/cosmosdb/cosmos/src/retry/RetryContext.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. export interface RetryContext { - retryCount: number; + retryCount?: number; retryRequestOnPreferredLocations?: boolean; clearSessionTokenNotAvailable?: boolean; } diff --git a/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts b/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts index 87ee429c5de7..883d7deaf3fc 100644 --- a/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts +++ b/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts @@ -36,7 +36,7 @@ interface RetryPolicies { * @hidden */ export async function execute({ - retryContext = { retryCount: 0 }, + retryContext = {}, retryPolicies, requestContext, executeRequest @@ -64,7 +64,6 @@ export async function execute({ } if (retryContext && retryContext.clearSessionTokenNotAvailable) { requestContext.client.clearSessionToken(requestContext.path); - delete requestContext.headers["x-ms-session-token"]; } requestContext.endpoint = await requestContext.globalEndpointManager.resolveServiceEndpoint( requestContext.resourceType, From fe1da8ad1c5d5ba90800097b3b0b0544f3005e13 Mon Sep 17 00:00:00 2001 From: Hector Hernandez Guzman Date: Tue, 31 Aug 2021 16:53:30 -0700 Subject: [PATCH 06/15] WIP --- sdk/cosmosdb/cosmos/src/retry/retryUtility.ts | 3 +- .../cosmos/test/internal/session.spec.ts | 29 ++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts b/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts index 883d7deaf3fc..87ee429c5de7 100644 --- a/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts +++ b/sdk/cosmosdb/cosmos/src/retry/retryUtility.ts @@ -36,7 +36,7 @@ interface RetryPolicies { * @hidden */ export async function execute({ - retryContext = {}, + retryContext = { retryCount: 0 }, retryPolicies, requestContext, executeRequest @@ -64,6 +64,7 @@ export async function execute({ } if (retryContext && retryContext.clearSessionTokenNotAvailable) { requestContext.client.clearSessionToken(requestContext.path); + delete requestContext.headers["x-ms-session-token"]; } requestContext.endpoint = await requestContext.globalEndpointManager.resolveServiceEndpoint( requestContext.resourceType, diff --git a/sdk/cosmosdb/cosmos/test/internal/session.spec.ts b/sdk/cosmosdb/cosmos/test/internal/session.spec.ts index 1d62d66d5a63..1df8ce42a7d9 100644 --- a/sdk/cosmosdb/cosmos/test/internal/session.spec.ts +++ b/sdk/cosmosdb/cosmos/test/internal/session.spec.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. + import assert from "assert"; import { Suite } from "mocha"; import { ClientContext, Container, PluginConfig, PluginOn } from "../../src"; @@ -8,12 +9,12 @@ import { ConsistencyLevel } from "../../src"; import { CosmosClient } from "../../src"; import { SessionContainer } from "../../src/session/sessionContainer"; import { endpoint, masterKey } from "../public/common/_testConfig"; -import { getTestDatabase, removeAllDatabases } from "../public/common/TestHelpers"; +import { addEntropy, getTestDatabase, removeAllDatabases } from "../public/common/TestHelpers"; import { RequestContext } from "../../src"; import { Response } from "../../src/request/Response"; -describe("New session token", function() { - it("preserves tokens", async function() { +describe("New session token", function () { + it("preserves tokens", async function () { let response: Response; let rqContext: RequestContext; const plugins: PluginConfig[] = [ @@ -68,12 +69,13 @@ describe("New session token", function() { }); }); -describe("Session Token", function(this: Suite) { - beforeEach(async function() { +// For some reason this test does not pass against the emulator. Skipping it for now +describe.skip("Session Token", function (this: Suite) { + beforeEach(async function () { await removeAllDatabases(); }); - it("retries session not found successfully", async function() { + it("retries session not found successfully", async function () { const clientA = new CosmosClient({ endpoint, key: masterKey, @@ -95,28 +97,33 @@ describe("Session Token", function(this: Suite) { if (context.headers["x-ms-session-token"]) { context.headers["x-ms-session-token"] = "0:0#900000#3=8600000#10=-1"; } - return next(context); + const response = await next(context); + return response; } } ] }); + const dbId = addEntropy("sessionTestDB"); + const containerId = addEntropy("sessionTestContainer"); + // Create Database and Container const { database } = await clientA.databases.createIfNotExists({ - id: "sessionTest" + id: dbId }); const { container } = await database.containers.createIfNotExists({ - id: "sessionTest" + id: containerId }); // Create items using both clients so they each establish a session with the backend - const container2 = await clientB.database("sessionTest").container("sessionTest"); + const container2 = clientB.database(dbId).container(containerId); await Promise.all([createItem(container), createItem(container2)]); // Create an item using client const id = await createItem(container); const { resource, statusCode } = await container2.item(id).read(); - assert(resource); + console.log(statusCode, resource); + assert.ok(resource); assert.strictEqual(statusCode, 200); }); }); From 996f7a0ee9276f8bed82321101b54bfd404b2016 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 1 Sep 2021 11:21:25 -0700 Subject: [PATCH 07/15] Rush update --- common/config/rush/pnpm-lock.yaml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 23f68ee9cdd5..2ff397a24984 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -469,7 +469,7 @@ packages: integrity: sha512-CxaMaEjwtsmIhWtjHyGimKO7RmES0YxPqGQ9+jKqGygNlhG5NYHktDaiQu6w7k3g+I51VaLXtVSt+BVFd6VWfQ== /@azure/identity/1.2.5_debug@4.3.2: dependencies: - '@azure/core-http': 1.2.3 + '@azure/core-http': 1.2.6 '@azure/core-tracing': 1.0.0-preview.9 '@azure/logger': 1.0.2 '@azure/msal-node': 1.0.0-beta.6_debug@4.3.2 @@ -1262,6 +1262,12 @@ packages: node: '>=8.0.0' resolution: integrity: sha512-t4fKikazahwNKmwD+CE/icHyuZldWvNMupJhjxdk9T/KxHFx3zCGjHT3MKavwYP6abzgAAm5WwzD1oHlmj7dyg== + /@opentelemetry/semantic-conventions/0.24.0: + dev: false + engines: + node: '>=8.0.0' + resolution: + integrity: sha512-a/szuMQV0Quy0/M7kKdglcbRSoorleyyOwbTNNJ32O+RBN766wbQlMTvdimImTmwYWGr+NJOni1EcC242WlRcA== /@opentelemetry/tracing/0.22.0_@opentelemetry+api@1.0.2: dependencies: '@opentelemetry/api': 1.0.2 @@ -2830,7 +2836,7 @@ packages: integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== /debug/3.2.6: dependencies: - ms: 2.1.1 + ms: 2.1.3 deprecated: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797) dev: false resolution: @@ -3566,7 +3572,7 @@ packages: integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== /extract-zip/2.0.1: dependencies: - debug: 4.3.1 + debug: 4.3.2 get-stream: 5.2.0 yauzl: 2.10.0 dev: false @@ -11021,7 +11027,7 @@ packages: '@opentelemetry/instrumentation-http': 0.22.0_@opentelemetry+api@1.0.2 '@opentelemetry/node': 0.22.0_@opentelemetry+api@1.0.2 '@opentelemetry/resources': 0.22.0_@opentelemetry+api@1.0.2 - '@opentelemetry/semantic-conventions': 0.22.0 + '@opentelemetry/semantic-conventions': 0.24.0 '@opentelemetry/tracing': 0.22.0_@opentelemetry+api@1.0.2 '@types/mocha': 7.0.2 '@types/node': 12.20.21 @@ -11043,7 +11049,7 @@ packages: dev: false name: '@rush-temp/monitor-opentelemetry-exporter' resolution: - integrity: sha512-WYH8CaZOliclSLRlTl5xrJfy5dZtOvEc1ypw2W6qwQABZOeeF8geFXO6glkcxMCrEKIIeAjN14yyDXDQMsF3fg== + integrity: sha512-6x73k57tzSG7uXsMYo/N19DtEBt+PgxbV54C8le9hNOFRxpi6okX3gvZS+9QF7JmxKrXa7ohxH9331gOVmcnEQ== tarball: file:projects/monitor-opentelemetry-exporter.tgz version: 0.0.0 file:projects/monitor-query.tgz: From 5b021e706804ee00cc698ce8bc91de8b6d95c032 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 1 Sep 2021 11:47:41 -0700 Subject: [PATCH 08/15] Format --- .../utils/constants/applicationinsights.ts | 3 +- .../src/utils/spanUtils.ts | 60 ++++++++++--------- .../test/internal/spanUtils.test.ts | 7 ++- .../test/utils/assert.ts | 10 +++- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts index ca34b4476dfa..fa76ab0cf462 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts @@ -22,10 +22,9 @@ export const TIME_SINCE_ENQUEUED = "timeSinceEnqueued"; */ export const packageVersion = "1.0.0-beta.4"; - export enum DependencyTypes { InProc = "InProc", QueueMessage = "Queue Message", Sql = "SQL", - Http = "Http", + Http = "Http" } diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts index 5ae894d87c61..5b9c0d94c795 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts @@ -6,14 +6,21 @@ import { URL } from "url"; import { ReadableSpan } from "@opentelemetry/tracing"; import { hrTimeToMilliseconds } from "@opentelemetry/core"; import { diag, SpanKind, SpanStatusCode, Link } from "@opentelemetry/api"; -import { SemanticResourceAttributes, SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import { + SemanticResourceAttributes, + SemanticAttributes +} from "@opentelemetry/semantic-conventions"; import { Tags, Properties, MSLink, Measurements } from "../types"; import { msToTimeSpan } from "./breezeUtils"; import { getInstance } from "../platform"; import { DependencyTypes, MS_LINKS } from "./constants/applicationinsights"; -import { RemoteDependencyData, RequestData, TelemetryItem as Envelope, KnownContextTagKeys } from "../generated"; - +import { + RemoteDependencyData, + RequestData, + TelemetryItem as Envelope, + KnownContextTagKeys +} from "../generated"; function createTagsFromSpan(span: ReadableSpan): Tags { const context = getInstance(); @@ -33,7 +40,8 @@ function createTagsFromSpan(span: ReadableSpan): Tags { tags[KnownContextTagKeys.AiCloudRole] = String(serviceName); } } - const serviceInstanceId = span.resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID]; + const serviceInstanceId = + span.resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID]; if (serviceInstanceId) { tags[KnownContextTagKeys.AiCloudRoleInstance] = String(serviceInstanceId); } else { @@ -57,12 +65,10 @@ function createTagsFromSpan(span: ReadableSpan): Tags { tags[KnownContextTagKeys.AiOperationName] = `${httpMethod as string} ${span.name as string}`; if (httpClientIp) { tags[KnownContextTagKeys.AiLocationIp] = String(httpClientIp); - } - else if (netPeerIp) { + } else if (netPeerIp) { tags[KnownContextTagKeys.AiLocationIp] = String(netPeerIp); } - } - else { + } else { tags[KnownContextTagKeys.AiOperationName] = span.name; if (netPeerIp) { tags[KnownContextTagKeys.AiLocationIp] = String(netPeerIp); @@ -84,8 +90,15 @@ function createPropertiesFromSpan(span: ReadableSpan): [Properties, Measurements const measurements: Measurements = {}; for (const key of Object.keys(span.attributes)) { - if (!(key.startsWith("http.") || key.startsWith("rpc.") || key.startsWith("db.") - || key.startsWith("peer.") || key.startsWith("net."))) { + if ( + !( + key.startsWith("http.") || + key.startsWith("rpc.") || + key.startsWith("db.") || + key.startsWith("peer.") || + key.startsWith("net.") + ) + ) { properties[key] = span.attributes[key] as string; } } @@ -108,8 +121,7 @@ function getUrl(span: ReadableSpan): string { const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; if (httpUrl) { return String(httpUrl); - } - else { + } else { const httpScheme = span.attributes[SemanticAttributes.HTTP_SCHEME]; if (httpScheme) { const httpTarget = span.attributes[SemanticAttributes.HTTP_TARGET]; @@ -117,15 +129,13 @@ function getUrl(span: ReadableSpan): string { const httpHost = span.attributes[SemanticAttributes.HTTP_HOST]; if (httpHost) { return `${httpScheme}://${httpHost}${httpTarget}`; - } - else { + } else { const netPeerPort = span.attributes[SemanticAttributes.NET_PEER_PORT]; if (netPeerPort) { const netPeerName = span.attributes[SemanticAttributes.NET_PEER_NAME]; if (netPeerName) { return `${httpScheme}://${netPeerName}:${netPeerPort}`; - } - else { + } else { const netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; if (netPeerIp) { return `${httpScheme}://${netPeerIp}:${netPeerPort}`; @@ -148,17 +158,13 @@ function getTarget(span: ReadableSpan): string { const netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; if (peerService) { return String(peerService); - } - else if (httpHost) { + } else if (httpHost) { return String(httpHost); - } - else if (httpUrl) { + } else if (httpUrl) { return String(httpUrl); - } - else if (netPeerName) { + } else if (netPeerName) { return String(netPeerName); - } - else if (netPeerIp) { + } else if (netPeerIp) { return String(netPeerIp); } return ""; @@ -206,8 +212,7 @@ function createDependencyData(span: ReadableSpan): RemoteDependencyData { const dbName = span.attributes[SemanticAttributes.DB_NAME]; if (target) { remoteDependencyData.target = dbName ? `${target}/${dbName}` : `${target}`; - } - else { + } else { remoteDependencyData.target = dbName ? `${dbName}` : `${dbSystem}`; } } @@ -220,8 +225,7 @@ function createDependencyData(span: ReadableSpan): RemoteDependencyData { let target = getTarget(span); if (target) { remoteDependencyData.target = `${target}`; - } - else { + } else { const rpcSystem = span.attributes[SemanticAttributes.RPC_SYSTEM]; if (rpcSystem) { remoteDependencyData.target = String(rpcSystem); diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts index c55866b0afe6..4ba5aa94563d 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts @@ -6,7 +6,10 @@ import { SpanKind, SpanStatusCode, ROOT_CONTEXT } from "@opentelemetry/api"; import * as assert from "assert"; import { hrTimeToMilliseconds } from "@opentelemetry/core"; import { Resource } from "@opentelemetry/resources"; -import { SemanticAttributes, SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; +import { + SemanticAttributes, + SemanticResourceAttributes +} from "@opentelemetry/semantic-conventions"; import { Tags, Properties, Measurements } from "../../src/types"; import * as ai from "../../src/utils/constants/applicationinsights"; @@ -97,7 +100,7 @@ describe("spanUtils.ts", () => { span.end(); const expectedTags: Tags = { [KnownContextTagKeys.AiOperationId]: "traceid", - [KnownContextTagKeys.AiOperationParentId]: "parentSpanId", + [KnownContextTagKeys.AiOperationParentId]: "parentSpanId" }; const expectedProperties = { "extra.attribute": "foo" diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts index 2a00a2bebb26..4d00064820e7 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts @@ -3,7 +3,12 @@ import * as assert from "assert"; import { Expectation } from "./types"; -import { MonitorBase, RequestData, TelemetryItem as Envelope, KnownContextTagKeys } from "../../src/generated"; +import { + MonitorBase, + RequestData, + TelemetryItem as Envelope, + KnownContextTagKeys +} from "../../src/generated"; import { TelemetryItem as EnvelopeMapper } from "../../src/generated/models/mappers"; export const assertData = (actual: MonitorBase, expected: MonitorBase): void => { @@ -79,7 +84,8 @@ export const assertExpectation = (actual: Envelope[], expectations: Expectation[ if (envelope.length !== 1) { assert.ok( false, - `assertExpectation: could not find exported envelope: ${(expectation.data?.baseData as RequestData).name + `assertExpectation: could not find exported envelope: ${ + (expectation.data?.baseData as RequestData).name }` ); } From 41db8a5382c9e1ee1c959b0cc7323acad245eb25 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 3 Sep 2021 11:37:38 -0700 Subject: [PATCH 09/15] Addressing comments --- common/config/rush/pnpm-lock.yaml | 2 +- .../src/Declarations/Constants.ts | 28 ----- .../utils/constants/applicationinsights.ts | 3 +- .../src/utils/constants/span/azAttributes.ts | 18 +++ .../src/utils/eventhub.ts | 70 +++++++++++ .../src/utils/spanUtils.ts | 119 ++++++++++-------- .../test/internal/eventhub.test.ts | 110 ++++++++++++++++ .../test/internal/spanUtils.test.ts | 5 +- 8 files changed, 270 insertions(+), 85 deletions(-) create mode 100644 sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/azAttributes.ts create mode 100644 sdk/monitor/monitor-opentelemetry-exporter/src/utils/eventhub.ts create mode 100644 sdk/monitor/monitor-opentelemetry-exporter/test/internal/eventhub.test.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index a6034604c830..665a333c5b65 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -11127,7 +11127,7 @@ packages: dev: false name: '@rush-temp/monitor-opentelemetry-exporter' resolution: - integrity: sha512-6x73k57tzSG7uXsMYo/N19DtEBt+PgxbV54C8le9hNOFRxpi6okX3gvZS+9QF7JmxKrXa7ohxH9331gOVmcnEQ== + integrity: sha512-+klVek1Lkefq0LRB5rQidbhIfR/5ktG4iBHONoiIcneSW9skjy0LBnxeyOOTMdnu96R5KU+ssTS6e0DXrfog7Q== tarball: file:projects/monitor-opentelemetry-exporter.tgz version: 0.0.0 file:projects/monitor-query.tgz: diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/Declarations/Constants.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/Declarations/Constants.ts index 4893869dd68c..ba3d0a221fed 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/Declarations/Constants.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/Declarations/Constants.ts @@ -127,31 +127,3 @@ export type QuickPulseType = | "RequestTelemetryDocument" | "DependencyTelemetryDocument" | "AvailabilityTelemetryDocument"; - -/** - * OpenTelemetry Span Attributes. - * @internal - */ -export const SpanAttribute = { - // HTTP - HttpHost: "http.host", - HttpMethod: "http.method", - HttpPort: "http.port", - HttpStatusCode: "http.status_code", - HttpUrl: "http.url", - HttpUserAgent: "http.user_agent", - - // GRPC - GrpcMethod: "grpc.method", - GrpcService: "rpc.service" // rpc not grpc -}; - -/** - * OpenTelemetry dependency type names. - * @internal - */ -export const DependencyTypeName = { - Grpc: "GRPC", - Http: "HTTP", - InProc: "InProc" -}; diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts index dbd949bda904..2dafb6e8ed1e 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts @@ -26,5 +26,6 @@ export enum DependencyTypes { InProc = "InProc", QueueMessage = "Queue Message", Sql = "SQL", - Http = "Http" + Http = "Http", + Grpc = "GRPC", } diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/azAttributes.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/azAttributes.ts new file mode 100644 index 000000000000..439894fee189 --- /dev/null +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/span/azAttributes.ts @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Azure SDK namespace. + * @internal + */ +export const AzNamespace = "az.namespace"; +/** + * Azure SDK Eventhub. + * @internal + */ +export const MicrosoftEventHub = "Microsoft.EventHub"; +/** + * Azure SDK message bus destination. + * @internal + */ +export const MessageBusDestination = "message_bus.destination"; diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/eventhub.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/eventhub.ts new file mode 100644 index 000000000000..c6750362efb6 --- /dev/null +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/eventhub.ts @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { SpanKind } from "@opentelemetry/api"; +import { hrTimeToMilliseconds } from "@opentelemetry/core"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import { ReadableSpan } from "@opentelemetry/tracing"; +import { RemoteDependencyData, RequestData } from "../generated"; +import { TIME_SINCE_ENQUEUED, ENQUEUED_TIME } from "./constants/applicationinsights"; +import { + AzNamespace, + MessageBusDestination, + MicrosoftEventHub +} from "./constants/span/azAttributes"; + +/** + * Average span.links[].attributes.enqueuedTime + */ +const getTimeSinceEnqueued = (span: ReadableSpan) => { + let countEnqueueDiffs = 0; + let sumEnqueueDiffs = 0; + const startTimeMs = hrTimeToMilliseconds(span.startTime); + + span.links.forEach(({ attributes }) => { + const enqueuedTime = attributes?.[ENQUEUED_TIME] as string | number; + if (enqueuedTime) { + countEnqueueDiffs += 1; + sumEnqueueDiffs += startTimeMs - (parseFloat(enqueuedTime.toString()) || 0); + } + }); + + return Math.max(sumEnqueueDiffs / (countEnqueueDiffs || 1), 0); +}; + +/** + * Implementation of Mapping to Azure Monitor + * + * https://gist.github.com/lmolkova/e4215c0f44a49ef824983382762e6b92#mapping-to-azure-monitor-application-insights-telemetry + * @internal + */ +export const parseEventHubSpan = ( + span: ReadableSpan, + baseData: RequestData | RemoteDependencyData +): void => { + const namespace = span.attributes[AzNamespace] as typeof MicrosoftEventHub; + const peerAddress = ((span.attributes[SemanticAttributes.NET_PEER_NAME] || + span.attributes["peer.address"] || + "unknown") as string).replace(/\/$/g, ""); // remove trailing "/" + const messageBusDestination = (span.attributes[MessageBusDestination] || "unknown") as string; + + switch (span.kind) { + case SpanKind.CLIENT: + baseData.type = namespace; + baseData.target = `${peerAddress}/${messageBusDestination}`; + break; + case SpanKind.PRODUCER: + baseData.type = `Queue Message | ${namespace}`; + baseData.target = `${peerAddress}/${messageBusDestination}`; + break; + case SpanKind.CONSUMER: + baseData.type = `Queue Message | ${namespace}`; + (baseData as any).source = `${peerAddress}/${messageBusDestination}`; + baseData.measurements = { + ...baseData.measurements, + [TIME_SINCE_ENQUEUED]: getTimeSinceEnqueued(span) + }; + break; + default: // no op + } +}; diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts index 5b9c0d94c795..8d2c1d4886d1 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts @@ -2,7 +2,6 @@ // Licensed under the MIT license. import os from "os"; -import { URL } from "url"; import { ReadableSpan } from "@opentelemetry/tracing"; import { hrTimeToMilliseconds } from "@opentelemetry/core"; import { diag, SpanKind, SpanStatusCode, Link } from "@opentelemetry/api"; @@ -14,7 +13,9 @@ import { import { Tags, Properties, MSLink, Measurements } from "../types"; import { msToTimeSpan } from "./breezeUtils"; import { getInstance } from "../platform"; +import { parseEventHubSpan } from "./eventhub"; import { DependencyTypes, MS_LINKS } from "./constants/applicationinsights"; +import { AzNamespace, MicrosoftEventHub } from "./constants/span/azAttributes"; import { RemoteDependencyData, RequestData, @@ -57,9 +58,9 @@ function createTagsFromSpan(span: ReadableSpan): Tags { if (httpMethod) { let httpClientIp = null; let netPeerIp = null; - if (span.resource && span.resource.attributes) { - httpClientIp = span.resource.attributes[SemanticAttributes.HTTP_CLIENT_IP]; - netPeerIp = span.resource.attributes[SemanticAttributes.NET_PEER_IP]; + if (span.attributes) { + httpClientIp = span.attributes[SemanticAttributes.HTTP_CLIENT_IP]; + netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; } if (span.kind === SpanKind.SERVER) { tags[KnownContextTagKeys.AiOperationName] = `${httpMethod as string} ${span.name as string}`; @@ -74,14 +75,13 @@ function createTagsFromSpan(span: ReadableSpan): Tags { tags[KnownContextTagKeys.AiLocationIp] = String(netPeerIp); } } - const httpUserAgent = span.resource.attributes[SemanticAttributes.HTTP_USER_AGENT]; + // TODO: Operation Name TBD for non HTTP + const httpUserAgent = span.attributes[SemanticAttributes.HTTP_USER_AGENT]; if (httpUserAgent) { // TODO: Not exposed in Swagger, need to update def tags["ai.user.userAgent"] = String(httpUserAgent); } } - // TODO: Operation Name TBD for non HTTP - return tags; } @@ -123,23 +123,21 @@ function getUrl(span: ReadableSpan): string { return String(httpUrl); } else { const httpScheme = span.attributes[SemanticAttributes.HTTP_SCHEME]; - if (httpScheme) { - const httpTarget = span.attributes[SemanticAttributes.HTTP_TARGET]; - if (httpTarget) { - const httpHost = span.attributes[SemanticAttributes.HTTP_HOST]; - if (httpHost) { - return `${httpScheme}://${httpHost}${httpTarget}`; - } else { - const netPeerPort = span.attributes[SemanticAttributes.NET_PEER_PORT]; - if (netPeerPort) { - const netPeerName = span.attributes[SemanticAttributes.NET_PEER_NAME]; - if (netPeerName) { - return `${httpScheme}://${netPeerName}:${netPeerPort}`; - } else { - const netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; - if (netPeerIp) { - return `${httpScheme}://${netPeerIp}:${netPeerPort}`; - } + const httpTarget = span.attributes[SemanticAttributes.HTTP_TARGET]; + if (httpScheme && httpTarget) { + const httpHost = span.attributes[SemanticAttributes.HTTP_HOST]; + if (httpHost) { + return `${httpScheme}://${httpHost}${httpTarget}`; + } else { + const netPeerPort = span.attributes[SemanticAttributes.NET_PEER_PORT]; + if (netPeerPort) { + const netPeerName = span.attributes[SemanticAttributes.NET_PEER_NAME]; + if (netPeerName) { + return `${httpScheme}://${netPeerName}:${netPeerPort}${httpTarget}`; + } else { + const netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; + if (netPeerIp) { + return `${httpScheme}://${netPeerIp}:${netPeerPort}${httpTarget}`; } } } @@ -150,7 +148,7 @@ function getUrl(span: ReadableSpan): string { return ""; } -function getTarget(span: ReadableSpan): string { +function getDependencyTarget(span: ReadableSpan): string { const peerService = span.attributes[SemanticAttributes.PEER_SERVICE]; const httpHost = span.attributes[SemanticAttributes.HTTP_HOST]; const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; @@ -180,9 +178,16 @@ function createDependencyData(span: ReadableSpan): RemoteDependencyData { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), version: 2 }; + if (span.kind === SpanKind.PRODUCER) { + remoteDependencyData.type = DependencyTypes.QueueMessage; + } + if (span.kind === SpanKind.INTERNAL && span.parentSpanId) { + remoteDependencyData.type = DependencyTypes.InProc; + } + const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; const dbSystem = span.attributes[SemanticAttributes.DB_SYSTEM]; - const rpcMethod = span.attributes[SemanticAttributes.RPC_METHOD]; + const rpcSystem = span.attributes[SemanticAttributes.RPC_SYSTEM]; // HTTP Dependency if (httpMethod) { remoteDependencyData.type = DependencyTypes.Http; @@ -191,14 +196,25 @@ function createDependencyData(span: ReadableSpan): RemoteDependencyData { if (httpStatusCode) { remoteDependencyData.resultCode = String(httpStatusCode); } - let target = getTarget(span); + let target = getDependencyTarget(span); if (target) { - let url = new URL(target); - // Ignore port if it is a default port - if (url.port === "80" || url.port === "443") { - url.port = ""; + try { + // Remove default port + let portRegex = new RegExp(/(https?)(:\/\/.*)(:\d+)(\S*)/); + let res = portRegex.exec(target); + if (res != null) { + let protocol = res[1]; + let port = res[3]; + if ((protocol == "https" && port == ":443") || + (protocol == "http" && port == ":80")) { + // Drop port + target = res[1] + res[2] + res[4]; + } + } + } - remoteDependencyData.target = `${url}`; + catch (error) { } + remoteDependencyData.target = `${target}`; } } // DB Dependency @@ -208,7 +224,7 @@ function createDependencyData(span: ReadableSpan): RemoteDependencyData { if (dbStatement) { remoteDependencyData.data = String(dbStatement); } - let target = getTarget(span); + let target = getDependencyTarget(span); const dbName = span.attributes[SemanticAttributes.DB_NAME]; if (target) { remoteDependencyData.target = dbName ? `${target}/${dbName}` : `${target}`; @@ -217,19 +233,17 @@ function createDependencyData(span: ReadableSpan): RemoteDependencyData { } } // grpc Dependency - else if (rpcMethod) { + else if (rpcSystem) { + remoteDependencyData.type = DependencyTypes.Grpc; const grpcStatusCode = span.attributes[SemanticAttributes.RPC_GRPC_STATUS_CODE]; if (grpcStatusCode) { remoteDependencyData.resultCode = String(grpcStatusCode); } - let target = getTarget(span); + let target = getDependencyTarget(span); if (target) { remoteDependencyData.target = `${target}`; - } else { - const rpcSystem = span.attributes[SemanticAttributes.RPC_SYSTEM]; - if (rpcSystem) { - remoteDependencyData.target = String(rpcSystem); - } + } else if (rpcSystem) { + remoteDependencyData.target = String(rpcSystem); } } return remoteDependencyData; @@ -245,14 +259,14 @@ function createRequestData(span: ReadableSpan): RequestData { version: 2, source: undefined }; - const httpUrl = span.attributes[SemanticAttributes.HTTP_URL]; - if (httpUrl) { - requestData.url = String(httpUrl); - } - const httpStatusCode = span.attributes[SemanticAttributes.HTTP_STATUS_CODE]; + const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; const grpcStatusCode = span.attributes[SemanticAttributes.RPC_GRPC_STATUS_CODE]; - if (httpStatusCode) { - requestData.responseCode = String(httpStatusCode); + if (httpMethod) { + requestData.url = getUrl(span); + const httpStatusCode = span.attributes[SemanticAttributes.HTTP_STATUS_CODE]; + if (httpStatusCode) { + requestData.responseCode = String(httpStatusCode); + } } else if (grpcStatusCode) { requestData.responseCode = String(grpcStatusCode); } @@ -293,12 +307,13 @@ export function readableSpanToEnvelope(span: ReadableSpan, ikey: string): Envelo throw new Error(`Unsupported span kind ${span.kind}`); } - if (span.kind === SpanKind.PRODUCER) { - baseData.type = DependencyTypes.QueueMessage; - } - if (span.kind === SpanKind.INTERNAL && span.parentSpanId) { - baseData.type = DependencyTypes.InProc; + // Azure SDK + if (span.attributes[AzNamespace] === MicrosoftEventHub) { + parseEventHubSpan(span, baseData); + } else if (span.attributes[AzNamespace] && span.kind === SpanKind.INTERNAL) { + baseData.type = `${DependencyTypes.InProc} | ${span.attributes[AzNamespace]}` } + return { name, sampleRate, diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/eventhub.test.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/eventhub.test.ts new file mode 100644 index 000000000000..7303ed4a393a --- /dev/null +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/eventhub.test.ts @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { SpanAttributes, HrTime, SpanContext, SpanKind, ROOT_CONTEXT } from "@opentelemetry/api"; +import { timeInputToHrTime } from "@opentelemetry/core"; +import { BasicTracerProvider, Span } from "@opentelemetry/tracing"; +import * as assert from "assert"; +import { ENQUEUED_TIME, TIME_SINCE_ENQUEUED } from "../../src/utils/constants/applicationinsights"; +import { + AzNamespace, + MessageBusDestination, + MicrosoftEventHub +} from "../../src/utils/constants/span/azAttributes"; +import { parseEventHubSpan } from "../../src/utils/eventhub"; +import { RemoteDependencyData, TelemetryItem as Envelope } from "../../src/generated"; + +const tracer = new BasicTracerProvider().getTracer("default"); + +describe("#parseEventHubSpan(...)", () => { + const peerAddress = "example.servicebus.windows.net"; + const destination = "test123"; + const attributes: SpanAttributes = { + [AzNamespace]: MicrosoftEventHub, + ["peer.address"]: peerAddress, + [MessageBusDestination]: destination + }; + + it("should correctly parse SpanKind.CLIENT", () => { + const envelope = { data: { baseData: {} } } as Envelope; + const span = new Span( + tracer, + ROOT_CONTEXT, + "test span", + { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, + SpanKind.CLIENT + ); + span.setAttributes(attributes); + + const baseData = envelope.data?.baseData as RemoteDependencyData; + parseEventHubSpan(span, baseData); + + assert.strictEqual(baseData.type, attributes[AzNamespace]); + assert.strictEqual(baseData.target, `${peerAddress}/${destination}`); + + assert.strictEqual((baseData as any).source, undefined); + assert.strictEqual(baseData.measurements, undefined); + }); + + it("should correctly parse SpanKind.PRODUCER", () => { + const envelope = { data: { baseData: {} } } as Envelope; + const span = new Span( + tracer, + ROOT_CONTEXT, + "test span", + { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, + SpanKind.PRODUCER + ); + span.setAttributes(attributes); + + const baseData = envelope.data?.baseData as RemoteDependencyData; + parseEventHubSpan(span, baseData); + + assert.strictEqual(baseData.type, `Queue Message | ${attributes[AzNamespace]}`); + assert.strictEqual(baseData.target, `${peerAddress}/${destination}`); + + assert.strictEqual((baseData as any).source, undefined); + assert.strictEqual(baseData.measurements, undefined); + }); + + it("should correctly parse SpanKind.CONSUMER", () => { + const startTime = Date.now(); + const envelope = { data: { baseData: {} } } as Envelope; + const span = new Span( + tracer, + ROOT_CONTEXT, + "test span", + { traceId: "traceid", spanId: "spanId", traceFlags: 0 }, + SpanKind.CONSUMER, + undefined, + [ + { + context: (null as unknown) as SpanContext, + attributes: { [ENQUEUED_TIME]: startTime - 111 } + }, + { + context: (null as unknown) as SpanContext, + attributes: { [ENQUEUED_TIME]: startTime - 222 } + }, + { + context: (null as unknown) as SpanContext, + attributes: { [ENQUEUED_TIME]: startTime - 111 } + } + ] + ); + + // cast since startTime is readonly + (span as { startTime: HrTime }).startTime = timeInputToHrTime(startTime); + span.setAttributes(attributes); + + const baseData = envelope.data?.baseData as RemoteDependencyData; + parseEventHubSpan(span, baseData); + assert.strictEqual(baseData.type, `Queue Message | ${attributes[AzNamespace]}`); + assert.strictEqual((baseData as any).source, `${peerAddress}/${destination}`); + assert.deepStrictEqual(baseData.measurements, { + [TIME_SINCE_ENQUEUED]: 148 + }); + + assert.strictEqual(baseData.target, undefined); + }); +}); diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts index 4ba5aa94563d..575c0689e314 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts @@ -92,7 +92,7 @@ describe("spanUtils.ts", () => { span.setAttributes({ "extra.attribute": "foo", [SemanticAttributes.RPC_GRPC_STATUS_CODE]: 123, - [SemanticAttributes.RPC_METHOD]: "/foo.Example/Foo" + [SemanticAttributes.RPC_SYSTEM]: "test rpc system" }); span.setStatus({ code: SpanStatusCode.OK @@ -141,7 +141,6 @@ describe("spanUtils.ts", () => { span.setAttributes({ "extra.attribute": "foo", [SemanticAttributes.RPC_GRPC_STATUS_CODE]: 123, - [SemanticAttributes.RPC_METHOD]: "/foo.Example/Foo", [SemanticAttributes.RPC_SYSTEM]: "test rpc system" }); span.setStatus({ @@ -162,7 +161,7 @@ describe("spanUtils.ts", () => { success: true, resultCode: "123", target: "test rpc system", - type: "Dependency", + type: "GRPC", name: `parent span`, version: 2, properties: expectedProperties, From 50158a0ffece5848ddbbb38ee1de5dcd532bd3f4 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 3 Sep 2021 11:38:36 -0700 Subject: [PATCH 10/15] Format --- .../src/utils/constants/applicationinsights.ts | 2 +- .../src/utils/spanUtils.ts | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts index 2dafb6e8ed1e..b11a464b99da 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/constants/applicationinsights.ts @@ -27,5 +27,5 @@ export enum DependencyTypes { QueueMessage = "Queue Message", Sql = "SQL", Http = "Http", - Grpc = "GRPC", + Grpc = "GRPC" } diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts index 8d2c1d4886d1..c54565cbf972 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts @@ -205,15 +205,12 @@ function createDependencyData(span: ReadableSpan): RemoteDependencyData { if (res != null) { let protocol = res[1]; let port = res[3]; - if ((protocol == "https" && port == ":443") || - (protocol == "http" && port == ":80")) { + if ((protocol == "https" && port == ":443") || (protocol == "http" && port == ":80")) { // Drop port target = res[1] + res[2] + res[4]; } } - - } - catch (error) { } + } catch (error) {} remoteDependencyData.target = `${target}`; } } @@ -311,7 +308,7 @@ export function readableSpanToEnvelope(span: ReadableSpan, ikey: string): Envelo if (span.attributes[AzNamespace] === MicrosoftEventHub) { parseEventHubSpan(span, baseData); } else if (span.attributes[AzNamespace] && span.kind === SpanKind.INTERNAL) { - baseData.type = `${DependencyTypes.InProc} | ${span.attributes[AzNamespace]}` + baseData.type = `${DependencyTypes.InProc} | ${span.attributes[AzNamespace]}`; } return { From 5903a58cbf206c03ff43660ef4797deab313e796 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 3 Sep 2021 13:03:23 -0700 Subject: [PATCH 11/15] Addressing comments --- .../src/utils/spanUtils.ts | 29 ++++++++++++++++--- .../test/internal/spanUtils.test.ts | 12 ++++---- .../test/utils/assert.ts | 10 +------ 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts index c54565cbf972..50beec269d60 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts @@ -7,7 +7,8 @@ import { hrTimeToMilliseconds } from "@opentelemetry/core"; import { diag, SpanKind, SpanStatusCode, Link } from "@opentelemetry/api"; import { SemanticResourceAttributes, - SemanticAttributes + SemanticAttributes, + DbSystemValues } from "@opentelemetry/semantic-conventions"; import { Tags, Properties, MSLink, Measurements } from "../types"; @@ -115,6 +116,22 @@ function createPropertiesFromSpan(span: ReadableSpan): [Properties, Measurements return [properties, measurements]; } +function isSqlDB(dbSystem: string) { + return ( + dbSystem === DbSystemValues.DB2 || + dbSystem === DbSystemValues.DERBY || + dbSystem === DbSystemValues.MARIADB || + dbSystem === DbSystemValues.MYSQL || + dbSystem === DbSystemValues.MSSQL || + dbSystem === DbSystemValues.ORACLE || + dbSystem === DbSystemValues.POSTGRESQL || + dbSystem === DbSystemValues.SQLITE || + dbSystem === DbSystemValues.OTHER_SQL || + dbSystem === DbSystemValues.HSQLDB || + dbSystem === DbSystemValues.H2 + ); +} + function getUrl(span: ReadableSpan): string { const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; if (httpMethod) { @@ -171,7 +188,7 @@ function getDependencyTarget(span: ReadableSpan): string { function createDependencyData(span: ReadableSpan): RemoteDependencyData { const remoteDependencyData: RemoteDependencyData = { name: span.name, - id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, + id: `${span.spanContext().spanId}`, success: span.status.code != SpanStatusCode.ERROR, resultCode: "0", type: "Dependency", @@ -216,7 +233,11 @@ function createDependencyData(span: ReadableSpan): RemoteDependencyData { } // DB Dependency else if (dbSystem) { - remoteDependencyData.type = String(dbSystem); + if (isSqlDB(String(dbSystem))) { + remoteDependencyData.dependencyTypeName = "SQL"; + } else { + remoteDependencyData.dependencyTypeName = String(dbSystem); + } const dbStatement = span.attributes[SemanticAttributes.DB_STATEMENT]; if (dbStatement) { remoteDependencyData.data = String(dbStatement); @@ -249,7 +270,7 @@ function createDependencyData(span: ReadableSpan): RemoteDependencyData { function createRequestData(span: ReadableSpan): RequestData { const requestData: RequestData = { name: span.name, - id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, + id: `${span.spanContext().spanId}`, success: span.status.code != SpanStatusCode.ERROR, responseCode: "0", duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts index 575c0689e314..a7a39c5fb53a 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts @@ -109,7 +109,7 @@ describe("spanUtils.ts", () => { const expectedBaseData: Partial = { source: undefined, duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), - id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, + id: `${span.spanContext().spanId}`, success: true, responseCode: "123", name: `parent span`, @@ -157,7 +157,7 @@ describe("spanUtils.ts", () => { const expectedBaseData: Partial = { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), - id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, + id: `${span.spanContext().spanId}`, success: true, resultCode: "123", target: "test rpc system", @@ -208,7 +208,7 @@ describe("spanUtils.ts", () => { const expectedBaseData: Partial = { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), - id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, + id: `${span.spanContext().spanId}`, success: true, responseCode: "0", name: `parent span`, @@ -257,7 +257,7 @@ describe("spanUtils.ts", () => { const expectedBaseData: Partial = { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), - id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, + id: `${span.spanContext().spanId}`, success: true, resultCode: "0", type: "Dependency", @@ -312,7 +312,7 @@ describe("spanUtils.ts", () => { const expectedBaseData: RequestData = { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), - id: `|${span.spanContext().traceId}.${span.spanContext().spanId}.`, + id: `${span.spanContext().spanId}`, success: true, responseCode: "200", url: "https://example.com/api/example", @@ -364,7 +364,7 @@ describe("spanUtils.ts", () => { const expectedBaseData: RemoteDependencyData = { duration: msToTimeSpan(hrTimeToMilliseconds(span.duration)), - id: `|traceid.spanId.`, + id: `spanId`, success: true, resultCode: "200", type: "Http", diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts index 4d00064820e7..32fc225aad76 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/utils/assert.ts @@ -38,17 +38,9 @@ export const assertTrace = (actual: Envelope[], expectation: Expectation): void } const operationId = envelope[0].tags![KnownContextTagKeys.AiOperationId]; - const parseId = (id: string): { traceId: string; spanId: string } => { - const parts = id.replace("|", "").split("."); - return { - traceId: parts[0], - spanId: parts[1] - }; - }; - for (const child of expectation.children) { const childEnvelopes = actual.filter((e) => { - const { spanId } = parseId((envelope[0].data!.baseData as RequestData).id); + const spanId = (envelope[0].data!.baseData as RequestData).id; return ( e.tags![KnownContextTagKeys.AiOperationId] === operationId && From 68354ecda6db0ccfee741fef97a0a1c17460983c Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 3 Sep 2021 13:37:40 -0700 Subject: [PATCH 12/15] Addressing comments --- .../src/platform/nodejs/context/context.ts | 2 +- .../src/utils/spanUtils.ts | 29 +++++++++---------- .../test/internal/spanUtils.test.ts | 4 +-- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts index f634e9f90c6a..2010aa26acae 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts @@ -141,7 +141,7 @@ export class Context { this.tags[ KnownContextTagKeys.AiInternalSdkVersion - ] = `node${Context.nodeVersion}:ot${SDK_INFO.VERSION}:ext${Context.sdkVersion}`; + ] = `node${Context.nodeVersion}:otel${SDK_INFO.VERSION}:ext${Context.sdkVersion}`; } } diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts index 50beec269d60..a42ef645aadf 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/utils/spanUtils.ts @@ -54,16 +54,11 @@ function createTagsFromSpan(span: ReadableSpan): Tags { tags[KnownContextTagKeys.AiUserId] = String(endUserId); } } - // HTTP Spans - const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; - if (httpMethod) { - let httpClientIp = null; - let netPeerIp = null; - if (span.attributes) { - httpClientIp = span.attributes[SemanticAttributes.HTTP_CLIENT_IP]; - netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; - } - if (span.kind === SpanKind.SERVER) { + if (span.kind === SpanKind.SERVER) { + const httpMethod = span.attributes[SemanticAttributes.HTTP_METHOD]; + let httpClientIp = span.attributes[SemanticAttributes.HTTP_CLIENT_IP]; + let netPeerIp = span.attributes[SemanticAttributes.NET_PEER_IP]; + if (httpMethod) { tags[KnownContextTagKeys.AiOperationName] = `${httpMethod as string} ${span.name as string}`; if (httpClientIp) { tags[KnownContextTagKeys.AiLocationIp] = String(httpClientIp); @@ -76,13 +71,15 @@ function createTagsFromSpan(span: ReadableSpan): Tags { tags[KnownContextTagKeys.AiLocationIp] = String(netPeerIp); } } - // TODO: Operation Name TBD for non HTTP - const httpUserAgent = span.attributes[SemanticAttributes.HTTP_USER_AGENT]; - if (httpUserAgent) { - // TODO: Not exposed in Swagger, need to update def - tags["ai.user.userAgent"] = String(httpUserAgent); - } } + // TODO: Operation Name and Location IP TBD for non server spans + + const httpUserAgent = span.attributes[SemanticAttributes.HTTP_USER_AGENT]; + if (httpUserAgent) { + // TODO: Not exposed in Swagger, need to update def + tags["ai.user.userAgent"] = String(httpUserAgent); + } + return tags; } diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts index a7a39c5fb53a..b5c6c300c227 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts @@ -200,7 +200,8 @@ describe("spanUtils.ts", () => { const expectedTime = new Date(hrTimeToMilliseconds(span.startTime)); const expectedTags: Tags = { [KnownContextTagKeys.AiOperationId]: "traceid", - [KnownContextTagKeys.AiOperationParentId]: "parentSpanId" + [KnownContextTagKeys.AiOperationParentId]: "parentSpanId", + [KnownContextTagKeys.AiOperationName]: "parent span" }; const expectedProperties = { "extra.attribute": "foo" @@ -357,7 +358,6 @@ describe("spanUtils.ts", () => { const expectedTags: Tags = {}; expectedTags[KnownContextTagKeys.AiOperationId] = span.spanContext().traceId; expectedTags[KnownContextTagKeys.AiOperationParentId] = "parentSpanId"; - expectedTags[KnownContextTagKeys.AiOperationName] = "parent span"; const expectedProperties = { "extra.attribute": "foo" }; From 842c61959d7b79c943096e0058ad35820e5d9d4d Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 3 Sep 2021 14:03:20 -0700 Subject: [PATCH 13/15] rush update --- common/config/rush/pnpm-lock.yaml | 124 +++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 27 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 665a333c5b65..3e2f94f31565 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -1075,6 +1075,14 @@ packages: dev: false resolution: integrity: sha512-EU+U09Mj65zUH0qwPF4PFJiL6Y+PQQE/RRGEHEDGJJzab/mRQDpKOyrzSdb00xvcd/URehIHJqC55cY2Y4jGOA== + /@microsoft/api-extractor-model/7.13.5: + dependencies: + '@microsoft/tsdoc': 0.13.2 + '@microsoft/tsdoc-config': 0.15.2 + '@rushstack/node-core-library': 3.40.0 + dev: false + resolution: + integrity: sha512-il6AebNltYo5hEtqXZw4DMvrwBPn6+F58TxwqmsLY+U+sSJNxaYn2jYksArrjErXVPR3gUgRMqD6zsdIkg+WEQ== /@microsoft/api-extractor-model/7.7.10: dependencies: '@microsoft/tsdoc': 0.12.19 @@ -1099,6 +1107,24 @@ packages: hasBin: true resolution: integrity: sha512-2fD0c8OxZW+e6NTaxbtrdNxXVuX7aqil3+cqig3pKsHymvUuRJVCEAcAJmZrJ/ENqYXNiB265EyqOT6VxbMysw== + /@microsoft/api-extractor/7.18.7: + dependencies: + '@microsoft/api-extractor-model': 7.13.5 + '@microsoft/tsdoc': 0.13.2 + '@microsoft/tsdoc-config': 0.15.2 + '@rushstack/node-core-library': 3.40.0 + '@rushstack/rig-package': 0.3.0 + '@rushstack/ts-command-line': 4.9.0 + colors: 1.2.5 + lodash: 4.17.21 + resolve: 1.17.0 + semver: 7.3.5 + source-map: 0.6.1 + typescript: 4.3.5 + dev: false + hasBin: true + resolution: + integrity: sha512-JhtV8LoyLuIecbgCPyZQg08G1kngIRWpai2UzwNil9mGVGYiDZVeeKx8c2phmlPcogmMDm4oQROxyuiYt5sJiw== /@microsoft/api-extractor/7.7.11: dependencies: '@microsoft/api-extractor-model': 7.7.10 @@ -1435,6 +1461,20 @@ packages: dev: false resolution: integrity: sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg== + /@rushstack/node-core-library/3.40.0: + dependencies: + '@types/node': 10.17.13 + colors: 1.2.5 + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.17.0 + semver: 7.3.5 + timsort: 0.3.0 + z-schema: 3.18.4 + dev: false + resolution: + integrity: sha512-P6uMPI7cqTdawLSPAG5BQrBu1MHlGRPqecp7ruIRgyukIEzkmh0QAnje4jAL/l1r3hw0qe4e+Dz5ZSnukT/Egg== /@rushstack/rig-package/0.2.10: dependencies: resolve: 1.17.0 @@ -1442,6 +1482,13 @@ packages: dev: false resolution: integrity: sha512-WXYerEJEPf8bS3ruqfM57NnwXtA7ehn8VJjLjrjls6eSduE5CRydcob/oBTzlHKsQ7N196XKlqQl9P6qIyYG2A== + /@rushstack/rig-package/0.3.0: + dependencies: + resolve: 1.17.0 + strip-json-comments: 3.1.1 + dev: false + resolution: + integrity: sha512-Lj6noF7Q4BBm1hKiBDw94e6uZvq1xlBwM/d2cBFaPqXeGdV+G6r3qaCWfRiSXK0pcHpGGpV5Tb2MdfhVcO6G/g== /@rushstack/ts-command-line/4.3.13: dependencies: '@types/argparse': 1.0.33 @@ -1459,6 +1506,15 @@ packages: dev: false resolution: integrity: sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA== + /@rushstack/ts-command-line/4.9.0: + dependencies: + '@types/argparse': 1.0.38 + argparse: 1.0.10 + colors: 1.2.5 + string-argv: 0.3.1 + dev: false + resolution: + integrity: sha512-kmT8t+JfnvphISF1C5WwY56RefjwgajhSjs9J4ckvAFXZDXR6F5cvF5/RTh7fGCzIomg8esy2PHO/b52zFoZvA== /@sinonjs/commons/1.8.3: dependencies: type-detect: 4.0.8 @@ -2421,6 +2477,13 @@ packages: dev: false resolution: integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + /buffer/6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + resolution: + integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== /builtin-modules/2.0.0: dev: false engines: @@ -7809,6 +7872,13 @@ packages: hasBin: true resolution: integrity: sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== + /typescript/4.3.5: + dev: false + engines: + node: '>=4.2.0' + hasBin: true + resolution: + integrity: sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== /ua-parser-js/0.7.28: dev: false resolution: @@ -8350,7 +8420,7 @@ packages: dev: false name: '@rush-temp/agrifood-farming' resolution: - integrity: sha512-fI0ZnOU97ijUuuYLyfEmoblNdvsESeIFjLoqXElcFHbHpt3oY29P9TeOXZGkYPZWn0rqdS6ThSrwtm4whLGXhw== + integrity: sha512-cqKpvpFUi1vmXReL/3Kr/NFHD4h94JoyZFOQ1m+wfBVnwVOuv/43PvlE7u3zvnOf4dPqk+EvhWUWYNBrTPx/tw== tarball: file:projects/agrifood-farming.tgz version: 0.0.0 file:projects/ai-anomaly-detector.tgz: @@ -8441,7 +8511,7 @@ packages: dev: false name: '@rush-temp/ai-document-translator' resolution: - integrity: sha512-IzEwMBXTUYe48UJSbAhbemKiVE6/y54KMHUucFE2JkGuqIBa2Ci4J6W4mrFMICX5yhweisFxOxWUF7X4P1wjHw== + integrity: sha512-RTnU8NxdkRVFfwo3U+Xeiq7/nhTYqqiMPhh61x4ff25/2pI9x3II0hGAJsMA1hTYKoItr8EDGrZWrVTFx7FRTA== tarball: file:projects/ai-document-translator.tgz version: 0.0.0 file:projects/ai-form-recognizer.tgz: @@ -8535,7 +8605,7 @@ packages: file:projects/ai-text-analytics.tgz: dependencies: '@azure/core-tracing': 1.0.0-preview.13 - '@microsoft/api-extractor': 7.7.11 + '@microsoft/api-extractor': 7.18.7 '@types/chai': 4.2.21 '@types/chai-as-promised': 7.1.4 '@types/mocha': 7.0.2 @@ -8575,7 +8645,7 @@ packages: dev: false name: '@rush-temp/ai-text-analytics' resolution: - integrity: sha512-W4JVfxmJk3JLpEPpTznb+LQSNckSvz40muA5H7egoMY7gLubinQri5m5jVOYFkO2tBV98gI/8t/Rw8QVC3ip2Q== + integrity: sha512-WdRP5jqx0OKR6I9C5Q3EWkg+lExWNvjRM99PAldPL0XiF8KwKg1weHFRVAoJ11bt5q2M00yNNpxnETdaZeKUUQ== tarball: file:projects/ai-text-analytics.tgz version: 0.0.0 file:projects/app-configuration.tgz: @@ -8959,7 +9029,7 @@ packages: '@types/chai-as-promised': 7.1.4 '@types/mocha': 7.0.2 '@types/node': 12.20.21 - buffer: 5.7.1 + buffer: 6.0.3 chai: 4.3.4 chai-as-promised: 7.1.1_chai@4.3.4 cross-env: 7.0.3 @@ -8999,7 +9069,7 @@ packages: dev: false name: '@rush-temp/attestation' resolution: - integrity: sha512-hwq2gatjuRwwGiomTmGewT2YRMPVoWon2uUoZDZt+waWlYZv82gi6479oDGNumoLF6TBOi+GkrWydbaXQ2E+nw== + integrity: sha512-4AaflJcDOLrs/HUIpu7YNjHpCNeuuSTTAKPbA8ckbO78c4v1icVjuYAeLOlvq6DyNRxnJzrHaNNZa9HfKAktVQ== tarball: file:projects/attestation.tgz version: 0.0.0 file:projects/communication-chat.tgz: @@ -9365,7 +9435,7 @@ packages: dev: false name: '@rush-temp/confidential-ledger' resolution: - integrity: sha512-45GvfLJPR9/2EHGurNlqEjXLpJPSA0TWViAhKkAG3e3jlZxFRIBVhJgmjZ4ges76gQfWjBh0hK7H4F6jnNcEWw== + integrity: sha512-lQYlLhA8jsiSwPsCRn0A+03cVkbIds7xTk4dqHv3dUeXmFoKYxUvPd6hBQfnVSMjrLZj6QMiViKnUS8/b5SUkA== tarball: file:projects/confidential-ledger.tgz version: 0.0.0 file:projects/container-registry.tgz: @@ -9426,7 +9496,7 @@ packages: '@types/node': 12.20.21 '@types/sinon': 9.0.11 '@types/ws': 7.4.7 - buffer: 5.7.1 + buffer: 6.0.3 chai: 4.3.4 cross-env: 7.0.3 debug: 4.3.2 @@ -9462,7 +9532,7 @@ packages: dev: false name: '@rush-temp/core-amqp' resolution: - integrity: sha512-yfQLB9OGJC8Sz7AzXe76gGqcBByxsPmzZhEFkimWNVsfJWv2J+flLOsObeTtD5xv9NsB6zTrZJGGv4T9GEQd0A== + integrity: sha512-bVLkGbbgyzPAg60EVgT/U307L10emkRAuwG5H/U2XJZsrYwnm2wondpbxF5gasIHHKQUkFcGGH8I0UcHJo1nIQ== tarball: file:projects/core-amqp.tgz version: 0.0.0 file:projects/core-asynciterator-polyfill.tgz: @@ -9579,7 +9649,7 @@ packages: dev: false name: '@rush-temp/core-client-lro' resolution: - integrity: sha512-n7OJnwa3mZ4WMtwlX0t8UY0ZAPc877e+UfhsTLa5V+3Re0wkEvKUo1IvMedAgKPLag3MoTrRcxl3c3KXGXxGRA== + integrity: sha512-M4evzMVsUDQjqh47XEQtziCB+jT5OhhmOrmpzyI1yE/2vYMM3Bz0kZPBWGq9KZDcBmkFCNNHhjk4uY8oG/BWMA== tarball: file:projects/core-client-lro.tgz version: 0.0.0 file:projects/core-client-paging.tgz: @@ -9617,7 +9687,7 @@ packages: dev: false name: '@rush-temp/core-client-paging' resolution: - integrity: sha512-bsDMFvEcIAb5YzNyy2nbN/9r6iqMizurHzWN6bT+C4son4pDW7+ij6qMx/FKi9l+HiMhx8Ns6prqJypy8g30IQ== + integrity: sha512-jR9MSynY/6hjmMjyWjs+hFAAwk0VN2zJV0yBg0vH/9dv5ANOkjoAYfk54Vu3V0CHHQ7njmLScWqrFdqcRkqypw== tarball: file:projects/core-client-paging.tgz version: 0.0.0 file:projects/core-client.tgz: @@ -9654,7 +9724,7 @@ packages: dev: false name: '@rush-temp/core-client' resolution: - integrity: sha512-A+GvNgdlwTt5h3ZbVfNlhfUSKtdo96ruE9aQ7Vvf83wsasti6gtLOQFPXPiLMvhvQoKg4eQ3iZU9TtNxW2RY+g== + integrity: sha512-VGDp4uzLU6NL5A31+vMbrE2BUlAtWssXoUiG5q21WDHo/5bdVv5Ksl+r9UzYxzgaEWDeNVr5KxfoBvZo7+xJAw== tarball: file:projects/core-client.tgz version: 0.0.0 file:projects/core-crypto.tgz: @@ -10047,7 +10117,7 @@ packages: dev: false name: '@rush-temp/cosmos' resolution: - integrity: sha512-Nfj3USkLkf7QCn7UuB/PrIEELRhpS9jJ7QWwpo0j6H6/eBa8KCf9368JVYh6I1k7VwFMedUIhEkQPosdjdL0pQ== + integrity: sha512-oKrAd9gDxuz3oVa7STvsN0LPnMpfj3rnS48A/Bax8io2vLyabufw/rIDF3+vc+TjDcN1tqA766S7wgbfzHlXrg== tarball: file:projects/cosmos.tgz version: 0.0.0 file:projects/data-tables.tgz: @@ -10236,7 +10306,7 @@ packages: file:projects/event-hubs.tgz: dependencies: '@azure/core-tracing': 1.0.0-preview.13 - '@microsoft/api-extractor': 7.7.11 + '@microsoft/api-extractor': 7.18.7 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-inject': 4.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.1.0_rollup@1.32.1 @@ -10255,7 +10325,7 @@ packages: '@types/uuid': 8.3.1 '@types/ws': 7.4.7 assert: 1.5.0 - buffer: 5.7.1 + buffer: 6.0.3 chai: 4.3.4 chai-as-promised: 7.1.1_chai@4.3.4 chai-exclude: 2.0.3_chai@4.3.4 @@ -10303,7 +10373,7 @@ packages: dev: false name: '@rush-temp/event-hubs' resolution: - integrity: sha512-xuqq+iie3jgTet8h7aIAqAKOF/fOBYG0zBnAEuH0Dwhj8d6zEwT6KAvawPzhwwy+XDvvj7kdtJqCy+N+PR9kBg== + integrity: sha512-fEaozFyATZzYhts6rZVjOawlHHX3R360pcrt4Zd+mT/69QOoIDEWTlPHL7DRBa5HvBzO/bwCXKz6dU7NUhREDg== tarball: file:projects/event-hubs.tgz version: 0.0.0 file:projects/event-processor-host.tgz: @@ -11177,7 +11247,7 @@ packages: dev: false name: '@rush-temp/monitor-query' resolution: - integrity: sha512-8l2Vg6YWtfMUMyjBH28Djy6Jb2lp7guFk0bHo4sC6Q1+baB5QE4TE8AylUW8ns16V/12BxE3MP4CBSwwsIPvVA== + integrity: sha512-wTWUYsAt7ut9FCaEDjKBqgcZnXvS7jRCafdZOHQCw7BY3cUTrNyRmmWmu/OYyJ7lB91pPYXhVeUzbN0fQq8AZQ== tarball: file:projects/monitor-query.tgz version: 0.0.0 file:projects/perf-ai-form-recognizer.tgz: @@ -11486,7 +11556,7 @@ packages: dev: false name: '@rush-temp/purview-account' resolution: - integrity: sha512-if5JygVMi5EaFVhbOVYimljQS6RFD/AbV1lQe4zIJ2W/gMAzTTkb4zUwX3wUbTDuUE4Mzn6fnasa139O1luSsA== + integrity: sha512-tc6KFxfVCGUIwZjdgRZ80vPbwro2EwMEy4B3MQahsza/FZ4i3AHouoborKtNJg30pkTh1n2l7xOeaQJr/hQ7wg== tarball: file:projects/purview-account.tgz version: 0.0.0 file:projects/purview-catalog.tgz: @@ -11529,7 +11599,7 @@ packages: dev: false name: '@rush-temp/purview-catalog' resolution: - integrity: sha512-A6d6LOn/IF/XQhtWl/O/PrdkXsIDLWDLypn2hTE5tZCIqgRXFXdnvL1JYnxMc4Ov+z5Tj0I6OCwBjRCw50uZ0g== + integrity: sha512-BpZ4lOcV2MTB83zU2HMD+uUDLS9RjPLFiZXEL0RfjELRDVYHW7Mzc8iHm4hh0z0KJ8LgJU6IicmaNvMP/X8LBA== tarball: file:projects/purview-catalog.tgz version: 0.0.0 file:projects/purview-scanning.tgz: @@ -11572,7 +11642,7 @@ packages: dev: false name: '@rush-temp/purview-scanning' resolution: - integrity: sha512-U0BqvzNxYHfqTIpxFfs48K55AOgH9utGrS73zZVPI9dPSc4WywkLsuxQrzOou86FJ5B44Khjp8ae9pZPj672qw== + integrity: sha512-HX4JV2mB44SB0PkGp9I4CQ/Bu0yYtFOkwu5WHNn9sathdM9wxEbX+GXP/1e9Xh4EpGweOSDprabMN+IGzK+4Tw== tarball: file:projects/purview-scanning.tgz version: 0.0.0 file:projects/quantum-jobs.tgz: @@ -11632,7 +11702,7 @@ packages: file:projects/schema-registry-avro.tgz: dependencies: '@azure/core-tracing': 1.0.0-preview.13 - '@microsoft/api-extractor': 7.7.11 + '@microsoft/api-extractor': 7.18.7 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-inject': 4.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.1.0_rollup@1.32.1 @@ -11644,7 +11714,7 @@ packages: '@types/mocha': 7.0.2 '@types/node': 12.20.21 avsc: 5.7.3 - buffer: 5.7.1 + buffer: 6.0.3 chai: 4.3.4 chai-as-promised: 7.1.1_chai@4.3.4 cross-env: 7.0.3 @@ -11681,13 +11751,13 @@ packages: dev: false name: '@rush-temp/schema-registry-avro' resolution: - integrity: sha512-b/4qmNi7UubdfLQaKucMttTKHZbfggm1CTjDo2PLNotEvnOGZqYvC9553kyjxLJ/mnMeUoE4SXhtRCikP/386A== + integrity: sha512-2kk4+GZuXE74COL6SC0UFTKcKNsRm27+D1bCgI2PYwpXAblWQaws9fClYJVVI+3k4vH0AniKnOqtNB10CngUmQ== tarball: file:projects/schema-registry-avro.tgz version: 0.0.0 file:projects/schema-registry.tgz: dependencies: '@azure/core-tracing': 1.0.0-preview.13 - '@microsoft/api-extractor': 7.7.11 + '@microsoft/api-extractor': 7.18.7 '@types/chai': 4.2.21 '@types/chai-as-promised': 7.1.4 '@types/mocha': 7.0.2 @@ -11723,7 +11793,7 @@ packages: dev: false name: '@rush-temp/schema-registry' resolution: - integrity: sha512-Wa0ZNJ563uHSOi4xiKpFUhWOZc8yXbeXvHm4xvunwe48Ow2+1Yahgc1TOVjFh8mJt3NdmnD6ufgneunTviNNoA== + integrity: sha512-CFUPH7NJ1Zd+6QO+Ag+gQzuW9SZhLYxOelzOdL7fYSaDytiO3UdbyveTnhIMNjrXulfHtG+dSh8RHu/4RfP/GA== tarball: file:projects/schema-registry.tgz version: 0.0.0 file:projects/search-documents.tgz: @@ -11801,7 +11871,7 @@ packages: '@types/sinon': 9.0.11 '@types/ws': 7.4.7 assert: 1.5.0 - buffer: 5.7.1 + buffer: 6.0.3 chai: 4.3.4 chai-as-promised: 7.1.1_chai@4.3.4 chai-exclude: 2.0.3_chai@4.3.4 @@ -11852,7 +11922,7 @@ packages: dev: false name: '@rush-temp/service-bus' resolution: - integrity: sha512-P1Z6R/jpmdcQ7sFHv+TjYwLNqJpHE1hDwQSdR9RHCwUKa7sRTXn9uuN37kRp6Lh0xcEPxr4kQyPM9cvax3Hz2g== + integrity: sha512-Rb/TXPhQdXj5r7qaoeW4cgF9/1xiTBgzExBE0f8VZzn9APG+aGs0a54djPmFECMsH6HNCgFZgILyE0Vj4MVLyQ== tarball: file:projects/service-bus.tgz version: 0.0.0 file:projects/storage-blob-changefeed.tgz: From ab1471d09398909e7b06b1eeb726f78a5e6cb198 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 3 Sep 2021 14:58:08 -0700 Subject: [PATCH 14/15] Do not throw when version cannot be loaded --- .../src/platform/nodejs/context/context.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts index 2010aa26acae..e569cb9aa33a 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts @@ -130,7 +130,6 @@ export class Context { packageJson = JSON.parse(fs.readFileSync(packageJsonPathTsNode, "utf8")) as PackageJson; } catch (exception) { diag.warn("Failed to load Exporter version", exception); - throw exception; } } From 601cf76416b5a8e32fa4aa29427607f26d93023f Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 3 Sep 2021 15:28:00 -0700 Subject: [PATCH 15/15] Fix test --- .../src/platform/nodejs/context/context.ts | 1 + .../test/internal/spanUtils.test.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts b/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts index e569cb9aa33a..2010aa26acae 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/src/platform/nodejs/context/context.ts @@ -130,6 +130,7 @@ export class Context { packageJson = JSON.parse(fs.readFileSync(packageJsonPathTsNode, "utf8")) as PackageJson; } catch (exception) { diag.warn("Failed to load Exporter version", exception); + throw exception; } } diff --git a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts index b5c6c300c227..24b6c6240c8b 100644 --- a/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts +++ b/sdk/monitor/monitor-opentelemetry-exporter/test/internal/spanUtils.test.ts @@ -100,7 +100,8 @@ describe("spanUtils.ts", () => { span.end(); const expectedTags: Tags = { [KnownContextTagKeys.AiOperationId]: "traceid", - [KnownContextTagKeys.AiOperationParentId]: "parentSpanId" + [KnownContextTagKeys.AiOperationParentId]: "parentSpanId", + [KnownContextTagKeys.AiOperationName]: "parent span" }; const expectedProperties = { "extra.attribute": "foo"