Add a “critical” severity level for analytics events.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 9962c936c6
commit 543c05b2c5

@ -42,19 +42,19 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
#define kOWSProdAssertParameterEnvelopeIsLegacy @"envelope_is_legacy" #define kOWSAnalyticsParameterEnvelopeIsLegacy @"envelope_is_legacy"
#define kOWSProdAssertParameterEnvelopeHasContent @"has_content" #define kOWSAnalyticsParameterEnvelopeHasContent @"has_content"
#define kOWSProdAssertParameterEnvelopeDescription @"envelope_description" #define kOWSAnalyticsParameterEnvelopeDescription @"envelope_description"
#define kOWSProdAssertParameterEnvelopeEncryptedLength @"encrypted_length" #define kOWSAnalyticsParameterEnvelopeEncryptedLength @"encrypted_length"
#define AnalyticsParametersFromEnvelope(__envelope) \ #define AnalyticsParametersFromEnvelope(__envelope) \
^{ \ ^{ \
NSData *__encryptedData = __envelope.hasContent ? __envelope.content : __envelope.legacyMessage; \ NSData *__encryptedData = __envelope.hasContent ? __envelope.content : __envelope.legacyMessage; \
return (@{ \ return (@{ \
kOWSProdAssertParameterEnvelopeIsLegacy : @(__envelope.hasLegacyMessage), \ kOWSAnalyticsParameterEnvelopeIsLegacy : @(__envelope.hasLegacyMessage), \
kOWSProdAssertParameterEnvelopeHasContent : @(__envelope.hasContent), \ kOWSAnalyticsParameterEnvelopeHasContent : @(__envelope.hasContent), \
kOWSProdAssertParameterEnvelopeDescription : [self descriptionForEnvelopeType:__envelope], \ kOWSAnalyticsParameterEnvelopeDescription : [self descriptionForEnvelopeType:__envelope], \
kOWSProdAssertParameterEnvelopeEncryptedLength : @(__encryptedData.length), \ kOWSAnalyticsParameterEnvelopeEncryptedLength : @(__encryptedData.length), \
}); \ }); \
} }

