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
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,15 @@ describe('encodeAttributes', () => {
);
});

it('does not modify original object when attribute limit is reached', () => {
it('does not modify original object when encoding many attributes', () => {
const input: Record<string, string> = {};
for (let i = 0; i < 200; i++) {
input[`k${i}`] = `v${i}`;
}
const snapshot = { ...input };

const result = encodeAttributes(input);
expect(Object.keys(result)).toHaveLength(128);
expect(Object.keys(result)).toHaveLength(200);
Comment thread
marco-saia-datadog marked this conversation as resolved.
expect(input).toEqual(snapshot); // original still has 200 keys
});

Expand Down Expand Up @@ -380,31 +380,30 @@ describe('encodeAttributes', () => {
);
});

it('drops attributes after reaching the 128 limit and warns once', () => {
// Prepare 200 simple attributes — max=128
it('warns once when the 256 attribute limit is exceeded, but does not drop attributes', () => {
// Prepare 300 simple attributes — exceeds max=256
const input: Record<string, number> = {};
for (let i = 0; i < 200; i++) {
for (let i = 0; i < 300; i++) {
input[`key${i}`] = i;
}

const result = encodeAttributes(input);

// Check that only 128 attributes remain
expect(Object.keys(result)).toHaveLength(128);
// All 300 attributes are preserved — no hard cap
expect(Object.keys(result)).toHaveLength(300);

// Check the first ones are preserved
// All attributes are present
expect(result).toHaveProperty('key0', 0);
expect(result).toHaveProperty('key127', 127);

// Check later ones were dropped
expect(result).not.toHaveProperty('key128');
expect(result).toHaveProperty('key255', 255);
expect(result).toHaveProperty('key256', 256);
expect(result).toHaveProperty('key299', 299);

// Check that a warning was shown at least once
// A single warning is emitted when the limit is exceeded
expect(warn).toHaveBeenCalledWith(
expect.stringContaining('Attribute limit')
);

// Check there is only one "limit reached" warning (even if multiple attributes were dropped)
// Exactly one "limit exceeded" warning (not one per extra attribute)
const limitWarnings = (warn as jest.Mock).mock.calls.filter(([msg]) =>
msg.includes('Attribute limit')
);
Expand Down
27 changes: 9 additions & 18 deletions packages/core/src/sdk/AttributesEncoding/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import type { AttributeEncoder, Encodable } from './types';
import { formatPathForLog, isPlainObject, warn } from './utils';

const MAX_ATTRIBUTES = 128;
const MAX_ATTRIBUTES = 256;

export interface EncodeContext {
numOfAttributes: number;
Expand Down Expand Up @@ -152,25 +152,16 @@ function addEncodedAttribute(
value: Encodable,
context: EncodeContext
): void {
if (context.numOfAttributes >= MAX_ATTRIBUTES) {
// Only warn once to avoid log spam
if (!context.limitReachedWarned) {
warn(
`Attribute limit of ${MAX_ATTRIBUTES} reached; further attributes will be dropped.`
);
context.limitReachedWarned = true;
}
out[path.join('.')] = value;
context.numOfAttributes++;

// Optional: warn for specific dropped attribute (if desired)
if (
context.numOfAttributes > MAX_ATTRIBUTES &&
!context.limitReachedWarned
) {
warn(
`Dropped attribute at '${formatPathForLog(
path
)}' because limit of ${MAX_ATTRIBUTES} attributes was reached. All further attributes will be dropped.`
`Attribute limit of ${MAX_ATTRIBUTES} exceeded; the backend may drop some attributes.`
);

return;
context.limitReachedWarned = true;
}

out[path.join('.')] = value;
context.numOfAttributes++;
}