Sources/Core/SLSCocoa.m (372 lines of code) (raw):
//
// SLSCocoa.m
// AliyunLogCore
//
// Created by gordon on 2022/7/20.
//
#import "SLSSystemCapabilities.h"
#if SLS_HAS_UIKIT
#import <UIKit/UIKit.h>
#else
#import <AppKit/AppKit.h>
#endif
#import "SLSCocoa.h"
#import "AliyunLogProducer.h"
#import "SLSSdkSender.h"
#import "SLSAppUtils.h"
#import "SLSFeatureProtocol.h"
#import "SLSUtdid.h"
#import "SLSDeviceUtils.h"
#import "NSString+SLS.h"
#import "SLSUtils.h"
#import "SLSPrivocyUtils.h"
@interface SLSCocoa ()
@property(atomic, assign) BOOL hasPreInit;
@property(atomic, assign) BOOL hasInitialize;
@property(nonatomic, copy) SLSCredentials *credentials;
@property(nonatomic, strong) SLSConfiguration *configuration;
@property(nonatomic, strong) NSMutableArray<id<SLSFeatureProtocol>> *features;
@property(nonatomic, strong) SLSExtraProvider *extraProvider;
- (BOOL) internalPreInit: (SLSCredentials *) credentials configuration: (void (^)(SLSConfiguration *configuration)) configuration;
- (BOOL) internalInitialize: (SLSCredentials *) credentials configuration: (void (^)(SLSConfiguration *configuration)) configuration;
- (void) initializeDefaultSpanProvider;
- (void) initializeSdkSender;
- (void) preInitFeature: (NSString *) clazzName;
- (void) initFeature: (NSString *) clazzName;
@end
@implementation SLSCocoa
#pragma mark - instance
+ (instancetype) sharedInstance {
static SLSCocoa * ins = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
ins = [[SLSCocoa alloc] init];
});
return ins;
}
#pragma mark - initialize
- (instancetype)init
{
self = [super init];
if (self) {
_features = [NSMutableArray array];
_extraProvider = [[SLSExtraProvider alloc] init];
}
return self;
}
- (BOOL) preInit: (SLSCredentials *) credentials configuration: (void (^)(SLSConfiguration *configuration)) configuration {
return [self internalPreInit: credentials configuration:configuration];
}
- (BOOL) initialize: (SLSCredentials *) credentials configuration: (void (^)(SLSConfiguration *configuration)) configuration {
return [self internalInitialize: credentials configuration:configuration];
}
- (BOOL) internalPreInit: (SLSCredentials *) credentials configuration: (void (^)(SLSConfiguration *configuration)) configuration {
if (!configuration) {
return NO;
}
// disable privocy while pre-init
[SLSPrivocyUtils setEnablePrivocy:NO];
if (_hasPreInit) {
return NO;
}
_credentials = credentials;
_configuration = [[SLSConfiguration alloc] init];
configuration(_configuration);
[_configuration setup];
[self initializeDefaultSpanProvider];
[self initializeSdkSender];
if (_configuration.enableCrashReporter || _configuration.enableBlockDetection) {
[self preInitFeature: @"SLSCrashReporterFeature"];
}
if (_configuration.enableNetworkDiagnosis) {
[self preInitFeature: @"SLSNetworkDiagnosisFeature"];
}
if (_configuration.enableTrace) {
[self preInitFeature:@"SLSTraceFeature"];
}
_hasPreInit = YES;
return YES;
}
- (BOOL) internalInitialize: (SLSCredentials *) credentials configuration: (void (^)(SLSConfiguration *configuration)) configuration {
// should pre init first
[self internalPreInit:credentials configuration:configuration];
if (!configuration) {
return NO;
}
// enable privocy while real-init
[SLSPrivocyUtils setEnablePrivocy:YES];
if (_hasInitialize) {
return NO;
}
if (_configuration.enableCrashReporter || _configuration.enableBlockDetection) {
[self initFeature: @"SLSCrashReporterFeature"];
}
if (_configuration.enableNetworkDiagnosis) {
[self initFeature: @"SLSNetworkDiagnosisFeature"];
}
if (_configuration.enableTrace) {
[self initFeature:@"SLSTraceFeature"];
}
_hasInitialize = YES;
return YES;
}
- (void) initializeDefaultSpanProvider {
SLSSpanProviderDelegate *delegate = [SLSSpanProviderDelegate provider:_configuration credentials:_credentials extraProvider:_extraProvider];
_configuration.spanProvider = delegate;
}
- (void) initializeSdkSender {
id<SLSSenderProtocol> sender = (id<SLSSenderProtocol>)_configuration.spanProcessor;
[sender initialize: _credentials];
}
- (void) preInitFeature: (NSString *) clazzName {
if (!clazzName || clazzName.length <= 0) {
return;
}
SLSLog(@"preInitFeature, start init: %@", clazzName);
Class clazz = NSClassFromString(clazzName);
if (!clazz || ![clazz conformsToProtocol:@protocol(SLSFeatureProtocol)]) {
SLSLog(@"preInitFeature, feature class not found.");
return;
}
id<SLSFeatureProtocol> feature = [[clazz alloc] init];
if (!feature) {
SLSLog(@"preInitFeature, feature init error.");
return;
}
[feature preInit:_credentials configuration:_configuration];
[_features addObject:feature];
SLSLog(@"preInitFeature, init: %@ success.", clazzName);
}
- (void) initFeature: (NSString *) clazzName {
if (!clazzName || clazzName.length <= 0 || nil == _features) {
return;
}
for (id<SLSFeatureProtocol> feature in _features) {
SLSLog(@"initFeature, start init: %@", [feature name]);
[feature initialize:_credentials configuration:_configuration];
SLSLog(@"initFeature, init: %@ success.", [feature name]);
}
}
#pragma mark - setter
- (void) setCredentials: (SLSCredentials *) credentials {
if (!credentials) {
return;
}
if (credentials.instanceId && credentials.instanceId.length > 0) {
_credentials.instanceId = credentials.instanceId;
}
if (credentials.endpoint && credentials.endpoint.length > 0) {
_credentials.endpoint = credentials.endpoint;
}
if (credentials.project && credentials.project.length > 0) {
_credentials.project = credentials.project;
}
if (credentials.accessKeyId && credentials.accessKeyId.length > 0) {
_credentials.accessKeyId = credentials.accessKeyId;
}
if (credentials.accessKeySecret && credentials.accessKeySecret.length > 0) {
_credentials.accessKeySecret = credentials.accessKeySecret;
}
if (credentials.securityToken && credentials.securityToken.length > 0) {
_credentials.securityToken = credentials.securityToken;
}
[(id<SLSSenderProtocol>) _configuration.spanProcessor setCredentials:credentials];
for (id<SLSFeatureProtocol> feature in _features) {
[feature setCredentials:credentials];
}
}
- (void) setUserInfo: (SLSUserInfo *) userInfo {
if (!_configuration || !userInfo) {
return;
}
_configuration.userInfo = userInfo;
}
- (void)registerCredentialsCallback:(CredentialsCallback)callback {
[(SLSSdkSender *) _configuration.spanProcessor setCallback: callback];
for (id<SLSFeatureProtocol> feature in _features) {
[feature setCallback:callback];
}
}
#pragma mark - extras
- (void) setExtra: (NSString *)key value: (NSString *)value {
[_extraProvider setExtra:key value:value];
}
- (void) setExtra: (NSString *)key dictValue: (NSDictionary<NSString *, NSString *> *)value {
[_extraProvider setExtra:key dictValue:value];
}
- (void) removeExtra: (NSString *)key {
[_extraProvider removeExtra:key];
}
- (void) clearExtras {
[_extraProvider clearExtras];
}
- (void) setUtdid: (NSString *) utdid {
[SLSUtdid setUtdid:utdid];
}
@end
#pragma mark - SLSSpanProviderDelegate
@interface SLSSpanProviderDelegate ()
@property(nonatomic, strong) SLSConfiguration *configuration;
@property(nonatomic, strong) SLSCredentials *credentials;
@property(nonatomic, strong) id<SLSSpanProviderProtocol> spanProvider;
@property(nonatomic, strong) SLSExtraProvider *extraProvider;
- (instancetype) initWithConfiguration: (SLSConfiguration *)configuration credentials: (SLSCredentials *) credentials extraProvider: (SLSExtraProvider *)extraProvider;
- (void) provideExtra: (NSMutableArray<SLSAttribute *> *)attributes;
- (SLSResource *) createDefaultResource;
@end
@implementation SLSSpanProviderDelegate
- (instancetype) initWithConfiguration: (SLSConfiguration *)configuration credentials: (SLSCredentials *) credentials extraProvider: (SLSExtraProvider *)extraProvider {
self = [super init];
if (self) {
_configuration = configuration;
_credentials = credentials;
_spanProvider = configuration.spanProvider;
_extraProvider = extraProvider;
}
return self;
}
+ (instancetype) provider: (SLSConfiguration *)configuration credentials: (SLSCredentials *) credentials extraProvider: (SLSExtraProvider *)extraProvider {
return [[SLSSpanProviderDelegate alloc] initWithConfiguration:configuration credentials:credentials extraProvider:extraProvider];
}
- (SLSResource *) createDefaultResource {
BOOL privocy = [SLSPrivocyUtils isEnablePrivocy];
SLSResource *resource = [[SLSResource alloc] init];
[resource add:@"sdk.language" value:@"Objective-C"];
// device specification, ref: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/device.md
[resource add:@"device.id" value:[[SLSUtdid getUtdid] copy]];
[resource add:@"device.model.identifier" value:privocy ? [SLSDeviceUtils getDeviceModelIdentifier] : @""];
[resource add:@"device.model.name" value:privocy ? [SLSDeviceUtils getDeviceModelIdentifier] : @""];
[resource add:@"device.manufacturer" value:@"Apple"];
[resource add:@"device.resolution" value:privocy ? [SLSDeviceUtils getResolution] : @""];
// os specification, ref: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/os.md
#if SLS_HAS_UIKIT
NSString *systemName = [[[UIDevice currentDevice] systemName] copy];
NSString *systemVersion = [[[UIDevice currentDevice] systemVersion] copy];
#else
NSString *systemName = [[[NSProcessInfo processInfo] operatingSystemName] copy];
NSString *systemVersion = [[[NSProcessInfo processInfo] operatingSystemVersionString] copy];
#endif
[resource add:@"os.type" value: @"darwin"];
[resource add:@"os.description" value: [NSString stringWithFormat:@"%@ %@", systemName, systemVersion]];
#if SLS_HOST_MAC
[resource add:@"os.name" value: @"macOS"];
#elif SLS_HOST_TV
[resource add:@"os.name" value: @"tvOS"];
#else
[resource add:@"os.name" value: @"iOS"];
#endif
[resource add:@"os.version" value: systemVersion];
[resource add:@"os.root" value: privocy ? [SLSDeviceUtils isJailBreak] : @""];
// @"os.sdk": [[TelemetryAttributeValue alloc] initWithStringValue:@"iOS"],
// host specification, ref: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/host.md
#if SLS_HOST_MAC
[resource add:@"host.name" value: @"macOS"];
#elif SLS_HOST_TV
[resource add:@"host.name" value: @"tvOS"];
#else
[resource add:@"host.name" value: @"iOS"];
#endif
[resource add:@"host.type" value: systemName];
[resource add:@"host.arch" value: privocy ? [SLSDeviceUtils getCPUArch] : @""];
[resource add:@"sls.sdk.language" value: @"Objective-C"];
[resource add:@"sls.sdk.name" value: @"SLSCocoa"];
[resource add:@"sls.sdk.version" value: [SLSUtils getSdkVersion]];
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *appName = [infoDictionary objectForKey:@"CFBundleDisplayName"];
if (!appName) {
appName = [infoDictionary objectForKey:@"CFBundleName"];
}
NSString *appVersion = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
NSString *buildCode = [infoDictionary objectForKey:@"CFBundleVersion"];
[resource add:@"app.version" value:(!appVersion ? @"-" : appVersion)];
[resource add:@"app.build_code" value:(!buildCode ? @"-" : buildCode)];
[resource add:@"app.name" value:(!appName ? @"-" : appName)];
[resource add:@"net.access" value: privocy ? [SLSDeviceUtils getNetworkTypeName] : @""];
[resource add:@"net.access_subtype" value: privocy ? [SLSDeviceUtils getNetworkSubTypeName] : @""];
[resource add:@"carrier" value: privocy ? [[SLSDeviceUtils getCarrier] copy] : @""];
return resource;
}
- (SLSResource *)provideResource {
return [[self createDefaultResource] copy];
}
- (NSArray<SLSAttribute *> *)provideAttribute{
NSMutableArray<SLSAttribute*> *attributes =
(NSMutableArray<SLSAttribute*> *) [SLSAttribute of:
// [SLSKeyValue create:@"page.name" value:([SLSAppUtils sharedInstance].foreground ? @"true" : @"false")],
[SLSKeyValue create:@"foreground" value:([SLSAppUtils sharedInstance].foreground ? @"true" : @"false")],
[SLSKeyValue create:@"instance" value:_credentials.instanceId],
[SLSKeyValue create:@"env" value:(_configuration.env ? _configuration.env : @"default")],
nil
];
[self provideExtra:attributes];
if (_configuration.userInfo) {
[self provideUserInfo:attributes userinfo:_configuration.userInfo];
}
NSArray<SLSAttribute *> *userAttributes = [_spanProvider provideAttribute];
if (userAttributes) {
[attributes addObjectsFromArray:userAttributes];
}
return attributes;
}
- (void) provideExtra: (NSMutableArray<SLSAttribute *> *)attributes {
NSDictionary<NSString *, NSString *> *extras = [_extraProvider getExtras];
if (!extras) {
return;
}
for (NSString *k in extras) {
NSString *key = [NSString stringWithFormat:@"extras.%@", k];
if ([[extras valueForKey:k] isKindOfClass:[NSDictionary<NSString *, NSString *> class]]) {
[attributes addObject:[SLSAttribute of:key
value:[NSString stringWithDictionary:(NSDictionary *)[extras valueForKey:k]]
]
];
} else {
[attributes addObject:[SLSAttribute of:key
value:[extras valueForKey:k]
]
];
}
}
}
- (void) provideUserInfo: (NSMutableArray<SLSAttribute *> *) attributes userinfo: (SLSUserInfo *) info {
if (info.uid.length > 0) {
[attributes addObject:[SLSAttribute of:@"user.uid"
value:[info.uid copy]
]
];
}
if (info.channel.length > 0) {
[attributes addObject:[SLSAttribute of:@"user.channel"
value:[info.channel copy]
]
];
}
if (info.ext) {
for (NSString *k in info.ext) {
if (k.length == 0) {
continue;
}
[attributes addObject:[SLSAttribute of:[NSString stringWithFormat:@"user.%@", k]
value:[[info.ext valueForKey:k] copy]
]
];
}
}
}
@end
#pragma mark - SLSExtraProvider
@interface SLSExtraProvider()
@property(nonatomic, strong, readonly) NSMutableDictionary *dict;
@end
@implementation SLSExtraProvider : NSObject
- (instancetype)init {
if (self = [super init]) {
_dict = [NSMutableDictionary dictionary];
}
return self;
}
- (void) setExtra: (NSString *)key value: (NSString *)value {
[_dict setObject:[value copy] forKey:[key copy]];
}
- (void) setExtra: (NSString *)key dictValue: (NSDictionary<NSString *, NSString *> *)value {
if (![value isKindOfClass:[NSDictionary<NSString *, NSString *> class]]) {
return;
}
[_dict setObject:[value copy] forKey:[key copy]];
}
- (void) removeExtra: (NSString *)key {
[_dict removeObjectForKey:key];
}
- (void) clearExtras {
[_dict removeAllObjects];
}
- (NSDictionary<NSString *, NSString *> *) getExtras {
return [_dict copy];
}
@end