Skip to content

Instantly share code, notes, and snippets.

@daehn

daehn/Log.swift

Last active Nov 12, 2018
Embed
What would you like to do?
Lightweight logging class supporting different topics
import Foundation
final class Log: NSObject {
enum Topic: String {
case network, app
}
private enum Level: String {
case info = ""
case warn = ""
}
var isEnabled: Bool {
return Environment.current.logTopics.contains(topic)
}
static var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.timeStyle = .medium
formatter.dateStyle = .short
return formatter
}()
private let topic: Topic
init(topic: Topic) {
self.topic = topic
}
private func log(
level: Level,
message: String,
file: String = #file,
function: String = #function,
line: Int = #line
) {
guard isEnabled else { return }
let fileUrl = URL(fileURLWithPath: file)
let fileExtension = fileUrl.pathExtension
let fileName = fileUrl.deletingPathExtension().lastPathComponent
let date = Log.dateFormatter.string(from: .init())
let source = "\(fileName).\(fileExtension):\(line) \(function)"
var output = "\(date) [\(topic.rawValue.uppercased())] \(source): \(message)"
if !level.rawValue.isEmpty {
output = "\(level.rawValue) \(output)"
}
// FIXME: Using `print` is probably not the best choice here, in theory it would be best to
// use os_log as seen here: https://github.com/jaredsinclair/etcetera/blob/master/OSLog.swift.
print(output)
}
func info(
_ message: String,
file: String = #file,
function: String = #function,
line: Int = #line
) {
log(level: .info, message: message, file: file, function: function, line: line)
}
func warn(
_ message: String,
file: String = #file,
function: String = #function,
line: Int = #line
) {
log(level: .warn, message: message, file: file, function: function, line: line)
}
}
// MARK: - Environment
struct Environment {
static let current = Environment()
/// Add cases here to use them in Scheme (⌘ ⇧ ,) → Run → Environment Variables
private enum Keys: String {
case logTopics = "LogTopics"
case runningTests = "XCTestConfigurationFilePath"
}
enum Configuration: String {
case debug, release
}
let isRunningTests: Bool
let logTopics: Set<Log.Topic>
private init() {
let env = ProcessInfo.processInfo.environment
isRunningTests = nil != env[Keys.runningTests]
switch (Environment.buildConfiguration, isRunningTests) {
case (.release, _), (.debug, true):
// Disable logs when running in release configuration or when running tests.
logTopics = []
case (.debug, false):
let topics = env[Keys.logTopics]?.components(separatedBy: ",") ?? []
logTopics = Set(topics.compactMap { Log.Topic(rawValue: $0.lowercased()) })
}
}
static var buildConfiguration: Configuration {
#if RELEASE
return .release
#else
return .debug
#endif
}
}
// MARK: - CustomStringConvertible
extension Environment: CustomStringConvertible {
var description: String {
let configuration = Environment.buildConfiguration.rawValue.uppercased()
let logs = "Enabled log topics: \(logTopics.map { "\"\($0)\"" }.joined(separator: ", "))"
return "Configuration: \(configuration). \(logs).\n"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment