The markdown parsing is broken/disabled for release notes. Sorry about that, I'm chasing the source of a crash that's been bringing this website down for the last couple of days.
# New Protocols
```swift
public protocol ChronicleFormatter {
var dateFormatter: DateFormatter { get }
func format(label: String) -> String
func format(logLevel: Chronicle.LogLevel) -> String
func output(
formattedDate: String,
formattedLabel: String,
formattedLogMessage: String
) -> String
}
public protocol ChronicleHandler {
func handle(output: String) -> Void
func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel)
}
```
## Default implementations
### Formatter
```swift
extension Chronicle {
public struct DefaultFormatters {
public struct DefaultFormatter: ChronicleFormatter {
public init() { }
public var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .long
return formatter
}()
public func format(label: String) -> String { "[\(label)]" }
public func format(logLevel: Chronicle.LogLevel) -> String {
"\(logLevel.emoji): \(logLevel.output)"
}
public func output(
formattedDate: String,
formattedLabel: String,
formattedLogMessage: String
) -> String {
formattedDate + " " + formattedLabel + " " + formattedLogMessage
}
}
}
}
```
### Handlers
```swift
extension Chronicle {
public struct DefaultHandlers {
public struct PrintHandler: ChronicleHandler {
public init() { }
public func handle(output: String) { print(output) }
public func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel) { }
}
public struct FileHandler: ChronicleHandler {
private let lock = NSLock()
public var fileURL: URL {
FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)[0]
.appendingPathComponent(fileName)
}
public var fileName: String
public init(fileName: String = "chronicle.log") {
self.fileName = fileName
}
public func handle(output: String) {
var fileOutput = ""
lock.lock()
defer {
fileOutput.append(output)
do {
try fileOutput.write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
} catch {
dump(error)
}
lock.unlock()
}
guard let contents = try? Data(contentsOf: fileURL) else {
return
}
let fileContents = String(data: contents, encoding: .utf8)
if let fileContents = fileContents {
fileOutput.append(fileContents + "\n")
}
}
public func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel) { }
}
}
}
```
# New Protocols
```swift
public protocol ChronicleFormatter {
var dateFormatter: DateFormatter { get }
func format(label: String) -> String
func format(logLevel: Chronicle.LogLevel) -> String
func output(
formattedDate: String,
formattedLabel: String,
formattedLogMessage: String
) -> String
}
public protocol ChronicleHandler {
func handle(output: String) -> Void
func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel)
}
```
## Default implementations
### Formatter
```swift
extension Chronicle {
public struct DefaultFormatters {
public struct DefaultFormatter: ChronicleFormatter {
public init() { }
public var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .long
return formatter
}()
public func format(label: String) -> String { "[\(label)]" }
public func format(logLevel: Chronicle.LogLevel) -> String {
"\(logLevel.emoji): \(logLevel.output)"
}
public func output(
formattedDate: String,
formattedLabel: String,
formattedLogMessage: String
) -> String {
formattedDate + " " + formattedLabel + " " + formattedLogMessage
}
}
}
}
```
### Handlers
```swift
extension Chronicle {
public struct DefaultHandlers {
public struct PrintHandler: ChronicleHandler {
public init() { }
public func handle(output: String) { print(output) }
public func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel) { }
}
public struct FileHandler: ChronicleHandler {
public var fileURL: URL {
FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)[0]
.appendingPathComponent(fileName)
}
public var fileName: String
public init(fileName: String = "chronicle.log") {
self.fileName = fileName
}
public func handle(output: String) {
var fileOutput = ""
defer {
fileOutput.append(output)
do {
try fileOutput.write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
} catch {
dump(error)
}
}
guard let contents = try? Data(contentsOf: fileURL) else {
return
}
let fileContents = String(data: contents, encoding: .utf8)
if let fileContents = fileContents {
fileOutput.append(fileContents + "\n")
}
}
public func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel) { }
}
}
}
```
ChronicleFormatter and ChronicleHandler
3 years ago
# New Protocols
```swift
public protocol ChronicleFormatter {
var dateFormatter: DateFormatter { get }
func format(label: String) -> String
func format(logLevel: Chronicle.LogLevel) -> String
func output(
formattedDate: String,
formattedLabel: String,
formattedLogMessage: String
) -> String
}
public protocol ChronicleHandler {
func handle(output: String) -> Void
func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel)
}
```
## Default implementations
### Formatter
```swift
extension Chronicle {
public struct DefaultFormatters {
public struct DefaultFormatter: ChronicleFormatter {
public init() { }
public var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .long
return formatter
}()
public func format(label: String) -> String { "[\(label)]" }
public func format(logLevel: Chronicle.LogLevel) -> String {
"\(logLevel.emoji): \(logLevel.output)"
}
public func output(
formattedDate: String,
formattedLabel: String,
formattedLogMessage: String
) -> String {
formattedDate + " " + formattedLabel + " " + formattedLogMessage
}
}
}
}
```
### Handlers
```swift
extension Chronicle {
public struct DefaultHandlers {
public struct PrintHandler: ChronicleHandler {
public init() { }
public func handle(output: String) { print(output) }
public func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel) { }
}
public struct FileHandler: ChronicleHandler {
public static var fileURL: URL {
FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)[0]
.appendingPathComponent(fileName)
}
public static var fileName: String = "chronicle.log"
public init() { }
public func handle(output: String) {
var fileOutput = ""
defer {
fileOutput.append(output)
do {
try output.write(to: FileHandler.fileURL, atomically: true, encoding: String.Encoding.utf8)
} catch {
dump(error)
}
}
guard let contents = try? Data(contentsOf: FileHandler.fileURL) else {
return
}
let fileContents = String(data: contents, encoding: .utf8)
if let fileContents = fileContents {
fileOutput.append(fileContents + "\n")
}
}
public func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel) { }
}
}
}
```
ChronicleFormatter and ChronicleHandler
3 years ago
# New Protocols
```swift
public protocol ChronicleFormatter {
var dateFormatter: DateFormatter { get }
func format(label: String) -> String
func format(logLevel: Chronicle.LogLevel) -> String
func output(
formattedDate: String,
formattedLabel: String,
formattedLogMessage: String
) -> String
}
public protocol ChronicleHandler {
func handle(output: String) -> Void
func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel)
}
```
## Default implementations
### Formatter
```swift
extension Chronicle {
public struct DefaultFormatters {
public struct DefaultFormatter: ChronicleFormatter {
public init() { }
public var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .long
return formatter
}()
public func format(label: String) -> String { "[\(label)]" }
public func format(logLevel: Chronicle.LogLevel) -> String {
"\(logLevel.emoji): \(logLevel.output)"
}
public func output(
formattedDate: String,
formattedLabel: String,
formattedLogMessage: String
) -> String {
formattedDate + " " + formattedLabel + " " + formattedLogMessage
}
}
}
}
```
### Handlers
```swift
extension Chronicle {
public struct DefaultHandlers {
public struct PrintHandler: ChronicleHandler {
public init() { }
public func handle(output: String) { print(output) }
public func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel) { }
}
public struct FileHandler: ChronicleHandler {
private static var fileURL: URL {
FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)[0]
.appendingPathComponent(fileName)
}
public static var fileName: String = "chronicle.log"
public init() { }
public func handle(output: String) {
var fileOutput = ""
defer {
fileOutput.append(output)
do {
try output.write(to: FileHandler.fileURL, atomically: true, encoding: String.Encoding.utf8)
} catch {
dump(error)
}
}
guard let contents = try? Data(contentsOf: FileHandler.fileURL) else {
return
}
let fileContents = String(data: contents, encoding: .utf8)
if let fileContents = fileContents {
fileOutput.append(fileContents + "\n")
}
}
public func didHandle(chronicle: Chronicle, level: Chronicle.LogLevel) { }
}
}
}
```
# Chronicle
*Simple Swift Logger in under 90 loc*
## [Log ๐ชต Message Format](https://github.com/0xLeif/Chronicle/blob/546de4daa3fa150abf5fb430048a3e02adee5b92/Sources/Chronicle/Chronicle.swift#L80)
**`{Date} {Label} {Emoji}: {Message}`**
### Default Format
4/5/21, 7:05:42 PM CDT [com.example.chronicle] โน๏ธ: Info
```
Date = 4/5/21, 7:05:42 PM CDT
Label = [com.example.chronicle]
Emoji = โน๏ธ
Message = Info
```
## Examples
### Default Chronicle
```swift
let chrono = Chronicle(
label: "com.example.chronicle"
)
enum SomeError: Error { case abc }
chrono.log(level: .success("Success"))
chrono.log(level: .info("Info"))
chrono.log(level: .warning("Warning"))
chrono.log(level: .error("Error", SomeError.abc))
chrono.log(level: .fatal("Fatal", SomeError.abc))
```
**Logging**
```
4/5/21, 7:05:42 PM CDT [com.example.chronicle] โ
: Success
4/5/21, 7:05:42 PM CDT [com.example.chronicle] โน๏ธ: Info
4/5/21, 7:05:42 PM CDT [com.example.chronicle] โ ๏ธ: Warning
4/5/21, 7:05:42 PM CDT [com.example.chronicle] โ๏ธ: Error
{
abc: The operation couldnโt be completed. (ChronicleTests.ChronicleTests.(unknown context at $107087918).(unknown context at $107087964).SomeError error 0.)
}
4/5/21, 7:05:42 PM CDT [com.example.chronicle] ๐จ: Fatal
{
abc: The operation couldnโt be completed. (ChronicleTests.ChronicleTests.(unknown context at $107087918).(unknown context at $107087964).SomeError error 0.)
}
```
### DateFormatter Chronicle
```swift
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .none
dateFormatter.dateStyle = .full
let chrono = Chronicle(
label: "com.example.chronicle",
dateFormatter: dateFormatter
)
enum SomeError: Error { case abc }
chrono.log(level: .success("Success"))
chrono.log(level: .info("Info"))
chrono.log(level: .warning("Warning"))
chrono.log(level: .error("Error", SomeError.abc))
chrono.log(level: .fatal("Fatal", SomeError.abc))
```
**Logging**
```
Monday, April 5, 2021 [com.example.chronicle] โ
: Success
Monday, April 5, 2021 [com.example.chronicle] โน๏ธ: Info
Monday, April 5, 2021 [com.example.chronicle] โ ๏ธ: Warning
Monday, April 5, 2021 [com.example.chronicle] โ๏ธ: Error
{
abc: The operation couldnโt be completed. (ChronicleTests.ChronicleTests.(unknown context at $100f876d8).(unknown context at $100f87724).SomeError error 0.)
}
Monday, April 5, 2021 [com.example.chronicle] ๐จ: Fatal
{
abc: The operation couldnโt be completed. (ChronicleTests.ChronicleTests.(unknown context at $100f876d8).(unknown context at $100f87724).SomeError error 0.)
}
```
### LabelFormatter Chronicle
```swift
let chrono = Chronicle(
label: "com.example.chronicle",
labelFormatter: { "๐ \($0) ๐" }
)
enum SomeError: Error { case abc }
chrono.log(level: .success("Success"))
chrono.log(level: .info("Info"))
chrono.log(level: .warning("Warning"))
chrono.log(level: .error("Error", SomeError.abc))
chrono.log(level: .fatal("Fatal", SomeError.abc))
```
**Logging**
```
4/5/21, 7:27:47 PM CDT ๐ com.example.chronicle ๐ โ
: Success
4/5/21, 7:27:47 PM CDT ๐ com.example.chronicle ๐ โน๏ธ: Info
4/5/21, 7:27:47 PM CDT ๐ com.example.chronicle ๐ โ ๏ธ: Warning
4/5/21, 7:27:47 PM CDT ๐ com.example.chronicle ๐ โ๏ธ: Error
{
abc: The operation couldnโt be completed. (ChronicleTests.ChronicleTests.(unknown context at $100f87918).(unknown context at $100f87964).SomeError error 0.)
}
4/5/21, 7:27:47 PM CDT ๐ com.example.chronicle ๐ ๐จ: Fatal
{
abc: The operation couldnโt be completed. (ChronicleTests.ChronicleTests.(unknown context at $100f87918).(unknown context at $100f87964).SomeError error 0.)
}
```
### OutputFormatter Chronicle
```swift
let chrono = Chronicle(
label: "com.example.chronicle",
outputFormatter: { $0.emoji + " " + $0.output + " " + $0.emoji }
)
enum SomeError: Error { case abc }
chrono.log(level: .success("Success"))
chrono.log(level: .info("Info"))
chrono.log(level: .warning("Warning"))
chrono.log(level: .error("Error", SomeError.abc))
chrono.log(level: .fatal("Fatal", SomeError.abc))
```
**Logging**
```
4/5/21, 7:29:21 PM CDT [com.example.chronicle] โ
: โ
Success โ
4/5/21, 7:29:21 PM CDT [com.example.chronicle] โน๏ธ: โน๏ธ Info โน๏ธ
4/5/21, 7:29:21 PM CDT [com.example.chronicle] โ ๏ธ: โ ๏ธ Warning โ ๏ธ
4/5/21, 7:29:21 PM CDT [com.example.chronicle] โ๏ธ: โ๏ธ Error
{
abc: The operation couldnโt be completed. (ChronicleTests.ChronicleTests.(unknown context at $107087918).(unknown context at $107087964).SomeError error 0.)
} โ๏ธ
4/5/21, 7:29:21 PM CDT [com.example.chronicle] ๐จ: ๐จ Fatal
{
abc: The operation couldnโt be completed. (ChronicleTests.ChronicleTests.(unknown context at $107087918).(unknown context at $107087964).SomeError error 0.)
} ๐จ
```
# Chronical
*Simple Swift Logger*
## Examples
### Default Chronical
```swift
let chrono = Chronical(
label: "com.example.chronical"
)
enum SomeError: Error { case abc }
chrono.log(level: .success("Success"))
chrono.log(level: .info("Info"))
chrono.log(level: .warning("Warning"))
chrono.log(level: .error("Error", SomeError.abc))
chrono.log(level: .fatal("Fatal", SomeError.abc))
```
**Logging**
```
4/5/21, 7:05:42 PM CDT [com.example.chronical] โ
: Success
4/5/21, 7:05:42 PM CDT [com.example.chronical] โน๏ธ: Info
4/5/21, 7:05:42 PM CDT [com.example.chronical] โ ๏ธ: Warning
4/5/21, 7:05:42 PM CDT [com.example.chronical] โ๏ธ: Error
{
abc: The operation couldnโt be completed. (ChronicalTests.ChronicalTests.(unknown context at $107087918).(unknown context at $107087964).SomeError error 0.)
}
4/5/21, 7:05:42 PM CDT [com.example.chronical] ๐จ: Fatal
{
abc: The operation couldnโt be completed. (ChronicalTests.ChronicalTests.(unknown context at $107087918).(unknown context at $107087964).SomeError error 0.)
}
```
### DateFormatter Chronical
```swift
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .none
dateFormatter.dateStyle = .full
let chrono = Chronical(
label: "com.example.chronical",
dateFormatter: dateFormatter
)
enum SomeError: Error { case abc }
chrono.log(level: .success("Success"))
chrono.log(level: .info("Info"))
chrono.log(level: .warning("Warning"))
chrono.log(level: .error("Error", SomeError.abc))
chrono.log(level: .fatal("Fatal", SomeError.abc))
```
**Logging**
```
Monday, April 5, 2021 [com.example.chronical] โ
: Success
Monday, April 5, 2021 [com.example.chronical] โน๏ธ: Info
Monday, April 5, 2021 [com.example.chronical] โ ๏ธ: Warning
Monday, April 5, 2021 [com.example.chronical] โ๏ธ: Error
{
abc: The operation couldnโt be completed. (ChronicalTests.ChronicalTests.(unknown context at $100f876d8).(unknown context at $100f87724).SomeError error 0.)
}
Monday, April 5, 2021 [com.example.chronical] ๐จ: Fatal
{
abc: The operation couldnโt be completed. (ChronicalTests.ChronicalTests.(unknown context at $100f876d8).(unknown context at $100f87724).SomeError error 0.)
}
```
### LabelFormatter Chronical
```swift
let chrono = Chronical(
label: "com.example.chronical",
labelFormatter: { "๐ \($0) ๐" }
)
enum SomeError: Error { case abc }
chrono.log(level: .success("Success"))
chrono.log(level: .info("Info"))
chrono.log(level: .warning("Warning"))
chrono.log(level: .error("Error", SomeError.abc))
chrono.log(level: .fatal("Fatal", SomeError.abc))
```
**Logging**
```
4/5/21, 7:27:47 PM CDT ๐ com.example.chronical ๐ โ
: Success
4/5/21, 7:27:47 PM CDT ๐ com.example.chronical ๐ โน๏ธ: Info
4/5/21, 7:27:47 PM CDT ๐ com.example.chronical ๐ โ ๏ธ: Warning
4/5/21, 7:27:47 PM CDT ๐ com.example.chronical ๐ โ๏ธ: Error
{
abc: The operation couldnโt be completed. (ChronicalTests.ChronicalTests.(unknown context at $100f87918).(unknown context at $100f87964).SomeError error 0.)
}
4/5/21, 7:27:47 PM CDT ๐ com.example.chronical ๐ ๐จ: Fatal
{
abc: The operation couldnโt be completed. (ChronicalTests.ChronicalTests.(unknown context at $100f87918).(unknown context at $100f87964).SomeError error 0.)
}
```
### OutputFormatter Chronical
```swift
let chrono = Chronical(
label: "com.example.chronical",
outputFormatter: { $0.emoji + " " + $0.output + " " + $0.emoji }
)
enum SomeError: Error { case abc }
chrono.log(level: .success("Success"))
chrono.log(level: .info("Info"))
chrono.log(level: .warning("Warning"))
chrono.log(level: .error("Error", SomeError.abc))
chrono.log(level: .fatal("Fatal", SomeError.abc))
```
**Logging**
```
4/5/21, 7:29:21 PM CDT [com.example.chronical] โ
: โ
Success โ
4/5/21, 7:29:21 PM CDT [com.example.chronical] โน๏ธ: โน๏ธ Info โน๏ธ
4/5/21, 7:29:21 PM CDT [com.example.chronical] โ ๏ธ: โ ๏ธ Warning โ ๏ธ
4/5/21, 7:29:21 PM CDT [com.example.chronical] โ๏ธ: โ๏ธ Error
{
abc: The operation couldnโt be completed. (ChronicalTests.ChronicalTests.(unknown context at $107087918).(unknown context at $107087964).SomeError error 0.)
} โ๏ธ
4/5/21, 7:29:21 PM CDT [com.example.chronical] ๐จ: ๐จ Fatal
{
abc: The operation couldnโt be completed. (ChronicalTests.ChronicalTests.(unknown context at $107087918).(unknown context at $107087964).SomeError error 0.)
} ๐จ
```