Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
239 views
in Technique[技术] by (71.8m points)

ios - How to disable logging?

I have an app that is making use of UITextChecker class provided by Apple. This class has a following bug: If the iOS device is offline (expected to be often for my app) each time I call some of UITextCheckers methods it logs following to console:

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."

I do not want to have logs spammed by this messages. Is there a way to disable logging from code? I would disable logging before call to any of UITextCheckers methods and reenable it afterwards. Or is there perhaps any way how to disable logging selectively per class (event if it is foundation class not mine)? Or any other solution?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Warning: This answer uses the private-yet-sort-of-documented Cocoa C functions _NSSetLogCStringFunction() and _NSLogCStringFunction().

_NSSetLogCStringFunction() is an interface created by Apple to handle the implementation function for NSLog. It was initially exposed for WebObjects to hook into NSLog statements on Windows machines, but still exists in the iOS and OS X APIs today. It is documented in this support article.

The function takes in a function pointer with the arguments const char* message, the string to log, unsigned length, the length of the message, and a BOOL withSysLogBanner which toggles the standard logging banner. If we create our own hook function for logging that doesn't actually do anything (an empty implementation rather than calling fprintf like NSLog does behind-the-scenes), we can effectively disable all logging for your application.

Objective-C Example (or Swift with bridging header):

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!

");  // observe this isn't logged

An example implementation of this can be found in YILogHook, which provides an interface to add an array of blocks to any NSLog statement (write to file, etc).

Pure Swift Example:

@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!

");  // observe this isn't logged

In Swift, you can also chose to ignore all of the block parameters without using hookFunc like so:

_NSSetLogCStringFunction { _,_,_ in }

To turn logging back on using Objective-C, just pass in NULL as the function pointer:

_NSSetLogCStringFunction(NULL);

With Swift things are a little different, since the compiler will complain about a type mismatch if we try to pass in nil or a nil pointer (NULL is unavailable in Swift). To solve this, we need to access another system function, _NSLogCStringFunction, to get a pointer to the default logging implementation, retain that reference while logging is disabled, and set the reference back when we want to turn logging back on.

I've cleaned up the Swift implementation of this by adding a NSLogCStringFunc typedef:

/// 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.")

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...