react-native-test-adx 0.1.17 → 0.1.19
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/lib/module/sdkVersion.js +1 -1
- package/lib/module/services/logging/AdaxonLogger.js +136 -106
- package/lib/module/services/logging/sanitizeLogMetadata.js +20 -6
- package/lib/typescript/src/sdkVersion.d.ts +1 -1
- package/lib/typescript/src/services/logging/AdaxonLogger.d.ts +2 -1
- package/lib/typescript/src/services/logging/sanitizeLogMetadata.d.ts +6 -2
- package/package.json +1 -1
package/lib/module/sdkVersion.js
CHANGED
|
@@ -5,12 +5,19 @@ import { AdaxonSdkError } from "../../errors/AdaxonSdkError.js";
|
|
|
5
5
|
import { sanitizeLogMetadata } from "./sanitizeLogMetadata.js";
|
|
6
6
|
const DEFAULT_LEVEL = 'info';
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
const
|
|
10
|
-
|
|
8
|
+
/** Milestones + native dev warn/error → Sentry Message when Logs are off. */
|
|
9
|
+
const SENTRY_MESSAGE_EVENTS = new Set(['sdk.initialize.completed', 'sdk.initialize.failed', 'recommendation.response.received', 'ad.displayed', 'ad.dismissed', 'ad.show.failed']);
|
|
10
|
+
|
|
11
|
+
/** Native dev: avoid captureMessage during interstitial UI (fake Sentry Issues + bundle stacks). */
|
|
12
|
+
const NATIVE_DEV_BREADCRUMB_ONLY_EVENTS = new Set(['ad.show.started', 'ad.displayed', 'ad.dismissed']);
|
|
11
13
|
let cachedSentryModule;
|
|
12
14
|
let missingSentryPeerWarningShown = false;
|
|
13
|
-
|
|
15
|
+
function isDevBuild() {
|
|
16
|
+
return typeof __DEV__ !== 'undefined' && __DEV__;
|
|
17
|
+
}
|
|
18
|
+
function usesSentryLogs(platform) {
|
|
19
|
+
return platform === 'web' || !isDevBuild();
|
|
20
|
+
}
|
|
14
21
|
function loadSentryModule() {
|
|
15
22
|
if (cachedSentryModule !== undefined) {
|
|
16
23
|
return cachedSentryModule;
|
|
@@ -24,14 +31,14 @@ function loadSentryModule() {
|
|
|
24
31
|
return cachedSentryModule;
|
|
25
32
|
}
|
|
26
33
|
function warnSentryUnavailableOnce(hasDsn) {
|
|
27
|
-
if (!hasDsn || missingSentryPeerWarningShown ||
|
|
34
|
+
if (!hasDsn || missingSentryPeerWarningShown || !isDevBuild()) {
|
|
28
35
|
return;
|
|
29
36
|
}
|
|
30
37
|
if (loadSentryModule()) {
|
|
31
38
|
return;
|
|
32
39
|
}
|
|
33
40
|
missingSentryPeerWarningShown = true;
|
|
34
|
-
console.warn(
|
|
41
|
+
console.warn('[Adaxon] Activity logging is on and Sentry DSN is set, but @sentry/react-native could not be loaded. ' + 'Reinstall dependencies and rebuild. Until then, use [Adaxon activity] in Metro/logcat.');
|
|
35
42
|
}
|
|
36
43
|
function sentryLevel(level, target) {
|
|
37
44
|
if (level === 'warn') {
|
|
@@ -46,34 +53,59 @@ function sentryLevel(level, target) {
|
|
|
46
53
|
return 'info';
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
/**
|
|
50
|
-
function
|
|
51
|
-
if (
|
|
52
|
-
return
|
|
56
|
+
/** What to send after structured Logs (if any) are attempted. */
|
|
57
|
+
function planSentryDelivery(input, level, platform, sentStructuredLog, ownsSentryClient) {
|
|
58
|
+
if (level === 'debug') {
|
|
59
|
+
return {
|
|
60
|
+
breadcrumb: null,
|
|
61
|
+
sendMessage: false
|
|
62
|
+
};
|
|
53
63
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
const nativeDev = platform !== 'web' && isDevBuild();
|
|
65
|
+
let sendMessage = false;
|
|
66
|
+
if (ownsSentryClient && !sentStructuredLog) {
|
|
67
|
+
if (input.captureAsEvent && input.error === undefined) {
|
|
68
|
+
sendMessage = true;
|
|
69
|
+
} else if (SENTRY_MESSAGE_EVENTS.has(input.eventName)) {
|
|
70
|
+
const skipMessage = nativeDev && NATIVE_DEV_BREADCRUMB_ONLY_EVENTS.has(input.eventName);
|
|
71
|
+
if (!skipMessage) {
|
|
72
|
+
sendMessage = true;
|
|
73
|
+
}
|
|
74
|
+
} else if (nativeDev && (level === 'warn' || level === 'error')) {
|
|
75
|
+
sendMessage = true;
|
|
76
|
+
}
|
|
59
77
|
}
|
|
60
|
-
|
|
61
|
-
|
|
78
|
+
let breadcrumb = null;
|
|
79
|
+
if (level === 'warn' || level === 'error') {
|
|
80
|
+
breadcrumb = 'adaxon.sdk';
|
|
81
|
+
} else if (nativeDev && !sentStructuredLog && !sendMessage) {
|
|
82
|
+
breadcrumb = 'adaxon.activity';
|
|
62
83
|
}
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
return {
|
|
85
|
+
breadcrumb,
|
|
86
|
+
sendMessage
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Never throw or leave a rejected Sentry promise unhandled. */
|
|
91
|
+
function neverBreakSdk(run) {
|
|
92
|
+
try {
|
|
93
|
+
const result = run();
|
|
94
|
+
if (result && typeof result.then === 'function') {
|
|
95
|
+
Promise.resolve(result).catch(() => undefined);
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
// Activity logging must not affect the SDK.
|
|
65
99
|
}
|
|
66
|
-
// Native dev: primary Sentry channel (Messages). Release: fallback when Logs fail.
|
|
67
|
-
return true;
|
|
68
100
|
}
|
|
69
|
-
function
|
|
101
|
+
function tryStructuredLog(sentry, level, eventName, attributes) {
|
|
70
102
|
try {
|
|
71
103
|
const loggerFn = sentry.logger?.[level];
|
|
72
104
|
if (typeof loggerFn !== 'function') {
|
|
73
105
|
return false;
|
|
74
106
|
}
|
|
75
107
|
loggerFn.call(sentry.logger, eventName, {
|
|
76
|
-
...
|
|
108
|
+
...attributes,
|
|
77
109
|
event_name: eventName
|
|
78
110
|
});
|
|
79
111
|
return true;
|
|
@@ -81,52 +113,30 @@ function emitStructuredLog(sentry, level, eventName, metadata) {
|
|
|
81
113
|
return false;
|
|
82
114
|
}
|
|
83
115
|
}
|
|
84
|
-
function
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
...base,
|
|
96
|
-
enableLogs: true
|
|
97
|
-
};
|
|
116
|
+
function isUnitTestRun() {
|
|
117
|
+
const env = globalThis.process?.env;
|
|
118
|
+
return env?.NODE_ENV === 'test';
|
|
119
|
+
}
|
|
120
|
+
function deferOnNative(platform, run) {
|
|
121
|
+
const sync = platform === 'web' || isUnitTestRun();
|
|
122
|
+
if (sync) {
|
|
123
|
+
run();
|
|
124
|
+
} else {
|
|
125
|
+
setTimeout(run, 0);
|
|
98
126
|
}
|
|
99
|
-
|
|
100
|
-
// Native dev: JS-only client — captureMessage without Sentry Logs (Metro-safe).
|
|
101
|
-
return {
|
|
102
|
-
...base,
|
|
103
|
-
enableLogs: false,
|
|
104
|
-
enableNative: false,
|
|
105
|
-
autoInitializeNativeSdk: false,
|
|
106
|
-
defaultIntegrations: false,
|
|
107
|
-
integrations: []
|
|
108
|
-
};
|
|
109
127
|
}
|
|
110
|
-
function
|
|
111
|
-
if (
|
|
128
|
+
function mirrorToDevConsole(level, eventName, metadata) {
|
|
129
|
+
if (!isDevBuild()) {
|
|
112
130
|
return;
|
|
113
131
|
}
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
// when Sentry integrations run alongside consoleSandbox after captureMessage.
|
|
117
|
-
consoleFn(`[Adaxon activity] ${eventName}`, metadata);
|
|
118
|
-
}
|
|
119
|
-
function activityTags(eventName, platform) {
|
|
120
|
-
return {
|
|
121
|
-
adaxon_event: eventName,
|
|
122
|
-
adaxon_platform: platform,
|
|
123
|
-
adaxon_sdk_version: SDK_VERSION
|
|
124
|
-
};
|
|
132
|
+
const log = level === 'error' ? console.error : level === 'warn' ? console.warn : console.info;
|
|
133
|
+
log(`[Adaxon activity] ${eventName}`, metadata);
|
|
125
134
|
}
|
|
126
135
|
class AdaxonLoggerService {
|
|
127
136
|
enabled = false;
|
|
128
137
|
platform = 'web';
|
|
129
138
|
sentryReady = false;
|
|
139
|
+
ownsSentryClient = false;
|
|
130
140
|
configure(input) {
|
|
131
141
|
this.enabled = Boolean(input.enabled);
|
|
132
142
|
this.platform = input.platform;
|
|
@@ -141,6 +151,7 @@ class AdaxonLoggerService {
|
|
|
141
151
|
this.enabled = false;
|
|
142
152
|
this.sentryReady = false;
|
|
143
153
|
this.platform = 'web';
|
|
154
|
+
this.ownsSentryClient = false;
|
|
144
155
|
cachedSentryModule = undefined;
|
|
145
156
|
missingSentryPeerWarningShown = false;
|
|
146
157
|
}
|
|
@@ -149,71 +160,90 @@ class AdaxonLoggerService {
|
|
|
149
160
|
return;
|
|
150
161
|
}
|
|
151
162
|
const level = input.level ?? DEFAULT_LEVEL;
|
|
152
|
-
const
|
|
163
|
+
const baseMetadata = {
|
|
153
164
|
...input.metadata,
|
|
154
165
|
sdk_version: SDK_VERSION,
|
|
155
166
|
platform: this.platform,
|
|
156
167
|
timestamp: new Date().toISOString()
|
|
168
|
+
};
|
|
169
|
+
mirrorToDevConsole(level, input.eventName, sanitizeLogMetadata(baseMetadata));
|
|
170
|
+
const sentryAttributes = sanitizeLogMetadata({
|
|
171
|
+
...baseMetadata,
|
|
172
|
+
event_name: input.eventName
|
|
173
|
+
}, {
|
|
174
|
+
forSentry: true
|
|
157
175
|
});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
176
|
+
deferOnNative(this.platform, () => neverBreakSdk(() => this.pushToSentry(input, level, sentryAttributes)));
|
|
177
|
+
}
|
|
178
|
+
pushToSentry(input, level, sentryAttributes) {
|
|
179
|
+
const sentry = loadSentryModule();
|
|
180
|
+
if (!sentry || !this.sentryReady) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const sentStructuredLog = usesSentryLogs(this.platform) && tryStructuredLog(sentry, level, input.eventName, sentryAttributes);
|
|
184
|
+
const plan = planSentryDelivery(input, level, this.platform, sentStructuredLog, this.ownsSentryClient);
|
|
185
|
+
const tags = {
|
|
186
|
+
adaxon_event: input.eventName,
|
|
187
|
+
adaxon_platform: this.platform,
|
|
188
|
+
adaxon_sdk_version: SDK_VERSION
|
|
189
|
+
};
|
|
190
|
+
if (plan.breadcrumb) {
|
|
191
|
+
const category = plan.breadcrumb;
|
|
192
|
+
neverBreakSdk(() => sentry.addBreadcrumb({
|
|
193
|
+
category,
|
|
194
|
+
message: input.eventName,
|
|
195
|
+
level: sentryLevel(level, 'breadcrumb'),
|
|
196
|
+
data: sentryAttributes,
|
|
197
|
+
timestamp: Date.now() / 1000
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
if (plan.sendMessage && typeof sentry.captureMessage === 'function') {
|
|
201
|
+
neverBreakSdk(() => sentry.captureMessage(input.eventName, {
|
|
202
|
+
level: sentryLevel(level, 'message'),
|
|
203
|
+
tags,
|
|
204
|
+
extra: sentryAttributes
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
if (input.captureAsEvent && input.error !== undefined && this.ownsSentryClient) {
|
|
208
|
+
const err = input.error instanceof Error ? input.error : new Error(AdaxonSdkError.messageOf(input.error, `Adaxon SDK activity: ${input.eventName}`));
|
|
209
|
+
neverBreakSdk(() => sentry.captureException(err, {
|
|
210
|
+
tags,
|
|
211
|
+
extra: sentryAttributes
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
191
214
|
}
|
|
192
215
|
ensureSentry(dsn, testMode) {
|
|
193
|
-
|
|
216
|
+
neverBreakSdk(() => {
|
|
194
217
|
const sentry = loadSentryModule();
|
|
195
218
|
if (!sentry) {
|
|
196
219
|
return;
|
|
197
220
|
}
|
|
221
|
+
this.ownsSentryClient = false;
|
|
198
222
|
if (!sentry.getClient()) {
|
|
199
223
|
if (!dsn?.trim()) {
|
|
200
224
|
return;
|
|
201
225
|
}
|
|
202
|
-
|
|
203
|
-
|
|
226
|
+
sentry.init({
|
|
227
|
+
dsn: dsn.trim(),
|
|
228
|
+
environment: testMode ? 'adaxon-sdk-test' : 'adaxon-sdk',
|
|
229
|
+
enableLogs: usesSentryLogs(this.platform),
|
|
230
|
+
enableAutoConsoleLogs: false,
|
|
231
|
+
enableNative: false,
|
|
232
|
+
autoInitializeNativeSdk: false,
|
|
233
|
+
defaultIntegrations: false,
|
|
234
|
+
integrations: [],
|
|
235
|
+
tracesSampleRate: 0,
|
|
236
|
+
enableAutoSessionTracking: false,
|
|
237
|
+
sendDefaultPii: false
|
|
238
|
+
});
|
|
239
|
+
this.ownsSentryClient = true;
|
|
240
|
+
}
|
|
241
|
+
for (const [key, value] of [['adaxon_sdk_version', SDK_VERSION], ['adaxon_platform', this.platform]]) {
|
|
242
|
+
sentry.setTag(key, value);
|
|
204
243
|
}
|
|
205
|
-
sentry.setTag('adaxon_sdk_version', SDK_VERSION);
|
|
206
|
-
sentry.setTag('adaxon_platform', this.platform);
|
|
207
244
|
this.sentryReady = true;
|
|
208
245
|
});
|
|
209
246
|
}
|
|
210
|
-
safe(run) {
|
|
211
|
-
try {
|
|
212
|
-
run();
|
|
213
|
-
} catch {
|
|
214
|
-
// Logging must never interrupt SDK behavior.
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
247
|
}
|
|
218
248
|
export const AdaxonLogger = new AdaxonLoggerService();
|
|
219
249
|
export function logSdkActivity(input) {
|
|
@@ -6,19 +6,28 @@ const MAX_NESTED_KEYS = 40;
|
|
|
6
6
|
function truncate(value) {
|
|
7
7
|
return value.length <= MAX_STRING_LENGTH ? value : `${value.slice(0, MAX_STRING_LENGTH)}…`;
|
|
8
8
|
}
|
|
9
|
-
function sanitizeField(key, value) {
|
|
9
|
+
function sanitizeField(key, value, forSentry) {
|
|
10
10
|
if (SENSITIVE_KEY.test(key)) {
|
|
11
11
|
return '[redacted]';
|
|
12
12
|
}
|
|
13
13
|
if (value === null || value === undefined) {
|
|
14
|
-
return value;
|
|
14
|
+
return forSentry ? undefined : value;
|
|
15
15
|
}
|
|
16
16
|
if (typeof value === 'string') {
|
|
17
17
|
return truncate(value);
|
|
18
18
|
}
|
|
19
|
-
if (typeof value === '
|
|
19
|
+
if (typeof value === 'boolean') {
|
|
20
20
|
return value;
|
|
21
21
|
}
|
|
22
|
+
if (typeof value === 'number') {
|
|
23
|
+
if (Number.isFinite(value)) {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
return forSentry ? undefined : value;
|
|
27
|
+
}
|
|
28
|
+
if (forSentry) {
|
|
29
|
+
return truncate(String(value));
|
|
30
|
+
}
|
|
22
31
|
if (Array.isArray(value)) {
|
|
23
32
|
return value.map(item => typeof item === 'string' ? truncate(item) : item);
|
|
24
33
|
}
|
|
@@ -32,14 +41,19 @@ function sanitizeField(key, value) {
|
|
|
32
41
|
return String(value);
|
|
33
42
|
}
|
|
34
43
|
|
|
35
|
-
/** Redact secrets
|
|
36
|
-
export function sanitizeLogMetadata(metadata) {
|
|
44
|
+
/** Redact secrets, cap strings; optional Sentry flattening (primitives only). */
|
|
45
|
+
export function sanitizeLogMetadata(metadata, options = {}) {
|
|
37
46
|
if (!metadata) {
|
|
38
47
|
return {};
|
|
39
48
|
}
|
|
49
|
+
const forSentry = Boolean(options.forSentry);
|
|
40
50
|
const output = {};
|
|
41
51
|
for (const [key, value] of Object.entries(metadata)) {
|
|
42
|
-
|
|
52
|
+
const sanitized = sanitizeField(key, value, forSentry);
|
|
53
|
+
if (forSentry && sanitized === undefined) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
output[key] = sanitized;
|
|
43
57
|
}
|
|
44
58
|
return output;
|
|
45
59
|
}
|
|
@@ -18,11 +18,12 @@ declare class AdaxonLoggerService {
|
|
|
18
18
|
private enabled;
|
|
19
19
|
private platform;
|
|
20
20
|
private sentryReady;
|
|
21
|
+
private ownsSentryClient;
|
|
21
22
|
configure(input: AdaxonLoggerConfigureInput): void;
|
|
22
23
|
reset(): void;
|
|
23
24
|
logActivity(input: AdaxonActivityLogInput): void;
|
|
25
|
+
private pushToSentry;
|
|
24
26
|
private ensureSentry;
|
|
25
|
-
private safe;
|
|
26
27
|
}
|
|
27
28
|
export declare const AdaxonLogger: AdaxonLoggerService;
|
|
28
29
|
export declare function logSdkActivity(input: AdaxonActivityLogInput): void;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export type SanitizeLogMetadataOptions = {
|
|
2
|
+
/** Flatten to string/number/boolean for Sentry Logs and captureMessage (React Native). */
|
|
3
|
+
forSentry?: boolean;
|
|
4
|
+
};
|
|
5
|
+
/** Redact secrets, cap strings; optional Sentry flattening (primitives only). */
|
|
6
|
+
export declare function sanitizeLogMetadata(metadata: Record<string, unknown> | undefined, options?: SanitizeLogMetadataOptions): Record<string, unknown>;
|
|
3
7
|
//# sourceMappingURL=sanitizeLogMetadata.d.ts.map
|
package/package.json
CHANGED