๐Logger
๐พ ็จๅผ๏ผ replit โฌ๏ธ ้่ฆ๏ผ str.pad(), collection.columnWidths
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// 2022.01.27 * (v.1) + line, spaces, prefixDoubleSlash, log, underline, doubleline, debug
// 2022.01.28 / line() replace "-" by "โ" (longer dash),
// r line() renmaed to "_line()"
// / log() `dot`: Bool -> String?
// + line() draw line
// + topline() line on top of message
// + error() error message
// 2022.01.29 + codeBlock() `///` prefixed code block
// + table() draw a table for 2D data
// 2022.01.30 / table() + param options: TableOptions
// / table() + return value (Bool), + @discardableResult
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// #todo: โ โก โจ
// - table() + โ dot โก vertical borders โก vertical grid lines
// - codeBlock() + โก padding before code โก comments after code
// import Extensions // for String + .pad()
// โโโโโโโโโโโโโโโโ
// โ Logger โ
// โโโโโโโโโโโโโโโโ
public struct Logger {
public static var prefixDoubleSlash = false
}
// helper
extension Logger {
/// `Logger.line(3) == "-" * 3 == "---"`
static func _line (_ n: Int) -> String { return "โ" * n }
/// `Logger.spaces(3) == " " * 3 == " "`
static func spaces(_ n: Int) -> String { return " " * n }
}
extension Logger {
public struct TableOptions: OptionSet {
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
// โญ๏ธ unit sets (containing only one element)
public static let dot = TableOptions(rawValue: 1 << 0)
}
}
extension Logger {
/// `Logger.log(msg)`
public static func log(
_ message: String = "",
padding n: Int = 0,
dot: String? = nil
) {
let prefix = prefixDoubleSlash ? "// " : ""
let padding = spaces(n)
let dotted = (dot == nil) ? "" : "\(dot!) "
print(prefix + padding + dotted + message)
}
/// `Logger.error(msg)`
public static func error(
_ message: String = "",
padding n: Int = 0
) {
Logger.log(message, padding: n, dot: "โ")
}
/// `Logger.line(n)`
public static func line(_ n: Int, padding p: Int = 0) {
log(Logger._line(n), padding: p)
}
/// `Logger.box(title)`
public static func box(_ title: String, padding: Int = 4) {
let w = title.count + padding * 2
let line = "โ" * w
let spaces = " " * padding
Logger.log("โ" + line + "โ")
Logger.log("โ" + spaces + title + spaces + "โ")
Logger.log("โ" + line + "โ")
}
/// `Logger._drawline(msg)`
static func _drawline(
_ message : String,
length n: Int? = nil, // line length
padding p: Int = 0, // padding on both sides of message
topline : Bool = false, // draw top line
bottomline: Bool = true // draw bottom line
) {
let defaultLength = message.count + p * 2
let line = Logger._line(n ?? defaultLength)
let padding = spaces(p)
if topline { log(line) }
log(padding + message + padding)
if bottomline { log(line) }
}
/// `Logger.underline(msg)`
public static func underline(_ message: String, length n: Int? = nil, padding p: Int = 0) {
_drawline(message, length: n, padding: p)
}
/// `Logger.doubleline(msg)`
public static func doubleline(
_ message: String,
length n: Int? = nil, // line length
padding p: Int = 4, // padding on both sides of message
newline : Bool = true // newline before message
) {
if newline { log() } // new line
_drawline(message, length: n, padding: p, topline: true)
}
/// `Logger.topline(msg)`
public static func topline(
_ message: String,
length n: Int? = nil, // line length
padding p: Int = 0 // padding on both sides of message
) {
_drawline(message, length: n, padding: p, topline: true, bottomline: false)
}
/// `Logger.debug(msg)`
public static func debug(
_ message: String = "",
file : String = #file, // โญ file name
line : Int = #line, // โญ line number
function : String = #function, // โญ function name
note : String? = nil // additional info
) {
let msgStr = "๐ \(message)"
let funcStr = "func: '\(function)'"
let lineStr = "line: \(line)"
let fileStr = "file: '\(file)'"
let debugInfo = " [\(funcStr), \(lineStr), \(fileStr)]"
log()
log(msgStr)
log(debugInfo)
if let note = note { log(note) }
}
/// print a `///` + ```-delimited code block to console.
/// #todo: + 1. padding before code, 2. comments after code
public static func codeBlock(_ code: [String]) {
print("/// ```")
code.forEach { print("/// " + $0) }
print("/// ```")
}
/// log a table of data
@discardableResult
public static func table(
_ data: [[String]],
padding p: Int = 2,
alignments: [Int:String.PadAlign] = [:],
options: TableOptions = []
) -> Bool {
// ๐ Collection+
guard let w = data.columnWidths else {
return false // Logger.table() failed
}
// all widths + spacing
var lineLength = w.reduce(0, +) + (w.count) * 2 * p
if options.contains(.dot) { lineLength += 1 + p } // + (padding) + (dot) on left-side of 1st cell
let hr = "โ" * lineLength // horizontal line
let padding = " " * p
// table top border
Logger.log(hr)
// for each row in the data
for i in data.indices {
var line = ""
let row = data[i]
// dot before each line
let dotted = options.contains(.dot)
? padding + (i == 0 ? " " : "๐ธ") // header without dot
: ""
// for each string in the row
for j in row.indices {
let str = row[j] // string in cell
let width = w[j] // cell width
let align = i == 0 ? .center : alignments[j+1] ?? .left // header is always centered
// ๐ String+
let cell = str.pad(width: width, align: align) // padded cell
// padding on both sides of each cell
line += padding + cell + padding
}
line = dotted + line
Logger.log(line)
if i == 0 { Logger.log(hr) } // header line
}
// table bottom border
Logger.log(hr)
return true // Logger.table() successful
}
}
๐พ ็จๅผ๏ผ replit
func testLogger() {
Logger.log("log message prefixed with a `dot`.", dot: "๐ธ")
Logger.debug("`debug` info from Logger.")
// topline, underline, doubleline
Logger.topline("`topline`")
Logger.doubleline("`doubleline` without a newline before it.", newline: false)
Logger.doubleline("`doubleline` (defalult: newline before it)")
// box
Logger.box("`box`")
// table
Logger.box("(dotted) table")
Logger.table(MockData.persons,
alignments: [2: .right, 3: .center],
options: [.dot]
)
let data = [["a"],["b","c"]]
// failed Logger.table()
let successful = Logger.table(data)
if !successful {
Logger.debug("Rows have different lengths or empty data.")
Logger.log("data = \(data)", dot: "๐ธ")
Logger.log("lengths of rows = \(data.lengthsOfElements)", dot: "๐ธ")
}
}
Result:
// ๐ธ log message prefixed with a `dot`.
//
// ๐ `debug` info from Logger.
// [func: 'testLogger()', line: 4, file: 'test/testLogger.swift']
// โโโโโโโโโ
// `topline`
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// `doubleline` without a newline before it.
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
//
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// `doubleline` (defalult: newline before it)
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// โโโโโโโโโโโโโโโ
// โ `box` โ
// โโโโโโโโโโโโโโโ
// โโโโโโโโโโโโโโโโโโโโโโโโ
// โ (dotted) table โ
// โโโโโโโโโโโโโโโโโโโโโโโโ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Name Age Type
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ๐ธ Joe 8 Boy
// ๐ธ May 24 Young Lady
// ๐ธ Peter 123 Old Man
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
//
// ๐ Rows have different lengths or empty data.
// [func: 'testLogger()', line: 25, file: 'test/testLogger.swift']
// ๐ธ data = [["a"], ["b", "c"]]
// ๐ธ lengths of rows = [1, 2]
Swift Ref โฉ Expressions โฉ Primary Expressions โฉ Literal Expression
Macros in Swift? - custom log function using #file, #line, #function โญ๏ธ
used by HasMirrors.
.logType() - inspect a view's type.
Logger.TableOptions is an OptionSet.
History
(2022.01.25) * (v.1) as a global function.
(2022.01.28) + topline(), error()
๐พ ็จๅผ๏ผ replit
// ----------------------
// โญ log function
// ----------------------
func log(
_ message: String = "",
file : String = #file, // โญ file name
line : Int = #line, // โญ line number
function : String = #function, // โญ function name
note : String? = nil // additional info
) {
let noteString = (note == nil) ? "" : "\n \(note!)"
print("\n๐ \(message)\n ( func: '\(function)', line: \(line), file: '\(file)' )\(noteString)")
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// 2022.01.27 * (v.1) + line, spaces, prefixDoubleSlash, log, underline, doubleline, debug
// 2022.01.28 - line: replace "-" by "โ" (longer dash), renamed "_line"
// / log(dot:): `dot`: Bool -> String?
// + line (draw line), topline (line on top of message)
// + error(msg)`
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// โโโโโโโโโโโโโโโโโโโโโโโโโ
// โ Character * Int โ
// โโโโโโโโโโโโโโโโโโโโโโโโโ
/// `"-" * 3 == "---"`
func * (a: Character, n: Int) -> String {
return String(repeating: a, count: n)
}
// โโโโโโโโโโโโโโโโ
// โ Logger โ
// โโโโโโโโโโโโโโโโ
public struct Logger {
public static var prefixDoubleSlash = false
}
// helper
extension Logger {
/// `Logger.line(3) == "-" * 3 == "---"`
static func _line (_ n: Int) -> String { return "โ" * n }
/// `Logger.spaces(3) == " " * 3 == " "`
static func spaces(_ n: Int) -> String { return " " * n }
}
extension Logger {
/// `Logger.log(msg)`
public static func log(
_ message: String = "",
padding n: Int = 0,
dot: String? = nil
) {
let prefix = prefixDoubleSlash ? "// " : ""
let padding = spaces(n)
let dotted = (dot == nil) ? "" : "\(dot!) "
print(prefix + padding + dotted + message)
}
/// `Logger.error(msg)`
public static func error(
_ message: String = "",
padding n: Int = 0
) {
Logger.log(message, padding: n, dot: "โ")
}
/// `Logger.line(n)`
public static func line(_ n: Int, padding p: Int = 0) {
log(Logger._line(n), padding: p)
}
/// `Logger.box(title)`
public static func box(_ title: String, padding: Int = 4) {
let w = title.count + padding * 2
let line = "โ" * w
let spaces = " " * padding
Logger.log("โ" + line + "โ")
Logger.log("โ" + spaces + title + spaces + "โ")
Logger.log("โ" + line + "โ")
}
/// `Logger._drawline(msg)`
static func _drawline(
_ message : String,
length n: Int? = nil, // line length
padding p: Int = 0, // padding on both sides of message
topline : Bool = false, // draw top line
bottomline: Bool = true // draw bottom line
) {
let defaultLength = message.count + p * 2
let line = Logger._line(n ?? defaultLength)
let padding = spaces(p)
if topline { log(line) }
log(padding + message + padding)
if bottomline { log(line) }
}
/// `Logger.underline(msg)`
public static func underline(_ message: String, length n: Int? = nil, padding p: Int = 0) {
_drawline(message, length: n, padding: p)
}
/// `Logger.doubleline(msg)`
public static func doubleline(
_ message: String,
length n: Int? = nil, // line length
padding p: Int = 4, // padding on both sides of message
newline : Bool = true // newline before message
) {
if newline { log() } // new line
_drawline(message, length: n, padding: p, topline: true)
}
/// `Logger.topline(msg)`
public static func topline(
_ message: String,
length n: Int? = nil, // line length
padding p: Int = 0 // padding on both sides of message
) {
_drawline(message, length: n, padding: p, topline: true, bottomline: false)
}
/// `Logger.debug(msg)`
public static func debug(
_ message: String = "",
file : String = #file, // โญ file name
line : Int = #line, // โญ line number
function : String = #function, // โญ function name
note : String? = nil // additional info
) {
let msgStr = "๐ \(message)"
let funcStr = "func: '\(function)'"
let lineStr = "line: \(line)"
let fileStr = "file: '\(file)'"
let debugInfo = " [\(funcStr), \(lineStr), \(fileStr)]"
log()
log(msgStr)
log(debugInfo)
if let note = note { log(note) }
}
}
Last updated