react-native-test-adx 0.1.17 → 0.1.18
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 +130 -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,16 @@ 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
|
-
/** undefined = not loaded yet; null = unavailable in this app bundle */
|
|
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']);
|
|
11
10
|
let cachedSentryModule;
|
|
12
11
|
let missingSentryPeerWarningShown = false;
|
|
13
|
-
|
|
12
|
+
function isDevBuild() {
|
|
13
|
+
return typeof __DEV__ !== 'undefined' && __DEV__;
|
|
14
|
+
}
|
|
15
|
+
function usesSentryLogs(platform) {
|
|
16
|
+
return platform === 'web' || !isDevBuild();
|
|
17
|
+
}
|
|
14
18
|
function loadSentryModule() {
|
|
15
19
|
if (cachedSentryModule !== undefined) {
|
|
16
20
|
return cachedSentryModule;
|
|
@@ -24,14 +28,14 @@ function loadSentryModule() {
|
|
|
24
28
|
return cachedSentryModule;
|
|
25
29
|
}
|
|
26
30
|
function warnSentryUnavailableOnce(hasDsn) {
|
|
27
|
-
if (!hasDsn || missingSentryPeerWarningShown ||
|
|
31
|
+
if (!hasDsn || missingSentryPeerWarningShown || !isDevBuild()) {
|
|
28
32
|
return;
|
|
29
33
|
}
|
|
30
34
|
if (loadSentryModule()) {
|
|
31
35
|
return;
|
|
32
36
|
}
|
|
33
37
|
missingSentryPeerWarningShown = true;
|
|
34
|
-
console.warn(
|
|
38
|
+
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
39
|
}
|
|
36
40
|
function sentryLevel(level, target) {
|
|
37
41
|
if (level === 'warn') {
|
|
@@ -46,34 +50,56 @@ function sentryLevel(level, target) {
|
|
|
46
50
|
return 'info';
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
/**
|
|
50
|
-
function
|
|
51
|
-
if (
|
|
52
|
-
return
|
|
53
|
+
/** What to send after structured Logs (if any) are attempted. */
|
|
54
|
+
function planSentryDelivery(input, level, platform, sentStructuredLog, ownsSentryClient) {
|
|
55
|
+
if (level === 'debug') {
|
|
56
|
+
return {
|
|
57
|
+
breadcrumb: null,
|
|
58
|
+
sendMessage: false
|
|
59
|
+
};
|
|
53
60
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
const nativeDev = platform !== 'web' && isDevBuild();
|
|
62
|
+
let sendMessage = false;
|
|
63
|
+
if (ownsSentryClient && !sentStructuredLog) {
|
|
64
|
+
if (input.captureAsEvent && input.error === undefined) {
|
|
65
|
+
sendMessage = true;
|
|
66
|
+
} else if (SENTRY_MESSAGE_EVENTS.has(input.eventName)) {
|
|
67
|
+
sendMessage = true;
|
|
68
|
+
} else if (nativeDev && (level === 'warn' || level === 'error')) {
|
|
69
|
+
sendMessage = true;
|
|
70
|
+
}
|
|
59
71
|
}
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
let breadcrumb = null;
|
|
73
|
+
if (level === 'warn' || level === 'error') {
|
|
74
|
+
breadcrumb = 'adaxon.sdk';
|
|
75
|
+
} else if (nativeDev && !sentStructuredLog && !sendMessage) {
|
|
76
|
+
breadcrumb = 'adaxon.activity';
|
|
62
77
|
}
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
return {
|
|
79
|
+
breadcrumb,
|
|
80
|
+
sendMessage
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Never throw or leave a rejected Sentry promise unhandled. */
|
|
85
|
+
function neverBreakSdk(run) {
|
|
86
|
+
try {
|
|
87
|
+
const result = run();
|
|
88
|
+
if (result && typeof result.then === 'function') {
|
|
89
|
+
Promise.resolve(result).catch(() => undefined);
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
// Activity logging must not affect the SDK.
|
|
65
93
|
}
|
|
66
|
-
// Native dev: primary Sentry channel (Messages). Release: fallback when Logs fail.
|
|
67
|
-
return true;
|
|
68
94
|
}
|
|
69
|
-
function
|
|
95
|
+
function tryStructuredLog(sentry, level, eventName, attributes) {
|
|
70
96
|
try {
|
|
71
97
|
const loggerFn = sentry.logger?.[level];
|
|
72
98
|
if (typeof loggerFn !== 'function') {
|
|
73
99
|
return false;
|
|
74
100
|
}
|
|
75
101
|
loggerFn.call(sentry.logger, eventName, {
|
|
76
|
-
...
|
|
102
|
+
...attributes,
|
|
77
103
|
event_name: eventName
|
|
78
104
|
});
|
|
79
105
|
return true;
|
|
@@ -81,52 +107,30 @@ function emitStructuredLog(sentry, level, eventName, metadata) {
|
|
|
81
107
|
return false;
|
|
82
108
|
}
|
|
83
109
|
}
|
|
84
|
-
function
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
...base,
|
|
96
|
-
enableLogs: true
|
|
97
|
-
};
|
|
110
|
+
function isUnitTestRun() {
|
|
111
|
+
const env = globalThis.process?.env;
|
|
112
|
+
return env?.NODE_ENV === 'test';
|
|
113
|
+
}
|
|
114
|
+
function deferOnNative(platform, run) {
|
|
115
|
+
const sync = platform === 'web' || isUnitTestRun();
|
|
116
|
+
if (sync) {
|
|
117
|
+
run();
|
|
118
|
+
} else {
|
|
119
|
+
setTimeout(run, 0);
|
|
98
120
|
}
|
|
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
121
|
}
|
|
110
|
-
function
|
|
111
|
-
if (
|
|
122
|
+
function mirrorToDevConsole(level, eventName, metadata) {
|
|
123
|
+
if (!isDevBuild()) {
|
|
112
124
|
return;
|
|
113
125
|
}
|
|
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
|
-
};
|
|
126
|
+
const log = level === 'error' ? console.error : level === 'warn' ? console.warn : console.info;
|
|
127
|
+
log(`[Adaxon activity] ${eventName}`, metadata);
|
|
125
128
|
}
|
|
126
129
|
class AdaxonLoggerService {
|
|
127
130
|
enabled = false;
|
|
128
131
|
platform = 'web';
|
|
129
132
|
sentryReady = false;
|
|
133
|
+
ownsSentryClient = false;
|
|
130
134
|
configure(input) {
|
|
131
135
|
this.enabled = Boolean(input.enabled);
|
|
132
136
|
this.platform = input.platform;
|
|
@@ -141,6 +145,7 @@ class AdaxonLoggerService {
|
|
|
141
145
|
this.enabled = false;
|
|
142
146
|
this.sentryReady = false;
|
|
143
147
|
this.platform = 'web';
|
|
148
|
+
this.ownsSentryClient = false;
|
|
144
149
|
cachedSentryModule = undefined;
|
|
145
150
|
missingSentryPeerWarningShown = false;
|
|
146
151
|
}
|
|
@@ -149,71 +154,90 @@ class AdaxonLoggerService {
|
|
|
149
154
|
return;
|
|
150
155
|
}
|
|
151
156
|
const level = input.level ?? DEFAULT_LEVEL;
|
|
152
|
-
const
|
|
157
|
+
const baseMetadata = {
|
|
153
158
|
...input.metadata,
|
|
154
159
|
sdk_version: SDK_VERSION,
|
|
155
160
|
platform: this.platform,
|
|
156
161
|
timestamp: new Date().toISOString()
|
|
162
|
+
};
|
|
163
|
+
mirrorToDevConsole(level, input.eventName, sanitizeLogMetadata(baseMetadata));
|
|
164
|
+
const sentryAttributes = sanitizeLogMetadata({
|
|
165
|
+
...baseMetadata,
|
|
166
|
+
event_name: input.eventName
|
|
167
|
+
}, {
|
|
168
|
+
forSentry: true
|
|
157
169
|
});
|
|
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
|
-
|
|
170
|
+
deferOnNative(this.platform, () => neverBreakSdk(() => this.pushToSentry(input, level, sentryAttributes)));
|
|
171
|
+
}
|
|
172
|
+
pushToSentry(input, level, sentryAttributes) {
|
|
173
|
+
const sentry = loadSentryModule();
|
|
174
|
+
if (!sentry || !this.sentryReady) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const sentStructuredLog = usesSentryLogs(this.platform) && tryStructuredLog(sentry, level, input.eventName, sentryAttributes);
|
|
178
|
+
const plan = planSentryDelivery(input, level, this.platform, sentStructuredLog, this.ownsSentryClient);
|
|
179
|
+
const tags = {
|
|
180
|
+
adaxon_event: input.eventName,
|
|
181
|
+
adaxon_platform: this.platform,
|
|
182
|
+
adaxon_sdk_version: SDK_VERSION
|
|
183
|
+
};
|
|
184
|
+
if (plan.breadcrumb) {
|
|
185
|
+
const category = plan.breadcrumb;
|
|
186
|
+
neverBreakSdk(() => sentry.addBreadcrumb({
|
|
187
|
+
category,
|
|
188
|
+
message: input.eventName,
|
|
189
|
+
level: sentryLevel(level, 'breadcrumb'),
|
|
190
|
+
data: sentryAttributes,
|
|
191
|
+
timestamp: Date.now() / 1000
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
if (plan.sendMessage && typeof sentry.captureMessage === 'function') {
|
|
195
|
+
neverBreakSdk(() => sentry.captureMessage(input.eventName, {
|
|
196
|
+
level: sentryLevel(level, 'message'),
|
|
197
|
+
tags,
|
|
198
|
+
extra: sentryAttributes
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
if (input.captureAsEvent && input.error !== undefined && this.ownsSentryClient) {
|
|
202
|
+
const err = input.error instanceof Error ? input.error : new Error(AdaxonSdkError.messageOf(input.error, `Adaxon SDK activity: ${input.eventName}`));
|
|
203
|
+
neverBreakSdk(() => sentry.captureException(err, {
|
|
204
|
+
tags,
|
|
205
|
+
extra: sentryAttributes
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
191
208
|
}
|
|
192
209
|
ensureSentry(dsn, testMode) {
|
|
193
|
-
|
|
210
|
+
neverBreakSdk(() => {
|
|
194
211
|
const sentry = loadSentryModule();
|
|
195
212
|
if (!sentry) {
|
|
196
213
|
return;
|
|
197
214
|
}
|
|
215
|
+
this.ownsSentryClient = false;
|
|
198
216
|
if (!sentry.getClient()) {
|
|
199
217
|
if (!dsn?.trim()) {
|
|
200
218
|
return;
|
|
201
219
|
}
|
|
202
|
-
|
|
203
|
-
|
|
220
|
+
sentry.init({
|
|
221
|
+
dsn: dsn.trim(),
|
|
222
|
+
environment: testMode ? 'adaxon-sdk-test' : 'adaxon-sdk',
|
|
223
|
+
enableLogs: usesSentryLogs(this.platform),
|
|
224
|
+
enableAutoConsoleLogs: false,
|
|
225
|
+
enableNative: false,
|
|
226
|
+
autoInitializeNativeSdk: false,
|
|
227
|
+
defaultIntegrations: false,
|
|
228
|
+
integrations: [],
|
|
229
|
+
tracesSampleRate: 0,
|
|
230
|
+
enableAutoSessionTracking: false,
|
|
231
|
+
sendDefaultPii: false
|
|
232
|
+
});
|
|
233
|
+
this.ownsSentryClient = true;
|
|
234
|
+
}
|
|
235
|
+
for (const [key, value] of [['adaxon_sdk_version', SDK_VERSION], ['adaxon_platform', this.platform]]) {
|
|
236
|
+
sentry.setTag(key, value);
|
|
204
237
|
}
|
|
205
|
-
sentry.setTag('adaxon_sdk_version', SDK_VERSION);
|
|
206
|
-
sentry.setTag('adaxon_platform', this.platform);
|
|
207
238
|
this.sentryReady = true;
|
|
208
239
|
});
|
|
209
240
|
}
|
|
210
|
-
safe(run) {
|
|
211
|
-
try {
|
|
212
|
-
run();
|
|
213
|
-
} catch {
|
|
214
|
-
// Logging must never interrupt SDK behavior.
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
241
|
}
|
|
218
242
|
export const AdaxonLogger = new AdaxonLoggerService();
|
|
219
243
|
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