在我的AppCode项目创建过程中,我想念最多的一件事是:能跳转到记录控制台信息的指定文件和行。
Xcode不提供这样的功能,而我不是一个喜欢抱怨的人,所以我决定自己写个插件。我用Swift来编写这个插件。
想法
如果一个控制台记录了fileName.extension:XX 这样一个名字,转换成可点击的超链接,这个链接将会打开指定的文件并将那行代码高亮。
那样你可以使用自己的记录机制,只要添加这个简单的前缀,比如:
【代码】
1 2 3 |
func logMessage(message: String, filename: String = __FILE__, line: Int = __LINE__, funct: String = __FUNCTION__) { print("\((filename as NSString).lastPathComponent):\(line) \(funct):\r\(message)") } |
或者可以使用CocoaLumberjack,你要想一些好的日志,可以用我的自定义格式。
Swift版本(Objective-C版本是KZBootstrap的一部分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import Foundation import CocoaLumberjack.DDDispatchQueueLogFormatter class KZFormatter: DDDispatchQueueLogFormatter { lazy var formatter: NSDateFormatter = { let dateFormatter = NSDateFormatter() dateFormatter.formatterBehavior = .Behavior10_4 dateFormatter.dateFormat = "HH:mm:ss.SSS" return dateFormatter }() override func formatLogMessage(logMessage: DDLogMessage!) -> String { let dateAndTime = formatter.stringFromDate(logMessage.timestamp) var logLevel: String let logFlag = logMessage.flag if logFlag.contains(.Error) { logLevel = "ERR" } else if logFlag.contains(.Warning){ logLevel = "WRN" } else if logFlag.contains(.Info) { logLevel = "INF" } else if logFlag.contains(.Debug) { logLevel = "DBG" } else if logFlag.contains(.Verbose) { logLevel = "VRB" } else { logLevel = "???" } let formattedLog = "\(dateAndTime) |\(logLevel)| \((logMessage.file as NSString).lastPathComponent):\(logMessage.line): ( \(logMessage.function) ): \(logMessage.message)" return formattedLog; } } |
实现—主要部分
要实现那些需求我们需要做到两点:
1、控制台NSTextStorage fixAttributesInRange–这样我们可以在找到正则表达式日志的时候随时更改属性。
2、NSTextView mouseDown–这样在控制台的链接里点击鼠标的时候,我们可以强迫Xcode打开文件并高亮那一行。
怎样把我们的功能注入到那些操作里去?
简单调整:
1 2 3 4 5 6 7 |
static func swizzleMethods() { let original = class_getInstanceMethod(NSClassFromString("NSTextStorage"), Selector("fixAttributesInRange:")) method_exchangeImplementations(original, class_getInstanceMethod(NSClassFromString("NSTextStorage"), Selector("kz_fixAttributesInRange:"))) let original2 = class_getInstanceMethod(NSClassFromString("NSTextView"), Selector("mouseDown:")) method_exchangeImplementations(original2, class_getInstanceMethod(NSClassFromString("NSTextView"), Selector("kz_mouseDown:"))) } |
我们如何确定一个NSTextStorage 是控制台实际的那个?
我们可以观察IDEControlGroupDidChangeNotification ,找到IDEConsoleTextView 并使用相关对象把存储标记为控制台的那个,这个随后就会排上用场。