杨锴
2024-08-14 909e20941e45f8712c012db602034b47da0bfdb0
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
174
175
176
177
178
179
180
181
//
//  BaseDestination.swift
//  XCGLogger: https://github.com/DaveWoodCom/XCGLogger
//
//  Created by Dave Wood on 2014-06-06.
//  Copyright © 2014 Dave Wood, Cerebral Gardens.
//  Some rights reserved: https://github.com/DaveWoodCom/XCGLogger/blob/main/LICENSE.txt
//
 
import Foundation
 
// MARK: - BaseDestination
/// A base class destination that doesn't actually output the log anywhere and is intended to be subclassed
open class BaseDestination: DestinationProtocol, CustomDebugStringConvertible {
    // MARK: - Properties
    /// Logger that owns the destination object
    open var owner: XCGLogger?
 
    /// Identifier for the destination (should be unique)
    open var identifier: String
 
    /// Log level for this destination
    open var outputLevel: XCGLogger.Level = .debug
 
    /// Flag whether or not we've logged the app details to this destination
    open var haveLoggedAppDetails: Bool = false
 
    /// Array of log formatters to apply to messages before they're output
    open var formatters: [LogFormatterProtocol]? = nil
 
    /// Array of log filters to apply to messages before they're output
    open var filters: [FilterProtocol]? = nil
 
    /// Option: whether or not to output the log identifier
    open var showLogIdentifier: Bool = false
 
    /// Option: whether or not to output the function name that generated the log
    open var showFunctionName: Bool = true
 
    /// Option: whether or not to output the thread's name the log was created on
    open var showThreadName: Bool = false
 
    /// Option: whether or not to output the fileName that generated the log
    open var showFileName: Bool = true
 
    /// Option: whether or not to output the line number where the log was generated
    open var showLineNumber: Bool = true
 
    /// Option: whether or not to output the log level of the log
    open var showLevel: Bool = true
 
    /// Option: whether or not to output the date the log was created
    open var showDate: Bool = true
 
    /// Option: override descriptions of log levels
    open var levelDescriptions: [XCGLogger.Level: String] = [:]
 
    // MARK: - CustomDebugStringConvertible
    open var debugDescription: String {
        get {
            return "\(extractTypeName(self)): \(identifier) - Level: \(outputLevel) showLogIdentifier: \(showLogIdentifier) showFunctionName: \(showFunctionName) showThreadName: \(showThreadName) showLevel: \(showLevel) showFileName: \(showFileName) showLineNumber: \(showLineNumber) showDate: \(showDate)"
        }
    }
 
    // MARK: - Life Cycle
    public init(owner: XCGLogger? = nil, identifier: String = "") {
        self.owner = owner
        self.identifier = identifier
    }
 
    // MARK: - Methods to Process Log Details
    /// Process the log details.
    ///
    /// - Parameters:
    ///     - logDetails:   Structure with all of the details for the log to process.
    ///
    /// - Returns:  Nothing
    ///
    open func process(logDetails: LogDetails) {
        guard let owner = owner else { return }
 
        var extendedDetails: String = ""
 
        if showDate {
            extendedDetails += "\((owner.dateFormatter != nil) ? owner.dateFormatter!.string(from: logDetails.date) : logDetails.date.description) "
        }
 
        if showLevel {
            extendedDetails += "[\(levelDescriptions[logDetails.level] ?? owner.levelDescriptions[logDetails.level] ?? logDetails.level.description)] "
        }
 
        if showLogIdentifier {
            extendedDetails += "[\(owner.identifier)] "
        }
 
        if showThreadName {
            if Thread.isMainThread {
                extendedDetails += "[main] "
            }
            else {
                if let threadName = Thread.current.name, !threadName.isEmpty {
                    extendedDetails += "[\(threadName)] "
                }
                else if let queueName = DispatchQueue.currentQueueLabel, !queueName.isEmpty {
                    extendedDetails += "[\(queueName)] "
                }
                else {
                    extendedDetails += String(format: "[%p] ", Thread.current)
                }
            }
        }
 
        if showFileName {
            extendedDetails += "[\((logDetails.fileName as NSString).lastPathComponent)\((showLineNumber ? ":" + String(logDetails.lineNumber) : ""))] "
        }
        else if showLineNumber {
            extendedDetails += "[\(logDetails.lineNumber)] "
        }
 
        if showFunctionName {
            extendedDetails += "\(logDetails.functionName) "
        }
 
        output(logDetails: logDetails, message: "\(extendedDetails)> \(logDetails.message)")
    }
 
    /// Process the log details (internal use, same as process(logDetails:) but omits function/file/line info).
    ///
    /// - Parameters:
    ///     - logDetails:   Structure with all of the details for the log to process.
    ///
    /// - Returns:  Nothing
    ///
    open func processInternal(logDetails: LogDetails) {
        guard let owner = owner else { return }
 
        var extendedDetails: String = ""
 
        if showDate {
            extendedDetails += "\((owner.dateFormatter != nil) ? owner.dateFormatter!.string(from: logDetails.date) : logDetails.date.description) "
        }
 
        if showLevel {
            extendedDetails += "[\(logDetails.level)] "
        }
 
        if showLogIdentifier {
            extendedDetails += "[\(owner.identifier)] "
        }
 
        output(logDetails: logDetails, message: "\(extendedDetails)> \(logDetails.message)")
    }
 
    // MARK: - Misc methods
    /// Check if the destination's log level is equal to or lower than the specified level.
    ///
    /// - Parameters:
    ///     - level: The log level to check.
    ///
    /// - Returns:
    ///     - true:     Log destination is at the log level specified or lower.
    ///     - false:    Log destination is at a higher log level.
    ///
    open func isEnabledFor(level: XCGLogger.Level) -> Bool {
        return level >= self.outputLevel
    }
 
    // MARK: - Methods that must be overridden in subclasses
    /// Output the log to the destination.
    ///
    /// - Parameters:
    ///     - logDetails:   The log details.
    ///     - message:   Formatted/processed message ready for output.
    ///
    /// - Returns:  Nothing
    ///
    open func output(logDetails: LogDetails, message: String) {
        // Do something with the text in an overridden version of this method
        precondition(false, "Must override this")
    }
}