diff --git a/CHANGELOG.md b/CHANGELOG.md index f136cf3b..a2f39954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,13 @@ The change log has moved to this repo's [GitHub Releases Page](https://github.co ## Release Notes +### 2.2.0 + +- feat: resolve #148 - MemTel: Implement the memory usage Telemetry auto-collection based on the config options. +- feat: resolve #147 - MemTel: Implement necessary Telemetry auto-collection options config settings with the first available option being the memory usage collection. +- feat: resolve #146 - MemTel: Define custom data fields for a Manual Telemetry event to keep the collected data. Implement helpers to manage that data. +- test: resolve #148 - MemTel: Add unit tests. + ### 2.1.0 - feat: resolve #141 - Apply developer options of the persisted payload when sending the payload diff --git a/RollbarAUL.podspec b/RollbarAUL.podspec index 1c14c879..fb41a9ab 100644 --- a/RollbarAUL.podspec +++ b/RollbarAUL.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.1.0" + s.version = "2.2.0" s.name = "RollbarAUL" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC diff --git a/RollbarCommon.podspec b/RollbarCommon.podspec index 339ec334..1e34c20b 100644 --- a/RollbarCommon.podspec +++ b/RollbarCommon.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.1.0" + s.version = "2.2.0" s.name = "RollbarCommon" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC diff --git a/RollbarCommon/Sources/RollbarCommon/DTOs/RollbarDTO+Protected.m b/RollbarCommon/Sources/RollbarCommon/DTOs/RollbarDTO+Protected.m index ac579ce1..3688717d 100644 --- a/RollbarCommon/Sources/RollbarCommon/DTOs/RollbarDTO+Protected.m +++ b/RollbarCommon/Sources/RollbarCommon/DTOs/RollbarDTO+Protected.m @@ -220,4 +220,22 @@ - (void)setInteger:(NSInteger)data forKey:(NSString *)key { [self setNumber:number forKey:key]; } +- (NSTimeInterval)safelyGetTimeIntervalByKey:(NSString *)key + withDefault:(NSTimeInterval)defaultValue { + + NSNumber *value = [self safelyGetNumberByKey:key]; + if (value) { + return value.doubleValue; + } + else { + return defaultValue; + } +} + +- (void)setTimeInterval:(NSTimeInterval)data forKey:(NSString *)key { + + NSNumber *number = @(data); + [self setNumber:number forKey:key]; +} + @end diff --git a/RollbarCommon/Sources/RollbarCommon/RollbarMemoryStatsDescriptors.m b/RollbarCommon/Sources/RollbarCommon/RollbarMemoryStatsDescriptors.m new file mode 100644 index 00000000..46bd6b3f --- /dev/null +++ b/RollbarCommon/Sources/RollbarCommon/RollbarMemoryStatsDescriptors.m @@ -0,0 +1,81 @@ +#import "RollbarMemoryStatsDescriptors.h" + +#pragma mark - Rollbar memorty stats related descriptors + +static NSString *const Rollbar_memory_physical = @"physical"; +static NSString *const Rollbar_memory_physical_totalMB = @"total_MB"; + +static NSString *const Rollbar_memory_vm = @"vm"; +static NSString *const Rollbar_memory_vm_free = @"free_MB"; +static NSString *const Rollbar_memory_vm_active = @"active_MB"; +static NSString *const Rollbar_memory_vm_inactive = @"inactive_MB"; +static NSString *const Rollbar_memory_vm_speculative = @"speculative_MB"; +static NSString *const Rollbar_memory_vm_wired = @"wired_MB"; + +#pragma mark - RollbarMemoryStatsDescriptors class + +static RollbarMemoryStatsDescriptors *singleton = nil; + +@implementation RollbarMemoryStatsDescriptors +{ +@private + NSArray *memoryTypeIndex; + NSArray *physicalMemoryStatsIndex; + NSArray *virtualMemoryStatsIndex; +} + +#pragma mark - Sigleton pattern + ++ (nonnull instancetype)sharedInstance { + + //static RollbarMemoryStatsDescriptors *singleton = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + + singleton = [[[self class] alloc] init]; + singleton->memoryTypeIndex = @[ + // match order of RollbarMemoryStatsType values: + Rollbar_memory_physical, + Rollbar_memory_vm, + ]; + singleton->physicalMemoryStatsIndex = @[ + // match order of RollbarPhysicalMemory values: + Rollbar_memory_physical_totalMB, + ]; + singleton->virtualMemoryStatsIndex = @[ + // match order of RollbarVirtualMemory values: + Rollbar_memory_vm_free, + Rollbar_memory_vm_active, + Rollbar_memory_vm_inactive, + Rollbar_memory_vm_speculative, + Rollbar_memory_vm_wired, + ]; + }); + + return singleton; +} + ++ (BOOL)sharedInstanceExists { + + return (nil != singleton); +} + +#pragma mark - Instance methods + +- (nonnull NSString *)getMemoryStatsTypeDescriptor: (RollbarMemoryStatsType)memoryAttribute { + + return self->memoryTypeIndex[memoryAttribute]; +} + +- (nonnull NSString *)getPhysicalMemoryDescriptor: (RollbarPhysicalMemory)memoryAttribute { + + return self->physicalMemoryStatsIndex[memoryAttribute]; +} + +- (nonnull NSString *)getVirtualMemoryDescriptor: (RollbarVirtualMemory)memoryAttribute { + + return self->virtualMemoryStatsIndex[memoryAttribute]; +} + +@end diff --git a/RollbarCommon/Sources/RollbarCommon/RollbarMemoryUtil.m b/RollbarCommon/Sources/RollbarCommon/RollbarMemoryUtil.m new file mode 100644 index 00000000..9168e2c6 --- /dev/null +++ b/RollbarCommon/Sources/RollbarCommon/RollbarMemoryUtil.m @@ -0,0 +1,136 @@ +#import "RollbarMemoryUtil.h" +#import "RollbarMemoryStatsDescriptors.h" +#import + +static NSByteCountFormatter *formatter = nil; +static const NSInteger bytesInMB = 1024 * 1024; + +@implementation RollbarMemoryUtil + ++ (void)initialize { + + formatter = [[NSByteCountFormatter alloc] init]; + formatter.countStyle = NSByteCountFormatterCountStyleMemory; + formatter.includesActualByteCount = YES; + formatter.adaptive = YES; + formatter.includesUnit = YES; + formatter.zeroPadsFractionDigits = YES; +} + +#pragma mark - memory stats getters + ++ (nonnull NSDictionary *)getMemoryStats { + + NSObject *physicalMemoryStats = [RollbarMemoryUtil getPhysicalMemoryStats]; + NSObject *virtualMemoryStats = [RollbarMemoryUtil getVirtualMemoryStats]; + + return @{ + + [[RollbarMemoryStatsDescriptors sharedInstance] getMemoryStatsTypeDescriptor:RollbarMemoryStatsType_Physical] : + physicalMemoryStats ? physicalMemoryStats : [NSNull null], + [[RollbarMemoryStatsDescriptors sharedInstance] getMemoryStatsTypeDescriptor:RollbarMemoryStatsType_VM] : + virtualMemoryStats ? virtualMemoryStats : [NSNull null], + }; +} + ++ (nullable NSDictionary *)getPhysicalMemoryStats { + + return @{ + [[RollbarMemoryStatsDescriptors sharedInstance] getPhysicalMemoryDescriptor:RollbarPhysicalMemory_TotalMB] : + [NSNumber numberWithUnsignedInteger: [RollbarMemoryUtil getPhysicalMemoryInMBs]], + }; +} + ++ (nullable NSDictionary *)getVirtualMemoryStats { + + vm_statistics_data_t vmStats; + mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT; + kern_return_t kernReturn = host_statistics( + mach_host_self(), + HOST_VM_INFO, + (host_info_t)&vmStats, &infoCount + ); + if(kernReturn != KERN_SUCCESS) { + + return nil; + } + + NSUInteger memoryMBs; + + memoryMBs = [RollbarMemoryUtil convertToMBs:(vm_page_size * vmStats.free_count)]; + NSNumber *freeMemory = [NSNumber numberWithUnsignedInteger:[RollbarMemoryUtil convertToMBs:memoryMBs]]; + + memoryMBs = [RollbarMemoryUtil convertToMBs:(vm_page_size * vmStats.active_count)]; + NSNumber *activeMemory = [NSNumber numberWithUnsignedInteger:[RollbarMemoryUtil convertToMBs:memoryMBs]]; + + memoryMBs = [RollbarMemoryUtil convertToMBs:(vm_page_size * vmStats.inactive_count)]; + NSNumber *inactiveMemory = [NSNumber numberWithUnsignedInteger:[RollbarMemoryUtil convertToMBs:memoryMBs]]; + + memoryMBs = [RollbarMemoryUtil convertToMBs:(vm_page_size * vmStats.speculative_count)]; + NSNumber *speculativeMemory = [NSNumber numberWithUnsignedInteger:[RollbarMemoryUtil convertToMBs:memoryMBs]]; + + memoryMBs = [RollbarMemoryUtil convertToMBs:(vm_page_size * vmStats.wire_count)]; + NSNumber *wiredMemory = [NSNumber numberWithUnsignedInteger:[RollbarMemoryUtil convertToMBs:memoryMBs]]; + + return @{ + + [[RollbarMemoryStatsDescriptors sharedInstance] getVirtualMemoryDescriptor:RollbarVirtualMemory_FreeMB] : + freeMemory ? freeMemory : [NSNull null], + [[RollbarMemoryStatsDescriptors sharedInstance] getVirtualMemoryDescriptor:RollbarVirtualMemory_ActiveMB] : + activeMemory ? activeMemory : [NSNull null], + [[RollbarMemoryStatsDescriptors sharedInstance] getVirtualMemoryDescriptor:RollbarVirtualMemory_InactiveMB] : + inactiveMemory ? inactiveMemory : [NSNull null], + [[RollbarMemoryStatsDescriptors sharedInstance] getVirtualMemoryDescriptor:RollbarVirtualMemory_SpeculativeMB] : + speculativeMemory ? speculativeMemory : [NSNull null], + [[RollbarMemoryStatsDescriptors sharedInstance] getVirtualMemoryDescriptor:RollbarVirtualMemory_WiredMB] : + wiredMemory ? wiredMemory : [NSNull null], + }; +} + ++ (NSUInteger)getPhysicalMemoryInBytes { + + static unsigned long long bytesTotal = 0; + + if (0 == bytesTotal) { + + bytesTotal = [[NSProcessInfo processInfo] physicalMemory]; + } + return (NSUInteger)bytesTotal; +} + +#pragma mark - converters + ++ (NSUInteger)convertToMBs:(NSUInteger)bytesCount { + + NSUInteger result = (bytesCount / bytesInMB); + result += (bytesCount % bytesInMB); + return result; +} + ++ (NSString *)convertToHumanFriendlyFormat:(NSUInteger)bytesCount { + + NSString *result = [formatter stringFromByteCount:bytesCount]; + return result; +} + +#pragma mark - convenience methods + ++ (NSUInteger)getPhysicalMemoryInMBs { + + static NSInteger result = 0; + if (0 == result) { + + result = [RollbarMemoryUtil convertToMBs:[RollbarMemoryUtil getPhysicalMemoryInBytes]]; + } + return result; +} + +#pragma mark - convenience string presenters + ++ (NSString *)getPhysicalMemorySizeWithUnits { + + NSString *result = [RollbarMemoryUtil convertToHumanFriendlyFormat:[RollbarMemoryUtil getPhysicalMemoryInBytes]]; + return result; +} + +@end diff --git a/RollbarCommon/Sources/RollbarCommon/include/RollbarDTO+Protected.h b/RollbarCommon/Sources/RollbarCommon/include/RollbarDTO+Protected.h index 087902a0..3de15f77 100644 --- a/RollbarCommon/Sources/RollbarCommon/include/RollbarDTO+Protected.h +++ b/RollbarCommon/Sources/RollbarCommon/include/RollbarDTO+Protected.h @@ -51,19 +51,33 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Convenience API - (RollbarDTO *)safelyGetDataTransferObjectByKey:(NSString *)key; -- (void)setDataTransferObject:(RollbarDTO *)data forKey:(NSString *)key; +- (void)setDataTransferObject:(RollbarDTO *)data + forKey:(NSString *)key; - (RollbarTriStateFlag)safelyGetTriStateFlagByKey:(NSString *)key; -- (void)setTriStateFlag:(RollbarTriStateFlag)data forKey:(NSString *)key; +- (void)setTriStateFlag:(RollbarTriStateFlag)data + forKey:(NSString *)key; -- (BOOL)safelyGetBoolByKey:(NSString *)key withDefault:(BOOL)defaultValue; -- (void)setBool:(BOOL)data forKey:(NSString *)key; +- (BOOL)safelyGetBoolByKey:(NSString *)key + withDefault:(BOOL)defaultValue; +- (void)setBool:(BOOL)data + forKey:(NSString *)key; -- (NSUInteger)safelyGetUIntegerByKey:(NSString *)key withDefault:(NSUInteger)defaultValue; -- (void)setUInteger:(NSUInteger)data forKey:(NSString *)key; +- (NSUInteger)safelyGetUIntegerByKey:(NSString *)key + withDefault:(NSUInteger)defaultValue; +- (void)setUInteger:(NSUInteger)data + forKey:(NSString *)key; + +- (NSInteger)safelyGetIntegerByKey:(NSString *)key + withDefault:(NSInteger)defaultValue; +- (void)setInteger:(NSInteger)data + forKey:(NSString *)key; + +- (NSTimeInterval)safelyGetTimeIntervalByKey:(NSString *)key + withDefault:(NSTimeInterval)defaultValue; +- (void)setTimeInterval:(NSTimeInterval)data + forKey:(NSString *)key; -- (NSInteger)safelyGetIntegerByKey:(NSString *)key withDefault:(NSInteger)defaultValue; -- (void)setInteger:(NSInteger)data forKey:(NSString *)key; @end diff --git a/RollbarCommon/Sources/RollbarCommon/include/RollbarMemoryStatsDescriptors.h b/RollbarCommon/Sources/RollbarCommon/include/RollbarMemoryStatsDescriptors.h new file mode 100644 index 00000000..429b1918 --- /dev/null +++ b/RollbarCommon/Sources/RollbarCommon/include/RollbarMemoryStatsDescriptors.h @@ -0,0 +1,65 @@ +// +// RollbarMemoryStatsDescriptors.h +// +// +// Created by Andrey Kornich on 2022-04-07. +// + +@import Foundation; + +#pragma mark - RollbarMemoryStatsType enum + +typedef NS_ENUM(NSUInteger, RollbarMemoryStatsType) { + RollbarMemoryStatsType_Physical = 0, + RollbarMemoryStatsType_VM, +}; + +#pragma mark - RollbarPhysicalMemory enum + +typedef NS_ENUM(NSUInteger, RollbarPhysicalMemory) { + RollbarPhysicalMemory_TotalMB = 0, +}; + +#pragma mark - RollbarVirtualMemory enum + +typedef NS_ENUM(NSUInteger, RollbarVirtualMemory) { + RollbarVirtualMemory_FreeMB = 0, + RollbarVirtualMemory_ActiveMB, + RollbarVirtualMemory_InactiveMB, + RollbarVirtualMemory_SpeculativeMB, + RollbarVirtualMemory_WiredMB +}; + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - RollbarMemoryStatsDescriptors class + +@interface RollbarMemoryStatsDescriptors : NSObject +{ +} + +#pragma mark - Sigleton pattern + ++ (nonnull instancetype)sharedInstance; ++ (BOOL)sharedInstanceExists; + ++ (instancetype)new NS_UNAVAILABLE; ++ (instancetype)allocWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (instancetype)alloc NS_UNAVAILABLE; ++ (id)copyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (id)mutableCopyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; + +- (instancetype)init NS_UNAVAILABLE; +- (void)dealloc NS_UNAVAILABLE; +- (id)copy NS_UNAVAILABLE; +- (id)mutableCopy NS_UNAVAILABLE; + +#pragma mark - Instance methods + +- (nonnull NSString *)getMemoryStatsTypeDescriptor: (RollbarMemoryStatsType)memoryAttribute; +- (nonnull NSString *)getPhysicalMemoryDescriptor: (RollbarPhysicalMemory)memoryAttribute; +- (nonnull NSString *)getVirtualMemoryDescriptor: (RollbarVirtualMemory)memoryAttribute; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarCommon/Sources/RollbarCommon/include/RollbarMemoryUtil.h b/RollbarCommon/Sources/RollbarCommon/include/RollbarMemoryUtil.h new file mode 100644 index 00000000..a3a6519d --- /dev/null +++ b/RollbarCommon/Sources/RollbarCommon/include/RollbarMemoryUtil.h @@ -0,0 +1,44 @@ +// +// RollbarMemoryUtil.h +// +// +// Created by Andrey Kornich on 2022-04-07. +// + +@import Foundation; + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarMemoryUtil : NSObject + +#pragma mark - memory stats getters + ++ (nonnull NSDictionary *)getMemoryStats; ++ (nullable NSDictionary *)getPhysicalMemoryStats; ++ (nullable NSDictionary *)getVirtualMemoryStats; ++ (NSUInteger)getPhysicalMemoryInBytes; + +#pragma mark - converters + ++ (NSUInteger)convertToMBs:(NSUInteger)bytesCount; ++ (NSString *)convertToHumanFriendlyFormat:(NSUInteger)bytesCount; + +#pragma mark - convenience methods + ++ (NSUInteger)getPhysicalMemoryInMBs; + +#pragma mark - convenience string presenters + ++ (NSString *)getPhysicalMemorySizeWithUnits; + +#pragma mark - static utility nature + ++ (instancetype)new NS_UNAVAILABLE; ++ (instancetype)allocWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (instancetype)alloc NS_UNAVAILABLE; ++ (id)copyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (id)mutableCopyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarCommon/Tests/RollbarCommonTests/RollbarMemoryStatsDescriptorsTests.swift b/RollbarCommon/Tests/RollbarCommonTests/RollbarMemoryStatsDescriptorsTests.swift new file mode 100644 index 00000000..2631cba2 --- /dev/null +++ b/RollbarCommon/Tests/RollbarCommonTests/RollbarMemoryStatsDescriptorsTests.swift @@ -0,0 +1,48 @@ +import XCTest +import Foundation +@testable import RollbarCommon + +final class RollbarMemoryStatsDescriptorsTests: XCTestCase { + + func testRollbarMemoryStatsDescriptors_basics() { + + XCTAssertEqual( + "physical", + RollbarMemoryStatsDescriptors.sharedInstance().getMemoryStatsTypeDescriptor(RollbarMemoryStatsType.physical) + ); + XCTAssertEqual( + "vm", + RollbarMemoryStatsDescriptors.sharedInstance().getMemoryStatsTypeDescriptor(RollbarMemoryStatsType.VM) + ); + + XCTAssertEqual( + "total_MB", + RollbarMemoryStatsDescriptors.sharedInstance().getPhysicalMemoryDescriptor(RollbarPhysicalMemory.totalMB) + ); + + XCTAssertEqual( + "free_MB", + RollbarMemoryStatsDescriptors.sharedInstance().getVirtualMemoryDescriptor(RollbarVirtualMemory.freeMB) + ); + XCTAssertEqual( + "active_MB", + RollbarMemoryStatsDescriptors.sharedInstance().getVirtualMemoryDescriptor(RollbarVirtualMemory.activeMB) + ); + XCTAssertEqual( + "inactive_MB", + RollbarMemoryStatsDescriptors.sharedInstance().getVirtualMemoryDescriptor(RollbarVirtualMemory.inactiveMB) + ); + XCTAssertEqual( + "speculative_MB", + RollbarMemoryStatsDescriptors.sharedInstance().getVirtualMemoryDescriptor(RollbarVirtualMemory.speculativeMB) + ); + XCTAssertEqual( + "wired_MB", + RollbarMemoryStatsDescriptors.sharedInstance().getVirtualMemoryDescriptor(RollbarVirtualMemory.wiredMB) + ); + } + + static var allTests = [ + ("testRollbarMemoryStatsDescriptors_basics", testRollbarMemoryStatsDescriptors_basics), + ] +} diff --git a/RollbarCommon/Tests/RollbarCommonTests/RollbarMemoryUtilTests.swift b/RollbarCommon/Tests/RollbarCommonTests/RollbarMemoryUtilTests.swift new file mode 100644 index 00000000..b7c53f6e --- /dev/null +++ b/RollbarCommon/Tests/RollbarCommonTests/RollbarMemoryUtilTests.swift @@ -0,0 +1,36 @@ +import XCTest +import Foundation +@testable import RollbarCommon + +final class RollbarMemoryUtilTests: XCTestCase { + + func testRollbarMemoryUtil_Basics() throws { + + let memoryStats = RollbarMemoryUtil.getMemoryStats(); + + print(memoryStats); + + XCTAssertTrue(memoryStats["physical"] is NSDictionary); + let physicalMemoryStats = memoryStats["physical"] as! NSDictionary; + XCTAssertTrue((physicalMemoryStats["total_MB"] as! NSNumber).uintValue > 0); + + XCTAssertTrue(memoryStats["vm"] is NSDictionary); + let virtualMemoryStats = memoryStats["vm"] as! NSDictionary; + XCTAssertTrue((virtualMemoryStats["free_MB"] as! NSNumber).uintValue > 0); + XCTAssertTrue((virtualMemoryStats["active_MB"] as! NSNumber).uintValue > 0); + XCTAssertTrue((virtualMemoryStats["inactive_MB"] as! NSNumber).uintValue > 0); + XCTAssertTrue((virtualMemoryStats["speculative_MB"] as! NSNumber).uintValue > 0); + XCTAssertTrue((virtualMemoryStats["wired_MB"] as! NSNumber).uintValue > 0); + } + + func testRollbarMemoryUtil_Performance() throws { + + measure { + _ = RollbarMemoryUtil.getMemoryStats(); + } + } + static var allTests = [ + ("testRollbarMemoryUtil_basics", testRollbarMemoryUtil_Basics), + ("testPerformance", testRollbarMemoryUtil_Performance) + ] +} diff --git a/RollbarDeploys.podspec b/RollbarDeploys.podspec index f3f230c9..56df0257 100644 --- a/RollbarDeploys.podspec +++ b/RollbarDeploys.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.1.0" + s.version = "2.2.0" s.name = "RollbarDeploys" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC diff --git a/RollbarKSCrash.podspec b/RollbarKSCrash.podspec index 54fe7e25..a4a52870 100644 --- a/RollbarKSCrash.podspec +++ b/RollbarKSCrash.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.1.0" + s.version = "2.2.0" s.name = "RollbarKSCrash" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC diff --git a/RollbarNotifier.podspec b/RollbarNotifier.podspec index 72c1e3a5..26ab0e65 100644 --- a/RollbarNotifier.podspec +++ b/RollbarNotifier.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.1.0" + s.version = "2.2.0" s.name = "RollbarNotifier" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarConfig.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarConfig.m index 153aacb6..d6797b75 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarConfig.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarConfig.m @@ -14,7 +14,7 @@ #pragma mark - constants -static NSString * const NOTIFIER_VERSION = @"2.1.0"; +static NSString * const NOTIFIER_VERSION = @"2.2.0"; static NSString * const NOTIFIER_NAME = @"rollbar-apple"; diff --git a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarTelemetryOptions.m b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarTelemetryOptions.m index 4f5c3289..33d6eb87 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarTelemetryOptions.m +++ b/RollbarNotifier/Sources/RollbarNotifier/DTOs/RollbarTelemetryOptions.m @@ -7,6 +7,7 @@ static BOOL const DEFAULT_CAPTURE_LOG_FLAG = NO; static BOOL const DEFAULT_CAPTURE_CONNECTIVITY_FLAG = NO; static NSUInteger const DEFAULT_MAX_TELEMETRY_DATA = 10; +static NSTimeInterval const DEFAULT_AUTOCOLLECTION_INTERVAL_MEM_STATS = 0; #pragma mark - data field keys @@ -15,6 +16,7 @@ static NSString *const DFK_CAPTURE_CONNECTIVITY_FLAG = @"captureConnectivity"; static NSString *const DFK_MAX_TELEMETRY_DATA = @"maximumTelemetryData"; static NSString *const DFK_VIEW_INPUTS_SCRUBBER = @"vewInputsScrubber"; +static NSString *const DFK_AUTOCOLLECTION_INTERVAL_MEM_STATS = @"memoryStatsAutocollectionInterval"; #pragma mark - class implementation @@ -29,11 +31,12 @@ - (instancetype)initWithEnabled:(BOOL)enabled viewInputsScrubber:(RollbarScrubbingOptions *)viewInputsScrubber { self = [super initWithDictionary:@{ - DFK_ENABLED_FLAG:[NSNumber numberWithBool:enabled], - DFK_CAPTURE_LOG_FLAG:[NSNumber numberWithBool:captureLog], - DFK_CAPTURE_CONNECTIVITY_FLAG:[NSNumber numberWithBool:captureConnectivity], - DFK_VIEW_INPUTS_SCRUBBER:viewInputsScrubber.jsonFriendlyData, - DFK_MAX_TELEMETRY_DATA:[NSNumber numberWithUnsignedInt:DEFAULT_MAX_TELEMETRY_DATA] + DFK_ENABLED_FLAG : [NSNumber numberWithBool:enabled], + DFK_CAPTURE_LOG_FLAG : [NSNumber numberWithBool:captureLog], + DFK_CAPTURE_CONNECTIVITY_FLAG : [NSNumber numberWithBool:captureConnectivity], + DFK_VIEW_INPUTS_SCRUBBER : viewInputsScrubber.jsonFriendlyData, + DFK_MAX_TELEMETRY_DATA : [NSNumber numberWithUnsignedInt:DEFAULT_MAX_TELEMETRY_DATA], + DFK_AUTOCOLLECTION_INTERVAL_MEM_STATS : [NSNumber numberWithDouble:DEFAULT_AUTOCOLLECTION_INTERVAL_MEM_STATS], }]; return self; } @@ -119,4 +122,14 @@ - (void)setViewInputsScrubber:(RollbarScrubbingOptions *)scrubber { [self setDataTransferObject:scrubber forKey:DFK_VIEW_INPUTS_SCRUBBER]; } +- (NSTimeInterval)memoryStatsAutocollectionInterval { + NSTimeInterval result = [self safelyGetTimeIntervalByKey:DFK_AUTOCOLLECTION_INTERVAL_MEM_STATS + withDefault:DEFAULT_AUTOCOLLECTION_INTERVAL_MEM_STATS]; + return result; +} + +- (void)setMemoryStatsAutocollectionInterval:(NSTimeInterval)value { + [self setTimeInterval:value forKey:DFK_AUTOCOLLECTION_INTERVAL_MEM_STATS]; +} + @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/Rollbar.m b/RollbarNotifier/Sources/RollbarNotifier/Rollbar.m index a0c87dfa..e22940ae 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/Rollbar.m +++ b/RollbarNotifier/Sources/RollbarNotifier/Rollbar.m @@ -5,6 +5,7 @@ #import "RollbarConfig.h" #import "RollbarDestination.h" #import "RollbarLoggingOptions.h" +#import "RollbarTelemetryThread.h" #import "RollbarTelemetryOptions.h" #import "RollbarTelemetryOptionsObserver.h" #import "RollbarScrubbingOptions.h" @@ -60,7 +61,6 @@ + (void)initWithAccessToken:(nullable NSString *)accessToken configuration:(nullable RollbarConfig *)configuration crashCollector:(nullable id)crashCollector { - [RollbarTelemetry sharedInstance]; // Load saved data, if any if (logger) { RollbarSdkLog(@"Rollbar has already been initialized."); @@ -108,12 +108,8 @@ + (void)updateConfiguration:(RollbarConfig *)configuration { } if (configuration && configuration.telemetry) { - [RollbarTelemetry sharedInstance].enabled = configuration.telemetry.enabled; - [RollbarTelemetry sharedInstance].scrubViewInputs = configuration.telemetry.viewInputsScrubber.enabled; - [RollbarTelemetry sharedInstance].viewInputsToScrub = - [NSSet setWithArray:configuration.telemetry.viewInputsScrubber.scrubFields].mutableCopy; - [[RollbarTelemetry sharedInstance] setCaptureLog:configuration.telemetry.captureLog]; - [[RollbarTelemetry sharedInstance] setDataLimit:configuration.telemetry.maximumTelemetryData]; + + [[RollbarTelemetry sharedInstance] configureWithOptions:configuration.telemetry]; } } diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger.m index f7b5d4bb..9afe48b5 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger.m +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarLogger.m @@ -9,6 +9,7 @@ #import "RollbarLogger.h" #import "RollbarLogger+Test.h" #import "RollbarThread.h" +#import "RollbarTelemetryThread.h" #import "RollbarReachability.h" #import #import "RollbarTelemetry.h" @@ -111,9 +112,10 @@ + (void)initialize { [self saveQueueState]; } + RollbarConfig *config = [RollbarConfig new]; + // Setup the worker thread that sends the items that have been queued up in the item file set above: // TODO: !!! this needs to be redesigned taking in account multiple access tokens and endpoints !!! - RollbarConfig *config = [RollbarConfig new]; RollbarLogger *logger = [[RollbarLogger alloc] initWithConfiguration:config]; rollbarThread = [[RollbarThread alloc] initWithNotifier:logger @@ -164,6 +166,7 @@ - (instancetype)initWithAccessToken:(NSString *)accessToken { - (instancetype)initWithConfiguration:(RollbarConfig *)configuration { if ((self = [super init])) { + [self updateConfiguration:configuration]; NSString *cachesDirectory = [RollbarCachesDirectory directory]; diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetry.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetry.m index ca54d9bf..a8b11b5f 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetry.m +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetry.m @@ -3,6 +3,9 @@ #import "RollbarTelemetry.h" #import "RollbarCachesDirectory.h" +#import "RollbarTelemetryOptions.h" +#import "RollbarScrubbingOptions.h" +#import "RollbarTelemetryThread.h" #import "RollbarTelemetryEvent.h" #import "RollbarTelemetryBody.h" #import "RollbarTelemetryLogBody.h" @@ -18,6 +21,8 @@ static BOOL captureLog = false; +static RollbarTelemetry * _Nullable singleton = nil; + // this queue is used for serializing state changes to the various // state in this class: captureLog, limit, dataArray static dispatch_queue_t queue = nil; @@ -32,18 +37,7 @@ @implementation RollbarTelemetry { NSString *_dataFilePath; } -+ (instancetype)sharedInstance { - - static RollbarTelemetry *sharedInstance = nil; - static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - sharedInstance = [[RollbarTelemetry alloc] init]; - queue = dispatch_queue_create("com.rollbar.telemetryQueue", DISPATCH_QUEUE_SERIAL); - fileQueue = dispatch_queue_create("com.rollbar.telemetryFileQueue", DISPATCH_QUEUE_SERIAL); - }); - return sharedInstance; -} +#pragma mark - NSLog redirection + (void)NSLogReplacement:(NSString *)format, ... { @@ -63,6 +57,25 @@ + (void)NSLogReplacement:(NSString *)format, ... { va_end(args); } +#pragma mark - Singleton pattern + ++ (instancetype)sharedInstance { + + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + singleton = [[RollbarTelemetry alloc] init]; + queue = dispatch_queue_create("com.rollbar.telemetryQueue", DISPATCH_QUEUE_SERIAL); + fileQueue = dispatch_queue_create("com.rollbar.telemetryFileQueue", DISPATCH_QUEUE_SERIAL); + }); + return singleton; +} + ++ (BOOL)sharedInstanceExists { + + return (nil != singleton); +} + #pragma mark - Initializers - (instancetype)init { @@ -86,6 +99,22 @@ - (instancetype)init { #pragma mark - Config options +- (instancetype)configureWithOptions:(nonnull RollbarTelemetryOptions *)telemetryOptions { + + self.enabled = telemetryOptions.enabled; + self.scrubViewInputs = telemetryOptions.viewInputsScrubber.enabled; + self.viewInputsToScrub = [NSSet setWithArray:telemetryOptions.viewInputsScrubber.scrubFields].mutableCopy; + + [self setCaptureLog:telemetryOptions.captureLog]; + [self setDataLimit:telemetryOptions.maximumTelemetryData]; + + [[RollbarTelemetryThread sharedInstance] configureWithOptions:telemetryOptions]; + if (![RollbarTelemetryThread sharedInstance].active) { + + [[RollbarTelemetryThread sharedInstance] start]; + } +} + - (void)setCaptureLog:(BOOL)shouldCapture { dispatch_async(queue, ^{ @@ -101,16 +130,6 @@ - (void)setDataLimit:(NSInteger)dataLimit { }); } -- (void)trimDataArray { - - if (@available(iOS 10.0, macOS 10.12, *)) { - dispatch_assert_queue_debug(queue); - } - if (_limit > 0 && _dataArray.count > _limit) { - [_dataArray removeObjectsInRange:NSMakeRange(0, _dataArray.count - _limit)]; - } -} - #pragma mark - Telemetry data/event recording methods - (void)recordEvent:(nonnull RollbarTelemetryEvent *)event { @@ -207,31 +226,6 @@ - (void)recordViewEventForLevel:(RollbarLevel)level RollbarTelemetryBody *body = [[RollbarTelemetryViewBody alloc] initWithElement:element extraData:extraData]; [self recordEventWithLevel:level eventBody:body]; - -// NSMutableDictionary *data = [NSMutableDictionary dictionary]; -// if (extraData) { -// [data addEntriesFromDictionary:extraData]; -// } -// -// // check if the extradata needs some scrubbing based on the element name: -// __block BOOL needsScrubbing = false; -// [_viewInputsToScrub enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) { -// if( [element caseInsensitiveCompare:obj] == NSOrderedSame ) { -// needsScrubbing = true; -// *stop = YES; -// } -// }]; -// // scrub the extradata as needed: -// if (needsScrubbing) { -// for (NSString * key in data) -// { -// [data setValue:@"[scrubbed]" forKey:key]; -// } -// } -// // add the element name: -// [data setObject:element forKey:@"element"]; -// -// [self recordEventForLevel:level type:RollbarTelemetryView data:data]; } - (void)recordNetworkEventForLevel:(RollbarLevel)level @@ -250,17 +244,6 @@ - (void)recordNetworkEventForLevel:(RollbarLevel)level } [self recordEventWithLevel:level eventBody:body]; - -// NSMutableDictionary *data = [NSMutableDictionary dictionary]; -// if (extraData) { -// [data addEntriesFromDictionary:extraData]; -// } -// -// [data setObject:method forKey:@"method"]; -// [data setObject:url forKey:@"url"]; -// [data setObject:statusCode forKey:@"status_code"]; -// -// [self recordEventForLevel:level type:RollbarTelemetryNetwork data:data]; } - (void)recordConnectivityEventForLevel:(RollbarLevel)level @@ -274,15 +257,6 @@ - (void)recordConnectivityEventForLevel:(RollbarLevel)level RollbarTelemetryConnectivityBody *body = [[RollbarTelemetryConnectivityBody alloc] initWithStatus:status extraData:extraData]; [self recordEventWithLevel:level eventBody:body]; - -// NSMutableDictionary *data = [NSMutableDictionary dictionary]; -// if (extraData) { -// [data addEntriesFromDictionary:extraData]; -// } -// -// [data setObject:status forKey:@"change"]; -// -// [self recordEventForLevel:level type:RollbarTelemetryConnectivity data:data]; } - (void)recordErrorEventForLevel:(RollbarLevel)level @@ -296,15 +270,6 @@ - (void)recordErrorEventForLevel:(RollbarLevel)level RollbarTelemetryErrorBody *body = [[RollbarTelemetryErrorBody alloc] initWithMessage:message extraData:extraData]; [self recordEventWithLevel:level eventBody:body]; - -// NSMutableDictionary *data = [NSMutableDictionary dictionary]; -// if (extraData) { -// [data addEntriesFromDictionary:extraData]; -// } -// -// [data setObject:message forKey:@"message"]; -// -// [self recordEventForLevel:level type:RollbarTelemetryError data:data]; } - (void)recordNavigationEventForLevel:(RollbarLevel)level @@ -320,16 +285,6 @@ - (void)recordNavigationEventForLevel:(RollbarLevel)level toLocation:to extraData:extraData]; [self recordEventWithLevel:level eventBody:body]; - -// NSMutableDictionary *data = [NSMutableDictionary dictionary]; -// if (extraData) { -// [data addEntriesFromDictionary:extraData]; -// } -// -// [data setObject:from forKey:@"from"]; -// [data setObject:to forKey:@"to"]; -// -// [self recordEventForLevel:level type:RollbarTelemetryNavigation data:data]; } - (void)recordManualEventForLevel:(RollbarLevel)level @@ -341,13 +296,6 @@ - (void)recordManualEventForLevel:(RollbarLevel)level RollbarTelemetryManualBody *body = [[RollbarTelemetryManualBody alloc] initWithDictionary:extraData]; [self recordEventWithLevel:level eventBody:body]; - -// NSMutableDictionary *data = [NSMutableDictionary dictionary]; -// if (extraData) { -// [data addEntriesFromDictionary:extraData]; -// } -// -// [self recordEventForLevel:level type:RollbarTelemetryManual data:data]; } - (void)recordLogEventForLevel:(RollbarLevel)level @@ -361,18 +309,9 @@ - (void)recordLogEventForLevel:(RollbarLevel)level RollbarTelemetryLogBody *body = [[RollbarTelemetryLogBody alloc] initWithMessage:message extraData:extraData]; [self recordEventWithLevel:level eventBody:body]; - -// NSMutableDictionary *data = [NSMutableDictionary dictionary]; -// if (extraData) { -// [data addEntriesFromDictionary:extraData]; -// } -// -// [data setObject:message forKey:@"message"]; -// -// [self recordEventForLevel:level type:RollbarTelemetryLog data:data]; } -#pragma mark - Tlemetry cache access methods +#pragma mark - Telemetry Data access methods -(nonnull NSArray *)getAllEvents { NSArray *telemetryData = [self getAllData]; @@ -424,7 +363,19 @@ - (void)clearAllData { }); } -#pragma mark - Data storage +#pragma mark - Telemetry Data manipulation + +- (void)trimDataArray { + + if (@available(iOS 10.0, macOS 10.12, *)) { + dispatch_assert_queue_debug(queue); + } + if (_limit > 0 && _dataArray.count > _limit) { + [_dataArray removeObjectsInRange:NSMakeRange(0, _dataArray.count - _limit)]; + } +} + +#pragma mark - Telemetry Data persistence // This is used for getting a read-only copy of our shared dataArray // which can later be written to a file. This method must be called @@ -469,4 +420,15 @@ - (void)loadTelemetryData { } } +//#pragma mark - Memory stats collection +// +//- (void)collectMemoryStats { +// +// NSObject *memoryStats = [RollbarMemoryUtil getMemoryStats]; +// +// [self recordManualEventForLevel:RollbarLevel_Info withData:@{ +// @"memory_stats" : memoryStats, +// }]; +//} + @end diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.h b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.h new file mode 100644 index 00000000..cc7053fc --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.h @@ -0,0 +1,44 @@ +// +// RollbarTelemetryThread.h +// +// +// Created by Andrey Kornich on 2022-04-11. +// + +#import + +@class RollbarTelemetryOptions; + +NS_ASSUME_NONNULL_BEGIN + +@interface RollbarTelemetryThread : NSThread + +/// Configures this instance with provided options +/// @param telemetryOptions desired Telemetry options +- (instancetype)configureWithOptions:(nonnull RollbarTelemetryOptions *)telemetryOptions; + +/// Signifies that the thread is active or not. +@property(atomic) BOOL active; + +#pragma mark - Sigleton pattern + ++ (nonnull instancetype)sharedInstance; ++ (BOOL)sharedInstanceExists; + ++ (instancetype)new NS_UNAVAILABLE; ++ (instancetype)allocWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (instancetype)alloc NS_UNAVAILABLE; ++ (id)copyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; ++ (id)mutableCopyWithZone:(struct _NSZone *)zone NS_UNAVAILABLE; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_UNAVAILABLE; +- (instancetype)initWithBlock:(void (^)(void))block NS_UNAVAILABLE; + +- (void)dealloc NS_UNAVAILABLE; +- (id)copy NS_UNAVAILABLE; +- (id)mutableCopy NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.m b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.m new file mode 100644 index 00000000..26878a71 --- /dev/null +++ b/RollbarNotifier/Sources/RollbarNotifier/RollbarTelemetryThread.m @@ -0,0 +1,240 @@ +@import RollbarCommon; + +#import "RollbarTelemetryThread.h" +#import "RollbarTelemetryOptions.h" + +#import "RollbarTelemetry.h" +#import "RollbarTelemetryEvent.h" +#import "RollbarTelemetryBody.h" +#import "RollbarTelemetryManualBody.h" + +static RollbarTelemetryThread * _Nullable singleton = nil; + +@implementation RollbarTelemetryThread { + + @private NSTimer *_timer; + @private NSTimeInterval _collectionTimeInterval; + @private RollbarTelemetryOptions *_telemetryOptions; +} + +#pragma mark - Sigleton pattern + ++ (nonnull instancetype)sharedInstance { + + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + + singleton = [[self class] alloc]; + if ((singleton = [singleton initWithTarget:singleton //self + selector:@selector(run) + object:nil + ])) { + + singleton.name = [RollbarTelemetryThread rollbar_objectClassName]; //@"RollbarTelemetryThread"; + + singleton->_telemetryOptions = nil; + + singleton->_collectionTimeInterval = 0; + + singleton.active = NO; + } + + }); + + return singleton; +} + ++ (BOOL)sharedInstanceExists { + + return (nil != singleton); +} + +- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument { + + if ((self = [super initWithTarget:target + selector:selector + object:argument + ])) { + + self.name = [RollbarTelemetryThread rollbar_objectClassName]; //@"RollbarTelemetryThread"; + + self->_telemetryOptions = nil; + + self->_collectionTimeInterval = 0; + + self.active = NO; + } + + return self; +} + +- (instancetype)configureWithOptions:(nonnull RollbarTelemetryOptions *)telemetryOptions { + + self->_telemetryOptions = telemetryOptions; + + if (![self setupTimer]) { + + return self; + } + + if (!self.executing) { + + [self start]; + } + + return self; +} + +- (BOOL)setupTimer { + + self->_collectionTimeInterval = [RollbarTelemetryThread calculateCollectionTimeInterval:self->_telemetryOptions]; + + if (self->_timer && self->_timer.timeInterval == self->_collectionTimeInterval) { + + // nothing fundamental needs reconfiguration... + return !(0.0 == self->_timer.timeInterval); + } + + if (self->_timer) { + + // shut down the existing timer: + [self->_timer invalidate]; + while(self->_timer.valid) { + + //no-op... + } + self->_timer = nil; + } + + if (0.0 == self->_collectionTimeInterval) { + + // nothing to collect... + return NO; + } + + self->_timer = [NSTimer timerWithTimeInterval:self->_collectionTimeInterval + target:self + selector:@selector(attemptCollection) + userInfo:nil + repeats:YES + ]; + + return YES; +} + ++ (NSTimeInterval)calculateCollectionTimeInterval:(nonnull RollbarTelemetryOptions *)telemetryOptions { + + // iterate through all the autocollection intervals existing in the telemetryOptions + // and find the shortest one, then divide it by 10 and return the value: + + NSTimeInterval intervals[] = { + telemetryOptions.memoryStatsAutocollectionInterval, + //add all other new auto-collection intervals defined within the telemetryOptions... + }; + + int intervalsCount = sizeof(intervals)/sizeof(NSTimeInterval); + switch (intervalsCount) { + case 0: + return 0; + case 1: + return intervals[0]; + default: { + NSTimeInterval min = intervals[0]; + int i = 1; + while (i < intervalsCount) { + if (intervals[i] < min) { + min = intervals[i]; + } + i++; + } + return min; + } + } +} + +- (void)start { + + if (!self.active && [self setupTimer]) { + + + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + + [super start]; + }); + } +} + +- (void)run { + + if (self.active) { + + return; + } + + @autoreleasepool { + + self.active = YES; + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + [runLoop addTimer:self->_timer forMode:NSDefaultRunLoopMode]; + while (self.active) { + if (self->_timer) { + [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + } + [runLoop run]; + } +} + +- (void)attemptCollection { + + if (self.cancelled) { + + if (self->_timer) { + + [self->_timer invalidate]; + self->_timer = nil; + } + + [NSThread exit]; + } + + @autoreleasepool { + + [self attemptMemoryStatsCollection]; + // add attempts of other auto-collections here... + } +} + +- (void)attemptMemoryStatsCollection { + + static NSDate *nextCollection = nil; + if (!nextCollection) { + + [self collectMemoryStats]; + nextCollection = [[NSDate date] + dateByAddingTimeInterval:self->_telemetryOptions.memoryStatsAutocollectionInterval + ]; + } + else if ([[NSDate date] compare:nextCollection] == NSOrderedAscending) { + + [self collectMemoryStats]; + nextCollection = + [nextCollection dateByAddingTimeInterval:self->_telemetryOptions.memoryStatsAutocollectionInterval]; + } +} + +- (void)collectMemoryStats { + + NSObject *memoryStats = [RollbarMemoryUtil getMemoryStats]; + + [[RollbarTelemetry sharedInstance] recordManualEventForLevel:RollbarLevel_Info + withData:@{ + @"memory_stats" : memoryStats, + } + ]; +} + +@end diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetry.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetry.h index 942a4651..618f8aff 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetry.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetry.h @@ -4,6 +4,7 @@ #import "RollbarSource.h" #import "RollbarTelemetryType.h" +@class RollbarTelemetryOptions; @class RollbarTelemetryEvent; @class RollbarTelemetryBody; @class RollbarTelemetryLogBody; @@ -22,12 +23,35 @@ /// Shared service instance/singleton + (nonnull instancetype)sharedInstance; +#pragma mark - Sigleton pattern + ++ (nonnull instancetype)sharedInstance; ++ (BOOL)sharedInstanceExists; + ++ (nonnull instancetype)new NS_UNAVAILABLE; ++ (nonnull instancetype)allocWithZone:(nonnull struct _NSZone *)zone NS_UNAVAILABLE; ++ (nonnull instancetype)alloc NS_UNAVAILABLE; ++ (nullable id)copyWithZone:(nonnull struct _NSZone *)zone NS_UNAVAILABLE; ++ (nullable id)mutableCopyWithZone:(nonnull struct _NSZone *)zone NS_UNAVAILABLE; + +- (nonnull instancetype)init NS_UNAVAILABLE; + +- (void)dealloc NS_UNAVAILABLE; +- (nonnull id)copy NS_UNAVAILABLE; +- (nonnull id)mutableCopy NS_UNAVAILABLE; + +#pragma mark - NSLog redirection + /// NSLog replacement /// @param format NSLog entry format + (void)NSLogReplacement:(nonnull NSString *)format, ...; #pragma mark - Config options +/// Configures this instance with provided options +/// @param telemetryOptions desired Telemetry options +- (nonnull instancetype)configureWithOptions:(nonnull RollbarTelemetryOptions *)telemetryOptions; + /// Telemetry collection enable/disable switch @property (readwrite, atomic) BOOL enabled; diff --git a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetryOptions.h b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetryOptions.h index f16fd02f..5fb4a6df 100644 --- a/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetryOptions.h +++ b/RollbarNotifier/Sources/RollbarNotifier/include/RollbarTelemetryOptions.h @@ -23,6 +23,10 @@ NS_ASSUME_NONNULL_BEGIN /// Telemetry scrubbing options @property (nonatomic, strong) RollbarScrubbingOptions *viewInputsScrubber; +/// Time interval for auto-collecting memtory stats +/// @note 0.0 means no collection! +@property (atomic) NSTimeInterval memoryStatsAutocollectionInterval; //[sec] + #pragma mark - initializers /// Initializer diff --git a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTelemetryTests.swift b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTelemetryTests.swift index 0d742d65..4684081a 100644 --- a/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTelemetryTests.swift +++ b/RollbarNotifier/Tests/RollbarNotifierTests/RollbarNotifierTelemetryTests.swift @@ -8,9 +8,11 @@ import UnitTesting final class RollbarNotifierTelemetryTests: XCTestCase { override func setUp() { + super.setUp(); RollbarTestUtil.clearLogFile(); RollbarTestUtil.clearTelemetryFile(); + if Rollbar.currentConfiguration() != nil { print("Info: Rollbar already pre-configured!"); } @@ -25,6 +27,62 @@ final class RollbarNotifierTelemetryTests: XCTestCase { super.tearDown(); } + func testMemoryTelemetryAutocapture() { + + RollbarTestUtil.clearLogFile(); + RollbarTestUtil.clearTelemetryFile(); + + let config = RollbarConfig(); + config.destination.accessToken = RollbarTestHelper.getRollbarPayloadsAccessToken(); + config.destination.environment = RollbarTestHelper.getRollbarEnvironment(); + config.developerOptions.transmit = false; + config.telemetry.enabled = true; + config.telemetry.memoryStatsAutocollectionInterval = 0.5; + Rollbar.updateConfiguration(config); + + Thread.sleep(forTimeInterval: 5.0); + Rollbar.criticalMessage("Must contain memory telemetry!"); + RollbarTestUtil.waitForPesistenceToComplete(); + + let logItem = RollbarTestUtil.readFirstItemStringsFromLogFile()!; + let payload = RollbarPayload(jsonString: logItem); + let telemetryEvents = payload.data.body.telemetry!; + XCTAssertTrue(telemetryEvents.count > 0); + var totalMemoryStatsEvents = 0; + for event in telemetryEvents { + if (event.type == RollbarTelemetryType.manual) { + if let content = event.body as? RollbarTelemetryManualBody { + if !content.isEmpty && content.description.contains("memory_stats") { + totalMemoryStatsEvents += 1; + } + } + } + } + XCTAssertTrue(totalMemoryStatsEvents > 0); + } + + func testMemoryTelemetryAutocapture_Live() { + + RollbarTestUtil.clearLogFile(); + RollbarTestUtil.clearTelemetryFile(); + + let config = RollbarConfig(); + config.destination.accessToken = RollbarTestHelper.getRollbarPayloadsAccessToken(); + config.destination.environment = RollbarTestHelper.getRollbarEnvironment(); + config.telemetry.enabled = true; + config.telemetry.memoryStatsAutocollectionInterval = 0.5; + + Rollbar.updateConfiguration(config); + + //let resultingConfig = Rollbar.currentConfiguration(); + Rollbar.criticalMessage("Rollbar will be testing memory telemetry!"); + RollbarTestUtil.waitForPesistenceToComplete(); + Thread.sleep(forTimeInterval: 2.0); + Rollbar.criticalMessage("Must contain memory telemetry!"); + RollbarTestUtil.waitForPesistenceToComplete(); + Thread.sleep(forTimeInterval: 3.0); + } + func testTelemetryCapture() { RollbarTestUtil.clearLogFile(); @@ -237,6 +295,8 @@ final class RollbarNotifierTelemetryTests: XCTestCase { } static var allTests = [ + ("testMemoryTelemetryAutocapture", testMemoryTelemetryAutocapture), + ("testMemoryTelemetryAutocapture_Live", testMemoryTelemetryAutocapture_Live), ("testTelemetryCapture", testTelemetryCapture), ("testErrorReportingWithTelemetry", testErrorReportingWithTelemetry), ("testTelemetryViewEventScrubbing", testTelemetryViewEventScrubbing), diff --git a/RollbarPLCrashReporter.podspec b/RollbarPLCrashReporter.podspec index 2313f85d..dee1f8d8 100644 --- a/RollbarPLCrashReporter.podspec +++ b/RollbarPLCrashReporter.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.1.0" + s.version = "2.2.0" s.name = "RollbarPLCrashReporter" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC diff --git a/RollbarSDK.experimental_podspec b/RollbarSDK.experimental_podspec index 8b8ef4a3..e859ecd6 100644 --- a/RollbarSDK.experimental_podspec +++ b/RollbarSDK.experimental_podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |sdk| # Rollbar SDK: # ============ - sdk.version = "2.1.0" + sdk.version = "2.2.0" sdk.name = "RollbarSDK" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." sdk.description = <<-DESC diff --git a/RollbarSwift.podspec b/RollbarSwift.podspec index 90a0f688..6d764b81 100644 --- a/RollbarSwift.podspec +++ b/RollbarSwift.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| - s.version = "2.1.0" + s.version = "2.2.0" s.name = "RollbarSwift" s.summary = "Application or client side SDK for interacting with the Rollbar API Server." s.description = <<-DESC