Skip to content

Commit

Permalink
Added LogData
Browse files Browse the repository at this point in the history
  • Loading branch information
ikhvorost committed Feb 3, 2025
1 parent 99e178f commit b556d54
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 71 deletions.
4 changes: 2 additions & 2 deletions Sources/DLog/Log.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,12 @@ public class Log: NSObject {
/// - Returns: An `LogScope` object for the new scope.
///
@discardableResult
public func scope(_ name: String, metadata: Metadata? = nil, fileID: String = #fileID, file: String = #file, function: String = #function, line: UInt = #line, closure: ((LogScope) -> Void)? = nil) -> LogScope? {
public func scope(_ name: String, fileID: String = #fileID, file: String = #file, function: String = #function, line: UInt = #line, closure: ((LogScope) -> Void)? = nil) -> LogScope? {
guard logger.output != nil else {
return nil
}
let location = LogLocation(fileID: fileID, file: file, function: function, line: line)
let scope = LogScope(name: name, logger: logger, category: category, config: config, metadata: metadata, location: location)
let scope = LogScope(name: name, logger: logger, category: category, config: config, metadata: metadata.data, location: location)
if let closure {
scope.enter(fileID: fileID, file: file, function: function, line: line)
closure(scope)
Expand Down
70 changes: 70 additions & 0 deletions Sources/DLog/LogData.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// LogData.swift
//
// Created by Iurii Khvorost <[email protected]> on 2025/01/04.
// Copyright © 2025 Iurii Khvorost. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Foundation

typealias LogData = [String : Any]

extension LogData {

static func data<Option: OptionSet>(from items: [(Option, String, Any)], options: Option) -> LogData {
let keyValues: [(String, Any)] = items
.compactMap { (option: Option, key: String, value: Any) in
// Option
assert(option is Option.Element)
guard options.contains(option as! Option.Element) else {
return nil
}

// Key
assert(key.isEmpty == false)

// Value
if let text = value as? String, text.isEmpty {
return nil
}
else if let dict = value as? LogData, dict.isEmpty {
return nil
}

return (key, value)
}
return Dictionary(uniqueKeysWithValues: keyValues)
}
}

extension Dictionary {

func json(pretty: Bool = false) -> String {
guard count > 0 else {
return ""
}
let options: JSONSerialization.WritingOptions = pretty ? [.sortedKeys, .prettyPrinted] : [.sortedKeys]
guard self.isEmpty == false,
let data = try? JSONSerialization.data(withJSONObject: self, options: options),
let json = String(data: data, encoding: .utf8) else {
return ""
}
return json.replacingOccurrences(of: "\"", with: "")
}
}
4 changes: 2 additions & 2 deletions Sources/DLog/LogInterval.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public class LogInterval {
return text.replacingOccurrences(of: "[INTERVAL]", with: "[INTERVAL:\(message)]")
}

override func data() -> Metadata? {
override func data() -> LogData? {
let items: [(IntervalOptions, String, Any)] = [
(.duration, "duration", stringFromTimeInterval(duration)),
(.count, "count", stats.count),
Expand All @@ -144,7 +144,7 @@ public class LogInterval {
(.max, "max", stringFromTimeInterval(stats.max)),
(.average, "average", stringFromTimeInterval(stats.average)),
]
return Metadata.metadata(from: items, options: config.intervalConfig.options)
return LogData.data(from: items, options: config.intervalConfig.options)
}

override func messageText() -> String {
Expand Down
12 changes: 6 additions & 6 deletions Sources/DLog/LogItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ extension Log {
}
}

func data() -> Metadata? {
func data() -> LogData? {
nil
}

Expand All @@ -274,11 +274,11 @@ extension Log {

public var description: String {
var sign = "\(config.sign)"
var time = Log.Item.dateFormatter.string(from: self.time)
var level = String(format: "[%02d]", self.stack?.count ?? 0)
var category = "[\(self.category)]"
var location = "<\(self.location.fileName):\(self.location.line)>"
var metadata = self.metadata.json()
var time = Log.Item.dateFormatter.string(from: time)
var level = String(format: "[%02d]", stack?.count ?? 0)
var category = "[\(category)]"
var location = "<\(location.fileName):\(location.line)>"
var metadata = metadata.json()
var data = data()?.json() ?? ""

switch config.style {
Expand Down
51 changes: 5 additions & 46 deletions Sources/DLog/LogMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,16 @@

import Foundation

/// Metadata dictionary type
// TODO: LogData
public typealias Metadata = [String : Any]

extension Metadata {

static func metadata<Option: OptionSet>(from items: [(Option, String, Any)], options: Option) -> Metadata {
let keyValues: [(String, Any)] = items
.compactMap { (option: Option, key: String, value: Any) in
// Option
assert(option is Option.Element)
guard options.contains(option as! Option.Element) else {
return nil
}

// Key
assert(key.isEmpty == false)

// Value
if let text = value as? String, text.isEmpty {
return nil
}
else if let dict = value as? Metadata, dict.isEmpty {
return nil
}

return (key, value)
}
return Dictionary(uniqueKeysWithValues: keyValues)
}

func json(pretty: Bool = false) -> String {
guard count > 0 else {
return ""
}
let options: JSONSerialization.WritingOptions = pretty ? [.sortedKeys, .prettyPrinted] : [.sortedKeys]
guard self.isEmpty == false,
let data = try? JSONSerialization.data(withJSONObject: self, options: options),
let json = String(data: data, encoding: .utf8) else {
return ""
}
return json.replacingOccurrences(of: "\"", with: "")
}
}
public typealias Metadata = [String : Codable]


/// Contextual metadata
@objcMembers
public class LogMetadata: NSObject {
private var _data = Metadata()
private var _data: Metadata

init(data: Metadata = Metadata()) {
init(data: Metadata) {
_data = data
}

Expand All @@ -82,7 +41,7 @@ public class LogMetadata: NSObject {
}

/// Gets and sets a value by a string key to metadata.
public subscript(name: String) -> Any? {
public subscript(name: String) -> Codable? {
get {
synchronized(self) { _data[name] }
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/DLog/LogScope.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public class LogScope: Log {
return text.replacingOccurrences(of: "[SCOPE]", with: "[SCOPE:\(message)]")
}

override func data() -> Metadata? {
override func data() -> LogData? {
duration > 0 ? ["duration": stringFromTimeInterval(duration)] : nil
}

Expand All @@ -102,10 +102,10 @@ public class LogScope: Log {
}
}

init(name: String, logger: DLog, category: String, config: LogConfig, metadata: Metadata?, location: LogLocation) {
init(name: String, logger: DLog, category: String, config: LogConfig, metadata: Metadata, location: LogLocation) {
self.name = name
self.location = location
super.init(logger: logger, category: category, config: config, metadata: metadata ?? logger.metadata.data)
super.init(logger: logger, category: category, config: config, metadata: metadata)
}

private func item(type: LogType, stack: [Bool]) -> Item {
Expand Down
4 changes: 2 additions & 2 deletions Sources/DLog/LogTrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ public class LogTrace: Log.Item {
super.init(time: Date(), category: category, stack: stack, type: .trace, location: location, metadata: metadata, message: message, config: config)
}

override func data() -> Metadata? {
override func data() -> LogData? {
let items: [(TraceOptions, String, Any)] = [
(.function, "func", funcInfo(function: location.function, config: config.traceConfig.funcConfig)),
(.process, "process", processMetadata(processInfo: traceInfo.processInfo, config: config.traceConfig.processConfig)),
(.queue, "queue", traceInfo.queueLabel),
(.stack, "stack", stackMetadata(moduleName: location.moduleName, stackAddresses: traceInfo.stackAddresses, config: config.traceConfig.stackConfig)),
(.thread, "thread", threadMetadata(thread: traceInfo.thread, tid: traceInfo.tid, config: config.traceConfig.threadConfig)),
]
return Metadata.metadata(from: items, options: config.traceConfig.options)
return LogData.data(from: items, options: config.traceConfig.options)
}
}
8 changes: 4 additions & 4 deletions Sources/DLog/TraceProcess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,17 @@ public struct ProcessConfig {
public var wakeupsOptions: WakeupsOptions = .all
}

func wakeupsMetadata(options: WakeupsOptions) -> Metadata {
func wakeupsMetadata(options: WakeupsOptions) -> LogData {
let power = TaskInfo.power
let items: [(WakeupsOptions, String, Any)] = [
(.interrupt, "interrupt", power.task_interrupt_wakeups),
(.idle, "idle", power.task_platform_idle_wakeups),
(.timer, "timer", power.task_timer_wakeups_bin_1 + power.task_timer_wakeups_bin_2)
]
return Metadata.metadata(from: items, options: options)
return LogData.data(from: items, options: options)
}

func processMetadata(processInfo: ProcessInfo, config: ProcessConfig) -> Metadata {
func processMetadata(processInfo: ProcessInfo, config: ProcessConfig) -> LogData {
let items: [(ProcessOptions, String, Any)] = [
(.cpu, "cpu", "\(threadsInfo().cpuUsage)%"),
(.guid, "guid", processInfo.globallyUniqueString),
Expand All @@ -111,5 +111,5 @@ func processMetadata(processInfo: ProcessInfo, config: ProcessConfig) -> Metadat
(.threads, "threads", threadsInfo().threadsCount),
(.wakeups, "wakeups", wakeupsMetadata(options: config.wakeupsOptions)),
]
return Metadata.metadata(from: items, options: config.options)
return LogData.data(from: items, options: config.options)
}
4 changes: 2 additions & 2 deletions Sources/DLog/TraceStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ fileprivate func swift_demangle(_ mangled: String) -> String? {
return String(cString: cString)
}

func stackMetadata(moduleName: String, stackAddresses: ArraySlice<NSNumber>, config: StackConfig) -> [Metadata] {
func stackMetadata(moduleName: String, stackAddresses: ArraySlice<NSNumber>, config: StackConfig) -> [LogData] {
stackAddresses
.compactMap { item -> (address: UInt, module: String, offset: UInt, symbol: String)? in
let address = item.uintValue
Expand Down Expand Up @@ -117,6 +117,6 @@ func stackMetadata(moduleName: String, stackAddresses: ArraySlice<NSNumber>, con
(.offset, "offset", item.element.offset),
(.symbol, "symbol", swift_demangle(item.element.symbol) ?? item.element.symbol),
]
return Metadata.metadata(from: items, options: config.options)
return LogData.data(from: items, options: config.options)
}
}
4 changes: 2 additions & 2 deletions Sources/DLog/TraceThread.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public struct ThreadConfig {
public var options: ThreadOptions = .compact
}

func threadMetadata(thread: Thread, tid: UInt64, config: ThreadConfig) -> Metadata {
func threadMetadata(thread: Thread, tid: UInt64, config: ThreadConfig) -> LogData {
let items: [(ThreadOptions, String, Any)] = [
(.name, "name", thread.info.name),
(.number, "number", thread.info.number),
Expand All @@ -111,5 +111,5 @@ func threadMetadata(thread: Thread, tid: UInt64, config: ThreadConfig) -> Metada
(.stackSize, "stackSize", "\(ByteCountFormatter.string(fromByteCount: Int64(thread.stackSize), countStyle: .memory))"),
(.tid, "tid", tid),
]
return Metadata.metadata(from: items, options: config.options)
return LogData.data(from: items, options: config.options)
}
4 changes: 2 additions & 2 deletions Tests/DLogTests/DLogTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ let Empty = ">$"
final class DLogTests: XCTestCase {

func test_trace() {
let log = DLog(metadata: ["a": 100])
let item = log.trace()
let log = DLog()
let item = log.trace("trace")
XCTAssert(item?.traceInfo.queueLabel == "com.apple.main-thread")
}

Expand Down

0 comments on commit b556d54

Please sign in to comment.