QNUploadInfoReporter.m 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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 "QNDefine.h"
  9. #import "QNZoneInfo.h"
  10. #import "QNUploadInfoReporter.h"
  11. #import "QNResponseInfo.h"
  12. #import "QNUtils.h"
  13. #import "QNFile.h"
  14. #import "QNUpToken.h"
  15. #import "QNUserAgent.h"
  16. #import "QNAsyncRun.h"
  17. #import "QNVersion.h"
  18. #import "QNReportConfig.h"
  19. #import "NSData+QNGZip.h"
  20. #import "QNTransactionManager.h"
  21. #import "QNRequestTransaction.h"
  22. #define kQNUplogDelayReportTransactionName @"com.qiniu.uplog"
  23. @interface QNUploadInfoReporter ()
  24. @property (nonatomic, strong) QNReportConfig *config;
  25. @property (nonatomic, assign) NSTimeInterval lastReportTime;
  26. @property (nonatomic, strong) NSString *recorderFilePath;
  27. @property (nonatomic, strong) NSString *recorderTempFilePath;
  28. @property (nonatomic, copy) NSString *X_Log_Client_Id;
  29. @property (nonatomic, strong) QNRequestTransaction *transaction;
  30. @property (nonatomic, assign) BOOL isReporting;
  31. @property (nonatomic, strong) dispatch_queue_t recordQueue;
  32. @property (nonatomic, strong) dispatch_semaphore_t semaphore;
  33. @end
  34. @implementation QNUploadInfoReporter
  35. + (instancetype)sharedInstance {
  36. static QNUploadInfoReporter *sharedInstance = nil;
  37. static dispatch_once_t onceToken;
  38. dispatch_once(&onceToken, ^{
  39. sharedInstance = [[self alloc] init];
  40. });
  41. return sharedInstance;
  42. }
  43. - (instancetype)init {
  44. self = [super init];
  45. if (self) {
  46. _config = [QNReportConfig sharedInstance];
  47. _lastReportTime = 0;
  48. _recorderFilePath = [NSString stringWithFormat:@"%@/%@", _config.recordDirectory, @"qiniu.log"];
  49. _recorderTempFilePath = [NSString stringWithFormat:@"%@/%@", _config.recordDirectory, @"qiniuTemp.log"];
  50. _recordQueue = dispatch_queue_create("com.qiniu.reporter", DISPATCH_QUEUE_SERIAL);
  51. }
  52. return self;
  53. }
  54. - (void)clean {
  55. [self cleanRecorderFile];
  56. [self cleanTempRecorderFile];
  57. }
  58. - (void)cleanRecorderFile {
  59. NSFileManager *manager = [NSFileManager defaultManager];
  60. if ([manager fileExistsAtPath:_recorderFilePath]) {
  61. NSError *error = nil;
  62. [manager removeItemAtPath:_recorderFilePath error:&error];
  63. if (error) {
  64. NSLog(@"remove recorder file failed: %@", error);
  65. return;
  66. }
  67. }
  68. }
  69. - (void)cleanTempRecorderFile {
  70. NSFileManager *manager = [NSFileManager defaultManager];
  71. if ([manager fileExistsAtPath:_recorderTempFilePath]) {
  72. NSError *error = nil;
  73. [manager removeItemAtPath:_recorderTempFilePath error:&error];
  74. if (error) {
  75. NSLog(@"remove recorder temp file failed: %@", error);
  76. return;
  77. }
  78. }
  79. }
  80. - (BOOL)checkReportAvailable {
  81. if (!_config.isReportEnable) {
  82. return NO;
  83. }
  84. if (_config.maxRecordFileSize <= _config.uploadThreshold) {
  85. NSLog(@"maxRecordFileSize must be larger than uploadThreshold");
  86. return NO;
  87. }
  88. return YES;
  89. }
  90. - (void)report:(NSString *)jsonString token:(NSString *)token {
  91. if (![self checkReportAvailable] || !jsonString || !token || token.length == 0) {
  92. return;
  93. }
  94. // 串行队列处理文件读写
  95. dispatch_async(self.recordQueue, ^{
  96. [kQNReporter saveReportJsonString:jsonString];
  97. [kQNReporter reportToServerIfNeeded:token];
  98. });
  99. }
  100. - (void)saveReportJsonString:(NSString *)jsonString {
  101. NSString *finalRecordInfo = [jsonString stringByAppendingString:@"\n"];
  102. NSFileManager *fileManager = [NSFileManager defaultManager];
  103. if (![fileManager fileExistsAtPath:self.recorderFilePath]) {
  104. // 如果recordFile不存在,创建文件并写入首行,首次不上传
  105. [finalRecordInfo writeToFile:_recorderFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
  106. } else {
  107. NSDictionary *recorderFileAttr = [fileManager attributesOfItemAtPath:self.recorderFilePath error:nil];
  108. if ([recorderFileAttr fileSize] > self.config.maxRecordFileSize) {
  109. return;
  110. }
  111. NSFileHandle *fileHandler = nil;
  112. @try {
  113. // 上传信息写入recorder文件
  114. fileHandler = [NSFileHandle fileHandleForUpdatingAtPath:_recorderFilePath];
  115. [fileHandler seekToEndOfFile];
  116. [fileHandler writeData: [finalRecordInfo dataUsingEncoding:NSUTF8StringEncoding]];
  117. } @catch (NSException *exception) {
  118. NSLog(@"NSFileHandle cannot write data: %@", exception.description);
  119. } @finally {
  120. [fileHandler closeFile];
  121. }
  122. }
  123. }
  124. - (void)reportToServerIfNeeded:(NSString *)tokenString {
  125. BOOL needToReport = NO;
  126. long currentTime = [[NSDate date] timeIntervalSince1970];
  127. long interval = self.config.interval * 10;
  128. NSFileManager *fileManager = [NSFileManager defaultManager];
  129. NSDictionary *recorderFileAttr = [fileManager attributesOfItemAtPath:self.recorderFilePath error:nil];
  130. if ([fileManager fileExistsAtPath:self.recorderTempFilePath]) {
  131. needToReport = YES;
  132. } else if ((self.lastReportTime == 0 || (currentTime - self.lastReportTime) >= interval || [recorderFileAttr fileSize] > self.config.uploadThreshold) &&
  133. ([fileManager moveItemAtPath:self.recorderFilePath toPath:self.recorderTempFilePath error:nil])) {
  134. needToReport = YES;
  135. }
  136. if (needToReport && !self.isReporting) {
  137. [self reportToServer:tokenString];
  138. } else {
  139. // 有未上传日志存在,则 interval 时间后再次重试一次
  140. if (![fileManager fileExistsAtPath:self.recorderFilePath] || [recorderFileAttr fileSize] == 0) {
  141. return;
  142. }
  143. NSArray *transactionList = [kQNTransactionManager transactionsForName:kQNUplogDelayReportTransactionName];
  144. if (transactionList != nil && transactionList.count > 1) {
  145. return;
  146. }
  147. if (transactionList != nil && transactionList.count == 1) {
  148. QNTransaction *transaction = transactionList.firstObject;
  149. if (transaction != nil && !transaction.isExecuting) {
  150. return;
  151. }
  152. }
  153. QNTransaction *transaction = [QNTransaction transaction:kQNUplogDelayReportTransactionName after:interval action:^{
  154. [kQNReporter reportToServerIfNeeded:tokenString];
  155. }];
  156. [kQNTransactionManager addTransaction:transaction];
  157. }
  158. }
  159. - (void)reportToServer:(NSString *)tokenString {
  160. if (tokenString == nil) {
  161. return;
  162. }
  163. QNUpToken *token = [QNUpToken parse:tokenString];
  164. if (!token.isValid) {
  165. return;
  166. }
  167. NSData *logData = [self getLogData];
  168. if (logData == nil) {
  169. return;
  170. }
  171. self.isReporting = YES;
  172. logData = [NSData qn_gZip:logData];
  173. QNRequestTransaction *transaction = [self createUploadRequestTransaction:token];
  174. [transaction reportLog:logData logClientId:self.X_Log_Client_Id complete:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
  175. if (responseInfo.isOK) {
  176. self.lastReportTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970];
  177. if (!self.X_Log_Client_Id) {
  178. self.X_Log_Client_Id = responseInfo.responseHeader[@"x-log-client-id"];
  179. }
  180. [self cleanTempRecorderFile];
  181. } else {
  182. NSLog(@"upload info report failed: %@", responseInfo);
  183. }
  184. self.isReporting = NO;
  185. [self destroyUploadRequestTransaction:transaction];
  186. }];
  187. }
  188. - (NSData *)getLogData {
  189. return [NSData dataWithContentsOfFile:_recorderTempFilePath];
  190. }
  191. - (QNRequestTransaction *)createUploadRequestTransaction:(QNUpToken *)token{
  192. if (self.config.serverURL) {
  193. }
  194. NSArray *hosts = nil;
  195. if (self.config.serverHost) {
  196. hosts = @[self.config.serverHost];
  197. }
  198. QNRequestTransaction *transaction = [[QNRequestTransaction alloc] initWithHosts:hosts
  199. regionId:QNZoneInfoEmptyRegionId
  200. token:token];
  201. self.transaction = transaction;
  202. return transaction;
  203. }
  204. - (void)destroyUploadRequestTransaction:(QNRequestTransaction *)transaction{
  205. self.transaction = nil;
  206. }
  207. @end