@posthog/core 1.48.6 → 1.48.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -74,7 +74,7 @@ class PostHogLogs {
74
74
  if (null === filtered) return;
75
75
  if (!filtered.body) return void this._logger.info("Log was rejected in beforeSend function");
76
76
  if (!this._checkRateLimit()) return;
77
- const record = (0, external_logs_utils_js_namespaceObject.buildOtlpLogRecord)(filtered, this._getContext());
77
+ const record = (0, external_logs_utils_js_namespaceObject.buildOtlpLogRecord)(filtered, this._getContext(), this._logger);
78
78
  const entry = {
79
79
  record
80
80
  };
@@ -46,7 +46,7 @@ class PostHogLogs {
46
46
  if (null === filtered) return;
47
47
  if (!filtered.body) return void this._logger.info("Log was rejected in beforeSend function");
48
48
  if (!this._checkRateLimit()) return;
49
- const record = buildOtlpLogRecord(filtered, this._getContext());
49
+ const record = buildOtlpLogRecord(filtered, this._getContext(), this._logger);
50
50
  const entry = {
51
51
  record
52
52
  };
@@ -1,20 +1,11 @@
1
1
  import type { CaptureLogOptions, LogAttributeValue, LogSeverityLevel, OtlpAnyValue, OtlpKeyValue, OtlpLogRecord, OtlpLogsPayload, OtlpSeverityText } from '@posthog/types';
2
+ import type { Logger } from '../types';
2
3
  import type { LogSdkContext, ResolvedPostHogLogsConfig } from './types';
3
4
  export declare function getOtlpSeverityText(level: LogSeverityLevel): OtlpSeverityText;
4
5
  export declare function getOtlpSeverityNumber(level: LogSeverityLevel): number;
5
- export declare function toOtlpAnyValue(value: LogAttributeValue): OtlpAnyValue;
6
- export declare function toOtlpKeyValueList(attrs: Record<string, LogAttributeValue>): OtlpKeyValue[];
7
- /**
8
- * Builds a single OTLP log record.
9
- *
10
- * Auto-attribute population is shape-driven: any field present on `sdkContext`
11
- * is emitted as the corresponding attribute. Each SDK populates only the
12
- * fields that apply to it (browser fills `currentUrl`; mobile fills
13
- * `screenName` / `appState`), so a missing field never adds a stray attribute.
14
- *
15
- * User-provided `options.attributes` always wins on conflicts.
16
- */
17
- export declare function buildOtlpLogRecord(options: CaptureLogOptions, sdkContext: LogSdkContext): OtlpLogRecord;
6
+ export declare function toOtlpAnyValue(value: LogAttributeValue, logger?: Logger): OtlpAnyValue;
7
+ export declare function toOtlpKeyValueList(attrs: Record<string, LogAttributeValue>, logger?: Logger): OtlpKeyValue[];
8
+ export declare function buildOtlpLogRecord(options: CaptureLogOptions, sdkContext: LogSdkContext, logger?: Logger): OtlpLogRecord;
18
9
  /**
19
10
  * OTLP resource attributes for every batch, shared by the core flush path and
20
11
  * SDK-specific paths that bypass it (e.g. the browser's synchronous sendBeacon
@@ -1 +1 @@
1
- {"version":3,"file":"logs-utils.d.ts","sourceRoot":"","sources":["../../src/logs/logs-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,eAAe,EAEf,gBAAgB,EACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AAkBvE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,CAE7E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAErE;AAMD,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAiCrE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,YAAY,EAAE,CAU3F;AAiBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,GAAG,aAAa,CA4DvG;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,yBAAyB,EACjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CASnC;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,aAAa,EAAE,EAC3B,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,eAAe,CAcjB"}
1
+ {"version":3,"file":"logs-utils.d.ts","sourceRoot":"","sources":["../../src/logs/logs-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,eAAe,EAEf,gBAAgB,EACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AA4BvE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,CAE7E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAErE;AAqBD,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAQtF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAM5G;AAmMD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,aAAa,EACzB,MAAM,CAAC,EAAE,MAAM,GACd,aAAa,CA+Ef;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,yBAAyB,EACjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CASnC;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,aAAa,EAAE,EAC3B,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,eAAe,CAcjB"}
@@ -33,6 +33,7 @@ __webpack_require__.d(__webpack_exports__, {
33
33
  toOtlpAnyValue: ()=>toOtlpAnyValue
34
34
  });
35
35
  const index_js_namespaceObject = require("../utils/index.js");
36
+ const json_utils_js_namespaceObject = require("../utils/json-utils.js");
36
37
  const OTLP_SEVERITY_MAP = {
37
38
  trace: {
38
39
  text: 'TRACE',
@@ -66,7 +67,35 @@ function getOtlpSeverityText(level) {
66
67
  function getOtlpSeverityNumber(level) {
67
68
  return (OTLP_SEVERITY_MAP[level] || DEFAULT_OTLP_SEVERITY).number;
68
69
  }
69
- function toOtlpAnyValue(value) {
70
+ const INT64_RANGE_LIMIT = 9223372036854775808;
71
+ const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
72
+ function newState() {
73
+ return {
74
+ ancestors: new WeakSet(),
75
+ remainingNodes: json_utils_js_namespaceObject.MAX_JSON_SAFE_VALUE_NODES
76
+ };
77
+ }
78
+ function toOtlpAnyValue(value, logger) {
79
+ try {
80
+ return encodeAnyValue(value, logger, newState(), 0);
81
+ } catch {
82
+ return {
83
+ stringValue: json_utils_js_namespaceObject.UNSERIALIZABLE_VALUE
84
+ };
85
+ }
86
+ }
87
+ function toOtlpKeyValueList(attrs, logger) {
88
+ try {
89
+ return encodeKeyValueList(attrs, logger, newState(), 0);
90
+ } catch {
91
+ return [];
92
+ }
93
+ }
94
+ function encodeAnyValue(value, logger, state, depth) {
95
+ if (state.remainingNodes <= 0) return {
96
+ stringValue: json_utils_js_namespaceObject.TRUNCATED_VALUE
97
+ };
98
+ state.remainingNodes--;
70
99
  if ((0, index_js_namespaceObject.isBoolean)(value)) return {
71
100
  boolValue: value
72
101
  };
@@ -74,46 +103,129 @@ function toOtlpAnyValue(value) {
74
103
  if (!Number.isFinite(value)) return {
75
104
  stringValue: String(value)
76
105
  };
77
- if (Number.isInteger(value)) return {
78
- intValue: value
79
- };
106
+ if (Number.isInteger(value)) {
107
+ if (Number.isSafeInteger(value)) return {
108
+ intValue: String(value)
109
+ };
110
+ if ('undefined' == typeof BigInt) return {
111
+ stringValue: String(value)
112
+ };
113
+ const decimal = BigInt(value).toString();
114
+ if (value >= INT64_RANGE_LIMIT || value < -INT64_RANGE_LIMIT) {
115
+ logger?.debug(`Attribute ${decimal} is outside the int64 range; encoding it as a string`);
116
+ return {
117
+ stringValue: decimal
118
+ };
119
+ }
120
+ return {
121
+ intValue: decimal
122
+ };
123
+ }
80
124
  return {
81
125
  doubleValue: value
82
126
  };
83
127
  }
84
128
  if ('string' == typeof value) return {
85
- stringValue: value
129
+ stringValue: (0, json_utils_js_namespaceObject.sanitizeString)(value)
86
130
  };
87
- if ((0, index_js_namespaceObject.isArray)(value)) return {
88
- arrayValue: {
89
- values: value.map((v)=>toOtlpAnyValue(v))
90
- }
131
+ if ('function' == typeof value) return {
132
+ stringValue: json_utils_js_namespaceObject.FUNCTION_VALUE
91
133
  };
92
- try {
93
- return {
94
- stringValue: JSON.stringify(value)
134
+ if ('symbol' == typeof value) return {
135
+ stringValue: String(value)
136
+ };
137
+ if ('object' == typeof value && null !== value) {
138
+ if (state.ancestors.has(value)) return {
139
+ stringValue: json_utils_js_namespaceObject.CIRCULAR_VALUE
95
140
  };
96
- } catch {
97
- return {
98
- stringValue: String(value)
141
+ if (depth >= json_utils_js_namespaceObject.MAX_JSON_SAFE_VALUE_DEPTH) return {
142
+ stringValue: json_utils_js_namespaceObject.TRUNCATED_VALUE
99
143
  };
144
+ if (value instanceof Date) {
145
+ const time = value.getTime();
146
+ const iso = Number.isFinite(time) ? value.toISOString() : String(value);
147
+ return {
148
+ stringValue: 'string' == typeof iso ? (0, json_utils_js_namespaceObject.sanitizeString)(iso) : String(iso)
149
+ };
150
+ }
151
+ state.ancestors.add(value);
152
+ try {
153
+ try {
154
+ const toJSON = value.toJSON;
155
+ if ('function' == typeof toJSON) return encodeAnyValue(toJSON.call(value), logger, state, depth + 1);
156
+ } catch {}
157
+ if ((0, index_js_namespaceObject.isArray)(value)) return {
158
+ arrayValue: {
159
+ values: encodeArrayValues(value, logger, state, depth + 1)
160
+ }
161
+ };
162
+ return {
163
+ kvlistValue: {
164
+ values: encodeKeyValueList(value, logger, state, depth + 1)
165
+ }
166
+ };
167
+ } finally{
168
+ state.ancestors.delete(value);
169
+ }
100
170
  }
171
+ return {
172
+ stringValue: (0, json_utils_js_namespaceObject.sanitizeString)(String(value))
173
+ };
101
174
  }
102
- function toOtlpKeyValueList(attrs) {
175
+ function encodeArrayValues(values, logger, state, depth) {
103
176
  const result = [];
104
- for(const key in attrs){
105
- const value = attrs[key];
106
- if (!((0, index_js_namespaceObject.isNull)(value) || (0, index_js_namespaceObject.isUndefined)(value))) result.push({
107
- key,
108
- value: toOtlpAnyValue(value)
177
+ const itemCount = Math.min(values.length, json_utils_js_namespaceObject.MAX_JSON_SAFE_VALUE_ITEMS);
178
+ let index = 0;
179
+ for(; index < itemCount && state.remainingNodes > 0; index++)try {
180
+ const element = index in values ? values[index] : void 0;
181
+ if ((0, index_js_namespaceObject.isNullish)(element)) continue;
182
+ result.push(encodeAnyValue(element, logger, state, depth));
183
+ } catch {
184
+ result.push({
185
+ stringValue: json_utils_js_namespaceObject.UNSERIALIZABLE_VALUE
109
186
  });
110
187
  }
188
+ if (values.length > index) result.push({
189
+ stringValue: json_utils_js_namespaceObject.TRUNCATED_VALUE
190
+ });
191
+ return result;
192
+ }
193
+ function encodeKeyValueList(attrs, logger, state, depth) {
194
+ const result = [];
195
+ for(const key in attrs)if (propertyIsEnumerable.call(attrs, key)) {
196
+ if (result.length >= json_utils_js_namespaceObject.MAX_JSON_SAFE_VALUE_ITEMS || state.remainingNodes <= 0) {
197
+ logger?.debug('Attributes truncated: the value exceeds the OTLP encoder budget');
198
+ break;
199
+ }
200
+ try {
201
+ const value = attrs[key];
202
+ if ((0, index_js_namespaceObject.isNull)(value) || (0, index_js_namespaceObject.isUndefined)(value)) continue;
203
+ result.push({
204
+ key: (0, json_utils_js_namespaceObject.sanitizeString)(key),
205
+ value: encodeAnyValue(value, logger, state, depth)
206
+ });
207
+ } catch {
208
+ result.push({
209
+ key: (0, json_utils_js_namespaceObject.sanitizeString)(key),
210
+ value: {
211
+ stringValue: json_utils_js_namespaceObject.UNSERIALIZABLE_VALUE
212
+ }
213
+ });
214
+ }
215
+ }
111
216
  return result;
112
217
  }
113
218
  function timestampToUnixNano() {
114
219
  return String(Date.now()) + '000000';
115
220
  }
116
- function buildOtlpLogRecord(options, sdkContext) {
221
+ function encodeBody(body) {
222
+ try {
223
+ return (0, json_utils_js_namespaceObject.sanitizeString)(String(body));
224
+ } catch {
225
+ return json_utils_js_namespaceObject.UNSERIALIZABLE_VALUE;
226
+ }
227
+ }
228
+ function buildOtlpLogRecord(options, sdkContext, logger) {
117
229
  const level = options.level || 'info';
118
230
  const { text: severityText, number: severityNumber } = OTLP_SEVERITY_MAP[level] || DEFAULT_OTLP_SEVERITY;
119
231
  const now = timestampToUnixNano();
@@ -128,18 +240,40 @@ function buildOtlpLogRecord(options, sdkContext) {
128
240
  if (sdkContext.appState) autoAttributes['app.state'] = sdkContext.appState;
129
241
  if (sdkContext.activeFeatureFlags && sdkContext.activeFeatureFlags.length > 0) autoAttributes.feature_flags = sdkContext.activeFeatureFlags;
130
242
  const mergedAttributes = {
131
- ...autoAttributes,
132
- ...options.attributes || {}
243
+ ...autoAttributes
133
244
  };
245
+ const userAttributes = options.attributes;
246
+ if (userAttributes) {
247
+ let keys = [];
248
+ try {
249
+ keys = Object.keys(userAttributes);
250
+ } catch {
251
+ keys = [];
252
+ }
253
+ for (const key of keys){
254
+ let value;
255
+ try {
256
+ value = userAttributes[key];
257
+ } catch {
258
+ value = json_utils_js_namespaceObject.UNSERIALIZABLE_VALUE;
259
+ }
260
+ Object.defineProperty(mergedAttributes, key, {
261
+ value,
262
+ enumerable: true,
263
+ writable: true,
264
+ configurable: true
265
+ });
266
+ }
267
+ }
134
268
  const record = {
135
269
  timeUnixNano: now,
136
270
  observedTimeUnixNano: now,
137
271
  severityNumber,
138
272
  severityText,
139
273
  body: {
140
- stringValue: options.body
274
+ stringValue: encodeBody(options.body)
141
275
  },
142
- attributes: toOtlpKeyValueList(mergedAttributes)
276
+ attributes: toOtlpKeyValueList(mergedAttributes, logger)
143
277
  };
144
278
  if (options.trace_id) record.traceId = options.trace_id;
145
279
  if (options.span_id) record.spanId = options.span_id;
@@ -1,4 +1,5 @@
1
1
  import { isArray, isBoolean, isNull, isNullish, isUndefined } from "../utils/index.mjs";
2
+ import { CIRCULAR_VALUE, FUNCTION_VALUE, MAX_JSON_SAFE_VALUE_DEPTH, MAX_JSON_SAFE_VALUE_ITEMS, MAX_JSON_SAFE_VALUE_NODES, TRUNCATED_VALUE, UNSERIALIZABLE_VALUE, sanitizeString } from "../utils/json-utils.mjs";
2
3
  const OTLP_SEVERITY_MAP = {
3
4
  trace: {
4
5
  text: 'TRACE',
@@ -32,7 +33,35 @@ function getOtlpSeverityText(level) {
32
33
  function getOtlpSeverityNumber(level) {
33
34
  return (OTLP_SEVERITY_MAP[level] || DEFAULT_OTLP_SEVERITY).number;
34
35
  }
35
- function toOtlpAnyValue(value) {
36
+ const INT64_RANGE_LIMIT = 9223372036854775808;
37
+ const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
38
+ function newState() {
39
+ return {
40
+ ancestors: new WeakSet(),
41
+ remainingNodes: MAX_JSON_SAFE_VALUE_NODES
42
+ };
43
+ }
44
+ function toOtlpAnyValue(value, logger) {
45
+ try {
46
+ return encodeAnyValue(value, logger, newState(), 0);
47
+ } catch {
48
+ return {
49
+ stringValue: UNSERIALIZABLE_VALUE
50
+ };
51
+ }
52
+ }
53
+ function toOtlpKeyValueList(attrs, logger) {
54
+ try {
55
+ return encodeKeyValueList(attrs, logger, newState(), 0);
56
+ } catch {
57
+ return [];
58
+ }
59
+ }
60
+ function encodeAnyValue(value, logger, state, depth) {
61
+ if (state.remainingNodes <= 0) return {
62
+ stringValue: TRUNCATED_VALUE
63
+ };
64
+ state.remainingNodes--;
36
65
  if (isBoolean(value)) return {
37
66
  boolValue: value
38
67
  };
@@ -40,46 +69,129 @@ function toOtlpAnyValue(value) {
40
69
  if (!Number.isFinite(value)) return {
41
70
  stringValue: String(value)
42
71
  };
43
- if (Number.isInteger(value)) return {
44
- intValue: value
45
- };
72
+ if (Number.isInteger(value)) {
73
+ if (Number.isSafeInteger(value)) return {
74
+ intValue: String(value)
75
+ };
76
+ if ('undefined' == typeof BigInt) return {
77
+ stringValue: String(value)
78
+ };
79
+ const decimal = BigInt(value).toString();
80
+ if (value >= INT64_RANGE_LIMIT || value < -INT64_RANGE_LIMIT) {
81
+ logger?.debug(`Attribute ${decimal} is outside the int64 range; encoding it as a string`);
82
+ return {
83
+ stringValue: decimal
84
+ };
85
+ }
86
+ return {
87
+ intValue: decimal
88
+ };
89
+ }
46
90
  return {
47
91
  doubleValue: value
48
92
  };
49
93
  }
50
94
  if ('string' == typeof value) return {
51
- stringValue: value
95
+ stringValue: sanitizeString(value)
52
96
  };
53
- if (isArray(value)) return {
54
- arrayValue: {
55
- values: value.map((v)=>toOtlpAnyValue(v))
56
- }
97
+ if ('function' == typeof value) return {
98
+ stringValue: FUNCTION_VALUE
57
99
  };
58
- try {
59
- return {
60
- stringValue: JSON.stringify(value)
100
+ if ('symbol' == typeof value) return {
101
+ stringValue: String(value)
102
+ };
103
+ if ('object' == typeof value && null !== value) {
104
+ if (state.ancestors.has(value)) return {
105
+ stringValue: CIRCULAR_VALUE
61
106
  };
62
- } catch {
63
- return {
64
- stringValue: String(value)
107
+ if (depth >= MAX_JSON_SAFE_VALUE_DEPTH) return {
108
+ stringValue: TRUNCATED_VALUE
65
109
  };
110
+ if (value instanceof Date) {
111
+ const time = value.getTime();
112
+ const iso = Number.isFinite(time) ? value.toISOString() : String(value);
113
+ return {
114
+ stringValue: 'string' == typeof iso ? sanitizeString(iso) : String(iso)
115
+ };
116
+ }
117
+ state.ancestors.add(value);
118
+ try {
119
+ try {
120
+ const toJSON = value.toJSON;
121
+ if ('function' == typeof toJSON) return encodeAnyValue(toJSON.call(value), logger, state, depth + 1);
122
+ } catch {}
123
+ if (isArray(value)) return {
124
+ arrayValue: {
125
+ values: encodeArrayValues(value, logger, state, depth + 1)
126
+ }
127
+ };
128
+ return {
129
+ kvlistValue: {
130
+ values: encodeKeyValueList(value, logger, state, depth + 1)
131
+ }
132
+ };
133
+ } finally{
134
+ state.ancestors.delete(value);
135
+ }
66
136
  }
137
+ return {
138
+ stringValue: sanitizeString(String(value))
139
+ };
67
140
  }
68
- function toOtlpKeyValueList(attrs) {
141
+ function encodeArrayValues(values, logger, state, depth) {
69
142
  const result = [];
70
- for(const key in attrs){
71
- const value = attrs[key];
72
- if (!(isNull(value) || isUndefined(value))) result.push({
73
- key,
74
- value: toOtlpAnyValue(value)
143
+ const itemCount = Math.min(values.length, MAX_JSON_SAFE_VALUE_ITEMS);
144
+ let index = 0;
145
+ for(; index < itemCount && state.remainingNodes > 0; index++)try {
146
+ const element = index in values ? values[index] : void 0;
147
+ if (isNullish(element)) continue;
148
+ result.push(encodeAnyValue(element, logger, state, depth));
149
+ } catch {
150
+ result.push({
151
+ stringValue: UNSERIALIZABLE_VALUE
75
152
  });
76
153
  }
154
+ if (values.length > index) result.push({
155
+ stringValue: TRUNCATED_VALUE
156
+ });
157
+ return result;
158
+ }
159
+ function encodeKeyValueList(attrs, logger, state, depth) {
160
+ const result = [];
161
+ for(const key in attrs)if (propertyIsEnumerable.call(attrs, key)) {
162
+ if (result.length >= MAX_JSON_SAFE_VALUE_ITEMS || state.remainingNodes <= 0) {
163
+ logger?.debug('Attributes truncated: the value exceeds the OTLP encoder budget');
164
+ break;
165
+ }
166
+ try {
167
+ const value = attrs[key];
168
+ if (isNull(value) || isUndefined(value)) continue;
169
+ result.push({
170
+ key: sanitizeString(key),
171
+ value: encodeAnyValue(value, logger, state, depth)
172
+ });
173
+ } catch {
174
+ result.push({
175
+ key: sanitizeString(key),
176
+ value: {
177
+ stringValue: UNSERIALIZABLE_VALUE
178
+ }
179
+ });
180
+ }
181
+ }
77
182
  return result;
78
183
  }
79
184
  function timestampToUnixNano() {
80
185
  return String(Date.now()) + '000000';
81
186
  }
82
- function buildOtlpLogRecord(options, sdkContext) {
187
+ function encodeBody(body) {
188
+ try {
189
+ return sanitizeString(String(body));
190
+ } catch {
191
+ return UNSERIALIZABLE_VALUE;
192
+ }
193
+ }
194
+ function buildOtlpLogRecord(options, sdkContext, logger) {
83
195
  const level = options.level || 'info';
84
196
  const { text: severityText, number: severityNumber } = OTLP_SEVERITY_MAP[level] || DEFAULT_OTLP_SEVERITY;
85
197
  const now = timestampToUnixNano();
@@ -94,18 +206,40 @@ function buildOtlpLogRecord(options, sdkContext) {
94
206
  if (sdkContext.appState) autoAttributes['app.state'] = sdkContext.appState;
95
207
  if (sdkContext.activeFeatureFlags && sdkContext.activeFeatureFlags.length > 0) autoAttributes.feature_flags = sdkContext.activeFeatureFlags;
96
208
  const mergedAttributes = {
97
- ...autoAttributes,
98
- ...options.attributes || {}
209
+ ...autoAttributes
99
210
  };
211
+ const userAttributes = options.attributes;
212
+ if (userAttributes) {
213
+ let keys = [];
214
+ try {
215
+ keys = Object.keys(userAttributes);
216
+ } catch {
217
+ keys = [];
218
+ }
219
+ for (const key of keys){
220
+ let value;
221
+ try {
222
+ value = userAttributes[key];
223
+ } catch {
224
+ value = UNSERIALIZABLE_VALUE;
225
+ }
226
+ Object.defineProperty(mergedAttributes, key, {
227
+ value,
228
+ enumerable: true,
229
+ writable: true,
230
+ configurable: true
231
+ });
232
+ }
233
+ }
100
234
  const record = {
101
235
  timeUnixNano: now,
102
236
  observedTimeUnixNano: now,
103
237
  severityNumber,
104
238
  severityText,
105
239
  body: {
106
- stringValue: options.body
240
+ stringValue: encodeBody(options.body)
107
241
  },
108
- attributes: toOtlpKeyValueList(mergedAttributes)
242
+ attributes: toOtlpKeyValueList(mergedAttributes, logger)
109
243
  };
110
244
  if (options.trace_id) record.traceId = options.trace_id;
111
245
  if (options.span_id) record.spanId = options.span_id;
@@ -268,7 +268,7 @@ class PostHogMetrics {
268
268
  };
269
269
  byMetric.set(metricKey, metric);
270
270
  }
271
- const attributes = (0, logs_utils_js_namespaceObject.toOtlpKeyValueList)(state.attributes ?? {});
271
+ const attributes = (0, logs_utils_js_namespaceObject.toOtlpKeyValueList)(state.attributes ?? {}, this._logger);
272
272
  const startNano = (0, external_metrics_utils_js_namespaceObject.msToUnixNano)(state.windowStartMs);
273
273
  if ('count' === state.type) {
274
274
  const dp = {
@@ -236,7 +236,7 @@ class PostHogMetrics {
236
236
  };
237
237
  byMetric.set(metricKey, metric);
238
238
  }
239
- const attributes = toOtlpKeyValueList(state.attributes ?? {});
239
+ const attributes = toOtlpKeyValueList(state.attributes ?? {}, this._logger);
240
240
  const startNano = msToUnixNano(state.windowStartMs);
241
241
  if ('count' === state.type) {
242
242
  const dp = {
@@ -2,7 +2,7 @@ import { FetchLike } from '../types';
2
2
  export * from './bot-detection';
3
3
  export * from './browser-utils';
4
4
  export * from './bucketed-rate-limiter';
5
- export * from './json-utils';
5
+ export { toJsonSafeValue } from './json-utils';
6
6
  export * from './number-utils';
7
7
  export * from './string-utils';
8
8
  export * from './type-utils';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA;AACxB,cAAc,oBAAoB,CAAA;AAElC,eAAO,MAAM,aAAa,SAAS,CAAA;AAEnC,eAAO,MAAM,UAAU,QAAoE,CAAA;AAE3F,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE3D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,MAAM,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI9D;AASD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,SAAS,CAMxG;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;CACtC;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC,CAqB5F;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG,CAOnE;AAED,wBAAsB,eAAe,CAAC,CAAC,EACrC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAoBnB;AAGD,eAAO,MAAM,SAAS,GAAI,KAAK,GAAG,KAAG,GAAG,IAAI,OAAO,CAAC,GAAG,CAEtD,CAAA;AAED,wBAAgB,QAAQ,IAAI,SAAS,GAAG,SAAS,CAEhD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAC1C,OAAO,CAAC,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,CAAC,CAStF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AAGvC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA;AACxB,cAAc,oBAAoB,CAAA;AAElC,eAAO,MAAM,aAAa,SAAS,CAAA;AAEnC,eAAO,MAAM,UAAU,QAAoE,CAAA;AAE3F,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE3D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,MAAM,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI9D;AASD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,SAAS,CAMxG;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;CACtC;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC,CAqB5F;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG,CAOnE;AAED,wBAAsB,eAAe,CAAC,CAAC,EACrC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAoBnB;AAGD,eAAO,MAAM,SAAS,GAAI,KAAK,GAAG,KAAG,GAAG,IAAI,OAAO,CAAC,GAAG,CAEtD,CAAA;AAED,wBAAgB,QAAQ,IAAI,SAAS,GAAG,SAAS,CAEhD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAC1C,OAAO,CAAC,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,CAAC,CAStF"}