Skip to content

Instantly share code, notes, and snippets.

@DevAndArtist
Last active December 8, 2017 15:11
Show Gist options
  • Save DevAndArtist/04dbb1c980b4484a64ecb5b1fce411d2 to your computer and use it in GitHub Desktop.
Save DevAndArtist/04dbb1c980b4484a64ecb5b1fce411d2 to your computer and use it in GitHub Desktop.
Logger (WIP)
import Darwin
import UIKit
protocol _LoggerDelegate : AnyObject {
///
func logger(_ logger: Logger, received newString: String)
}
final class Logger {
///
private let pipe = Pipe()
///
private var runLoopSource: CFRunLoopSource?
///
private let fileDescriptor: FileDescriptor
///
private var oldFileDescriptor: FileDescriptor?
///
typealias Delegate = _LoggerDelegate
///
weak var delegate: Delegate?
///
var isLogRerouted = false {
didSet { if isLogRerouted != oldValue { reroute(isLogRerouted) } }
}
///
var isLogForwarded = true
///
init(source: Source) {
fileDescriptor = source.fileDescriptor
switch source {
case .standardOutput:
setbuf(stdout, nil)
case .standardError:
setbuf(stderr, nil)
default:
break
}
}
///
deinit { isLogRerouted = false }
}
extension Logger {
///
private func reroute(_ shouldReroute: Bool) {
if shouldReroute {
oldFileDescriptor = dup(fileDescriptor)
guard oldFileDescriptor != -1 else { return assertionFailure("Could not dup file descriptor.") }
guard dup2(pipe.fileHandleForWriting.fileDescriptor, fileDescriptor) != -1 else {
return assertionFailure("Could not dup2 file descriptor.")
}
monitor(true)
} else {
guard
let unwrappedOldFileDescriptor = oldFileDescriptor,
dup2(unwrappedOldFileDescriptor, fileDescriptor) != -1
else { return assertionFailure("Could not dup2 back file descriptor.") }
monitor(false)
}
}
///
private func monitor(_ shouldMonitor: Bool) {
if shouldMonitor {
var context = CFSocketContext(version: 0,
info: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()),
retain: nil,
release: nil,
copyDescription: nil)
///
func receivedMessage(fromSocket socket: CFSocket?,
callbackType: CFSocketCallBackType,
cfData: CFData?,
dataPointer: UnsafeRawPointer?,
infoPointer: UnsafeMutableRawPointer?) {
guard let unwrappedDataPointer = dataPointer, let unwrappedInfoPointer = infoPointer else { return }
let realCFData = Unmanaged<CFData>.fromOpaque(unwrappedDataPointer).takeUnretainedValue()
let logger = Unmanaged<Logger>.fromOpaque(unwrappedInfoPointer).takeUnretainedValue()
let data = realCFData as NSData as Data
guard let string = String(data: data, encoding: .utf8) else { return }
logger.notifyDelegate(with: string)
guard
logger.isLogForwarded,
let oldFileDescriptor = logger.oldFileDescriptor,
data.withUnsafeBytes({ write(oldFileDescriptor, $0, data.count) }) != -1
else { return NSLog("Couldn't write to old file descriptor.") }
}
let cfSocket = CFSocketCreateWithNative(
kCFAllocatorDefault,
pipe.fileHandleForReading.fileDescriptor,
CFSocketCallBackType.dataCallBack.rawValue,
receivedMessage(fromSocket:callbackType:cfData:dataPointer:infoPointer:),
&context
)
guard let unwrappedCFSocket = cfSocket else { return NSLog("Couldn't make cfsocket.") }
runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, unwrappedCFSocket, 0)
guard let runLoopSource = runLoopSource else {
return NSLog("Couldn't create run loop source.")
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .defaultMode)
} else {
guard let runLoopSource = runLoopSource else { return }
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, .defaultMode)
self.runLoopSource = nil
}
}
///
private func notifyDelegate(with string: String) {
delegate?.logger(self, received: string)
}
}
extension Logger {
///
typealias FileDescriptor = Int32
///
enum Source {
case standardOutput
case standardError
case fileDescriptor(FileDescriptor)
///
var fileDescriptor: FileDescriptor {
switch self {
case .standardOutput:
return STDOUT_FILENO
case .standardError:
return STDERR_FILENO
case .fileDescriptor(let fileDescriptor):
return fileDescriptor
}
}
///
init(fileDescriptor: FileDescriptor) {
switch fileDescriptor {
case STDOUT_FILENO:
self = .standardOutput
case STDERR_FILENO:
self = .standardError
default:
self = .fileDescriptor(fileDescriptor)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment