[TYPE] [DATE] [TIME] [PID:TID] message, file: #, line: #
This is the great home-made product in Swift. Light-weight logging lover.
1:Log to the console.
2:Log to macOS Console.
3:Log to custom output.
4:Collect log messages.
5:Delegate log message.
CPLis a single author and personale solution developed inP2Prelationship paradigm.
CPLstands forConsolePerseusLogger.
PGKstands forPerseusGeoKit.
PDMstands forPerseusDarkMode.
P2Pstands forPerson-to-Person.
A3stands forAppleAppsApprobation.
T3stands forTheTechnologicalTree.
In approbation:iOS appmacOS app
In business:The Dark MoonPerseusGeoKitPerseusDarkModeConvertor mov2gif
For details:Approbation and A3 Environment/CHANGELOG/Xcode Playground
- In brief
- Build requirements
- Third-party software
- Installation
- Usage
- Points taken into account
- License
- Credits
- Author
USE LOGGER LIKE A VARIABLE ANYWHERE YOU WANT
Tip
As the single source code CPLStar.swift CPL with minimum changes can be used even in Xcode 10.1, just remove all statements starting with if #available(iOS 14.0, macOS 11.0, *).
| Type | Name | License |
|---|---|---|
| Style | SwiftLint / v0.57.0 for Monterey+ | MIT |
| Script | SwiftLint Shell Script to run SwiftLint | MIT |
| Action | mxcl/xcodebuild@v3 | Unlicense |
| Action | cirruslabs/swiftlint-action@v1 | MIT |
Standalone: Place CPLStar.swift into your project directly
log.message("[\(type(of: self))].\(#function)")Swift Package Manager: https://github.com/perseusrealdeal/ConsolePerseusLogger
Exact Version is strongly recommended.
import ConsolePerseusLogger
log.message("[\(type(of: self))].\(#function)")Note
If output is .consoleapp and Environment Variable OS_ACTIVITY_MODE in disable log messaging will be restricted for Xcode console, but only.
import ConsolePerseusLogger
log.message("[\(type(of: self))].\(#function)")Note
To pass messages to Console.app CPL employs Logger structure (from iOS 14.0, macOS 11.0) and OSLog.
Case 1: Redirect all messages to .consoleapp
log.output = .consoleapp
log.message("[\(type(of: self))].\(#function)")Case 2: Redirect the message to .consoleapp
log.message("[\(type(of: self))].\(#function)", .debug, .consoleapp)Tip
Set custom titles for Console.app Subsystem and Category
log.logObject = ("MyApp", "MyLover") // Customs for Console.app Subsystem and Category.For any specific combination of the log message details.
import ConsolePerseusLogger
func customPrint(_ instance: LogMessage) {
let time = "[\(instance.localTime.date)] [\(instance.localTime.time)]"
let utc = instance.localTime.timeUTC
let id = "[\(instance.owner.pid):\(instance.owner.tid)]"
let dirs = "file: \(instance.fileline.fileName), line: \(instance.fileline.line)"
print("[MYLOG] \(instance.text) \(id) \(time) UTC: \(utc), \(dirs)")
}
log.customActionOnMessage = customPrint(_:)
log.format = .textonly
log.output = .custom
log.message("The app's start point...", .info)Case 1: As Is
Image(systemName: "globe")
.onAppear {
log.message("This is the debug output.")
}Case 2: Wrapper
Extend View with the method
message(_:_:_:_:_:):
extension View {
func message(_ text: @autoclosure () -> String,
_ type: PerseusLogger.Level = .debug,
_ oput: PerseusLogger.Output = PerseusLogger.output,
_ user: PerseusLogger.User = .operative,
_ file: StaticString = #file,
_ line: UInt = #line) -> Self {
log.message(text(), type, oput, user, file, line)
return self
}
}Then use
messageasa view modifierto output a message:
VStack {
ForEach(colors, id: \.self) { color in
Circle()
.foregroundColor(color)
.message("\(color)")
}
}CPL applies the most common log types for indicating information category.
| Level | Message Type | Description |
|---|---|---|
| 5 | DEBUG | Debugging only |
| 4 | INFO | Helpful, but not essential |
| 3 | NOTICE | Might result in a failure |
| 2 | ERROR | Errors seen during the code execution |
| 1 | FAULT | Faults and bugs in the code |
Important
NOTICE in use with end-user notifications as a recommended type, it's a default and almost neutral level.
Also, CPL considers Message Type to filter, look how it works:
Default values of CPL options depend on DEBUG/RELEASE.
| Option | Default in DEBUG | Default in RELEASE |
|---|---|---|
| tuned | .on | .off |
| output | .standard | .consoleapp |
| level | .debug | .notice |
Other CPL options are the same for DEBUG/RELEASE by default.
| Option | Default in DEBUG | Default in RELEASE |
|---|---|---|
| subsecond | .nanosecond | .nanosecond |
| tidnumber | .hexadecimal | .hexadecimal |
| format | .short | .short |
| linemode | .singleLine | .singleLine |
| marks | true | true |
| time | false | false |
| owner | false | false |
| directives | false | false |
| logObject | ("Perseus", "Lover") | ("Perseus", "Lover") |
Special option goes kinda lifehack. Matter only if Simulator.
| Option | Default in DEBUG | Default in RELEASE |
|---|---|---|
| debugIsInfo | true | true |
Each option can be reseted in run time with json config except option
turned.
Case 1: Using predefined json profile
import ConsolePerseusLogger
let isReseted = log.loadConfig(.debugRoutine)
let result = isReseted ? "CPL current options loaded." : "Failed to load options!"
log.message(result)Case 2: Using custom profile (URL required)
import ConsolePerseusLogger
var result = ""
if let path = Bundle.main.url(forResource: "CPLConfig", withExtension: "json") {
let isLoaded = log.loadConfig(path)
result = isLoaded ? "Options successfully loaded." : "Failed to load options!"
} else {
result = "Failed to create URL!"
}
log.message(result)In SPM package
CPLcan be used as standalone, just place CPLStar.swift into your project directly.
Important
Statement typealias log = PerseusLogger in SPM package should be not public.
Important
Statement protocol PerseusDelegatedMessage in SPM package should be not public, also.
//
// main.swift
//
import ConsolePerseusLogger
import class PackageA.PerseusLogger
import class PackageB.PerseusLogger
typealias logA = PackageA.PerseusLogger
typealias logB = PackageB.PerseusLogger
// MARK: - Subloggers
logA.turned = .off
logB.turned = .off
// MARK: - The Logger
log.message("The app's start point...", .info)Note
CPL employs Logger structure (from iOS 14.0, macOS 11.0) and OSLog to pass messages to macOS Console.
Just a matter of fact that Console.app doesn't show any DEBUG message from any app running on Simulator (even if "Include Debug Messages" tapped in Console.app).
Console Perseus Logger running on Simulator doesn't pass DEBUG message to Console.app, instead it passes INFO message with text of DEBUG message by default, so, a passed message being INFO looks like a DEBUG and it works perfactly well.
If for some reasons CPL must pass DEBUG as a DEBUG message the option should take false
log.debugIsInfo = false, but Console.app will not show DEBUG messages.
#if targetEnvironment(simulator)
log.debugIsInfo = false
#endifPerseusLogger.Report and KVO can be used to view last log messages.
import Foundation
let report = PerseusLogger.Report()
var observation: NSKeyValueObservation?
observation = report.observe(\.lastMessage, options: .new) { _, change in
// Refresh code
}
log.customActionOnMessage = report.report(_:)Step 1: Create a report
Step 2: Create an observer for the last message to refresh on change
Step 3: log.customActionOnMessage = report.report(_:)
Note
Override method report(_:) of PerseusLogger.Report to meet expectations with specifics.
Warning
You must be careful to avoid infinite recursion when you try to add a log statement to KVO block definition.
observation = report.observe(\.lastMessage, options: .new) { _, change in
// Infinite recursion case, fatal error.
log.message("[\(type(of: self))].\(#function)") // Leads to stack overflow.
// Refresh code
}A log message can be used for easy creating end-user notifications in a way like this:
log.message("Notification...", .notice, .custom, .enduser)Important
- End-user message egnores
log.turned = .off, butlog.levelis still matter. - Message type
.noticeis recommended, it's a default and almost neutral level. - Output
.customis in use to process end-user messages.
Step 1: Find a type to implement PerseusDelegatedMessage protocol
Step 2: Create a report of PerseusLogger.Report
Step 3: Set the delegate report.messageDelegate to the PerseusDelegatedMessage one
Step 4: log.customActionOnMessage = report.report(_:)
import ConsolePerseusLogger
class MyEndUserMessageClass: PerseusDelegatedMessage {
var message: String = "" {
didSet {
didSetEndUserMessage()
}
}
private func didSetEndUserMessage() {
log.message("[\(type(of: self))].\(#function): \(message)")
}
}
func customPrint(_ instance: LogMessage) {
if instance.user == .enduser {
delegate?.message = instance.text
return
}
print(instance.getMessage() + " customed!")
}
log.customActionOnMessage = customPrint(_:)
let delegate: PerseusDelegatedMessage? = MyEndUserMessageClass()
let greeting = "Hello"
log.message("\(greeting) 1", .notice) // .standard
log.message("\(greeting) 2", .notice, .custom) // .custom
log.message("\(greeting) 3", .notice, .custom, .enduser) // .custom delegated to .enduser- Preconfigured Swift Package manifest Package.swift
- Preconfigured SwiftLint config .swiftlint.yml
- Preconfigured SwiftLint CI swiftlint.yml
- Preconfigured GitHub config .gitignore
- Preconfigured GitHub CI main.yml
License: MIT
Copyright © 7531 - 7534 Mikhail A. Zhigulin of Novosibirsk
Copyright © 7531 - 7534 PerseusRealDeal
- The year starts from the creation of the world according to a Slavic calendar.
- September, the 1st of Slavic year. It means that "Sep 01, 2025" is the beginning of 7534.
© 2025 The SwiftLint Contributors for SwiftLint
© GitHub for GitHub Action cirruslabs/swiftlint-action@v1
© 2021 Alexandre Colucci, geteimy.com for Shell Script SucceedsPostAction.sh
LICENSE for details.
| Balance and Control | kept by | Mikhail Zhigulin |
| Source Code | written by | Mikhail Zhigulin |
| Documentation | prepared by | Mikhail Zhigulin |
| Product Approbation | tested by | Mikhail Zhigulin |
- Language support: Reverso
- Git clients: SmartGit and GitHub Desktop
© Mikhail A. Zhigulin of Novosibirsk.











