2017-01-10 22:41:40 +01:00
|
|
|
//
|
2018-01-10 17:14:02 +01:00
|
|
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
2017-01-10 22:41:40 +01:00
|
|
|
//
|
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
#import "OWSAnalytics.h"
|
2017-11-29 21:27:19 +01:00
|
|
|
#import "AppContext.h"
|
2017-12-15 19:03:03 +01:00
|
|
|
#import "OWSBackgroundTask.h"
|
2018-03-05 15:30:58 +01:00
|
|
|
#import "OWSPrimaryStorage.h"
|
2017-07-26 16:52:15 +02:00
|
|
|
#import "OWSQueues.h"
|
2018-10-16 19:01:11 +02:00
|
|
|
#import "SSKEnvironment.h"
|
2017-12-19 03:42:50 +01:00
|
|
|
#import "YapDatabaseConnection+OWS.h"
|
2017-01-10 22:41:40 +01:00
|
|
|
#import <CocoaLumberjack/CocoaLumberjack.h>
|
2017-07-21 21:08:37 +02:00
|
|
|
#import <Reachability/Reachability.h>
|
2018-09-25 19:09:55 +02:00
|
|
|
#import <SignalCoreKit/Cryptography.h>
|
2017-12-19 03:42:50 +01:00
|
|
|
#import <YapDatabase/YapDatabase.h>
|
2017-01-10 22:41:40 +01:00
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
// TODO: Disable analytics for debug builds.
|
|
|
|
//#define NO_SIGNAL_ANALYTICS
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NSString *const kOWSAnalytics_EventsCollection = @"kOWSAnalytics_EventsCollection";
|
|
|
|
|
|
|
|
// Percentage of analytics events to discard. 0 <= x <= 100.
|
|
|
|
const int kOWSAnalytics_DiscardFrequency = 0;
|
|
|
|
|
2017-07-25 16:29:46 +02:00
|
|
|
NSString *NSStringForOWSAnalyticsSeverity(OWSAnalyticsSeverity severity)
|
|
|
|
{
|
|
|
|
switch (severity) {
|
|
|
|
case OWSAnalyticsSeverityInfo:
|
|
|
|
return @"Info";
|
|
|
|
case OWSAnalyticsSeverityError:
|
|
|
|
return @"Error";
|
|
|
|
case OWSAnalyticsSeverityCritical:
|
|
|
|
return @"Critical";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
@interface OWSAnalytics ()
|
|
|
|
|
|
|
|
@property (nonatomic, readonly) Reachability *reachability;
|
|
|
|
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
|
|
|
|
|
|
|
@property (atomic) BOOL hasRequestInFlight;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
#pragma mark -
|
2017-01-10 22:41:40 +01:00
|
|
|
|
|
|
|
@implementation OWSAnalytics
|
|
|
|
|
|
|
|
+ (instancetype)sharedInstance
|
|
|
|
{
|
|
|
|
static OWSAnalytics *instance = nil;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
2017-07-21 21:08:37 +02:00
|
|
|
instance = [[self alloc] initDefault];
|
2017-01-10 22:41:40 +01:00
|
|
|
});
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2017-07-25 16:29:46 +02:00
|
|
|
// We lazy-create the analytics DB connection, so that we can handle
|
2018-03-05 15:30:58 +01:00
|
|
|
// errors that occur while initializing OWSPrimaryStorage.
|
2017-07-25 16:29:46 +02:00
|
|
|
+ (YapDatabaseConnection *)dbConnection
|
2017-07-21 21:08:37 +02:00
|
|
|
{
|
2018-10-16 19:01:11 +02:00
|
|
|
return SSKEnvironment.shared.analyticsDBConnection;
|
2017-07-21 21:08:37 +02:00
|
|
|
}
|
|
|
|
|
2017-07-25 16:29:46 +02:00
|
|
|
- (instancetype)initDefault
|
2017-07-21 21:08:37 +02:00
|
|
|
{
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
_reachability = [Reachability reachabilityForInternetConnection];
|
|
|
|
|
|
|
|
[self observeNotifications];
|
|
|
|
|
|
|
|
OWSSingletonAssert();
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)observeNotifications
|
|
|
|
{
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
selector:@selector(reachabilityChanged)
|
|
|
|
name:kReachabilityChangedNotification
|
|
|
|
object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
selector:@selector(applicationDidBecomeActive)
|
2018-01-10 17:14:02 +01:00
|
|
|
name:OWSApplicationDidBecomeActiveNotification
|
2017-07-21 21:08:37 +02:00
|
|
|
object:nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)reachabilityChanged
|
|
|
|
{
|
2017-12-19 17:38:25 +01:00
|
|
|
OWSAssertIsOnMainThread();
|
2017-07-21 21:08:37 +02:00
|
|
|
|
|
|
|
[self tryToSyncEvents];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applicationDidBecomeActive
|
2017-01-10 22:41:40 +01:00
|
|
|
{
|
2017-12-19 17:38:25 +01:00
|
|
|
OWSAssertIsOnMainThread();
|
2017-01-10 22:41:40 +01:00
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
[self tryToSyncEvents];
|
2017-01-10 22:41:40 +01:00
|
|
|
}
|
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
- (void)tryToSyncEvents
|
|
|
|
{
|
|
|
|
dispatch_async(self.serialQueue, ^{
|
2017-07-24 21:35:54 +02:00
|
|
|
// Don't try to sync if:
|
|
|
|
//
|
|
|
|
// * There's no network available.
|
|
|
|
// * There's already a sync request in flight.
|
2017-07-25 16:29:46 +02:00
|
|
|
if (!self.reachability.isReachable) {
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogVerbose(@"Not reachable");
|
2017-07-25 16:29:46 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (self.hasRequestInFlight) {
|
2017-07-24 21:35:54 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
__block NSString *firstEventKey = nil;
|
|
|
|
__block NSDictionary *firstEventDictionary = nil;
|
|
|
|
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
// Take any event. We don't need to deliver them in any particular order.
|
|
|
|
[transaction enumerateKeysInCollection:kOWSAnalytics_EventsCollection
|
|
|
|
usingBlock:^(NSString *key, BOOL *_Nonnull stop) {
|
|
|
|
firstEventKey = key;
|
|
|
|
*stop = YES;
|
|
|
|
}];
|
|
|
|
if (!firstEventKey) {
|
|
|
|
return;
|
|
|
|
}
|
2017-07-25 16:29:46 +02:00
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
firstEventDictionary = [transaction objectForKey:firstEventKey inCollection:kOWSAnalytics_EventsCollection];
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(firstEventDictionary);
|
|
|
|
OWSAssertDebug([firstEventDictionary isKindOfClass:[NSDictionary class]]);
|
2017-07-21 21:08:37 +02:00
|
|
|
}];
|
|
|
|
|
2017-07-25 16:29:46 +02:00
|
|
|
if (firstEventDictionary) {
|
|
|
|
[self sendEvent:firstEventDictionary eventKey:firstEventKey isCritical:NO];
|
2017-07-21 21:08:37 +02:00
|
|
|
}
|
2017-07-25 16:29:46 +02:00
|
|
|
});
|
|
|
|
}
|
2017-07-21 21:08:37 +02:00
|
|
|
|
2017-07-25 16:29:46 +02:00
|
|
|
- (void)sendEvent:(NSDictionary *)eventDictionary eventKey:(NSString *)eventKey isCritical:(BOOL)isCritical
|
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(eventDictionary);
|
|
|
|
OWSAssertDebug(eventKey);
|
2017-07-26 16:52:15 +02:00
|
|
|
AssertOnDispatchQueue(self.serialQueue);
|
2017-07-25 16:29:46 +02:00
|
|
|
|
|
|
|
if (isCritical) {
|
|
|
|
[self submitEvent:eventDictionary
|
|
|
|
eventKey:eventKey
|
|
|
|
success:^{
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogDebug(@"sendEvent[critical] succeeded: %@", eventKey);
|
2017-07-25 16:29:46 +02:00
|
|
|
}
|
|
|
|
failure:^{
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogError(@"sendEvent[critical] failed: %@", eventKey);
|
2017-07-25 16:29:46 +02:00
|
|
|
}];
|
|
|
|
} else {
|
2017-07-21 21:08:37 +02:00
|
|
|
self.hasRequestInFlight = YES;
|
2017-07-26 17:58:41 +02:00
|
|
|
__block BOOL isComplete = NO;
|
2017-07-25 16:29:46 +02:00
|
|
|
[self submitEvent:eventDictionary
|
|
|
|
eventKey:eventKey
|
|
|
|
success:^{
|
2017-07-26 16:52:15 +02:00
|
|
|
if (isComplete) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
isComplete = YES;
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogDebug(@"sendEvent succeeded: %@", eventKey);
|
2017-07-25 16:29:46 +02:00
|
|
|
dispatch_async(self.serialQueue, ^{
|
|
|
|
self.hasRequestInFlight = NO;
|
|
|
|
|
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
// Remove from queue.
|
|
|
|
[transaction removeObjectForKey:eventKey inCollection:kOWSAnalytics_EventsCollection];
|
|
|
|
}];
|
|
|
|
|
|
|
|
// Wait a second between network requests / retries.
|
|
|
|
dispatch_after(
|
|
|
|
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
|
[self tryToSyncEvents];
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
failure:^{
|
2017-07-26 16:52:15 +02:00
|
|
|
if (isComplete) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
isComplete = YES;
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogError(@"sendEvent failed: %@", eventKey);
|
2017-07-25 16:29:46 +02:00
|
|
|
dispatch_async(self.serialQueue, ^{
|
|
|
|
self.hasRequestInFlight = NO;
|
|
|
|
|
|
|
|
// Wait a second between network requests / retries.
|
|
|
|
dispatch_after(
|
|
|
|
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
|
[self tryToSyncEvents];
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
2017-07-24 21:35:54 +02:00
|
|
|
|
2017-07-25 16:29:46 +02:00
|
|
|
- (void)submitEvent:(NSDictionary *)eventDictionary
|
|
|
|
eventKey:(NSString *)eventKey
|
2017-12-01 01:57:56 +01:00
|
|
|
success:(void (^_Nonnull)(void))successBlock
|
|
|
|
failure:(void (^_Nonnull)(void))failureBlock
|
2017-07-25 16:29:46 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(eventDictionary);
|
|
|
|
OWSAssertDebug(eventKey);
|
2017-07-26 16:52:15 +02:00
|
|
|
AssertOnDispatchQueue(self.serialQueue);
|
2017-07-24 23:18:15 +02:00
|
|
|
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogDebug(@"submitting: %@", eventKey);
|
2017-07-24 21:35:54 +02:00
|
|
|
|
2017-12-15 19:03:03 +01:00
|
|
|
__block OWSBackgroundTask *backgroundTask =
|
|
|
|
[OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__
|
|
|
|
completionBlock:^(BackgroundTaskState backgroundTaskState) {
|
|
|
|
if (backgroundTaskState == BackgroundTaskState_Success) {
|
|
|
|
successBlock();
|
|
|
|
} else {
|
|
|
|
failureBlock();
|
|
|
|
}
|
|
|
|
}];
|
2017-07-21 21:08:37 +02:00
|
|
|
|
2017-07-25 16:29:46 +02:00
|
|
|
// Until we integrate with an analytics platform, behave as though all event delivery succeeds.
|
|
|
|
dispatch_async(self.serialQueue, ^{
|
2017-12-15 19:03:03 +01:00
|
|
|
backgroundTask = nil;
|
2017-07-21 21:08:37 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
- (dispatch_queue_t)serialQueue
|
|
|
|
{
|
|
|
|
static dispatch_queue_t queue = nil;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
queue = dispatch_queue_create("org.whispersystems.analytics.serial", DISPATCH_QUEUE_SERIAL);
|
|
|
|
});
|
|
|
|
return queue;
|
|
|
|
}
|
|
|
|
|
2017-07-26 20:48:36 +02:00
|
|
|
- (NSString *)operatingSystemVersionString
|
|
|
|
{
|
|
|
|
static NSString *result = nil;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
NSOperatingSystemVersion operatingSystemVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
|
2018-07-18 03:08:53 +02:00
|
|
|
result = [NSString stringWithFormat:@"%lu.%lu.%lu",
|
|
|
|
(unsigned long)operatingSystemVersion.majorVersion,
|
|
|
|
(unsigned long)operatingSystemVersion.minorVersion,
|
|
|
|
(unsigned long)operatingSystemVersion.patchVersion];
|
2017-07-26 20:48:36 +02:00
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
- (NSDictionary<NSString *, id> *)eventSuperProperties
|
|
|
|
{
|
|
|
|
NSMutableDictionary<NSString *, id> *result = [NSMutableDictionary new];
|
2017-07-26 20:48:36 +02:00
|
|
|
result[@"app_version"] = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
|
|
|
result[@"platform"] = @"ios";
|
|
|
|
result[@"ios_version"] = self.operatingSystemVersionString;
|
2017-07-21 21:08:37 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (long)orderOfMagnitudeOf:(long)value
|
|
|
|
{
|
|
|
|
return [OWSAnalytics orderOfMagnitudeOf:value];
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (long)orderOfMagnitudeOf:(long)value
|
|
|
|
{
|
|
|
|
if (value <= 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return (long)round(pow(10, floor(log10(value))));
|
|
|
|
}
|
|
|
|
|
2017-07-25 16:29:46 +02:00
|
|
|
- (void)addEvent:(NSString *)eventName severity:(OWSAnalyticsSeverity)severity properties:(NSDictionary *)properties
|
2017-07-21 21:08:37 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(eventName.length > 0);
|
|
|
|
OWSAssertDebug(properties);
|
2017-07-21 21:08:37 +02:00
|
|
|
|
2017-07-26 23:10:24 +02:00
|
|
|
#ifndef NO_SIGNAL_ANALYTICS
|
2017-07-25 16:29:46 +02:00
|
|
|
BOOL isError = severity == OWSAnalyticsSeverityError;
|
|
|
|
BOOL isCritical = severity == OWSAnalyticsSeverityCritical;
|
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
uint32_t discardValue = arc4random_uniform(101);
|
2017-07-25 16:29:46 +02:00
|
|
|
if (!isError && !isCritical && discardValue < kOWSAnalytics_DiscardFrequency) {
|
2018-08-27 18:00:28 +02:00
|
|
|
OWSLogVerbose(@"Discarding event: %@", eventName);
|
2017-07-21 21:08:37 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-01 01:57:56 +01:00
|
|
|
void (^addEvent)(void) = ^{
|
2017-07-21 21:08:37 +02:00
|
|
|
// Add super properties.
|
|
|
|
NSMutableDictionary *eventProperties = (properties ? [properties mutableCopy] : [NSMutableDictionary new]);
|
|
|
|
[eventProperties addEntriesFromDictionary:self.eventSuperProperties];
|
|
|
|
|
|
|
|
NSDictionary *eventDictionary = [eventProperties copy];
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(eventDictionary);
|
2017-07-21 21:08:37 +02:00
|
|
|
NSString *eventKey = [NSUUID UUID].UUIDString;
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogDebug(@"enqueuing event: %@", eventKey);
|
2017-07-21 21:08:37 +02:00
|
|
|
|
2017-07-25 16:29:46 +02:00
|
|
|
if (isCritical) {
|
|
|
|
// Critical events should not be serialized or enqueued - they should be submitted immediately.
|
|
|
|
[self sendEvent:eventDictionary eventKey:eventKey isCritical:YES];
|
|
|
|
} else {
|
|
|
|
// Add to queue.
|
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
const int kMaxQueuedEvents = 5000;
|
|
|
|
if ([transaction numberOfKeysInCollection:kOWSAnalytics_EventsCollection] > kMaxQueuedEvents) {
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogError(@"Event queue overflow.");
|
2017-07-25 16:29:46 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
[transaction setObject:eventDictionary forKey:eventKey inCollection:kOWSAnalytics_EventsCollection];
|
|
|
|
}];
|
|
|
|
|
|
|
|
[self tryToSyncEvents];
|
|
|
|
}
|
2017-07-24 21:31:15 +02:00
|
|
|
};
|
|
|
|
|
2018-06-20 22:39:53 +02:00
|
|
|
if ([self shouldReportAsync:severity]) {
|
2017-07-25 16:29:46 +02:00
|
|
|
dispatch_async(self.serialQueue, addEvent);
|
2017-07-24 21:31:15 +02:00
|
|
|
} else {
|
2017-07-25 16:29:46 +02:00
|
|
|
dispatch_sync(self.serialQueue, addEvent);
|
2017-07-24 21:31:15 +02:00
|
|
|
}
|
2017-07-21 21:08:37 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (void)logEvent:(NSString *)eventName
|
2017-01-10 22:41:40 +01:00
|
|
|
severity:(OWSAnalyticsSeverity)severity
|
2017-07-21 21:08:37 +02:00
|
|
|
parameters:(nullable NSDictionary *)parameters
|
2017-01-10 22:41:40 +01:00
|
|
|
location:(const char *)location
|
2017-07-21 21:08:37 +02:00
|
|
|
line:(int)line
|
2017-01-10 22:41:40 +01:00
|
|
|
{
|
2017-07-21 21:08:37 +02:00
|
|
|
[[self sharedInstance] logEvent:eventName severity:severity parameters:parameters location:location line:line];
|
|
|
|
}
|
2017-01-10 22:41:40 +01:00
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
- (void)logEvent:(NSString *)eventName
|
|
|
|
severity:(OWSAnalyticsSeverity)severity
|
|
|
|
parameters:(nullable NSDictionary *)parameters
|
|
|
|
location:(const char *)location
|
|
|
|
line:(int)line
|
|
|
|
{
|
2017-01-10 22:41:40 +01:00
|
|
|
DDLogFlag logFlag;
|
|
|
|
switch (severity) {
|
|
|
|
case OWSAnalyticsSeverityInfo:
|
|
|
|
logFlag = DDLogFlagInfo;
|
|
|
|
break;
|
|
|
|
case OWSAnalyticsSeverityError:
|
|
|
|
logFlag = DDLogFlagError;
|
|
|
|
break;
|
|
|
|
case OWSAnalyticsSeverityCritical:
|
|
|
|
logFlag = DDLogFlagError;
|
|
|
|
break;
|
|
|
|
default:
|
2018-08-27 16:29:51 +02:00
|
|
|
OWSFailDebug(@"Unknown severity.");
|
2017-01-10 22:41:40 +01:00
|
|
|
logFlag = DDLogFlagDebug;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Log the event.
|
2017-07-21 21:08:37 +02:00
|
|
|
NSString *logString = [NSString stringWithFormat:@"%s:%d %@", location, line, eventName];
|
2017-01-10 22:41:40 +01:00
|
|
|
if (!parameters) {
|
2018-06-20 22:39:53 +02:00
|
|
|
LOG_MAYBE([self shouldReportAsync:severity], LOG_LEVEL_DEF, logFlag, 0, nil, location, @"%@", logString);
|
2017-01-10 22:41:40 +01:00
|
|
|
} else {
|
2018-06-20 22:39:53 +02:00
|
|
|
LOG_MAYBE([self shouldReportAsync:severity],
|
|
|
|
LOG_LEVEL_DEF,
|
|
|
|
logFlag,
|
|
|
|
0,
|
|
|
|
nil,
|
|
|
|
location,
|
|
|
|
@"%@ %@",
|
|
|
|
logString,
|
|
|
|
parameters);
|
2017-07-21 21:08:37 +02:00
|
|
|
}
|
2018-06-20 22:39:53 +02:00
|
|
|
if (![self shouldReportAsync:severity]) {
|
2017-07-21 21:08:37 +02:00
|
|
|
[DDLog flushLog];
|
2017-01-10 22:41:40 +01:00
|
|
|
}
|
|
|
|
|
2017-07-21 21:08:37 +02:00
|
|
|
NSMutableDictionary *eventProperties = (parameters ? [parameters mutableCopy] : [NSMutableDictionary new]);
|
|
|
|
eventProperties[@"event_location"] = [NSString stringWithFormat:@"%s:%d", location, line];
|
2017-07-25 16:29:46 +02:00
|
|
|
[self addEvent:eventName severity:severity properties:eventProperties];
|
|
|
|
}
|
|
|
|
|
2018-06-20 22:39:53 +02:00
|
|
|
- (BOOL)shouldReportAsync:(OWSAnalyticsSeverity)severity
|
2017-07-25 16:29:46 +02:00
|
|
|
{
|
2018-06-19 23:01:46 +02:00
|
|
|
return severity != OWSAnalyticsSeverityCritical;
|
2017-07-21 21:08:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Logging
|
|
|
|
|
|
|
|
+ (void)appLaunchDidBegin
|
|
|
|
{
|
|
|
|
[self.sharedInstance appLaunchDidBegin];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)appLaunchDidBegin
|
|
|
|
{
|
2017-07-27 18:29:05 +02:00
|
|
|
OWSProdInfo([OWSAnalyticsEvents appLaunch]);
|
2017-07-21 21:08:37 +02:00
|
|
|
}
|
|
|
|
|
2017-01-10 22:41:40 +01:00
|
|
|
@end
|
2017-07-21 21:08:37 +02:00
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|