WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Redirecting output to a log file when not attached to debugger

Forums > iOS

Can anyone help with an issue I'm having redirecting console output to a logfile? I've been able to find a lot of info on the web about this and have used various different approaches, but I still haven't managed to figure out a complete solution. I've based my approach on one I found here.

I created a very simple demo app to show the issue. Here's the code for the view controller.

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()        
        FileLogger.logToFile()        
        print("viewDidLoad")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("viewDidAppear")
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            self.makeNetworkRequest()
        }
    }

    private func makeNetworkRequest() {
        print("making network request...")
        self.label.text = "Waiting"
        let request = URLRequest(url: URL(string: "http://192.168.1.123")!, timeoutInterval: 5)
        URLSession.shared.dataTask(with: request) { data, response, error in
            print("got response")
            DispatchQueue.main.async {
                self.label.text = "Done"
            }
        }.resume()
    }

}

I deliberately used a bad URL in order to produce an error.

Here's the relevant code in the FileLogger class.

private static func openConsolePipe() {
        setvbuf(stdout, nil, _IONBF, 0)
        setvbuf(stderr, nil, _IONBF, 0)

        // open a pipe to consume data sent into STDOUT and STDERR
        inputPipe = Pipe()

        // open a pipe to output data back to STDOUT
        outputPipe = Pipe()

        guard let inputPipe = inputPipe, let outputPipe = outputPipe else {
            return
        }

        let inputPipeReadHandle = inputPipe.fileHandleForReading

        // Redirect data sent into the output pipe into STDOUT, too.
        dup2(STDOUT_FILENO, outputPipe.fileHandleForWriting.fileDescriptor)

        // Redirect data sent into STDOUT and STDERR into the input pipe, too.
        dup2(inputPipe.fileHandleForWriting.fileDescriptor, STDOUT_FILENO)
        dup2(inputPipe.fileHandleForWriting.fileDescriptor, STDERR_FILENO)

        // listen for the readCompletionNotification.
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(didReceiveReadCompletionNotification),
                                               name: FileHandle.readCompletionNotification,
                                               object: inputPipeReadHandle)

        // We want to be notified of any data coming into our input pipe.
        inputPipeReadHandle.readInBackgroundAndNotify()
    }

    @objc static func didReceiveReadCompletionNotification(notification: Notification) {
        // Need to call this to keep getting notified.
        inputPipe?.fileHandleForReading.readInBackgroundAndNotify()

        if let data = notification.userInfo?[NSFileHandleNotificationDataItem] as? Data,
           let str = String(data: data, encoding: .utf8) {

            // Write the data back into the output pipe.
            // The output pipe's write file descriptor points to STDOUT,
            // which makes the logs show up in the Xcode console.
            outputPipe?.fileHandleForWriting.write(data)

            writeToLogfile(str.trimmingCharacters(in: .whitespacesAndNewlines))
        }
    }

When I run this code in the debugger, I see the following in the Xcode console:

log to file
docDirName
Library/Developer/CoreSimulator/Devices/6B7F8E2C-C7F6-4EBA-8349-285F1BC981FF/data/Containers/Data/Application/D5EC52F2-9313-4DB8-9B4F-43150CE97431/Documents/
logfileName 11-22-34.log
viewDidLoad
viewDidAppear
making network request...
2021-01-12 11:22:37.650138-0500 LogTestSimple[3331:114318] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
2021-01-12 11:22:42.571694-0500 LogTestSimple[3331:114324] Task <66107057-57AB-4488-9E78-577A46CD5BEE>.<1> finished with error [-1001] Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={_kCFStreamErrorCodeKey=-2102, NSUnderlyingError=0x600001928960 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <66107057-57AB-4488-9E78-577A46CD5BEE>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <66107057-57AB-4488-9E78-577A46CD5BEE>.<1>"
), NSLocalizedDescription=The request timed out., NSErrorFailingURLStringKey=http://192.168.1.123/, NSErrorFailingURLKey=http://192.168.1.123/, _kCFStreamErrorDomainKey=4}
got response

The output contains messages generated both from my print statements as well as system output telling me about the network error. When I look at the log file, it also contains all the output I see in the console. So far, so good.

The problem is when I run the same code when the debugger is not attached (in other words, in the actual use case for this functionality). In that case, the log file contains only the outut from my print statements, but not the system output.

Can anybody tell me what I'm doing wrong? Does the system output go into some stream other than STDOUT or STDERR when the debugger is not attached? Or is it suppressed entirely when the debugger isn't attached?

Any help with this would be very much appreciated.

2      

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.