Skip to content

Commit 2b21f49

Browse files
committed
Finished readme + added Message.id
1 parent b7785f8 commit 2b21f49

File tree

4 files changed

+274
-15
lines changed

4 files changed

+274
-15
lines changed

Demo/Demo/AppDelegate.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
1313

1414
func application(_ application: UIApplication,
1515
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16-
17-
// Lumberjack
1816

17+
// Lumberjack
18+
1919
Lumberjack.buildAndRegister(loggerWithId: "custom") { logger in
2020

2121
logger.symbol = .just("📱")
@@ -43,7 +43,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
4343

4444
// Done
4545

46-
DEBUG("did finish launching")
46+
DEBUG("Hello, world!")
4747

4848
return true
4949

Demo/Demo/ViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class ViewController: UIViewController {
3030
category: "Instance",
3131
components: .defaultNoTimestamp
3232
))
33-
33+
3434
Lumberjack
3535
.anyMessagePublisher
3636
.sink { [weak self] message in

README.md

Lines changed: 267 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The easiest way to get started is by installing via Xcode. Just add Lumberjack a
2121

2222
If you're adding Lumberjack as a dependency of your own Swift package, just add a package entry to your dependencies.
2323

24-
```
24+
```swift
2525
.package(
2626
name: "Lumberjack",
2727
url: "https://github.com/mitchtreece/Lumberjack",
@@ -34,27 +34,283 @@ If you're adding Lumberjack as a dependency of your own Swift package, just add
3434
Lumberjack is a lightweight Swift logging library built to help cut (🪓) down on development and (more importantly) debugging time.
3535
Designed with customization & extensibility, Lumberjack can easily be integrated into any project / workflow.
3636

37-
### Get Started
37+
### Getting Started
38+
39+
The easiest way to get started with Lumberjack, is by using the default logger. By default, all global logging functions use this target. The following are all equivalent:
40+
41+
```swift
42+
// Global
43+
44+
DEBUG("Hello, world!")
45+
46+
// Logger
47+
48+
Logger
49+
.default
50+
.debug("Hello, world!")
51+
52+
// Lumberjack
53+
54+
Lumberjack
55+
.defaultLogger
56+
.debug("Hello, world!")
57+
58+
// Result
59+
60+
"⚪️ [DEBUG] 2023-06-30T15:06:37.099 Demo.AppDelegate::46 ➡️ Hello, world!"
61+
```
62+
63+
Lumberjack provides several global log-level functions,
64+
as-well-as equivalent logger instance counterparts:
3865

39-
TODO: Working with the default logger
66+
```swift
67+
LOG(...) logger.log(...)
68+
DEBUG(...) logger.debug(...)
69+
INFO(...) logger.info(...)
70+
NOTICE(...) logger.notice(...)
71+
WARN(...) logger.warning(...)
72+
ERROR(...) logger.error(...)
73+
FATAL(...) logger.fatal(...)
74+
```
75+
76+
Each one of these functions optionally takes in several arguments that can override / augment the output of logged messages. More on that later! 🙌🏼
77+
78+
### Global Settings & Configuration
79+
80+
The top-level `Lumberjack` object houses several global settings, as-well-as easy access to the default logger, custom loggers, associated configuration settings, & message publishers.
81+
82+
```swift
83+
Lumberjack
84+
.verbosity = .just(.error)
85+
86+
Lumberjack
87+
.defaultLogger
88+
.configuration
89+
.timestampFormat = "yyyy-MM-dd"
4090

41-
### Global Logging & Configuration
91+
Lumberjack
92+
.anyMessagePublisher
93+
.sink { print($0.body(formatted: false)) }
94+
.store(in: &self.bag)
95+
```
96+
97+
You're also not limited to just one "default" logger. Custom loggers can also be created:
98+
99+
```swift
100+
let logger = Logger(id: "custom")
42101

43-
TODO: Lumberjack global settings (i.e. verbosity) & loggers
44-
changing default settings, registering shared loggers, etc
102+
logger
103+
.configuration
104+
.symbol = .just("😎")
105+
106+
// -- or --
107+
108+
let logger = Logger(id: "custom") { make in
109+
make.symbol = .just("😎")
110+
}
111+
```
112+
113+
... and globally registered:
114+
115+
```swift
116+
Lumberjack
117+
.register(logger)
118+
119+
// -- or --
120+
121+
Lumberjack.buildAndRegister(loggerWithId: "custom") { make in
122+
make.symbol = .just("😎")
123+
}
124+
```
125+
126+
... and accessed:
127+
128+
```swift
129+
let logger = Logger
130+
.with(id: "custom")
131+
132+
// -- or --
133+
134+
let logger = Lumberjack
135+
.logger(id: "custom")
136+
```
45137

46138
### Loggers
47139

48-
TODO: Creating logger instances, high-level customization,
49-
and logging functions
140+
As shown above, logger instances can be created as-needed. However, sometimes you don't need a logger to be globally registered. For example, you might want a logger tied to a specific view / view-controller in your project.
141+
142+
```swift
143+
import UIKit
144+
import Lumberjack
145+
146+
class CustomViewController: UIViewController {
147+
148+
private var logger: Logger!
149+
150+
override func viewDidLoad() {
151+
152+
super.viewDidLoad()
153+
154+
self.logger = Logger { make in
155+
156+
make.symbol = .just("📱")
157+
make.category = "CustomView"
158+
make.components = .simple
159+
160+
}
161+
162+
}
163+
164+
override func viewWillAppear(_ animated: Bool) {
165+
166+
super.viewWillAppear(animated)
167+
168+
self.logger
169+
.debug("view will appear!")
170+
171+
}
50172

51-
### Customization
173+
}
174+
```
175+
176+
#### Customization
177+
178+
Loggers can be customized in several different ways. At their core, they use a component-based system to determine what kind of info gets logged to the console. For example, the default message component set consists of the following:
179+
180+
```swift
181+
[
182+
.level(
183+
.symbol,
184+
spacing: .trailing(1)
185+
),
186+
.level(
187+
.name,
188+
spacing: .trailing(1)
189+
),
190+
.category(spacing: .trailing(1)),
191+
.timestamp(spacing: .trailing(1)),
192+
.module(),
193+
.text("."),
194+
.file(),
195+
.text("::"),
196+
.line(spacing: .trailing(1)),
197+
.text("➡️"),
198+
.message(spacing: .leading(1))
199+
]
200+
```
201+
202+
This results in the following format:
203+
204+
```
205+
{symbol} [{level}] <{category}> {timestamp} {module}.{file}::{line} ➡️ {message}
206+
```
207+
208+
... and example message:
209+
210+
```swift
211+
"⚪️ [DEBUG] <Example> 2023-06-30T15:06:37.099 Demo.AppDelegate::46 ➡️ Hello, world!"
212+
```
52213

53-
TODO: Detailed logger customization
214+
The individual component values are determined based on the logger's configuration, as-well-as any passed in logging function overrides.
215+
216+
```swift
217+
let logger = Logger { make in
218+
219+
make.symbol = .just("📱")
220+
make.category = "Example"
221+
222+
}
223+
224+
logger
225+
.debug("no overrides")
226+
227+
// => "📱 [DEBUG] <Example> 2023-06-30T15:06:37.099 Demo.AppDelegate::46 ➡️ no overrides"
228+
229+
logger.info(
230+
"some overrides",
231+
symbol: "⭐️",
232+
category: "Star"
233+
)
234+
235+
// => "⭐️ [INFO] <Star> 2023-06-30T15:06:37.099 Demo.AppDelegate::46 ➡️ some overrides"
236+
237+
```
54238

55239
### Message Hooks
56240

57-
TODO: Message hooks, and the log chain
241+
Besides logging to the console, sometimes you might need to do additional work with messages (i.e. uploading to a server, writing to disk, etc) - or prevent them from being logged at all. That's where the message hook system comes into play! A hook that persists messages to disk might look something like this:
242+
243+
```swift
244+
import Lumberjack
245+
246+
struct SaveMessageHook: MessageHook {
247+
248+
func hook(_ message: Message) -> MessageHookResult {
249+
250+
save(message)
251+
return .next
252+
253+
}
254+
255+
private func save(_ message: Message) {
256+
257+
let url = FileManager
258+
.default
259+
.urls(
260+
for: .documentDirectory,
261+
in: .userDomainMask
262+
)
263+
.first!
264+
.appendingPathComponent("\(message.id).txt")
265+
266+
do {
267+
268+
try message
269+
.body(formatted: true)
270+
.write(
271+
to: url,
272+
atomically: true,
273+
encoding: .utf8
274+
)
275+
276+
}
277+
catch {
278+
print("Error writing message to disk: \(error)")
279+
}
280+
281+
}
282+
283+
}
284+
```
285+
286+
A message hook simply receives a message, does whatever tasks it needs to with it, then returns a result indicating if the message should be passed on to downstream hooks, or stop and prevent the message from being logged.
287+
288+
Lumberjack's internal logging system is even implemented using a message hook similiar to the following:
289+
290+
```swift
291+
import Lumberjack
292+
293+
struct PrintMessageHook: MessageHook {
294+
295+
func hook(_ message: Message) -> MessageHookResult {
296+
297+
if shouldPrint(message) {
298+
299+
print(message.body(formatted: true))
300+
return .next
301+
302+
}
303+
304+
return .stop
305+
306+
}
307+
308+
private func shouldPrint(_ message: Message) -> Bool {
309+
...
310+
}
311+
312+
}
313+
```
58314

59315
## Contributing
60316

Sources/Lumberjack/Message/Message.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public struct Message {
3636

3737
}
3838

39+
/// The message's identifier.
40+
public let id: String = UUID().uuidString
41+
3942
/// The message's parent logger identifier.
4043
public let loggerId: String
4144

0 commit comments

Comments
 (0)