Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Refactor telemetry keys #3620

Closed
wants to merge 9 commits into from
36 changes: 12 additions & 24 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -444,15 +444,11 @@ - (void)commonInit
_pendingLatitude = NAN;
_pendingLongitude = NAN;

// metrics: map load event
mbgl::LatLng latLng = _mbglMap->getLatLng(padding);
// telemetry: map load event
int zoom = round(_mbglMap->getZoom());

[MGLMapboxEvents pushEvent:MGLEventTypeMapLoad withAttributes:@{
MGLEventKeyLatitude: @(latLng.latitude),
MGLEventKeyLongitude: @(latLng.longitude),
MGLEventKeyZoomLevel: @(zoom),
MGLEventKeyPushEnabled: @([MGLMapboxEvents checkPushEnabled])
MGLEventKeyZoomLevel: @(zoom)
}];
}

Expand Down Expand Up @@ -1060,7 +1056,7 @@ - (void)handlePanGesture:(UIPanGestureRecognizer *)pan

if (pan.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan];
[self trackGestureEvent:MGLEventGesturePanStart];

self.userTrackingMode = MGLUserTrackingModeNone;

Expand Down Expand Up @@ -1093,14 +1089,10 @@ - (void)handlePanGesture:(UIPanGestureRecognizer *)pan

[self notifyGestureDidEndWithDrift:drift];

// metrics: pan end
CGPoint pointInView = CGPointMake([pan locationInView:pan.view].x, [pan locationInView:pan.view].y);
CLLocationCoordinate2D panCoordinate = [self convertPoint:pointInView toCoordinateFromView:pan.view];
// telemetry: pan/drag end
int zoom = round([self zoomLevel]);

[MGLMapboxEvents pushEvent:MGLEventTypeMapDragEnd withAttributes:@{
MGLEventKeyLatitude: @(panCoordinate.latitude),
MGLEventKeyLongitude: @(panCoordinate.longitude),
MGLEventKeyZoomLevel: @(zoom)
}];
}
Expand All @@ -1122,7 +1114,7 @@ - (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch

if (pinch.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch];
[self trackGestureEvent:MGLEventGesturePinchStart];

self.scale = _mbglMap->getScale();

Expand Down Expand Up @@ -1193,7 +1185,7 @@ - (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate

if (rotate.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate];
[self trackGestureEvent:MGLEventGestureRotateStart];

self.angle = MGLRadiansFromDegrees(_mbglMap->getBearing()) * -1;

Expand Down Expand Up @@ -1256,7 +1248,7 @@ - (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap
{
return;
}
[self trackGestureEvent:MGLEventGestureSingleTap forRecognizer:singleTap];
[self trackGestureEvent:MGLEventGestureSingleTap];

CGPoint tapPoint = [singleTap locationInView:self];

Expand Down Expand Up @@ -1301,7 +1293,7 @@ - (void)handleDoubleTapGesture:(UITapGestureRecognizer *)doubleTap

if (doubleTap.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap];
[self trackGestureEvent:MGLEventGestureDoubleTap];
}
else if (doubleTap.state == UIGestureRecognizerStateEnded)
{
Expand Down Expand Up @@ -1333,7 +1325,7 @@ - (void)handleTwoFingerTapGesture:(UITapGestureRecognizer *)twoFingerTap

if (twoFingerTap.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap];
[self trackGestureEvent:MGLEventGestureTwoFingerSingleTap];
}
else if (twoFingerTap.state == UIGestureRecognizerStateEnded)
{
Expand Down Expand Up @@ -1363,7 +1355,7 @@ - (void)handleQuickZoomGesture:(UILongPressGestureRecognizer *)quickZoom

if (quickZoom.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom];
[self trackGestureEvent:MGLEventGestureQuickZoom];

self.scale = _mbglMap->getScale();

Expand Down Expand Up @@ -1404,7 +1396,7 @@ - (void)handleTwoFingerDragGesture:(UIPanGestureRecognizer *)twoFingerDrag

if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag];
[self trackGestureEvent:MGLEventGesturePitchStart];
[self notifyGestureDidBegin];
}
else if (twoFingerDrag.state == UIGestureRecognizerStateBegan || twoFingerDrag.state == UIGestureRecognizerStateChanged)
Expand Down Expand Up @@ -1479,15 +1471,11 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni
return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]);
}

