深入理解代替单纯记忆
本文对CocoaLumberjack(3.5.3版本)中log level(日志级别)能力进行学习和记录
关于什么是log level,log leve有什么作用,本文不做说明,网上可以找到很多资料
CocoaLumberjack官方对log leve有这样的描述:
Configure your logging however you want. Change log levels per file (perfect for debugging). Change log levels per logger (verbose console, but concise log file). Change log levels per xcode configuration (verbose debug, but concise release). Have your log statements compiled out of the release build. Customize the number of log levels for your application.
总结一下CocoaLumberjack的log level能做哪些事:
- 可以为每个文件设置独立的log level
- 可以每个logger设置独立的log level
- 可以为不同的Xcode configuratioin设置不同的log level
- 可以自定义log level数量
LogLevel工作原理
CocoaLumberjack内部使用两个类型(DDLogFlag和DDLogLevel)来控制哪些日志可以输出,哪些需要被过滤掉
typedef NS_OPTIONS(NSUInteger, DDLogFlag){
/**
* 0...00001 DDLogFlagError
*/
DDLogFlagError = (1 << 0),
/**
* 0...00010 DDLogFlagWarning
*/
DDLogFlagWarning = (1 << 1),
/**
* 0...00100 DDLogFlagInfo
*/
DDLogFlagInfo = (1 << 2),
/**
* 0...01000 DDLogFlagDebug
*/
DDLogFlagDebug = (1 << 3),
/**
* 0...10000 DDLogFlagVerbose
*/
DDLogFlagVerbose = (1 << 4)
};
typedef NS_ENUM(NSUInteger, DDLogLevel){
/**
* No logs
*/
DDLogLevelOff = 0,
/**
* Error logs only
*/
DDLogLevelError = (DDLogFlagError),
/**
* Error and warning logs
*/
DDLogLevelWarning = (DDLogLevelError | DDLogFlagWarning),
/**
* Error, warning and info logs
*/
DDLogLevelInfo = (DDLogLevelWarning | DDLogFlagInfo),
/**
* Error, warning, info and debug logs
*/
DDLogLevelDebug = (DDLogLevelInfo | DDLogFlagDebug),
/**
* Error, warning, info, debug and verbose logs
*/
DDLogLevelVerbose = (DDLogLevelDebug | DDLogFlagVerbose),
/**
* All logs (1...11111)
*/
DDLogLevelAll = NSUIntegerMax
}
那么满足什么样条件的日志会被放行即输出到日志文件或控制台呢?
只要LogLevel & LogFlag
判断结果为true,就放行
因为涉及到与和位移运算,使用表格看起来更容易理解
DDLogFlag | 二进制表示 | 十进制 | DDLogLevel | 二进制表示 | 十进制 |
---|---|---|---|---|---|
DDLogFlagError | 0001 | 1 | DDLogLevelOff | 0 | 0 |
DDLogFlagWarning | 0010 | 2 | DDLogLevelError | 0001 | 1 |
DDLogFlagInfo | 0100 | 4 | DDLogLevelWarning | 0011 | 3 |
DDLogFlagDebug | 1000 | 8 | DDLogLevelInfo | 0111 | 7 |
DDLogFlagVerbose | 10000 | 16 | DDLogLevelDebug | 1111 | 15 |
DDLogLevelVerbose | 11111 | 31 | |||
DDLogLevelAll | NSUIntegerMax | 很大 |
随便举几个例子:
-
我们使用
DDLogInfo
宏打印日志,对应着LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagInfo, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
- 假设此时我们设置
LOG_LEVEL_DEF
=DDLogLevelDebug
- 则
LogLevel & LogFlag
为DDLogLevelDebug & DDLogFlagInfo
,等价于0100 & 1111,结果是0100,判断结果为true,放行! - 通俗的解释就是,我们将logleve设置为debug级别,所以
DDLogInfo
的日志肯定会输出
- 假设此时我们设置
-
同样使用
DDLogInfo
,假设我们设置LOG_LEVEL_DEF
=DDLogLevelError
- 则
LogLevel & LogFlag
为DDLogLevelError & DDLogFlagInfo
,等价于0100 & 1111,结果是0000,判断结果为false,不放行! - 通俗的解释就是,我们将logleve设置为error级别,所以使用
DDLogInfo
输出日志时最终不会输出
- 则
设置全局LogLevel
Objective C
CocoaLumberjack定义了LOG_LEVEL_DEF
宏,取值为ddLogLevel
#ifndef LOG_LEVEL_DEF
#define LOG_LEVEL_DEF ddLogLevel
#endif
然后在执行写日志方法处使用了LOG_LEVEL_DEF
#define DDLogDebug(frmt, ...) LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagDebug, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
#define DDLogVerbose(frmt, ...) LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagVerbose, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
所以,将ddLogLevel
当做一个全局变量时,就实现了全局LogLevel能力
使用方法如下:
DDLogLevel ddLogLevel;
ddLogLevel = DDLogLevelVerbose;
DDLogError(@"%@: Error", THIS_FILE);
ddLogLevel = DDLogLevelInfo;
DDLogInfo(@"log flag info : %ld", DDLogFlagInfo);
Swift
由于Swift不支持C语言中的宏定义,所以全局LogLevel有所不同
通过查看CocoaLumberjack.swift
中的日志上报逻辑,发现log leve是从DDDefaultLogLevel
取的,这是一个全局静态常量
public func DDLogInfo(_ message: @autoclosure () -> String,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool = asyncLoggingEnabled,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(), level: level, flag: .info, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
}
#ifndef DD_LOG_LEVEL
// #warning 'DD_LOG_LEVEL' is not defined. Using 'DDLogLevelAll' as default. Consider defining it yourself.
#define DD_LOG_LEVEL DDLogLevelAll
#endif
static const DDLogLevel DDDefaultLogLevel = DD_LOG_LEVEL;
不过,需要注意的是,由于DDDefaultLogLevel
是常量,无法动态修改,所以已经不再建议使用了,改为建议使用dynamicLogLevel
,且默认值是DDLogLevelAll
,具体使用方法如下
public var dynamicLogLevel = DDLogLevel.all
dynamicLogLevel = .info
DDLogInfo("123")
不同文件设置不同LogLevel
Objective C
// File1.m
static DDLogLevel ddLogLevel = DDLogLevelInfo;
@implementation File1
- (void)method {
DDLogInfo(@"xxxx");
}
@end
// File2.m
static DDLogLevel ddLogLevel = DDLogLevelError;
@implementation File2
- (void)method {
DDLogInfo(@"xxxx");
}
@end
Swift
Swift中稍微麻烦点
// File1.swift
let file1LogLevel: DDLogLevel = .debug
class File1: NSObject {
func log() {
DDLogInfo("123", level:file1LogLevel)
}
}
// File2.swift
let file2LogLevel: DDLogLevel = .warning
class File2: NSObject {
func log() {
DDLogInfo("123", level:file2LogLevel)
}
}
不同Xcode configuratioin设置不同LogLevel
Objective C
#ifdef DEBUG
static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
#else
static const DDLogLevel ddLogLevel = DDLogLevelWarning;
#endif
Swift
Swift下类似
注意,以下代码需要设置Swift other flag
#if DEBUG
dynamicLogLevel = .verbose
#else
dynamicLogLevel = .error
#endif
DDLogError("error 123")
不同Logger设置不同的LogLevel
Objective C和Swift的API是一致的
// OC
[DDLog addLogger:logger withLevel:DDLogLevelError];
// Swift
DDLog.add(logger, with: .error)
自定义LogLevel
自定义loglevel时,可以参考官方的LogLevel和LogFlag增加、或重写自己的loglevel和logflag,最终将参数传入对应的log方法即可。可以参考Demo中的CustomLogLevels
工程