/
MPLogSwift.swift
173 lines (153 loc) · 5.82 KB
/
MPLogSwift.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//
// MPLogSwift.swift
// MPLogSwift
//
// Created by Michael Peternell on 06.04.2019.
// Copyright © 2019 Michael Peternell. All rights reserved.
//
import Foundation
fileprivate class MPThreadIdAtom: NSObject {
let number: Int
private init(number: Int) {
self.number = number
}
var letter: String {
if number == 100 {
return "+"
}
return String(UnicodeScalar(UInt8(97 + number)))
}
private static var UnavailableAtoms = Set<Int>()
private static var CurrentNumber = 0
private static let Lock = NSLock()
private static var OverflowAtom = MPThreadIdAtom(number: 100)
static func newId() -> MPThreadIdAtom {
Lock.lock()
defer { Lock.unlock() }
for i in 0..<26 {
let theNumber = (CurrentNumber + i) % 26
if !UnavailableAtoms.contains(theNumber) {
let atom = MPThreadIdAtom(number: theNumber)
UnavailableAtoms.insert(theNumber)
CurrentNumber = (theNumber + 1) % 26
return atom
}
}
return OverflowAtom
}
deinit {
MPThreadIdAtom.Lock.lock()
MPThreadIdAtom.UnavailableAtoms.remove(number)
MPThreadIdAtom.Lock.unlock()
}
}
public final class MPLogger {
public struct Config {
var verboseEnabled = false
var debugEnabled = true
var infoEnabled = true
var warningEnabled = true
var errorEnabled = true
}
public var config = Config()
static func nullLogger() -> MPLogger {
let logger = MPLogger()
logger.config.verboseEnabled = false
logger.config.debugEnabled = false
logger.config.infoEnabled = false
logger.config.warningEnabled = false
logger.config.errorEnabled = false
return logger
}
static func warningsAndErrorLogger() -> MPLogger {
let logger = nullLogger()
logger.config.warningEnabled = true
logger.config.errorEnabled = true
return logger
}
static func debugLogger() -> MPLogger {
return MPLogger()
}
private static let dateFormatter: DateFormatter = {
var df = DateFormatter()
df.timeZone = TimeZone.current
df.locale = Locale(identifier: "en_US_POSIX")
df.dateFormat = "HH:mm:ss.SSS"
return df
}()
private static let threadIdKey = "MPLog_ThreadId"
/// returns the current "thread ID".
/// This is defined as:
/// 'M' if the current thread is the main thread.
/// The letters 'a' to 'z' for background threads.
/// If we run out of lowercase letters, the letters are recycled. If there are
/// more than 26 background threads that do logging simultanuously, some
/// threads will get the special thread ID '+'.
public static func getThreadId() -> String {
let t = Thread.current
if t.isMainThread {
return "M"
}
if let atom = t.threadDictionary[threadIdKey] as? MPThreadIdAtom {
return atom.letter
}
let atom = MPThreadIdAtom.newId()
t.threadDictionary[threadIdKey] = atom
return atom.letter
}
private func log(message: String, logType: String, filename: String, lineNumber: Int) {
let now = Date()
let dateString = MPLogger.dateFormatter.string(from: now)
let threadId = MPLogger.getThreadId()
let basename = (filename as NSString).lastPathComponent
let fullString = "\(dateString) \(threadId) \(logType) \(basename)(\(lineNumber)): \(message)"
print(fullString)
}
public func verbose(_ message: String, filename: String = #file, linenumber: Int = #line) {
if config.verboseEnabled {
log(message: message, logType: "TRACE", filename: filename, lineNumber: linenumber)
}
}
public func debug(_ message: String, filename: String = #file, linenumber: Int = #line) {
if config.debugEnabled {
log(message: message, logType: "DEBUG", filename: filename, lineNumber: linenumber)
}
}
public func debugFunctionStart(_ myself: Any? = nil, functionName: String = #function, filename: String = #file, linenumber: Int = #line) {
if config.debugEnabled {
let message: String
if let myself = myself {
message = "Start \(functionName) (\(myself))"
} else {
message = "Start \(functionName)"
}
log(message: message, logType: "DEBUG", filename: filename, lineNumber: linenumber)
}
}
public func info(_ message: String, filename: String = #file, linenumber: Int = #line) {
if config.infoEnabled {
log(message: message, logType: "INFO ", filename: filename, lineNumber: linenumber)
}
}
public func infoFunctionStart(_ myself: Any? = nil, functionName: String = #function, filename: String = #file, linenumber: Int = #line) {
if config.infoEnabled {
let message: String
if let myself = myself {
message = "Start \(functionName) (\(myself))"
} else {
message = "Start \(functionName)"
}
log(message: message, logType: "INFO ", filename: filename, lineNumber: linenumber)
}
}
public func warning(_ message: String, filename: String = #file, linenumber: Int = #line) {
if config.warningEnabled {
log(message: message, logType: "WARN ", filename: filename, lineNumber: linenumber)
}
}
public func error(_ message: String, filename: String = #file, linenumber: Int = #line) {
if config.errorEnabled {
log(message: message, logType: "ERROR", filename: filename, lineNumber: linenumber)
}
}
}