Files
swiftGrammar/Pods/SwiftyBeaver/Sources/SwiftyBeaver.swift
2024-08-12 10:49:20 +08:00

247 lines
9.8 KiB
Swift

//
// SwiftyBeaver.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 28.11.15.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
open class SwiftyBeaver {
/// version string of framework
public static let version = "2.1.1" // UPDATE ON RELEASE!
/// build number of framework
public static let build = 2110 // version 1.6.2 -> 1620, UPDATE ON RELEASE!
public enum Level: Int {
case verbose = 0
case debug = 1
case info = 2
case warning = 3
case error = 4
case critical = 5
case fault = 6
}
// a set of active destinations
public private(set) static var destinations = Set<BaseDestination>()
/// A private queue for synchronizing access to `destinations`.
/// Read accesses are done concurrently.
/// Write accesses are done with a barrier, ensuring only 1 operation is ran at that time.
private static let queue = DispatchQueue(label: "destination queue", attributes: .concurrent)
// MARK: Destination Handling
/// returns boolean about success
@discardableResult
open class func addDestination(_ destination: BaseDestination) -> Bool {
return queue.sync(flags: DispatchWorkItemFlags.barrier) {
if destinations.contains(destination) {
return false
}
destinations.insert(destination)
return true
}
}
/// returns boolean about success
@discardableResult
open class func removeDestination(_ destination: BaseDestination) -> Bool {
return queue.sync(flags: DispatchWorkItemFlags.barrier) {
if destinations.contains(destination) == false {
return false
}
destinations.remove(destination)
return true
}
}
/// if you need to start fresh
open class func removeAllDestinations() {
queue.sync(flags: DispatchWorkItemFlags.barrier) {
destinations.removeAll()
}
}
/// returns the amount of destinations
open class func countDestinations() -> Int {
return queue.sync { destinations.count }
}
/// returns the current thread name
open class func threadName() -> String {
#if os(Linux)
// on 9/30/2016 not yet implemented in server-side Swift:
// > import Foundation
// > Thread.isMainThread
return ""
#else
if Thread.isMainThread {
return ""
} else {
let name = __dispatch_queue_get_label(nil)
return String(cString: name, encoding: .utf8) ?? Thread.current.description
}
#endif
}
// MARK: Levels
/// log something generally unimportant (lowest priority)
open class func verbose(_ message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .verbose, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .verbose, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which help during debugging (low priority)
open class func debug(_ message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .debug, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .debug, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which you are really interested but which is not an issue or error (normal priority)
open class func info(_ message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .info, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .info, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which may cause big trouble soon (high priority)
open class func warning(_ message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .warning, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .warning, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which will keep you awake at night (highest priority)
open class func error(_ message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .error, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .error, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which will keep you awake at night (highest priority)
open class func critical(_ message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .critical, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .critical, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which will keep you awake at night (highest priority)
open class func fault(_ message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .fault, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .fault, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// custom logging to manually adjust values, should just be used by other frameworks
open class func custom(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
dispatch_send(level: level, message: message(), thread: threadName(),
file: file, function: function, line: line, context: context)
#else
dispatch_send(level: level, message: message, thread: threadName(),
file: file, function: function, line: line, context: context)
#endif
}
/// internal helper which dispatches send to dedicated queue if minLevel is ok
class func dispatch_send(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
thread: String, file: String, function: String, line: Int, context: Any?) {
var resolvedMessage: String?
let destinations = queue.sync { self.destinations }
for dest in destinations {
guard let queue = dest.queue else {
continue
}
resolvedMessage = resolvedMessage == nil && dest.hasMessageFilters() ? "\(message())" : resolvedMessage
if dest.shouldLevelBeLogged(level, path: file, function: function, message: resolvedMessage) {
// try to convert msg object to String and put it on queue
let msgStr = resolvedMessage == nil ? "\(message())" : resolvedMessage!
let f = stripParams(function: function)
if dest.asynchronously {
queue.async {
_ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
}
} else {
queue.sync {
_ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
}
}
}
}
}
/// flush all destinations to make sure all logging messages have been written out
/// returns after all messages flushed or timeout seconds
/// returns: true if all messages flushed, false if timeout or error occurred
public class func flush(secondTimeout: Int64) -> Bool {
let grp = DispatchGroup()
let destinations = queue.sync { self.destinations }
for dest in destinations {
guard let queue = dest.queue else {
continue
}
grp.enter()
if dest.asynchronously {
queue.async {
dest.flush()
grp.leave()
}
} else {
queue.sync {
dest.flush()
grp.leave()
}
}
}
return grp.wait(timeout: .now() + .seconds(Int(secondTimeout))) == .success
}
/// removes the parameters from a function because it looks weird with a single param
class func stripParams(function: String) -> String {
var f = function
if let indexOfBrace = f.find("(") {
#if swift(>=4.0)
f = String(f[..<indexOfBrace])
#else
f = f.substring(to: indexOfBrace)
#endif
}
f += "()"
return f
}
}