Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/moody-avocados-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperdx/node-opentelemetry': patch
---

Fix issue where OTEL_EXPORTER_OTLP_HEADERS are not passed to health check endpoints
71 changes: 71 additions & 0 deletions packages/node-opentelemetry/src/__tests__/parseHeaders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { parseOtlpHeaders } from '../utils';

describe('Parse OTLP Headers', () => {
it('should return an empty object when no headers string is provided', () => {
expect(parseOtlpHeaders()).toEqual({});
});

it('should correctly parse a single header', () => {
expect(parseOtlpHeaders('key1=value1')).toEqual({ key1: 'value1' });
});

it('should correctly parse multiple headers', () => {
expect(parseOtlpHeaders('key1=value1,key2=value2')).toEqual({
key1: 'value1',
key2: 'value2',
});
});

it('should handle spaces around keys and values', () => {
expect(parseOtlpHeaders(' key1 = value1 , key2 = value2 ')).toEqual({
key1: 'value1',
key2: 'value2',
});
});

it('should ignore malformed headers without "="', () => {
expect(parseOtlpHeaders('key1=value1,malformedHeader,key2=value2')).toEqual(
{
key1: 'value1',
key2: 'value2',
},
);
});

it('should handle empty values', () => {
expect(parseOtlpHeaders('key1=,key2=value2')).toEqual({
key1: '',
key2: 'value2',
});
});

it('should handle special characters in values', () => {
expect(
parseOtlpHeaders(
'Authorization=Bearer token123,Content-Type=application/json',
),
).toEqual({
Authorization: 'Bearer token123',
'Content-Type': 'application/json',
});
});

it('should handle trailing comma', () => {
expect(parseOtlpHeaders('key1=value1,')).toEqual({
key1: 'value1',
});
});

it('should handle leading comma', () => {
expect(parseOtlpHeaders(',key1=value1')).toEqual({
key1: 'value1',
});
});

it('should handle multiple consecutive commas', () => {
expect(parseOtlpHeaders('key1=value1,,key2=value2')).toEqual({
key1: 'value1',
key2: 'value2',
});
});
});
20 changes: 11 additions & 9 deletions packages/node-opentelemetry/src/otel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { getHyperDXMetricReader } from './metrics';
import { MutableAsyncLocalStorageContextManager } from './MutableAsyncLocalStorageContextManager';
import { Logger as OtelLogger } from './otel-logger';
import HyperDXSpanProcessor from './spanProcessor';
import { parseOtlpHeaders } from './utils';

const UI_LOG_PREFIX = '[⚡HyperDX]';

Expand Down Expand Up @@ -215,6 +216,13 @@ export const initSDK = (config: SDKConfig) => {
});
ui.succeed('Set default otel envs');

// Parse OTLP headers from environment variable
const otlpHeaders = parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS);
const healthCheckHeaders = {
'Content-Type': 'application/json',
...otlpHeaders,
};

const stopOnTerminationSignals =
config.stopOnTerminationSignals ??
DEFAULT_HDX_NODE_STOP_ON_TERMINATION_SIGNALS; // Stop by default
Expand Down Expand Up @@ -244,23 +252,17 @@ export const initSDK = (config: SDKConfig) => {
Promise.all([
healthCheckUrl(ui, DEFAULT_OTEL_TRACES_EXPORTER_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: healthCheckHeaders,
body: JSON.stringify({}),
}),
healthCheckUrl(ui, _logger.getExporterUrl(), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: healthCheckHeaders,
body: JSON.stringify({}),
}),
healthCheckUrl(ui, DEFAULT_OTEL_METRICS_EXPORTER_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: healthCheckHeaders,
body: JSON.stringify({}),
}),
]);
Expand Down
37 changes: 37 additions & 0 deletions packages/node-opentelemetry/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,40 @@ export const stringToBoolean = (stringValue: string | undefined) => {
return undefined;
}
};

/**
* Parses OTEL_EXPORTER_OTLP_HEADERS environment variable into a structured headers object.
* Format: "key1=value1,key2=value2" -> { key1: "value1", key2: "value2" }
*/
export const parseOtlpHeaders = (
headersString?: string,
): Record<string, string> => {
if (!headersString) {
return {};
}

const headers: Record<string, string> = {};
const pairs = headersString.split(',');

for (const pair of pairs) {
const trimmedPair = pair.trim();
if (!trimmedPair) {
continue;
}

const equalIndex = trimmedPair.indexOf('=');
if (equalIndex === -1) {
// Skip malformed pairs without '='
continue;
}

const key = trimmedPair.substring(0, equalIndex).trim();
const value = trimmedPair.substring(equalIndex + 1).trim();

if (key) {
headers[key] = value;
}
}

return headers;
};