为项目及第三方库重写NSLog方法
Great question! Let’s tackle this from two angles: overriding NSLog so your custom logic applies to third-party libraries, and alternative ways to intercept and modify all console output before it’s printed.
Since NSLog is a C function (not an Objective-C method), standard method swizzling won’t work here. The key is to hook the underlying system call that both your code and third-party libraries use when calling NSLog.
The most reliable way to do this is with fishhook, a lightweight library that lets you dynamically replace symbols in Mach-O binaries. This works for any code (including third-party libraries) that links against the system NSLog function dynamically.
Here’s a step-by-step implementation:
- Add fishhook to your project (via CocoaPods or manual integration).
- Write the hook logic to replace
NSLogwith your custom implementation:
#include <stdio.h> #include <stdarg.h> #include "fishhook.h" // Store a pointer to the original NSLog function void (*original_NSLog)(NSString *format, ...); // Your custom NSLog implementation with string replacement logic void custom_NSLog(NSString *format, ...) { va_list args; va_start(args, format); // Format the original log message NSString *originalMessage = [[NSString alloc] initWithFormat:format arguments:args]; // Apply your custom rule: replace "dog" with 🐶 NSString *modifiedMessage = [originalMessage stringByReplacingOccurrencesOfString:@"dog" withString:@"🐶"]; // Pass the modified message to the original NSLog original_NSLog(@"%@", modifiedMessage); va_end(args); } // Set up the hook early in your app's lifecycle void hookNSLog() { struct rebinding nslogRebinding = { "NSLog", custom_NSLog, (void *)&original_NSLog }; rebind_symbols((struct rebinding[1]){nslogRebinding}, 1); }
- Call
hookNSLog()at app startup—for example, inapplication:didFinishLaunchingWithOptions:ormain.mbeforeUIApplicationMain.
Important Notes:
- This works for almost all third-party libraries, as most dynamically link against
NSLog. If a library statically links and inlinesNSLog(a rare edge case), this method won’t catch those calls. - Always handle variable arguments correctly with
va_listto avoid crashes or malformed output.
If you want to capture all console output (not just NSLog—including printf, NSLogv, and system-level logs), you can redirect the standard error stream (stderr, which is where NSLog writes) to a pipe, process the output, then forward it to the original stream.
Here’s how to implement this with NSPipe:
#import <Foundation/Foundation.h> void redirectConsoleOutput() { // Create a pipe to capture stderr output NSPipe *pipe = [NSPipe pipe]; NSFileHandle *pipeReadHandle = [pipe fileHandleForReading]; // Redirect stderr to the pipe's write end dup2([[pipe fileHandleForWriting] fileDescriptor], STDERR_FILENO); // Start a background thread to read and process the output dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ while (YES) { NSData *data = [pipeReadHandle readDataToEndOfFile]; if (data.length == 0) continue; // Convert raw data to a string NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // Apply your custom modification NSString *modifiedOutput = [output stringByReplacingOccurrencesOfString:@"dog" withString:@"🐶"]; // Write the modified output back to the original stderr fprintf(stderr, "%s", modifiedOutput.UTF8String); fflush(stderr); } }); }
Call redirectConsoleOutput() early in your app’s lifecycle. This approach captures every bit of output sent to stderr, including logs from third-party libraries and system calls.
For iOS 10+, you could also use the os_log framework’s logging callbacks, but this requires more setup and specific entitlements for full log access. The pipe redirect method is simpler for most common use cases.
内容的提问来源于stack exchange,提问作者user16554276

