@coralogix/react-native-plugin 0.11.1 → 0.11.3
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.
- package/CHANGELOG.md +13 -0
- package/CxSdk.podspec +3 -3
- package/android/src/main/java/com/cxsdk/CxSdkModule.kt +25 -5
- package/android/src/main/java/com/cxsdk/RUMClient.kt +2 -2
- package/index.cjs.js +122 -4
- package/index.esm.js +122 -4
- package/ios/CxSdk.swift +38 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## 0.11.3 (2026-09-26)
|
|
2
|
+
|
|
3
|
+
### 🩹 Fixes
|
|
4
|
+
|
|
5
|
+
- Fixed a crash on iOS when calling `setLabels`. Labels with non-string values (such as numbers or booleans) are now preserved instead of being dropped.
|
|
6
|
+
- `log` now handles missing or incorrectly typed arguments gracefully instead of crashing your app.
|
|
7
|
+
|
|
8
|
+
## 0.11.2 (2026-09-19)
|
|
9
|
+
|
|
10
|
+
### ⬆️ Dependency Updates
|
|
11
|
+
|
|
12
|
+
- Native iOS SDK bumped to **2.18.4** — the crash handler is now installed as the app loads rather than when the SDK initializes, so a crash during startup is captured instead of lost, and the handler is in place before other crash reporters such as Firebase Crashlytics install theirs
|
|
13
|
+
|
|
1
14
|
## 0.11.1 (2026-09-14)
|
|
2
15
|
|
|
3
16
|
### ⬆️ Dependency Updates
|
package/CxSdk.podspec
CHANGED
|
@@ -16,9 +16,9 @@ Pod::Spec.new do |s|
|
|
|
16
16
|
|
|
17
17
|
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
18
18
|
|
|
19
|
-
s.dependency 'Coralogix','2.18.
|
|
20
|
-
s.dependency 'CoralogixInternal','2.18.
|
|
21
|
-
s.dependency 'SessionReplay','2.18.
|
|
19
|
+
s.dependency 'Coralogix','2.18.4'
|
|
20
|
+
s.dependency 'CoralogixInternal','2.18.4'
|
|
21
|
+
s.dependency 'SessionReplay','2.18.4'
|
|
22
22
|
|
|
23
23
|
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
|
24
24
|
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|
|
@@ -109,9 +109,14 @@ class CxSdkModule(reactContext: ReactApplicationContext) :
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
@ReactMethod
|
|
112
|
-
override fun setLabels(labelsMap: ReadableMap) {
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
override fun setLabels(labelsMap: ReadableMap?) {
|
|
113
|
+
// Nullable: a JS null would fail Kotlin's null check and crash the app.
|
|
114
|
+
if (labelsMap == null) {
|
|
115
|
+
Log.w("CxSdkModule", "setLabels: labels is null, ignoring")
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
CoralogixRum.setLabels(labelsMap.toStringAnyMap())
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
@ReactMethod
|
|
@@ -126,8 +131,23 @@ class CxSdkModule(reactContext: ReactApplicationContext) :
|
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
@ReactMethod
|
|
129
|
-
override fun log(severity: Int, message: String
|
|
130
|
-
|
|
134
|
+
override fun log(severity: Int, message: String?, data: ReadableMap?, labels: ReadableMap?) {
|
|
135
|
+
// Nullable: a JS null would fail Kotlin's null check and crash the app.
|
|
136
|
+
// Non-string messages fail earlier, in RN's parsing; `toSafeMessage` (JS) covers them.
|
|
137
|
+
|
|
138
|
+
// Drop unknown severities, like iOS, rather than invent one.
|
|
139
|
+
val cxSeverity = runCatching { severity.toCoralogixLogSeverity() }.getOrNull()
|
|
140
|
+
if (cxSeverity == null) {
|
|
141
|
+
Log.w("CxSdkModule", "log: invalid severity $severity, dropping log. Must be one of [1, 2, 3, 4, 5, 6]")
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
CoralogixRum.log(
|
|
146
|
+
cxSeverity,
|
|
147
|
+
message ?: "",
|
|
148
|
+
data?.toStringAnyMap() ?: emptyMap(),
|
|
149
|
+
labels?.toStringAnyMap() ?: emptyMap()
|
|
150
|
+
)
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
@ReactMethod
|
|
@@ -10,10 +10,10 @@ interface RUMClient {
|
|
|
10
10
|
fun getUserContext(promise: Promise)
|
|
11
11
|
fun setApplicationContext(appContextMap: ReadableMap)
|
|
12
12
|
fun getSessionId(promise: Promise)
|
|
13
|
-
fun setLabels(labelsMap: ReadableMap)
|
|
13
|
+
fun setLabels(labelsMap: ReadableMap?)
|
|
14
14
|
fun getLabels(promise: Promise)
|
|
15
15
|
fun setViewContext(viewContext: String)
|
|
16
|
-
fun log(severity: Int, message: String
|
|
16
|
+
fun log(severity: Int, message: String?, data: ReadableMap?, labels: ReadableMap?)
|
|
17
17
|
fun reportNetworkRequest(requestDetails: ReadableMap)
|
|
18
18
|
fun sendCustomMeasurement(measurement: ReadableMap)
|
|
19
19
|
fun startTimeMeasure(name: String, labels: ReadableMap?)
|
package/index.cjs.js
CHANGED
|
@@ -1140,7 +1140,7 @@ function stopJsRefreshRateSampler() {
|
|
|
1140
1140
|
appStateSub = null;
|
|
1141
1141
|
}
|
|
1142
1142
|
|
|
1143
|
-
var version = "0.11.
|
|
1143
|
+
var version = "0.11.3";
|
|
1144
1144
|
var pkg = {
|
|
1145
1145
|
version: version};
|
|
1146
1146
|
|
|
@@ -1741,17 +1741,121 @@ let initPromise;
|
|
|
1741
1741
|
// `CoralogixRum.isInited` be true while native has no RUM behind it.
|
|
1742
1742
|
let nativeOwnsSamplingRoll = false;
|
|
1743
1743
|
let fetchInstrumentation;
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* Whether RN can send `value` as a dictionary; never throws. RN sends an object's
|
|
1747
|
+
* enumerable properties, so plain objects and class instances with fields work,
|
|
1748
|
+
* while `Map`, `Set` and `Date` (none enumerable) would arrive as `{}`.
|
|
1749
|
+
*/
|
|
1750
|
+
function isPlainObjectArgument(value) {
|
|
1751
|
+
try {
|
|
1752
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
1753
|
+
return false;
|
|
1754
|
+
}
|
|
1755
|
+
const proto = Object.getPrototypeOf(value);
|
|
1756
|
+
if (proto === Object.prototype || proto === null) {
|
|
1757
|
+
return true;
|
|
1758
|
+
}
|
|
1759
|
+
for (const _key in value) {
|
|
1760
|
+
return true;
|
|
1761
|
+
}
|
|
1762
|
+
return false;
|
|
1763
|
+
} catch (_unused) {
|
|
1764
|
+
return false;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/** Whether `severity` is one of the `CoralogixLogSeverity` values (1–6). */
|
|
1769
|
+
function isValidSeverity(severity) {
|
|
1770
|
+
return Number.isInteger(severity) && severity >= 1 && severity <= 6;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
/** Type name for warnings; never throws. */
|
|
1774
|
+
function describeArgument(value) {
|
|
1775
|
+
if (value === null) {
|
|
1776
|
+
return 'null';
|
|
1777
|
+
}
|
|
1778
|
+
try {
|
|
1779
|
+
var _Object$getPrototypeO;
|
|
1780
|
+
if (Array.isArray(value)) {
|
|
1781
|
+
return 'array';
|
|
1782
|
+
}
|
|
1783
|
+
const name = typeof value === 'object' ? (_Object$getPrototypeO = Object.getPrototypeOf(value)) == null || (_Object$getPrototypeO = _Object$getPrototypeO.constructor) == null ? void 0 : _Object$getPrototypeO.name : undefined;
|
|
1784
|
+
// Minified builds rename classes to one letter, which would only confuse.
|
|
1785
|
+
return name && name !== 'Object' && name.length > 1 ? name : typeof value;
|
|
1786
|
+
} catch (_unused2) {
|
|
1787
|
+
return 'an uninspectable value';
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1744
1790
|
function toSafeRecord(name, value) {
|
|
1745
1791
|
if (value == null) {
|
|
1746
1792
|
return {};
|
|
1747
1793
|
}
|
|
1748
|
-
if (
|
|
1749
|
-
logger.warn(`CoralogixRum.log: \`${name}\` must be a plain object; received ${
|
|
1794
|
+
if (!isPlainObjectArgument(value)) {
|
|
1795
|
+
logger.warn(`CoralogixRum.log: \`${name}\` must be a plain object; received ${describeArgument(value)}. Ignoring.`);
|
|
1750
1796
|
return {};
|
|
1751
1797
|
}
|
|
1752
1798
|
return value;
|
|
1753
1799
|
}
|
|
1754
1800
|
|
|
1801
|
+
/** Stringifies any value without throwing (`JSON.stringify` and `String()` both can). */
|
|
1802
|
+
function stringifyUnknown(value) {
|
|
1803
|
+
try {
|
|
1804
|
+
const json = JSON.stringify(value);
|
|
1805
|
+
if (typeof json === 'string') {
|
|
1806
|
+
return json;
|
|
1807
|
+
}
|
|
1808
|
+
} catch (_unused3) {
|
|
1809
|
+
// fall through to String()
|
|
1810
|
+
}
|
|
1811
|
+
try {
|
|
1812
|
+
return String(value);
|
|
1813
|
+
} catch (_unused4) {
|
|
1814
|
+
return '[unserializable]';
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
/**
|
|
1819
|
+
* Readable text for an `Error` or a function, which `JSON.stringify` renders as `"{}"`
|
|
1820
|
+
* or source code. Returns undefined for anything else; never throws.
|
|
1821
|
+
*/
|
|
1822
|
+
function describeSpecialMessage(value) {
|
|
1823
|
+
try {
|
|
1824
|
+
if (value instanceof Error) {
|
|
1825
|
+
return value.stack || String(value);
|
|
1826
|
+
}
|
|
1827
|
+
if (typeof value === 'function') {
|
|
1828
|
+
return `[function ${value.name || 'anonymous'}]`;
|
|
1829
|
+
}
|
|
1830
|
+
} catch (_unused5) {
|
|
1831
|
+
// fall through to stringifyUnknown
|
|
1832
|
+
}
|
|
1833
|
+
return undefined;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/**
|
|
1837
|
+
* Coerces `message` to a string before the bridge. On Android a non-string throws
|
|
1838
|
+
* inside RN's argument parsing, before native code runs, so only JS can guard it.
|
|
1839
|
+
*/
|
|
1840
|
+
function toSafeMessage(value) {
|
|
1841
|
+
if (typeof value === 'string') {
|
|
1842
|
+
return value;
|
|
1843
|
+
}
|
|
1844
|
+
if (value == null) {
|
|
1845
|
+
logger.warn(`CoralogixRum.log: \`message\` must be a string; received ${value === null ? 'null' : 'undefined'}. Sending an empty message.`);
|
|
1846
|
+
return '';
|
|
1847
|
+
}
|
|
1848
|
+
let coerced;
|
|
1849
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
1850
|
+
coerced = String(value);
|
|
1851
|
+
} else {
|
|
1852
|
+
var _describeSpecialMessa;
|
|
1853
|
+
coerced = (_describeSpecialMessa = describeSpecialMessage(value)) != null ? _describeSpecialMessa : stringifyUnknown(value);
|
|
1854
|
+
}
|
|
1855
|
+
logger.warn(`CoralogixRum.log: \`message\` must be a string; received ${describeArgument(value)}. Coerced to a string.`);
|
|
1856
|
+
return coerced;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1755
1859
|
/**
|
|
1756
1860
|
* The real init work. Runs at most once per in-flight/finished init: `CoralogixRum.init`
|
|
1757
1861
|
* memoizes the promise this returns, so concurrent callers share this single run.
|
|
@@ -1961,6 +2065,13 @@ const CoralogixRum = {
|
|
|
1961
2065
|
logger.debug('CoralogixRum must be initiated before setting labels');
|
|
1962
2066
|
return;
|
|
1963
2067
|
}
|
|
2068
|
+
|
|
2069
|
+
// Native reads this as a dictionary unchecked, so a non-object crashes the app.
|
|
2070
|
+
// Return instead of sending `{}`, which would wipe existing labels.
|
|
2071
|
+
if (!isPlainObjectArgument(labels)) {
|
|
2072
|
+
logger.warn(`CoralogixRum.setLabels: \`labels\` must be a plain object; received ${describeArgument(labels)}. Ignoring.`);
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
1964
2075
|
CxSdk.setLabels(labels);
|
|
1965
2076
|
},
|
|
1966
2077
|
getLabels: async () => {
|
|
@@ -1982,9 +2093,16 @@ const CoralogixRum = {
|
|
|
1982
2093
|
logger.debug('CoralogixRum must be initiated before using log');
|
|
1983
2094
|
return;
|
|
1984
2095
|
}
|
|
2096
|
+
|
|
2097
|
+
// A non-integer severity crashes Android while RN parses the arguments.
|
|
2098
|
+
if (!isValidSeverity(severity)) {
|
|
2099
|
+
logger.warn(`CoralogixRum.log: \`severity\` must be a CoralogixLogSeverity (1-6); received ${describeArgument(severity)}. Dropping the log.`);
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
const safeMessage = toSafeMessage(message);
|
|
1985
2103
|
const safeData = toSafeRecord('data', data);
|
|
1986
2104
|
const safeLabels = toSafeRecord('labels', labels);
|
|
1987
|
-
CxSdk.log(severity,
|
|
2105
|
+
CxSdk.log(severity, safeMessage, safeData, safeLabels);
|
|
1988
2106
|
},
|
|
1989
2107
|
debug(message, data) {
|
|
1990
2108
|
this.log(CoralogixLogSeverity.Debug, message, data);
|
package/index.esm.js
CHANGED
|
@@ -1138,7 +1138,7 @@ function stopJsRefreshRateSampler() {
|
|
|
1138
1138
|
appStateSub = null;
|
|
1139
1139
|
}
|
|
1140
1140
|
|
|
1141
|
-
var version = "0.11.
|
|
1141
|
+
var version = "0.11.3";
|
|
1142
1142
|
var pkg = {
|
|
1143
1143
|
version: version};
|
|
1144
1144
|
|
|
@@ -1739,17 +1739,121 @@ let initPromise;
|
|
|
1739
1739
|
// `CoralogixRum.isInited` be true while native has no RUM behind it.
|
|
1740
1740
|
let nativeOwnsSamplingRoll = false;
|
|
1741
1741
|
let fetchInstrumentation;
|
|
1742
|
+
|
|
1743
|
+
/**
|
|
1744
|
+
* Whether RN can send `value` as a dictionary; never throws. RN sends an object's
|
|
1745
|
+
* enumerable properties, so plain objects and class instances with fields work,
|
|
1746
|
+
* while `Map`, `Set` and `Date` (none enumerable) would arrive as `{}`.
|
|
1747
|
+
*/
|
|
1748
|
+
function isPlainObjectArgument(value) {
|
|
1749
|
+
try {
|
|
1750
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
1751
|
+
return false;
|
|
1752
|
+
}
|
|
1753
|
+
const proto = Object.getPrototypeOf(value);
|
|
1754
|
+
if (proto === Object.prototype || proto === null) {
|
|
1755
|
+
return true;
|
|
1756
|
+
}
|
|
1757
|
+
for (const _key in value) {
|
|
1758
|
+
return true;
|
|
1759
|
+
}
|
|
1760
|
+
return false;
|
|
1761
|
+
} catch (_unused) {
|
|
1762
|
+
return false;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
/** Whether `severity` is one of the `CoralogixLogSeverity` values (1–6). */
|
|
1767
|
+
function isValidSeverity(severity) {
|
|
1768
|
+
return Number.isInteger(severity) && severity >= 1 && severity <= 6;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
/** Type name for warnings; never throws. */
|
|
1772
|
+
function describeArgument(value) {
|
|
1773
|
+
if (value === null) {
|
|
1774
|
+
return 'null';
|
|
1775
|
+
}
|
|
1776
|
+
try {
|
|
1777
|
+
var _Object$getPrototypeO;
|
|
1778
|
+
if (Array.isArray(value)) {
|
|
1779
|
+
return 'array';
|
|
1780
|
+
}
|
|
1781
|
+
const name = typeof value === 'object' ? (_Object$getPrototypeO = Object.getPrototypeOf(value)) == null || (_Object$getPrototypeO = _Object$getPrototypeO.constructor) == null ? void 0 : _Object$getPrototypeO.name : undefined;
|
|
1782
|
+
// Minified builds rename classes to one letter, which would only confuse.
|
|
1783
|
+
return name && name !== 'Object' && name.length > 1 ? name : typeof value;
|
|
1784
|
+
} catch (_unused2) {
|
|
1785
|
+
return 'an uninspectable value';
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1742
1788
|
function toSafeRecord(name, value) {
|
|
1743
1789
|
if (value == null) {
|
|
1744
1790
|
return {};
|
|
1745
1791
|
}
|
|
1746
|
-
if (
|
|
1747
|
-
logger.warn(`CoralogixRum.log: \`${name}\` must be a plain object; received ${
|
|
1792
|
+
if (!isPlainObjectArgument(value)) {
|
|
1793
|
+
logger.warn(`CoralogixRum.log: \`${name}\` must be a plain object; received ${describeArgument(value)}. Ignoring.`);
|
|
1748
1794
|
return {};
|
|
1749
1795
|
}
|
|
1750
1796
|
return value;
|
|
1751
1797
|
}
|
|
1752
1798
|
|
|
1799
|
+
/** Stringifies any value without throwing (`JSON.stringify` and `String()` both can). */
|
|
1800
|
+
function stringifyUnknown(value) {
|
|
1801
|
+
try {
|
|
1802
|
+
const json = JSON.stringify(value);
|
|
1803
|
+
if (typeof json === 'string') {
|
|
1804
|
+
return json;
|
|
1805
|
+
}
|
|
1806
|
+
} catch (_unused3) {
|
|
1807
|
+
// fall through to String()
|
|
1808
|
+
}
|
|
1809
|
+
try {
|
|
1810
|
+
return String(value);
|
|
1811
|
+
} catch (_unused4) {
|
|
1812
|
+
return '[unserializable]';
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
/**
|
|
1817
|
+
* Readable text for an `Error` or a function, which `JSON.stringify` renders as `"{}"`
|
|
1818
|
+
* or source code. Returns undefined for anything else; never throws.
|
|
1819
|
+
*/
|
|
1820
|
+
function describeSpecialMessage(value) {
|
|
1821
|
+
try {
|
|
1822
|
+
if (value instanceof Error) {
|
|
1823
|
+
return value.stack || String(value);
|
|
1824
|
+
}
|
|
1825
|
+
if (typeof value === 'function') {
|
|
1826
|
+
return `[function ${value.name || 'anonymous'}]`;
|
|
1827
|
+
}
|
|
1828
|
+
} catch (_unused5) {
|
|
1829
|
+
// fall through to stringifyUnknown
|
|
1830
|
+
}
|
|
1831
|
+
return undefined;
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
/**
|
|
1835
|
+
* Coerces `message` to a string before the bridge. On Android a non-string throws
|
|
1836
|
+
* inside RN's argument parsing, before native code runs, so only JS can guard it.
|
|
1837
|
+
*/
|
|
1838
|
+
function toSafeMessage(value) {
|
|
1839
|
+
if (typeof value === 'string') {
|
|
1840
|
+
return value;
|
|
1841
|
+
}
|
|
1842
|
+
if (value == null) {
|
|
1843
|
+
logger.warn(`CoralogixRum.log: \`message\` must be a string; received ${value === null ? 'null' : 'undefined'}. Sending an empty message.`);
|
|
1844
|
+
return '';
|
|
1845
|
+
}
|
|
1846
|
+
let coerced;
|
|
1847
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
1848
|
+
coerced = String(value);
|
|
1849
|
+
} else {
|
|
1850
|
+
var _describeSpecialMessa;
|
|
1851
|
+
coerced = (_describeSpecialMessa = describeSpecialMessage(value)) != null ? _describeSpecialMessa : stringifyUnknown(value);
|
|
1852
|
+
}
|
|
1853
|
+
logger.warn(`CoralogixRum.log: \`message\` must be a string; received ${describeArgument(value)}. Coerced to a string.`);
|
|
1854
|
+
return coerced;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1753
1857
|
/**
|
|
1754
1858
|
* The real init work. Runs at most once per in-flight/finished init: `CoralogixRum.init`
|
|
1755
1859
|
* memoizes the promise this returns, so concurrent callers share this single run.
|
|
@@ -1959,6 +2063,13 @@ const CoralogixRum = {
|
|
|
1959
2063
|
logger.debug('CoralogixRum must be initiated before setting labels');
|
|
1960
2064
|
return;
|
|
1961
2065
|
}
|
|
2066
|
+
|
|
2067
|
+
// Native reads this as a dictionary unchecked, so a non-object crashes the app.
|
|
2068
|
+
// Return instead of sending `{}`, which would wipe existing labels.
|
|
2069
|
+
if (!isPlainObjectArgument(labels)) {
|
|
2070
|
+
logger.warn(`CoralogixRum.setLabels: \`labels\` must be a plain object; received ${describeArgument(labels)}. Ignoring.`);
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
1962
2073
|
CxSdk.setLabels(labels);
|
|
1963
2074
|
},
|
|
1964
2075
|
getLabels: async () => {
|
|
@@ -1980,9 +2091,16 @@ const CoralogixRum = {
|
|
|
1980
2091
|
logger.debug('CoralogixRum must be initiated before using log');
|
|
1981
2092
|
return;
|
|
1982
2093
|
}
|
|
2094
|
+
|
|
2095
|
+
// A non-integer severity crashes Android while RN parses the arguments.
|
|
2096
|
+
if (!isValidSeverity(severity)) {
|
|
2097
|
+
logger.warn(`CoralogixRum.log: \`severity\` must be a CoralogixLogSeverity (1-6); received ${describeArgument(severity)}. Dropping the log.`);
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
const safeMessage = toSafeMessage(message);
|
|
1983
2101
|
const safeData = toSafeRecord('data', data);
|
|
1984
2102
|
const safeLabels = toSafeRecord('labels', labels);
|
|
1985
|
-
CxSdk.log(severity,
|
|
2103
|
+
CxSdk.log(severity, safeMessage, safeData, safeLabels);
|
|
1986
2104
|
},
|
|
1987
2105
|
debug(message, data) {
|
|
1988
2106
|
this.log(CoralogixLogSeverity.Debug, message, data);
|
package/ios/CxSdk.swift
CHANGED
|
@@ -79,11 +79,15 @@ class CxSdk: RCTEventEmitter {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
@objc(setLabels:withResolver:withRejecter:)
|
|
82
|
-
func setLabels(labelsMap: NSDictionary
|
|
82
|
+
func setLabels(labelsMap: NSDictionary?,
|
|
83
83
|
resolve:RCTPromiseResolveBlock,
|
|
84
84
|
reject:RCTPromiseRejectBlock) -> Void {
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
// Ignore nil or a non-dictionary, as Android does, rather than wipe the labels.
|
|
86
|
+
guard let labelsMap = labelsMap, labelsMap.isKind(of: NSDictionary.self) else {
|
|
87
|
+
resolve(nil)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
resolve(coralogixRum?.set(labels: bridgedDictionary(labelsMap)))
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
@objc(getLabels:withRejecter:)
|
|
@@ -128,18 +132,19 @@ class CxSdk: RCTEventEmitter {
|
|
|
128
132
|
|
|
129
133
|
@objc(log:message:data:labels:withResolver:withRejecter:)
|
|
130
134
|
func log(severity: Int,
|
|
131
|
-
message:
|
|
132
|
-
data: NSDictionary
|
|
133
|
-
labels: NSDictionary
|
|
135
|
+
message: NSString?,
|
|
136
|
+
data: NSDictionary?,
|
|
137
|
+
labels: NSDictionary?,
|
|
134
138
|
resolve:RCTPromiseResolveBlock,
|
|
135
139
|
reject:RCTPromiseRejectBlock) -> Void {
|
|
136
140
|
|
|
137
141
|
guard let cxLogSeverity = CoralogixLogSeverity(rawValue: Int(severity)) else {
|
|
138
142
|
return
|
|
139
143
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
coralogixRum?.log(severity: cxLogSeverity,
|
|
145
|
+
message: bridgedString(message),
|
|
146
|
+
data: bridgedDictionary(data),
|
|
147
|
+
labels: bridgedDictionary(labels))
|
|
143
148
|
resolve("log success")
|
|
144
149
|
}
|
|
145
150
|
|
|
@@ -832,6 +837,30 @@ class CxSdk: RCTEventEmitter {
|
|
|
832
837
|
return set
|
|
833
838
|
}
|
|
834
839
|
|
|
840
|
+
/// Reads a bridged dictionary without trusting its type: release builds skip RN's
|
|
841
|
+
/// type check, so a JS array/string/number can arrive here. Must be `isKind(of:)` —
|
|
842
|
+
/// `as? NSDictionary` gets optimised away. Values pass through as `Any`, like Android.
|
|
843
|
+
private func bridgedDictionary(_ value: NSDictionary?) -> [String: Any] {
|
|
844
|
+
guard let object = value, object.isKind(of: NSDictionary.self) else {
|
|
845
|
+
return [String: Any]()
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
var result = [String: Any]()
|
|
849
|
+
object.enumerateKeysAndObjects { key, value, _ in
|
|
850
|
+
guard let name = key as? String else { return }
|
|
851
|
+
result[name] = value
|
|
852
|
+
}
|
|
853
|
+
return result
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/// Same for strings: a Swift `String` parameter would force-cast an `NSNumber` and crash.
|
|
857
|
+
private func bridgedString(_ value: NSString?) -> String {
|
|
858
|
+
guard let object = value, object.isKind(of: NSString.self) else {
|
|
859
|
+
return ""
|
|
860
|
+
}
|
|
861
|
+
return object as String
|
|
862
|
+
}
|
|
863
|
+
|
|
835
864
|
private func toAnyDict(_ dict: NSDictionary?) -> [String: Any]? {
|
|
836
865
|
guard let dict = dict, dict.count > 0 else { return nil }
|
|
837
866
|
var result = [String: Any]()
|