2016-01-11 84 views
9

我在寫一個Objective-C庫,並且在某些地方我想記錄一些信息。使用NSLog並不理想,因爲它不可配置,既沒有級別支持也沒有標籤支持。 CocoaLumberjack和NSLogger都是支持級別和上下文/標籤的流行日誌庫,但我不想依賴第三方日誌庫。我應該如何處理Objective-C庫中的日誌?

如何以可配置的方式生成日誌,而不會強制特定的日誌記錄庫存在我的用戶上?

回答

19

TL; DR在您的API中公開日誌處理程序塊。


這是一個建議,使日誌記錄類作爲公共API的一部分很容易配置。讓我們把它叫做MYLibraryLogger

// MYLibraryLogger.h 

#import <Foundation/Foundation.h> 

typedef NS_ENUM(NSUInteger, MYLogLevel) { 
    MYLogLevelError = 0, 
    MYLogLevelWarning = 1, 
    MYLogLevelInfo = 2, 
    MYLogLevelDebug = 3, 
    MYLogLevelVerbose = 4, 
}; 

@interface MYLibraryLogger : NSObject 

+ (void) setLogHandler:(void (^)(NSString * (^message)(void), MYLogLevel level, const char *file, const char *function, NSUInteger line))logHandler; 

@end 

這個類有一個方法,它允許客戶註冊一個日誌處理程序塊。這使得客戶使用他們最喜歡的庫實現日誌記錄變得微不足道。下面是一個客戶端將與NSLogger使用它:

[MYLibraryLogger setLogHandler:^(NSString * (^message)(void), MYLogLevel level, const char *file, const char *function, NSUInteger line) { 
    LogMessageRawF(file, (int)line, function, @"MYLibrary", (int)level, message()); 
}]; 

CocoaLumberjack

[MYLibraryLogger setLogHandler:^(NSString * (^message)(void), MYLogLevel level, const char *file, const char *function, NSUInteger line) { 
    // The `MYLogLevel` enum matches the `DDLogFlag` options from DDLog.h when shifted 
    [DDLog log:YES message:message() level:ddLogLevel flag:(1 << level) context:MYLibraryLumberjackContext file:file function:function line:line tag:nil]; 
}]; 

這裏是MYLibraryLogger與只記錄錯誤和警告默認日誌處理程序的實現:

// MYLibraryLogger.m 

#import "MYLibraryLogger.h" 

static void (^LogHandler)(NSString * (^)(void), MYLogLevel, const char *, const char *, NSUInteger) = ^(NSString *(^message)(void), MYLogLevel level, const char *file, const char *function, NSUInteger line) 
{ 
    if (level == MYLogLevelError || level == MYLogLevelWarning) 
     NSLog(@"[MYLibrary] %@", message()); 
}; 

@implementation MYLibraryLogger 

+ (void) setLogHandler:(void (^)(NSString * (^message)(void), MYLogLevel level, const char *file, const char *function, NSUInteger line))logHandler 
{ 
    LogHandler = logHandler; 
} 

+ (void) logMessage:(NSString * (^)(void))message level:(MYLogLevel)level file:(const char *)file function:(const char *)function line:(NSUInteger)line 
{ 
    if (LogHandler) 
     LogHandler(message, level, file, function, line); 
} 

@end 

此解決方案的最後一個缺失部分是您通過您的庫使用的一組宏。

// MYLibraryLogger+Private.h 

#import <Foundation/Foundation.h> 

#import "MYLibraryLogger.h" 

@interface MYLibraryLogger() 

+ (void) logMessage:(NSString * (^)(void))message level:(MYLogLevel)level file:(const char *)file function:(const char *)function line:(NSUInteger)line; 

@end 

#define MYLibraryLog(_level, _message) [MYLibraryLogger logMessage:(_message) level:(_level) file:__FILE__ function:__PRETTY_FUNCTION__ line:__LINE__] 

#define MYLibraryLogError(format, ...) MYLibraryLog(MYLogLevelError, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 
#define MYLibraryLogWarning(format, ...) MYLibraryLog(MYLogLevelWarning, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 
#define MYLibraryLogInfo(format, ...) MYLibraryLog(MYLogLevelInfo, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 
#define MYLibraryLogDebug(format, ...) MYLibraryLog(MYLogLevelDebug, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 
#define MYLibraryLogVerbose(format, ...) MYLibraryLog(MYLogLevelVerbose, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 

然後你只需像這樣使用你的圖書館裏:

MYLibraryLogError(@"Operation finished with error: %@", error); 

通知日誌消息是如何返回一個字符串,而不是隻是一個字符串的塊。這樣,如果定義的日誌處理程序決定不評估消息(例如,根據上面的默認日誌處理程序中的日誌級別),則可以避免昂貴的計算。這可以讓您編寫帶有潛在代價高昂的日誌消息的單線程日誌,以便在日誌丟失的情況下進行計算而不會影響性能,例如:

MYLibraryLogDebug(@"Object: %@", ^{ return object.debugDescription; }()); 
+0

可能要記錄回調的線程合約。 – Dad

+0

日誌處理程序僅在日誌發出的線程上調用。 [CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/f57de5cb54c9334e0685c7de2f33f1da5658d2f8/Classes/DDLog.m#L963)和[NSLogger](https://github.com/fpillet/NSLogger/blob/2259236c4ba1f5070c58f31797888fe96abcc492/客戶端%20Logger/iOS/LoggerClient.m#L2123)使用* current *隊列/線程獲取其名稱。 – 0xced

+0

這是蘋果確實在[CustomHTTPProtocol(https://developer.apple.com/library/ios/samplecode/CustomHTTPProtocol/Listings/CustomHTTPProtocol_Core_Code_CustomHTTPProtocol_h.html#//apple_ref/doc/uid/DTS40013653-CustomHTTPProtocol_Core_Code_CustomHTTPProtocol_h-DontLinkElementID_9)(請參閱'customHTTPProtocol:protocol logWithFormat:arguments:') –

相關問題