From c8714a639c702d2eed88c0d354001ca42ddd48d9 Mon Sep 17 00:00:00 2001 From: Tony Stone Date: Wed, 6 Apr 2022 18:16:22 -0700 Subject: [PATCH 1/9] Adding multiple libraries for ObjC. --- Package.swift | 43 +++++++++++++++---- .../include/{ => TraceLog}/TraceLog.h | 0 Tests/TraceLogObjCTests/TraceLogObjCTests.m | 2 +- 3 files changed, 36 insertions(+), 9 deletions(-) rename Sources/TraceLogObjC/include/{ => TraceLog}/TraceLog.h (100%) diff --git a/Package.swift b/Package.swift index 68161572..3069a46a 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,5 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. /// /// Package.swift /// @@ -22,17 +23,43 @@ import PackageDescription let package = Package( name: "TraceLog", + platforms: [.iOS(.v9), .macOS(.v10_13), .tvOS(.v9), .watchOS(.v2)], + products: [ + .library( + name: "TraceLog", + type: .dynamic, + targets: ["TraceLog"] + ), + .library( + name: "TraceLogObjC", + targets: ["TraceLogObjC"] + )], targets: [ /// Module targets - .target(name: "TraceLog", dependencies: [], path: "Sources/TraceLog"), - .target(name: "TraceLogObjC", dependencies: ["TraceLog"], path: "Sources/TraceLogObjC"), + .target(name: "TraceLog", + dependencies: [], + path: "Sources/TraceLog"), + .target(name: "TraceLogObjC", + dependencies: ["TraceLog"], + path: "Sources/TraceLogObjC", + publicHeadersPath: "include", + cSettings: [ + .headerSearchPath("../.."), + ]), /// Tests - .testTarget(name: "TraceLogTests", dependencies: ["TraceLog"], path: "Tests/TraceLogTests"), - .testTarget(name: "TraceLogObjCTests", dependencies: ["TraceLogObjC"], path: "Tests/TraceLogObjCTests") + .testTarget(name: "TraceLogTests", + dependencies: ["TraceLog"], + path: "Tests/TraceLogTests", + exclude: ["Internal/Utilities/Streams/FileOutputStreamError+PosixTests.swift.gyb", + "Internal/Utilities/Streams/OutputStreamError+PosixTests.swift.gyb", + "Writers & Formatters/Textformat+EncodingTests.swift.gyb", + "Writers & Formatters/TextFormat+InternationalLanguagesTests.swift.gyb", + "Writers & Formatters/FileStrategyManager+FailureReasonTests.swift.gyb"]), + .testTarget(name: "TraceLogObjCTests", + dependencies: ["TraceLogObjC"], + path: "Tests/TraceLogObjCTests", + exclude: []) ], swiftLanguageVersions: [.version("5")] ) - -/// Main products section -package.products.append(.library(name: "TraceLog", type: .dynamic, targets: ["TraceLog", "TraceLogObjC"])) diff --git a/Sources/TraceLogObjC/include/TraceLog.h b/Sources/TraceLogObjC/include/TraceLog/TraceLog.h similarity index 100% rename from Sources/TraceLogObjC/include/TraceLog.h rename to Sources/TraceLogObjC/include/TraceLog/TraceLog.h diff --git a/Tests/TraceLogObjCTests/TraceLogObjCTests.m b/Tests/TraceLogObjCTests/TraceLogObjCTests.m index 0c1082eb..29958522 100644 --- a/Tests/TraceLogObjCTests/TraceLogObjCTests.m +++ b/Tests/TraceLogObjCTests/TraceLogObjCTests.m @@ -20,7 +20,7 @@ */ @import XCTest; -#import "TraceLog.h" +#import @interface TraceLogObjCTests : XCTestCase @end From 3eff9e414f2cb88616ca9c5c183359ddb912217a Mon Sep 17 00:00:00 2001 From: Tony Stone Date: Wed, 6 Apr 2022 18:24:44 -0700 Subject: [PATCH 2/9] Making TraceLogObjC dynamic. --- Package.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Package.swift b/Package.swift index 3069a46a..3d8f4850 100644 --- a/Package.swift +++ b/Package.swift @@ -32,6 +32,7 @@ let package = Package( ), .library( name: "TraceLogObjC", + type: .dynamic, targets: ["TraceLogObjC"] )], targets: [ From 71e7544d3cc4a6f086b2d990e7acde4e4547c83a Mon Sep 17 00:00:00 2001 From: Tony Stone Date: Wed, 6 Apr 2022 18:29:34 -0700 Subject: [PATCH 3/9] Removing dynamic on libraries. --- Package.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Package.swift b/Package.swift index 3d8f4850..eac6c3b7 100644 --- a/Package.swift +++ b/Package.swift @@ -27,12 +27,10 @@ let package = Package( products: [ .library( name: "TraceLog", - type: .dynamic, targets: ["TraceLog"] ), .library( name: "TraceLogObjC", - type: .dynamic, targets: ["TraceLogObjC"] )], targets: [ From a4c96b3b4c529f04c7fbe24dad4785fceee2f796 Mon Sep 17 00:00:00 2001 From: Tony Stone Date: Wed, 6 Apr 2022 21:14:07 -0700 Subject: [PATCH 4/9] Reorder TraceLog header moving TLlogger to the top to avoid having to import TraceLog. --- .../TraceLogObjC/include/TraceLog/TraceLog.h | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/Sources/TraceLogObjC/include/TraceLog/TraceLog.h b/Sources/TraceLogObjC/include/TraceLog/TraceLog.h index 3440727b..70fba32a 100644 --- a/Sources/TraceLogObjC/include/TraceLog/TraceLog.h +++ b/Sources/TraceLogObjC/include/TraceLog/TraceLog.h @@ -21,6 +21,36 @@ #ifndef Pods_TraceLog_h #define Pods_TraceLog_h +#if !COCOAPODS +/// +/// Swift Package Manager requires these to be declared otherwise, the +/// TraceLog module needs to be included with this header for Objective-C +/// which is undesierable. +/// +#import + +/// Internal class exposed to objective-C for low level logging. +/// +/// - Warning: This is a private class and nothing in this class should be used on it's own. Please see TraceLog.h for the public interface to this. +/// +/// - Note: In order to continue to support Objective-C, this class must be public and also visible to both Swift and ObjC. This class is not meant to be +/// used directly in either language. +/// +@interface TLLogger: NSObject + + @property (class, nonatomic, readonly) NSInteger LogLevelError; + @property (class, nonatomic, readonly) NSInteger LogLevelWarning; + @property (class, nonatomic, readonly) NSInteger LogLevelInfo; + @property (class, nonatomic, readonly) NSInteger LogLevelTrace1; + @property (class, nonatomic, readonly) NSInteger LogLevelTrace2; + @property (class, nonatomic, readonly) NSInteger LogLevelTrace3; + @property (class, nonatomic, readonly) NSInteger LogLevelTrace4; + + + (void) logPrimitive: (NSInteger) level tag: (NSString * __nonnull) tag file: (NSString * __nonnull) file function: (NSString * __nonnull) function line: (NSUInteger) line message: (NSString * _Nonnull (^_Nonnull)(void)) messageBlock; +@end +#endif + + /// Instance level macros /** @@ -175,33 +205,5 @@ */ #define CLogTrace(level,tag,format,...) LogIfEnabled(TLLogger.LogLevelTrace1 + ((int)level) - 1, tag, format, ##__VA_ARGS__) -#if !COCOAPODS -/// -/// Swift Package Manager requires these to be declared otherwise, the -/// TraceLog module needs to be included with this header for Objective-C -/// which is undesierable. -/// -#import - -/// Internal class exposed to objective-C for low level logging. -/// -/// - Warning: This is a private class and nothing in this class should be used on it's own. Please see TraceLog.h for the public interface to this. -/// -/// - Note: In order to continue to support Objective-C, this class must be public and also visible to both Swift and ObjC. This class is not meant to be -/// used directly in either language. -/// -@interface TLLogger: NSObject - - @property (class, nonatomic, readonly) NSInteger LogLevelError; - @property (class, nonatomic, readonly) NSInteger LogLevelWarning; - @property (class, nonatomic, readonly) NSInteger LogLevelInfo; - @property (class, nonatomic, readonly) NSInteger LogLevelTrace1; - @property (class, nonatomic, readonly) NSInteger LogLevelTrace2; - @property (class, nonatomic, readonly) NSInteger LogLevelTrace3; - @property (class, nonatomic, readonly) NSInteger LogLevelTrace4; - - + (void) logPrimitive: (NSInteger) level tag: (NSString * __nonnull) tag file: (NSString * __nonnull) file function: (NSString * __nonnull) function line: (NSUInteger) line message: (NSString * _Nonnull (^_Nonnull)(void)) messageBlock; -@end -#endif #endif From a29c1ab572c5f7ae0f126d6e467feb69159f78cd Mon Sep 17 00:00:00 2001 From: Tony Stone Date: Wed, 6 Apr 2022 21:16:25 -0700 Subject: [PATCH 5/9] Removing Cocoapods check. --- Sources/TraceLogObjC/include/TraceLog/TraceLog.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Sources/TraceLogObjC/include/TraceLog/TraceLog.h b/Sources/TraceLogObjC/include/TraceLog/TraceLog.h index 70fba32a..00a15758 100644 --- a/Sources/TraceLogObjC/include/TraceLog/TraceLog.h +++ b/Sources/TraceLogObjC/include/TraceLog/TraceLog.h @@ -21,7 +21,6 @@ #ifndef Pods_TraceLog_h #define Pods_TraceLog_h -#if !COCOAPODS /// /// Swift Package Manager requires these to be declared otherwise, the /// TraceLog module needs to be included with this header for Objective-C @@ -48,8 +47,6 @@ + (void) logPrimitive: (NSInteger) level tag: (NSString * __nonnull) tag file: (NSString * __nonnull) file function: (NSString * __nonnull) function line: (NSUInteger) line message: (NSString * _Nonnull (^_Nonnull)(void)) messageBlock; @end -#endif - /// Instance level macros From 9dc3a78ad093b323e1d176b49ab87b6df375d4c8 Mon Sep 17 00:00:00 2001 From: Tony Stone Date: Fri, 8 Apr 2022 11:44:39 -0700 Subject: [PATCH 6/9] Adding UnifiedLogWriter for writing to Apple's Unified logging system. --- .../TraceLog/Writers/UnifiedLogWriter.swift | 106 ++++++++ .../TestHarness/UnifiedLogReader.swift | 128 ++++++++++ .../UnifiedLogWriterTests.swift | 229 ++++++++++++++++++ 3 files changed, 463 insertions(+) create mode 100644 Sources/TraceLog/Writers/UnifiedLogWriter.swift create mode 100644 Tests/TraceLogTests/TestHarness/UnifiedLogReader.swift create mode 100644 Tests/TraceLogTests/Writers & Formatters/UnifiedLogWriterTests.swift diff --git a/Sources/TraceLog/Writers/UnifiedLogWriter.swift b/Sources/TraceLog/Writers/UnifiedLogWriter.swift new file mode 100644 index 00000000..fe11726d --- /dev/null +++ b/Sources/TraceLog/Writers/UnifiedLogWriter.swift @@ -0,0 +1,106 @@ + +/// +/// UnifiedLogWriter.swift +/// +/// Copyright 2022 Tony Stone +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Created by Tony Stone on 4/8/22. +/// +/// +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + +import Foundation +import Swift +import os.log + +public struct Platform { + /// + /// The LogLevel type for all Platforms. + /// + public typealias LogLevel = Int32 +} + +/// +/// Apple Unified Logging System log writer for TraceLog. +/// +/// Implementation of a TraceLog `Writer` to write to Apple's Unified Logging System. +/// +/// - SeeAlso: https://developer.apple.com/documentation/os/logging +/// +@available(iOS 10.0, macOS 10.12, watchOS 3.0, tvOS 10.0, *) +public class UnifiedLogWriter: Writer { + + /// + /// The default LogLevel Conversion Table. + /// + static let defaultLogLevelConversion: [TraceLog.LogLevel: Platform.LogLevel] = [ + .error: Platform.LogLevel(OSLogType.error.rawValue), + .warning: Platform.LogLevel(OSLogType.default.rawValue), + .info: Platform.LogLevel(OSLogType.default.rawValue), + .trace1: Platform.LogLevel(OSLogType.debug.rawValue), + .trace2: Platform.LogLevel(OSLogType.debug.rawValue), + .trace3: Platform.LogLevel(OSLogType.debug.rawValue), + .trace4: Platform.LogLevel(OSLogType.debug.rawValue) + ] + + /// + /// Custom subsystem passed to os_log + /// + internal let subsystem: String + + /// + /// A dictionary keyed by TraceLog LogLevels with the value to convert to the os_log level. + /// + private let logLevelConversion: [LogLevel: Platform.LogLevel] + + /// + /// Initializes an UnifiedLoggingWriter. + /// + /// - Parameters: + /// - sybsystem: An identifier string, usually in reverse DNS notation, representing the subsystem that’s performing logging (defaults to current process name). + /// - logLevelConversion: A dictionary keyed by TraceLog LogLevels with the value to convert to the os_log level. + /// + required init(subsystem: String? = nil, logLevelConversion: [TraceLog.LogLevel: Platform.LogLevel] = defaultLogLevelConversion) { + self.subsystem = subsystem ?? ProcessInfo.processInfo.processName + self.logLevelConversion = logLevelConversion + } + + /// + /// Required log function for the `Writer`. + /// + @inline(__always) + public func write(_ entry: Writer.LogEntry) -> Result { + + let log = OSLog(subsystem: self.subsystem, category: entry.tag) + + os_log("%{public}@", log: log, type: OSLogType(UInt8(platformLogLevel(for: entry.level))), entry.message) + + return .success(entry.message.count) + } + + /// + /// Converts TraceLog level to os_log. + /// + @inline(__always) + func platformLogLevel(for level: LogLevel) -> Platform.LogLevel { + + guard let level = self.logLevelConversion[level] + else { return Platform.LogLevel(OSLogType.default.rawValue) } + + return level + } +} + +#endif diff --git a/Tests/TraceLogTests/TestHarness/UnifiedLogReader.swift b/Tests/TraceLogTests/TestHarness/UnifiedLogReader.swift new file mode 100644 index 00000000..d19e639d --- /dev/null +++ b/Tests/TraceLogTests/TestHarness/UnifiedLogReader.swift @@ -0,0 +1,128 @@ +/// +/// DarwinPlatformValidator.swift +/// +/// Copyright 2018 Tony Stone +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Created by Tony Stone on 6/16/18. +/// +import XCTest +@testable import TraceLog + +#if os(macOS) + +import os.log + + +@available(iOS 10.0, macOS 10.13, watchOS 3.0, tvOS 10.0, *) +class UnifiedLogReader: Reader { + + func logEntry(for writer: UnifiedLogWriter, timestamp: Double, level: LogLevel, tag: String, message: String, runtimeContext: RuntimeContext, staticContext: StaticContext) -> TestLogEntry? { + + let osLogType = OSLogType(rawValue: UInt8(writer.platformLogLevel(for: level))) + + /// If Unified Logging is not configured for this test, we fail early. + let log = OSLog(subsystem: writer.subsystem, category: tag) + + guard log.isEnabled(type: osLogType) + else { + + XCTFail("\n\nCannot complete log entry search.\n\n" + + "\tUnified Logging is not configured for this LogLevel.\n\n" + + "\tPlease run `sudo log config --subsystem \"\(writer.subsystem)\" --mode \"persist:debug\"` before running this test.\n") + return nil + } + + let command = "log show --predicate 'eventMessage == \"\(message)\"' --info --debug --style json" + + /// + /// Note: Unified takes an undetermined time before log entries are available to + /// the log command so we try up to 10 times to find the value before giving up. + /// + var retryTime: useconds_t = 1000 + + for _ in 0...10 { + + guard let data = try? shell(command + " --last \(retryTime / 1000)") + else { XCTFail("Could not run shell command \(command + " --last \(retryTime / 1000)")."); return nil } + + let objects: Any + do { + objects = try JSONSerialization.jsonObject(with: data) + } catch { + XCTFail("Could not parse JSON \(String(data: data, encoding: .utf8) ?? "nil"), error: \(error)."); return nil + } + + guard let logEntries = objects as? [[String: Any]] + else { + XCTFail("Incorrect json object returned from parsing log show results, expected [[String: Any]] but got \(type(of: objects))."); return nil + } + + guard logEntries.count > 0 + else { + usleep(retryTime) + retryTime = retryTime * 2 /// progressivly sleep longer for each retry. + + continue + } + + /// Find the journal entry by message string (message string should be unique based on the string + timestamp). + for jsonEntry in logEntries where jsonEntry["eventMessage"] as? String ?? "" == message { + + var customAttributes: [String: Any]? = nil + + if let subsystem = jsonEntry["subsystem"] as? String { + customAttributes = ["subsystem": subsystem] + } + + return TestLogEntry(timestamp: timestamp, + level: level, + message: message, // Note we return the input message because we know it matches because we searched by message + tag: jsonEntry["category"] as? String, + customAttributes: customAttributes) + // assertValue(for: jsonEntry, key: "messageType", eqauls: "\(osLogType.description)") + } + } + return nil + } +} + +@available(iOS 10.0, macOS 10.13, watchOS 3.0, tvOS 10.0, *) +extension OSLogType { + + public var description: String { + switch self { + case .fault: return "Fault" + case .error: return "Error" + case .debug: return "Debug" + default: return "Default" + } + } +} + + +@available(iOS 10.0, macOS 10.13, watchOS 3.0, tvOS 10.0, *) +class DarwinPlatformValidator { + + static var `default`: Platform.LogLevel { return Platform.LogLevel(OSLogType.default.rawValue) } + static var error: Platform.LogLevel { return Platform.LogLevel(OSLogType.error.rawValue) } + static var warning: Platform.LogLevel { return Platform.LogLevel(OSLogType.default.rawValue) } + static var info: Platform.LogLevel { return Platform.LogLevel(OSLogType.default.rawValue) } + static var trace1: Platform.LogLevel { return Platform.LogLevel(OSLogType.debug.rawValue) } + static var trace2: Platform.LogLevel { return Platform.LogLevel(OSLogType.debug.rawValue) } + static var trace3: Platform.LogLevel { return Platform.LogLevel(OSLogType.debug.rawValue) } + static var trace4: Platform.LogLevel { return Platform.LogLevel(OSLogType.debug.rawValue) } +} + +#endif diff --git a/Tests/TraceLogTests/Writers & Formatters/UnifiedLogWriterTests.swift b/Tests/TraceLogTests/Writers & Formatters/UnifiedLogWriterTests.swift new file mode 100644 index 00000000..99bbebda --- /dev/null +++ b/Tests/TraceLogTests/Writers & Formatters/UnifiedLogWriterTests.swift @@ -0,0 +1,229 @@ +/// +/// OSLogWriterTests.swift +/// +/// Copyright 2022 Tony Stone +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Created by Tony Stone on 4/8/22. +/// + +import XCTest + +@testable import TraceLog + +@available(iOS 10.0, macOS 10.13, watchOS 3.0, tvOS 10.0, *) +private let testEqual: (UnifiedLogWriter, TestLogEntry?, TestLogEntry) -> Void = { writer, result, expected in + + guard let result = result + else { XCTFail("Failed to locate log entry."); return } + + XCTAssertEqual(result.timestamp, expected.timestamp) + XCTAssertEqual(result.level, expected.level) + XCTAssertEqual(result.message, expected.message) + XCTAssertEqual(result.tag, expected.tag) + + /// Note: These platforms due to Apple Unified Logging os_log, don't support these attributes. + #if !os(macOS) && !os(iOS) && !os(tvOS) && !os(watchOS) + + XCTAssertEqual(result.file, expected.file) + XCTAssertEqual(result.function, expected.function) + XCTAssertEqual(result.line, expected.line) + #endif + + XCTAssertEqual(result.customAttributes?["subsystem"] as? String, writer.subsystem) +} + +/// +/// Direct Logging to the Logger +/// +@available(iOS 10.0, macOS 10.13, watchOS 3.0, tvOS 10.0, *) +class UnifiedLogWriterTests: XCTestCase { + + let testHarness = TestHarness(writer: UnifiedLogWriter(), reader: UnifiedLogReader()) + + // MARK: - Log Level Conversation with default table + + func testConvertLogLevelForError() { + XCTAssertEqual(testHarness.writer.platformLogLevel(for: .error), DarwinPlatformValidator.error) + } + + func testConvertLogLevelForWarning() { + XCTAssertEqual(testHarness.writer.platformLogLevel(for: .warning), DarwinPlatformValidator.warning) + } + + func testConvertLogLevelForInfo() { + XCTAssertEqual(testHarness.writer.platformLogLevel(for: .info), DarwinPlatformValidator.info) + } + + func testConvertLogLevelForTrace1() { + XCTAssertEqual(testHarness.writer.platformLogLevel(for: .trace1), DarwinPlatformValidator.trace1) + } + + func testConvertLogLevelForTrace2() { + XCTAssertEqual(testHarness.writer.platformLogLevel(for: .trace2), DarwinPlatformValidator.trace2) + } + + func testConvertLogLevelForTrace3() { + XCTAssertEqual(testHarness.writer.platformLogLevel(for: .trace3), DarwinPlatformValidator.trace3) + } + + func testConvertLogLevelForTrace4() { + XCTAssertEqual(testHarness.writer.platformLogLevel(for: .trace4), DarwinPlatformValidator.trace4) + } + + // MARK: - Log Level Conversation with empty table + + func testConvertLogLvelErrorWithEmptyConversionTable() { + XCTAssertEqual(UnifiedLogWriter(logLevelConversion: [:]).platformLogLevel(for: .error), DarwinPlatformValidator.default) + } + + func testConvertLogLvelWarningWithEmptyConversionTable() { + XCTAssertEqual(UnifiedLogWriter(logLevelConversion: [:]).platformLogLevel(for: .warning), DarwinPlatformValidator.default) + } + + func testConvertLogLvelInfoWithEmptyConversionTable() { + XCTAssertEqual(UnifiedLogWriter(logLevelConversion: [:]).platformLogLevel(for: .info), DarwinPlatformValidator.default) + } + + func testConvertLogLvelTrace1WithEmptyConversionTable() { + XCTAssertEqual(UnifiedLogWriter(logLevelConversion: [:]).platformLogLevel(for: .trace1), DarwinPlatformValidator.default) + } + + func testConvertLogLvelTrace2WithEmptyConversionTable() { + XCTAssertEqual(UnifiedLogWriter(logLevelConversion: [:]).platformLogLevel(for: .trace2), DarwinPlatformValidator.default) + } + + func testConvertLogLvelTrace3WithEmptyConversionTable() { + XCTAssertEqual(UnifiedLogWriter(logLevelConversion: [:]).platformLogLevel(for: .trace3), DarwinPlatformValidator.default) + } + + func testConvertLogLvelTrace4WithEmptyConversionTable() { + XCTAssertEqual(UnifiedLogWriter(logLevelConversion: [:]).platformLogLevel(for: .trace4), DarwinPlatformValidator.default) + } + + // MARK: - Init method tests + + func testSyslogIdentifier() { + let subsystemIdentifier = "TestSubsystemIdentifier" + + /// Create a custom instance of the TestHarness so the subsystem can be passed. + TestHarness(writer: UnifiedLogWriter(subsystem: subsystemIdentifier), reader: UnifiedLogReader()).testLog(for: .error, validationBlock: testEqual); + } + + // MARK: - Direct calls to the writer with default conversion table. + + func testLogError() { + testHarness.testLog(for: .error, validationBlock: testEqual) + } + + func testLogWarning() { + testHarness.testLog(for: .warning, validationBlock: testEqual) + } + + func testLogInfo() { + testHarness.testLog(for: .info, validationBlock: testEqual) + } + + func testLogTrace1() { + testHarness.testLog(for: .trace1, validationBlock: testEqual) + } + + func testLogTrace2() { + testHarness.testLog(for: .trace2, validationBlock: testEqual) + } + + func testLogTrace3() { + testHarness.testLog(for: .trace3, validationBlock: testEqual) + } + + func testLogTrace4() { + testHarness.testLog(for: .trace4, validationBlock: testEqual) + } +} + +/// +/// Logging through TraceLog to the Logger +/// +@available(iOS 10.0, macOS 10.13, watchOS 3.0, tvOS 10.0, *) +class TraceLogWithAdaptiveWriterTests: XCTestCase { + + let testHarness = TestHarness(writer: UnifiedLogWriter(), reader: UnifiedLogReader()) + + override func setUp() { + TraceLog.configure(writers: [testHarness.writer], environment: ["LOG_ALL": "TRACE4"]) + } + + func testLogError() { + + testHarness.testLog(for: .error, testBlock: { (tag, message, file, function, line) in + + logError(tag, file, function, line) { message } + + }, validationBlock: testEqual) + } + + func testLogWarning() { + + testHarness.testLog(for: .warning, testBlock: { (tag, message, file, function, line) in + + logWarning(tag, file, function, line) { message } + + }, validationBlock: testEqual) + } + + func testLogInfo() { + + testHarness.testLog(for: .info, testBlock: { (tag, message, file, function, line) in + + logInfo(tag, file, function, line) { message } + + }, validationBlock: testEqual) + } + + func testLogTrace1() { + + testHarness.testLog(for: .trace1, testBlock: { (tag, message, file, function, line) in + + logTrace(tag, level: 1, file, function, line) { message } + + }, validationBlock: testEqual) + } + + func testLogTrace2() { + + testHarness.testLog(for: .trace2, testBlock: { (tag, message, file, function, line) in + + logTrace(tag, level: 2, file, function, line) { message } + + }, validationBlock: testEqual) + } + + func testLogTrace3() { + + testHarness.testLog(for: .trace3, testBlock: { (tag, message, file, function, line) in + + logTrace(tag, level: 3, file, function, line) { message } + + }, validationBlock: testEqual) + } + + func testLogTrace4() { + + testHarness.testLog(for: .trace4, testBlock: { (tag, message, file, function, line) in + + logTrace(tag, level: 4, file, function, line) { message } + + }, validationBlock: testEqual) + } +} From fff199fe4f47bb6e3ba6eab9d5bf65bdb41756b2 Mon Sep 17 00:00:00 2001 From: Tony Stone Date: Fri, 8 Apr 2022 12:12:56 -0700 Subject: [PATCH 7/9] Changing init to public along with the defaults. --- Sources/TraceLog/Writers/UnifiedLogWriter.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/TraceLog/Writers/UnifiedLogWriter.swift b/Sources/TraceLog/Writers/UnifiedLogWriter.swift index fe11726d..2b370b27 100644 --- a/Sources/TraceLog/Writers/UnifiedLogWriter.swift +++ b/Sources/TraceLog/Writers/UnifiedLogWriter.swift @@ -45,7 +45,7 @@ public class UnifiedLogWriter: Writer { /// /// The default LogLevel Conversion Table. /// - static let defaultLogLevelConversion: [TraceLog.LogLevel: Platform.LogLevel] = [ + public static let defaultLogLevelConversion: [TraceLog.LogLevel: Platform.LogLevel] = [ .error: Platform.LogLevel(OSLogType.error.rawValue), .warning: Platform.LogLevel(OSLogType.default.rawValue), .info: Platform.LogLevel(OSLogType.default.rawValue), @@ -72,7 +72,7 @@ public class UnifiedLogWriter: Writer { /// - sybsystem: An identifier string, usually in reverse DNS notation, representing the subsystem that’s performing logging (defaults to current process name). /// - logLevelConversion: A dictionary keyed by TraceLog LogLevels with the value to convert to the os_log level. /// - required init(subsystem: String? = nil, logLevelConversion: [TraceLog.LogLevel: Platform.LogLevel] = defaultLogLevelConversion) { + public init(subsystem: String? = nil, logLevelConversion: [TraceLog.LogLevel: Platform.LogLevel] = defaultLogLevelConversion) { self.subsystem = subsystem ?? ProcessInfo.processInfo.processName self.logLevelConversion = logLevelConversion } From 9f80fc26f9a4abbab0999e3949449ad09a11d642 Mon Sep 17 00:00:00 2001 From: Tony Stone Date: Mon, 10 Jul 2023 15:44:01 -0500 Subject: [PATCH 8/9] Update README.md Adding the UnifiedLogWriter to the README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bda5a0b0..533f114e 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ * Built-in (`OutputStreamWriter`s) * **Stdout (ConsoleWriter)** - A simple standard out (stdout) writer for logging to the console or terminal. * **File (FileWriter)** - A file writer which writes log output to files on local disk managing rotation and archive of files as needed. + * **Apple Unified Logging (UnifiedLogWriter)** - On Apple platforms the AdaptiveWriter writes to the Unified Logging System * External * **Apple Unified Logging (AdaptiveWriter)** - On Apple platforms the AdaptiveWriter writes to the Unified Logging System (see [https://github.com/tonystone/tracelog-adaptive-writer](https://github.com/tonystone/tracelog-adaptive-writer)). * **Linux systemd Journal (AdaptiveWriter)** - On Linux platforms the AdaptiveWriter writes to the systemd journal (see [https://github.com/tonystone/tracelog-adaptive-writer](https://github.com/tonystone/tracelog-adaptive-writer)) From cd0afff1b6570beacdf4ca9cf6f5a44dee6d7375 Mon Sep 17 00:00:00 2001 From: Tony Stone Date: Mon, 10 Jul 2023 15:46:03 -0500 Subject: [PATCH 9/9] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9d4a70d..37b2f312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log All significant changes to this project will be documented in this file. +## [5.1.0](https://github.com/tonystone/tracelog/tree/5.1.0) + +#### Added +- Added the UnifiedLogWriter to write to the Apple Unified Logging system on Apple platforms. + ## [5.0.1](https://github.com/tonystone/tracelog/tree/5.0.1) #### Fixed