Skip to content

[WIP] entities prototype #5620

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/opentelemetry-resources/src/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { Attributes } from '@opentelemetry/api';
import { RawResourceAttribute } from './types';
import { Entity } from './entity';

/**
* An interface that represents a resource. A Resource describes the entity for which signals (metrics or trace) are
Expand All @@ -41,6 +42,11 @@ export interface Resource {
*/
readonly attributes: Attributes;

/**
* @returns a list of Entities associated with the resource
*/
readonly entities: Entity[];

/**
* Returns a promise that will never be rejected. Resolves when all async attributes have finished being added to
* this Resource's attributes. This is useful in exporters to block until resource detection
Expand Down
2 changes: 1 addition & 1 deletion packages/opentelemetry-resources/src/detect-resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { diag } from '@opentelemetry/api';
import { Resource } from './Resource';
import { emptyResource, resourceFromDetectedResource } from './ResourceImpl';
import { emptyResource, resourceFromDetectedResource } from './resource-impl';
import { ResourceDetectionConfig } from './config';

/**
Expand Down
182 changes: 182 additions & 0 deletions packages/opentelemetry-resources/src/entity-impl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
* 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 { Attributes, AttributeValue, diag } from '@opentelemetry/api';
import { DetectedEntity, DetectedResourceAttributeValue } from './types';
import { identity, isPromiseLike } from './utils';
import { Entity } from './entity';

export class EntityImpl implements Entity {
private _type: string;
private _schema_url?: string;
private _identifier: Attributes;
private _asyncAttributesPending = false;

Check warning on line 26 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L26

Added line #L26 was not covered by tests
private _rawAttributes: [string, DetectedResourceAttributeValue][];
private _memoizedAttributes?: Attributes;

constructor(entity: DetectedEntity) {
this._type = entity.type;
this._schema_url = entity.schema_url;
this._identifier = entity.identifier;

Check warning on line 33 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L30-L33

Added lines #L30 - L33 were not covered by tests

if (entity.attributes) {
this._rawAttributes = Object.entries(entity.attributes).map(([k, v]) => {
if (isPromiseLike(v)) {

Check warning on line 37 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L35-L37

Added lines #L35 - L37 were not covered by tests
// side-effect
this._asyncAttributesPending = true;

Check warning on line 39 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L39

Added line #L39 was not covered by tests

return [

Check warning on line 41 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L41

Added line #L41 was not covered by tests
k,
v.then(identity, err => {
diag.debug(

Check warning on line 44 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L43-L44

Added lines #L43 - L44 were not covered by tests
"a resource's async attributes promise rejected: %s",
err
);
return [k, undefined];

Check warning on line 48 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L48

Added line #L48 was not covered by tests
}),
];
}

return [k, v];

Check warning on line 53 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L53

Added line #L53 was not covered by tests
});
} else {
this._rawAttributes = [];

Check warning on line 56 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L55-L56

Added lines #L55 - L56 were not covered by tests
}
}

get type() {
return this._type;

Check warning on line 61 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L60-L61

Added lines #L60 - L61 were not covered by tests
}

get schema_url() {
return this._schema_url;

Check warning on line 65 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L64-L65

Added lines #L64 - L65 were not covered by tests
}

get identifier() {
return this._identifier;

Check warning on line 69 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L68-L69

Added lines #L68 - L69 were not covered by tests
}

get asyncAttributesPending() {
return this._asyncAttributesPending;

Check warning on line 73 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L72-L73

Added lines #L72 - L73 were not covered by tests
}

public async waitForAsyncAttributes(): Promise<void> {
if (!this._asyncAttributesPending) {
return;

Check warning on line 78 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L76-L78

Added lines #L76 - L78 were not covered by tests
}
this._rawAttributes = await Promise.all(

Check warning on line 80 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L80

Added line #L80 was not covered by tests
this._rawAttributes.map<Promise<[string, AttributeValue | undefined]>>(
async ([k, v]) => [k, await v]

Check warning on line 82 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L82

Added line #L82 was not covered by tests
)
);
this._asyncAttributesPending = false;

Check warning on line 85 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L85

Added line #L85 was not covered by tests
}

public get attributes(): Attributes {
if (this.asyncAttributesPending) {
diag.error(

Check warning on line 90 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L88-L90

Added lines #L88 - L90 were not covered by tests
'Accessing resource attributes before async attributes settled'
);
}

if (this._memoizedAttributes) {
return this._memoizedAttributes;

Check warning on line 96 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L95-L96

Added lines #L95 - L96 were not covered by tests
}

const attrs: Attributes = {};
for (const [k, v] of this._rawAttributes) {
if (isPromiseLike(v)) {
diag.debug(`Unsettled resource attribute ${k} skipped`);
continue;

Check warning on line 103 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L99-L103

Added lines #L99 - L103 were not covered by tests
}
attrs[k] ??= v;

Check warning on line 105 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L105

Added line #L105 was not covered by tests
}

// only memoize output if all attributes are settled
if (!this._asyncAttributesPending) {
this._memoizedAttributes = attrs;

Check warning on line 110 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L109-L110

Added lines #L109 - L110 were not covered by tests
}

return attrs;

Check warning on line 113 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L113

Added line #L113 was not covered by tests
}
}

/**
* Merge detected entities. Entities are assumed to be in priority order (highest first).
*/
export function mergeEntities(...entities: Entity[]): Entity[] {
// Construct a set of detected entities, E
const entityMap: Record<string, EntityImpl> = {};

// For each entity detector D, detect entities (already done)

// For each entity detected, d'
for (const entity of entities) {
// If an entity e' exists in E with same entity type as d', do one of the following:
const prevEntity = entityMap[entity.type];
if (prevEntity != null) {

Check warning on line 130 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L129-L130

Added lines #L129 - L130 were not covered by tests
// If the entity identity is different: drop the new entity d'.
if (!attrsEqual(prevEntity.identifier, entity.identifier)) {
continue;

Check warning on line 133 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L132-L133

Added lines #L132 - L133 were not covered by tests
}

// If the entity identity is the same, but schema_url is different: drop the new entity d' Note: We could offer configuration in this case
if (entity.schema_url !== prevEntity.schema_url) {
continue;

Check warning on line 138 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L137-L138

Added lines #L137 - L138 were not covered by tests
}

// If the entity identiy and schema_url are the same, merge the descriptive attributes of d' into e':
// For each descriptive attribute da' in d'
for (const [k, v] of Object.entries(entity.attributes)) {

Check warning on line 143 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L143

Added line #L143 was not covered by tests
// If da'.key does not exist in e', then add da' to ei
if (prevEntity.attributes[k] != null) {
prevEntity.attributes[k] = v;

Check warning on line 146 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L145-L146

Added lines #L145 - L146 were not covered by tests
}

// otherwise, ignore
}
}
}

return [...Object.values(entityMap)];
}

