Skip to content

Commit 02bc966

Browse files
committed
Migrate from using System’s FileDescriptor to the higher level Foundation’s FileHandle
1 parent d2d2be7 commit 02bc966

File tree

3 files changed

+30
-42
lines changed

3 files changed

+30
-42
lines changed

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,18 @@ let package = Package(
88
.macOS(.v11),
99
.tvOS(.v14),
1010
.iOS(.v14),
11-
.watchOS(.v7)
11+
.watchOS(.v7),
1212
],
1313
products: [
14-
.library(name: "CLTLogger", targets: ["CLTLogger"])
14+
.library(name: "CLTLogger", targets: ["CLTLogger"]),
15+
],
16+
dependencies: [
17+
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.1"),
1518
],
16-
dependencies: {
17-
var ret = [Package.Dependency]()
18-
ret.append(.package(url: "https://github.com/apple/swift-log.git", from: "1.5.1"))
19-
#if !canImport(System)
20-
ret.append(.package(url: "https://github.com/apple/swift-system.git", from: "1.0.0"))
21-
#endif
22-
return ret
23-
}(),
2419
targets: [
25-
.target(name: "CLTLogger", dependencies: {
26-
var ret = [Target.Dependency]()
27-
ret.append(.product(name: "Logging", package: "swift-log"))
28-
#if !canImport(System)
29-
ret.append(.product(name: "SystemPackage", package: "swift-system"))
30-
#endif
31-
return ret
32-
}(), path: "Sources"),
20+
.target(name: "CLTLogger", dependencies: [
21+
.product(name: "Logging", package: "swift-log"),
22+
], path: "Sources"),
3323
.testTarget(name: "CLTLoggerTests", dependencies: ["CLTLogger"], path: "Tests")
3424
]
3525
)

Sources/CLTLogger.swift

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
import Foundation
2-
#if canImport(System)
3-
import System
4-
#else
5-
import SystemPackage
6-
#endif
72

83
import 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

Comments
 (0)