我在寫一個Objective-C庫,並且在某些地方我想記錄一些信息。使用NSLog
並不理想,因爲它不可配置,既沒有級別支持也沒有標籤支持。 CocoaLumberjack和NSLogger都是支持級別和上下文/標籤的流行日誌庫,但我不想依賴第三方日誌庫。我應該如何處理Objective-C庫中的日誌?
如何以可配置的方式生成日誌,而不會強制特定的日誌記錄庫存在我的用戶上?
我在寫一個Objective-C庫,並且在某些地方我想記錄一些信息。使用NSLog
並不理想,因爲它不可配置,既沒有級別支持也沒有標籤支持。 CocoaLumberjack和NSLogger都是支持級別和上下文/標籤的流行日誌庫,但我不想依賴第三方日誌庫。我應該如何處理Objective-C庫中的日誌?
如何以可配置的方式生成日誌,而不會強制特定的日誌記錄庫存在我的用戶上?
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());
}];
[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; }());
可能要記錄回調的線程合約。 – Dad
日誌處理程序僅在日誌發出的線程上調用。 [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
這是蘋果確實在[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:') –