function attrsEqual(obj1: Attributes, obj2: Attributes) {
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
return false;

Check warning on line 159 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L157-L159

Added lines #L157 - L159 were not covered by tests
}

for (const [k, v] of Object.entries(obj1)) {
const v2 = obj2[k];

Check warning on line 163 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L162-L163

Added lines #L162 - L163 were not covered by tests

if (Array.isArray(v)) {
if (!Array.isArray(v2) || v.length !== v2.length) {
return false;

Check warning on line 167 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L165-L167

Added lines #L165 - L167 were not covered by tests
}

// arrays can only contain primitives, so simple equality checks are sufficient
for (let i = 0; i < v.length; i++) {
if (v[i] !== v2[i]) {
return false;

Check warning on line 173 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L171-L173

Added lines #L171 - L173 were not covered by tests
}
}
} else if (v !== v2) {
return false;

Check warning on line 177 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L176-L177

Added lines #L176 - L177 were not covered by tests
}
}

return true;

Check warning on line 181 in packages/opentelemetry-resources/src/entity-impl.ts

View check run for this annotation

Codecov / codecov/patch

packages/opentelemetry-resources/src/entity-impl.ts#L181

Added line #L181 was not covered by tests
}
10 changes: 10 additions & 0 deletions packages/opentelemetry-resources/src/entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Attributes } from "@opentelemetry/api";

export interface Entity {
type: string;
identifier: Attributes;
attributes: Attributes;
schema_url?: string;
asyncAttributesPending: boolean;
waitForAsyncAttributes(): Promise<void>;
}
2 changes: 1 addition & 1 deletion packages/opentelemetry-resources/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export {
resourceFromAttributes,
defaultResource,
emptyResource,
} from './ResourceImpl';
} from './resource-impl';
export { defaultServiceName } from './platform';
export {
ResourceDetector,
Expand Down
Loading
Loading