Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature - Refactoring Modifiers and Writers #9

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Example/Frameworks/Database/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ extension Logger {

/// The single `Logger` instance used throughout Database.
public var log: Logger! = {
struct PrefixModifier: Modifier {
struct PrefixModifier: LogMessageModifier {
func modifyMessage(_ message: String, with: LogLevel) -> String {
return "[Database] => \(message)"
}
Expand All @@ -70,7 +70,7 @@ public var log: Logger! = {
let warnColorModifier = ColorModifier(foregroundColor: orange, backgroundColor: nil)
let errorColorModifier = ColorModifier(foregroundColor: red, backgroundColor: nil)

let modifiers: [LogLevel: [Modifier]] = [
let modifiers: [LogLevel: [LogMessageModifier]] = [
.sql: [prefixModifier, timestampModifier, sqlColorModifier],
.debug: [prefixModifier, timestampModifier, debugColorModifier],
.info: [prefixModifier, timestampModifier, infoColorModifier],
Expand Down
4 changes: 2 additions & 2 deletions Example/Frameworks/Network/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import Willow

/// The single `Logger` instance used throughout Network.
public var log: Logger = {
struct PrefixModifier: Modifier {
struct PrefixModifier: LogMessageModifier {
func modifyMessage(_ message: String, with: LogLevel) -> String {
return "[Network] => \(message)"
}
Expand All @@ -49,7 +49,7 @@ public var log: Logger = {
let warnColorModifier = ColorModifier(foregroundColor: orange, backgroundColor: nil)
let errorColorModifier = ColorModifier(foregroundColor: red, backgroundColor: nil)

let modifiers: [LogLevel: [Modifier]] = [
let modifiers: [LogLevel: [LogMessageModifier]] = [
.debug: [prefixModifier, timestampModifier, debugColorModifier],
.info: [prefixModifier, timestampModifier, infoColorModifier],
.event: [prefixModifier, timestampModifier, eventColorModifier],
Expand Down
16 changes: 8 additions & 8 deletions Example/iOS Example/WillowConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct WillowConfiguration {

// MARK: Modifiers

private struct PrefixModifier: Modifier {
private struct PrefixModifier: LogMessageModifier {
let prefix: String

init(prefix: String) {
Expand All @@ -46,13 +46,13 @@ struct WillowConfiguration {
}
}

private struct WarningPrefixModifier: Modifier {
private struct WarningPrefixModifier: LogMessageModifier {
func modifyMessage(_ message: String, with: LogLevel) -> String {
return "🚨🚨🚨 \(message)"
}
}

private struct ErrorPrefixModifier: Modifier {
private struct ErrorPrefixModifier: LogMessageModifier {
func modifyMessage(_ message: String, with: LogLevel) -> String {
return "💣💥💣💥 \(message)"
}
Expand All @@ -67,7 +67,7 @@ struct WillowConfiguration {
coloredOutputEnabled: Bool = true,
asynchronous: Bool = false)
{
let writers: [LogLevel: [Writer]] = [.all: [ConsoleWriter()]]
let writers: [LogLevel: [LogMessageWriter]] = [.all: [ConsoleWriter()]]
let executionMethod: LoggerConfiguration.ExecutionMethod

if asynchronous {
Expand Down Expand Up @@ -111,17 +111,17 @@ struct WillowConfiguration {
backgroundColor: UIColor? = nil,
prefix: String,
modifierLogLevel: LogLevel,
writers: [LogLevel: [Writer]],
writers: [LogLevel: [LogMessageWriter]],
executionMethod: LoggerConfiguration.ExecutionMethod)
-> Logger
{
let prefixModifier = PrefixModifier(prefix: prefix)
let timestampModifier = TimestampModifier()
let colorModifier = ColorModifier(foregroundColor: foregroundColor, backgroundColor: backgroundColor)

let modifiers: [LogLevel: [Modifier]] = {
let modifiers: [Modifier] = {
var modifiers: [Modifier] = [prefixModifier, timestampModifier]
let modifiers: [LogLevel: [LogMessageModifier]] = {
let modifiers: [LogMessageModifier] = {
var modifiers: [LogMessageModifier] = [prefixModifier, timestampModifier]
if foregroundColor != nil || backgroundColor != nil { modifiers.append(colorModifier) }
return modifiers
}()
Expand Down
60 changes: 30 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ Willow is a powerful, yet lightweight logging library written in Swift.
- [Synchronous and Asynchronous Logging](#synchronous-and-asynchronous-logging)
- [Synchronous Logging](#synchronous-logging)
- [Asynchronous Logging](#asynchronous-logging)
- [Modifiers](#modifiers)
- [Log Message Modifiers](#log-message-modifiers)
- [Color Modifiers](#color-modifiers)
- [Multiple Modifiers](#multiple-modifiers)
- [Writers](#writers)
- [Log Message Writers](#log-message-writers)
- [Multiple Writers](#multiple-writers)
- [Per LogLevel Writers](#per-loglevel-writers)
- [Advanced Usage](#advanced-usage)
Expand Down Expand Up @@ -241,28 +241,28 @@ Asynchronous logging should be used for deployment builds of your application or

> These are large generalizations about the typical use cases for one approach versus the other. Before making a final decision about which approach to use when, you should really break down your use case in detail.

### Modifiers
### Log Message Modifiers

Log message customization is something that `Willow` specializes in. Some devs want to add a prefix to their library output, some want different timestamp formats, some even want colors! There's no way to predict all the types of custom formatting teams are going to want to use. This is where `Modifier` objects come in.
Log message customization is something that `Willow` specializes in. Some devs want to add a prefix to their library output, some want different timestamp formats, some even want colors! There's no way to predict all the types of custom formatting teams are going to want to use. This is where `LogMessageModifier` objects come in.

```swift
public protocol Modifier {
public protocol LogMessageModifier {
func modifyMessage(_ message: String, with logLevel: LogLevel) -> String
}
```

The `Modifier` protocol has only a single API. It receives the `message` and `logLevel` and returns a newly formatted `String`. This is about as flexible as you can get. The `Logger` allows you to pass in your own `Modifier` objects and apply them to a `LogLevel`. Let's walk through a simple example for adding a prefix to only the `debug` and `info` log levels.
The `LogMessageModifier` protocol has only a single API. It receives the `message` and `logLevel` and returns a newly formatted `String`. This is about as flexible as you can get. The `Logger` allows you to pass in your own `LogMessageModifier` objects and apply them to a `LogLevel`. Let's walk through a simple example for adding a prefix to only the `debug` and `info` log levels.

```swift
class PrefixModifier: Modifier {
class PrefixModifier: LogMessageModifier {
func modifyMessage(_ message: String, with logLevel: Logger.LogLevel) -> String {
return "[Willow] \(message)"
}
}

let prefixModifier = PrefixModifier()

let modifiers: [Logger.LogLevel: [Modifier]] = [
let modifiers: [Logger.LogLevel: [LogMessageModifier]] = [
.debug: [prefixModifier],
.info: [prefixModifier]
]
Expand All @@ -271,11 +271,11 @@ let configuration = LoggerConfiguration(modifiers: modifiers)
let log = Logger(configuration: configuration)
```

`Modifier` objects are very powerful and can manipulate the message in any way.
`LogMessageModifier` objects are very powerful and can manipulate the message in any way.

#### Color Modifiers

There is a special `Modifier` in `Willow` called a `ColorModifier`. It was designed to take a foreground and backround color in the form of a `UIColor` or `NSColor`. It then formats the message to match the coloring scheme of the [XcodeColors](https://github.com/robbiehanson/XcodeColors) plugin. This allows you to change the foreground and background colors of logging output in the Xcode console. This can make it much easier to dig through thousands of lines of logging output.
There is a special `LogMessageModifier` in `Willow` called a `ColorModifier`. It was designed to take a foreground and backround color in the form of a `UIColor` or `NSColor`. It then formats the message to match the coloring scheme of the [XcodeColors](https://github.com/robbiehanson/XcodeColors) plugin. This allows you to change the foreground and background colors of logging output in the Xcode console. This can make it much easier to dig through thousands of lines of logging output.

```swift
let purple = UIColor.purple()
Expand All @@ -286,7 +286,7 @@ let red = UIColor.red()
let white = UIColor.white()
let black = UIColor.black()

let colorModifiers: [Logger.LogLevel: [Modifier]] = [
let colorModifiers: [Logger.LogLevel: [LogMessageModifier]] = [
LogLevel.debug: [ColorModifier(foregroundColor: purple, backgroundColor: nil)],
LogLevel.info: [ColorModifier(foregroundColor: blue, backgroundColor: nil)],
LogLevel.event: [ColorModifier(foregroundColor: green, backgroundColor: nil)],
Expand All @@ -302,7 +302,7 @@ let log = Logger(configuration: configuration)

#### Multiple Modifiers

Multiple `Modifier` objects can be stacked together onto a single log level to perform multiple actions. Let's walk through using the `TimestampModifier` (prefixes the message with a timestamp) in combination with the `ColorModifier` objects from the previous example.
Multiple `LogMessageModifier` objects can be stacked together onto a single log level to perform multiple actions. Let's walk through using the `TimestampModifier` (prefixes the message with a timestamp) in combination with the `ColorModifier` objects from the previous example.

```swift
let purple = UIColor.purple()
Expand All @@ -315,7 +315,7 @@ let black = UIColor.black()

let timestampModifier = TimestampModifier()

let modifiers: [Logger.LogLevel: [Modifier]] = [
let modifiers: [Logger.LogLevel: [LogMessageModifier]] = [
LogLevel.debug: [timestampModifier, ColorModifier(foregroundColor: purple, backgroundColor: nil)],
LogLevel.info: [timestampModifier, ColorModifier(foregroundColor: blue, backgroundColor: nil)],
LogLevel.event: [timestampModifier, ColorModifier(foregroundColor: green, backgroundColor: nil)],
Expand All @@ -327,25 +327,25 @@ let configuration = LoggerConfiguration(modifiers: modifiers)
let log = Logger(configuration: configuration)
```

`Willow` doesn't have any hard limits on the total number of `Modifier` objects that can be applied to a single log level. Just keep in mind that performance is key.
`Willow` doesn't have any hard limits on the total number of `LogMessageModifier` objects that can be applied to a single log level. Just keep in mind that performance is key.

> The default `ConsoleWriter` will execute the modifiers in the same order they were added into the `Array`. In the previous example, Willow would log a much different message if the `ColorModifier` was inserted before the `TimestampModifier`.

### Writers
### Log Message Writers

Writing log messages to various locations is an essential feature of any robust logging library. This is made possible in `Willow` through the `Writer` protocol.
Writing log messages to various locations is an essential feature of any robust logging library. This is made possible in `Willow` through the `LogMessageWriter` protocol.

```swift
public protocol Writer {
func writeMessage(_ message: String, logLevel: LogLevel, modifiers: [Modifier]?)
public protocol LogMessageWriter {
func writeMessage(_ message: String, logLevel: LogLevel, modifiers: [LogMessageModifier]?)
}
```

Again, this is an extremely lightweight design to allow for ultimate flexibility. As long as your `Writer` classes conform, you can do anything with those log messages that you want. You could write the message to the console, append it to a file, send it to a server, etc. Here's a quick look at the implementation of the default `ConsoleWriter` created by the `Logger` if you don't specify your own.
Again, this is an extremely lightweight design to allow for ultimate flexibility. As long as your `LogMessageWriter` classes conform, you can do anything with those log messages that you want. You could write the message to the console, append it to a file, send it to a server, etc. Here's a quick look at the implementation of the default `ConsoleWriter` created by the `Logger` if you don't specify your own.

```swift
public class ConsoleWriter: Writer {
public func writeMessage(_ message: String, logLevel: LogLevel, formatters: [Formatter]?) {
public class ConsoleWriter: LogMessageWriter {
public func writeMessage(_ message: String, logLevel: LogLevel, modifiers: [LogMessageModifier]?) {
var mutableMessage = message
modifiers?.map { mutableMessage = $0.modifyMessage(mutableMessage, with: logLevel) }
print(mutableMessage)
Expand All @@ -355,31 +355,31 @@ public class ConsoleWriter: Writer {

#### Multiple Writers

So what about logging to both a file and the console at the same time? No problem. You can pass multiple `Writer` objects into the `Logger` initializer. The `Logger` will execute each `Writer` in the order it was passed in. For example, let's create a `FileWriter` and combine that with our `ConsoleWriter`.
So what about logging to both a file and the console at the same time? No problem. You can pass multiple `LogMessageWriter` objects into the `Logger` initializer. The `Logger` will execute each `LogMessageWriter` in the order it was passed in. For example, let's create a `FileWriter` and combine that with our `ConsoleWriter`.

```swift
public class FileWriter: Writer {
public func writeMessage(_ message: String, logLevel: Logger.LogLevel, modifiers: [Modifier]?) {
public class FileWriter: LogMessageWriter {
public func writeMessage(_ message: String, logLevel: Logger.LogLevel, modifiers: [LogMessageModifier]?) {
var mutableMessage = message
modifiers?.map { mutableMessage = $0.modifyMessage(mutableMessage, with: logLevel) }
// Write the formatted message to a file (I'll leave this to you!)
}
}

let writers: [LogLevel: Writer] = [.all: [FileWriter(), ConsoleWriter()]]
let writers: [LogLevel: LogMessageWriter] = [.all: [FileWriter(), ConsoleWriter()]]

let configuration = LoggerConfiguration(writers: writers)
let log = Logger(configuration: configuration)
```

> `Writer` objects can also be selective about which modifiers they want to run for a particular log level. All the examples run all the modifiers, but you can be selective if you want to be.
> `LogMessageWriter` objects can also be selective about which modifiers they want to run for a particular log level. All the examples run all the modifiers, but you can be selective if you want to be.

#### Per LogLevel Writers

It is also possible to specify different combinations of `Writer` objects for each `LogLevel`. Let's say we want to log `.warn` and `.error` messages to the console, and we want to log all messages to a file writer.
It is also possible to specify different combinations of `LogMessageWriter` objects for each `LogLevel`. Let's say we want to log `.warn` and `.error` messages to the console, and we want to log all messages to a file writer.

```swift
let writers: [LogLevel: Writer] = [
let writers: [LogLevel: LogMessageWriter] = [
.all: [FileWriter()],
[.warn, .error]: [ConsoleWriter()]
]
Expand Down Expand Up @@ -440,7 +440,7 @@ public var log = Logger(configuration: LoggerConfiguration(writers: [.warn, .err
//=========== Calculator.swift ===========
import Math

let writers = [.all: [FileWriter(), ConsoleWriter()]]
let writers: [LogLevel: [LogMessageWriter]] = [.all: [FileWriter(), ConsoleWriter()]]
var log = Logger(configuration: LoggerConfiguration(writers: writers))

// Replace the Math.log with the Calculator.log to share the same Logger instance
Expand All @@ -464,7 +464,7 @@ import Math
let sharedQueue = DispatchQueue(label: "com.math.logger", attributes: [.serial, .qosUtility])

// Create the Calculator.log with multiple writers and a .Debug log level
let writers = [.all: [FileWriter(), ConsoleWriter()]]
let writers: [LogLevel: [LogMessageWriter]] = [.all: [FileWriter(), ConsoleWriter()]]
let configuration = LoggerConfiguration(
writers: writers,
executionMethod: .Asynchronous(queue: sharedQueue)
Expand Down
12 changes: 6 additions & 6 deletions Source/Modifier.swift → Source/LogMessageModifier.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Modifier.swift
// LogMessageModifier.swift
//
// Copyright (c) 2015-2016 Nike, Inc. (https://www.nike.com)
//
Expand Down Expand Up @@ -32,16 +32,16 @@ import Cocoa
public typealias Color = NSColor
#endif

/// The Modifier protocol defines a single method for modifying a message after it has been constructed. This is
/// very flexible allowing any object that conforms to modify messages in any way it wants.
public protocol Modifier {
/// The LogMessageModifier protocol defines a single method for modifying a log message after it has been constructed.
/// This is very flexible allowing any object that conforms to modify messages in any way it wants.
public protocol LogMessageModifier {
func modifyMessage(_ message: String, with logLevel: LogLevel) -> String
}

// MARK:

/// The TimestampModifier class applies a timestamp to the beginning of the message.
public class TimestampModifier: Modifier {
public class TimestampModifier: LogMessageModifier {
private let timestampFormatter: DateFormatter = {
var formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
Expand Down Expand Up @@ -71,7 +71,7 @@ public class TimestampModifier: Modifier {
/// XcodeColors plugin color formatting scheme.
///
/// NOTE: These should only be used with the XcodeColors plugin.
public class ColorModifier: Modifier {
public class ColorModifier: LogMessageModifier {

// MARK: Helper Types

Expand Down
14 changes: 7 additions & 7 deletions Source/Writer.swift → Source/LogMessageWriter.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Writer.swift
// LogMessageWriter.swift
//
// Copyright (c) 2015-2016 Nike, Inc. (https://www.nike.com)
//
Expand All @@ -24,18 +24,18 @@

import Foundation

/// The Writer protocol defines a single API for writing a message. The message can be written in any way the
/// conforming object sees fit. For example, it could write to the console, write to a file, remote log to a third
/// The LogMessageWriter protocol defines a single API for writing a log message. The message can be written in any way
/// the conforming object sees fit. For example, it could write to the console, write to a file, remote log to a third
/// party service, etc.
public protocol Writer {
func writeMessage(_ message: String, logLevel: LogLevel, modifiers: [Modifier]?)
public protocol LogMessageWriter {
func writeMessage(_ message: String, logLevel: LogLevel, modifiers: [LogMessageModifier]?)
}

// MARK:

/// The ConsoleWriter class runs all modifiers in the order they were created and prints the resulting message
/// to the console.
public class ConsoleWriter: Writer {
public class ConsoleWriter: LogMessageWriter {
/// Initializes a console writer instance.
///
/// - returns: A new console writer instance.
Expand All @@ -49,7 +49,7 @@ public class ConsoleWriter: Writer {
/// - parameter message: The original message to write to the console.
/// - parameter logLevel: The log level associated with the message.
/// - parameter modifiers: The modifier objects to run over the message before writing to the console.
public func writeMessage(_ message: String, logLevel: LogLevel, modifiers: [Modifier]?) {
public func writeMessage(_ message: String, logLevel: LogLevel, modifiers: [LogMessageModifier]?) {
var mutableMessage = message
modifiers?.forEach { mutableMessage = $0.modifyMessage(mutableMessage, with: logLevel) }

Expand Down
Loading