@dynatrace/react-native-plugin 2.341.1 → 2.343.1
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/README.md +37 -9
- package/android/build.gradle +11 -2
- package/android/src/main/java/com/dynatrace/android/agent/DynatraceAppStartModule.kt +9 -31
- package/android/src/main/java/com/dynatrace/android/agent/DynatraceConfigurationModule.kt +1 -0
- package/android/src/main/java/com/dynatrace/android/agent/DynatraceRNBridgeImpl.kt +41 -0
- package/android/src/main/java/com/dynatrace/android/agent/DynatraceUtils.kt +1 -0
- package/android/src/main/java/com/dynatrace/android/agent/ScreenshotSelfMonitor.kt +175 -0
- package/android/src/main/java/com/dynatrace/android/agent/UIChangeScreenshotListener.kt +124 -0
- package/android/src/new/java/com/dynatrace/android/agent/DynatraceRNBridge.kt +33 -0
- package/android/src/old/java/com/dynatrace/android/agent/DynatraceRNBridge.kt +40 -0
- package/files/plugin.gradle +1 -1
- package/instrumentation/libs/UserInteraction.js +1 -1
- package/ios/DTXScreenshotSelfMonitor.h +29 -0
- package/ios/DTXScreenshotSelfMonitor.mm +213 -0
- package/ios/DTXScreenshotSelfMonitorSwizzler.h +15 -0
- package/ios/DTXScreenshotSelfMonitorSwizzler.mm +68 -0
- package/ios/DynatraceRNBridge.h +6 -0
- package/ios/DynatraceRNBridge.mm +53 -0
- package/lib/core/Dynatrace.js +2 -0
- package/lib/core/logging/LogMessages.js +42 -0
- package/lib/features/ui-interaction/IUserInteractionEvent.js +1 -1
- package/lib/features/ui-interaction/Runtime.js +1 -1
- package/lib/next/Dynatrace.js +26 -1
- package/lib/next/events/EventPipeline.js +48 -31
- package/lib/next/events/HttpRequestEventData.js +1 -1
- package/lib/next/events/spec/EventSpecContstants.js +1 -1
- package/lib/next/userAction/NullUserAction.js +15 -0
- package/lib/next/userAction/UserAction.js +2 -0
- package/lib/next/userAction/UserActionConfiguration.js +10 -0
- package/lib/next/userAction/UserActionImpl.js +70 -0
- package/lib/next/util/TraceContextUtils.js +8 -14
- package/lib/next/util/Utils.js +13 -0
- package/package.json +7 -3
- package/public.js +3 -1
- package/react-native-dynatrace.podspec +10 -3
- package/scripts/Config.js +17 -3
- package/scripts/core/InstrumentCall.js +12 -4
- package/src/lib/core/interface/NativeDynatraceBridge.ts +34 -1
- package/types.d.ts +29 -2
|
@@ -83,7 +83,7 @@ const flushTouch = (responderComponentInfo) => {
|
|
|
83
83
|
clearTimeout(timeoutId);
|
|
84
84
|
pendingTouch = null;
|
|
85
85
|
const eventTimestamp = new EventTimestamp_1.EventTimestamp(TimestampProvider_1.defaultTimestampProvider);
|
|
86
|
-
const event = Object.assign(Object.assign({ 'characteristics.has_user_interaction': true, 'ui_element.detected_name': touchedComponentInfo.detectedName, 'ui_element.components': touchedComponentInfo.components, 'ui_element.id': touchedComponentInfo.id, 'interaction.
|
|
86
|
+
const event = Object.assign(Object.assign({ 'characteristics.has_user_interaction': true, 'ui_element.detected_name': touchedComponentInfo.detectedName, 'ui_element.components': touchedComponentInfo.components, 'ui_element.id': touchedComponentInfo.id, 'interaction.type': 'touch', positions, 'ui_element.name_origin': touchedComponentInfo.nameOrigin }, (responderComponentInfo && {
|
|
87
87
|
'ui_element.responder.detected_name': responderComponentInfo.detectedName,
|
|
88
88
|
'ui_element.responder.components': responderComponentInfo.components,
|
|
89
89
|
'ui_element.responder.id': responderComponentInfo.id,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// DTXScreenshotSelfMonitor.h
|
|
3
|
+
//
|
|
4
|
+
// Self-monitoring counterpart of the Session Replay UI-change listener. Instead of capturing a
|
|
5
|
+
// screenshot it only increments the cross-platform screenshot counter via the agent, to estimate
|
|
6
|
+
// how many screenshots Session Replay would take in production.
|
|
7
|
+
//
|
|
8
|
+
// Monitoring is gated by the remote `replay_metrics_enabled` flag delivered through the
|
|
9
|
+
// configuration; it stays dormant until the server enables it.
|
|
10
|
+
//
|
|
11
|
+
|
|
12
|
+
#import <Foundation/Foundation.h>
|
|
13
|
+
#import "ConfigurationSubscriber.h"
|
|
14
|
+
|
|
15
|
+
// Build-time master switch for the screenshot self-monitoring (replay metrics) feature.
|
|
16
|
+
// Defaults to enabled. Disable for a build with `DYNATRACE_REPLAY_METRICS_ENABLED=0 pod install`
|
|
17
|
+
// (the podspec then injects `#define DTX_REPLAY_METRICS_ENABLED 0` via the prefix header). When 0,
|
|
18
|
+
// the monitor is never registered, regardless of the remote `replay_metrics_enabled` flag.
|
|
19
|
+
#ifndef DTX_REPLAY_METRICS_ENABLED
|
|
20
|
+
#define DTX_REPLAY_METRICS_ENABLED 1
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
24
|
+
|
|
25
|
+
@interface DTXScreenshotSelfMonitor : NSObject <ConfigurationSubscriber>
|
|
26
|
+
|
|
27
|
+
@end
|
|
28
|
+
|
|
29
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
//
|
|
2
|
+
// DTXScreenshotSelfMonitor.mm
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#import "DTXScreenshotSelfMonitor.h"
|
|
6
|
+
#import "DTXScreenshotSelfMonitorSwizzler.h"
|
|
7
|
+
#import <UIKit/UIKit.h>
|
|
8
|
+
#import <QuartzCore/QuartzCore.h>
|
|
9
|
+
|
|
10
|
+
static const double kDebounceMs = 500.0;
|
|
11
|
+
static const double kMaxDebounceMs = 2000.0;
|
|
12
|
+
|
|
13
|
+
// Remote feature-flag key in the cross-platform configuration map. Monitoring stays off unless the
|
|
14
|
+
// server delivers this flag as `true`.
|
|
15
|
+
static NSString *const kReplayMetricsConfigKey = @"replay_metrics_enabled";
|
|
16
|
+
|
|
17
|
+
// DEM-26735: replay metrics experiment sunsets on 2026-10-15T00:00:00Z; remove this class after that date.
|
|
18
|
+
static const NSTimeInterval kExperimentExpiryEpoch = 1792022400.0;
|
|
19
|
+
|
|
20
|
+
static BOOL DTXIsReplayMetricsExperimentExpired(void) {
|
|
21
|
+
return [[NSDate date] timeIntervalSince1970] >= kExperimentExpiryEpoch;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Minimal declaration of the agent's HybridBridge facade. The implementation is provided by the
|
|
25
|
+
// Dynatrace framework; we only need the screenshot-tracking entry point here.
|
|
26
|
+
//
|
|
27
|
+
// NOTE: the underlying store moved to ios-core (DynatraceCoreStatic's
|
|
28
|
+
// Assembly.shared.crossPlatformScreenshotTracker). DynatraceCoreStatic is not exposed to this plugin
|
|
29
|
+
// (it is statically merged into the Dynatrace framework and not re-exported), so we keep calling the
|
|
30
|
+
// ObjC HybridBridge facade; the agent's HybridBridge delegates to the ios-core tracker
|
|
31
|
+
// (+[HybridBridge trackCrossPlatformScreenshot] → [Assembly.shared.crossPlatformScreenshotTracker trackScreenshot]).
|
|
32
|
+
@interface HybridBridge : NSObject
|
|
33
|
+
+ (void)trackCrossPlatformScreenshot;
|
|
34
|
+
@end
|
|
35
|
+
|
|
36
|
+
@implementation DTXScreenshotSelfMonitor {
|
|
37
|
+
BOOL _started;
|
|
38
|
+
BOOL _swizzleInstalled;
|
|
39
|
+
BOOL _pending;
|
|
40
|
+
double _lastChangeTime;
|
|
41
|
+
double _firstChangeTime;
|
|
42
|
+
uint32_t _generation;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
- (instancetype)init {
|
|
46
|
+
self = [super init];
|
|
47
|
+
if (self) {
|
|
48
|
+
_started = NO;
|
|
49
|
+
_swizzleInstalled = NO;
|
|
50
|
+
_pending = NO;
|
|
51
|
+
_lastChangeTime = 0;
|
|
52
|
+
_firstChangeTime = 0;
|
|
53
|
+
_generation = 0;
|
|
54
|
+
}
|
|
55
|
+
return self;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
- (void)dealloc {
|
|
59
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
60
|
+
if (_swizzleInstalled) {
|
|
61
|
+
[DTXScreenshotSelfMonitorSwizzler uninstall];
|
|
62
|
+
_swizzleInstalled = NO;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#pragma mark - ConfigurationSubscriber
|
|
67
|
+
|
|
68
|
+
- (void)notifyWithConfiguration:(NSDictionary<NSString *, id> *)configuration {
|
|
69
|
+
id value = configuration[kReplayMetricsConfigKey];
|
|
70
|
+
BOOL flagEnabled = [value isKindOfClass:[NSNumber class]] && [value boolValue];
|
|
71
|
+
BOOL enabled = flagEnabled && !DTXIsReplayMetricsExperimentExpired();
|
|
72
|
+
if (flagEnabled && !enabled) {
|
|
73
|
+
NSLog(@"[dtxScreenshotSelfMonitor] config: %@=YES but experiment expired — treating as disabled", kReplayMetricsConfigKey);
|
|
74
|
+
} else {
|
|
75
|
+
NSLog(@"[dtxScreenshotSelfMonitor] config: %@=%@ → %@", kReplayMetricsConfigKey, value, enabled ? @"ENABLED" : @"DISABLED");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Configuration is delivered on a background queue; start/stop touch UIApplication and the global
|
|
79
|
+
// swizzle, so they must run on the main thread. start/stop are idempotent.
|
|
80
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
81
|
+
if (enabled) {
|
|
82
|
+
[self start];
|
|
83
|
+
} else {
|
|
84
|
+
[self stop];
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#pragma mark - Lifecycle (must be called on the main thread)
|
|
90
|
+
|
|
91
|
+
- (void)start {
|
|
92
|
+
if (_started) {
|
|
93
|
+
NSLog(@"[dtxScreenshotSelfMonitor] start ignored — already started");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
_started = YES;
|
|
97
|
+
|
|
98
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
99
|
+
selector:@selector(appDidBecomeActive)
|
|
100
|
+
name:UIApplicationDidBecomeActiveNotification
|
|
101
|
+
object:nil];
|
|
102
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
103
|
+
selector:@selector(appDidEnterBackground)
|
|
104
|
+
name:UIApplicationDidEnterBackgroundNotification
|
|
105
|
+
object:nil];
|
|
106
|
+
|
|
107
|
+
UIApplicationState appState = [UIApplication sharedApplication].applicationState;
|
|
108
|
+
NSLog(@"[dtxScreenshotSelfMonitor] start (appState=%ld, mainThread=%d)", (long)appState, (int)[NSThread isMainThread]);
|
|
109
|
+
|
|
110
|
+
// Install immediately unless we are backgrounded (avoids observing while there is nothing to do).
|
|
111
|
+
if (appState != UIApplicationStateBackground) {
|
|
112
|
+
[self installSwizzle];
|
|
113
|
+
} else {
|
|
114
|
+
NSLog(@"[dtxScreenshotSelfMonitor] start: app backgrounded — deferring swizzle install to appDidBecomeActive");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
- (void)stop {
|
|
119
|
+
if (!_started) return;
|
|
120
|
+
_started = NO;
|
|
121
|
+
NSLog(@"[dtxScreenshotSelfMonitor] stop");
|
|
122
|
+
|
|
123
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
124
|
+
[self uninstallSwizzle];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#pragma mark - Swizzle install / uninstall
|
|
128
|
+
|
|
129
|
+
- (void)installSwizzle {
|
|
130
|
+
if (_swizzleInstalled) return;
|
|
131
|
+
_swizzleInstalled = YES;
|
|
132
|
+
NSLog(@"[dtxScreenshotSelfMonitor] installing layer swizzle");
|
|
133
|
+
|
|
134
|
+
__weak __typeof__(self) weakSelf = self;
|
|
135
|
+
[DTXScreenshotSelfMonitorSwizzler installWithChangeHandler:^{
|
|
136
|
+
[weakSelf onLayerChanged];
|
|
137
|
+
}];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
- (void)uninstallSwizzle {
|
|
141
|
+
if (!_swizzleInstalled) return;
|
|
142
|
+
_swizzleInstalled = NO;
|
|
143
|
+
NSLog(@"[dtxScreenshotSelfMonitor] uninstalling layer swizzle (disarming handler)");
|
|
144
|
+
|
|
145
|
+
[DTXScreenshotSelfMonitorSwizzler uninstall];
|
|
146
|
+
_generation++;
|
|
147
|
+
_pending = NO;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#pragma mark - App lifecycle
|
|
151
|
+
|
|
152
|
+
- (void)appDidBecomeActive {
|
|
153
|
+
NSLog(@"[dtxScreenshotSelfMonitor] appDidBecomeActive");
|
|
154
|
+
[self installSwizzle];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
- (void)appDidEnterBackground {
|
|
158
|
+
NSLog(@"[dtxScreenshotSelfMonitor] appDidEnterBackground");
|
|
159
|
+
[self uninstallSwizzle];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
#pragma mark - CALayer change callback
|
|
163
|
+
|
|
164
|
+
- (void)onLayerChanged {
|
|
165
|
+
double now = CACurrentMediaTime() * 1000.0;
|
|
166
|
+
_lastChangeTime = now;
|
|
167
|
+
|
|
168
|
+
if (!_pending) {
|
|
169
|
+
_pending = YES;
|
|
170
|
+
_firstChangeTime = now;
|
|
171
|
+
NSLog(@"[dtxScreenshotSelfMonitor] UI change detected — starting %.0fms debounce", kDebounceMs);
|
|
172
|
+
[self scheduleCapture];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#pragma mark - Debounce & capture
|
|
177
|
+
|
|
178
|
+
- (void)scheduleCapture {
|
|
179
|
+
uint32_t gen = _generation;
|
|
180
|
+
__weak __typeof__(self) weakSelf = self;
|
|
181
|
+
dispatch_after(
|
|
182
|
+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDebounceMs * NSEC_PER_MSEC)),
|
|
183
|
+
dispatch_get_main_queue(),
|
|
184
|
+
^{
|
|
185
|
+
__typeof__(self) strongSelf = weakSelf;
|
|
186
|
+
if (!strongSelf || gen != strongSelf->_generation) return;
|
|
187
|
+
[strongSelf performCapture];
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
- (void)performCapture {
|
|
193
|
+
double now = CACurrentMediaTime() * 1000.0;
|
|
194
|
+
double elapsed = now - _lastChangeTime;
|
|
195
|
+
double sinceFirst = now - _firstChangeTime;
|
|
196
|
+
|
|
197
|
+
if (elapsed < kDebounceMs && sinceFirst < kMaxDebounceMs) {
|
|
198
|
+
NSLog(@"[dtxScreenshotSelfMonitor] UI still active (%.0fms since last change) — rescheduling", elapsed);
|
|
199
|
+
[self scheduleCapture];
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
_pending = NO;
|
|
204
|
+
|
|
205
|
+
@try {
|
|
206
|
+
NSLog(@"[dtxScreenshotSelfMonitor] capture settled — calling [HybridBridge trackCrossPlatformScreenshot]");
|
|
207
|
+
[HybridBridge trackCrossPlatformScreenshot];
|
|
208
|
+
} @catch (NSException *exception) {
|
|
209
|
+
NSLog(@"[dtxScreenshotSelfMonitor] trackCrossPlatformScreenshot threw: %@", exception.name);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//
|
|
2
|
+
// DTXScreenshotSelfMonitorSwizzler.h
|
|
3
|
+
//
|
|
4
|
+
// Detects UI changes by swizzling -[CALayer layoutSublayers] and invoking a change handler on
|
|
5
|
+
// the main thread. Used by the cross-platform screenshot self-monitoring feature.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <Foundation/Foundation.h>
|
|
9
|
+
|
|
10
|
+
@interface DTXScreenshotSelfMonitorSwizzler : NSObject
|
|
11
|
+
|
|
12
|
+
+ (void)installWithChangeHandler:(void (^)(void))handler;
|
|
13
|
+
+ (void)uninstall;
|
|
14
|
+
|
|
15
|
+
@end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
//
|
|
2
|
+
// DTXScreenshotSelfMonitorSwizzler.mm
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#import "DTXScreenshotSelfMonitorSwizzler.h"
|
|
6
|
+
#import <QuartzCore/QuartzCore.h>
|
|
7
|
+
#import <objc/runtime.h>
|
|
8
|
+
|
|
9
|
+
static void (^_changeHandler)(void) = nil;
|
|
10
|
+
static BOOL _installed = NO;
|
|
11
|
+
static BOOL _isHandlingChange = NO;
|
|
12
|
+
|
|
13
|
+
static IMP original_layoutSublayers_imp = NULL;
|
|
14
|
+
|
|
15
|
+
static void notifyChange(void) {
|
|
16
|
+
if (!_changeHandler) return;
|
|
17
|
+
if (![NSThread isMainThread]) return;
|
|
18
|
+
if (_isHandlingChange) return;
|
|
19
|
+
|
|
20
|
+
_isHandlingChange = YES;
|
|
21
|
+
_changeHandler();
|
|
22
|
+
_isHandlingChange = NO;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static void swizzled_layoutSublayers(id self, SEL _cmd) {
|
|
26
|
+
((void (*)(id, SEL))original_layoutSublayers_imp)(self, _cmd);
|
|
27
|
+
notifyChange();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@implementation DTXScreenshotSelfMonitorSwizzler
|
|
31
|
+
|
|
32
|
+
+ (void)installWithChangeHandler:(void (^)(void))handler {
|
|
33
|
+
_changeHandler = [handler copy];
|
|
34
|
+
|
|
35
|
+
// Swizzle exactly once for the process lifetime, then drive activity through _changeHandler.
|
|
36
|
+
//
|
|
37
|
+
// We must NOT swizzle/unswizzle on every foreground/background. -[CALayer layoutSublayers] is a
|
|
38
|
+
// shared selector that DynatraceSessionReplay also swizzles; two independent swizzlers each
|
|
39
|
+
// restoring + re-installing the IMP can interleave such that an install captures an
|
|
40
|
+
// already-swizzled IMP as its "original". swizzled_layoutSublayers() then calls back into a
|
|
41
|
+
// swizzled implementation, recursing without bound → stack overflow (EXC_BAD_ACCESS on
|
|
42
|
+
// foreground). Installing once removes all IMP churn and keeps the swizzle chain stable.
|
|
43
|
+
if (_installed) {
|
|
44
|
+
NSLog(@"[dtxScreenshotSelfMonitor] swizzler: handler re-armed (already installed)");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
_installed = YES;
|
|
48
|
+
|
|
49
|
+
Class cls = [CALayer class];
|
|
50
|
+
Method m = class_getInstanceMethod(cls, @selector(layoutSublayers));
|
|
51
|
+
IMP current = method_getImplementation(m);
|
|
52
|
+
// Defensive: never capture our own swizzled function as the original (would self-recurse).
|
|
53
|
+
if (current != (IMP)swizzled_layoutSublayers) {
|
|
54
|
+
original_layoutSublayers_imp = current;
|
|
55
|
+
}
|
|
56
|
+
method_setImplementation(m, (IMP)swizzled_layoutSublayers);
|
|
57
|
+
NSLog(@"[dtxScreenshotSelfMonitor] swizzler: installed on -[CALayer layoutSublayers] (one-time)");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
+ (void)uninstall {
|
|
61
|
+
// Intentionally does NOT restore the original IMP (see installWithChangeHandler:). Restoring is
|
|
62
|
+
// what enables the cross-swizzler recursion above, and it would also clobber any swizzler layered
|
|
63
|
+
// on top of us. We leave the one-time swizzle in place and simply stop notifying — notifyChange()
|
|
64
|
+
// no-ops while _changeHandler is nil, so there is no work and no risk when monitoring is off.
|
|
65
|
+
_changeHandler = nil;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@end
|
package/ios/DynatraceRNBridge.h
CHANGED
|
@@ -22,6 +22,12 @@
|
|
|
22
22
|
+ (void)startView:(NSDictionary<NSString*,id>* _Nullable)fields NS_SWIFT_NAME(startView(fields:));
|
|
23
23
|
+ (void)stopView;
|
|
24
24
|
+ (void)addConfigurationSubscriber:(id<ConfigurationSubscriber>)subscriber;
|
|
25
|
+
+ (void)trackCrossPlatformScreenshot;
|
|
26
|
+
+ (void)setAutomaticUserActionDetection:(BOOL)enabled NS_SWIFT_NAME(setAutomaticUserActionDetection(enabled:));
|
|
27
|
+
+ (void)createUserAction:(NSString *)actionId name:(NSString *)name completeAutomatically:(BOOL)completeAutomatically properties:(NSDictionary<NSString*,id>* _Nullable)properties NS_SWIFT_NAME(createUserAction(actionId:name:completeAutomatically:properties:));
|
|
28
|
+
+ (void)addEventPropertyToUserAction:(NSString *)actionId key:(NSString *)key value:(id _Nullable)value NS_SWIFT_NAME(addEventPropertyToUserAction(actionId:key:value:));
|
|
29
|
+
+ (void)completeUserAction:(NSString *)actionId NS_SWIFT_NAME(completeUserAction(actionId:));
|
|
30
|
+
+ (void)setCompleteUserActionAutomatically:(NSString *)actionId enabled:(BOOL)enabled NS_SWIFT_NAME(setCompleteUserActionAutomatically(actionId:enabled:));
|
|
25
31
|
@end
|
|
26
32
|
|
|
27
33
|
typedef enum : NSUInteger {
|
package/ios/DynatraceRNBridge.mm
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
#import "DynatraceRNBridge.h"
|
|
6
|
+
#import "DTXScreenshotSelfMonitor.h"
|
|
6
7
|
|
|
7
8
|
// For Turbo Module
|
|
8
9
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
@@ -111,6 +112,11 @@ static bool remoteConfigSubscribed;
|
|
|
111
112
|
static NSDictionary<NSString*, id>* lastRuntimeConfiguration;
|
|
112
113
|
static RNConfigurationSubscriber* configurationSubscriber;
|
|
113
114
|
|
|
115
|
+
// Screenshot self-monitoring. Registered once per process; the configuration publisher retains it
|
|
116
|
+
// as a subscriber and the remote `replay_metrics_enabled` flag toggles monitoring on/off.
|
|
117
|
+
static bool screenshotMonitorSubscribed;
|
|
118
|
+
static DTXScreenshotSelfMonitor* screenshotMonitor;
|
|
119
|
+
|
|
114
120
|
RCT_EXPORT_MODULE(DynatraceBridge);
|
|
115
121
|
|
|
116
122
|
- (instancetype) init
|
|
@@ -307,6 +313,18 @@ template<typename T = void> std::enable_if_t<(facebook::react::ReactNativeVersio
|
|
|
307
313
|
}
|
|
308
314
|
remoteConfigSubscribed = YES;
|
|
309
315
|
[HybridBridge addConfigurationSubscriber:(id)configurationSubscriber];
|
|
316
|
+
|
|
317
|
+
// Start the screenshot self-monitor here — same proven-safe timing as the config subscriber
|
|
318
|
+
// above (post-startup). Started once; -start touches UIApplication and the layer swizzle, so it
|
|
319
|
+
// must run on the main thread.
|
|
320
|
+
#if DTX_REPLAY_METRICS_ENABLED
|
|
321
|
+
if (!screenshotMonitorSubscribed) {
|
|
322
|
+
screenshotMonitorSubscribed = YES;
|
|
323
|
+
NSLog(@"[dtxScreenshotSelfMonitor] bridge: registering screenshot self-monitor as config subscriber");
|
|
324
|
+
screenshotMonitor = [[DTXScreenshotSelfMonitor alloc] init];
|
|
325
|
+
[HybridBridge addConfigurationSubscriber:(id)screenshotMonitor];
|
|
326
|
+
}
|
|
327
|
+
#endif
|
|
310
328
|
}
|
|
311
329
|
|
|
312
330
|
- (void)emitToJS:(NSString*)event body:(id)body
|
|
@@ -627,6 +645,41 @@ RCT_EXPORT_METHOD(forwardAppStartEvent:(NSDictionary<NSString*, id>*) attributes
|
|
|
627
645
|
[HybridBridge forwardAppStartEvent:attributes keys:appStartKeys];
|
|
628
646
|
}
|
|
629
647
|
|
|
648
|
+
RCT_EXPORT_METHOD(setAutomaticUserActionDetection:(BOOL)enabled)
|
|
649
|
+
{
|
|
650
|
+
[HybridBridge setAutomaticUserActionDetection:enabled];
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
RCT_EXPORT_METHOD(createUserAction:(NSString *)actionId name:(NSString *)name completeAutomatically:(BOOL)completeAutomatically properties:(NSDictionary<NSString*, id>* _Nullable)properties)
|
|
654
|
+
{
|
|
655
|
+
[HybridBridge createUserAction:actionId name:name completeAutomatically:completeAutomatically properties:properties];
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
RCT_EXPORT_METHOD(addEventStringPropertyToUserAction:(NSString *)actionId key:(NSString *)key value:(NSString *)value)
|
|
659
|
+
{
|
|
660
|
+
[HybridBridge addEventPropertyToUserAction:actionId key:key value:value];
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
RCT_EXPORT_METHOD(addEventDoublePropertyToUserAction:(NSString *)actionId key:(NSString *)key value:(double)value)
|
|
664
|
+
{
|
|
665
|
+
[HybridBridge addEventPropertyToUserAction:actionId key:key value:@(value)];
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
RCT_EXPORT_METHOD(addEventBooleanPropertyToUserAction:(NSString *)actionId key:(NSString *)key value:(BOOL)value)
|
|
669
|
+
{
|
|
670
|
+
[HybridBridge addEventPropertyToUserAction:actionId key:key value:@(value)];
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
RCT_EXPORT_METHOD(completeUserAction:(NSString *)actionId)
|
|
674
|
+
{
|
|
675
|
+
[HybridBridge completeUserAction:actionId];
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
RCT_EXPORT_METHOD(setCompleteUserActionAutomatically:(NSString *)actionId enabled:(BOOL)enabled)
|
|
679
|
+
{
|
|
680
|
+
[HybridBridge setCompleteUserActionAutomatically:actionId enabled:enabled];
|
|
681
|
+
}
|
|
682
|
+
|
|
630
683
|
RCT_EXPORT_METHOD(setGPSLocation:(double)latitude andLongitude: (double)longitude platform: (NSString *) platform)
|
|
631
684
|
{
|
|
632
685
|
if ([self shouldWorkOnIosWithPlatform: platform])
|
package/lib/core/Dynatrace.js
CHANGED
|
@@ -426,4 +426,6 @@ exports.Dynatrace = {
|
|
|
426
426
|
generateTraceContext(traceparent, tracestate) {
|
|
427
427
|
return Dynatrace_1.Dynatrace.generateTraceContext(traceparent, tracestate);
|
|
428
428
|
},
|
|
429
|
+
createUserAction: (configuration) => Dynatrace_1.Dynatrace.createUserAction(configuration),
|
|
430
|
+
setAutomaticUserActionDetection: (enabled) => Dynatrace_1.Dynatrace.setAutomaticUserActionDetection(enabled),
|
|
429
431
|
};
|
|
@@ -36,6 +36,8 @@ var LogMessage;
|
|
|
36
36
|
LogMessage["COMPONENT_NOT_STARTED"] = "COMPONENT_NOT_STARTED";
|
|
37
37
|
LogMessage["CONFIGURATION_DUPLICATE_STARTUP"] = "CONFIGURATION_DUPLICATE_STARTUP";
|
|
38
38
|
LogMessage["CONFIGURATION_SET"] = "CONFIGURATION_SET";
|
|
39
|
+
LogMessage["CREATE_USER_ACTION"] = "CREATE_USER_ACTION";
|
|
40
|
+
LogMessage["CREATE_USER_ACTION_NOT_STARTED"] = "CREATE_USER_ACTION_NOT_STARTED";
|
|
39
41
|
LogMessage["END_SESSION"] = "END_SESSION";
|
|
40
42
|
LogMessage["END_SESSION_NOT_STARTED"] = "END_SESSION_NOT_STARTED";
|
|
41
43
|
LogMessage["ENTER_AUTO_ACTION"] = "ENTER_AUTO_ACTION";
|
|
@@ -115,6 +117,8 @@ var LogMessage;
|
|
|
115
117
|
LogMessage["SEND_SESSION_PROPERTY_EVENT"] = "SEND_SESSION_PROPERTY_EVENT";
|
|
116
118
|
LogMessage["SEND_SESSION_PROPERTY_EVENT_INVALID_TYPE"] = "SEND_SESSION_PROPERTY_EVENT_INVALID_TYPE";
|
|
117
119
|
LogMessage["SESSION_PROPERTIES_FILTERED"] = "SESSION_PROPERTIES_FILTERED";
|
|
120
|
+
LogMessage["SET_AUTOMATIC_USER_ACTION_DETECTION"] = "SET_AUTOMATIC_USER_ACTION_DETECTION";
|
|
121
|
+
LogMessage["SET_AUTOMATIC_USER_ACTION_DETECTION_NOT_STARTED"] = "SET_AUTOMATIC_USER_ACTION_DETECTION_NOT_STARTED";
|
|
118
122
|
LogMessage["SET_BEACON_HEADERS"] = "SET_BEACON_HEADERS";
|
|
119
123
|
LogMessage["SET_BEACON_HEADERS_NOT_STARTED"] = "SET_BEACON_HEADERS_NOT_STARTED";
|
|
120
124
|
LogMessage["SET_CRASH_REPORTING_OPTED_IN"] = "SET_CRASH_REPORTING_OPTED_IN";
|
|
@@ -137,6 +141,10 @@ var LogMessage;
|
|
|
137
141
|
LogMessage["TRACE_CONTEXT_NOT_STARTED"] = "TRACE_CONTEXT_NOT_STARTED";
|
|
138
142
|
LogMessage["TRIM_STRING_VALUE_LIMITED"] = "TRIM_STRING_VALUE_LIMITED";
|
|
139
143
|
LogMessage["UI_INTERACTION_DEBUG"] = "UI_INTERACTION_DEBUG";
|
|
144
|
+
LogMessage["USER_ACTION_ADD_EVENT_PROPERTY"] = "USER_ACTION_ADD_EVENT_PROPERTY";
|
|
145
|
+
LogMessage["USER_ACTION_COMPLETE"] = "USER_ACTION_COMPLETE";
|
|
146
|
+
LogMessage["USER_ACTION_CREATED"] = "USER_ACTION_CREATED";
|
|
147
|
+
LogMessage["USER_ACTION_SET_COMPLETE_AUTOMATICALLY"] = "USER_ACTION_SET_COMPLETE_AUTOMATICALLY";
|
|
140
148
|
LogMessage["VALUE_SIZE_LIMITED"] = "VALUE_SIZE_LIMITED";
|
|
141
149
|
LogMessage["WEB_REQUEST_TIMING_START"] = "WEB_REQUEST_TIMING_START";
|
|
142
150
|
LogMessage["WEB_REQUEST_TIMING_START_FAILED"] = "WEB_REQUEST_TIMING_START_FAILED";
|
|
@@ -281,6 +289,15 @@ exports.LOG_MESSAGES = {
|
|
|
281
289
|
template: ({ config }) => `Configuration set: ${config}`,
|
|
282
290
|
logType: LogType_1.LogType.Info,
|
|
283
291
|
},
|
|
292
|
+
[LogMessage.CREATE_USER_ACTION]: {
|
|
293
|
+
template: ({ customName, completeAutomatically, }) => `createUserAction(${customName}, ${completeAutomatically})`,
|
|
294
|
+
logType: LogType_1.LogType.Info,
|
|
295
|
+
},
|
|
296
|
+
[LogMessage.CREATE_USER_ACTION_NOT_STARTED]: {
|
|
297
|
+
template: 'createUserAction(): React Native plugin has not been started yet! User action will not be created!',
|
|
298
|
+
logType: LogType_1.LogType.Warning,
|
|
299
|
+
documentationUri: 'https://dt-url.net/r2g3975',
|
|
300
|
+
},
|
|
284
301
|
[LogMessage.END_SESSION]: {
|
|
285
302
|
template: 'Dynatrace endSession()',
|
|
286
303
|
logType: LogType_1.LogType.Info,
|
|
@@ -640,6 +657,15 @@ exports.LOG_MESSAGES = {
|
|
|
640
657
|
logType: LogType_1.LogType.Warning,
|
|
641
658
|
documentationUri: 'https://dt-url.net/lsk39cn',
|
|
642
659
|
},
|
|
660
|
+
[LogMessage.SET_AUTOMATIC_USER_ACTION_DETECTION]: {
|
|
661
|
+
template: ({ enabled }) => `setAutomaticUserActionDetection(${enabled})`,
|
|
662
|
+
logType: LogType_1.LogType.Info,
|
|
663
|
+
},
|
|
664
|
+
[LogMessage.SET_AUTOMATIC_USER_ACTION_DETECTION_NOT_STARTED]: {
|
|
665
|
+
template: 'setAutomaticUserActionDetection(): React Native plugin has not been started yet! Setting will not be applied!',
|
|
666
|
+
logType: LogType_1.LogType.Warning,
|
|
667
|
+
documentationUri: 'https://dt-url.net/r2g3975',
|
|
668
|
+
},
|
|
643
669
|
[LogMessage.SET_BEACON_HEADERS]: {
|
|
644
670
|
template: 'setBeaconHeaders(headers)',
|
|
645
671
|
logType: LogType_1.LogType.Info,
|
|
@@ -738,6 +764,22 @@ exports.LOG_MESSAGES = {
|
|
|
738
764
|
template: ({ event }) => `[DT UI]${event}`,
|
|
739
765
|
logType: LogType_1.LogType.Info,
|
|
740
766
|
},
|
|
767
|
+
[LogMessage.USER_ACTION_ADD_EVENT_PROPERTY]: {
|
|
768
|
+
template: ({ key, value, customName, }) => `addEventProperty(${key}, ${value}): in UserAction - ${customName}`,
|
|
769
|
+
logType: LogType_1.LogType.Info,
|
|
770
|
+
},
|
|
771
|
+
[LogMessage.USER_ACTION_COMPLETE]: {
|
|
772
|
+
template: ({ customName }) => `complete(): ${customName}`,
|
|
773
|
+
logType: LogType_1.LogType.Info,
|
|
774
|
+
},
|
|
775
|
+
[LogMessage.USER_ACTION_CREATED]: {
|
|
776
|
+
template: ({ customName, completeAutomatically, actionId, }) => `UserAction created: ${customName} (actionId: ${actionId}, completeAutomatically: ${completeAutomatically})`,
|
|
777
|
+
logType: LogType_1.LogType.Info,
|
|
778
|
+
},
|
|
779
|
+
[LogMessage.USER_ACTION_SET_COMPLETE_AUTOMATICALLY]: {
|
|
780
|
+
template: ({ enabled, customName, }) => `setCompleteAutomatically(${enabled}): ${customName}`,
|
|
781
|
+
logType: LogType_1.LogType.Info,
|
|
782
|
+
},
|
|
741
783
|
[LogMessage.VALUE_SIZE_LIMITED]: {
|
|
742
784
|
template: ({ key, maxLength }) => `restrictingValueSize(): Limiting value of ${key} as maximum value length (${maxLength}) is reached!`,
|
|
743
785
|
logType: LogType_1.LogType.Warning,
|
|
@@ -8,7 +8,7 @@ var UserInteractionEventKey;
|
|
|
8
8
|
UserInteractionEventKey["UiElementComponents"] = "ui_element.components";
|
|
9
9
|
UserInteractionEventKey["UiElementId"] = "ui_element.id";
|
|
10
10
|
UserInteractionEventKey["UiElementNameOrigin"] = "ui_element.name_origin";
|
|
11
|
-
UserInteractionEventKey["
|
|
11
|
+
UserInteractionEventKey["InteractionType"] = "interaction.type";
|
|
12
12
|
UserInteractionEventKey["UiElementResponderDetectedName"] = "ui_element.responder.detected_name";
|
|
13
13
|
UserInteractionEventKey["UiElementResponderComponents"] = "ui_element.responder.components";
|
|
14
14
|
UserInteractionEventKey["UiElementResponderOriginName"] = "ui_element.responder.name_origin";
|
|
@@ -330,7 +330,7 @@ function _an_flat(e) {
|
|
|
330
330
|
[IUserInteractionEvent_1.UserInteractionEventKey.UiElementDetectedName]: (_c = (_b = e === null || e === void 0 ? void 0 : e.ui_element) === null || _b === void 0 ? void 0 : _b.detected_name) !== null && _c !== void 0 ? _c : null,
|
|
331
331
|
[IUserInteractionEvent_1.UserInteractionEventKey.UiElementComponents]: normalizedUiComponents,
|
|
332
332
|
[IUserInteractionEvent_1.UserInteractionEventKey.UiElementId]: (_e = (_d = e === null || e === void 0 ? void 0 : e.ui_element) === null || _d === void 0 ? void 0 : _d.id) !== null && _e !== void 0 ? _e : null,
|
|
333
|
-
[IUserInteractionEvent_1.UserInteractionEventKey.
|
|
333
|
+
[IUserInteractionEvent_1.UserInteractionEventKey.InteractionType]: _an_interaction_type(e),
|
|
334
334
|
positions: Array.isArray(e === null || e === void 0 ? void 0 : e.positions)
|
|
335
335
|
? e.positions.map((pos) => ({
|
|
336
336
|
x: Math.trunc(pos.x),
|
package/lib/next/Dynatrace.js
CHANGED
|
@@ -14,6 +14,8 @@ const EventData_1 = require("./events/EventData");
|
|
|
14
14
|
const SessionPropertyEventData_1 = require("./events/SessionPropertyEventData");
|
|
15
15
|
const ExceptionEventData_1 = require("./events/ExceptionEventData");
|
|
16
16
|
const HttpRequestEventData_1 = require("./events/HttpRequestEventData");
|
|
17
|
+
const NullUserAction_1 = require("./userAction/NullUserAction");
|
|
18
|
+
const UserActionImpl_1 = require("./userAction/UserActionImpl");
|
|
17
19
|
const DynatraceArgValidators_1 = require("./DynatraceArgValidators");
|
|
18
20
|
const TraceContextUtils_1 = require("./util/TraceContextUtils");
|
|
19
21
|
const DECISION_REASON_TEXT = {
|
|
@@ -169,12 +171,14 @@ class DynatraceImpl {
|
|
|
169
171
|
let parsedTraceparent = traceparent === undefined
|
|
170
172
|
? undefined
|
|
171
173
|
: (0, TraceContextUtils_1.parseTraceparent)(traceparent);
|
|
174
|
+
let traceparentFromDynatrace = false;
|
|
172
175
|
if (parsedTraceparent === undefined) {
|
|
173
176
|
traceparent = (0, TraceContextUtils_1.generateTraceparentHeader)();
|
|
174
177
|
parsedTraceparent = (0, TraceContextUtils_1.parseTraceparent)(traceparent);
|
|
175
178
|
tracestate = undefined;
|
|
179
|
+
traceparentFromDynatrace = true;
|
|
176
180
|
}
|
|
177
|
-
tracestate = (0, TraceContextUtils_1.generateTracestate)(parsedTraceparent.parentId, tracestate);
|
|
181
|
+
tracestate = (0, TraceContextUtils_1.generateTracestate)(parsedTraceparent.parentId, traceparentFromDynatrace, tracestate);
|
|
178
182
|
if (tracestate === undefined) {
|
|
179
183
|
this.logger.info(LogMessages_1.LogMessage.TRACE_CONTEXT_DISABLED_OR_INVALID, {
|
|
180
184
|
existingTraceparent: existingTraceparent !== null && existingTraceparent !== void 0 ? existingTraceparent : 'undefined',
|
|
@@ -187,5 +191,26 @@ class DynatraceImpl {
|
|
|
187
191
|
tracestate,
|
|
188
192
|
};
|
|
189
193
|
}
|
|
194
|
+
createUserAction(configuration) {
|
|
195
|
+
if (!ConfigurationHandler_1.ConfigurationHandler.isConfigurationAvailable()) {
|
|
196
|
+
this.logger.info(LogMessages_1.LogMessage.CREATE_USER_ACTION_NOT_STARTED);
|
|
197
|
+
return new NullUserAction_1.NullUserAction();
|
|
198
|
+
}
|
|
199
|
+
this.logger.debug(LogMessages_1.LogMessage.CREATE_USER_ACTION, {
|
|
200
|
+
customName: configuration.customName,
|
|
201
|
+
completeAutomatically: configuration.completeAutomatically,
|
|
202
|
+
});
|
|
203
|
+
return new UserActionImpl_1.UserActionImpl(configuration);
|
|
204
|
+
}
|
|
205
|
+
setAutomaticUserActionDetection(enabled) {
|
|
206
|
+
if (!ConfigurationHandler_1.ConfigurationHandler.isConfigurationAvailable()) {
|
|
207
|
+
this.logger.info(LogMessages_1.LogMessage.SET_AUTOMATIC_USER_ACTION_DETECTION_NOT_STARTED);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
this.logger.debug(LogMessages_1.LogMessage.SET_AUTOMATIC_USER_ACTION_DETECTION, {
|
|
211
|
+
enabled,
|
|
212
|
+
});
|
|
213
|
+
DynatraceBridge_1.DynatraceNative.setAutomaticUserActionDetection(enabled);
|
|
214
|
+
}
|
|
190
215
|
}
|
|
191
216
|
exports.Dynatrace = new DynatraceImpl(TimestampProvider_1.defaultTimestampProvider);
|