Skip to content

Commit 75098c5

Browse files
committed
Copy in files exactly from GoogleCloudPlatform/opentelemetry-operations-js
1 parent fb1aa1e commit 75098c5

File tree

10 files changed

+1186
-0
lines changed

10 files changed

+1186
-0
lines changed
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {
16+
CLOUDPLATFORMVALUES_GCP_APP_ENGINE,
17+
CLOUDPLATFORMVALUES_GCP_CLOUD_FUNCTIONS,
18+
CLOUDPLATFORMVALUES_GCP_CLOUD_RUN,
19+
CLOUDPLATFORMVALUES_GCP_COMPUTE_ENGINE,
20+
CLOUDPLATFORMVALUES_GCP_KUBERNETES_ENGINE,
21+
CLOUDPROVIDERVALUES_GCP,
22+
SEMRESATTRS_CLOUD_ACCOUNT_ID,
23+
SEMRESATTRS_CLOUD_AVAILABILITY_ZONE,
24+
SEMRESATTRS_CLOUD_PLATFORM,
25+
SEMRESATTRS_CLOUD_PROVIDER,
26+
SEMRESATTRS_CLOUD_REGION,
27+
SEMRESATTRS_FAAS_INSTANCE,
28+
SEMRESATTRS_FAAS_NAME,
29+
SEMRESATTRS_FAAS_VERSION,
30+
SEMRESATTRS_HOST_ID,
31+
SEMRESATTRS_HOST_NAME,
32+
SEMRESATTRS_HOST_TYPE,
33+
SEMRESATTRS_K8S_CLUSTER_NAME,
34+
} from '@opentelemetry/semantic-conventions';
35+
36+
import {AttributeValue, Attributes} from '@opentelemetry/api';
37+
import {
38+
DetectedResource,
39+
DetectedResourceAttributes,
40+
emptyResource,
41+
Resource,
42+
ResourceDetector,
43+
resourceFromAttributes,
44+
} from '@opentelemetry/resources';
45+
import * as metadata from 'gcp-metadata';
46+
import * as faas from './faas';
47+
import * as gae from './gae';
48+
import * as gce from './gce';
49+
import * as gke from './gke';
50+
51+
const ATTRIBUTE_NAMES = [
52+
SEMRESATTRS_CLOUD_PLATFORM,
53+
SEMRESATTRS_CLOUD_AVAILABILITY_ZONE,
54+
SEMRESATTRS_CLOUD_REGION,
55+
SEMRESATTRS_K8S_CLUSTER_NAME,
56+
SEMRESATTRS_HOST_TYPE,
57+
SEMRESATTRS_HOST_ID,
58+
SEMRESATTRS_HOST_NAME,
59+
SEMRESATTRS_CLOUD_PROVIDER,
60+
SEMRESATTRS_CLOUD_ACCOUNT_ID,
61+
SEMRESATTRS_FAAS_NAME,
62+
SEMRESATTRS_FAAS_VERSION,
63+
SEMRESATTRS_FAAS_INSTANCE,
64+
] as const;
65+
66+
// Ensure that all resource keys are accounted for in ATTRIBUTE_NAMES
67+
type GcpResourceAttributeName = (typeof ATTRIBUTE_NAMES)[number];
68+
type GcpResourceAttributes = Partial<
69+
Record<GcpResourceAttributeName, AttributeValue>
70+
>;
71+
72+
async function detect(): Promise<Resource> {
73+
if (!(await metadata.isAvailable())) {
74+
return emptyResource();
75+
}
76+
77+
// Note the order of these if checks is significant with more specific resources coming
78+
// first. E.g. Cloud Functions gen2 are executed in Cloud Run so it must be checked first.
79+
if (await gke.onGke()) {
80+
return await gkeResource();
81+
} else if (await faas.onCloudFunctions()) {
82+
return await cloudFunctionsResource();
83+
} else if (await faas.onCloudRun()) {
84+
return await cloudRunResource();
85+
} else if (await gae.onAppEngine()) {
86+
return await gaeResource();
87+
} else if (await gce.onGce()) {
88+
return await gceResource();
89+
}
90+
91+
return emptyResource();
92+
}
93+
94+
async function gkeResource(): Promise<Resource> {
95+
const [zoneOrRegion, k8sClusterName, hostId] = await Promise.all([
96+
gke.availabilityZoneOrRegion(),
97+
gke.clusterName(),
98+
gke.hostId(),
99+
]);
100+
101+
return await makeResource({
102+
[SEMRESATTRS_CLOUD_PLATFORM]: CLOUDPLATFORMVALUES_GCP_KUBERNETES_ENGINE,
103+
[zoneOrRegion.type === 'zone'
104+
? SEMRESATTRS_CLOUD_AVAILABILITY_ZONE
105+
: SEMRESATTRS_CLOUD_REGION]: zoneOrRegion.value,
106+
[SEMRESATTRS_K8S_CLUSTER_NAME]: k8sClusterName,
107+
[SEMRESATTRS_HOST_ID]: hostId,
108+
});
109+
}
110+
111+
async function cloudRunResource(): Promise<Resource> {
112+
const [faasName, faasVersion, faasInstance, faasCloudRegion] =
113+
await Promise.all([
114+
faas.faasName(),
115+
faas.faasVersion(),
116+
faas.faasInstance(),
117+
faas.faasCloudRegion(),
118+
]);
119+
120+
return await makeResource({
121+
[SEMRESATTRS_CLOUD_PLATFORM]: CLOUDPLATFORMVALUES_GCP_CLOUD_RUN,
122+
[SEMRESATTRS_FAAS_NAME]: faasName,
123+
[SEMRESATTRS_FAAS_VERSION]: faasVersion,
124+
[SEMRESATTRS_FAAS_INSTANCE]: faasInstance,
125+
[SEMRESATTRS_CLOUD_REGION]: faasCloudRegion,
126+
});
127+
}
128+
129+
async function cloudFunctionsResource(): Promise<Resource> {
130+
const [faasName, faasVersion, faasInstance, faasCloudRegion] =
131+
await Promise.all([
132+
faas.faasName(),
133+
faas.faasVersion(),
134+
faas.faasInstance(),
135+
faas.faasCloudRegion(),
136+
]);
137+
138+
return await makeResource({
139+
[SEMRESATTRS_CLOUD_PLATFORM]: CLOUDPLATFORMVALUES_GCP_CLOUD_FUNCTIONS,
140+
[SEMRESATTRS_FAAS_NAME]: faasName,
141+
[SEMRESATTRS_FAAS_VERSION]: faasVersion,
142+
[SEMRESATTRS_FAAS_INSTANCE]: faasInstance,
143+
[SEMRESATTRS_CLOUD_REGION]: faasCloudRegion,
144+
});
145+
}
146+
147+
async function gaeResource(): Promise<Resource> {
148+
let zone, region;
149+
if (await gae.onAppEngineStandard()) {
150+
[zone, region] = await Promise.all([
151+
gae.standardAvailabilityZone(),
152+
gae.standardCloudRegion(),
153+
]);
154+
} else {
155+
({zone, region} = await gce.availabilityZoneAndRegion());
156+
}
157+
const [faasName, faasVersion, faasInstance] = await Promise.all([
158+
gae.serviceName(),
159+
gae.serviceVersion(),
160+
gae.serviceInstance(),
161+
]);
162+
163+
return await makeResource({
164+
[SEMRESATTRS_CLOUD_PLATFORM]: CLOUDPLATFORMVALUES_GCP_APP_ENGINE,
165+
[SEMRESATTRS_FAAS_NAME]: faasName,
166+
[SEMRESATTRS_FAAS_VERSION]: faasVersion,
167+
[SEMRESATTRS_FAAS_INSTANCE]: faasInstance,
168+
[SEMRESATTRS_CLOUD_AVAILABILITY_ZONE]: zone,
169+
[SEMRESATTRS_CLOUD_REGION]: region,
170+
});
171+
}
172+
173+
async function gceResource(): Promise<Resource> {
174+
const [zoneAndRegion, hostType, hostId, hostName] = await Promise.all([
175+
gce.availabilityZoneAndRegion(),
176+
gce.hostType(),
177+
gce.hostId(),
178+
gce.hostName(),
179+
]);
180+
181+
return await makeResource({
182+
[SEMRESATTRS_CLOUD_PLATFORM]: CLOUDPLATFORMVALUES_GCP_COMPUTE_ENGINE,
183+
[SEMRESATTRS_CLOUD_AVAILABILITY_ZONE]: zoneAndRegion.zone,
184+
[SEMRESATTRS_CLOUD_REGION]: zoneAndRegion.region,
185+
[SEMRESATTRS_HOST_TYPE]: hostType,
186+
[SEMRESATTRS_HOST_ID]: hostId,
187+
[SEMRESATTRS_HOST_NAME]: hostName,
188+
});
189+
}
190+
191+
async function makeResource(attrs: GcpResourceAttributes): Promise<Resource> {
192+
const project = await metadata.project<string>('project-id');
193+
194+
return resourceFromAttributes({
195+
[SEMRESATTRS_CLOUD_PROVIDER]: CLOUDPROVIDERVALUES_GCP,
196+
[SEMRESATTRS_CLOUD_ACCOUNT_ID]: project,
197+
...attrs,
198+
} satisfies GcpResourceAttributes);
199+
}
200+
201+
/**
202+
* Google Cloud resource detector which populates attributes based on the environment this
203+
* process is running in. If not on GCP, returns an empty resource.
204+
*/
205+
export class GcpDetectorSync implements ResourceDetector {
206+
private async _asyncAttributes(): Promise<Attributes> {
207+
return (await detect()).attributes;
208+
}
209+
210+
detect(): DetectedResource {
211+
const asyncAttributes = this._asyncAttributes();
212+
const attributes = {} as DetectedResourceAttributes;
213+
ATTRIBUTE_NAMES.forEach(name => {
214+
// Each resource attribute is determined asynchronously in _gatherData().
215+
attributes[name] = asyncAttributes.then(data => data[name]);
216+
});
217+
218+
return {attributes};
219+
}
220+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/**
16+
* Implementation in this file copied from
17+
* https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/blob/v1.8.0/detectors/gcp/faas.go
18+
*/
19+
20+
import * as metadata from 'gcp-metadata';
21+
22+
const ID_METADATA_ATTR = 'id';
23+
const CLOUD_RUN_CONFIG_ENV = 'K_CONFIGURATION';
24+
const CLOUD_FUNCTION_TARGET_ENV = 'FUNCTION_TARGET';
25+
const FAAS_SERVICE_ENV = 'K_SERVICE';
26+
const FAAS_REVISION_ENV = 'K_REVISION';
27+
const REGION_METADATA_ATTR = 'region';
28+
29+
export async function onCloudRun(): Promise<boolean> {
30+
return process.env[CLOUD_RUN_CONFIG_ENV] !== undefined;
31+
}
32+
33+
export async function onCloudFunctions(): Promise<boolean> {
34+
return process.env[CLOUD_FUNCTION_TARGET_ENV] !== undefined;
35+
}
36+
37+
/**
38+
* The name of the Cloud Run or Cloud Function. Check that {@link onCloudRun()} or {@link
39+
* onCloudFunctions()} is true before calling this, or it may throw exceptions.
40+
*/
41+
export async function faasName(): Promise<string> {
42+
return lookupEnv(FAAS_SERVICE_ENV);
43+
}
44+
45+
/**
46+
* The version/revision of the Cloud Run or Cloud Function. Check that {@link onCloudRun()} or
47+
* {@link onCloudFunctions()} is true before calling this, or it may throw exceptions.
48+
*/
49+
export async function faasVersion(): Promise<string> {
50+
return lookupEnv(FAAS_REVISION_ENV);
51+
}
52+
53+
/**
54+
* The ID for the running instance of a Cloud Run or Cloud Function. Check that {@link
55+
* onCloudRun()} or {@link onCloudFunctions()} is true before calling this, or it may throw
56+
* exceptions.
57+
*/
58+
export async function faasInstance(): Promise<string> {
59+
// May be a bignumber.js BigNumber which can just be converted with toString(). See
60+
// https://github.com/googleapis/gcp-metadata#take-care-with-large-number-valued-properties
61+
const id = await metadata.instance<number | Object>(ID_METADATA_ATTR);
62+
return id.toString();
63+
}
64+
65+
/**
66+
* The cloud region where the running instance of a Cloud Run or Cloud Function is located.
67+
* Check that {@link onCloudRun()} or {@link onCloudFunctions()} is true before calling this,
68+
* or it may throw exceptions.
69+
*/
70+
export async function faasCloudRegion(): Promise<string> {
71+
const region = await metadata.instance<string>(REGION_METADATA_ATTR);
72+
return region.slice(region.lastIndexOf('/') + 1);
73+
}
74+
75+
function lookupEnv(key: string): string {
76+
const val = process.env[key];
77+
if (val === undefined) {
78+
throw new Error(`Environment variable ${key} not found`);
79+
}
80+
return val;
81+
}

0 commit comments

Comments
 (0)