日志记录是调试的重要部分,我经常对 NSLog 的灵活性不够感到沮丧。因此,我编写了 MTLog —— 这是我需要的灵活日志工具。
#import "MTLog.h"
以使 MTLog 在所有类中生效pod 'MTLog'
pod install
#import "MTLog.h"
以使 MTLog 在所有类中生效如果您想了解更多的 CocoaPods,请参阅 这篇简短教程。
MTLog 通过为日志消息添加脚本功能来工作。您按照常规使用 NSLog,但可以在日志消息文本中包含特定命令或添加自己的命令,这些命令将被执行。
最好您通过下面的示例进行阅读。
注意!总是确保您已导入 MTLog.h
默认情况下,新的 MTLog 实例会为您的消息提供一个默认的前缀。默认前缀包括当前类的文件名和当前行号(以下为示例输出)
NSLog(@"My log message!");
MyClass.m:20 > My log message!
如果您需要除默认以外的前缀,可以使用前缀命令来更改它
NSLog(@"_prefix:set($method $line) My log message!");
NSLog(@"My second message.");
myMethod 20 > My log message! myMethod 21 > My second message!
注意!:_prefix:set(…) 将设置当前日志消息的前缀,以及之后所有记录的日志消息。
除了文本,您还可以在日志的前缀中使用变量,这些变量将被它们的值替换并应用于每个日志消息。以下是可用变量的列表
$file | 当前文件的名称 |
$class | 当前类名 |
$method | 当前方法名 |
$line | 当前行号 |
$counter | 从1开始的计数器。每当你在同一文件的同一线路上进行日志记录时,它都会递增。 |
如果不希望对日志消息使用特殊的前缀,只需调用set而不带任何参数
NSLog(@"_prefix:set()");
如果您只想更改当前日志消息的前缀,请使用 _prefix:use(…)
NSLog(@"My log message!");
NSLog(@"_prefix:use($method $line) My log message!");
NSLog(@"My second message.");
MyClass.m:20 > My log message! myMethod 21 > My log message! MyClass.m:22 > My second message!
如果您更改前缀并想恢复到默认状态,请将“default”常量传递给前缀命令的set方法
NSLog(@"_prefix:set(default)");
您可以使用过滤器命令临时过滤日志输出,例如,如果您正在调试某个方法,您不想在此方法完成后看到所有其他日志语句的输出。
您可以使用过滤器以几种不同的方式。
在此日志语句后向 _filter 传递一个文件名,您将只能看到该文件的输出。
YourClass.m
...
+(void)message
{
NSLog(@"Your message!");
}
MyClass.m
...
NSLog(@"_filter:MyClass.m");
NSLog(@"My message");
[YourClass message];
MyClass.m:20 > My message // "Your message" will be filtered and not show up in the output console
您可以通过行号过滤输出。向 _filter 传递一个文件名,并以参数提供应生成控制台输出的行号范围。
NSLog(@"_filter:MyClass.m(10,100)");
此命令将只允许第10行到第100行之间的日志语句生成控制台输出。
您也可以将方法签名传递给 _filter 命令 - 然后,它将只允许与此签名匹配的方法的输出。
您可以堆叠 _filter 命令。例如,您只想看到 init 方法的输出,尽管您的每个类都有一个 init 方法。您可以将按文件名过滤和按方法名过滤的组合起来。
NSLog(@"_filter:MyClass.m");
NSLog(@"_filter:init");
在此之后,只有 MyClass.m 中的 "init" 的 NSLog 语句将生成输出。
有时您只想看到您代码中的特定行的输出,不会看到更多(例如,如果 NSLog 语句在 for 循环中)。
NSLog(@"begin counting");
for (int i=1;i<=3;i++) {
NSLog(@"_filter:$this i=%i", i);
NSLog(@"more loop output");
}
NSLog(@"_filter:$this loop ended");
NSLog(@"last log message");
一旦您使用 _filter:$this,后续的所有日志消息都将被过滤掉。除了也使用 _filter:$this 的那些之外。上述代码的输出是
MyClass.m:20 > begin counting MyClass.m:22 > i=1 MyClass.m:22 > i=2 MyClass.m:22 > i=3 MyClass.m:25 > loop ended
由于 filter 等命令会影响后续的所有日志消息,您需要一种方法来禁用它们。
使用 "_remove:" 后跟最初使用的确切命令。如果以上面的例子为例,并结合删除代码,代码可能如下所示
NSLog(@"_filter:$this Message 1");
NSLog(@"Message 2");
NSLog(@"_remove:_filter:$this"); //remove: + the command including arguments
NSLog(@"Message 3");
}
由于删除命令会删除过滤器,您将在输出控制台中看到最后一条日志。
MyClass.m:20 > Message 1 MyClass.m:23 > Message 3
一般来说,所有命令在执行后都会影响所有的日志。因此,您需要使用 _remove 来停用它们。
一些命令,如 _prefix:use(…),是一次性命令,因此您不需要对它们使用 _remove。
路由命令允许您将控制台中的输出克隆到日志文件中。如果您想要多次运行应用程序并分析日志内容,这将非常方便。
该命令在您的应用程序的文档文件夹中创建 "log.txt",并将所有输出(在命令活动期间)保存到此文件。
如果您提供了一个文件的全路径,则命令将在您指定的位置创建(或覆盖)它。
如果您将 "append" 常量作为第二个参数传递,则您的应用程序将在每次运行时向文件中添加内容(而不是每次运行时覆盖它)。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(@"message one");
NSLog(@"message two");
return YES;
}
--- 2013-09-14 08:36:21 +0000 ---- New log entry ---- [application:didFinishLaunchingWithOptions: 19] message one [application:didFinishLaunchingWithOptions: 20] message two --- 2013-09-14 08:42:01 +0000 ---- New log entry ---- [application:didFinishLaunchingWithOptions: 19] message one [application:didFinishLaunchingWithOptions: 20] message two
有时控制台中的输出如此之多,以至于您找不到您真正想看到的某一行。而且很难找到在代码中的哪一行产生了那个输出,并在这里进行断点。搜索命令可以帮助您找到特定的日志消息。
此命令在包含搜索项的每一行之前添加20行空行。当搜索项出现时,它有点像“清理”控制台,以便您可以更容易地找到它。
NSLog(@"_search:clear(vacation)");
…
NSLog(@"Yupee!");
NSLog(@"I'm going on vacation!");
MyClass.m:20 > Yupee! … //19 more empty lines MyClass.m:22 > I'm going on vacation!
每当搜索项出现在日志消息中时,应用程序会抛出异常并捕获它,这样执行就不会中断。如果您为所有异常设置了断点,则 Xcode 将在搜索插件内中断,这样您就可以调试代码到堆栈。
现在是最好的命令 - 允许您将新命令注册到 MTLog。有关于可以在调试时真正帮助您的命令的想法吗?加一个!
您需要子类化 MTLogPlugin 并覆盖以下方法(按照调用顺序)中的某些或全部方法
// to tell MTLog how many arguments you are expecting
// for example return [0,2] - for zero, one or two args
+(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command;
// the custom plugin init- name contains the command, value is the part after the colon
// args is an array of the arguments (not trimmed of spaces)
-(instancetype)initWithName:(NSString*)name value:(NSString*)value args:(NSArray*)args;
// the method is invoked just before the plugin is added to enabled plugins list
-(void)willEnableForLog:(MTLog*)log;
// text contains the log message, you can alter it in any way and return it
// look up "env" below
-(NSString*)preProcessLogMessage:(NSString*)text env:(NSArray*)env;
// use this method to react to a command after the message is logged
// look up "env" below
-(void)postProcessLogMessage:(NSString*)text env:(NSArray*)env;
"env" 数组包含以下内容
如果您需要,可以更改启用的和注册的插件列表。
让我们看看一个新的名为 “smilie” 的命令的代码,这个命令能为每个记录的消息添加一个表情。
PluginSmilie.h
#import "MTLogPlugin.h"
@interface PluginSmilie : MTLogPlugin
@end
PluginSmilie.m
#import "PluginSmilie.h"
@implementation PluginSmilie
+(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command
{
if ([command isEqualToString:@"extended"]) {
return NSMakeRange(1, 1);
}
return NSMakeRange(0, 0);
}
-(NSString*)preProcessLogMessage:(NSString *)text env:(NSArray *)env
{
if ([self.value isEqualToString:@"classic"]) {
return [NSString stringWithFormat:@"%@ :-)", text];
}
if ([self.value isEqualToString:@"extended"]) {
return [NSString stringWithFormat:@"%@ :%@)", text, self.args.firstObject];
}
return text;
}
@end
这是MTLog的一个完整的工作命令。
在 expectedNumberOfArgumentsForCommand:
中,你设置你期望 "_smilie:extend" 命令有1到1个参数,而 "_smilie:classic" 命令没有参数。
然后在 preProcessLogMessage:env:
中,你将一个表情添加到文本参数中,这将改变正在记录的消息。
让我们看看如何在你的代码中使用这个新的命令。
#import "PluginSmilie.h"
NSLog(@"_register:smilie(PluginSmilie)");
…
NSLog(@"Message One");
NSLog(@"_smilie:classic Message Two");
NSLog(@"Message Three");
NSLog(@"_remove:_smilie:classic");
NSLog(@"Message Four");
NSLog(@"_smilie:extended(-{)");
NSLog(@"A hipster message");
MyClass.m:20 > Message One MyClass.m:21 > Message Two :-) MyClass.m:22 > Message Three :-) MyClass.m:25 > Message Four MyClass.m:28 > A hipster message :-{)
此代码根据MIT许可的条款和条件进行分发。
注意:如果你修复了发现的bug或添加了新功能,请还添加一个单元测试,这样我就能在合并之前确切地知道如何重现bug。