如何处理Objective-C库中的日志?

我正在写一个Objective-C库,在某些地方我想记录一些信息.使用NSLog并不理想,因为它不可配置,既没有级别支持不支持标签. CocoaLumberjack和NSLogger都是支持级别和上下文/标签的受欢迎的日志库,但我不希望依赖于第三方日志记录库.

如何以可配置的方式生成日志,不会强制我的用户使用特定的日志库?

解决方法

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),NSUInteger line) {
    LogMessageRawF(file,(int)line,function,@"MYLibrary",(int)level,message());
}];

或与CocoaLumberjack

[MYLibraryLogger setLogHandler:^(Nsstring * (^message)(void),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 *,NSUInteger) = ^(Nsstring *(^message)(void),NSUInteger line)
{
    if (level == MYLogLevelError || level == MYLogLevelWarning)
        NSLog(@"[MYLibrary] %@",message());
};

@implementation MYLibraryLogger

+ (void) setLogHandler:(void (^)(Nsstring * (^message)(void),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,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,##__VA_ARGS__]; }))
#define MYLibraryLogInfo(format,...)    MYLibraryLog(MYLogLevelInfo,##__VA_ARGS__]; }))
#define MYLibraryLogDebug(format,...)   MYLibraryLog(MYLogLevelDebug,##__VA_ARGS__]; }))
#define MYLibraryLogVerbose(format,...) MYLibraryLog(MYLogLevelVerbose,##__VA_ARGS__]; }))

那么你只需在你的库中使用它:

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

请注意日志消息是如何返回字符串而不是字符串的块.这样,如果定义的日志处理程序决定不评估消息(例如,基于上述认日志处理程序中的日志级别),则可以避免昂贵的计算.这样,如果日志被丢弃,您可以编写具有潜在昂贵的日志消息的单行日志进行计算,而不会导致性能下降,例如:

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

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...