- (void)trackGestureEvent:(NSString *)gestureID forRecognizer:(UIGestureRecognizer *)recognizer
- (void)trackGestureEvent:(NSString *)gestureID
{
CGPoint pointInView = CGPointMake([recognizer locationInView:recognizer.view].x, [recognizer locationInView:recognizer.view].y);
CLLocationCoordinate2D gestureCoordinate = [self convertPoint:pointInView toCoordinateFromView:recognizer.view];
int zoom = round([self zoomLevel]);

[MGLMapboxEvents pushEvent:MGLEventTypeMapTap withAttributes:@{
MGLEventKeyLatitude: @(gestureCoordinate.latitude),
MGLEventKeyLongitude: @(gestureCoordinate.longitude),
MGLEventKeyZoomLevel: @(zoom),
MGLEventKeyGestureID: gestureID
}];
Expand Down
6 changes: 0 additions & 6 deletions platform/ios/src/MGLMapboxEvents.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ extern NSString *const MGLEventKeyCourse;
extern NSString *const MGLEventKeyAltitude;
extern NSString *const MGLEventKeyHorizontalAccuracy;
extern NSString *const MGLEventKeyVerticalAccuracy;
extern NSString *const MGLEventKeyPushEnabled;
extern NSString *const MGLEventKeyEmailEnabled;
extern NSString *const MGLEventKeyGestureID;
extern NSString *const MGLEventKeyArrivalDate;
extern NSString *const MGLEventKeyDepartureDate;
Expand Down Expand Up @@ -63,10 +61,6 @@ typedef NS_MUTABLE_DICTIONARY_OF(NSString *, id) MGLMutableMapboxEventAttributes

+ (BOOL) debugLoggingEnabled;

// You can call these methods from any thread.
//
+ (BOOL) checkPushEnabled;

// You can call this method from any thread.
//
+ (void) flush;
Expand Down
104 changes: 56 additions & 48 deletions platform/ios/src/MGLMapboxEvents.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

#include <sys/sysctl.h>

static const NSUInteger version = 1;
static NSString *const MGLMapboxEventsUserAgent = @"MapboxEventsiOS/1.1";
static const NSUInteger version = 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What’s the difference between version and the version substring in MGLMapboxEventsUserAgent? Why was it possible for them to differ until now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I thought version was used in constructing the API endpoint, but it is not. As it stands, version is sent as a key with every event (and that's the only place it is used).

We bumped the user agent version to 1.1 when the last round of changes happened, at @camilleanne's request.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instrumentile doesn't have control over userAgent, so it sends version -- we might be able to drop it for mobile events by checking the userAgent, but that's why it's around.

static NSString *const MGLMapboxEventsUserAgent = @"MapboxEventsiOS/2.0";
static NSString *MGLMapboxEventsAPIBase = @"https://api.tiles.mapbox.com";

NSString *const MGLEventTypeAppUserTurnstile = @"appUserTurnstile";
Expand All @@ -32,8 +32,6 @@
NSString *const MGLEventKeyAltitude = @"altitude";
NSString *const MGLEventKeyHorizontalAccuracy = @"horizontalAccuracy";
NSString *const MGLEventKeyVerticalAccuracy = @"verticalAccuracy";
NSString *const MGLEventKeyPushEnabled = @"enabled.push";
NSString *const MGLEventKeyEmailEnabled = @"enabled.email";
NSString *const MGLEventKeyGestureID = @"gesture";
NSString *const MGLEventKeyArrivalDate = @"arrivalDate";
NSString *const MGLEventKeyDepartureDate = @"departureDate";
Expand Down Expand Up @@ -69,7 +67,6 @@ @implementation MGLMapboxEventsData
- (instancetype)init {
if (self = [super init]) {
_vendorId = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

_model = [self sysInfoByName:"hw.machine"];
_iOSVersion = [NSString stringWithFormat:@"%@ %@", [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion];
if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) {
Expand Down Expand Up @@ -121,7 +118,6 @@ @interface MGLMapboxEvents () <CLLocationManagerDelegate>
// the main thread, but can be read on any thread.
//
@property (atomic) MGLMapboxEventsData *data;
@property (atomic) NSString *appBundleId;
@property (atomic) NSString *appName;
@property (atomic) NSString *appVersion;
@property (atomic) NSString *appBuildNumber;
Expand Down Expand Up @@ -196,14 +192,14 @@ - (instancetype) init {

self = [super init];
if (self) {
_appBundleId = [[NSBundle mainBundle] bundleIdentifier];
_appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
_appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
_appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
_instanceID = [[NSUUID UUID] UUIDString];

NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString];
_serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
_serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", bundleID, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);

// Configure Events Infrastructure
// ===============================
Expand Down Expand Up @@ -419,39 +415,33 @@ - (void)startUpdatingLocation {
}

- (void) pushTurnstileEvent {

__weak MGLMapboxEvents *weakSelf = self;

dispatch_async(_serialQueue, ^{

MGLMapboxEvents *strongSelf = weakSelf;

if ( ! strongSelf) return;

// Build only IDFV event
NSString *vid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

if (!vid) return;
if ( ! idfv) return;

NSDictionary *vevt = @{
@"event" : MGLEventTypeAppUserTurnstile,
@"created" : [strongSelf.rfc3339DateFormatter stringFromDate:[NSDate date]],
@"appBundleId" : strongSelf.appBundleId,
@"vendorId": vid,
@"version": @(version),
@"instance": strongSelf.instanceID
};
NSDictionary *vevt = @{
@"event" : MGLEventTypeAppUserTurnstile,
@"created" : [strongSelf.rfc3339DateFormatter stringFromDate:[NSDate date]],
@"vendorId": idfv,
@"version": @(version),
@"enabled.telemetry": @([[strongSelf class] isEnabled])
};

// Add to Queue
[_eventQueue addObject:vevt];
[_eventQueue addObject:vevt];

// Flush
[strongSelf flush];
// Flush
[strongSelf flush];

if ([strongSelf debugLoggingEnabled]) {
[strongSelf writeEventToLocalDebugLog:vevt];
}

});
}

Expand All @@ -476,47 +466,64 @@ - (void) pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)

if ( ! strongSelf) return;

// Metrics Collection Has Been Paused
// Telemetry collection has been paused
if (_paused) {
return;
}

if (!event) return;
if ( ! event) return;

MGLMutableMapboxEventAttributes *evt = [MGLMutableMapboxEventAttributes dictionaryWithDictionary:attributeDictionary];
// mapbox-events stock attributes
// Send these keys with every event
[evt setObject:event forKey:@"event"];
[evt setObject:@(version) forKey:@"version"];
[evt setObject:[strongSelf.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"];
[evt setObject:strongSelf.instanceID forKey:@"instance"];
[evt setObject:strongSelf.data.vendorId forKey:@"vendorId"];
[evt setObject:strongSelf.appBundleId forKeyedSubscript:@"appBundleId"];

// mapbox-events-ios stock attributes
[evt setValue:strongSelf.data.model forKey:@"model"];
[evt setValue:strongSelf.data.iOSVersion forKey:@"operatingSystem"];
[evt setValue:[strongSelf deviceOrientation] forKey:@"orientation"];
[evt setValue:@((int)(100 * [UIDevice currentDevice].batteryLevel)) forKey:@"batteryLevel"];
[evt setValue:@(strongSelf.data.scale) forKey:@"resolution"];
[evt setValue:[strongSelf applicationState] forKey:@"applicationState"];

MGLReachability *reachability = [MGLReachability reachabilityForLocalWiFi];
[evt setValue:([reachability isReachableViaWiFi] ? @YES : @NO) forKey:@"wifi"];
// Send with every event, but exclude these keys from location & visit events.
if ( ! [event isEqualToString:MGLEventTypeLocation] && ! [event isEqualToString:MGLEventTypeVisit]) {
[evt setValue:[strongSelf deviceOrientation] forKey:@"orientation"];

[evt setValue:[strongSelf applicationState] forKey:@"applicationState"];
int batteryLevel = roundf(100 * [UIDevice currentDevice].batteryLevel);
[evt setValue:@(batteryLevel) forKey:@"batteryLevel"];

[evt setValue:@([strongSelf contentSizeScale]) forKey:@"accessibilityFontScale"];
UIDeviceBatteryState batteryState = [[UIDevice currentDevice] batteryState];
if (batteryState != UIDeviceBatteryStateUnknown) {
[evt setValue:(batteryState == UIDeviceBatteryStateUnplugged ? @NO : @YES) forKey:@"pluggedIn"];
}

MGLReachability *reachability = [MGLReachability reachabilityForLocalWiFi];
[evt setValue:([reachability isReachableViaWiFi] ? @YES : @NO) forKey:@"wifi"];
}

// Only additionally add IDFV and app instance UUID to location events
if ([event isEqualToString:MGLEventTypeLocation] || [event isEqualToString:MGLEventTypeVisit]) {
[evt setObject:strongSelf.data.vendorId forKey:@"vendorId"];
[evt setObject:strongSelf.instanceID forKey:@"instance"];
}

// Only send these (relatively static) keys with the map load event
if ([event isEqualToString:MGLEventTypeMapLoad]) {
[evt setObject:strongSelf.data.vendorId forKey:@"vendorId"];
[evt setValue:strongSelf.data.model forKey:@"model"];
[evt setValue:strongSelf.data.iOSVersion forKey:@"operatingSystem"];
[evt setValue:@(strongSelf.data.scale) forKey:@"resolution"];
[evt setValue:@([strongSelf contentSizeScale]) forKey:@"accessibilityFontScale"];
[evt setValue:@([strongSelf checkPushEnabled]) forKey:@"enabled.push"];
}

// Make Immutable Version
// Make immutable version
NSDictionary *finalEvent = [NSDictionary dictionaryWithDictionary:evt];

// Put On The Queue
// Put on the queue
[_eventQueue addObject:finalEvent];

// Has Flush Limit Been Reached?
// Has flush limit been reached?
if (_eventQueue.count >= MGLMaximumEventsPerFlush) {
[strongSelf flush];
} else if (_eventQueue.count == 1) {
// If this is first new event on queue start timer,
// If this is first new event on queue, start timer
[strongSelf startTimer];
}

Expand Down Expand Up @@ -775,7 +782,7 @@ - (NSInteger) contentSizeScale {

// Can be called from any thread.
//
+ (BOOL) checkPushEnabled {
- (BOOL) checkPushEnabled {
BOOL (^pushCheckBlock)(void) = ^{
BOOL blockResult;
if ([[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
Expand Down Expand Up @@ -1001,8 +1008,9 @@ - (void) writeEventToLocalDebugLog:(MGLMapboxEventAttributes *)event {
}

if ( ! _debugLogSerialQueue) {
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString];
_debugLogSerialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.debugLog", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
_debugLogSerialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.debugLog", bundleID, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
}

dispatch_sync(_debugLogSerialQueue, ^{
Expand Down