QNUploadInfoReporter.m 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. //
  2. // QNUploadInfoReporter.m
  3. // QiniuSDK
  4. //
  5. // Created by WorkSpace_Sun on 2019/6/24.
  6. // Copyright © 2019 Qiniu. All rights reserved.
  7. //
  8. #import "QNUploadInfoReporter.h"
  9. #import "QNResponseInfo.h"
  10. #import "QNFile.h"
  11. #import "QNUpToken.h"
  12. #import "QNUserAgent.h"
  13. #import "QNAsyncRun.h"
  14. #import "QNVersion.h"
  15. #import "QNReportConfig.h"
  16. #import "NSData+QNGZip.h"
  17. @interface QNUploadInfoReporter ()
  18. @property (nonatomic, strong) QNReportConfig *config;
  19. @property (nonatomic, assign) NSTimeInterval lastReportTime;
  20. @property (nonatomic, strong) NSFileManager *fileManager;
  21. @property (nonatomic, strong) NSString *recorderFilePath;
  22. @property (nonatomic, strong) dispatch_queue_t recordQueue;
  23. @property (nonatomic, strong) dispatch_semaphore_t semaphore;
  24. @property (nonatomic, copy) NSString *X_Log_Client_Id;
  25. @end
  26. @implementation QNUploadInfoReporter
  27. + (instancetype)sharedInstance {
  28. static QNUploadInfoReporter *sharedInstance = nil;
  29. static dispatch_once_t onceToken;
  30. dispatch_once(&onceToken, ^{
  31. sharedInstance = [[self alloc] init];
  32. });
  33. return sharedInstance;
  34. }
  35. - (instancetype)init {
  36. self = [super init];
  37. if (self) {
  38. _config = [QNReportConfig sharedInstance];
  39. _lastReportTime = 0;
  40. _recorderFilePath = [NSString stringWithFormat:@"%@/%@", _config.recordDirectory, @"qiniu.log"];
  41. _fileManager = [NSFileManager defaultManager];
  42. _recordQueue = dispatch_queue_create("com.qiniu.reporter", DISPATCH_QUEUE_SERIAL);
  43. }
  44. return self;
  45. }
  46. - (void)clean {
  47. if ([_fileManager fileExistsAtPath:_recorderFilePath]) {
  48. NSError *error = nil;
  49. [_fileManager removeItemAtPath:_recorderFilePath error:&error];
  50. if (error) {
  51. NSLog(@"remove recorder file failed: %@", error);
  52. return;
  53. }
  54. }
  55. }
  56. - (BOOL)checkReportAvailable {
  57. if (!_config.isReportEnable) {
  58. return NO;
  59. }
  60. if (_config.maxRecordFileSize <= _config.uploadThreshold) {
  61. NSLog(@"maxRecordFileSize must be larger than uploadThreshold");
  62. return NO;
  63. }
  64. return YES;
  65. }
  66. - (void)report:(NSString *)jsonString token:(NSString *)token {
  67. if (![self checkReportAvailable] || !jsonString) {
  68. return;
  69. }
  70. // 串行队列处理文件读写
  71. dispatch_async(_recordQueue, ^{
  72. [self innerReport:jsonString token:token];
  73. });
  74. }
  75. - (void)innerReport:(NSString *)jsonString token:(NSString *)token {
  76. // 检查recorder文件夹是否存在
  77. NSError *error = nil;
  78. if (![_fileManager fileExistsAtPath:_config.recordDirectory]) {
  79. [_fileManager createDirectoryAtPath:_config.recordDirectory withIntermediateDirectories:YES attributes:nil error:&error];
  80. if (error) {
  81. NSLog(@"create record directory failed, please check record directory: %@", error.localizedDescription);
  82. return;
  83. }
  84. }
  85. // 拼接换行符
  86. NSString *finalRecordInfo = [jsonString stringByAppendingString:@"\n"];
  87. if (![_fileManager fileExistsAtPath:_recorderFilePath]) {
  88. // 如果recordFile不存在,创建文件并写入首行,首次不上传
  89. [finalRecordInfo writeToFile:_recorderFilePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
  90. } else {
  91. // recordFile存在,拼接文件内容、上传到服务器
  92. QNFile *file = [[QNFile alloc] init:_recorderFilePath error:&error];
  93. if (error) {
  94. NSLog(@"create QNFile with path failed: %@", error.localizedDescription);
  95. return;
  96. }
  97. // 判断recorder文件大小是否超过maxRecordFileSize
  98. if (file.size < _config.maxRecordFileSize) {
  99. @try {
  100. // 上传信息写入recorder文件
  101. NSFileHandle *fileHandler = [NSFileHandle fileHandleForUpdatingAtPath:_recorderFilePath];
  102. [fileHandler seekToEndOfFile];
  103. [fileHandler writeData: [finalRecordInfo dataUsingEncoding:NSUTF8StringEncoding]];
  104. [fileHandler closeFile];
  105. } @catch (NSException *exception) {
  106. NSLog(@"NSFileHandle cannot write data: %@", exception.description);
  107. }
  108. }
  109. // 判断是否满足上传条件:文件大于上报临界值 || (首次上传 || 距上次上传时间大于_config.interval)
  110. NSTimeInterval currentTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970];
  111. if (file.size > _config.uploadThreshold || (_lastReportTime == 0 || currentTime - _lastReportTime > _config.interval * 60)) {
  112. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:_config.serverURL]];
  113. [request setValue:[NSString stringWithFormat:@"UpToken %@", token] forHTTPHeaderField:@"Authorization"];
  114. [request setValue:[[QNUserAgent sharedInstance] getUserAgent:[QNUpToken parse:token].access] forHTTPHeaderField:@"User-Agent"];
  115. if (self.X_Log_Client_Id) {
  116. [request setValue:self.X_Log_Client_Id forHTTPHeaderField:@"X-Log-Client-Id"];
  117. }
  118. [request setHTTPMethod:@"POST"];
  119. [request setTimeoutInterval:_config.timeoutInterval];
  120. NSData *reportData = [NSData dataWithContentsOfFile:_recorderFilePath];
  121. reportData = [NSData qn_gZip:reportData];
  122. __block NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
  123. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:reportData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  124. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  125. if (httpResponse.statusCode == 200) {
  126. self.lastReportTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970];
  127. NSDictionary *respHeader = httpResponse.allHeaderFields;
  128. if (!self.X_Log_Client_Id && [respHeader.allKeys containsObject:@"x-log-client-id"]) {
  129. self.X_Log_Client_Id = respHeader[@"x-log-client-id"];
  130. }
  131. [self clean];
  132. } else {
  133. NSLog(@"upload info report failed: %@", error.localizedDescription);
  134. }
  135. [session finishTasksAndInvalidate];
  136. dispatch_semaphore_signal(self.semaphore);
  137. }];
  138. [uploadTask resume];
  139. // 控制上传过程中,文件内容不被修改
  140. _semaphore = dispatch_semaphore_create(0);
  141. dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
  142. }
  143. }
  144. }
  145. @end