Last active
July 25, 2024 18:49
-
-
Save joncardasis/5612f19575766cbbd1672437427cba88 to your computer and use it in GitHub Desktop.
Reroute print and NSLogs to BOTH Xcode console and a file url [Swift + ObjC Solutions]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// DebugLogger.swift | |
// | |
// Created by Jonathan Cardasis. on 1/1/18. | |
// Copyright ©2018 Jonathan Cardasis. All rights reserved. | |
// | |
#if DEBUG | |
import Foundation | |
class DebugLogger { | |
static let shared: DebugLogger = { | |
let logger = DebugLogger() | |
logger.openConsolePipe() | |
return logger | |
}() | |
static private var inputPipe = Pipe() | |
static private var outputPipe = Pipe() | |
static private var fileRedirectURL: URL? | |
private init() { } | |
func redirectOutputToFile(_ fileURL: URL) { | |
DebugLogger.fileRedirectURL = fileURL | |
} | |
private func openConsolePipe() { | |
// Route everything that comes in throught the outputPipe back to xcode console | |
dup2(fileno(stdout), DebugLogger.outputPipe.fileHandleForWriting.fileDescriptor) | |
// Route printing functions that go to sys pipes (stdout & stderr) into local inputPipe | |
dup2(DebugLogger.inputPipe.fileHandleForWriting.fileDescriptor, fileno(stdout)) | |
dup2(DebugLogger.inputPipe.fileHandleForWriting.fileDescriptor, fileno(stderr)) | |
// Begin notification listener | |
let inputReadHandler = DebugLogger.inputPipe.fileHandleForReading | |
NotificationCenter.default.addObserver(self, selector: #selector(handlePipeNotification(notification:)), name: FileHandle.readCompletionNotification, object: inputReadHandler) | |
inputReadHandler.readInBackgroundAndNotify() | |
} | |
@objc private func handlePipeNotification(notification: Notification) { | |
// Listen for next input | |
DebugLogger.inputPipe.fileHandleForReading.readInBackgroundAndNotify() | |
if let data = notification.userInfo?[NSFileHandleNotificationDataItem] as? Data { | |
// Write data back to output pipe for xcode to display | |
DebugLogger.outputPipe.fileHandleForWriting.write(data) | |
// Write to file if applicable | |
if let fileURL = DebugLogger.fileRedirectURL { | |
if !FileManager.default.fileExists(atPath: fileURL.path) { | |
FileManager.default.createFile(atPath: fileURL.path, contents: nil, attributes: nil) | |
} | |
do { | |
let fileHandle = try FileHandle(forWritingTo: fileURL) | |
fileHandle.seekToEndOfFile() | |
fileHandle.write(data) | |
fileHandle.closeFile() | |
} catch { /* pass */ } | |
} | |
} | |
} | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// DebugLogger.h | |
// | |
// Created by Jonathan Cardasis. on 3/1/18. | |
// | |
#ifdef DEBUG | |
#import <Foundation/Foundation.h> | |
@interface DebugLogger : NSObject | |
+ (id)sharedInstance; | |
- (void)redirectOutputToFile:(NSURL*) fileURL; | |
@end | |
#endif | |
//----------------------------------------------// | |
// | |
// DebugLogger.m | |
// | |
// Created by Jonathan Cardasis. on 3/1/18. | |
// | |
#ifdef DEBUG | |
#import "DebugLogger.h" | |
@implementation DebugLogger | |
static NSPipe *inputPipe; | |
static NSPipe *outputPipe; | |
static NSURL *fileRedirectURL; | |
+ (id)sharedInstance { | |
static dispatch_once_t s = 0; | |
__strong static id _sharedObject = nil; | |
dispatch_once(&s, ^{ | |
_sharedObject = [[self alloc] init]; | |
inputPipe = [[NSPipe alloc] init]; | |
outputPipe = [[NSPipe alloc] init]; | |
[_sharedObject openConsolePipe]; | |
}); | |
return _sharedObject; | |
} | |
- (void)redirectOutputToFile:(NSURL*) fileURL { | |
fileRedirectURL = fileURL; | |
} | |
// MARK: - Private | |
- (void)openConsolePipe { | |
// Route everything that comes in throught the outputPipe back to xcode console | |
dup2(fileno(stdout), outputPipe.fileHandleForWriting.fileDescriptor); | |
// Route printing functions that go to sys pipes (stdout & stderr) into local inputPipe | |
dup2(inputPipe.fileHandleForWriting.fileDescriptor, fileno(stdout)); | |
dup2(inputPipe.fileHandleForWriting.fileDescriptor, fileno(stderr)); | |
// Begin notification listener | |
NSFileHandle *inputReadHandler = inputPipe.fileHandleForReading; | |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handlePipeNotification:) name:NSFileHandleReadCompletionNotification object:inputReadHandler]; | |
[inputReadHandler readInBackgroundAndNotify]; | |
} | |
- (void)handlePipeNotification:(NSNotification*) notification { | |
// Listen for next input | |
[inputPipe.fileHandleForReading readInBackgroundAndNotify]; | |
NSDictionary *userInfo = notification.userInfo; | |
if (userInfo == nil) { | |
return; | |
} | |
NSData *data = (NSData*)userInfo[NSFileHandleNotificationDataItem]; | |
if (data == nil) { | |
return; | |
} | |
[outputPipe.fileHandleForWriting writeData:data]; | |
NSURL *fileURL = fileRedirectURL; | |
if (fileURL != nil) { | |
if (![NSFileManager.defaultManager fileExistsAtPath:fileURL.path]) { | |
[NSFileManager.defaultManager createFileAtPath:fileURL.path contents:nil attributes:nil]; | |
} | |
NSError *err; | |
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingToURL:fileURL error:&err]; | |
if (err == nil) { | |
[fileHandle seekToEndOfFile]; | |
[fileHandle writeData:data]; | |
[fileHandle closeFile]; | |
} | |
} | |
} | |
@end | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func main() { | |
#if DEBUG | |
let path = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Documents/MyApp.log") | |
DebugLogger.shared.redirectOutputToFile(path) | |
#endif | |
print("Swiftly logging") | |
NSLog("Objectively saying something") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ... | |
#ifdef DEBUG | |
NSURL *debuggingLogURL = [[NSURL fileURLWithPath:NSHomeDirectory()] URLByAppendingPathComponent:@"Documents/MyApp.log"]; | |
[DebugLogger.sharedInstance redirectOutputToFile:debuggingLogURL]; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment