11import Foundation
2- #if canImport(System)
3- import System
4- #else
5- import SystemPackage
6- #endif
72
83import Logging
94
@@ -18,7 +13,7 @@ import Logging
1813 + Ouptuts to stderr by default.
1914 The idea is: “usable” text (text that is actually what the user asked for when launching your tool) should be output to stdout,
2015 presumably using `print`, the rest should be on stderr.
21- If needed you can setup the logger to use any fd , the logger will simply `write(2)` to it.
16+ If needed you can setup the logger to use any file descriptor (via a FileHandle) , the logger will simply `write(2)` to it.
2217 + Ouptut has special control chars for colors if the output fd is a tty and Xcode is not detected.
2318 You can force using or force not using colors.
2419 + If the write syscall fails, the log is lost (or partially lost; interrupts are retried; see SystemPackage for more info).
@@ -130,27 +125,27 @@ public struct CLTLogger : LogHandler {
130125 }
131126 public var metadataProvider : Logger . MetadataProvider ?
132127
133- public let outputFileDescriptor : FileDescriptor
128+ public let outputFileHandle : FileHandle
134129 public let multilineMode : MultilineMode
135130 public let constantsByLevel : [ Logger . Level : Constants ]
136131
137- public init ( fd : FileDescriptor = . standardError, multilineMode: MultilineMode = . default, logStyle: Style = . auto, metadataProvider: Logger . MetadataProvider ? = LoggingSystem . metadataProvider) {
138- let logPrefixStyle = ( logStyle != . auto ? logStyle : CLTLogger . autoLogStyle ( with: fd ) )
132+ public init ( fileHandle : FileHandle = . standardError, multilineMode: MultilineMode = . default, logStyle: Style = . auto, metadataProvider: Logger . MetadataProvider ? = LoggingSystem . metadataProvider) {
133+ let logPrefixStyle = ( logStyle != . auto ? logStyle : CLTLogger . autoLogStyle ( with: fileHandle ) )
139134
140135 let constantsByLevel : [ Logger . Level : Constants ]
141136 switch logPrefixStyle {
142137 case . none: constantsByLevel = [ : ]
143138 case . text: constantsByLevel = CLTLogger . defaultConstantsByLogLevelForText
144- case . emoji: constantsByLevel = CLTLogger . defaultConstantsByLogLevelForEmoji ( on: fd )
139+ case . emoji: constantsByLevel = CLTLogger . defaultConstantsByLogLevelForEmoji ( on: fileHandle )
145140 case . color: constantsByLevel = CLTLogger . defaultConstantsByLogLevelForColors
146141 case . auto: fatalError ( )
147142 }
148143
149- self . init ( fd : fd , multilineMode: multilineMode, constantsByLevel: constantsByLevel, metadataProvider: metadataProvider)
144+ self . init ( fileHandle : fileHandle , multilineMode: multilineMode, constantsByLevel: constantsByLevel, metadataProvider: metadataProvider)
150145 }
151146
152- public init ( fd : FileDescriptor = . standardError, multilineMode: MultilineMode = . default, constantsByLevel: [ Logger . Level : Constants ] , metadataProvider: Logger . MetadataProvider ? = LoggingSystem . metadataProvider) {
153- self . outputFileDescriptor = fd
147+ public init ( fileHandle : FileHandle = . standardError, multilineMode: MultilineMode = . default, constantsByLevel: [ Logger . Level : Constants ] , metadataProvider: Logger . MetadataProvider ? = LoggingSystem . metadataProvider) {
148+ self . outputFileHandle = fileHandle
154149 self . multilineMode = multilineMode
155150 self . constantsByLevel = constantsByLevel
156151
@@ -174,22 +169,25 @@ public struct CLTLogger : LogHandler {
174169 /* We compute the data to print outside of the lock. */
175170 let data = Self . format ( message: message. description, flatMetadata: effectiveFlatMetadata, multilineMode: multilineMode, constants: constants)
176171
177- Self . write ( data, to: outputFileDescriptor )
172+ Self . write ( data, to: outputFileHandle )
178173 }
179174
180175 /** Writes to the given file descriptor like the logger would. */
181- public static func write( _ data: Data , to fd : FileDescriptor ) {
176+ public static func write( _ data: Data , to fh : FileHandle ) {
182177 /* We lock, because the writeAll function might split the write in more than 1 write
183178 * (if the write system call only writes a part of the data).
184- * If another part of the program writes to fd , we might get interleaved data,
179+ * If another part of the program writes to the file descriptor , we might get interleaved data,
185180 * because they cannot be aware of our lock (and we cannot be aware of theirs if they have one). */
186181 CLTLogger . lock. withLock {
187182 /* Is there a better idea than silently drop the message in case of fail? */
188- _ = try ? fd. writeAll ( data)
183+ /* Is the write retried on interrupt?
184+ * We’ll assume yes, but we don’t and can’t know for sure
185+ * until FileHandle has been migrated to the open-source Foundation. */
186+ _ = try ? fh. write ( contentsOf: data)
189187 }
190188 }
191189
192- private static func autoLogStyle( with fd : FileDescriptor ) -> Style {
190+ private static func autoLogStyle( with fh : FileHandle ) -> Style {
193191 if let s = getenv ( " CLTLOGGER_LOG_STYLE " ) {
194192 switch String ( cString: s) {
195193 case " none " : return . none
@@ -204,14 +202,14 @@ public struct CLTLogger : LogHandler {
204202
205203 /* Is the fd on which we write a tty?
206204 * Most ttys nowadays support colors, with a notable exception: Xcode. */
207- if isatty ( fd . rawValue ) != 0 {
205+ if isatty ( fh . fileDescriptor ) != 0 {
208206 /* Xcode detection: it ain’t trivial.
209207 * I found checking for the existence of the __XCODE_BUILT_PRODUCTS_DIR_PATHS env var to be a possible solution.
210208 * We could also probably check for the existence of the TERM env var: Xcode does not set it.
211209 * (When Package.swift is built we can check if the value of the __CFBundleIdentifier env var is "com.apple.dt.Xcode".)
212210 * The solution we’re currently using is to check whether the fd on which we write has a foreground process group as Xcode does not set one.
213211 * Note: If Xcode detection is changed here, it should also be changed in defaultConstantsByLogLevelForEmoji. */
214- if tcgetpgrp ( fd . rawValue ) == - 1 && errno == ENOTTY {
212+ if tcgetpgrp ( fh . fileDescriptor ) == - 1 && errno == ENOTTY {
215213 /* We log using emojis in Xcode. */
216214 return . emoji
217215 }
@@ -264,10 +262,10 @@ public extension CLTLogger {
264262 ]
265263 } ( )
266264
267- static func defaultConstantsByLogLevelForEmoji( on fd : FileDescriptor ) -> [ Logger . Level : Constants ] {
265+ static func defaultConstantsByLogLevelForEmoji( on fh : FileHandle ) -> [ Logger . Level : Constants ] {
268266 func addMeta( _ str: String , _ padding: String ) -> Constants {
269267 var str = str
270- if isatty ( fd . rawValue ) != 0 , tcgetpgrp ( fd . rawValue ) == - 1 , errno == ENOTTY {
268+ if isatty ( fh . fileDescriptor ) != 0 , tcgetpgrp ( fh . fileDescriptor ) == - 1 , errno == ENOTTY {
271269 /* We’re in Xcode (probably).
272270 * By default we do not do the emoji padding, unless explicitly asked to (`CLTLOGGER_TERMINAL_EMOJI` set to anything but “NO”). */
273271 if let s = getenv ( " CLTLOGGER_TERMINAL_EMOJI " ) , String ( cString: s) != " NO " {
0 commit comments