// // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "OWSAnalyticsEvents.h" NS_ASSUME_NONNULL_BEGIN // TODO: We probably don't need all of these levels. typedef NS_ENUM(NSUInteger, OWSAnalyticsSeverity) { // Info events are routine. // // It's safe to discard a large fraction of these events. OWSAnalyticsSeverityInfo = 1, // Error events should never be discarded. OWSAnalyticsSeverityError = 3, // Critical events are special. They are submitted immediately // and not persisted, since the database may not be working. OWSAnalyticsSeverityCritical = 4 }; // This is a placeholder. We don't yet serialize or transmit analytics events. // // If/when we take this on, we'll want to develop a solution that can be used // report user activity - especially serious bugs - without compromising user // privacy in any way. We must _never_ include any identifying information. @interface OWSAnalytics : NSObject // description: A non-empty string without any leading whitespace. // This should conform to our analytics event naming conventions. // "category_event_name", e.g. "database_error_no_database_file_found". // parameters: Optional. // If non-nil, the keys should all be non-empty NSStrings. // Values should be NSStrings or NSNumbers. + (void)logEvent:(NSString *)eventName severity:(OWSAnalyticsSeverity)severity parameters:(nullable NSDictionary *)parameters location:(const char *)location line:(int)line; + (void)appLaunchDidBegin; + (long)orderOfMagnitudeOf:(long)value; @end typedef NSDictionary *_Nonnull (^OWSProdAssertParametersBlock)(void); // These methods should be used to assert errors for which we want to fire analytics events. // // In production, returns __Value, the assert value, so that we can handle this case. // In debug builds, asserts. // // parametersBlock is of type OWSProdAssertParametersBlock. // The "C" variants (e.g. OWSProdAssert() vs. OWSProdCAssert() should be used in free functions, // where there is no self. They can also be used in blocks to avoid capturing a reference to self. #define OWSProdAssertWParamsTemplate(__value, __eventName, __parametersBlock, __assertMacro) \ { \ if (!(BOOL)(__value)) { \ NSDictionary *__eventParameters = (__parametersBlock ? __parametersBlock() : nil); \ [DDLog flushLog]; \ [OWSAnalytics logEvent:__eventName \ severity:OWSAnalyticsSeverityError \ parameters:__eventParameters \ location:__PRETTY_FUNCTION__ \ line:__LINE__]; \ } \ __assertMacro(__value); \ return (BOOL)(__value); \ } #define OWSProdAssertWParams(__value, __eventName, __parametersBlock) \ OWSProdAssertWParamsTemplate(__value, __eventName, __parametersBlock, OWSAssert) #define OWSProdCAssertWParams(__value, __eventName, __parametersBlock) \ OWSProdAssertWParamsTemplate(__value, __eventName, __parametersBlock, OWSCAssert) #define OWSProdAssert(__value, __eventName) OWSProdAssertWParams(__value, __eventName, nil) #define OWSProdCAssert(__value, __eventName) OWSProdCAssertWParams(__value, __eventName, nil) #define OWSProdFailWParamsTemplate(__eventName, __parametersBlock, __failMacro) \ { \ NSDictionary *__eventParameters \ = (__parametersBlock ? ((OWSProdAssertParametersBlock)__parametersBlock)() : nil); \ [OWSAnalytics logEvent:__eventName \ severity:OWSAnalyticsSeverityCritical \ parameters:__eventParameters \ location:__PRETTY_FUNCTION__ \ line:__LINE__]; \ __failMacro(__eventName); \ } #define OWSProdFailWParams(__eventName, __parametersBlock) \ OWSProdFailWParamsTemplate(__eventName, __parametersBlock, OWSFailNoFormat) #define OWSProdCFailWParams(__eventName, __parametersBlock) \ OWSProdFailWParamsTemplate(__eventName, __parametersBlock, OWSCFailNoFormat) #define OWSProdFail(__eventName) OWSProdFailWParams(__eventName, nil) #define OWSProdCFail(__eventName) OWSProdCFailWParams(__eventName, nil) #define OWSProdCFail(__eventName) OWSProdCFailWParams(__eventName, nil) #define OWSProdEventWParams(__severityLevel, __eventName, __parametersBlock) \ { \ NSDictionary *__eventParameters \ = (__parametersBlock ? ((OWSProdAssertParametersBlock)__parametersBlock)() : nil); \ [OWSAnalytics logEvent:__eventName \ severity:__severityLevel \ parameters:__eventParameters \ location:__PRETTY_FUNCTION__ \ line:__LINE__]; \ } #pragma mark - Info Events #define OWSProdInfoWParams(__eventName, __parametersBlock) \ OWSProdEventWParams(OWSAnalyticsSeverityInfo, __eventName, __parametersBlock) #define OWSProdInfo(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityInfo, __eventName, nil) #pragma mark - Error Events #define OWSProdErrorWParams(__eventName, __parametersBlock) \ OWSProdEventWParams(OWSAnalyticsSeverityError, __eventName, __parametersBlock) #define OWSProdError(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityError, __eventName, nil) #pragma mark - Critical Events #define OWSProdCriticalWParams(__eventName, __parametersBlock) \ OWSProdEventWParams(OWSAnalyticsSeverityCritical, __eventName, __parametersBlock) #define OWSProdCritical(__eventName) OWSProdEventWParams(OWSAnalyticsSeverityCritical, __eventName, nil) #pragma mark - OWSMessageManager macros // Defined here rather than in OWSMessageManager so that our analytic event extraction script // can properly detect the event names. // // The debug logs can be more verbose than the analytics events. // // In this case `descriptionForEnvelope` is valuable enough to // log but too dangerous to include in the analytics event. #define OWSProdErrorWEnvelope(__analyticsEventName, __envelope) \ { \ DDLogError(@"%s:%d %@: %@", \ __PRETTY_FUNCTION__, \ __LINE__, \ __analyticsEventName, \ [self descriptionForEnvelope:__envelope]); \ OWSProdError(__analyticsEventName) \ } #define OWSProdInfoWEnvelope(__analyticsEventName, __envelope) \ { \ DDLogInfo(@"%s:%d %@: %@", \ __PRETTY_FUNCTION__, \ __LINE__, \ __analyticsEventName, \ [self descriptionForEnvelope:__envelope]); \ OWSProdInfo(__analyticsEventName) \ } NS_ASSUME_NONNULL_END