| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #import "WXApmForInstance.h" |
| #import "WXApmProtocol.h" |
| #import "WXHandlerFactory.h" |
| #import "WXSDKManager.h" |
| #import "WXAppConfiguration.h" |
| #import "WXUtility.h" |
| #import "WXComponentManager.h" |
| #import "WXConfigCenterProtocol.h" |
| #import "WXSDKEngine.h" |
| #import "WXSDKError.h" |
| #import "WXExceptionUtils.h" |
| #import "WXSDKInstance_performance.h" |
| #import "WXAnalyzerCenter+Transfer.h" |
| |
| |
| #pragma mark - const static string |
| |
| NSString* const WEEX_PAGE_TOPIC = @"weex_page"; |
| |
| /************** properties *****************/ |
| NSString* const KEY_PROPERTIES_ERROR_CODE = @"wxErrorCode"; |
| NSString* const KEY_PAGE_PROPERTIES_BIZ_ID = @"wxBizID"; |
| NSString* const KEY_PAGE_PROPERTIES_BUBDLE_URL = @"wxBundleUrl"; |
| NSString* const KEY_PAGE_PROPERTIES_JSLIB_VERSION = @"wxJSLibVersion"; |
| NSString* const KEY_PAGE_PROPERTIES_WEEX_VERSION = @"wxSDKVersion"; |
| NSString* const KEY_PAGE_PROPERTIES_REQUEST_TYPE = @"wxRequestType"; |
| NSString* const KEY_PAGE_PROPERTIES_Z_CACHE_INFO = @"wxZCacheInfo"; |
| NSString* const KEY_PAGE_PROPERTIES_GREY_BUNDLE = @"wxGreyBundle"; |
| NSString* const KEY_PAGE_PROPERTIES_JS_FM_INIT = @"wxJsFrameworkInit"; |
| NSString* const KEY_PAGE_PROPERTIES_BUNDLE_TYPE = @"wxBundleType"; |
| NSString* const KEY_PAGE_PROPERTIES_CONTAINER_NAME = @"wxContainerName"; |
| NSString* const KEY_PAGE_PROPERTIES_INSTANCE_TYPE = @"wxInstanceType"; |
| NSString* const KEY_PAGE_PROPERTIES_PARENT_PAGE = @"wxParentPage"; |
| NSString* const KEY_PAGE_PROPERTIES_RENDER_TYPE = @"wxRenderType"; |
| NSString* const KEY_PAGE_PROPERTIES_UIKIT_TYPE = @"wxUIKitType"; |
| |
| |
| |
| ///************** stages *****************/ |
| NSString* const KEY_PAGE_STAGES_START = @"wxRecordStart"; |
| NSString* const KEY_PAGE_STAGES_DOWN_BUNDLE_START = @"wxStartDownLoadBundle"; |
| NSString* const KEY_PAGE_STAGES_DOWN_BUNDLE_END = @"wxEndDownLoadBundle"; |
| NSString* const KEY_PAGE_STAGES_DOWN_JS_START = @"wxStartDownLoadJS"; |
| NSString* const KEY_PAGE_STAGES_DOWN_JS_END = @"wxEndDownLoadJS"; |
| NSString* const KEY_PAGE_STAGES_CUSTOM_PREPROCESS_START = @"wxCustomPreprocessStart"; |
| NSString* const KEY_PAGE_STAGES_CUSTOM_PREPROCESS_END = @"wxCustomPreprocessEnd"; |
| NSString* const KEY_PAGE_STAGES_RENDER_ORGIGIN = @"wxRenderTimeOrigin"; |
| NSString* const KEY_PAGE_STAGES_LOAD_BUNDLE_START = @"wxStartLoadBundle"; |
| NSString* const KEY_PAGE_STAGES_LOAD_BUNDLE_END = @"wxEndLoadBundle"; |
| NSString* const KEY_PAGE_STAGES_EXECUTE_BUNDLE_END = @"wxEndExecuteBundle"; |
| NSString* const KEY_PAGE_STAGES_EXECUTE_JSON_START = @"wxStartExecuteJson"; |
| NSString* const KEY_PAGE_STAGES_EXECUTE_JSON_END = @"wxEndExecuteJson"; |
| NSString* const KEY_PAGE_STAGES_CREATE_FINISH = @"wxJSBundleCreateFinish"; |
| NSString* const KEY_PAGE_STAGES_FSRENDER = @"wxFsRender"; |
| NSString* const KEY_PAGE_STAGES_NEW_FSRENDER = @"wxNewFsRender"; |
| NSString* const KEY_PAGE_STAGES_INTERACTION = @"wxInteraction"; |
| NSString* const KEY_PAGE_STAGES_DESTROY = @"wxDestroy"; |
| |
| ///************** stats *****************/ |
| NSString* const KEY_PAGE_STATS_BUNDLE_SIZE = @"wxBundleSize"; |
| NSString* const KEY_PAGE_STATS_FS_CALL_JS_TIME = @"wxFSCallJsTotalTime"; |
| NSString* const KEY_PAGE_STATS_FS_CALL_JS_NUM = @"wxFSCallJsTotalNum"; |
| NSString* const KEY_PAGE_STATS_EXECUTE_JS_TIME = @"wxExecJsCallBack"; |
| NSString* const KEY_PAGE_STATS_CREATE_COMPONENT_TIME = @"wxComponentCost"; |
| NSString* const KEY_PAGE_STATS_CREATE_VIEW_TIME = @"wxViewCost"; |
| NSString* const KEY_PAGE_STATS_LAYOUT_TIME = @"wxLayoutTime"; |
| NSString* const KEY_PAGE_STATS_FS_TIMER_NUM = @"wxFSTimerCount"; |
| NSString* const KEY_PAGE_STATS_FS_CALL_NATIVE_TIME = @"wxFSCallNativeTotalTime"; |
| NSString* const KEY_PAGE_STATS_FS_CALL_NATIVE_NUM = @"wxFSCallNativeTotalNum"; |
| NSString* const KEY_PAGE_STATS_FS_CALL_EVENT_NUM = @"wxFSCallEventTotalNum"; |
| NSString* const KEY_PAGE_STATS_FS_REQUEST_NUM = @"wxFSRequestNum"; |
| |
| NSString* const KEY_PAGE_STATS_SCROLLER_NUM = @"wxScrollerCount"; |
| NSString* const KEY_PAGE_STATS_CELL_EXCEED_NUM = @"wxCellExceedNum"; |
| NSString* const KEY_PAGE_STATS_CELL_UN_RE_USE_NUM = @"wxCellUnReUseCount"; |
| NSString* const KEY_PAGE_STATS_CELL_DATA_UN_RECYCLE_NUM = @"wxCellDataUnRecycleCount"; |
| NSString* const KEY_PAGE_STATS_EMBED_COUNT=@"wxEmbedCount"; |
| NSString* const KEY_PAGE_STATS_LARGE_IMG_COUNT=@"wxLargeImgMaxCount"; |
| |
| NSString* const KEY_PAGE_STATS_MAX_DEEP_VIEW = @"wxMaxDeepViewLayer"; |
| NSString* const KEY_PAGE_STATS_MAX_DEEP_DOM = @"wxMaxDeepVDomLayer"; |
| NSString* const KEY_PAGE_STATS_MAX_COMPONENT_NUM = @"wxMaxComponentCount"; |
| NSString* const KEY_PAGE_STATS_WRONG_IMG_SIZE_COUNT = @"wxWrongImgSizeCount"; |
| NSString* const KEY_PAGE_STATS_IMG_UN_RECYCLE_NUM = @"wxImgUnRecycleCount"; |
| |
| NSString* const KEY_PAGE_STATS_I_SCREEN_VIEW_COUNT = @"wxInteractionScreenViewCount"; |
| NSString* const KEY_PAGE_STATS_I_ALL_VIEW_COUNT = @"wxInteractionAllViewCount"; |
| |
| NSString* const KEY_PAGE_STATS_I_COMPONENT_CREATE_COUNT = @"wxInteractionComponentCreateCount"; |
| NSString* const KEY_PAGE_ANIM_BACK_NUM = @"wxAnimationInBackCount"; |
| NSString* const KEY_PAGE_TIMER_BACK_NUM = @"wxTimerInBackCount"; |
| NSString* const KEY_PAGE_STATS_ACTUAL_DOWNLOAD_TIME = @"wxActualNetworkTime"; |
| |
| NSString* const KEY_PAGE_STATS_IMG_LOAD_NUM = @"wxImgLoadCount"; |
| NSString* const KEY_PAGE_STATS_IMG_LOAD_SUCCESS_NUM = @"wxImgLoadSuccessCount"; |
| NSString* const KEY_PAGE_STATS_IMG_LOAD_FAIL_NUM = @"wxImgLoadFailCount"; |
| NSString* const KEY_PAGE_STATS_NET_NUM = @"wxNetworkRequestCount"; |
| NSString* const KEY_PAGE_STATS_NET_SUCCESS_NUM = @"wxNetworkRequestSuccessCount"; |
| NSString* const KEY_PAGE_STATS_NET_FAIL_NUM = @"wxNetworkRequestFailCount"; |
| NSString* const KEY_PAGE_STAGES_FIRST_INTERACTION_VIEW = @"wxFirstInteractionView"; |
| NSString* const KEY_PAGE_STATS_BODY_RATIO = @"wxBodyRatio"; |
| |
| ///************** value *****************/ |
| NSString* const VALUE_ERROR_CODE_DEFAULT = @"0"; |
| |
| @interface WXApmForInstance () |
| { |
| BOOL _isRecord; |
| BOOL _isEnd; |
| NSDictionary* _responseHeader; |
| BOOL _hasRecordInteractionTime; |
| BOOL _hasRecordDownLoadStart; |
| BOOL _hasRecordDownLoadEnd; |
| } |
| |
| @property (nonatomic,strong) id<WXApmProtocol> apmProtocolInstance; |
| @property (nonatomic,strong) NSString* instanceId; |
| @property (nonatomic,strong) NSMutableArray<NSNumber*>* recordUpdateComponentDataTimestamp; |
| @property (nonatomic,strong) NSMutableArray<NSNumber*>* recordUpdateComponentDataTime; |
| |
| @property (nonatomic,strong) NSMutableArray<WXJSExceptionInfo*>* errorList; |
| @end |
| |
| @implementation WXApmForInstance |
| |
| |
| - (instancetype) init |
| { |
| self = [super init]; |
| if (self) { |
| _recordStatsMap = [[NSMutableDictionary alloc] init]; |
| _recordStageMap = [[NSMutableDictionary alloc] init]; |
| _recordUpdateComponentDataTimestamp = [[NSMutableArray alloc] init]; |
| _recordUpdateComponentDataTime = [[NSMutableArray alloc] init]; |
| _errorList = [[NSMutableArray alloc] init]; |
| _isOpenApm = [self _loadApmSwitch]; |
| if (_isOpenApm) { |
| id<WXApmGeneratorProtocol> generater = [WXHandlerFactory handlerForProtocol:@protocol(WXApmGeneratorProtocol)]; |
| _apmProtocolInstance = [generater gengratorApmInstance:WEEX_PAGE_TOPIC]; |
| } |
| } |
| return self; |
| } |
| |
| - (void) onEvent:(NSString *)name withValue:(id)value |
| { |
| if (nil == _apmProtocolInstance || _isEnd) { |
| return; |
| } |
| [self.apmProtocolInstance onEvent:name withValue:value]; |
| } |
| |
| - (long) onStage:(NSString *)name |
| { |
| if(_isEnd){ |
| return 0; |
| } |
| if ([name isEqualToString:KEY_PAGE_STAGES_RENDER_ORGIGIN]) { |
| __weak typeof(self) weakSelf = self; |
| dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ |
| [weakSelf printRecordStageMap]; |
| }); |
| } |
| |
| long result = [WXUtility getUnixFixTimeMillis]; |
| [self onStageWithTime:name time:result]; |
| return result; |
| } |
| |
| - (void) onStageWithTime:(NSString*)name time:(long)unixTime |
| { |
| if(_isEnd){ |
| return; |
| } |
| if ([WXAnalyzerCenter isOpen]) { |
| [WXAnalyzerCenter transferPerformance:self.instanceId withType:@"stage" andKey:name andValue:@(unixTime)]; |
| } |
| |
| if (nil == _apmProtocolInstance) { |
| return; |
| } |
| if ([KEY_PAGE_STAGES_DOWN_BUNDLE_START isEqualToString:name]) { |
| if (_hasRecordDownLoadStart) { |
| return; |
| } |
| _hasRecordDownLoadStart = YES; |
| } |
| if ([KEY_PAGE_STAGES_DOWN_BUNDLE_END isEqualToString:name]) { |
| if (_hasRecordDownLoadEnd) { |
| return; |
| } |
| _hasRecordDownLoadEnd = YES; |
| } |
| |
| if ([KEY_PAGE_STAGES_INTERACTION isEqualToString:name]) { |
| _hasRecordInteractionTime = YES; |
| if (self.forceStopRecordInteractionTime) { |
| return; |
| } |
| } |
| [self.apmProtocolInstance onStage:name withValue:unixTime]; |
| __weak typeof(self) weakSelf = self; |
| WXPerformBlockOnComponentThread(^{ |
| [weakSelf.recordStageMap setObject:@(unixTime) forKey:name]; |
| }); |
| } |
| |
| - (void) setProperty:(NSString *)name withValue:(id)value |
| { |
| if(_isEnd){ |
| return; |
| } |
| |
| if ([WXAnalyzerCenter isOpen]) { |
| [WXAnalyzerCenter transferPerformance:self.instanceId withType:@"properties" andKey:name andValue:value]; |
| } |
| |
| if (nil == _apmProtocolInstance) { |
| return; |
| } |
| |
| [self.apmProtocolInstance addProperty:name withValue:value]; |
| } |
| |
| - (void) setStatistic:(NSString *)name withValue:(double)value |
| { |
| if(_isEnd){ |
| return; |
| } |
| if ([WXAnalyzerCenter isOpen]) { |
| [WXAnalyzerCenter transferPerformance:self.instanceId withType:@"stats" andKey:name andValue:@(value)]; |
| } |
| if (nil == _apmProtocolInstance) { |
| return; |
| } |
| |
| [self.apmProtocolInstance addStatistic:name withValue:value]; |
| } |
| |
| - (void)printRecordStageMap { |
| // These logs are for performance auto-test platforms. |
| __weak typeof(self) weakSelf = self; |
| WXPerformBlockOnComponentThread(^{ |
| if (!weakSelf) { |
| return; |
| } |
| WXSDKInstance *instance = [WXSDKManager instanceForID:weakSelf.instanceId]; |
| if (!instance) { |
| return; |
| } |
| |
| NSNumber* downloadBundleStart = weakSelf.recordStageMap[KEY_PAGE_STAGES_DOWN_BUNDLE_START]; |
| NSNumber* downloadBundleEnd = weakSelf.recordStageMap[KEY_PAGE_STAGES_DOWN_BUNDLE_END]; |
| if (downloadBundleStart) { |
| WXLogInfo(@"pageid: %@ start download url: %@ at timestamp: %lld", weakSelf.instanceId, instance.scriptURL.absoluteString, [downloadBundleStart longLongValue]); |
| } else { |
| NSNumber* renderTimeOrigin = weakSelf.recordStageMap[KEY_PAGE_STAGES_RENDER_ORGIGIN]; |
| WXLogInfo(@"pageid: %@ start render view at timestamp: %lld", weakSelf.instanceId, [renderTimeOrigin longLongValue]); |
| } |
| |
| if (downloadBundleStart && downloadBundleEnd) { |
| WXLogInfo(@"pageid: %@ download bundle time(ms): %lld", weakSelf.instanceId, [downloadBundleEnd longLongValue] - [downloadBundleStart longLongValue]); |
| } |
| |
| NSNumber* executeJsonStart = weakSelf.recordStageMap[KEY_PAGE_STAGES_EXECUTE_JSON_START]; |
| NSNumber* executeJsonEnd = weakSelf.recordStageMap[KEY_PAGE_STAGES_EXECUTE_JSON_END]; |
| if (executeJsonStart && executeJsonEnd) { |
| WXLogInfo(@"pageid: %@ execute json time(ms): %lld", weakSelf.instanceId, [executeJsonEnd longLongValue] - [executeJsonStart longLongValue]); |
| } |
| |
| NSNumber* downloadJsStart = weakSelf.recordStageMap[KEY_PAGE_STAGES_DOWN_JS_START]; |
| NSNumber* downloadJsEnd = weakSelf.recordStageMap[KEY_PAGE_STAGES_DOWN_JS_END]; |
| if (downloadJsStart && downloadJsEnd) { |
| WXLogInfo(@"pageid: %@ download javascript time(ms): %lld", weakSelf.instanceId, [downloadJsEnd longLongValue] - [downloadJsStart longLongValue]); |
| } |
| |
| NSNumber* loadBundleEnd = weakSelf.recordStageMap[KEY_PAGE_STAGES_LOAD_BUNDLE_END]; |
| NSNumber* executeBundleEnd = weakSelf.recordStageMap[KEY_PAGE_STAGES_EXECUTE_BUNDLE_END]; |
| if (loadBundleEnd && executeBundleEnd) { |
| WXLogInfo(@"pageid: %@ execute js bundle time(ms): %lld", weakSelf.instanceId, [executeBundleEnd longLongValue] - [loadBundleEnd longLongValue]); |
| } |
| |
| for (int i=0; i<weakSelf.recordUpdateComponentDataTimestamp.count; i++) { |
| NSNumber* timestamp = weakSelf.recordUpdateComponentDataTimestamp[i]; |
| WXLogInfo(@"pageid: %@ updateComponentData at: %lld cost time(ms): %lld", weakSelf.instanceId, [timestamp longLongValue], [weakSelf.recordUpdateComponentDataTime[i] longLongValue]); |
| } |
| |
| NSNumber* executeJsCallbackTime = weakSelf.recordStatsMap[KEY_PAGE_STATS_EXECUTE_JS_TIME]; |
| if (executeJsCallbackTime) { |
| WXLogInfo(@"pageid: %@ execute js callback time(ms): %f", weakSelf.instanceId, [executeJsCallbackTime doubleValue]); |
| } |
| |
| NSNumber* createComponentTime = weakSelf.recordStatsMap[KEY_PAGE_STATS_CREATE_COMPONENT_TIME]; |
| if (createComponentTime) { |
| WXLogInfo(@"pageid: %@ create components time(ms): %f", weakSelf.instanceId, [createComponentTime doubleValue]); |
| } |
| |
| NSNumber* createViewTime = weakSelf.recordStatsMap[KEY_PAGE_STATS_CREATE_VIEW_TIME]; |
| if (createViewTime) { |
| WXLogInfo(@"pageid: %@ create views time(ms): %f", weakSelf.instanceId, [createViewTime doubleValue]); |
| } |
| |
| NSNumber* layoutTime = weakSelf.recordStatsMap[KEY_PAGE_STATS_LAYOUT_TIME]; |
| if (layoutTime) { |
| WXLogInfo(@"pageid: %@ layout time(ms): %f", weakSelf.instanceId, [layoutTime doubleValue]); |
| } |
| |
| NSNumber* renderOrgin = weakSelf.recordStageMap[KEY_PAGE_STAGES_RENDER_ORGIGIN]; |
| NSNumber* interactionTime = weakSelf.recordStageMap[KEY_PAGE_STAGES_INTERACTION]; |
| if (renderOrgin && interactionTime) { |
| WXLogInfo(@"pageid: %@ interaction time(ms): %lld", weakSelf.instanceId, [interactionTime longLongValue] - [renderOrgin longLongValue]); |
| } |
| }); |
| } |
| |
| #pragma mark - instance record |
| |
| - (void) startRecord:(NSString*) instanceId |
| { |
| if (_isRecord || ![self _shouldRecordInfo]) { |
| return; |
| } |
| |
| _isRecord = YES; |
| _instanceId = instanceId; |
| |
| if (nil != _apmProtocolInstance) { |
| [self.apmProtocolInstance onStart:instanceId topic:WEEX_PAGE_TOPIC]; |
| } |
| [self onStage:KEY_PAGE_STAGES_START]; |
| WXSDKInstance* instance = [WXSDKManager instanceForID:instanceId]; |
| if (nil == instance) { |
| return; |
| } |
| |
| for (NSString* key in instance.continerInfo) { |
| id value = [instance.continerInfo objectForKey:key]; |
| [self setProperty:key withValue:value]; |
| } |
| |
| [self setProperty:KEY_PAGE_PROPERTIES_CONTAINER_NAME withValue:NSStringFromClass(instance.viewController.class)?:@"unknownVCName"]; |
| [self setProperty:KEY_PAGE_PROPERTIES_INSTANCE_TYPE withValue:@"page"]; |
| [self setProperty:KEY_PAGE_PROPERTIES_BIZ_ID withValue: instance.pageName?:@"unknownPageName"]; |
| [self setProperty:KEY_PAGE_PROPERTIES_BUBDLE_URL withValue:instance.scriptURL.absoluteString?:@"unknownUrl"]; |
| [self setProperty:KEY_PROPERTIES_ERROR_CODE withValue:VALUE_ERROR_CODE_DEFAULT]; |
| [self setProperty:KEY_PAGE_PROPERTIES_JSLIB_VERSION withValue:[WXAppConfiguration JSFrameworkVersion]?:@"unknownJSFrameworkVersion"]; |
| [self setProperty:KEY_PAGE_PROPERTIES_WEEX_VERSION withValue:WX_SDK_VERSION]; |
| if (self.pageRatio >0) { |
| [self setStatistic:KEY_PAGE_STATS_BODY_RATIO withValue:self.pageRatio]; |
| } |
| |
| //for apm protocl |
| //iOS/Android we default recycle img when imgView disapper form screen |
| //but in Android ,js can switch recycle or not |
| [self updateDiffStats:KEY_PAGE_STATS_IMG_UN_RECYCLE_NUM withDiffValue:0]; |
| } |
| |
| - (void) endRecord |
| { |
| if (_isEnd) { |
| return; |
| } |
| _isEnd = YES; |
| [self onStage:KEY_PAGE_STAGES_DESTROY]; |
| if (nil != _apmProtocolInstance) { |
| [self.apmProtocolInstance onEnd]; |
| } |
| |
| WXPerformBlockOnComponentThread(^{ |
| WXLogInfo(@"APM data of instance: %@, %@", self.instanceId, self.recordStageMap); |
| NSNumber* stageRenderOrigin = self.recordStageMap[KEY_PAGE_STAGES_RENDER_ORGIGIN]; |
| NSNumber* stageInteraction = self.recordStageMap[KEY_PAGE_STAGES_INTERACTION]; |
| if (stageRenderOrigin && stageInteraction) { |
| WXLogInfo(@"APM interaction time(ms): %lld", [stageInteraction longLongValue] - [stageRenderOrigin longLongValue]); |
| } |
| }); |
| } |
| |
| - (void) updateFSDiffStats:(NSString *)name withDiffValue:(double)diff |
| { |
| [self updateDiffStats:name withDiffValue:diff]; |
| } |
| |
| - (void) updateDiffStats:(NSString *)name withDiffValue:(double)diff |
| { |
| if (![self _shouldRecordInfo]) { |
| return; |
| } |
| __weak typeof(self) weakSelf = self; |
| WXPerformBlockOnComponentThread(^{ |
| NSNumber* preNumber = [weakSelf.recordStatsMap objectForKey:name]; |
| double preVal = nil == preNumber?0:preNumber.doubleValue; |
| double currentVal = preVal + diff; |
| [weakSelf.recordStatsMap setObject:@(currentVal) forKey:name]; |
| [weakSelf setStatistic:name withValue:currentVal]; |
| }); |
| } |
| |
| - (void) updateMaxStats:(NSString *)name curMaxValue:(double)currentValue |
| { |
| if (![self _shouldRecordInfo]) { |
| return; |
| } |
| __weak typeof(self) weakSelf = self; |
| WXPerformBlockOnComponentThread(^{ |
| NSNumber* maxNumber = [weakSelf.recordStatsMap objectForKey:name]; |
| double maxVal = nil == maxNumber?0:maxNumber.doubleValue; |
| |
| if (maxVal < currentValue) { |
| maxVal = currentValue; |
| [weakSelf.recordStatsMap setObject:@(maxVal) forKey:name]; |
| [weakSelf setStatistic:name withValue:maxVal]; |
| } |
| }); |
| } |
| |
| - (void) updateExtInfoFromResponseHeader:(NSDictionary*) extInfo |
| { |
| _responseHeader = extInfo; |
| |
| if (![self _shouldRecordInfo] || nil == extInfo) { |
| return; |
| } |
| |
| id wxGreyBundle = [extInfo objectForKey:KEY_PAGE_PROPERTIES_GREY_BUNDLE]; |
| if (nil != wxGreyBundle && [wxGreyBundle isKindOfClass:NSString.class]) { |
| [self setProperty:KEY_PAGE_PROPERTIES_GREY_BUNDLE withValue:wxGreyBundle]; |
| } |
| |
| id wxRequestType = [extInfo objectForKey:KEY_PAGE_PROPERTIES_REQUEST_TYPE]; |
| if (nil != wxRequestType && [wxRequestType isKindOfClass: NSString.class]) { |
| [self setProperty:KEY_PAGE_PROPERTIES_REQUEST_TYPE withValue:wxRequestType]; |
| } |
| |
| id wxNetLibDownBundleTime = [extInfo objectForKey:KEY_PAGE_STATS_ACTUAL_DOWNLOAD_TIME]; |
| if (nil != wxNetLibDownBundleTime && [wxNetLibDownBundleTime isKindOfClass: NSNumber.class]) { |
| double value = ((NSNumber *)wxNetLibDownBundleTime).doubleValue; |
| [self setStatistic:KEY_PAGE_STATS_ACTUAL_DOWNLOAD_TIME withValue:value]; |
| } |
| |
| id wxZcacheInfo = [extInfo objectForKey:@"X-ZCache-Info"]; |
| if (nil !=wxZcacheInfo && [wxZcacheInfo isKindOfClass: NSString.class]) { |
| [self setProperty:KEY_PAGE_PROPERTIES_Z_CACHE_INFO withValue:wxZcacheInfo]; |
| } |
| } |
| |
| #pragma mark - called by IWXHttpAdapter implementer |
| |
| - (void) actionNetRequest |
| { |
| if (![self _shouldRecordInfo]) { |
| return; |
| } |
| if (!self.isFSEnd) { |
| [self updateFSDiffStats:KEY_PAGE_STATS_FS_REQUEST_NUM withDiffValue:1]; |
| } |
| [self updateDiffStats:KEY_PAGE_STATS_NET_NUM withDiffValue:1]; |
| } |
| |
| - (void) actionNetRequestResult:(bool)succeed withErrorCode:(NSString*)errorCode |
| { |
| if (![self _shouldRecordInfo]) { |
| return; |
| } |
| if (succeed) { |
| [self updateDiffStats:KEY_PAGE_STATS_NET_SUCCESS_NUM withDiffValue:1]; |
| } else { |
| [self updateDiffStats:KEY_PAGE_STATS_NET_FAIL_NUM withDiffValue:1]; |
| } |
| } |
| |
| #pragma mark - called by IWXImgLoaderAdapter implementer |
| |
| - (void) actionImgLoad |
| { |
| if (![self _shouldRecordInfo]) { |
| return; |
| } |
| [self updateDiffStats:KEY_PAGE_STATS_IMG_LOAD_NUM withDiffValue:1]; |
| } |
| |
| - (void) actionImgLoadResult:(bool)succeed withErrorCode:(NSString*)errorCode |
| { |
| if (![self _shouldRecordInfo]) { |
| return; |
| } |
| if (succeed) { |
| [self updateDiffStats:KEY_PAGE_STATS_IMG_LOAD_SUCCESS_NUM withDiffValue:1]; |
| } else { |
| [self updateDiffStats:KEY_PAGE_STATS_IMG_LOAD_FAIL_NUM withDiffValue:1]; |
| } |
| } |
| |
| - (BOOL) _shouldRecordInfo |
| { |
| if (_isEnd) { |
| return NO; |
| } |
| return self.apmProtocolInstance != nil || [WXAnalyzerCenter isOpen]; |
| } |
| |
| - (void) recordErrorMsg:(WXJSExceptionInfo *)exception |
| { |
| if (nil == exception || !self.isOpenApm) { |
| return; |
| } |
| __weak typeof(self) weakSelf = self; |
| WXPerformBlockOnComponentThread(^{ |
| if (weakSelf.errorList.count > 5) { |
| [weakSelf.errorList removeObjectAtIndex:0]; |
| [weakSelf.errorList addObject:exception]; |
| } |
| }); |
| } |
| |
| - (NSArray<WXJSExceptionInfo*>*) topExceptionList |
| { |
| return self.errorList; |
| } |
| |
| - (void) _checkScreenEmptyAndReport |
| { |
| // if(self.isDownLoadFailed || self.hasAddView || !self.isStartRender || self.isDegrade){ |
| // return; |
| // } |
| // if (![self _isReportEmptyScreenError]) { |
| // return; |
| // } |
| // __weak WXApmForInstance* weakSelf = self; |
| // WXPerformBlockOnComponentThread(^{ |
| // __strong WXApmForInstance* strongSelf = weakSelf; |
| // if (nil == strongSelf) { |
| // return; |
| // } |
| // if (nil == [strongSelf.stageDic objectForKey:KEY_PAGE_STAGES_CREATE_FINISH]) { |
| // return; |
| // } |
| // long curTime = [WXUtility getUnixFixTimeMillis]; |
| // NSNumber* startExecJsTime = [strongSelf.stageDic objectForKey:KEY_PAGE_STAGES_LOAD_BUNDLE_END]; |
| // if (nil == startExecJsTime) { |
| // return; |
| // } |
| // long jsExecTime = curTime - startExecJsTime.longValue; |
| // if (jsExecTime < 4000) { |
| // return; |
| // } |
| // |
| // NSString *codeStr = [NSString stringWithFormat:@"%d",WX_KEY_EXCEPTION_EMPTY_SCREEN_JS]; |
| // NSDictionary* extInfo = @{ |
| // @"wxBeginRender":@(strongSelf.isStartRender), |
| // @"wxHasAddView":@(strongSelf.hasAddView), |
| // @"wxHasDegrade":@(strongSelf.isDegrade), |
| // @"wxJSExecTime":@(jsExecTime) |
| // }; |
| // NSString* errorMsg; |
| // if(self.errorList.count<=0){ |
| // errorMsg = @"whiteScreen :never add view until page destroy(js has execute > 3s)"; |
| // }else { |
| // errorMsg = [NSString stringWithFormat:@"writeScreen :history exception :%@",[strongSelf _convertTopExceptionListToStr]]; |
| // } |
| // [WXExceptionUtils commitCriticalExceptionRT:strongSelf.instanceId errCode:codeStr function:@"_checkScreenEmptyAndReport" |
| // exception:errorMsg extParams:extInfo]; |
| // }); |
| } |
| |
| - (NSString *)_convertTopExceptionListToStr |
| { |
| if (!self.isOpenApm) { |
| return @""; |
| } |
| |
| NSString* errorStr = @""; |
| for (WXJSExceptionInfo* exception in self.errorList) { |
| errorStr = [errorStr stringByAppendingFormat: |
| @"%@ -> code:%@,func:%@,exception:%@ ========>", |
| errorStr,exception.errorCode,exception.functionName,exception.exception |
| ]; |
| } |
| return errorStr; |
| } |
| |
| - (NSDictionary<NSString*,NSNumber*>*) stageDic |
| { |
| return self.recordStageMap; |
| } |
| |
| - (BOOL) _loadApmSwitch |
| { |
| BOOL openApm = YES; |
| id<WXConfigCenterProtocol> configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)]; |
| |
| if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) { |
| openApm = [[configCenter configForKey:@"iOS_weex_ext_config.open_apm" defaultValue:@(YES) isDefault:NULL] boolValue]; |
| } |
| return openApm; |
| } |
| |
| - (BOOL) _isReportEmptyScreenError |
| { |
| BOOL report = YES; |
| id<WXConfigCenterProtocol> configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)]; |
| |
| if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) { |
| report = [[configCenter configForKey:@"iOS_weex_ext_config.report_write_screen" defaultValue:@(YES) isDefault:NULL] boolValue]; |
| } |
| return report; |
| } |
| |
| - (NSString*) templateInfo |
| { |
| |
| NSString* headerStr = [WXUtility JSONString:_responseHeader]; |
| NSString* bundleVerfiInfo = nil; |
| WXSDKInstance* instance = [WXSDKManager instanceForID:self.instanceId]; |
| if (nil != instance) { |
| bundleVerfiInfo = [WXUtility JSONString:instance.userInfo]; |
| } |
| NSString* info = [NSString stringWithFormat:@"bundleVerfiInfo :%@, httpHeaderInfo:%@",bundleVerfiInfo?:@"unSetVerfiInfo",headerStr?:@"unSetHeader"]; |
| return info; |
| } |
| |
| - (void) forceSetInteractionTime:(long) unixTime |
| { |
| [self onStageWithTime:KEY_PAGE_STAGES_INTERACTION time:unixTime]; |
| _forceStopRecordInteractionTime = YES; |
| } |
| |
| - (void) addUpdateComponentDataTimestamp:(long)unixTime { |
| [_recordUpdateComponentDataTimestamp addObject:@(unixTime)]; |
| } |
| |
| - (void) addUpdateComponentDataTime:(long)unixTime { |
| [_recordUpdateComponentDataTime addObject:@(unixTime)]; |
| } |
| |
| @end |
| |