如何禁用日志记录?

时间:2016-03-09 00:59:03

标签: ios swift logging nslog uitextchecker

我的应用程序正在使用Apple提供的UITextChecker类。这个类有以下错误:如果iOS设备离线(预计通常是我的应用程序),每次我调用一些UITextChecker方法时,它会记录到控制台:

2016-03-08 23:48:02.119 HereIsMyAppName [4524:469877] UITextChecker sent string:isExemptFromTextCheckerWithCompletionHandler: to com.apple.TextInput.rdt but received error Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.TextInput.rdt was invalidated."

我不希望此邮件发送垃圾邮件。有没有办法从代码中禁用日志记录?我会在调用任何UITextChecker方法之前禁用日志记录,然后重新启用它。或者是否有任何方法可以选择性地禁用每个类的日志记录(如果基础类不是我的,则为事件)?还是其他任何解决方案?

1 个答案:

答案 0 :(得分:9)

警告:此答案使用私有但尚未记录的Cocoa C函数_NSSetLogCStringFunction()_NSLogCStringFunction()

_NSSetLogCStringFunction()是Apple创建的用于处理NSLog的实现功能的界面。它最初是为WebObjects公开,以挂钩到Windows机器上的NSLog语句,但仍然存在于今天的iOS和OS X API中。它记录在this支持文章中。

该函数接收带有参数const char* message的函数指针,要记录的字符串,unsigned length,消息的长度以及切换标准日志记录横幅的BOOL withSysLogBanner。如果我们为日志创建我们自己的钩子函数实际上没有做任何事情(一个空的实现,而不是像fprintf那样调用NSLog做幕后的事情),我们可以有效地禁用你的所有日志记录应用

Objective-C示例(或带有桥接头的Swift):

extern void _NSSetLogCStringFunction(void(*)(const char*, unsigned, BOOL));

static void hookFunc(const char* message, unsigned length, BOOL withSysLogBanner) { /* Empty */ }

// Later in your application

_NSSetLogCStringFunction(hookFunc);

NSLog(@"Hello _NSSetLogCStringFunction!\n\n");  // observe this isn't logged

可以在YILogHook中找到此示例实现,它提供了一个接口,用于向任何NSLog语句添加块数组(写入文件等)。

Pure Swift示例:

@asmname("_NSSetLogCStringFunction") // NOTE: As of Swift 2.2 @asmname has been renamed to @_silgen_name
func _NSSetLogCStringFunction(_: ((UnsafePointer<Int8>, UInt32, Bool) -> Void)) -> Void

func hookFunc(message: UnsafePointer<Int8>, _ length: UInt32, _ withSysLogBanner: Bool) -> Void { /* Empty */ }

_NSSetLogCStringFunction(hookFunc)

NSLog("Hello _NSSetLogCStringFunction!\n\n");  // observe this isn't logged

在Swift中,您也可以选择忽略所有块参数而不使用hookFunc,如下所示:

_NSSetLogCStringFunction { _,_,_ in }

要使用Objective-C重新开启记录,只需传入NULL作为函数指针:

_NSSetLogCStringFunction(NULL);

使用Swift的东西有点不同,因为如果我们尝试传入nilnil指针(NULL在Swift中不可用),编译器会抱怨类型不匹配。为了解决这个问题,我们需要访问另一个系统函数_NSLogCStringFunction,以获取指向默认日志记录实现的指针,在禁用日志记录时保留该引用,并在我们想要重新开启日志记录时设置引用。

我通过添加NSLogCStringFunc typedef来清理Swift的实现:

/// Represents the C function signature used under-the-hood by NSLog
typealias NSLogCStringFunc = (UnsafePointer<Int8>, UInt32, Bool) -> Void

/// Sets the C function used by NSLog
@_silgen_name("_NSSetLogCStringFunction") // NOTE: As of Swift 2.2 @asmname has been renamed to @_silgen_name
func _NSSetLogCStringFunction(_: NSLogCStringFunc) -> Void

/// Retrieves the current C function used by NSLog
@_silgen_name("_NSLogCStringFunction")
func _NSLogCStringFunction() -> NSLogCStringFunc

let logFunc = _NSLogCStringFunction() // get function pointer to the system log function before we override it

_NSSetLogCStringFunction { (_, _, _) in } // set our own log function to do nothing in an anonymous closure

NSLog("Observe this isn't logged.");

_NSSetLogCStringFunction(logFunc) // switch back to the system log function

NSLog("Observe this is logged.")