diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_template.yml b/.github/PULL_REQUEST_TEMPLATE/bug_template.md similarity index 100% rename from .github/PULL_REQUEST_TEMPLATE/bug_template.yml rename to .github/PULL_REQUEST_TEMPLATE/bug_template.md diff --git a/.github/PULL_REQUEST_TEMPLATE/feature_template.yml b/.github/PULL_REQUEST_TEMPLATE/feature_template.md similarity index 100% rename from .github/PULL_REQUEST_TEMPLATE/feature_template.yml rename to .github/PULL_REQUEST_TEMPLATE/feature_template.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d544d32..28d58cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,6 @@ on: - "Source/**" - "Tests/**" -concurrency: - group: ci - cancel-in-progress: true - jobs: SwiftLint: runs-on: ubuntu-latest @@ -28,40 +24,176 @@ jobs: args: --strict env: DIFF_BASE: ${{ github.base_ref }} - Latest: - name: Test Latest (iOS, macOS, tvOS, watchOS) - runs-on: macOS-12 + macOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - xcode: "Xcode_15.0" + runsOn: macos-13 + name: "macOS 13, Xcode 15.0, Swift 5.9.0" + - xcode: "Xcode_14.3.1" + runsOn: macos-13 + name: "macOS 13, Xcode 14.3.1, Swift 5.8.0" + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "Log" -destination "platform=macOS" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3.1.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + xcode: true + xcode_archive_path: test_output/${{ matrix.name }}.xcresult + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} + path: test_output + + iOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=17.0.1,name=iPhone 14 Pro" + name: "iOS 17.0.1" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=16.4,name=iPhone 14 Pro" + name: "iOS 16.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "Log" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} + path: test_output + + tvOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=17.0,name=Apple TV" + name: "tvOS 17.0" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=16.4,name=Apple TV" + name: "tvOS 16.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "Log" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3.1.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + xcode: true + xcode_archive_path: test_output/${{ matrix.name }}.xcresult + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} + path: test_output + + watchOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} env: - DEVELOPER_DIR: "/Applications/Xcode_14.1.app/Contents/Developer" - timeout-minutes: 10 + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 strategy: fail-fast: false matrix: include: - - destination: "OS=16.1,name=iPhone 14 Pro" - name: "iOS" - scheme: "Log" - sdk: iphonesimulator - - destination: "OS=16.1,name=Apple TV" - name: "tvOS" - scheme: "Log" - sdk: appletvsimulator - - destination: "OS=9.1,name=Apple Watch Series 8 (45mm)" - name: "watchOS" - scheme: "Log" - sdk: watchsimulator - - destination: "platform=macOS" - name: "macOS" - scheme: "Log" - sdk: macosx + - destination: "OS=10.0,name=Apple Watch Series 9 (45mm)" + name: "watchOS 10.0" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=9.4,name=Apple Watch Series 8 (45mm)" + name: "watchOS 9.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 steps: - uses: actions/checkout@v3 - name: ${{ matrix.name }} - run: xcodebuild test -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "./${{ matrix.sdk }}.xcresult" | xcpretty -r junit + run: xcodebuild test -scheme "Log" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3.1.0 with: token: ${{ secrets.CODECOV_TOKEN }} xcode: true - xcode_archive_path: "./${{ matrix.sdk }}.xcresult" - \ No newline at end of file + xcode_archive_path: test_output/${{ matrix.name }}.xcresult + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.name }} + path: test_output + + spm: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - name: "Xcode 15" + xcode: "Xcode_15.0" + runsOn: macos-13 + - name: "Xcode 14" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + run: swift build -c release + + merge-test-reports: + needs: [iOS, macOS, watchOS, tvOS] + runs-on: macos-13 + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: test_output + - run: xcrun xcresulttool merge test_output/**/*.xcresult --output-path test_output/final/final.xcresult + - name: Upload Merged Artifact + uses: actions/upload-artifact@v4 + with: + name: MergedResult + path: test_output/final + + discover-typos: + name: Discover Typos + runs-on: macOS-12 + env: + DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer + steps: + - uses: actions/checkout@v2 + - name: Discover typos + run: | + export PATH="$PATH:/Library/Frameworks/Python.framework/Versions/3.11/bin" + python3 -m pip install --upgrade pip + python3 -m pip install codespell + codespell --ignore-words-list="hart,inout,msdos,sur" --skip="./.build/*,./.git/*" \ No newline at end of file diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 1a0ab52..158ca87 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -15,7 +15,7 @@ jobs: - name: ruby setup uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7 + ruby-version: 3.1.4 bundler-cache: true - name: Checkout code uses: actions/checkout@v2 diff --git a/.swiftlint.yml b/.swiftlint.yml index 6d90edc..58036d3 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -16,7 +16,6 @@ opt_in_rules: # some rules are only opt-in - attributes - closure_body_length - closure_end_indentation - - closure_spacing - collection_alignment - contains_over_filter_count - contains_over_filter_is_empty diff --git a/CHANGELOG.md b/CHANGELOG.md index 259b66b..aa07494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,20 @@ All notable changes to this project will be documented in this file. #### 1.x Releases +- `1.1.x` Releases - [1.1.0](#110) - `1.0.x` Releases - [1.0.0](#100) +## [1.1.0](https://github.com/space-code/log/releases/tag/1.1.0) +#### Added +- Make the `logLevel` property changeable + - Added in Pull Request [#5](https://github.com/space-code/log/pull/5). +- Add files to comply with community standards + - Added in Pull Request [#4](https://github.com/space-code/log/pull/4). +- Update GitHub Actions workflow + - Added in Pull Request [#3](https://github.com/space-code/log/pull/3). +- Hide `IOSWriter` & `IConsoleWriter` from the public interface + - Added in Pull Request [#2](https://github.com/space-code/log/pull/2). + ## [1.0.0](https://github.com/space-code/log/releases/tag/1.0.0) Released on 2023-10-18. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..56c1661 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting one of the project maintainers https://github.com/orgs/space-code/people. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..349d9ec --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,61 @@ +# Contributing Guidelines + +This document contains information and guidelines about contributing to this project. +Please read it before you start participating. + +**Topics** + +* [Reporting Issues](#reporting-issues) +* [Submitting Pull Requests](#submitting-pull-requests) +* [Developers Certificate of Origin](#developers-certificate-of-origin) +* [Code of Conduct](#code-of-conduct) + +## Reporting Issues + +A great way to contribute to the project is to send a detailed issue when you encounter a problem. We always appreciate a well-written, thorough bug report. + +Check that the project issues database doesn't already include that problem or suggestion before submitting an issue. If you find a match, feel free to vote for the issue by adding a reaction. Doing this helps prioritize the most common problems and requests. + +When reporting issues, please fill out our issue template. The information the template asks for will help us review and fix your issue faster. + +## Submitting Pull Requests + +You can contribute by fixing bugs or adding new features. For larger code changes, we recommend first discussing your ideas on our [GitHub Discussions](https://github.com/space-code/log/discussions). When submitting a pull request, please add relevant tests and ensure your changes don't break any existing tests. + +## Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +- (a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +- (b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +- (c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +- (d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. + +## Code of Conduct + +The Code of Conduct governs how we behave in public or in private +whenever the project will be judged by our actions. +We expect it to be honored by everyone who contributes to this project. + +See [CODE_OF_CONDUCT.md](https://github.com/space-code/log/blob/master/CODE_OF_CONDUCT.md) for details. + +--- + +*Some of the ideas and wording for the statements above were based on work by the [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) and [Linux](https://elinux.org/Developer_Certificate_Of_Origin) communities. \ No newline at end of file diff --git a/Package@swift-5.8.swift b/Package@swift-5.8.swift new file mode 100644 index 0000000..6de492d --- /dev/null +++ b/Package@swift-5.8.swift @@ -0,0 +1,24 @@ +// swift-tools-version: 5.8 +// The swift-tools-version declares the minimum version of Swift required to build this package. +// swiftlint:disable all + +import PackageDescription + +let package = Package( + name: "Log", + platforms: [ + .macOS(.v10_15), + .iOS(.v13), + .watchOS(.v7), + .tvOS(.v13), + ], + products: [ + .library(name: "Log", targets: ["Log"]), + ], + dependencies: [], + targets: [ + .target(name: "Log", dependencies: []), + .testTarget(name: "LogTests", dependencies: ["Log"]), + ] +) +// swiftlint:enable all diff --git a/README.md b/README.md index 03b197d..d3d5638 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,13 @@

