Skip to content

Instantly share code, notes, and snippets.

@zwaldowski
Created February 8, 2017 19:23
Show Gist options
  • Save zwaldowski/08135f6a579e79a241db9ed85d667533 to your computer and use it in GitHub Desktop.
Save zwaldowski/08135f6a579e79a241db9ed85d667533 to your computer and use it in GitHub Desktop.
import Foundation
/// A type representing an concrete part of an application, such that it can be
/// identified in logs.
///
/// A typical use is as a nested type:
///
/// extension MyViewController {
/// enum Log: Error {
/// case user
/// case network
/// }
///
/// @IBAction func userTappedButton(_ sender: Any) {
/// Log.user.debug("They tapped the button!")
/// }
///
/// func requestFailed(error: Error) {
/// Log.network.error("Could not log in: %@", error)
/// }
/// }
///
protocol LogSubsystem {
/// The name of a subsystem, such as "com.myapp.networking".
///
/// By default, the fully-qualified name of `self`.
static var name: String { get }
/// A stage or grouping for a subsystem, such as "setup" or "teardown".
///
/// By default, the description of `self`.
var categoryName: String { get }
/// Whether to print `debug` messages.
static var isDebugEnabled: Bool { get }
/// Whether to print `info` messages.
static var isInfoEnabled: Bool { get }
}
/// A type with a customized log representation.
///
/// Types that conform to the `CustomLogStringConvertible` protocol can provide
/// their own representation to be used when logging using a `LogSubsystem`.
///
/// Accessing a type's `logDescription` property directly is discouraged.
protocol CustomLogStringConvertible {
/// A console log representation of `self`.
var logDescription: String { get }
}
extension String {
/// Creates a console log representing the given value.
init<Subject>(logging value: Subject) {
if let subject = value as? CustomLogStringConvertible {
self = String(describing: subject.logDescription)
} else {
self = String(describing: value)
}
}
}
extension LogSubsystem {
static var name: String {
return String(reflecting: self)
}
var categoryName: String {
return String(describing: self)
}
static var isDebugEnabled: Bool {
return false
}
static var isInfoEnabled: Bool {
return false
}
}
extension LogSubsystem {
/// Logging shim. This was intended to be backed by `os_log`, but as of
/// Xcode 8.2 that unconditionally mirrors to `stderr`, which completely
/// defeats the point.
///
/// When a future Xcode fixes this (probably the next one, see link below),
/// the prototypes below could be changed to something a la:
///
/// func log(_ message: StaticString, dso: UnsafeRawPointer?, level: OSLogType, _ arguments: [Any])
///
/// And encoding the `os_log` buffer from there.
///
/// - see: https://github.com/apple/swift-lldb/blob/swift-3.1-branch/docs/structured_data/DarwinLog.md
private func log(_ message: StaticString, _ arguments: [Any]) {
let cArguments = arguments.map { (argument) -> CVarArg in
switch argument {
case let value as CustomLogStringConvertible:
return value.logDescription as NSString
case let value as CVarArg:
return value
default:
return String(describing: argument) as NSString
}
}
withVaList(cArguments) {
NSLogv("[\(categoryName)] \(message)", $0)
}
}
func debug(_ message: StaticString, _ arguments: Any...) {
guard Self.isDebugEnabled else { return }
log(message, arguments)
}
func info(_ message: StaticString, _ arguments: Any...) {
guard Self.isInfoEnabled else { return }
log(message, arguments)
}
func show(_ message: StaticString, _ arguments: Any...) {
log(message, arguments)
}
func error(_ message: StaticString, _ arguments: Any...) {
log(message, arguments)
}
func fault(_ message: StaticString, _ arguments: Any...) {
log(message, arguments)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment