From beff0e0d246f75891d23dc3ba1e0278687d83e01 Mon Sep 17 00:00:00 2001 From: Mihir Soni Date: Sun, 13 Sep 2020 23:20:39 -0700 Subject: [PATCH] feat: process resource detector --- .../opentelemetry-resources/src/constants.ts | 21 ++++++ .../node/detectors/ProcessDetector.ts | 73 ++++++++++++++++++ .../src/platform/node/detectors/index.ts | 1 + .../test/detectors/ProcessDetector.test.ts | 75 +++++++++++++++++++ .../test/util/resource-assertions.ts | 40 ++++++++++ packages/opentelemetry-sdk-node/src/sdk.ts | 3 +- 6 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 packages/opentelemetry-resources/src/platform/node/detectors/ProcessDetector.ts create mode 100644 packages/opentelemetry-resources/test/detectors/ProcessDetector.test.ts diff --git a/packages/opentelemetry-resources/src/constants.ts b/packages/opentelemetry-resources/src/constants.ts index d60b604522..e736a6a397 100644 --- a/packages/opentelemetry-resources/src/constants.ts +++ b/packages/opentelemetry-resources/src/constants.ts @@ -116,3 +116,24 @@ export const SERVICE_RESOURCE = { /** The version string of the service API or implementation. */ VERSION: 'service.version', }; + +/** Attributes describing a Process service. */ +export const PROCESS_RESOURCE = { + /** A command which launced this proces. */ + COMMAND: 'process.command', + + /** The full command with arguments as string. */ + COMMAND_LINE: 'process.command_line', + + /** A name given to currently running porcess defaults to executable else set as process.title . */ + NAME: 'process.executable.name', + + /** An owner of currently running process. */ + OWNER: 'process.owner', + + /** The full path to the process executable. */ + PATH: 'process.executable.path', + + /** Process identifier of currently running process. */ + PID: 'process.id', +}; diff --git a/packages/opentelemetry-resources/src/platform/node/detectors/ProcessDetector.ts b/packages/opentelemetry-resources/src/platform/node/detectors/ProcessDetector.ts new file mode 100644 index 0000000000..ba8ee52fd3 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/detectors/ProcessDetector.ts @@ -0,0 +1,73 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Detector, + Resource, + PROCESS_RESOURCE, + ResourceDetectionConfigWithLogger, +} from '../../../'; +import { ResourceAttributes } from '../../../types'; + +/** + * ProcessDetector will be used to detect the resoureces related current process running + * and being instumented from the NodeJS Process module. + */ +class ProcessDetector implements Detector { + async detect(config: ResourceDetectionConfigWithLogger): Promise { + const processResource: ResourceAttributes = { + [PROCESS_RESOURCE.PID]: process.pid || '', + [PROCESS_RESOURCE.NAME]: process.title || '', + [PROCESS_RESOURCE.COMMAND]: process.argv[1] || '', + [PROCESS_RESOURCE.COMMAND_LINE]: process.argv.join(' ') || '', + }; + return this._getResourceAttributes(processResource, config); + } + /** + * Validates process resource attribute map from process varaibls + * + * @param processResource The unsantized resource attributes from process as key/value pairs. + * @param config: Config + * @returns The sanitized resource attributes. + */ + private _getResourceAttributes( + processResource: ResourceAttributes, + config: ResourceDetectionConfigWithLogger + ) { + if (processResource[PROCESS_RESOURCE.PID] === '') { + config.logger.debug( + 'ProcessDetector failed: unable to locate pid for currently running process' + ); + return Resource.empty(); + } else if ( + processResource[PROCESS_RESOURCE.NAME] === '' || + processResource[PROCESS_RESOURCE.PATH] === '' || + processResource[PROCESS_RESOURCE.COMMAND] === '' || + processResource[PROCESS_RESOURCE.COMMAND_LINE] === '' + ) { + config.logger.debug( + 'ProcessDetector failed: Unable to find required process resources. ' + ); + return Resource.empty(); + } else { + return new Resource({ + ...processResource, + }); + } + } +} + +export const processDetector = new ProcessDetector(); diff --git a/packages/opentelemetry-resources/src/platform/node/detectors/index.ts b/packages/opentelemetry-resources/src/platform/node/detectors/index.ts index f2b4223be1..d88f7412d3 100644 --- a/packages/opentelemetry-resources/src/platform/node/detectors/index.ts +++ b/packages/opentelemetry-resources/src/platform/node/detectors/index.ts @@ -15,3 +15,4 @@ */ export * from './EnvDetector'; +export * from './ProcessDetector'; diff --git a/packages/opentelemetry-resources/test/detectors/ProcessDetector.test.ts b/packages/opentelemetry-resources/test/detectors/ProcessDetector.test.ts new file mode 100644 index 0000000000..ab235bba3f --- /dev/null +++ b/packages/opentelemetry-resources/test/detectors/ProcessDetector.test.ts @@ -0,0 +1,75 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { processDetector, Resource } from '../../src'; +import { + assertProcessResource, + assertEmptyResource, +} from '../util/resource-assertions'; +import { NoopLogger } from '@opentelemetry/core'; + +describe('processDetector()', () => { + it('should return resource information from process', async () => { + Object.defineProperty(process, 'pid', { + value: 1234, + }); + Object.defineProperty(process, 'title', { + value: 'otProcess', + }); + Object.defineProperty(process, 'argv', { + value: ['/tmp/node', '/home/ot/test.js', 'arg1', 'arg2'], + }); + const resource: Resource = await processDetector.detect({ + logger: new NoopLogger(), + }); + assertProcessResource(resource, { + pid: 1234, + name: 'otProcess', + command: '/home/ot/test.js', + commandLine: '/tmp/node /home/ot/test.js arg1 arg2', + }); + }); + it('should return empty resources if title, command and commondLine is missing', async () => { + Object.defineProperty(process, 'pid', { + value: 1234, + }); + Object.defineProperty(process, 'title', { + value: undefined, + }); + Object.defineProperty(process, 'argv', { + value: [], + }); + const resource: Resource = await processDetector.detect({ + logger: new NoopLogger(), + }); + assertEmptyResource(resource); + }); + it('should return empty resources if pid is missing', async () => { + Object.defineProperty(process, 'pid', { + value: undefined, + }); + Object.defineProperty(process, 'title', { + value: 'otProcess', + }); + Object.defineProperty(process, 'argv', { + value: ['/tmp/node', '/home/ot/test.js', 'arg1', 'arg2'], + }); + const resource: Resource = await processDetector.detect({ + logger: new NoopLogger(), + }); + assertEmptyResource(resource); + }); +}); diff --git a/packages/opentelemetry-resources/test/util/resource-assertions.ts b/packages/opentelemetry-resources/test/util/resource-assertions.ts index 422415537c..3ebbb0286d 100644 --- a/packages/opentelemetry-resources/test/util/resource-assertions.ts +++ b/packages/opentelemetry-resources/test/util/resource-assertions.ts @@ -24,6 +24,7 @@ import { K8S_RESOURCE, TELEMETRY_SDK_RESOURCE, SERVICE_RESOURCE, + PROCESS_RESOURCE, } from '../../src/constants'; /** @@ -260,6 +261,45 @@ export const assertServiceResource = ( ); }; +/** + * Test utility method to validate an process resource + * + * @param resource the Resource to validate + * @param validations validations for the resource attributes + */ +export const assertProcessResource = ( + resource: Resource, + validations: { + pid?: number; + name?: string; + command?: string; + commandLine?: string; + } +) => { + assert.strictEqual( + resource.attributes[PROCESS_RESOURCE.PID], + validations.pid + ); + if (validations.name) { + assert.strictEqual( + resource.attributes[PROCESS_RESOURCE.NAME], + validations.name + ); + } + if (validations.command) { + assert.strictEqual( + resource.attributes[PROCESS_RESOURCE.COMMAND], + validations.command + ); + } + if (validations.commandLine) { + assert.strictEqual( + resource.attributes[PROCESS_RESOURCE.COMMAND_LINE], + validations.commandLine + ); + } +}; + /** * Test utility method to validate an empty resource * diff --git a/packages/opentelemetry-sdk-node/src/sdk.ts b/packages/opentelemetry-sdk-node/src/sdk.ts index a411ab85df..0cc6f714d5 100644 --- a/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/packages/opentelemetry-sdk-node/src/sdk.ts @@ -23,6 +23,7 @@ import { Resource, ResourceDetectionConfig, envDetector, + processDetector, } from '@opentelemetry/resources'; import { BatchSpanProcessor, SpanProcessor } from '@opentelemetry/tracing'; import { NodeSDKConfiguration } from './types'; @@ -128,7 +129,7 @@ export class NodeSDK { /** Detect resource attributes */ public async detectResources(config?: ResourceDetectionConfig) { const internalConfig: ResourceDetectionConfig = { - detectors: [awsEc2Detector, gcpDetector, envDetector], + detectors: [awsEc2Detector, gcpDetector, envDetector, processDetector], ...config, };