-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSTLogRecord.swift
More file actions
118 lines (105 loc) · 3.73 KB
/
STLogRecord.swift
File metadata and controls
118 lines (105 loc) · 3.73 KB
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
//
// STLogRecord.swift
// STBaseProject
//
// Created by 寒江孤影 on 2018/10/10.
//
import Foundation
/// Layout mode for `STLogRecord.formatted(layout:)`.
public enum STLogFormatLayout: Equatable {
/// One field per line.
case multiline
/// All fields joined with " | ".
case singleLine
}
public struct STLogRecord: Codable, Identifiable, Sendable {
public typealias Metadata = [String: String]
public let id: String
public let timestamp: Date
public let level: STLogLevel
public let label: String
public let message: String
public let file: String
public let function: String
public let line: Int
public let metadata: Metadata
public let thread: String
public let persistent: Bool
public init(
id: String = UUID().uuidString,
timestamp: Date = Date(),
level: STLogLevel,
label: String,
message: String,
file: String,
function: String,
line: Int,
metadata: Metadata = [:],
thread: String = STLogRecord.threadDescription(),
persistent: Bool
) {
self.id = id
self.timestamp = timestamp
self.level = level
self.label = label
self.message = message
self.file = file
self.function = function
self.line = line
self.metadata = metadata
self.thread = thread
self.persistent = persistent
}
public var fileName: String {
(file as NSString).lastPathComponent
}
public var searchableText: String {
let values = self.metadata.sorted { $0.key < $1.key }.map { "\($0.key)=\($0.value)" }.joined(separator: " ")
return [self.label, self.message, self.fileName, self.function, self.thread, values].joined(separator: " ").lowercased()
}
public func formatted(layout: STLogFormatLayout = .multiline) -> String {
let metadataText = self.metadata
.sorted { $0.key < $1.key }
.map { "\($0.key)=\($0.value)" }
.joined(separator: ", ")
let lines = [
"\n\(self.timestamp.formatted("yyyy-MM-dd HH:mm:ss.SSS")) \(fileName)",
"label: \(self.label)",
"funcName: \(self.function)",
"lineNum: (\(self.line))",
"thread: \(self.thread)",
"level: \(self.level.rawValue)",
metadataText.isEmpty ? nil : "metadata: \(metadataText)",
"message: \(self.message)"
].compactMap { $0 }
return layout == .multiline ? lines.joined(separator: "\n") : lines.joined(separator: " | ")
}
func jsonLine(using encoder: JSONEncoder) -> String? {
let data: Data
do {
data = try encoder.encode(self)
} catch {
STLog("[STLogRecord] 日志编码失败: \(error.localizedDescription)", level: .warning)
return nil
}
guard var line = String(data: data, encoding: .utf8) else {
return nil
}
if !line.hasSuffix("\n") {
line.append("\n")
}
return line
}
static func decode(from line: String, using decoder: JSONDecoder) -> STLogRecord? {
guard let data = line.data(using: .utf8) else { return nil }
return try? decoder.decode(STLogRecord.self, from: data)
}
public static func threadDescription() -> String {
if Thread.isMainThread { return "main" }
// Dispatch queue label(GCD 匿名线程可读名称,如 com.stbase.log.manager)
let queueLabel = String(cString: __dispatch_queue_get_label(nil))
if !queueLabel.isEmpty { return queueLabel }
if let name = Thread.current.name, !name.isEmpty { return name }
return "\(Unmanaged.passUnretained(Thread.current).toOpaque())"
}
}