Skip to content

Commit

Permalink
feat(opentelemetry-resources): add runtime version information (#2727)
Browse files Browse the repository at this point in the history
  • Loading branch information
cuichenli authored Mar 4, 2022
1 parent ef03d02 commit a901732
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 12 deletions.
65 changes: 65 additions & 0 deletions packages/opentelemetry-resources/src/detectors/BrowserDetector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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 { Detector, Resource, ResourceDetectionConfig } from '..';
import { ResourceAttributes } from '../types';

/**
* BrowserDetector will be used to detect the resources related to browser.
*/
class BrowserDetector implements Detector {
async detect(config?: ResourceDetectionConfig): Promise<Resource> {
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
if (!isBrowser) {
return Resource.empty();
}
const browserResource: ResourceAttributes = {
[SemanticResourceAttributes.PROCESS_RUNTIME_NAME]: 'browser',
[SemanticResourceAttributes.PROCESS_RUNTIME_DESCRIPTION]: 'Web Browser',
[SemanticResourceAttributes.PROCESS_RUNTIME_VERSION]: window.navigator.userAgent
};
return this._getResourceAttributes(browserResource, config);
}
/**
* Validates process resource attribute map from process variables
*
* @param browserResource The un-sanitized resource attributes from process as key/value pairs.
* @param config: Config
* @returns The sanitized resource attributes.
*/
private _getResourceAttributes(
browserResource: ResourceAttributes,
_config?: ResourceDetectionConfig
) {
if (
browserResource[SemanticResourceAttributes.PROCESS_RUNTIME_VERSION] === ''
) {
diag.debug(
'BrowserDetector failed: Unable to find required browser resources. '
);
return Resource.empty();
} else {
return new Resource({
...browserResource,
});
}
}
}


export const browserDetector = new BrowserDetector();
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class ProcessDetector implements Detector {
[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);
}
Expand All @@ -56,7 +59,8 @@ class ProcessDetector implements Detector {
processResource[SemanticResourceAttributes.PROCESS_EXECUTABLE_PATH] ===
'' ||
processResource[SemanticResourceAttributes.PROCESS_COMMAND] === '' ||
processResource[SemanticResourceAttributes.PROCESS_COMMAND_LINE] === ''
processResource[SemanticResourceAttributes.PROCESS_COMMAND_LINE] === '' ||
processResource[SemanticResourceAttributes.PROCESS_RUNTIME_VERSION] === ''
) {
diag.debug(
'ProcessDetector failed: Unable to find required process resources. '
Expand Down
1 change: 1 addition & 0 deletions packages/opentelemetry-resources/src/detectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@

export * from './EnvDetector';
export * from './ProcessDetector';
export * from './BrowserDetector';
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,35 @@
*/

import { Resource } from '../../Resource';
import { ResourceDetectionConfig } from '../../config';
import { diag } from '@opentelemetry/api';

/**
* Detects resources for the browser platform, which is currently only the
* telemetry SDK resource. More could be added in the future. This method
* is async to match the signature of corresponding method for node.
* Runs all resource detectors and returns the results merged into a single
* Resource.
*
* @param config Configuration for resource detection
*/
export const detectResources = async (): Promise<Resource> => {
return Resource.empty();
export const detectResources = async (
config: ResourceDetectionConfig = {}
): Promise<Resource> => {
const internalConfig: ResourceDetectionConfig = Object.assign(config);

const resources: Resource[] = await Promise.all(
(internalConfig.detectors || []).map(async d => {
try {
const resource = await d.detect(internalConfig);
diag.debug(`${d.constructor.name} found resource.`, resource);
return resource;
} catch (e) {
diag.debug(`${d.constructor.name} failed: ${e.message}`);
return Resource.empty();
}
})
);

return resources.reduce(
(acc, resource) => acc.merge(resource),
Resource.empty()
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const detectResources = async (
): Promise<Resource> => {
const internalConfig: ResourceDetectionConfig = Object.assign(config);

const resources: Array<Resource> = await Promise.all(
const resources: Resource[] = await Promise.all(
(internalConfig.detectors || []).map(async d => {
try {
const resource = await d.detect(internalConfig);
Expand All @@ -52,10 +52,11 @@ export const detectResources = async (
);
};


/**
* Writes debug information about the detected resources to the logger defined in the resource detection config, if one is provided.
*
* @param resources The array of {@link Resource} that should be logged. Empty entried will be ignored.
* @param resources The array of {@link Resource} that should be logged. Empty entries will be ignored.
*/
const logResources = (resources: Array<Resource>) => {
resources.forEach(resource => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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 * as sinon from 'sinon';
import { Resource } from '../../../src';
import { browserDetector } from '../../../src/detectors/BrowserDetector';
import { describeBrowser } from '../../util';
import {
assertResource,
assertEmptyResource,
} from '../../util/resource-assertions';


describeBrowser('browserDetector()', () => {

afterEach(() => {
sinon.restore();
});

it('should return browser information', async () => {
sinon.stub(window, 'navigator').value({
userAgent: 'dddd',
});

const resource: Resource = await browserDetector.detect();
assertResource(resource, {
version: 'dddd',
runtimeDescription: 'Web Browser',
runtimeName: 'browser',
});
});
it('should return empty resources if version is missing', async () => {
sinon.stub(window, 'navigator').value({
userAgent: '',
});
const resource: Resource = await browserDetector.detect();
assertEmptyResource(resource);
});
});

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 { Resource } from '../../../src';
import { browserDetector } from '../../../src/detectors/BrowserDetector';
import { describeNode } from '../../util';
import {
assertEmptyResource,
} from '../../util/resource-assertions';


describeNode('browserDetector()', () => {
it('should return empty resources if window.document is missing', async () => {
const resource: Resource = await browserDetector.detect();
assertEmptyResource(resource);
});
});

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import * as sinon from 'sinon';
import { processDetector, Resource } from '../../../src';
import {
assertProcessResource,
assertResource,
assertEmptyResource,
} from '../../util/resource-assertions';
import { describeNode } from '../../util';
Expand All @@ -32,13 +32,19 @@ describeNode('processDetector() on Node.js', () => {
sinon
.stub(process, 'argv')
.value(['/tmp/node', '/home/ot/test.js', 'arg1', 'arg2']);
sinon
.stub(process, 'versions')
.value({'node': '1.4.1'});

const resource: Resource = await processDetector.detect();
assertProcessResource(resource, {
assertResource(resource, {
pid: 1234,
name: 'otProcess',
command: '/home/ot/test.js',
commandLine: '/tmp/node /home/ot/test.js arg1 arg2',
version: '1.4.1',
runtimeDescription: 'Node.js',
runtimeName: 'nodejs',
});
});
it('should return empty resources if title, command and commondLine is missing', async () => {
Expand Down
25 changes: 23 additions & 2 deletions packages/opentelemetry-resources/test/util/resource-assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,18 +258,21 @@ export const assertServiceResource = (
};

/**
* Test utility method to validate a process resources
* Test utility method to validate a process / browser resources
*
* @param resource the Resource to validate
* @param validations validations for the resource attributes
*/
export const assertProcessResource = (
export const assertResource = (
resource: Resource,
validations: {
pid?: number;
name?: string;
command?: string;
commandLine?: string;
version?: string;
runtimeName?: string;
runtimeDescription?: string;
}
) => {
assert.strictEqual(
Expand All @@ -294,6 +297,24 @@ export const assertProcessResource = (
validations.commandLine
);
}
if (validations.version) {
assert.strictEqual(
resource.attributes[SemanticResourceAttributes.PROCESS_RUNTIME_VERSION],
validations.version
);
}
if (validations.runtimeName) {
assert.strictEqual(
resource.attributes[SemanticResourceAttributes.PROCESS_RUNTIME_NAME],
validations.runtimeName
);
}
if (validations.runtimeDescription) {
assert.strictEqual(
resource.attributes[SemanticResourceAttributes.PROCESS_RUNTIME_DESCRIPTION],
validations.runtimeDescription
);
}
};

export const assertWebEngineResource = (resource: Resource, validations: {
Expand Down

0 comments on commit a901732

Please sign in to comment.