diff --git a/CHANGELOG.md b/CHANGELOG.md index 8328794952e..507cc3b7c2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,12 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2 ### :rocket: Features -* feat(sdk-logs): implement log creation metrics [#6433](https://github.com/open-telemetry/opentelemetry-js/pull/6433) @anuraaga +* feat(sdk-logs): implement log creation metrics [#6433](https://github.com/open-telemetry/opentelemetry-js/pull/6433) @anuraaga ### :bug: Bug Fixes -fix(opentelemetry-resources): do not discard OTEL_RESOURCE_ATTRIBUTES when it contains empty kv pairs +* fix(opentelemetry-resources): do not discard OTEL_RESOURCE_ATTRIBUTES when it contains empty kv pairs +* fix(otlp-transformer): add check for possible unsafe json parse [#6588](https://github.com/open-telemetry/opentelemetry-js/pull/6588) @maryliag ### :books: Documentation diff --git a/experimental/packages/otlp-transformer/src/logs/json/logs.ts b/experimental/packages/otlp-transformer/src/logs/json/logs.ts index 4ecc1c12652..b9994535888 100644 --- a/experimental/packages/otlp-transformer/src/logs/json/logs.ts +++ b/experimental/packages/otlp-transformer/src/logs/json/logs.ts @@ -7,6 +7,7 @@ import type { ReadableLogRecord } from '@opentelemetry/sdk-logs'; import { createExportLogsServiceRequest } from '../internal'; import type { IExportLogsServiceResponse } from '../export-response'; import { JSON_ENCODER } from '../../common/utils'; +import { diag } from '@opentelemetry/api'; /* * @experimental this serializer may receive breaking changes in minor versions, pin this package's version when using this constant @@ -25,6 +26,13 @@ export const JsonLogsSerializer: ISerializer< return {}; } const decoder = new TextDecoder(); - return JSON.parse(decoder.decode(arg)) as IExportLogsServiceResponse; + try { + return JSON.parse(decoder.decode(arg)) as IExportLogsServiceResponse; + } catch (err) { + diag.warn( + `Failed to parse logs export response: ${err.message}. Returning empty response` + ); + return {}; + } }, }; diff --git a/experimental/packages/otlp-transformer/src/metrics/json/metrics.ts b/experimental/packages/otlp-transformer/src/metrics/json/metrics.ts index 409c675bb72..9c481b37de2 100644 --- a/experimental/packages/otlp-transformer/src/metrics/json/metrics.ts +++ b/experimental/packages/otlp-transformer/src/metrics/json/metrics.ts @@ -7,6 +7,7 @@ import type { ResourceMetrics } from '@opentelemetry/sdk-metrics'; import { createExportMetricsServiceRequest } from '../internal'; import type { IExportMetricsServiceResponse } from '../export-response'; import { JSON_ENCODER } from '../../common/utils'; +import { diag } from '@opentelemetry/api'; export const JsonMetricsSerializer: ISerializer< ResourceMetrics, @@ -22,6 +23,13 @@ export const JsonMetricsSerializer: ISerializer< return {}; } const decoder = new TextDecoder(); - return JSON.parse(decoder.decode(arg)) as IExportMetricsServiceResponse; + try { + return JSON.parse(decoder.decode(arg)) as IExportMetricsServiceResponse; + } catch (err) { + diag.warn( + `Failed to parse metrics export response: ${err.message}. Returning empty response` + ); + return {}; + } }, }; diff --git a/experimental/packages/otlp-transformer/src/trace/json/trace.ts b/experimental/packages/otlp-transformer/src/trace/json/trace.ts index c6a40d552c2..d6c153e9735 100644 --- a/experimental/packages/otlp-transformer/src/trace/json/trace.ts +++ b/experimental/packages/otlp-transformer/src/trace/json/trace.ts @@ -7,6 +7,7 @@ import type { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import type { IExportTraceServiceResponse } from '../export-response'; import { createExportTraceServiceRequest } from '../internal'; import { JSON_ENCODER } from '../../common/utils'; +import { diag } from '@opentelemetry/api'; export const JsonTraceSerializer: ISerializer< ReadableSpan[], @@ -22,6 +23,13 @@ export const JsonTraceSerializer: ISerializer< return {}; } const decoder = new TextDecoder(); - return JSON.parse(decoder.decode(arg)) as IExportTraceServiceResponse; + try { + return JSON.parse(decoder.decode(arg)) as IExportTraceServiceResponse; + } catch (err) { + diag.warn( + `Failed to parse trace export response: ${err.message}. Returning empty response` + ); + return {}; + } }, }; diff --git a/experimental/packages/otlp-transformer/test/logs.test.ts b/experimental/packages/otlp-transformer/test/logs.test.ts index f7db2c7638d..88f0a1f95b5 100644 --- a/experimental/packages/otlp-transformer/test/logs.test.ts +++ b/experimental/packages/otlp-transformer/test/logs.test.ts @@ -503,6 +503,21 @@ describe('Logs', () => { ); }); + it('deserializes a malformed response', () => { + const malformedResponse = + '{ "partialSuccess": { "errorMessage": foo, "rejectedLogRecords": 1, }'; + const encoder = new TextEncoder(); + const encodedResponse = encoder.encode(malformedResponse); + const deserializedResponse = + JsonLogsSerializer.deserializeResponse(encodedResponse); + + assert.deepEqual( + deserializedResponse, + {}, + 'Malformed response should result in an empty object being returned' + ); + }); + it('does not throw when deserializing an empty response', () => { assert.doesNotThrow(() => JsonLogsSerializer.deserializeResponse(new Uint8Array([])) diff --git a/experimental/packages/otlp-transformer/test/metrics.test.ts b/experimental/packages/otlp-transformer/test/metrics.test.ts index 04e39b88405..5e6e95658a8 100644 --- a/experimental/packages/otlp-transformer/test/metrics.test.ts +++ b/experimental/packages/otlp-transformer/test/metrics.test.ts @@ -984,6 +984,21 @@ describe('Metrics', () => { ); }); + it('deserializes a malformed response', () => { + const malformedResponse = + '{ "partialSuccess": { "errorMessage": foo, "rejectedLogRecords": 1, }'; + const encoder = new TextEncoder(); + const encodedResponse = encoder.encode(malformedResponse); + const deserializedResponse = + JsonMetricsSerializer.deserializeResponse(encodedResponse); + + assert.deepEqual( + deserializedResponse, + {}, + 'Malformed response should result in an empty object being returned' + ); + }); + it('does not throw when deserializing an empty response', () => { assert.doesNotThrow(() => JsonMetricsSerializer.deserializeResponse(new Uint8Array([])) diff --git a/experimental/packages/otlp-transformer/test/trace.test.ts b/experimental/packages/otlp-transformer/test/trace.test.ts index ab45295f41d..7cba28820e5 100644 --- a/experimental/packages/otlp-transformer/test/trace.test.ts +++ b/experimental/packages/otlp-transformer/test/trace.test.ts @@ -586,6 +586,21 @@ describe('Trace', () => { ); }); + it('deserializes a malformed response', () => { + const malformedResponse = + '{ "partialSuccess": { "errorMessage": foo, "rejectedLogRecords": 1, }'; + const encoder = new TextEncoder(); + const encodedResponse = encoder.encode(malformedResponse); + const deserializedResponse = + JsonTraceSerializer.deserializeResponse(encodedResponse); + + assert.deepEqual( + deserializedResponse, + {}, + 'Malformed response should result in an empty object being returned' + ); + }); + it('does not throw when deserializing an empty response', () => { assert.doesNotThrow(() => JsonTraceSerializer.deserializeResponse(new Uint8Array([]))