License -Platform -5.7 +Swift Compatibility +Platform Compatibility CI CodeCov +GitHub release; latest by date +GitHub commit activity

## Description diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..20dffca --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Reporting Security Vulnerabilities + +This software is built with security and data privacy in mind to ensure your data is safe. We are grateful for security researchers and users reporting a vulnerability to us, first. To ensure that your request is handled in a timely manner and non-disclosure of vulnerabilities can be assured, please follow the below guideline. + +**Please do not report security vulnerabilities directly on GitHub. GitHub Issues can be publicly seen and therefore would result in a direct disclosure.** + +* Please address questions about data privacy, security concepts, and other media requests to the nv3212@gmail.com mailbox. \ No newline at end of file diff --git a/Sources/Log/Classes/Core/Logger/Logger.swift b/Sources/Log/Classes/Core/Logger/Logger.swift index 8405788..2af975c 100644 --- a/Sources/Log/Classes/Core/Logger/Logger.swift +++ b/Sources/Log/Classes/Core/Logger/Logger.swift @@ -1,8 +1,10 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // +import Foundation + // MARK: - Logger /// A class responsible for logging functionality. @@ -10,7 +12,14 @@ open class Logger { // MARK: Properties /// The current log level for this logger. - let logLevel: LogLevel + private var _logLevel: Atomic + + /// The current log level for this logger. + public var logLevel: LogLevel { + get { _logLevel.value } + set { _logLevel.value = newValue } + } + /// An array of printer strategies to handle the log output. let printers: [IPrinterStrategy] @@ -26,7 +35,7 @@ open class Logger { logLevel: LogLevel ) { self.printers = printers - self.logLevel = logLevel + _logLevel = Atomic(value: logLevel) } // MARK: Private @@ -41,7 +50,7 @@ open class Logger { printers.forEach { $0.log(message, logLevel: logLevel) } } - /// Checks if the given `LogLevel` is allowed by the reciever. + /// Checks if the given `LogLevel` is allowed by the receiver. /// /// - Parameter logLevel: The log level to check. private func isLoggerEnabled(for logLevel: LogLevel) -> Bool { diff --git a/Sources/Log/Classes/Core/Printers/ConsolePrinter.swift b/Sources/Log/Classes/Core/Printers/ConsolePrinter.swift index 103e162..cb3018d 100644 --- a/Sources/Log/Classes/Core/Printers/ConsolePrinter.swift +++ b/Sources/Log/Classes/Core/Printers/ConsolePrinter.swift @@ -1,6 +1,6 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation @@ -15,16 +15,25 @@ public final class ConsolePrinter { public let formatters: [ILogFormatter] /// The console writer. - public let consoleWriter: IConsoleWriter + private let consoleWriter: IConsoleWriter // MARK: Initialization + /// Creates a new `ConsolePrinter` instance. + /// + /// - Parameters: + /// - formatters: An array of log formatters for customizing log messages. + public init(formatters: [ILogFormatter]) { + self.formatters = formatters + consoleWriter = ConsoleWriter() + } + /// Creates a new `ConsolePrinter` instance. /// /// - Parameters: /// - formatters: An array of log formatters for customizing log messages. /// - consoleWriter: The console writer. - public init(formatters: [ILogFormatter], consoleWriter: IConsoleWriter = ConsoleWriter()) { + init(formatters: [ILogFormatter], consoleWriter: IConsoleWriter) { self.formatters = formatters self.consoleWriter = consoleWriter } diff --git a/Sources/Log/Classes/Core/Printers/OSPrinter.swift b/Sources/Log/Classes/Core/Printers/OSPrinter.swift index 1964c56..10bcb55 100644 --- a/Sources/Log/Classes/Core/Printers/OSPrinter.swift +++ b/Sources/Log/Classes/Core/Printers/OSPrinter.swift @@ -1,6 +1,6 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation @@ -14,15 +14,9 @@ public final class OSPrinter { /// An array of log formatters used to customize log message output. public let formatters: [ILogFormatter] - /// The optional subsystem for categorizing log messages. - public let subsystem: String - /// The optional category for categorizing log messages. - public let category: String - /// The os writer. - public let osWriter: IOSWriter - /// An internal lazy property for initializing the OSLog instance. - private lazy var osLog: OSLog = .init(subsystem: subsystem, category: category) + /// An os writer. + private let osWriter: IOSWriter // MARK: Initialization @@ -32,15 +26,24 @@ public final class OSPrinter { /// - subsystem: An optional subsystem for categorizing log messages. /// - category: An optional category for categorizing log messages. /// - formatters: An array of log formatters for customizing log messages. - /// - osWriter: An os writer. public init( - subsystem: String = "os_printer", - category: String = "", - formatters: [ILogFormatter], - osWriter: IOSWriter = OSWriter() + subsystem: String, + category: String, + formatters: [ILogFormatter] ) { - self.subsystem = subsystem - self.category = category + self.formatters = formatters + osWriter = OSWriter( + subsystem: subsystem, + category: category + ) + } + + /// Creates a new `OSPrinter` instance. + /// + /// - Parameters: + /// - formatters: An array of log formatters for customizing log messages. + /// - osWriter: An os writer. + init(formatters: [ILogFormatter], osWriter: IOSWriter) { self.formatters = formatters self.osWriter = osWriter } @@ -52,7 +55,7 @@ extension OSPrinter: IStyleLogStrategy { public func log(_ message: String, logLevel: LogLevel) { let message = formatMessage(message, logLevel: logLevel) let type = sysLogPriority(logLevel) - osWriter.log("%s", log: osLog, type: type, message) + osWriter.log(type: type, message) } } diff --git a/Sources/Log/Classes/Helpers/Atomic/Atomic.swift b/Sources/Log/Classes/Helpers/Atomic/Atomic.swift new file mode 100644 index 0000000..6d0b4cb --- /dev/null +++ b/Sources/Log/Classes/Helpers/Atomic/Atomic.swift @@ -0,0 +1,43 @@ +// +// log +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - Atomic + +/// The Atomic class is designed to provide thread-safe access to a mutable value. +final class Atomic { + // MARK: Properties + + /// NSLock instance for synchronization. + private let lock = NSLock() + /// The actual mutable value. + private var _value: Value + + /// Computed property to get and set the value atomically + var value: Value { + get { lock.synchronized { _value } } + set { lock.synchronized { _value = newValue }} + } + + // MARK: Initialization + + /// Initializes the Atomic instance with an initial value. + init(value: Value) { + _value = value + } +} + +// MARK: - Extensions + +private extension NSLock { + /// Synchronizes a code block using the NSLock instance. + /// This helps ensure that only one thread can access the critical section at a time. + func synchronized(block: () throws -> T) rethrows -> T { + lock() + defer { unlock() } + return try block() + } +} diff --git a/Sources/Log/Classes/Helpers/Writers/ConsoleWriter/ConsoleWriter.swift b/Sources/Log/Classes/Helpers/Writers/ConsoleWriter/ConsoleWriter.swift index 1549580..adaf162 100644 --- a/Sources/Log/Classes/Helpers/Writers/ConsoleWriter/ConsoleWriter.swift +++ b/Sources/Log/Classes/Helpers/Writers/ConsoleWriter/ConsoleWriter.swift @@ -1,6 +1,6 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation @@ -8,15 +8,15 @@ import Foundation // MARK: - ConsoleWriter /// A class that conforms to the IConsoleWriter protocol and writes messages to the console output. -public final class ConsoleWriter: IConsoleWriter { +final class ConsoleWriter: IConsoleWriter { // MARK: Initialization /// Initializes a new ConsoleWriter instance. - public init() {} + init() {} // MARK: IConsoleWriter - public func print(_ message: String) { + func print(_ message: String) { Swift.print(message) } } diff --git a/Sources/Log/Classes/Helpers/Writers/ConsoleWriter/IConsoleWriter.swift b/Sources/Log/Classes/Helpers/Writers/ConsoleWriter/IConsoleWriter.swift index 09d5a93..b0b5b6c 100644 --- a/Sources/Log/Classes/Helpers/Writers/ConsoleWriter/IConsoleWriter.swift +++ b/Sources/Log/Classes/Helpers/Writers/ConsoleWriter/IConsoleWriter.swift @@ -1,12 +1,12 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation /// A protocol for writing messages to the console. -public protocol IConsoleWriter { +protocol IConsoleWriter { /// Prints a message to the console output. /// /// - Parameter message: The message to be printed as a String. diff --git a/Sources/Log/Classes/Helpers/Writers/OSWriter/IOSWriter.swift b/Sources/Log/Classes/Helpers/Writers/OSWriter/IOSWriter.swift index ec2fcab..29aa5f9 100644 --- a/Sources/Log/Classes/Helpers/Writers/OSWriter/IOSWriter.swift +++ b/Sources/Log/Classes/Helpers/Writers/OSWriter/IOSWriter.swift @@ -1,6 +1,6 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation @@ -11,9 +11,7 @@ public protocol IOSWriter { /// Writes a log message to the specified OSLog. /// /// - Parameters: - /// - message: A StaticString containing the log message format. - /// - log: An OSLog object representing the log subsystem and category. /// - type: An OSLogType indicating the log message type. - /// - args: A variadic list of CVarArg values to fill in the message format. - func log(_ message: StaticString, log: OSLog, type: OSLogType, _ args: CVarArg...) + /// - message: A variadic list of String values to fill in the message format. + func log(type: OSLogType, _ message: String) } diff --git a/Sources/Log/Classes/Helpers/Writers/OSWriter/OSWriter.swift b/Sources/Log/Classes/Helpers/Writers/OSWriter/OSWriter.swift index a4ad3ad..8af506b 100644 --- a/Sources/Log/Classes/Helpers/Writers/OSWriter/OSWriter.swift +++ b/Sources/Log/Classes/Helpers/Writers/OSWriter/OSWriter.swift @@ -1,6 +1,6 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation @@ -8,16 +8,63 @@ import OSLog // MARK: - OSWriter -/// A class that conforms to the IOSWriter protocol and writes log messages to the Apple OSLog system. -public final class OSWriter: IOSWriter { +final class OSWriter: IOSWriter { + // MARK: Properties + + /// The optional subsystem for categorizing log messages. + private let subsystem: String + /// The optional category for categorizing log messages. + private let category: String + + /// An internal lazy property for initializing the OSLog instance. + private var osLog: OSLog { OSLog(subsystem: subsystem, category: category) } + + /// An internal lazy property for initializing WriteStrategy instance. + private lazy var writerStrategy: IOSWriterStrategy = { + if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { + return os.Logger(osLog) + } else { + return LegacyOSLogger(osLog: osLog) + } + }() + // MARK: Initialization - /// Creates a new `OSWriter` instance. - public init() {} + /// Creates a `OSWriter` instance. + /// + /// - Parameters: + /// - subsystem: An optional subsystem for categorizing log messages. + /// - category: An optional category for categorizing log messages. + init(subsystem: String, category: String) { + self.subsystem = subsystem + self.category = category + } // MARK: IOSWriter - public func log(_ message: StaticString, log: OSLog, type: OSLogType, _ args: CVarArg...) { - os_log(message, log: log, type: type, args) + func log(type: OSLogType, _ message: String) { + writerStrategy.log(type: type, message) + } +} + +// MARK: OSWriter.LegacyOSLogger + +private extension OSWriter { + struct LegacyOSLogger: IOSWriterStrategy { + // MARK: Private + + private let osLog: OSLog + + // MARK: Initialization + + init(osLog: OSLog) { + self.osLog = osLog + } + + // MARK: IOSWriterStrategy + + func log(type: OSLogType, _ message: String) { + os_log("%s", log: osLog, type: type, message) + } } } diff --git a/Sources/Log/Classes/Helpers/Writers/OSWriter/Strategies/IOSWriterStrategy.swift b/Sources/Log/Classes/Helpers/Writers/OSWriter/Strategies/IOSWriterStrategy.swift new file mode 100644 index 0000000..bb8f3ed --- /dev/null +++ b/Sources/Log/Classes/Helpers/Writers/OSWriter/Strategies/IOSWriterStrategy.swift @@ -0,0 +1,27 @@ +// +// log +// Copyright © 2024 Space Code. All rights reserved. +// + +import os + +// MARK: - IOSWriterStrategy + +/// Protocol defining the contract for a logger strategy that writes logs to the iOS system logs using OSLog. +protocol IOSWriterStrategy { + /// Writes a log message to the iOS system logs with the specified log type. + /// + /// - Parameters: + /// - type: The type of the log message (debug, info, error, etc.). + /// - message: The message to be logged. + func log(type: OSLogType, _ message: String) +} + +// MARK: - os.Logger + IOSWriterStrategy + +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension os.Logger: IOSWriterStrategy { + func log(type: OSLogType, _ message: String) { + log(level: type, "\(message)") + } +} diff --git a/Tests/LogTests/IntegrationTests/LogIntegrationTests.swift b/Tests/LogTests/IntegrationTests/LogIntegrationTests.swift new file mode 100644 index 0000000..c87b149 --- /dev/null +++ b/Tests/LogTests/IntegrationTests/LogIntegrationTests.swift @@ -0,0 +1,70 @@ +// +// log +// Copyright © 2024 Space Code. All rights reserved. +// + +import Log +import XCTest + +// MARK: - LogIntegrationTests + +final class LogIntegrationTests: XCTestCase { + // MARK: Properties + + private var sut: Logger! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + let formatters: [ILogFormatter] = [ + TimestampLogFormatter(dateFormat: "dd/MM/yyyy"), + PrefixLogFormatter(name: "LogIntegrationTests"), + ] + + sut = Logger( + printers: [ + ConsolePrinter( + formatters: formatters + ), + OSPrinter( + subsystem: .subsystem, + category: .category, + formatters: formatters + ), + ], + logLevel: .all + ) + } + + override func tearDown() { + sut = nil + super.tearDown() + } + + // MARK: Tests + + // The test just checks that the methods don't cause a crash + // when printing a message to different outputs. + func test_logDoesNotThrowUnexpectedBehavior_whenLogMessages() { + // 1. Print an info message + sut.info(message: .message) + + // 2. Print a debug message + sut.debug(message: .message) + + // 3. Print an error message + sut.error(message: .message) + + // 4. Print a fault message + sut.fault(message: .message) + } +} + +// MARK: - Constants + +private extension String { + static let subsystem = "subsystem" + static let category = "category" + static let message = "text" +} diff --git a/Tests/LogTests/Mocks/ConsoleWriterMock.swift b/Tests/LogTests/Mocks/ConsoleWriterMock.swift index 7b0cec8..620f81b 100644 --- a/Tests/LogTests/Mocks/ConsoleWriterMock.swift +++ b/Tests/LogTests/Mocks/ConsoleWriterMock.swift @@ -1,10 +1,10 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation -import Log +@testable import Log final class ConsoleWriterMock: IConsoleWriter { var invokedPrint = false diff --git a/Tests/LogTests/Mocks/OSWriterMock.swift b/Tests/LogTests/Mocks/OSWriterMock.swift index 165dc09..0d00eb1 100644 --- a/Tests/LogTests/Mocks/OSWriterMock.swift +++ b/Tests/LogTests/Mocks/OSWriterMock.swift @@ -1,21 +1,21 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // -import Log +@testable import Log import OSLog final class OSWriterMock: IOSWriter { var invokedLog = false var invokedLogCount = 0 - var invokedLogParameters: (message: StaticString, log: OSLog, type: OSLogType, args: CVarArg)? - var invokedLogParametersList = [(message: StaticString, log: OSLog, type: OSLogType, args: CVarArg)]() + var invokedLogParameters: (type: OSLogType, message: String)? + var invokedLogParametersList = [(type: OSLogType, message: String)]() - func log(_ message: StaticString, log: OSLog, type: OSLogType, _ args: CVarArg...) { + func log(type: OSLogType, _ message: String) { invokedLog = true invokedLogCount += 1 - invokedLogParameters = (message, log, type, args) - invokedLogParametersList.append((message, log, type, args)) + invokedLogParameters = (type, message) + invokedLogParametersList.append((type, message)) } } diff --git a/Tests/LogTests/UnitTests/ConsolePrinterTests.swift b/Tests/LogTests/UnitTests/ConsolePrinterTests.swift index 4478dfd..c14640a 100644 --- a/Tests/LogTests/UnitTests/ConsolePrinterTests.swift +++ b/Tests/LogTests/UnitTests/ConsolePrinterTests.swift @@ -1,9 +1,9 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // -import Log +@testable import Log import XCTest // MARK: - ConsolePrinterTests diff --git a/Tests/LogTests/UnitTests/LogTests.swift b/Tests/LogTests/UnitTests/LogTests.swift index 4284ac4..a9bf700 100644 --- a/Tests/LogTests/UnitTests/LogTests.swift +++ b/Tests/LogTests/UnitTests/LogTests.swift @@ -1,6 +1,6 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Log @@ -119,6 +119,21 @@ final class LogTests: XCTestCase { XCTAssertNil(printerMock.invokedLogParameters?.message) } + func test_thatLoggerDoesNotPrintAnything_whenLogLevelValueDidChange() { + // given + let sut = prepareSut() + + // when + sut.logLevel = .info + sut.debug(message: .message) + sut.info(message: .message) + + // then + XCTAssertEqual(sut.logLevel, .info) + XCTAssertEqual(printerMock.invokedLogCount, 1) + XCTAssertEqual(printerMock.invokedLogParameters?.message, .message) + } + // MARK: Private private func prepareSut(logLevel: LogLevel = .all) -> Logger { diff --git a/Tests/LogTests/UnitTests/OSPrinterTests.swift b/Tests/LogTests/UnitTests/OSPrinterTests.swift index f4175a5..d3a69fd 100644 --- a/Tests/LogTests/UnitTests/OSPrinterTests.swift +++ b/Tests/LogTests/UnitTests/OSPrinterTests.swift @@ -1,9 +1,9 @@ // // log -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // -import Log +@testable import Log import XCTest // MARK: - OSPrinterTests @@ -23,8 +23,6 @@ final class OSPrinterTests: XCTestCase { formatterMock = LogFormatterMock() osWriterMock = OSWriterMock() sut = OSPrinter( - subsystem: .subsystem, - category: .category, formatters: [formatterMock], osWriter: osWriterMock ) @@ -47,7 +45,7 @@ final class OSPrinterTests: XCTestCase { sut.log(.message, logLevel: .all) // then - XCTAssertEqual((osWriterMock.invokedLogParameters?.args as? [String])?.first, .message) + XCTAssertEqual(osWriterMock.invokedLogParameters?.message, .message) } func test_thatConsolePrinterLogsMessage_whenLogLevelIsDebug() { @@ -58,7 +56,7 @@ final class OSPrinterTests: XCTestCase { sut.log(.message, logLevel: .debug) // then - XCTAssertEqual((osWriterMock.invokedLogParameters?.args as? [String])?.first, .message) + XCTAssertEqual(osWriterMock.invokedLogParameters?.message, .message) } func test_thatConsolePrinterLogsMessage_whenLogLevelIsInfo() { @@ -69,7 +67,7 @@ final class OSPrinterTests: XCTestCase { sut.log(.message, logLevel: .info) // then - XCTAssertEqual((osWriterMock.invokedLogParameters?.args as? [String])?.first, .message) + XCTAssertEqual(osWriterMock.invokedLogParameters?.message, .message) } func test_thatConsolePrinterLogsMessage_whenLogLevelIsError() { @@ -80,7 +78,7 @@ final class OSPrinterTests: XCTestCase { sut.log(.message, logLevel: .error) // then - XCTAssertEqual((osWriterMock.invokedLogParameters?.args as? [String])?.first, .message) + XCTAssertEqual(osWriterMock.invokedLogParameters?.message, .message) } func test_thatConsolePrinterLogsMessage_whenLogLevelIsFault() { @@ -91,7 +89,7 @@ final class OSPrinterTests: XCTestCase { sut.log(.message, logLevel: .fault) // then - XCTAssertEqual((osWriterMock.invokedLogParameters?.args as? [String])?.first, .message) + XCTAssertEqual(osWriterMock.invokedLogParameters?.message, .message) } } diff --git a/codecov.yml b/codecov.yml index b415604..8bb858a 100644 --- a/codecov.yml +++ b/codecov.yml @@ -32,7 +32,7 @@ coverage: target: 85% # Allow coverage to drop by X% - threshold: 5% + threshold: 50% changes: no comment: