diff --git a/docs/advanced-config.md b/docs/advanced-config.md index 3dc3a782..5a21d3e5 100644 --- a/docs/advanced-config.md +++ b/docs/advanced-config.md @@ -55,7 +55,7 @@ This distribution supports all the configuration options supported by the compon | --------------------------------------------------------------- | ----------------------- | ------- | --- | `OTEL_ATTRIBUTE_COUNT_LIMIT` | | Stable | Maximum allowed span attribute count | `OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT` | `12000`\* | Stable | Maximum allowed attribute value size -| `OTEL_EXPORTER_OTLP_ENDPOINT`
`endpoint` | `http://localhost:4317` | Stable | The OTLP endpoint to export to. +| `OTEL_EXPORTER_OTLP_ENDPOINT`
`endpoint` | `http://localhost:4318` | Stable | The OTLP endpoint to export to. | `OTEL_LOG_LEVEL` | | Stable | Log level to use in diagnostics logging. **Does not set the logger.** | `OTEL_PROPAGATORS`
`tracing.propagators` | `tracecontext,baggage` | Stable | Comma-delimited list of propagators to use. Valid keys: `baggage`, `tracecontext`, `b3multi`, `b3`. | `OTEL_RESOURCE_ATTRIBUTES` | | Stable | Comma-separated list of resource attributes added to every reported span.
Example`key1=val1,key2=val2`
@@ -97,10 +97,10 @@ The following config options can be set by passing them as tracing arguments to | Environment variable
``start()`` argument | Default value | Support | Notes | --------------------------------------------------------------- | ----------------------- | ------- | --- | `OTEL_SERVICE_NAME`
`serviceName` | `unnamed-node-service` | Stable | The service name of this Node service. -| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`
`endpoint` | `http://localhost:4317` | Stable | The OTLP endpoint to export to. +| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`
`endpoint` | `http://localhost:4318` | Stable | The OTLP endpoint to export to. | `OTEL_METRIC_EXPORT_INTERVAL`
`metrics.exportIntervalMillis` | `30000` | Stable | The interval, in milliseconds, of metrics collection and exporting. | `OTEL_METRICS_EXPORTER`
`metrics.metricReaderFactory` | `otlp` | Stable | Chooses the metric exporters. Comma-delimited list of exporters. Currently supported values: `otlp`, `console`, `none`. -| `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL`
`metrics.metricReaderFactory` | `grpc` | Stable | Chooses the metric exporter protocol. Currently supported values: `grpc`, `http/protobuf`. +| `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL`
`metrics.metricReaderFactory` | `http/protobuf` | Stable | Chooses the metric exporter protocol. Currently supported values: `grpc`, `http/protobuf`. | `OTEL_RESOURCE_ATTRIBUTES` | | Stable | The resource attributes to metric data.
Environment variable example`key1=val1,key2=val2`
| `SPLUNK_METRICS_ENABLED`
n/a (enabled by calling `start`) | `false` | Experimental | Sets up the metrics pipeline (global meter provider, exporters). | n/a
`metrics.resourceFactory` | | Experimental | Callback which allows to filter the default resource or provide a custom one. The function takes one argument of type `Resource` which is the resource pre-filled by the SDK containing the `service.name`, environment, host and process attributes. | @@ -114,7 +114,7 @@ The following config options can be set by passing them as tracing arguments to | --------------------------------------------------------------- | ----------------------- | ------- | --- | `SPLUNK_PROFILER_ENABLED` | `false` | Experimental | Enable continuous profiling. | `SPLUNK_PROFILER_MEMORY_ENABLED`
`profiling.memoryProfilingEnabled` | `false` | Experimental | Enable continuous memory profiling. -| `SPLUNK_PROFILER_LOGS_ENDPOINT`
`endpoint` | `http://localhost:4317` | Experimental | The OTLP logs receiver endpoint used for profiling data. +| `SPLUNK_PROFILER_LOGS_ENDPOINT`
`endpoint` | `http://localhost:4318` | Experimental | The OTLP logs receiver endpoint used for profiling data. | `OTEL_SERVICE_NAME`
`serviceName` | `unnamed-node-service` | Stable | Service name of the application. | `OTEL_RESOURCE_ATTRIBUTES` | | Stable | Comma-separated list of resource attributes.
Example`deployment.environment=demo,key2=val2`
diff --git a/examples/profiling/README.md b/examples/profiling/README.md index 56e85f44..e9f75116 100644 --- a/examples/profiling/README.md +++ b/examples/profiling/README.md @@ -1,7 +1,7 @@ # Profiling Example This example showcases enabling profiling for Splunk APM. There's no official support for profiling in OTel, so profiling requires working with some Splunk-specific components. -By default, the example requires the OTel Collector to run with the OTLP receiver listening for logs on `localhost:4317`. To export profiling data to APM, you must set up `splunk_hec` exporter in the Collector. See [the example collector config](./collector-config.yml). +By default, the example requires the OTel Collector to run with the OTLP receiver listening for logs on `localhost:4318`. To export profiling data to APM, you must set up `splunk_hec` exporter in the Collector. See [the example collector config](./collector-config.yml). ```shell # Replace <...> with the correct values @@ -17,7 +17,7 @@ Then run the script in a separate terminal: # Optional. To set the environment: export OTEL_SERVICE_NAME='profiling-example' export OTEL_RESOURCE_ATTRIBUTES='deployment.environment=dev' -export OTEL_LOG_LEVEL='DEBUG' +export OTEL_LOG_LEVEL=debug # Run the example: npm start ``` diff --git a/examples/profiling/collector-config.yml b/examples/profiling/collector-config.yml index 96730b77..eabcfd26 100644 --- a/examples/profiling/collector-config.yml +++ b/examples/profiling/collector-config.yml @@ -2,7 +2,9 @@ receivers: otlp: protocols: grpc: - signalfx: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 exporters: otlphttp: @@ -12,22 +14,19 @@ exporters: splunk_hec: token: "${SPLUNK_ACCESS_TOKEN}" endpoint: "https://ingest.${SPLUNK_REALM}.signalfx.com/v1/log" - logging/debug: - loglevel: debug + debug: + verbosity: detailed processors: batch: service: - telemetry: - logs: - level: "debug" pipelines: traces: receivers: [otlp] processors: [batch] - exporters: [logging/debug, otlphttp] + exporters: [debug, otlphttp] logs/profiling: receivers: [otlp] processors: [batch] - exporters: [logging/debug, splunk_hec] + exporters: [debug, splunk_hec] diff --git a/examples/profiling/docker-compose.yml b/examples/profiling/docker-compose.yml index 0abccb7b..63736535 100644 --- a/examples/profiling/docker-compose.yml +++ b/examples/profiling/docker-compose.yml @@ -1,13 +1,11 @@ -version: "3" services: otel-collector: - image: otel/opentelemetry-collector-contrib:0.50.0 + image: quay.io/signalfx/splunk-otel-collector:0.111.0 environment: + - SPLUNK_CONFIG: /etc/otel/config.yml - SPLUNK_ACCESS_TOKEN - SPLUNK_REALM - command: ["--config=/etc/otel-collector-config.yml"] volumes: - - ./collector-config.yml:/etc/otel-collector-config.yml + - ./collector.config.yml:/etc/otel/config.yml ports: - - "9943:9943" # signalfx - - "4317:4317" # otlp + - "4318:4318" # otlp diff --git a/examples/profiling/index.js b/examples/profiling/index.js index 7a723863..705e26d8 100644 --- a/examples/profiling/index.js +++ b/examples/profiling/index.js @@ -1,10 +1,5 @@ const { start, stop } = require('@splunk/otel'); -const { diag, DiagConsoleLogger, DiagLogLevel, trace, context } = require('@opentelemetry/api'); - -// If OTEL_LOG_LEVEL env var is set, configure logger -if (process.env.OTEL_LOG_LEVEL) { - diag.setLogger(new DiagConsoleLogger(), DiagLogLevel[process.env.OTEL_LOG_LEVEL]); -} +const { trace, context } = require('@opentelemetry/api'); start({ // Tracing is enabled by default and is required for profiling @@ -21,7 +16,7 @@ const doWork = () => { // setTimeout has to be here because profiling is currently started asyncronously to avoid blocking the runtime. // If we didn't we'd run stop before the profiling has started in the background. -setTimeout(() => { +setTimeout(async () => { const tracer = trace.getTracer('splunk-otel-example-profiling'); const span = tracer.startSpan('main'); const spanContext = trace.setSpan(context.active(), span); @@ -34,5 +29,5 @@ setTimeout(() => { span.end(); // Stop profiling to flush the collected samples - stop(); + await stop(); }, 10); diff --git a/package-lock.json b/package-lock.json index b9bae26f..fa9cb511 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,12 @@ "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.11.1", - "@grpc/proto-loader": "^0.7.13", "@opentelemetry/api": "^1.8.0", "@opentelemetry/api-logs": "^0.53.0", "@opentelemetry/context-async-hooks": "1.26.0", "@opentelemetry/core": "1.26.0", "@opentelemetry/exporter-logs-otlp-http": "0.53.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.53.0", "@opentelemetry/exporter-metrics-otlp-grpc": "0.53.0", "@opentelemetry/exporter-metrics-otlp-proto": "0.53.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.53.0", @@ -2134,6 +2134,27 @@ "@opentelemetry/api": "^1.0.0" } }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.53.0.tgz", + "integrity": "sha512-jhEcVL1deeWNmTUP05UZMriZPSWUBcfg94ng7JuBb1q2NExgnADQFl1VQQ+xo62/JepK+MxQe4xAwlsDQFbISA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.53.0", + "@opentelemetry/core": "1.26.0", + "@opentelemetry/otlp-exporter-base": "0.53.0", + "@opentelemetry/otlp-transformer": "0.53.0", + "@opentelemetry/resources": "1.26.0", + "@opentelemetry/sdk-logs": "0.53.0", + "@opentelemetry/sdk-trace-base": "1.26.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { "version": "0.53.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.53.0.tgz", @@ -9434,6 +9455,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11219,6 +11241,20 @@ "@opentelemetry/sdk-logs": "0.53.0" } }, + "@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.53.0.tgz", + "integrity": "sha512-jhEcVL1deeWNmTUP05UZMriZPSWUBcfg94ng7JuBb1q2NExgnADQFl1VQQ+xo62/JepK+MxQe4xAwlsDQFbISA==", + "requires": { + "@opentelemetry/api-logs": "0.53.0", + "@opentelemetry/core": "1.26.0", + "@opentelemetry/otlp-exporter-base": "0.53.0", + "@opentelemetry/otlp-transformer": "0.53.0", + "@opentelemetry/resources": "1.26.0", + "@opentelemetry/sdk-logs": "0.53.0", + "@opentelemetry/sdk-trace-base": "1.26.0" + } + }, "@opentelemetry/exporter-metrics-otlp-grpc": { "version": "0.53.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.53.0.tgz", diff --git a/package.json b/package.json index 6d411065..b6275a00 100644 --- a/package.json +++ b/package.json @@ -93,12 +93,12 @@ }, "dependencies": { "@grpc/grpc-js": "^1.11.1", - "@grpc/proto-loader": "^0.7.13", "@opentelemetry/api": "^1.8.0", "@opentelemetry/api-logs": "^0.53.0", "@opentelemetry/context-async-hooks": "1.26.0", "@opentelemetry/core": "1.26.0", "@opentelemetry/exporter-logs-otlp-http": "0.53.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.53.0", "@opentelemetry/exporter-metrics-otlp-grpc": "0.53.0", "@opentelemetry/exporter-metrics-otlp-proto": "0.53.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.53.0", diff --git a/src/metrics/index.ts b/src/metrics/index.ts index 4bbd8d28..f1a81198 100644 --- a/src/metrics/index.ts +++ b/src/metrics/index.ts @@ -158,7 +158,7 @@ export function createOtlpExporter(options: MetricsOptions) { } } - protocol = protocol ?? 'grpc'; + protocol = protocol ?? 'http/protobuf'; switch (protocol) { case 'grpc': { diff --git a/src/profiling/OTLPProfilingExporter.ts b/src/profiling/OTLPProfilingExporter.ts deleted file mode 100644 index 570a6ec9..00000000 --- a/src/profiling/OTLPProfilingExporter.ts +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * 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 - * - * http://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 protoLoader from '@grpc/proto-loader'; -import type * as grpc from '@grpc/grpc-js'; -import * as path from 'path'; -import { - CpuProfile, - HeapProfile, - ProfilingExporter, - ProfilingStacktrace, -} from './types'; -import { diag } from '@opentelemetry/api'; -import { Resource } from '@opentelemetry/resources'; -import { VERSION } from '@opentelemetry/core'; -import { - ATTR_TELEMETRY_SDK_LANGUAGE, - ATTR_TELEMETRY_SDK_VERSION, -} from '@opentelemetry/semantic-conventions'; -import { - parseEndpoint, - serialize, - serializeHeapProfile, - encode, -} from './utils'; - -export interface OTLPExporterOptions { - callstackInterval: number; - endpoint: string; - resource: Resource; -} - -interface LogsClient extends grpc.Client { - export: ( - request: unknown, - metadata: grpc.Metadata, - callback: Function - ) => void; -} - -const OTEL_PROFILING_VERSION = '0.1.0'; - -function commonAttributes( - profilingType: 'cpu' | 'allocation', - sampleCount: number -) { - return [ - { - key: 'profiling.data.format', - value: { stringValue: 'pprof-gzip-base64' }, - }, - { - key: 'profiling.data.type', - value: { stringValue: profilingType }, - }, - { - key: 'com.splunk.sourcetype', - value: { stringValue: 'otel.profiling' }, - }, - { - key: 'profiling.data.total.frame.count', - value: { intValue: sampleCount }, - }, - ]; -} - -function countSamples(stacktraces: ProfilingStacktrace[]) { - let sampleCount = 0; - - for (const profilingStacktrace of stacktraces) { - sampleCount += profilingStacktrace.stacktrace.length; - } - - return sampleCount; -} - -export class OTLPProfilingExporter implements ProfilingExporter { - protected _client: LogsClient; - protected _options: OTLPExporterOptions; - protected _resourceAttributes; - protected _grpc: typeof grpc; - - constructor(options: OTLPExporterOptions) { - this._options = options; - const protosDir = path.resolve(__dirname, '..', '..', 'protos'); - const packageDef = protoLoader.loadSync( - path.join( - protosDir, - 'opentelemetry/proto/collector/logs/v1/logs_service.proto' - ), - { - keepCase: false, - longs: String, - enums: String, - defaults: true, - oneofs: true, - includeDirs: [protosDir], - } - ); - - this._grpc = require('@grpc/grpc-js'); - const { host, credentials } = parseEndpoint(options.endpoint, this._grpc); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const packageObject: any = this._grpc.loadPackageDefinition(packageDef); - this._client = - new packageObject.opentelemetry.proto.collector.logs.v1.LogsService( - host, - credentials - ); - - const resource = new Resource({ - [ATTR_TELEMETRY_SDK_LANGUAGE]: 'node', - [ATTR_TELEMETRY_SDK_VERSION]: VERSION, - }).merge(options.resource); - - this._resourceAttributes = []; - for (const key in resource.attributes) { - const value = resource.attributes[key]; - - if (typeof value === 'string') { - this._resourceAttributes.push({ - key, - value: { stringValue: value }, - }); - } else if (typeof value === 'number') { - if (Number.isInteger(value)) { - this._resourceAttributes.push({ - key, - value: { intValue: value }, - }); - } else { - this._resourceAttributes.push({ - key, - value: { doubleValue: value }, - }); - } - } else { - this._resourceAttributes.push({ - key, - value: { boolValue: value }, - }); - } - } - } - - send(profile: CpuProfile) { - const { stacktraces } = profile; - - const sampleCount = countSamples(stacktraces); - - diag.debug(`profiling: Exporting ${sampleCount} CPU samples`); - const { callstackInterval } = this._options; - const attributes = commonAttributes('cpu', sampleCount); - encode(serialize(profile, { samplingPeriodMillis: callstackInterval })) - .then((serializedProfile) => { - const logs = [serializedProfile].map((st) => { - return { - name: 'otel.profiling', - body: { stringValue: st.toString('base64') }, - attributes, - }; - }); - const ilLogs = { - instrumentationLibrary: { - name: 'otel.profiling', - version: OTEL_PROFILING_VERSION, - }, - logs, - }; - const resourceLogs = [ - { - resource: { - attributes: this._resourceAttributes, - }, - instrumentationLibraryLogs: [ilLogs], - }, - ]; - const payload = { - resourceLogs, - }; - this._client.export( - payload, - new this._grpc.Metadata(), - (err: unknown) => { - if (err) { - diag.error('Error exporting profiling data', err); - } - } - ); - }) - .catch((err: unknown) => { - diag.error('Error exporting profiling data', err); - }); - } - - sendHeapProfile(profile: HeapProfile) { - const serialized = serializeHeapProfile(profile); - const sampleCount = profile.samples.length; - const attributes = commonAttributes('allocation', sampleCount); - diag.debug(`profiling: Exporting ${sampleCount} heap samples`); - encode(serialized) - .then((serializedProfile) => { - const logs = [serializedProfile].map((st) => { - return { - name: 'otel.profiling', - body: { stringValue: st.toString('base64') }, - attributes, - }; - }); - const ilLogs = { - instrumentationLibrary: { - name: 'otel.profiling', - version: OTEL_PROFILING_VERSION, - }, - logs, - }; - const resourceLogs = [ - { - resource: { - attributes: this._resourceAttributes, - }, - instrumentationLibraryLogs: [ilLogs], - }, - ]; - const payload = { - resourceLogs, - }; - this._client.export( - payload, - new this._grpc.Metadata(), - (err: unknown) => { - if (err) { - diag.error('Error exporting profiling data', err); - } - } - ); - }) - .catch((err: unknown) => { - diag.error('Error exporting profiling data', err); - }); - } -} diff --git a/src/profiling/OtlpHttpProfilingExporter.ts b/src/profiling/OtlpHttpProfilingExporter.ts new file mode 100644 index 00000000..b8879ff0 --- /dev/null +++ b/src/profiling/OtlpHttpProfilingExporter.ts @@ -0,0 +1,185 @@ +/* + * Copyright Splunk Inc. + * + * 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 + * + * http://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 { + CpuProfile, + HeapProfile, + ProfilingExporter, + ProfilingStacktrace, +} from './types'; +import { context, diag } from '@opentelemetry/api'; +import { Resource } from '@opentelemetry/resources'; +import { + hrTime, + InstrumentationScope, + suppressTracing, +} from '@opentelemetry/core'; +import type { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto'; +import { VERSION } from '@opentelemetry/core'; +import { + ATTR_TELEMETRY_SDK_LANGUAGE, + ATTR_TELEMETRY_SDK_VERSION, +} from '@opentelemetry/semantic-conventions'; +import { serialize, serializeHeapProfile, encode } from './utils'; +import { ReadableLogRecord } from '@opentelemetry/sdk-logs'; + +export interface ExporterOptions { + callstackInterval: number; + endpoint: string; + resource: Resource; +} + +function countSamples(stacktraces: ProfilingStacktrace[]) { + let sampleCount = 0; + + for (const profilingStacktrace of stacktraces) { + sampleCount += profilingStacktrace.stacktrace.length; + } + + return sampleCount; +} + +function commonAttributes( + profilingType: 'cpu' | 'allocation', + sampleCount: number +) { + return { + 'profiling.data.format': 'pprof-gzip-base64', + 'profiling.data.type': profilingType, + 'com.splunk.sourcetype': 'otel.profiling', + 'profiling.data.total.frame.count': sampleCount, + }; +} + +function createEndpoint(endpoint: string) { + if (endpoint.includes('/v1/logs')) { + return endpoint; + } + + return new URL('/v1/logs', endpoint).href; +} + +const OTEL_PROFILING_VERSION = '0.1.0'; + +export class OtlpHttpProfilingExporter implements ProfilingExporter { + _callstackInterval: number; + _endpoint: string; + _exporter: OTLPLogExporter | undefined; + _resource: Resource; + _scope: InstrumentationScope; + + constructor(options: ExporterOptions) { + this._callstackInterval = options.callstackInterval; + this._endpoint = createEndpoint(options.endpoint); + this._resource = new Resource({ + [ATTR_TELEMETRY_SDK_LANGUAGE]: 'node', + [ATTR_TELEMETRY_SDK_VERSION]: VERSION, + }).merge(options.resource); + + this._scope = { + name: 'otel.profiling', + version: OTEL_PROFILING_VERSION, + }; + } + + async send(profile: CpuProfile) { + const { stacktraces } = profile; + + const sampleCount = countSamples(stacktraces); + + diag.debug(`profiling: Exporting ${sampleCount} CPU samples`); + const attributes = commonAttributes('cpu', sampleCount); + + return encode( + serialize(profile, { samplingPeriodMillis: this._callstackInterval }) + ) + .then((serializedProfile) => { + const ts = hrTime(); + + const logs: ReadableLogRecord[] = [ + { + hrTime: ts, + hrTimeObserved: ts, + body: serializedProfile.toString('base64'), + resource: this._resource, + instrumentationScope: this._scope, + attributes, + droppedAttributesCount: 0, + }, + ]; + + context.with(suppressTracing(context.active()), () => { + this._getExporter().export(logs, (result) => { + if (result.error !== undefined) { + diag.error('Error exporting profiling data', result.error); + } + }); + }); + }) + .catch((err: unknown) => { + diag.error('Error encoding cpu profile', err); + }); + } + + async sendHeapProfile(profile: HeapProfile) { + const serialized = serializeHeapProfile(profile); + const sampleCount = profile.samples.length; + const attributes = commonAttributes('allocation', sampleCount); + diag.debug(`profiling: Exporting ${sampleCount} heap samples`); + return encode(serialized) + .then((serializedProfile) => { + const ts = hrTime(); + + const logs: ReadableLogRecord[] = [ + { + hrTime: ts, + hrTimeObserved: ts, + body: serializedProfile.toString('base64'), + resource: this._resource, + instrumentationScope: this._scope, + attributes, + droppedAttributesCount: 0, + }, + ]; + + context.with(suppressTracing(context.active()), () => { + this._getExporter().export(logs, (result) => { + if (result.error !== undefined) { + diag.error('Error exporting profiling data', result.error); + } + }); + }); + }) + .catch((err: unknown) => { + diag.error('Error encoding heap profile', err); + }); + } + + _getExporter(): OTLPLogExporter { + if (this._exporter !== undefined) { + return this._exporter; + } + + const { + OTLPLogExporter, + } = require('@opentelemetry/exporter-logs-otlp-proto'); + + this._exporter = new OTLPLogExporter({ + url: this._endpoint, + }); + + return this._exporter!; + } +} diff --git a/src/profiling/index.ts b/src/profiling/index.ts index a54b56c4..42bb73bf 100644 --- a/src/profiling/index.ts +++ b/src/profiling/index.ts @@ -27,7 +27,7 @@ import { recordHeapProfilerMetrics, } from '../metrics/debug_metrics'; import { detect as detectResource } from '../resource'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; import { HeapProfile, MemoryProfilingOptions, @@ -38,7 +38,7 @@ import { ProfilingStartOptions, } from './types'; import { ProfilingContextManager } from './ProfilingContextManager'; -import { OTLPProfilingExporter } from './OTLPProfilingExporter'; +import { OtlpHttpProfilingExporter } from './OtlpHttpProfilingExporter'; import { isTracingContextManagerEnabled } from '../tracing'; export { StartProfilingOptions }; @@ -83,7 +83,7 @@ export function defaultExporterFactory( options: ProfilingOptions ): ProfilingExporter[] { const exporters: ProfilingExporter[] = [ - new OTLPProfilingExporter({ + new OtlpHttpProfilingExporter({ endpoint: options.endpoint, callstackInterval: options.callstackInterval, resource: options.resource, @@ -104,7 +104,7 @@ export function startProfiling(options: ProfilingOptions) { if (extension === undefined) { return { - stop: () => {}, + stop: async () => {}, }; } @@ -169,7 +169,7 @@ export function startProfiling(options: ProfilingOptions) { }); return { - stop: () => { + stop: async () => { if (options.memoryProfilingEnabled) { clearInterval(memSamplesCollectInterval); extStopMemoryProfiling(extension); @@ -179,9 +179,17 @@ export function startProfiling(options: ProfilingOptions) { const cpuProfile = extStopProfiling(extension); if (cpuProfile) { - for (const exporter of exporters) { - exporter.send(cpuProfile); - } + const sends = exporters.map((e) => e.send(cpuProfile)); + await Promise.allSettled(sends).then((results) => { + for (const result of results) { + if (result.status === 'rejected') { + diag.error( + 'Failed sending CPU profile on shutdown', + result.reason + ); + } + } + }); } }, }; @@ -208,14 +216,14 @@ export function _setDefaultOptions( options.endpoint || getNonEmptyEnvVar('SPLUNK_PROFILER_LOGS_ENDPOINT') || getNonEmptyEnvVar('OTEL_EXPORTER_OTLP_ENDPOINT') || - 'http://localhost:4317'; + 'http://localhost:4318'; const combinedResource = detectResource(); const serviceName = String( options.serviceName || getNonEmptyEnvVar('OTEL_SERVICE_NAME') || - combinedResource.attributes[SemanticResourceAttributes.SERVICE_NAME] || + combinedResource.attributes[ATTR_SERVICE_NAME] || defaultServiceName() ); @@ -226,7 +234,7 @@ export function _setDefaultOptions( resource = resource.merge( new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: serviceName, + [ATTR_SERVICE_NAME]: serviceName, }) ); diff --git a/src/profiling/types.ts b/src/profiling/types.ts index 5f9fa099..5c3b86d1 100644 --- a/src/profiling/types.ts +++ b/src/profiling/types.ts @@ -105,8 +105,8 @@ export interface ProfilingOptions { export type StartProfilingOptions = Partial; export interface ProfilingExporter { - send(profile: CpuProfile): void; - sendHeapProfile(profile: HeapProfile): void; + send(profile: CpuProfile): Promise; + sendHeapProfile(profile: HeapProfile): Promise; } export const allowedProfilingOptions = [ diff --git a/src/profiling/utils.ts b/src/profiling/utils.ts index 763a3a86..389a9703 100644 --- a/src/profiling/utils.ts +++ b/src/profiling/utils.ts @@ -13,12 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as fs from 'fs'; import { gzip } from 'zlib'; import { promisify } from 'util'; -import type * as GrpcModule from '@grpc/grpc-js'; -import { diag } from '@opentelemetry/api'; -import { getNonEmptyEnvVar } from '../utils'; import { perftools } from './proto/profile'; import type { CpuProfile, HeapProfile } from './types'; @@ -220,48 +216,3 @@ export const encode = async function encode( const buffer = perftools.profiles.Profile.encode(profile).finish(); return gzipPromise(buffer); }; - -function readContentSync(location: string): Buffer | undefined { - try { - return fs.readFileSync(location); - } catch (e) { - diag.error(`Failed to read file at ${location}`, e); - } - - return undefined; -} - -function maybeReadPath(location: string | undefined): Buffer | undefined { - if (location === undefined) { - return undefined; - } - - return readContentSync(location); -} - -export function parseEndpoint( - endpoint: string, - grpc: typeof GrpcModule -): { - host: string; - credentials: GrpcModule.ChannelCredentials; -} { - let host = endpoint; - let credentials = grpc.ChannelCredentials.createInsecure(); - - if (endpoint.startsWith('https://')) { - host = endpoint.substr(8); - credentials = grpc.credentials.createSsl( - maybeReadPath(getNonEmptyEnvVar('OTEL_EXPORTER_OTLP_CERTIFICATE')), - maybeReadPath(getNonEmptyEnvVar('OTEL_EXPORTER_OTLP_CLIENT_KEY')), - maybeReadPath(getNonEmptyEnvVar('OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE')) - ); - } else if (endpoint.startsWith('http://')) { - host = endpoint.substr(7); - } - - return { - host, - credentials, - }; -} diff --git a/src/start.ts b/src/start.ts index feef7521..c8d9bf00 100644 --- a/src/start.ts +++ b/src/start.ts @@ -166,12 +166,7 @@ export const stop = async () => { } if (running.profiling) { - promises.push( - new Promise((resolve) => { - running.profiling!.stop(); - resolve(); - }) - ); + promises.push(promises.push(running.profiling!.stop())); running.profiling = null; } diff --git a/src/tracing/options.ts b/src/tracing/options.ts index ebdd9458..000ad42d 100644 --- a/src/tracing/options.ts +++ b/src/tracing/options.ts @@ -254,12 +254,7 @@ export function otlpSpanExporterFactory(options: Options): SpanExporter { 'OTEL_EXPORTER_OTLP_PROTOCOL', ]); - let endpoint = - options.endpoint ?? - getEnvValueByPrecedence([ - 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', - 'OTEL_EXPORTER_OTLP_ENDPOINT', - ]); + let endpoint = options.endpoint; const accessToken = options.accessToken; @@ -270,7 +265,12 @@ export function otlpSpanExporterFactory(options: Options): SpanExporter { ); } - if (endpoint === undefined) { + const envEndpoint = getEnvValueByPrecedence([ + 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', + 'OTEL_EXPORTER_OTLP_ENDPOINT', + ]); + + if (endpoint === undefined && envEndpoint === undefined) { endpoint = `https://ingest.${options.realm}.signalfx.com/v2/trace/otlp`; protocol = 'http/protobuf'; } else { @@ -280,7 +280,7 @@ export function otlpSpanExporterFactory(options: Options): SpanExporter { } } - protocol = protocol ?? 'grpc'; + protocol = protocol ?? 'http/protobuf'; switch (protocol) { case 'grpc': { @@ -341,7 +341,7 @@ export function defaultSpanProcessorFactory(options: Options): SpanProcessor[] { } // eslint-disable-next-line @typescript-eslint/no-unused-vars -export function defaultPropagatorFactory(options: Options): TextMapPropagator { +export function defaultPropagatorFactory(_options: Options): TextMapPropagator { const envPropagators = getEnvArray('OTEL_PROPAGATORS', [ 'tracecontext', 'baggage', diff --git a/test/examples/Dockerfile_app b/test/examples/Dockerfile_app index 298fb374..9fa8c16e 100644 --- a/test/examples/Dockerfile_app +++ b/test/examples/Dockerfile_app @@ -1,5 +1,5 @@ # Set up the environment, install deps -FROM node:18-alpine as base +FROM node:20-alpine AS base RUN apk add g++ make python3 USER node @@ -21,7 +21,7 @@ COPY --chown=node:node ./ . # Install deps for examples -FROM base as published +FROM base AS published RUN ./scripts/examples-npm-install @@ -31,7 +31,7 @@ CMD ["npm", "run", "start"] # Compile the package and use it in examples -FROM published as compiled +FROM published AS compiled # use a single prebuild for tests so we don't have to compile it each install RUN npm run prebuild:current diff --git a/test/examples/basic/app.env b/test/examples/basic/app.env index 2eb7a682..7c5f5b0e 100644 --- a/test/examples/basic/app.env +++ b/test/examples/basic/app.env @@ -2,4 +2,4 @@ PORT=80 OTEL_SERVICE_NAME='basic-example' OTEL_RESOURCE_ATTRIBUTES='deployment.environment=dev' OTEL_LOG_LEVEL='DEBUG' -OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317 +OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318 diff --git a/test/examples/basic/snapshot.js b/test/examples/basic/snapshot.js index cc9459d7..7fadc910 100644 --- a/test/examples/basic/snapshot.js +++ b/test/examples/basic/snapshot.js @@ -1,50 +1,18 @@ // a console.log from a previous run module.exports = [ { - traceId: '/9GShGd4h/tJ1Yei1rzV8A==', - id: '2PYWumUB8/Q=', - startTime: '2022-10-27T15:28:23.685136896Z', - name: 'GET /hello', - kind: 'server', - parentSpanId: undefined, - parent: undefined, - references: undefined, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-http', - 'otel.library.version': '0.33.0', - 'http.url': 'http://app/hello', - 'http.host': 'app', - 'net.host.name': 'app', - 'http.method': 'GET', - 'http.scheme': 'http', - 'http.target': '/hello', - 'http.user_agent': 'got (https://github.com/sindresorhus/got)', - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'net.host.ip': '::ffff:0.0.0.0', - 'net.host.port': undefined, - 'net.peer.ip': '::ffff:0.0.0.0', - 'net.peer.port': undefined, - 'http.status_code': undefined, - 'http.status_text': 'CREATED', - 'http.route': '/hello', - 'span.kind': 'server' - } - }, - { - traceId: '/9GShGd4h/tJ1Yei1rzV8A==', - id: 'aBuv7mmBta4=', - startTime: '2022-10-27T15:28:23.696763904Z', + traceId: 'LAWqC9bxJ/QJ4eXmjf6wpQ==', + id: '7D4S0IHq48A=', + startTime: '2024-10-23T11:49:39.286Z', + hrStartTime: 1729684179286000000n, name: 'middleware - query', kind: 'internal', - parentSpanId: '2PYWumUB8/Q=', - parent: { id: '2PYWumUB8/Q=', traceId: '/9GShGd4h/tJ1Yei1rzV8A==' }, - references: [], + parentSpanId: 'KUT43ADmiVo=', + parent: { id: 'KUT43ADmiVo=', traceId: 'LAWqC9bxJ/QJ4eXmjf6wpQ==' }, status: { code: undefined }, attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.31.2', + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', 'http.route': '/', 'express.name': 'query', 'express.type': 'middleware', @@ -52,18 +20,18 @@ module.exports = [ } }, { - traceId: '/9GShGd4h/tJ1Yei1rzV8A==', - id: 'ijCrFyvrC5A=', - startTime: '2022-10-27T15:28:23.699226624Z', + traceId: 'LAWqC9bxJ/QJ4eXmjf6wpQ==', + id: 'lcfZ8FvVJJ8=', + startTime: '2024-10-23T11:49:39.291Z', + hrStartTime: 1729684179291000000n, name: 'middleware - expressInit', kind: 'internal', - parentSpanId: '2PYWumUB8/Q=', - parent: { id: '2PYWumUB8/Q=', traceId: '/9GShGd4h/tJ1Yei1rzV8A==' }, - references: [], + parentSpanId: 'KUT43ADmiVo=', + parent: { id: 'KUT43ADmiVo=', traceId: 'LAWqC9bxJ/QJ4eXmjf6wpQ==' }, status: { code: undefined }, attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.31.2', + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', 'http.route': '/', 'express.name': 'expressInit', 'express.type': 'middleware', @@ -71,18 +39,18 @@ module.exports = [ } }, { - traceId: '/9GShGd4h/tJ1Yei1rzV8A==', - id: 'nUmcea9N0DM=', - startTime: '2022-10-27T15:28:23.700294656Z', + traceId: 'LAWqC9bxJ/QJ4eXmjf6wpQ==', + id: '6TdoTvXE+I4=', + startTime: '2024-10-23T11:49:39.293Z', + hrStartTime: 1729684179293000000n, name: 'request handler - /hello', kind: 'internal', - parentSpanId: '2PYWumUB8/Q=', - parent: { id: '2PYWumUB8/Q=', traceId: '/9GShGd4h/tJ1Yei1rzV8A==' }, - references: [], + parentSpanId: 'KUT43ADmiVo=', + parent: { id: 'KUT43ADmiVo=', traceId: 'LAWqC9bxJ/QJ4eXmjf6wpQ==' }, status: { code: undefined }, attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.31.2', + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', 'http.route': '/hello', 'express.name': '/hello', 'express.type': 'request_handler', @@ -90,18 +58,50 @@ module.exports = [ } }, { - traceId: '/9GShGd4h/tJ1Yei1rzV8A==', - id: 'ugJHEI0w8TA=', - startTime: '2022-10-27T15:28:23.700867328Z', + traceId: 'LAWqC9bxJ/QJ4eXmjf6wpQ==', + id: '1Al/kbAgRyM=', + startTime: '2024-10-23T11:49:39.294Z', + hrStartTime: 1729684179294000000n, name: 'hello', kind: 'internal', - parentSpanId: '2PYWumUB8/Q=', - parent: { id: '2PYWumUB8/Q=', traceId: '/9GShGd4h/tJ1Yei1rzV8A==' }, - references: [], + parentSpanId: 'KUT43ADmiVo=', + parent: { id: 'KUT43ADmiVo=', traceId: 'LAWqC9bxJ/QJ4eXmjf6wpQ==' }, status: { code: undefined }, attributes: { - 'otel.library.name': 'splunk-otel-example-basic', + 'otel.scope.name': 'splunk-otel-example-basic', 'span.kind': 'internal' } + }, + { + traceId: 'LAWqC9bxJ/QJ4eXmjf6wpQ==', + id: 'KUT43ADmiVo=', + startTime: '2024-10-23T11:49:39.271Z', + hrStartTime: 1729684179271000000n, + name: 'GET /hello', + kind: 'server', + parentSpanId: undefined, + parent: undefined, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-http', + 'otel.scope.version': '0.53.0', + 'http.url': 'http://app/hello', + 'http.host': 'app', + 'net.host.name': 'app', + 'http.method': 'GET', + 'http.scheme': 'http', + 'http.target': '/hello', + 'http.user_agent': 'node', + 'http.flavor': '1.1', + 'net.transport': 'ip_tcp', + 'net.host.ip': '::ffff:172.18.0.3', + 'net.host.port': undefined, + 'net.peer.ip': '::ffff:172.18.0.4', + 'net.peer.port': undefined, + 'http.status_code': undefined, + 'http.status_text': 'CREATED', + 'http.route': '/hello', + 'span.kind': 'server' + } } ]; diff --git a/test/examples/collector.config.yml b/test/examples/collector.config.yml index 58951c57..f8608f9e 100644 --- a/test/examples/collector.config.yml +++ b/test/examples/collector.config.yml @@ -13,17 +13,17 @@ exporters: logging: httpsink: endpoint: 0.0.0.0:8378 - logging/debug: - loglevel: debug + debug: + verbosity: detailed service: pipelines: traces: receivers: [otlp] - exporters: [httpsink, logging/debug] + exporters: [httpsink, debug] metrics: receivers: [otlp] - exporters: [logging/debug] + exporters: [debug] logs/profiling: receivers: [otlp] - exporters: [logging/debug] + exporters: [debug] diff --git a/test/examples/e2e.docker-compose.yml b/test/examples/e2e.docker-compose.yml index 0abfd784..f9f7f8da 100644 --- a/test/examples/e2e.docker-compose.yml +++ b/test/examples/e2e.docker-compose.yml @@ -1,7 +1,6 @@ -version: '3.4' services: collector: - image: quay.io/signalfx/splunk-otel-collector:0.50.1 + image: quay.io/signalfx/splunk-otel-collector:0.111.0 environment: SPLUNK_CONFIG: /etc/otel/config.yml volumes: @@ -21,7 +20,7 @@ services: command: node ./basic environment: REQ_URL: http://app/hello - COLLECTOR_URL: http://collector:8378 + COLLECTOR_URL: http://collector:8378/spans depends_on: - app - collector diff --git a/test/examples/express.override.yml b/test/examples/express.override.yml index 972b311a..b24d520a 100644 --- a/test/examples/express.override.yml +++ b/test/examples/express.override.yml @@ -8,7 +8,7 @@ services: command: node ./express environment: REQ_URL: http://app/animal - COLLECTOR_URL: http://collector:8378 + COLLECTOR_URL: http://collector:8378/spans depends_on: - app - collector diff --git a/test/examples/express/app.env b/test/examples/express/app.env index 8a46777c..897622e3 100644 --- a/test/examples/express/app.env +++ b/test/examples/express/app.env @@ -2,4 +2,4 @@ PORT=80 OTEL_SERVICE_NAME='express-example' OTEL_RESOURCE_ATTRIBUTES='deployment.environment=dev' OTEL_LOG_LEVEL='DEBUG' -OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317 +OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318 diff --git a/test/examples/express/snapshot.js b/test/examples/express/snapshot.js index a20966cc..707add8a 100644 --- a/test/examples/express/snapshot.js +++ b/test/examples/express/snapshot.js @@ -1,247 +1,230 @@ module.exports = [ - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: 'GexzSLvMxM0=', - startTime: '2023-07-25T14:50:08.443000064Z', - hrStartTime: 1690296608443000064n, - name: 'middleware - query', - kind: 'internal', - parentSpanId: 'kOYrlY2RC3c=', - parent: { id: 'kOYrlY2RC3c=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.33.0', - 'http.route': '/', - 'express.name': 'query', - 'express.type': 'middleware', - 'span.kind': 'internal' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: '/S0ovsDo2Qw=', - startTime: '2023-07-25T14:50:08.444999936Z', - hrStartTime: 1690296608444999936n, - name: 'middleware - expressInit', - kind: 'internal', - parentSpanId: 'kOYrlY2RC3c=', - parent: { id: 'kOYrlY2RC3c=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.33.0', - 'http.route': '/', - 'express.name': 'expressInit', - 'express.type': 'middleware', - 'span.kind': 'internal' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: 'HxX0RVOv3Rg=', - startTime: '2023-07-25T14:50:08.446000128Z', - hrStartTime: 1690296608446000128n, - name: 'request handler - /animal', - kind: 'internal', - parentSpanId: 'kOYrlY2RC3c=', - parent: { id: 'kOYrlY2RC3c=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.33.0', - 'http.route': '/animal', - 'express.name': '/animal', - 'express.type': 'request_handler', - 'span.kind': 'internal' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: '0KVEUJRmGY8=', - startTime: '2023-07-25T14:50:08.473999872Z', - hrStartTime: 1690296608473999872n, - name: 'middleware - query', - kind: 'internal', - parentSpanId: 'e5HVooMVkp4=', - parent: { id: 'e5HVooMVkp4=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.33.0', - 'http.route': '/', - 'express.name': 'query', - 'express.type': 'middleware', - 'span.kind': 'internal' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: 'qWoD2x6i4Ec=', - startTime: '2023-07-25T14:50:08.473999872Z', - hrStartTime: 1690296608473999872n, - name: 'middleware - expressInit', - kind: 'internal', - parentSpanId: 'e5HVooMVkp4=', - parent: { id: 'e5HVooMVkp4=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.33.0', - 'http.route': '/', - 'express.name': 'expressInit', - 'express.type': 'middleware', - 'span.kind': 'internal' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: 'DMajdvSLIc0=', - startTime: '2023-07-25T14:50:08.475000064Z', - hrStartTime: 1690296608475000064n, - name: 'request handler - /name', - kind: 'internal', - parentSpanId: 'e5HVooMVkp4=', - parent: { id: 'e5HVooMVkp4=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.33.0', - 'http.route': '/name', - 'express.name': '/name', - 'express.type': 'request_handler', - 'span.kind': 'internal' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: 'HTx7BLfkGIQ=', - startTime: '2023-07-25T14:50:08.465999872Z', - hrStartTime: 1690296608465999872n, - name: 'dns.lookup', - kind: 'client', - parentSpanId: 'zOH1AFRxX5Y=', - parent: { id: 'zOH1AFRxX5Y=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-dns', - 'otel.library.version': '0.32.0', - 'peer.ipv4': '127.0.0.1', - 'span.kind': 'client' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: 'd+593ui8uZY=', - startTime: '2023-07-25T14:50:08.464999936Z', - hrStartTime: 1690296608464999936n, - name: 'tcp.connect', - kind: 'internal', - parentSpanId: 'zOH1AFRxX5Y=', - parent: { id: 'zOH1AFRxX5Y=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-net', - 'otel.library.version': '0.32.0', - 'net.transport': 'ip_tcp', - 'net.peer.name': 'localhost', - 'net.peer.port': '80', - 'net.peer.ip': '127.0.0.1', - 'net.host.ip': '127.0.0.1', - 'net.host.port': undefined, - 'span.kind': 'internal' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: 'e5HVooMVkp4=', - startTime: '2023-07-25T14:50:08.473999872Z', - hrStartTime: 1690296608473999872n, - name: 'GET /name', - kind: 'server', - parentSpanId: 'zOH1AFRxX5Y=', - parent: { id: 'zOH1AFRxX5Y=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-http', - 'otel.library.version': '0.41.1', - 'http.url': 'http://localhost/name', - 'http.host': 'localhost', - 'net.host.name': 'localhost', - 'http.method': 'GET', - 'http.scheme': 'http', - 'http.target': '/name', - 'http.user_agent': 'axios/0.21.4', - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'net.host.ip': '::ffff:127.0.0.1', - 'net.host.port': undefined, - 'net.peer.ip': '::ffff:127.0.0.1', - 'net.peer.port': undefined, - 'http.status_code': undefined, - 'http.status_text': 'OK', - 'http.route': '/name', - 'span.kind': 'server' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: 'zOH1AFRxX5Y=', - startTime: '2023-07-25T14:50:08.463000064Z', - hrStartTime: 1690296608463000064n, - name: 'GET', - kind: 'client', - parentSpanId: 'kOYrlY2RC3c=', - parent: { id: 'kOYrlY2RC3c=', traceId: 'OsUGTyjZAT95YozWxOQrKg==' }, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-http', - 'otel.library.version': '0.41.1', - 'http.url': 'http://localhost/name', - 'http.method': 'GET', - 'http.target': '/name', - 'net.peer.name': 'localhost', - 'http.host': 'localhost:80', - 'net.peer.ip': '127.0.0.1', - 'net.peer.port': undefined, - 'http.response_content_length_uncompressed': undefined, - 'http.status_code': undefined, - 'http.status_text': 'OK', - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'span.kind': 'client' - } - }, - { - traceId: 'OsUGTyjZAT95YozWxOQrKg==', - id: 'kOYrlY2RC3c=', - startTime: '2023-07-25T14:50:08.433999872Z', - hrStartTime: 1690296608433999872n, - name: 'GET /animal', - kind: 'server', - parentSpanId: undefined, - parent: undefined, - status: { code: undefined }, - attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-http', - 'otel.library.version': '0.41.1', - 'http.url': 'http://app/animal', - 'http.host': 'app', - 'net.host.name': 'app', - 'http.method': 'GET', - 'http.scheme': 'http', - 'http.target': '/animal', - 'http.user_agent': 'undici', - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'net.host.ip': '::ffff:172.31.0.3', - 'net.host.port': undefined, - 'net.peer.ip': '::ffff:172.31.0.4', - 'net.peer.port': undefined, - 'http.status_code': undefined, - 'http.status_text': 'OK', - 'http.route': '/animal', - 'span.kind': 'server' - } - } + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: 'u2UfqEE6o2g=', + startTime: '2024-10-23T12:08:56.957Z', + hrStartTime: 1729685336957000000n, + name: 'middleware - query', + kind: 'internal', + parentSpanId: 'EjUwbQetNus=', + parent: { id: 'EjUwbQetNus=', traceId: 'c5zIzB9SgNYhBPCbTjwg4A==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', + 'http.route': '/', + 'express.name': 'query', + 'express.type': 'middleware', + 'span.kind': 'internal' + } + }, + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: 'th00mL0dypQ=', + startTime: '2024-10-23T12:08:56.960Z', + hrStartTime: 1729685336960000000n, + name: 'middleware - expressInit', + kind: 'internal', + parentSpanId: 'EjUwbQetNus=', + parent: { id: 'EjUwbQetNus=', traceId: 'c5zIzB9SgNYhBPCbTjwg4A==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', + 'http.route': '/', + 'express.name': 'expressInit', + 'express.type': 'middleware', + 'span.kind': 'internal' + } + }, + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: '1Er5vsUvUVg=', + startTime: '2024-10-23T12:08:56.961Z', + hrStartTime: 1729685336961000000n, + name: 'request handler - /animal', + kind: 'internal', + parentSpanId: 'EjUwbQetNus=', + parent: { id: 'EjUwbQetNus=', traceId: 'c5zIzB9SgNYhBPCbTjwg4A==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', + 'http.route': '/animal', + 'express.name': '/animal', + 'express.type': 'request_handler', + 'span.kind': 'internal' + } + }, + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: 'n6/1kjZEBD4=', + startTime: '2024-10-23T12:08:56.980Z', + hrStartTime: 1729685336980000000n, + name: 'middleware - query', + kind: 'internal', + parentSpanId: '4A4ribbJejI=', + parent: { id: '4A4ribbJejI=', traceId: 'c5zIzB9SgNYhBPCbTjwg4A==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', + 'http.route': '/', + 'express.name': 'query', + 'express.type': 'middleware', + 'span.kind': 'internal' + } + }, + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: '7Fogi1oddwA=', + startTime: '2024-10-23T12:08:56.981Z', + hrStartTime: 1729685336981000000n, + name: 'middleware - expressInit', + kind: 'internal', + parentSpanId: '4A4ribbJejI=', + parent: { id: '4A4ribbJejI=', traceId: 'c5zIzB9SgNYhBPCbTjwg4A==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', + 'http.route': '/', + 'express.name': 'expressInit', + 'express.type': 'middleware', + 'span.kind': 'internal' + } + }, + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: '+qi2mdPafOY=', + startTime: '2024-10-23T12:08:56.981Z', + hrStartTime: 1729685336981000000n, + name: 'request handler - /name', + kind: 'internal', + parentSpanId: '4A4ribbJejI=', + parent: { id: '4A4ribbJejI=', traceId: 'c5zIzB9SgNYhBPCbTjwg4A==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', + 'http.route': '/name', + 'express.name': '/name', + 'express.type': 'request_handler', + 'span.kind': 'internal' + } + }, + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: 'awOi2E+05aU=', + startTime: '2024-10-23T12:08:56.972Z', + hrStartTime: 1729685336972000000n, + name: 'tcp.connect', + kind: 'internal', + parentSpanId: '8ROHwPYvo4Q=', + parent: { id: '8ROHwPYvo4Q=', traceId: 'c5zIzB9SgNYhBPCbTjwg4A==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-net', + 'otel.scope.version': '0.39.0', + 'net.transport': 'ip_tcp', + 'net.peer.name': 'localhost', + 'net.peer.port': '80', + 'net.peer.ip': '::1', + 'net.host.ip': '::1', + 'net.host.port': undefined, + 'span.kind': 'internal' + } + }, + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: '4A4ribbJejI=', + startTime: '2024-10-23T12:08:56.980Z', + hrStartTime: 1729685336980000000n, + name: 'GET /name', + kind: 'server', + parentSpanId: '8ROHwPYvo4Q=', + parent: { id: '8ROHwPYvo4Q=', traceId: 'c5zIzB9SgNYhBPCbTjwg4A==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-http', + 'otel.scope.version': '0.53.0', + 'http.url': 'http://localhost/name', + 'http.host': 'localhost', + 'net.host.name': 'localhost', + 'http.method': 'GET', + 'http.scheme': 'http', + 'http.target': '/name', + 'http.user_agent': 'axios/0.28.1', + 'http.flavor': '1.1', + 'net.transport': 'ip_tcp', + 'net.host.ip': '::1', + 'net.host.port': undefined, + 'net.peer.ip': '::1', + 'net.peer.port': undefined, + 'http.status_code': undefined, + 'http.status_text': 'OK', + 'http.route': '/name', + 'span.kind': 'server' + } + }, + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: '8ROHwPYvo4Q=', + startTime: '2024-10-23T12:08:56.969Z', + hrStartTime: 1729685336969000000n, + name: 'GET', + kind: 'client', + parentSpanId: 'EjUwbQetNus=', + parent: { id: 'EjUwbQetNus=', traceId: 'c5zIzB9SgNYhBPCbTjwg4A==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-http', + 'otel.scope.version': '0.53.0', + 'http.url': 'http://localhost/name', + 'http.method': 'GET', + 'http.target': '/name', + 'net.peer.name': 'localhost', + 'http.host': 'localhost:80', + 'net.peer.ip': '::1', + 'net.peer.port': undefined, + 'http.response_content_length_uncompressed': undefined, + 'http.status_code': undefined, + 'http.status_text': 'OK', + 'http.flavor': '1.1', + 'net.transport': 'ip_tcp', + 'span.kind': 'client' + } + }, + { + traceId: 'c5zIzB9SgNYhBPCbTjwg4A==', + id: 'EjUwbQetNus=', + startTime: '2024-10-23T12:08:56.944Z', + hrStartTime: 1729685336944000000n, + name: 'GET /animal', + kind: 'server', + parentSpanId: undefined, + parent: undefined, + status: { code: undefined }, + attributes: { + 'otel.scope.name': '@opentelemetry/instrumentation-http', + 'otel.scope.version': '0.53.0', + 'http.url': 'http://app/animal', + 'http.host': 'app', + 'net.host.name': 'app', + 'http.method': 'GET', + 'http.scheme': 'http', + 'http.target': '/animal', + 'http.user_agent': 'node', + 'http.flavor': '1.1', + 'net.transport': 'ip_tcp', + 'net.host.ip': '::ffff:172.18.0.3', + 'net.host.port': undefined, + 'net.peer.ip': '::ffff:172.18.0.4', + 'net.peer.port': undefined, + 'http.status_code': undefined, + 'http.status_text': 'OK', + 'http.route': '/animal', + 'span.kind': 'server' + } + } ]; diff --git a/test/examples/log-injection.override.yml b/test/examples/log-injection.override.yml index e0968e96..1dcc3c05 100644 --- a/test/examples/log-injection.override.yml +++ b/test/examples/log-injection.override.yml @@ -5,4 +5,4 @@ services: test: command: node ./log-injection environment: - COLLECTOR_URL: http://collector:8378 + COLLECTOR_URL: http://collector:8378/spans diff --git a/test/examples/log-injection/app.env b/test/examples/log-injection/app.env index 0fedee11..bfb029b9 100644 --- a/test/examples/log-injection/app.env +++ b/test/examples/log-injection/app.env @@ -1,4 +1,4 @@ OTEL_SERVICE_NAME='log-injection-example' OTEL_RESOURCE_ATTRIBUTES='deployment.environment=dev' OTEL_LOG_LEVEL='DEBUG' -OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317 +OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318 diff --git a/test/examples/log-injection/snapshot.js b/test/examples/log-injection/snapshot.js index 26a67076..da266e44 100644 --- a/test/examples/log-injection/snapshot.js +++ b/test/examples/log-injection/snapshot.js @@ -1,17 +1,17 @@ // a console.log from a previous run module.exports = [ { - traceId: 'SugyaoARxWUXXGW7OE6nXg==', - id: 'B4g4YngKBnM=', - startTime: '2022-06-03T13:04:28.087011840Z', + traceId: 'zMEy3eC1iaInGGIWbtULUg==', + id: 'X0ohVf0SuXA=', + startTime: '2024-10-23T12:20:31.979Z', + hrStartTime: 1729686031979000000n, name: 'main', kind: 'internal', parentSpanId: undefined, parent: undefined, - references: undefined, status: { code: undefined }, attributes: { - 'otel.library.name': 'splunk-otel-example-log-injection', + 'otel.scope.name': 'splunk-otel-example-log-injection', 'span.kind': 'internal' } } diff --git a/test/examples/mixed.override.yml b/test/examples/mixed.override.yml index a843b499..d48d67ad 100644 --- a/test/examples/mixed.override.yml +++ b/test/examples/mixed.override.yml @@ -6,4 +6,4 @@ services: command: node ./mixed environment: REQ_URL: http://app/ - COLLECTOR_URL: http://collector:8378 + COLLECTOR_URL: http://collector:8378/spans diff --git a/test/examples/mixed/app.env b/test/examples/mixed/app.env index 2f39b34a..a49262ee 100644 --- a/test/examples/mixed/app.env +++ b/test/examples/mixed/app.env @@ -2,4 +2,4 @@ PORT=80 OTEL_SERVICE_NAME='mixed-example' OTEL_RESOURCE_ATTRIBUTES='deployment.environment=dev' OTEL_LOG_LEVEL='DEBUG' -OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317 +OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318 diff --git a/test/examples/mixed/snapshot.js b/test/examples/mixed/snapshot.js index c6b3402f..ca0b9e56 100644 --- a/test/examples/mixed/snapshot.js +++ b/test/examples/mixed/snapshot.js @@ -1,24 +1,43 @@ // a console.log from a previous run module.exports = [ { - traceId: '0Iqbr5g8SjiaX3WlJBQyTA==', - id: 'U9O4F1SzkzY=', - startTime: '2022-06-03T13:06:01.752053248Z', + traceId: '/pCytNplYFQSLCxtMUCv8g==', + id: '7Moln4ajdLY=', + startTime: '2024-10-23T12:26:15.117Z', + hrStartTime: 1729686375117000000n, + name: 'work', + kind: 'internal', + parentSpanId: 'qhrhxw7WAiE=', + parent: { id: 'qhrhxw7WAiE=', traceId: '/pCytNplYFQSLCxtMUCv8g==' }, + status: { code: undefined }, + attributes: { + 'otel.scope.name': 'splunk-otel-example-mixed', + 'work.expected_duration': undefined, + 'work.my_parameter': undefined, + 'work.result': undefined, + 'span.kind': 'internal' + } + }, + { + traceId: '/pCytNplYFQSLCxtMUCv8g==', + id: 'qhrhxw7WAiE=', + startTime: '2024-10-23T12:26:15.101Z', + hrStartTime: 1729686375101000000n, name: 'GET', kind: 'server', parentSpanId: undefined, parent: undefined, - references: undefined, status: { code: undefined }, attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-http', - 'otel.library.version': '0.28.0', + 'otel.scope.name': '@opentelemetry/instrumentation-http', + 'otel.scope.version': '0.53.0', 'http.url': 'http://app/', 'http.host': 'app', 'net.host.name': 'app', 'http.method': 'GET', + 'http.scheme': 'http', 'http.target': '/', - 'http.user_agent': 'got (https://github.com/sindresorhus/got)', + 'http.user_agent': 'node', 'http.flavor': '1.1', 'net.transport': 'ip_tcp', 'net.host.ip': '::ffff:172.18.0.3', @@ -29,23 +48,5 @@ module.exports = [ 'http.status_text': 'OK', 'span.kind': 'server' } - }, - { - traceId: '0Iqbr5g8SjiaX3WlJBQyTA==', - id: 'CnIjaAfhsFM=', - startTime: '2022-06-03T13:06:01.755319040Z', - name: 'work', - kind: 'internal', - parentSpanId: 'U9O4F1SzkzY=', - parent: { id: 'U9O4F1SzkzY=', traceId: '0Iqbr5g8SjiaX3WlJBQyTA==' }, - references: [], - status: { code: undefined }, - attributes: { - 'otel.library.name': 'splunk-otel-example-mixed', - 'work.expected_duration': undefined, - 'work.my_parameter': undefined, - 'work.result': undefined, - 'span.kind': 'internal' - } } ]; diff --git a/test/examples/profiling.override.yml b/test/examples/profiling.override.yml index b0136801..d793e6eb 100644 --- a/test/examples/profiling.override.yml +++ b/test/examples/profiling.override.yml @@ -5,4 +5,4 @@ services: test: command: node ./profiling environment: - COLLECTOR_URL: http://collector:8378 + COLLECTOR_URL: http://collector:8378/spans diff --git a/test/examples/profiling/app.env b/test/examples/profiling/app.env index d62e6adf..dd645c71 100644 --- a/test/examples/profiling/app.env +++ b/test/examples/profiling/app.env @@ -1,5 +1,5 @@ OTEL_SERVICE_NAME='profiling-example' OTEL_RESOURCE_ATTRIBUTES='deployment.environment=dev' OTEL_LOG_LEVEL='DEBUG' -OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317 -SPLUNK_PROFILER_LOGS_ENDPOINT=http://collector:4317 +OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318 +SPLUNK_PROFILER_LOGS_ENDPOINT=http://collector:4318 diff --git a/test/examples/profiling/index.js b/test/examples/profiling/index.js index 05befd07..b15e587a 100644 --- a/test/examples/profiling/index.js +++ b/test/examples/profiling/index.js @@ -1,7 +1,6 @@ const { assertSpans, logSpanTable, - request, waitSpans, } = require('../utils.js'); const snapshot = require('./snapshot.js'); diff --git a/test/examples/profiling/snapshot.js b/test/examples/profiling/snapshot.js index a2ded860..2c454015 100644 --- a/test/examples/profiling/snapshot.js +++ b/test/examples/profiling/snapshot.js @@ -1,17 +1,17 @@ // a console.log from a previous run module.exports = [ { - traceId: 'SugyaoARxWUXXGW7OE6nXg==', - id: 'B4g4YngKBnM=', - startTime: '2022-06-03T13:04:28.087011840Z', + traceId: 'F/F3qoc4BVuoI+5xc2e+uA==', + id: '/7kIDz0ib94=', + startTime: '2024-10-23T15:34:21.116Z', + hrStartTime: 1729697661116000000n, name: 'main', kind: 'internal', parentSpanId: undefined, parent: undefined, - references: undefined, status: { code: undefined }, attributes: { - 'otel.library.name': 'splunk-otel-example-profiling', + 'otel.scope.name': 'splunk-otel-example-profiling', 'span.kind': 'internal' } } diff --git a/test/examples/typescript.override.yml b/test/examples/typescript.override.yml index 80371de5..a4a16a4b 100644 --- a/test/examples/typescript.override.yml +++ b/test/examples/typescript.override.yml @@ -9,7 +9,7 @@ services: command: node ./typescript environment: REQ_URL: http://app/ - COLLECTOR_URL: http://collector:8378 + COLLECTOR_URL: http://collector:8378/spans depends_on: - app - collector diff --git a/test/examples/typescript/app.env b/test/examples/typescript/app.env index bc6229a0..8a3df9be 100644 --- a/test/examples/typescript/app.env +++ b/test/examples/typescript/app.env @@ -1,4 +1,4 @@ PORT=80 OTEL_RESOURCE_ATTRIBUTES='deployment.environment=dev' OTEL_LOG_LEVEL='DEBUG' -OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317 +OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318 diff --git a/test/examples/typescript/snapshot.js b/test/examples/typescript/snapshot.js index 7d86d6ff..0ffd96b7 100644 --- a/test/examples/typescript/snapshot.js +++ b/test/examples/typescript/snapshot.js @@ -1,17 +1,17 @@ module.exports = [ { - traceId: 'neZpe/CP+vAd5/rhg0Tw3A==', - id: 'fe+RxrZ76pE=', - startTime: '2023-09-18T20:16:38.412999936Z', - hrStartTime: 1695068198412999936n, + traceId: 'R+gxE9M1J6UTkIsOh7PMjg==', + id: 'V4DPhy8Psf4=', + startTime: '2024-10-23T13:06:59.060Z', + hrStartTime: 1729688819060000000n, name: 'middleware - query', kind: 'internal', - parentSpanId: 'NVeO5hUT2bw=', - parent: { id: 'NVeO5hUT2bw=', traceId: 'neZpe/CP+vAd5/rhg0Tw3A==' }, + parentSpanId: 'iApIRUZb7+s=', + parent: { id: 'iApIRUZb7+s=', traceId: 'R+gxE9M1J6UTkIsOh7PMjg==' }, status: { code: undefined }, attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.33.0', + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', 'http.route': '/', 'express.name': 'query', 'express.type': 'middleware', @@ -19,18 +19,18 @@ module.exports = [ } }, { - traceId: 'neZpe/CP+vAd5/rhg0Tw3A==', - id: 'CnuvgyiHryk=', - startTime: '2023-09-18T20:16:38.416Z', - hrStartTime: 1695068198416000000n, + traceId: 'R+gxE9M1J6UTkIsOh7PMjg==', + id: 'sDChkVN+hZg=', + startTime: '2024-10-23T13:06:59.063Z', + hrStartTime: 1729688819063000000n, name: 'middleware - expressInit', kind: 'internal', - parentSpanId: 'NVeO5hUT2bw=', - parent: { id: 'NVeO5hUT2bw=', traceId: 'neZpe/CP+vAd5/rhg0Tw3A==' }, + parentSpanId: 'iApIRUZb7+s=', + parent: { id: 'iApIRUZb7+s=', traceId: 'R+gxE9M1J6UTkIsOh7PMjg==' }, status: { code: undefined }, attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.33.0', + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', 'http.route': '/', 'express.name': 'expressInit', 'express.type': 'middleware', @@ -38,18 +38,18 @@ module.exports = [ } }, { - traceId: 'neZpe/CP+vAd5/rhg0Tw3A==', - id: 'ZXuWjxuKTqw=', - startTime: '2023-09-18T20:16:38.416999936Z', - hrStartTime: 1695068198416999936n, + traceId: 'R+gxE9M1J6UTkIsOh7PMjg==', + id: '1uGSa9Su+KI=', + startTime: '2024-10-23T13:06:59.064Z', + hrStartTime: 1729688819064000000n, name: 'request handler - /', kind: 'internal', - parentSpanId: 'NVeO5hUT2bw=', - parent: { id: 'NVeO5hUT2bw=', traceId: 'neZpe/CP+vAd5/rhg0Tw3A==' }, + parentSpanId: 'iApIRUZb7+s=', + parent: { id: 'iApIRUZb7+s=', traceId: 'R+gxE9M1J6UTkIsOh7PMjg==' }, status: { code: undefined }, attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-express', - 'otel.library.version': '0.33.0', + 'otel.scope.name': '@opentelemetry/instrumentation-express', + 'otel.scope.version': '0.42.0', 'http.route': '/', 'express.name': '/', 'express.type': 'request_handler', @@ -57,47 +57,47 @@ module.exports = [ } }, { - traceId: 'neZpe/CP+vAd5/rhg0Tw3A==', - id: '2rGvSI5L/p4=', - startTime: '2023-09-18T20:16:38.417999872Z', - hrStartTime: 1695068198417999872n, + traceId: 'R+gxE9M1J6UTkIsOh7PMjg==', + id: 'LJVoXT8xVY4=', + startTime: '2024-10-23T13:06:59.066Z', + hrStartTime: 1729688819066000000n, name: 'calculate-random', kind: 'internal', - parentSpanId: 'NVeO5hUT2bw=', - parent: { id: 'NVeO5hUT2bw=', traceId: 'neZpe/CP+vAd5/rhg0Tw3A==' }, + parentSpanId: 'iApIRUZb7+s=', + parent: { id: 'iApIRUZb7+s=', traceId: 'R+gxE9M1J6UTkIsOh7PMjg==' }, status: { code: undefined }, attributes: { - 'otel.library.name': 'rng-app', + 'otel.scope.name': 'rng-app', 'random-result': undefined, 'random-method': 'diceroll', 'span.kind': 'internal' } }, { - traceId: 'neZpe/CP+vAd5/rhg0Tw3A==', - id: 'NVeO5hUT2bw=', - startTime: '2023-09-18T20:16:38.400999936Z', - hrStartTime: 1695068198400999936n, + traceId: 'R+gxE9M1J6UTkIsOh7PMjg==', + id: 'iApIRUZb7+s=', + startTime: '2024-10-23T13:06:59.048Z', + hrStartTime: 1729688819048000000n, name: 'GET /', kind: 'server', parentSpanId: undefined, parent: undefined, status: { code: undefined }, attributes: { - 'otel.library.name': '@opentelemetry/instrumentation-http', - 'otel.library.version': '0.41.2', + 'otel.scope.name': '@opentelemetry/instrumentation-http', + 'otel.scope.version': '0.53.0', 'http.url': 'http://app/', 'http.host': 'app', 'net.host.name': 'app', 'http.method': 'GET', 'http.scheme': 'http', 'http.target': '/', - 'http.user_agent': 'undici', + 'http.user_agent': 'node', 'http.flavor': '1.1', 'net.transport': 'ip_tcp', - 'net.host.ip': '::ffff:172.31.0.3', + 'net.host.ip': '::ffff:172.18.0.3', 'net.host.port': undefined, - 'net.peer.ip': '::ffff:172.31.0.4', + 'net.peer.ip': '::ffff:172.18.0.4', 'net.peer.port': undefined, 'http.status_code': undefined, 'http.status_text': 'OK', diff --git a/test/examples/utils.js b/test/examples/utils.js index c90bebde..b497fbc7 100644 --- a/test/examples/utils.js +++ b/test/examples/utils.js @@ -111,14 +111,14 @@ function sortByName(spans) { const waitSpans = (count, timeout = 60) => { console.error(`Waiting for ${count} spans for ${timeout}s`); console.time('waitSpans'); - const collectorUrl = new URL(process.env.COLLECTOR_URL ?? 'http://localhost:8378'); + const collectorUrl = new URL(process.env.COLLECTOR_URL ?? 'http://localhost:8378/spans'); collectorUrl.searchParams.set('count', count); collectorUrl.searchParams.set('timeout', timeout); return fetch(collectorUrl) .then((res) => res.text()) .then((content) => { - if (content.match(/timed.*out.*waiting.*spans/)) { + if (content.match(/timed.*out.*while.*waiting.*for.*results/)) { assert.fail(`Timed out waiting for ${count} spans for ${timeout}s.`); } diff --git a/test/metrics.options.test.ts b/test/metrics.options.test.ts index 3e500d47..c094cbef 100644 --- a/test/metrics.options.test.ts +++ b/test/metrics.options.test.ts @@ -17,7 +17,7 @@ import * as api from '@opentelemetry/api'; import * as utils from './utils'; import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; -import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; +import { OTLPMetricExporter as OTLPGrpcMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; import { OTLPMetricExporter as OTLPHttpProtoMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto'; import { _setDefaultOptions } from '../src/metrics'; import { ConsoleMetricExporter } from '../src/metrics/ConsoleMetricExporter'; @@ -53,7 +53,7 @@ describe('metrics options', () => { assert.deepStrictEqual(readers.length, 2); - assert(readers[0]['_exporter'] instanceof OTLPMetricExporter); + assert(readers[0]['_exporter'] instanceof OTLPHttpProtoMetricExporter); assert(readers[1]['_exporter'] instanceof ConsoleMetricExporter); }); @@ -85,27 +85,30 @@ describe('metrics options', () => { }); it('is possible to use OTEL_EXPORTER_OTLP_PROTOCOL', () => { - process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf'; + process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'grpc'; const options = _setDefaultOptions(); const [reader] = options.metricReaderFactory(options); - assert(reader['_exporter'] instanceof OTLPHttpProtoMetricExporter); + assert(reader['_exporter'] instanceof OTLPGrpcMetricExporter); }); it('prefers OTEL_EXPORTER_OTLP_METRICS_PROTOCOL over OTEL_EXPORTER_OTLP_PROTOCOL', () => { - process.env.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL = 'http/protobuf'; - process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'grpc'; + process.env.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL = 'grpc'; + process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf'; const options = _setDefaultOptions(); const [reader] = options.metricReaderFactory(options); - assert(reader['_exporter'] instanceof OTLPHttpProtoMetricExporter); + assert(reader['_exporter'] instanceof OTLPGrpcMetricExporter); }); it('is possible to use OTEL_EXPORTER_OTLP_ENDPOINT', () => { - process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'foobar:4200'; + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foobar:4200'; const options = _setDefaultOptions(); const [reader] = options.metricReaderFactory(options); const exporter = reader['_exporter']; - assert(exporter instanceof OTLPMetricExporter); - assert.deepStrictEqual(exporter['_otlpExporter'].url, 'foobar:4200'); + assert(exporter instanceof OTLPHttpProtoMetricExporter); + assert.deepStrictEqual( + exporter['_otlpExporter'].url, + 'http://foobar:4200/v1/metrics' + ); }); }); @@ -164,7 +167,7 @@ describe('metrics options', () => { it('warns when realm and endpoint are both set', () => { process.env.SPLUNK_REALM = 'us0'; process.env.SPLUNK_ACCESS_TOKEN = 'abc'; - process.env.SPLUNK_METRICS_ENDPOINT = 'http://localhost:4317'; + process.env.SPLUNK_METRICS_ENDPOINT = 'http://localhost:4320'; const options = _setDefaultOptions(); const [reader] = options.metricReaderFactory(options); @@ -174,8 +177,11 @@ describe('metrics options', () => { 'OTLP metric exporter factory: Realm value ignored (full endpoint URL has been specified).'; assert.equal(logger.warn.mock.calls[0].arguments[0], msg); - assert(exporter instanceof OTLPMetricExporter); - assert.deepStrictEqual(exporter['_otlpExporter'].url, 'localhost:4317'); + assert(exporter instanceof OTLPHttpProtoMetricExporter); + assert.deepStrictEqual( + exporter['_otlpExporter'].url, + 'http://localhost:4320' + ); }); }); }); diff --git a/test/metrics.test.ts b/test/metrics.test.ts index 181a78c5..0b34c4c1 100644 --- a/test/metrics.test.ts +++ b/test/metrics.test.ts @@ -15,7 +15,7 @@ */ import { metrics } from '@opentelemetry/api'; -import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto'; import { Resource } from '@opentelemetry/resources'; import { AggregationTemporality, @@ -115,10 +115,11 @@ describe('metrics', () => { ); assert.deepEqual(options.runtimeMetricsEnabled, true); assert.deepEqual(options.runtimeMetricsCollectionIntervalMillis, 5000); + + const readers = options.metricReaderFactory(options); assert( - options.metricReaderFactory(options)[0]['_exporter'] instanceof - OTLPMetricExporter, - 'Expected the default metric exporter to be OTLP gRPC' + readers[0]['_exporter'] instanceof OTLPMetricExporter, + 'Expected the default metric exporter to be OTLP over HTTP' ); assert.deepEqual(options.debugMetricsEnabled, false); }); diff --git a/test/options.test.ts b/test/options.test.ts index 20f562d3..0356b715 100644 --- a/test/options.test.ts +++ b/test/options.test.ts @@ -16,8 +16,8 @@ import * as api from '@opentelemetry/api'; import { W3CBaggagePropagator } from '@opentelemetry/core'; -import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'; -import { OTLPTraceExporter as OTLPHttpTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; +import { OTLPTraceExporter as OTLPGrpcTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; import { InstrumentationBase } from '@opentelemetry/instrumentation'; import { Resource } from '@opentelemetry/resources'; import { @@ -75,9 +75,6 @@ const assertContainerId = (containerIdAttr) => { E.g. OTEL_RESOURCE_ATTRIBUTES="service.name=" */ const MATCH_SERVICE_NAME_WARNING = /service\.name.*not.*set/i; -// No instrumentations set to be loaded. Install an instrumentation package to enable auto-instrumentation. -const MATCH_NO_INSTRUMENTATIONS_WARNING = - /no.*instrumentation.*install.*package/i; describe('options', () => { let logger; @@ -377,7 +374,7 @@ describe('options', () => { assert.deepStrictEqual(exporters.length, 1); const [exporter] = exporters; - assert(exporter instanceof OTLPHttpTraceExporter); + assert(exporter instanceof OTLPTraceExporter); assert.deepStrictEqual( exporter.url, `https://ingest.us0.signalfx.com/v2/trace/otlp` @@ -405,7 +402,7 @@ describe('options', () => { assert(Array.isArray(exporters)); const [exporter] = exporters; - assert(exporter instanceof OTLPHttpTraceExporter); + assert(exporter instanceof OTLPTraceExporter); assert.deepStrictEqual( exporter.url, 'https://ingest.us0.signalfx.com/v2/trace/otlp' @@ -421,7 +418,8 @@ describe('options', () => { it('warns when realm and endpoint are both set', () => { process.env.SPLUNK_REALM = 'us0'; process.env.SPLUNK_ACCESS_TOKEN = 'abc'; - process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://localhost:4317'; + process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = + 'http://myendpoint:4333/v1/my-traces'; const options = _setDefaultOptions(); const exporters = options.spanExporterFactory(options); @@ -439,7 +437,10 @@ describe('options', () => { exporter instanceof OTLPTraceExporter, 'Expected exporter to be instance of OTLPTraceExporter' ); - assert.deepStrictEqual(exporter.url, 'localhost:4317'); + assert.deepStrictEqual( + exporter.url, + 'http://myendpoint:4333/v1/my-traces' + ); }); }); @@ -455,22 +456,22 @@ describe('options', () => { }); it('is possible to use OTEL_EXPORTER_OTLP_PROTOCOL', () => { - process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf'; + process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'grpc'; const options = _setDefaultOptions(); const exporters = options.spanExporterFactory(options); assert(Array.isArray(exporters)); const [exporter] = exporters; - assert(exporter instanceof OTLPHttpTraceExporter); + assert(exporter instanceof OTLPGrpcTraceExporter); }); it('prefers OTEL_EXPORTER_OTLP_TRACES_PROTOCOL over OTEL_EXPORTER_OTLP_PROTOCOL', () => { - process.env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'http/protobuf'; - process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'grpc'; + process.env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; + process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf'; const options = _setDefaultOptions(); const exporters = options.spanExporterFactory(options); assert(Array.isArray(exporters)); const [exporter] = exporters; - assert(exporter instanceof OTLPHttpTraceExporter); + assert(exporter instanceof OTLPGrpcTraceExporter); }); it('is possible to use OTEL_EXPORTER_OTLP_ENDPOINT', () => { @@ -480,7 +481,7 @@ describe('options', () => { assert(Array.isArray(exporters)); const [exporter] = exporters; assert(exporter instanceof OTLPTraceExporter); - assert.deepStrictEqual(exporter.url, 'foobar:4200'); + assert.deepStrictEqual(exporter.url, 'foobar:4200/v1/traces'); }); it('prefers OTEL_EXPORTER_OTLP_TRACES_ENDPOINT over OTEL_EXPORTER_OTLP_ENDPOINT', () => { diff --git a/test/profiling/exporter.test.ts b/test/profiling/exporter.test.ts deleted file mode 100644 index 1e00a8fb..00000000 --- a/test/profiling/exporter.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * 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 - * - * http://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 grpc from '@grpc/grpc-js'; -import { VERSION } from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; -import { strict as assert } from 'assert'; -import { beforeEach, describe, it, mock } from 'node:test'; -import { OTLPProfilingExporter } from '../../src/profiling/OTLPProfilingExporter'; -import * as utils from '../utils'; -import { cpuProfile, heapProfile } from './profiles'; - -describe('profiling OTLP exporter', () => { - describe('configuration', () => { - beforeEach(() => { - utils.cleanEnvironment(); - }); - - it('configures insecure gRPC credentials for endpoints without a scheme', () => { - const exporter = new OTLPProfilingExporter({ - endpoint: 'foobar:8181', - callstackInterval: 1000, - resource: Resource.empty(), - }); - assert.deepStrictEqual( - exporter['_client'].getChannel()['internalChannel']['credentials'], - grpc.ChannelCredentials.createInsecure() - ); - }); - - it('configures insecure gRPC credentials for http endpoints', () => { - const exporter = new OTLPProfilingExporter({ - endpoint: 'http://foobar:8181', - callstackInterval: 1000, - resource: Resource.empty(), - }); - assert.deepStrictEqual( - exporter['_client'].getChannel()['internalChannel']['credentials'], - grpc.ChannelCredentials.createInsecure() - ); - }); - - it('configures secure gRPC credentials for https endpoints', () => { - const exporter = new OTLPProfilingExporter({ - endpoint: 'https://foobar:8181', - callstackInterval: 1000, - resource: Resource.empty(), - }); - assert.deepStrictEqual( - exporter['_client'].getChannel()['internalChannel']['credentials'], - grpc.ChannelCredentials.createSsl() - ); - }); - }); - - describe('exporting', () => { - it('attaches common attributes when exporting CPU profiles', () => { - const exporter = new OTLPProfilingExporter({ - endpoint: 'http://foobar:8181', - callstackInterval: 1000, - resource: new Resource({ service: 'foo' }), - }); - - mock.method(exporter['_client'], 'export', (payload: unknown) => { - const { resourceLogs } = payload as any; - assert.deepStrictEqual(resourceLogs.length, 1); - const { instrumentationLibraryLogs, resource } = resourceLogs[0]; - assert.deepStrictEqual(resource.attributes, [ - { key: 'telemetry.sdk.language', value: { stringValue: 'node' } }, - { key: 'telemetry.sdk.version', value: { stringValue: VERSION } }, - { key: 'service', value: { stringValue: 'foo' } }, - ]); - assert.deepStrictEqual(instrumentationLibraryLogs.length, 1); - const { logs } = instrumentationLibraryLogs[0]; - assert.deepStrictEqual(logs.length, 1); - const log = logs[0]; - - assert.deepStrictEqual(log.attributes, [ - { - key: 'profiling.data.format', - value: { stringValue: 'pprof-gzip-base64' }, - }, - { key: 'profiling.data.type', value: { stringValue: 'cpu' } }, - { - key: 'com.splunk.sourcetype', - value: { stringValue: 'otel.profiling' }, - }, - { key: 'profiling.data.total.frame.count', value: { intValue: 2 } }, - ]); - }); - - exporter.send(cpuProfile); - }); - - it('attaches common attributes when exporting heap profiles', () => { - const exporter = new OTLPProfilingExporter({ - endpoint: 'http://foobar:8181', - callstackInterval: 1000, - resource: new Resource({ service: 'foo' }), - }); - - mock.method(exporter['_client'], 'export', (payload: unknown) => { - const { resourceLogs } = payload as any; - assert.deepStrictEqual(resourceLogs.length, 1); - const { instrumentationLibraryLogs, resource } = resourceLogs[0]; - assert.deepStrictEqual(resource.attributes, [ - { key: 'telemetry.sdk.language', value: { stringValue: 'node' } }, - { key: 'telemetry.sdk.version', value: { stringValue: VERSION } }, - { key: 'service', value: { stringValue: 'foo' } }, - ]); - assert.deepStrictEqual(instrumentationLibraryLogs.length, 1); - const { logs } = instrumentationLibraryLogs[0]; - assert.deepStrictEqual(logs.length, 1); - const log = logs[0]; - - assert.deepStrictEqual(log.attributes, [ - { - key: 'profiling.data.format', - value: { stringValue: 'pprof-gzip-base64' }, - }, - { key: 'profiling.data.type', value: { stringValue: 'allocation' } }, - { - key: 'com.splunk.sourcetype', - value: { stringValue: 'otel.profiling' }, - }, - { key: 'profiling.data.total.frame.count', value: { intValue: 3 } }, - ]); - }); - - exporter.sendHeapProfile(heapProfile); - }); - }); -}); diff --git a/test/profiling/http_exporter.test.ts b/test/profiling/http_exporter.test.ts new file mode 100644 index 00000000..7301fdbe --- /dev/null +++ b/test/profiling/http_exporter.test.ts @@ -0,0 +1,110 @@ +/* + * Copyright Splunk Inc. + * + * 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 + * + * http://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 { VERSION } from '@opentelemetry/core'; +import { Resource } from '@opentelemetry/resources'; +import { strict as assert } from 'assert'; +import { describe, it, mock } from 'node:test'; +import { OtlpHttpProfilingExporter } from '../../src/profiling/OtlpHttpProfilingExporter'; +import { cpuProfile, heapProfile } from './profiles'; +import { InMemoryLogRecordExporter } from '@opentelemetry/sdk-logs'; + +describe('profiling OTLP HTTP exporter', () => { + it('appends the logs path to the endpoint', () => { + const exporter = new OtlpHttpProfilingExporter({ + endpoint: 'http://localhost:4318', + callstackInterval: 1000, + resource: new Resource({}), + }); + + assert.strictEqual(exporter._endpoint, 'http://localhost:4318/v1/logs'); + }); + + it('does not append the logs path to the endpoint when one exists', () => { + const exporter = new OtlpHttpProfilingExporter({ + endpoint: 'http://abc:4200/v1/logs', + callstackInterval: 1000, + resource: new Resource({}), + }); + + assert.strictEqual(exporter._endpoint, 'http://abc:4200/v1/logs'); + }); + + it('attaches common attributes when exporting CPU profiles', async () => { + const exporter = new OtlpHttpProfilingExporter({ + endpoint: 'http://foobar:8181', + callstackInterval: 1000, + resource: new Resource({ xyz: 'foo' }), + }); + + const logExporter = new InMemoryLogRecordExporter(); + mock.method(exporter, '_getExporter', () => logExporter); + + await exporter.send(cpuProfile); + + const logs = logExporter.getFinishedLogRecords(); + + assert.strictEqual(logs.length, 1); + + const [log] = logs; + + assert.deepStrictEqual(log.resource.attributes, { + 'telemetry.sdk.language': 'node', + 'telemetry.sdk.version': VERSION, + xyz: 'foo', + }); + + assert.deepStrictEqual(log.attributes, { + 'profiling.data.format': 'pprof-gzip-base64', + 'profiling.data.type': 'cpu', + 'com.splunk.sourcetype': 'otel.profiling', + 'profiling.data.total.frame.count': 2, + }); + }); + + it('attaches common attributes when exporting heap profiles', async () => { + const exporter = new OtlpHttpProfilingExporter({ + endpoint: 'http://foobar:8181', + callstackInterval: 1000, + resource: new Resource({ xyz: 'foo' }), + }); + + const logExporter = new InMemoryLogRecordExporter(); + + mock.method(exporter, '_getExporter', () => logExporter); + + await exporter.sendHeapProfile(heapProfile); + + const logs = logExporter.getFinishedLogRecords(); + + assert.deepStrictEqual(logs.length, 1); + + const [log] = logs; + + assert.deepStrictEqual(log.resource.attributes, { + 'telemetry.sdk.language': 'node', + 'telemetry.sdk.version': VERSION, + xyz: 'foo', + }); + + assert.deepStrictEqual(log.attributes, { + 'profiling.data.format': 'pprof-gzip-base64', + 'profiling.data.type': 'allocation', + 'com.splunk.sourcetype': 'otel.profiling', + 'profiling.data.total.frame.count': 3, + }); + }); +}); diff --git a/test/profiling/profiling.test.ts b/test/profiling/profiling.test.ts index 50f49e20..713e8f95 100644 --- a/test/profiling/profiling.test.ts +++ b/test/profiling/profiling.test.ts @@ -61,7 +61,7 @@ describe('profiling', () => { assert.deepStrictEqual(defaultOtherAttrs, { serviceName: '@splunk/otel', - endpoint: 'http://localhost:4317', + endpoint: 'http://localhost:4318', callstackInterval: 1_000, collectionDuration: 30_000, exporterFactory: defaultExporterFactory, @@ -107,12 +107,12 @@ describe('profiling', () => { let sendCallCount = 0; const stacktracesReceived: ProfilingStacktrace[] = []; const exporter: ProfilingExporter = { - send(cpuProfile: CpuProfile) { + async send(cpuProfile: CpuProfile) { const { stacktraces } = cpuProfile; sendCallCount += 1; stacktracesReceived.push(...stacktraces); }, - sendHeapProfile(_profile: HeapProfile) {}, + async sendHeapProfile(_profile: HeapProfile) {}, }; // enabling tracing is required for span information to be caught diff --git a/test/runner.js b/test/runner.js index bf98a478..51e93baa 100644 --- a/test/runner.js +++ b/test/runner.js @@ -21,7 +21,22 @@ function findTestFiles(dir, fileList = []) { } // Find all test files in the ./test directory -const testFiles = findTestFiles(__dirname); +let testFiles = findTestFiles(__dirname); + +if (process.argv.length > 2) { + const patterns = process.argv.slice(2); + + testFiles = testFiles.filter((path) => { + for (const pattern of patterns) { + if (path.includes(pattern)) { + return true; + } + } + + return false; + }); +} + const args = [ '--require', diff --git a/test/tracing/common.ts b/test/tracing/common.ts index ab337ffc..39fbc2e9 100644 --- a/test/tracing/common.ts +++ b/test/tracing/common.ts @@ -18,7 +18,7 @@ import { strict as assert } from 'assert'; import { mock } from 'node:test'; import { trace } from '@opentelemetry/api'; -import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; import { ProxyTracerProvider } from '@opentelemetry/api'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; @@ -59,7 +59,9 @@ export function assertTracingPipeline( if (accessToken) { assert.equal( - exporter['_transport']['_parameters']['metadata']().get('x-sf-token')[0], + exporter['_transport']['_transport']['_parameters']['headers'][ + 'X-SF-TOKEN' + ], accessToken ); } diff --git a/test/tracing/default_setup.test.ts b/test/tracing/default_setup.test.ts index a2f81522..33bc6882 100644 --- a/test/tracing/default_setup.test.ts +++ b/test/tracing/default_setup.test.ts @@ -24,6 +24,10 @@ test('Tracing: set up with defaults', async () => { const mocks = setupMocks(); const { tracingOptions } = parseOptionsAndConfigureInstrumentations(); startTracing(tracingOptions); - assertTracingPipeline(mocks, 'localhost:4317', '@splunk/otel'); + assertTracingPipeline( + mocks, + 'http://localhost:4318/v1/traces', + '@splunk/otel' + ); await stopTracing(); }); diff --git a/test/tracing/env_options.test.ts b/test/tracing/env_options.test.ts index cef61555..c42cdfea 100644 --- a/test/tracing/env_options.test.ts +++ b/test/tracing/env_options.test.ts @@ -33,6 +33,6 @@ test('Tracing: set up with env options', async () => { const { tracingOptions } = parseOptionsAndConfigureInstrumentations(); startTracing(tracingOptions); - assertTracingPipeline(mocks, url, serviceName, accessToken); + assertTracingPipeline(mocks, `${url}/v1/traces`, serviceName, accessToken); await stopTracing(); }); diff --git a/test/utils.ts b/test/utils.ts index d8720dca..e4572432 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -122,7 +122,7 @@ export function calledWithExactly(mocked: any, expected: any) { assert(match, `Expected call with: ${JSON.stringify(expected)} not found`); } -export function calledOnceWithMatch(mocked: any, match: Object) { +export function calledOnceWithMatch(mocked: any, match: object) { assert.strictEqual( mocked.mock.calls.length, 1,