diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a54cca532..6389562883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :rocket: (Enhancement) * feat(sdk-metrics): add exponential histogram accumulation / aggregator [#3505](https://github.com/open-telemetry/opentelemetry-js/pull/3505) @mwear +* feat(resources): collect additional process attributes [#3605](https://github.com/open-telemetry/opentelemetry-js/pull/3605) @mwear ### :bug: (Bug Fix) diff --git a/packages/opentelemetry-resources/src/detectors/ProcessDetectorSync.ts b/packages/opentelemetry-resources/src/detectors/ProcessDetectorSync.ts deleted file mode 100644 index d63b856e3e..0000000000 --- a/packages/opentelemetry-resources/src/detectors/ProcessDetectorSync.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 { diag } from '@opentelemetry/api'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { Resource } from '../Resource'; -import { DetectorSync, ResourceAttributes } from '../types'; -import { ResourceDetectionConfig } from '../config'; -import { IResource } from '../IResource'; - -/** - * ProcessDetectorSync will be used to detect the resources related current process running - * and being instrumented from the NodeJS Process module. - */ -class ProcessDetectorSync implements DetectorSync { - detect(config?: ResourceDetectionConfig): IResource { - // Skip if not in Node.js environment. - if (typeof process !== 'object') { - return Resource.empty(); - } - const processResource: ResourceAttributes = { - [SemanticResourceAttributes.PROCESS_PID]: process.pid, - [SemanticResourceAttributes.PROCESS_EXECUTABLE_NAME]: process.title || '', - [SemanticResourceAttributes.PROCESS_COMMAND]: process.argv[1] || '', - [SemanticResourceAttributes.PROCESS_COMMAND_LINE]: - process.argv.join(' ') || '', - [SemanticResourceAttributes.PROCESS_RUNTIME_VERSION]: - process.versions.node, - [SemanticResourceAttributes.PROCESS_RUNTIME_NAME]: 'nodejs', - [SemanticResourceAttributes.PROCESS_RUNTIME_DESCRIPTION]: 'Node.js', - }; - return this._getResourceAttributes(processResource, config); - } - /** - * Validates process resource attribute map from process variables - * - * @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?: ResourceDetectionConfig - ) { - if ( - processResource[SemanticResourceAttributes.PROCESS_EXECUTABLE_NAME] === - '' || - processResource[SemanticResourceAttributes.PROCESS_EXECUTABLE_PATH] === - '' || - processResource[SemanticResourceAttributes.PROCESS_COMMAND] === '' || - processResource[SemanticResourceAttributes.PROCESS_COMMAND_LINE] === '' || - processResource[SemanticResourceAttributes.PROCESS_RUNTIME_VERSION] === '' - ) { - diag.debug( - 'ProcessDetector failed: Unable to find required process resources. ' - ); - return Resource.empty(); - } else { - return new Resource({ - ...processResource, - }); - } - } -} - -export const processDetectorSync = new ProcessDetectorSync(); diff --git a/packages/opentelemetry-resources/src/detectors/index.ts b/packages/opentelemetry-resources/src/detectors/index.ts index 5ed2b3f868..e7d885be6b 100644 --- a/packages/opentelemetry-resources/src/detectors/index.ts +++ b/packages/opentelemetry-resources/src/detectors/index.ts @@ -16,7 +16,5 @@ export * from './BrowserDetector'; export * from './EnvDetector'; -export * from './ProcessDetector'; export * from './BrowserDetectorSync'; export * from './EnvDetectorSync'; -export * from './ProcessDetectorSync'; diff --git a/packages/opentelemetry-resources/src/platform/browser/ProcessDetector.ts b/packages/opentelemetry-resources/src/platform/browser/ProcessDetector.ts new file mode 100644 index 0000000000..3d45d1c4fe --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/browser/ProcessDetector.ts @@ -0,0 +1,19 @@ +/* + * 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 { noopDetector } from '../../detectors/NoopDetector'; + +export const processDetector = noopDetector; diff --git a/packages/opentelemetry-resources/src/platform/browser/ProcessDetectorSync.ts b/packages/opentelemetry-resources/src/platform/browser/ProcessDetectorSync.ts new file mode 100644 index 0000000000..6651d30990 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/browser/ProcessDetectorSync.ts @@ -0,0 +1,19 @@ +/* + * 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 { noopDetector } from '../../detectors/NoopDetector'; + +export const processDetectorSync = noopDetector; diff --git a/packages/opentelemetry-resources/src/platform/browser/index.ts b/packages/opentelemetry-resources/src/platform/browser/index.ts index b18be97c9c..9ed1b0151b 100644 --- a/packages/opentelemetry-resources/src/platform/browser/index.ts +++ b/packages/opentelemetry-resources/src/platform/browser/index.ts @@ -19,3 +19,5 @@ export * from './HostDetector'; export * from './OSDetector'; export * from './HostDetectorSync'; export * from './OSDetectorSync'; +export * from './ProcessDetector'; +export * from './ProcessDetectorSync'; diff --git a/packages/opentelemetry-resources/src/detectors/ProcessDetector.ts b/packages/opentelemetry-resources/src/platform/node/ProcessDetector.ts similarity index 88% rename from packages/opentelemetry-resources/src/detectors/ProcessDetector.ts rename to packages/opentelemetry-resources/src/platform/node/ProcessDetector.ts index b1165f2c5b..20345e6af9 100644 --- a/packages/opentelemetry-resources/src/detectors/ProcessDetector.ts +++ b/packages/opentelemetry-resources/src/platform/node/ProcessDetector.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { Detector } from '../types'; -import { ResourceDetectionConfig } from '../config'; -import { IResource } from '../IResource'; +import { Detector } from '../../types'; +import { ResourceDetectionConfig } from '../../config'; +import { IResource } from '../../IResource'; import { processDetectorSync } from './ProcessDetectorSync'; /** diff --git a/packages/opentelemetry-resources/src/platform/node/ProcessDetectorSync.ts b/packages/opentelemetry-resources/src/platform/node/ProcessDetectorSync.ts new file mode 100644 index 0000000000..4cad06a458 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/ProcessDetectorSync.ts @@ -0,0 +1,61 @@ +/* + * 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 { diag } from '@opentelemetry/api'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { Resource } from '../../Resource'; +import { DetectorSync, ResourceAttributes } from '../../types'; +import { ResourceDetectionConfig } from '../../config'; +import { IResource } from '../../IResource'; +import * as os from 'os'; + +/** + * ProcessDetectorSync will be used to detect the resources related current process running + * and being instrumented from the NodeJS Process module. + */ +class ProcessDetectorSync implements DetectorSync { + detect(_config?: ResourceDetectionConfig): IResource { + const attributes: ResourceAttributes = { + [SemanticResourceAttributes.PROCESS_PID]: process.pid, + [SemanticResourceAttributes.PROCESS_EXECUTABLE_NAME]: process.title, + [SemanticResourceAttributes.PROCESS_EXECUTABLE_PATH]: process.execPath, + [SemanticResourceAttributes.PROCESS_COMMAND_ARGS]: [ + process.argv[0], + ...process.execArgv, + ...process.argv.slice(1), + ], + [SemanticResourceAttributes.PROCESS_RUNTIME_VERSION]: + process.versions.node, + [SemanticResourceAttributes.PROCESS_RUNTIME_NAME]: 'nodejs', + [SemanticResourceAttributes.PROCESS_RUNTIME_DESCRIPTION]: 'Node.js', + }; + + if (process.argv.length > 1) { + attributes[SemanticResourceAttributes.PROCESS_COMMAND] = process.argv[1]; + } + + try { + const userInfo = os.userInfo(); + attributes[SemanticResourceAttributes.PROCESS_OWNER] = userInfo.username; + } catch (e) { + diag.debug(`error obtaining process owner: ${e}`); + } + + return new Resource(attributes); + } +} + +export const processDetectorSync = new ProcessDetectorSync(); diff --git a/packages/opentelemetry-resources/src/platform/node/index.ts b/packages/opentelemetry-resources/src/platform/node/index.ts index b18be97c9c..9ed1b0151b 100644 --- a/packages/opentelemetry-resources/src/platform/node/index.ts +++ b/packages/opentelemetry-resources/src/platform/node/index.ts @@ -19,3 +19,5 @@ export * from './HostDetector'; export * from './OSDetector'; export * from './HostDetectorSync'; export * from './OSDetectorSync'; +export * from './ProcessDetector'; +export * from './ProcessDetectorSync'; diff --git a/packages/opentelemetry-resources/test/detectors/node/ProcessDetector.test.ts b/packages/opentelemetry-resources/test/detectors/node/ProcessDetector.test.ts index 6f5793b6ae..96fafbe44a 100644 --- a/packages/opentelemetry-resources/test/detectors/node/ProcessDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/node/ProcessDetector.test.ts @@ -15,11 +15,9 @@ */ import * as sinon from 'sinon'; import { processDetector, IResource } from '../../../src'; -import { - assertResource, - assertEmptyResource, -} from '../../util/resource-assertions'; +import { assertResource } from '../../util/resource-assertions'; import { describeNode } from '../../util'; +import * as os from 'os'; describeNode('processDetector() on Node.js', () => { afterEach(() => { @@ -27,29 +25,49 @@ describeNode('processDetector() on Node.js', () => { }); it('should return resource information from process', async () => { + const argv = ['/tmp/node', '/home/ot/test.js', 'arg1', 'arg2']; sinon.stub(process, 'pid').value(1234); sinon.stub(process, 'title').value('otProcess'); - sinon - .stub(process, 'argv') - .value(['/tmp/node', '/home/ot/test.js', 'arg1', 'arg2']); + sinon.stub(process, 'argv').value(argv); + sinon.stub(process, 'execPath').value(argv[0]); + sinon.stub(process, 'execArgv').value(['--trace-warnings']); sinon.stub(process, 'versions').value({ node: '1.4.1' }); + sinon + .stub(os, 'userInfo') + .returns({ username: 'appOwner' } as os.UserInfo); const resource: IResource = await processDetector.detect(); assertResource(resource, { pid: 1234, name: 'otProcess', command: '/home/ot/test.js', - commandLine: '/tmp/node /home/ot/test.js arg1 arg2', + commandArgs: [ + '/tmp/node', + '--trace-warnings', + '/home/ot/test.js', + 'arg1', + 'arg2', + ], + executablePath: argv[0], + owner: 'appOwner', version: '1.4.1', runtimeDescription: 'Node.js', runtimeName: 'nodejs', }); }); - it('should return empty resources if title, command and commondLine is missing', async () => { + + it('should return a resources if title, command and commandLine are missing', async () => { sinon.stub(process, 'pid').value(1234); - sinon.stub(process, 'title').value(undefined); + sinon.stub(process, 'title').value(''); sinon.stub(process, 'argv').value([]); + sinon.stub(process, 'versions').value({ node: '1.4.1' }); const resource: IResource = await processDetector.detect(); - assertEmptyResource(resource); + // at a minium we should be able to rely on pid runtime, runtime name, and description + assertResource(resource, { + pid: 1234, + version: '1.4.1', + runtimeDescription: 'Node.js', + runtimeName: 'nodejs', + }); }); }); diff --git a/packages/opentelemetry-resources/test/util/resource-assertions.ts b/packages/opentelemetry-resources/test/util/resource-assertions.ts index fdf541305e..02f1a70dd4 100644 --- a/packages/opentelemetry-resources/test/util/resource-assertions.ts +++ b/packages/opentelemetry-resources/test/util/resource-assertions.ts @@ -269,7 +269,9 @@ export const assertResource = ( pid?: number; name?: string; command?: string; - commandLine?: string; + commandArgs?: string[]; + executablePath?: string; + owner?: string; version?: string; runtimeName?: string; runtimeDescription?: string; @@ -291,10 +293,22 @@ export const assertResource = ( validations.command ); } - if (validations.commandLine) { + if (validations.commandArgs) { + assert.deepStrictEqual( + resource.attributes[SemanticResourceAttributes.PROCESS_COMMAND_ARGS], + validations.commandArgs + ); + } + if (validations.executablePath) { + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.PROCESS_EXECUTABLE_PATH], + validations.executablePath + ); + } + if (validations.owner) { assert.strictEqual( - resource.attributes[SemanticResourceAttributes.PROCESS_COMMAND_LINE], - validations.commandLine + resource.attributes[SemanticResourceAttributes.PROCESS_OWNER], + validations.owner ); } if (validations.version) {