@ -121,14 +121,16 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
// The best we can try to do is to discard the current database // The best we can try to do is to discard the current database
// and behave like a clean install. // and behave like a clean install.
OWSProdError(@"storage_error_could_not_load_database"); // NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCritical(@"storage_error_could_not_load_database");
// Try to reset app by deleting database. // Try to reset app by deleting database.
// Disabled resetting storage until we have better data on why this happens. // Disabled resetting storage until we have better data on why this happens.
// [self resetSignalStorage]; // [self resetSignalStorage];
if (![self tryToLoadDatabase]) { if (![self tryToLoadDatabase]) {
OWSProdError(@"storage_error_could_not_load_database_second_attempt"); // NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCritical(@"storage_error_could_not_load_database_second_attempt");
[NSException raise:TSStorageManagerExceptionNameNoDatabase format:@"Failed to initialize database."]; [NSException raise:TSStorageManagerExceptionNameNoDatabase format:@"Failed to initialize database."];
} }
@ -187,9 +189,9 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
OWSProdErrorWParams(@"storage_error_deserialization", ^{ OWSProdErrorWParams(@"storage_error_deserialization", ^{
return (@{ return (@{
@"collection" : collection, @"collection" : collection,
kOWSProdAssertParameterNSExceptionName : exception.name, kOWSAnalyticsParameterNSExceptionName : exception.name,
kOWSProdAssertParameterNSExceptionReason : exception.reason, kOWSAnalyticsParameterNSExceptionReason : exception.reason,
kOWSProdAssertParameterNSExceptionClassName : NSStringFromClass([exception class]), kOWSAnalyticsParameterNSExceptionClassName : NSStringFromClass([exception class]),
}); });
}); });
@throw exception; @throw exception;
@ -260,7 +262,8 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
BOOL success = [ressourceURL setResourceValues:resourcesAttrs error:&error]; BOOL success = [ressourceURL setResourceValues:resourcesAttrs error:&error];
if (error || !success) { if (error || !success) {
OWSProdErrorWNSError(@"storage_error_file_protecion", error); // NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCriticalWNSError(@"storage_error_file_protection", error);
return; return;
} }
} }
@ -358,7 +361,8 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
BOOL shouldHavePassword = [NSFileManager.defaultManager fileExistsAtPath:[self dbPath]]; BOOL shouldHavePassword = [NSFileManager.defaultManager fileExistsAtPath:[self dbPath]];
if (shouldHavePassword) { if (shouldHavePassword) {
OWSProdError(@"storage_error_could_not_load_database_second_attempt"); // NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCritical(@"storage_error_could_not_load_database_second_attempt");
} }
// Try to reset app by deleting database. // Try to reset app by deleting database.
@ -377,7 +381,8 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
NSError *keySetError; NSError *keySetError;
[SAMKeychain setPassword:newDBPassword forService:keychainService account:keychainDBPassAccount error:&keySetError]; [SAMKeychain setPassword:newDBPassword forService:keychainService account:keychainDBPassAccount error:&keySetError];
if (keySetError) { if (keySetError) {
OWSProdErrorWNSError(@"storage_error_could_not_store_database_password", keySetError); // NOTE: This analytics event will never be delivered, since the database isn't working.
OWSProdCriticalWNSError(@"storage_error_could_not_store_database_password", keySetError);
[self deletePasswordFromKeychain]; [self deletePasswordFromKeychain];

@ -6,23 +6,17 @@ NS_ASSUME_NONNULL_BEGIN
// TODO: We probably don't need all of these levels. // TODO: We probably don't need all of these levels.
typedef NS_ENUM(NSUInteger, OWSAnalyticsSeverity) { typedef NS_ENUM(NSUInteger, OWSAnalyticsSeverity) {
OWSAnalyticsSeverityDebug = 0, // Info events are routine.
//
// It's safe to discard a large fraction of these events.
OWSAnalyticsSeverityInfo = 1, OWSAnalyticsSeverityInfo = 1,
OWSAnalyticsSeverityWarn = 2, // Error events should never be discarded.
OWSAnalyticsSeverityError = 3, OWSAnalyticsSeverityError = 3,
// I suspect we'll stage the development of our analytics, // Critical events should never be discarded.
// initially building only a minimal solution: an endpoint which
// ignores most requests, and sends only the highest-severity
// events as email to developers.
//
// This "critical" level of severity is intended for that purpose (for now).
// //
// We might want to have an additional level of severity for // Additionally, to avoid losing critical events they should
// critical (crashing) bugs that occur during app startup. These // be persisted synchronously.
// events should be sent to the service immediately and the app OWSAnalyticsSeverityCritical = 4
// should block until that request completes.
OWSAnalyticsSeverityCritical = 4,
OWSAnalyticsSeverityOff = 5
}; };
// This is a placeholder. We don't yet serialize or transmit analytics events. // This is a placeholder. We don't yet serialize or transmit analytics events.
@ -54,13 +48,32 @@ typedef NS_ENUM(NSUInteger, OWSAnalyticsSeverity) {
typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)(); typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)();
#define kOWSProdAssertParameterDescription @"description" #define kOWSAnalyticsParameterDescription @"description"
#define kOWSProdAssertParameterNSErrorDomain @"nserror_domain" #define kOWSAnalyticsParameterNSErrorDomain @"nserror_domain"
#define kOWSProdAssertParameterNSErrorCode @"nserror_code" #define kOWSAnalyticsParameterNSErrorCode @"nserror_code"
#define kOWSProdAssertParameterNSErrorDescription @"nserror_description" #define kOWSAnalyticsParameterNSErrorDescription @"nserror_description"
#define kOWSProdAssertParameterNSExceptionName @"nsexception_name" #define kOWSAnalyticsParameterNSExceptionName @"nsexception_name"
#define kOWSProdAssertParameterNSExceptionReason @"nsexception_reason" #define kOWSAnalyticsParameterNSExceptionReason @"nsexception_reason"
#define kOWSProdAssertParameterNSExceptionClassName @"nsexception_classname" #define kOWSAnalyticsParameterNSExceptionClassName @"nsexception_classname"
#define AnalyticsParametersFromNSError(__nserror) \
^{ \
return (@{ \
kOWSAnalyticsParameterNSErrorDomain : (__nserror.domain ?: @"unknown"), \
kOWSAnalyticsParameterNSErrorCode : @(__nserror.code), \
kOWSAnalyticsParameterNSErrorDescription : (__nserror.description ?: @"unknown"), \
}); \
}
#define AnalyticsParametersFromNSException(__exception) \
^{ \
return (@{ \
kOWSAnalyticsParameterNSExceptionName : (__exception.name ?: @"unknown"), \
kOWSAnalyticsParameterNSExceptionReason : (__exception.reason ?: @"unknown"), \
kOWSAnalyticsParameterNSExceptionClassName : \
(__exception ? NSStringFromClass([__exception class]) : @"unknown"), \
}); \
}
// These methods should be used to assert errors for which we want to fire analytics events. // These methods should be used to assert errors for which we want to fire analytics events.
// //
@ -116,25 +129,6 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
#define OWSProdCFail(__analyticsEventName) OWSProdCFailWParams(__analyticsEventName, nil) #define OWSProdCFail(__analyticsEventName) OWSProdCFailWParams(__analyticsEventName, nil)
#define AnalyticsParametersFromNSError(__nserror) \
^{ \
return (@{ \
kOWSProdAssertParameterNSErrorDomain : (__nserror.domain ?: @"unknown"), \
kOWSProdAssertParameterNSErrorCode : @(__nserror.code), \
kOWSProdAssertParameterNSErrorDescription : (__nserror.description ?: @"unknown"), \
}); \
}
#define AnalyticsParametersFromNSException(__exception) \
^{ \
return (@{ \
kOWSProdAssertParameterNSExceptionName : (__exception.name ?: @"unknown"), \
kOWSProdAssertParameterNSExceptionReason : (__exception.reason ?: @"unknown"), \
kOWSProdAssertParameterNSExceptionClassName : \
(__exception ? NSStringFromClass([__exception class]) : @"unknown"), \
}); \
}
#define OWSProdFailWNSError(__analyticsEventName, __nserror) \ #define OWSProdFailWNSError(__analyticsEventName, __nserror) \
{ \ { \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __nserror.debugDescription); \ DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __nserror.debugDescription); \
@ -147,28 +141,32 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
OWSProdFailWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \ OWSProdFailWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \
} }
#define OWSProdCFail(__analyticsEventName) OWSProdCFailWParams(__analyticsEventName, nil)
#define OWSProdEventWParams(__severityLevel, __analyticsEventName, __parametersBlock) \ #define OWSProdEventWParams(__severityLevel, __analyticsEventName, __parametersBlock) \
{ \ { \
NSDictionary<NSString *, id> *__eventParameters \ NSDictionary<NSString *, id> *__eventParameters \
= (__parametersBlock ? ((OWSProdAssertParametersBlock)__parametersBlock)() : nil); \ = (__parametersBlock ? ((OWSProdAssertParametersBlock)__parametersBlock)() : nil); \
[OWSAnalytics logEvent:__analyticsEventName \ [OWSAnalytics logEvent:__analyticsEventName \
severity:OWSAnalyticsSeverityCritical \ severity:__severityLevel \
parameters:__eventParameters \ parameters:__eventParameters \
location:__PRETTY_FUNCTION__ \ location:__PRETTY_FUNCTION__ \
line:__LINE__]; \ line:__LINE__]; \
} }
#define OWSProdErrorWParams(__analyticsEventName, __parametersBlock) \ #pragma mark - Info Events
OWSProdEventWParams(OWSAnalyticsSeverityCritical, __analyticsEventName, __parametersBlock)
#define OWSProdError(__analyticsEventName) OWSProdEventWParams(OWSAnalyticsSeverityCritical, __analyticsEventName, nil)
#define OWSProdInfoWParams(__analyticsEventName, __parametersBlock) \ #define OWSProdInfoWParams(__analyticsEventName, __parametersBlock) \
OWSProdEventWParams(OWSAnalyticsSeverityInfo, __analyticsEventName, __parametersBlock) OWSProdEventWParams(OWSAnalyticsSeverityInfo, __analyticsEventName, __parametersBlock)
#define OWSProdInfo(__analyticsEventName) OWSProdEventWParams(OWSAnalyticsSeverityInfo, __analyticsEventName, nil) #define OWSProdInfo(__analyticsEventName) OWSProdEventWParams(OWSAnalyticsSeverityInfo, __analyticsEventName, nil)
#define OWSProdCFail(__analyticsEventName) OWSProdCFailWParams(__analyticsEventName, nil) #pragma mark - Error Events
#define OWSProdErrorWParams(__analyticsEventName, __parametersBlock) \
OWSProdEventWParams(OWSAnalyticsSeverityError, __analyticsEventName, __parametersBlock)
#define OWSProdError(__analyticsEventName) OWSProdEventWParams(OWSAnalyticsSeverityError, __analyticsEventName, nil)
#define OWSProdErrorWNSError(__analyticsEventName, __nserror) \ #define OWSProdErrorWNSError(__analyticsEventName, __nserror) \
{ \ { \
@ -182,4 +180,24 @@ typedef NSDictionary<NSString *, id> *_Nonnull (^OWSProdAssertParametersBlock)()
OWSProdErrorWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \ OWSProdErrorWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \
} }
#pragma mark - Critical Events
#define OWSProdCriticalWParams(__analyticsEventName, __parametersBlock) \
OWSProdEventWParams(OWSAnalyticsSeverityCritical, __analyticsEventName, __parametersBlock)
#define OWSProdCritical(__analyticsEventName) \
OWSProdEventWParams(OWSAnalyticsSeverityCritical, __analyticsEventName, nil)
#define OWSProdCriticalWNSError(__analyticsEventName, __nserror) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __nserror.debugDescription); \
OWSProdCriticalWParams(__analyticsEventName, AnalyticsParametersFromNSError(__nserror)) \
}
#define OWSProdCriticalWNSException(__analyticsEventName, __exception) \
{ \
DDLogError(@"%s:%d %@: %@", __PRETTY_FUNCTION__, __LINE__, __analyticsEventName, __exception); \
OWSProdCriticalWParams(__analyticsEventName, AnalyticsParametersFromNSException(__exception)) \
}
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -223,7 +223,7 @@ const int kOWSAnalytics_DiscardFrequency = 0;
return (long)round(pow(10, floor(log10(value)))); return (long)round(pow(10, floor(log10(value))));
} }
- (void)addEvent:(NSString *)eventName properties:(NSDictionary *)properties - (void)addEvent:(NSString *)eventName async:(BOOL)async properties:(NSDictionary *)properties
{ {
OWSAssert(eventName.length > 0); OWSAssert(eventName.length > 0);
@ -234,7 +234,7 @@ const int kOWSAnalytics_DiscardFrequency = 0;
} }
#ifndef NO_SIGNAL_ANALYTICS #ifndef NO_SIGNAL_ANALYTICS
dispatch_async(self.serialQueue, ^{ void (^writeEvent)() = ^{
// Add super properties. // Add super properties.
NSMutableDictionary *eventProperties = (properties ? [properties mutableCopy] : [NSMutableDictionary new]); NSMutableDictionary *eventProperties = (properties ? [properties mutableCopy] : [NSMutableDictionary new]);
[eventProperties addEntriesFromDictionary:self.eventSuperProperties]; [eventProperties addEntriesFromDictionary:self.eventSuperProperties];
@ -250,12 +250,18 @@ const int kOWSAnalytics_DiscardFrequency = 0;
DDLogError(@"%@ Event queue overflow.", self.tag); DDLogError(@"%@ Event queue overflow.", self.tag);
return; return;
} }
[transaction setObject:eventDictionary forKey:eventKey inCollection:kOWSAnalytics_EventsCollection]; [transaction setObject:eventDictionary forKey:eventKey inCollection:kOWSAnalytics_EventsCollection];
}]; }];
[self tryToSyncEvents]; [self tryToSyncEvents];
}); };
if (async) {
dispatch_async(self.serialQueue, writeEvent);
} else {
dispatch_sync(self.serialQueue, writeEvent);
}
#endif #endif
} }
@ -277,18 +283,11 @@ const int kOWSAnalytics_DiscardFrequency = 0;
DDLogFlag logFlag; DDLogFlag logFlag;
BOOL async = YES; BOOL async = YES;
switch (severity) { switch (severity) {
case OWSAnalyticsSeverityDebug:
logFlag = DDLogFlagDebug;
break;
case OWSAnalyticsSeverityInfo: case OWSAnalyticsSeverityInfo:
logFlag = DDLogFlagInfo; logFlag = DDLogFlagInfo;
break; break;
case OWSAnalyticsSeverityWarn:
logFlag = DDLogFlagWarning;
break;
case OWSAnalyticsSeverityError: case OWSAnalyticsSeverityError:
logFlag = DDLogFlagError; logFlag = DDLogFlagError;
async = NO;
break; break;
case OWSAnalyticsSeverityCritical: case OWSAnalyticsSeverityCritical:
logFlag = DDLogFlagError; logFlag = DDLogFlagError;
@ -313,7 +312,7 @@ const int kOWSAnalytics_DiscardFrequency = 0;
NSMutableDictionary *eventProperties = (parameters ? [parameters mutableCopy] : [NSMutableDictionary new]); NSMutableDictionary *eventProperties = (parameters ? [parameters mutableCopy] : [NSMutableDictionary new]);
eventProperties[@"event_location"] = [NSString stringWithFormat:@"%s:%d", location, line]; eventProperties[@"event_location"] = [NSString stringWithFormat:@"%s:%d", location, line];
[self addEvent:eventName properties:eventProperties]; [self addEvent:eventName async:async properties:eventProperties];
} }
#pragma mark - Logging #pragma mark - Logging

Loading…
Cancel
Save