Skip to content

Commit

Permalink
Merge pull request #15 from appunite/feature_decorated_levels
Browse files Browse the repository at this point in the history
Feature decorated levels
  • Loading branch information
lewandowskit93 authored Jan 24, 2020
2 parents 16ec81e + 4c3d5aa commit 172ac87
Show file tree
Hide file tree
Showing 21 changed files with 544 additions and 221 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Add colored formatter
- Add decorated formatter
- Add decorator for level color changing
- Add decorator for level emoji prefixes

## [0.1.0]
- Add .travis.yml for CI support
Expand Down
7 changes: 5 additions & 2 deletions Example/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import Spy
public struct Environment {
public static var spy: AnySpy<SpyLevel, SpyChannel> = {
return CompositeSpy()
.add(spy: ConsoleSpy<SpyLevel, SpyChannel, RawSpyFormatter>(
spyFormatter: RawSpyFormatter(),
.add(spy: ConsoleSpy<SpyLevel, SpyChannel, DecoratedSpyFormatter>(
spyFormatter: DecoratedSpyFormatter(
levelNameBuilder: DecoratedLevelNameBuilder<SpyLevel>()
.add(decorator: EmojiPrefixedSpyLevelNameDecorator().toAnyDecorator())
),
timestampProvider: CurrentTimestampProvider(),
configuration: SpyConfigurationBuilder()
.add(levels: SpyLevel.levelsFrom(loggingLevel))
Expand Down
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,32 @@ Logging is performed with **log** method as follows:
spy.log(level: .severe, channel: .foo, message: "Something bad happened")
```

#### ConsoleSpy
ConsoleSpy comes with two available output formatters **RawSpyFormatter** and **DecoratedSpyFormatter** with the later being extendable with decorators. You can always define your own output formatter.
Example output for *RawSpyFormatter* will look like:
```
info::foo::Hello Spy
```
And example output for *DecoratedSpyFormatter* may look like:
```
ℹ️ info::foo::Hello Spy
```
<p align="center">
<img src="resources/log.png" alt="Log example"/>
</p>

## Example
This is an example definition of the spies.
It utilizes *CompositeSpy* to allow you to log onto multiple destinations (*Console* and *Network*). Please note that *ConsoleSpy* is shipped with the *Spy* and *NetworkSpy* is not.
```swift
public struct Environment {
public static var spy: AnySpy<SpyLevel, SpyChannel> = {
return CompositeSpy()
.add(spy: ConsoleSpy<SpyLevel, SpyChannel, RawSpyFormatter>(
spyFormatter: RawSpyFormatter(),
.add(spy: ConsoleSpy<SpyLevel, SpyChannel, DecoratedSpyFormatter>(
spyFormatter: DecoratedSpyFormatter(
levelNameBuilder: DecoratedLevelNameBuilder<SpyLevel>()
.add(decorator: EmojiPrefixedSpyLevelNameDecorator().toAnyDecorator())
),
timestampProvider: CurrentTimestampProvider(),
configuration: SpyConfigurationBuilder()
.add(levels: SpyLevel.levelsFrom(loggingLevel))
Expand Down
400 changes: 245 additions & 155 deletions Spy.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

19 changes: 0 additions & 19 deletions Spy/Common/ColoredSpyFormatter.swift

This file was deleted.

38 changes: 38 additions & 0 deletions Spy/Common/Formatters/Decorated/AnyLevelDecorator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// AnyLevelDecorator.swift
// Spy
//
// Created by Tomasz Lewandowski on 24/01/2020.
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

public final class AnyLevelDecorator<Level: PSpyLevel>: PSpyLevelDecorator {
private let decorator: _AnyLevelDecoratorBase<Level>

public init<Decorator: PSpyLevelDecorator>(_ decorator: Decorator) where Decorator.Level == Level {
self.decorator = _AnyLevelDecoratorBox(decorator)
}

public func decorate(level: Level, value: String) -> String {
return decorator.decorate(level: level, value: value)
}
}

private final class _AnyLevelDecoratorBox<Decorator: PSpyLevelDecorator>: _AnyLevelDecoratorBase<Decorator.Level> {
let decorator: Decorator

init(_ decorator: Decorator) {
self.decorator = decorator
super.init()
}

override func decorate(level: Level, value: String) -> String {
return decorator.decorate(level: level, value: value)
}
}

private class _AnyLevelDecoratorBase<Level: PSpyLevel>: PSpyLevelDecorator {
func decorate(level: Level, value: String) -> String {
fatalError("Must be overriden")
}
}
33 changes: 33 additions & 0 deletions Spy/Common/Formatters/Decorated/ColoredSpyLevelNameDecorator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// ColoredSpyLevelNameDecorator.swift
// Spy
//
// Created by Tomasz Lewandowski on 24/01/2020.
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

public final class ColoredSpyLevelNameDecorator: PSpyLevelDecorator {
public typealias Level = SpyLevel

public init() {

}

public func decorate(level: SpyLevel, value: String) -> String {
return value.colored(with: level.color)
}
}

private extension SpyLevel {
var color: SpyColor {
switch self {
case .finest: return .white
case .finer: return .cyan
case .fine: return .blue
case .config: return .magenta
case .info: return .green
case .warning: return .yellow
case .severe: return .red
}
}
}
28 changes: 28 additions & 0 deletions Spy/Common/Formatters/Decorated/DecoratedLevelBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// DecoratedLevelNameBuilder.swift
// Spy
//
// Created by Tomasz Lewandowski on 24/01/2020.
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

public final class DecoratedLevelNameBuilder<Level: PSpyLevel> {
private var decorators: [AnyLevelDecorator<Level>] = []

public init() {
add(decorator: PlainLevelNameDecorator().toAnyDecorator())
}

@discardableResult public func add(decorator: AnyLevelDecorator<Level>) -> DecoratedLevelNameBuilder<Level> {
decorators.append(decorator)
return self
}

public func build(withLevel level: Level) -> String {
var decoratedLevelName = ""
for decorator in decorators {
decoratedLevelName = decorator.decorate(level: level, value: decoratedLevelName)
}
return decoratedLevelName
}
}
21 changes: 21 additions & 0 deletions Spy/Common/Formatters/Decorated/DecoratedSpyFormatter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// DecoratedSpyFormatter.swift
// Spy
//
// Created by Tomasz Lewandowski on 24/01/2020.
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

import Foundation

public final class DecoratedSpyFormatter<Level: PSpyLevel, Channel: PSpyChannel>: PSpyFormatter {
let levelNameBuilder: DecoratedLevelNameBuilder<Level>

public init(levelNameBuilder: DecoratedLevelNameBuilder<Level>) {
self.levelNameBuilder = levelNameBuilder
}

public func format(timestamp: TimeInterval, level: Level, channel: Channel, message: PSpyable) -> String {
return "\(Date(timeIntervalSince1970: timestamp)) \(levelNameBuilder.build(withLevel: level))::\(channel.channelName)::\(message.spyMessage)"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// EmojiPrefixedSpyLevelNameDecorator.swift
// Spy
//
// Created by Tomasz Lewandowski on 24/01/2020.
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

public final class EmojiPrefixedSpyLevelNameDecorator: PSpyLevelDecorator {
public init() {

}

public func decorate(level: SpyLevel, value: String) -> String {
return level.prefix + " " + value
}

public typealias Level = SpyLevel
}

private extension SpyLevel {
var prefix: String {
switch self {
case .finest: return "💬"
case .finer: return "🔊"
case .fine: return "📣"
case .config: return ""
case .info: return "ℹ️"
case .warning: return "⚠️"
case .severe: return ""
}
}
}
18 changes: 18 additions & 0 deletions Spy/Common/Formatters/Decorated/PSpyLevelDecorator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// PSpyLevelDecorator.swift
// Spy
//
// Created by Tomasz Lewandowski on 24/01/2020.
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

public protocol PSpyLevelDecorator {
associatedtype Level: PSpyLevel
func decorate(level: Level, value: String) -> String
}

public extension PSpyLevelDecorator {
func toAnyDecorator() -> AnyLevelDecorator<Level> {
return AnyLevelDecorator(self)
}
}
16 changes: 16 additions & 0 deletions Spy/Common/Formatters/Decorated/PlainLevelNameDecorator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// PlainLevelNameDecorator.swift
// Spy
//
// Created by Tomasz Lewandowski on 24/01/2020.
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

public final class PlainLevelNameDecorator<Level: PSpyLevel>: PSpyLevelDecorator {
public init() {

}
public func decorate(level: Level, value: String) -> String {
return level.levelName
}
}
File renamed without changes.
14 changes: 1 addition & 13 deletions Spy/Common/SpyLevel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

public enum SpyLevel: Int, PColoredSpyLevel {
public enum SpyLevel: Int, PSpyLevel {
case finest = 0
case finer = 1
case fine = 2
Expand Down Expand Up @@ -34,16 +34,4 @@ public enum SpyLevel: Int, PColoredSpyLevel {
public static func levelsFrom(_ level: SpyLevel) -> Set<SpyLevel> {
return Set([SpyLevel.finest, .finer, .fine, .config, .info, .warning, .severe]).filter { $0.levelPriority >= level.levelPriority }
}

public var color: SpyColor {
switch self {
case .finest: return .white
case .finer: return .cyan
case .fine: return .blue
case .config: return .magenta
case .info: return .green
case .warning: return .yellow
case .severe: return .red
}
}
}
11 changes: 0 additions & 11 deletions Spy/Core/PColoredSpyLevel.swift

This file was deleted.

18 changes: 0 additions & 18 deletions SpyTests/ColoredSpyFormatterTests.swift

This file was deleted.

33 changes: 33 additions & 0 deletions SpyTests/DecoratedSpyFormatterTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// DecoratedSpyFormatterTests.swift
// Spy
//
// Created by Tomasz Lewandowski on 24/01/2020.
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

import XCTest
import Spy

final class DecoratedSpyFormatterTests: XCTestCase {
func testFormat_WhenCreatedWithBuilderWithColorDecorator_ShouldReturnStringInCorrectFormat() {
let sut = DecoratedSpyFormatter<SpyLevel, SpyChannel>(levelNameBuilder: DecoratedLevelNameBuilder<SpyLevel>().add(decorator: ColoredSpyLevelNameDecorator().toAnyDecorator()))
let formattedLog = sut.format(timestamp: 0, level: .info, channel: .defaultChannel, message: "message")
XCTAssertEqual("1970-01-01 00:00:00 +0000 \u{001B}[0;32minfo\u{001B}[0;0m::default_channel::message", formattedLog)
}

func testFormat_WhenCreatedWithBuilderWithEmijiDecorator_ShouldReturnStringInCorrectFormat() {
let sut = DecoratedSpyFormatter<SpyLevel, SpyChannel>(levelNameBuilder: DecoratedLevelNameBuilder<SpyLevel>().add(decorator: EmojiPrefixedSpyLevelNameDecorator().toAnyDecorator()))
let formattedLog = sut.format(timestamp: 0, level: .info, channel: .defaultChannel, message: "message")
XCTAssertEqual("1970-01-01 00:00:00 +0000 ℹ️ info::default_channel::message", formattedLog)
}

func testFormat_WhenCreatedWithBuilderWithTwoDecorators_ShouldReturnStringInCorrectFormat() {
let sut = DecoratedSpyFormatter<SpyLevel, SpyChannel>(levelNameBuilder: DecoratedLevelNameBuilder<SpyLevel>()
.add(decorator: ColoredSpyLevelNameDecorator().toAnyDecorator())
.add(decorator: EmojiPrefixedSpyLevelNameDecorator().toAnyDecorator())
)
let formattedLog = sut.format(timestamp: 0, level: .info, channel: .defaultChannel, message: "message")
XCTAssertEqual("1970-01-01 00:00:00 +0000 ℹ️ \u{001B}[0;32minfo\u{001B}[0;0m::default_channel::message", formattedLog)
}
}
25 changes: 25 additions & 0 deletions SpyTests/EmojiPrefixedSpyLevelNameDecoratorTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// EmojiPrefixedSpyLevelNameDecoratorTests.swift
// Spy
//
// Created by Tomasz Lewandowski on 24/01/2020.
// Copyright © 2020 AppUnite Sp. z o.o. All rights reserved.
//

import XCTest
import Spy

public final class EmojiPrefixedSpyLevelNameDecoratorTests: XCTestCase {
func testDecorate_WhenCalledWithLevel_ShouldReturnCorrectString() {
// given
let sut = EmojiPrefixedSpyLevelNameDecorator()
// then
XCTAssertEqual("💬 name", sut.decorate(level: .finest, value: "name"))
XCTAssertEqual("🔊 name", sut.decorate(level: .finer, value: "name"))
XCTAssertEqual("📣 name", sut.decorate(level: .fine, value: "name"))
XCTAssertEqual("✅ name", sut.decorate(level: .config, value: "name"))
XCTAssertEqual("ℹ️ name", sut.decorate(level: .info, value: "name"))
XCTAssertEqual("⚠️ name", sut.decorate(level: .warning, value: "name"))
XCTAssertEqual("⛔ name", sut.decorate(level: .severe, value: "name"))
}
}
1 change: 1 addition & 0 deletions SpyTests/SpyColorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Spy

public final class SpyColorTests: XCTestCase {
func testModificator_WhenCalledForColor_ShouldReturnCorrectString() {
// then
XCTAssertEqual("\u{001B}[0;0m", SpyColor.`default`.modificator)
XCTAssertEqual("\u{001B}[0;30m", SpyColor.black.modificator)
XCTAssertEqual("\u{001B}[0;31m", SpyColor.red.modificator)
Expand Down
Loading

0 comments on commit 172ac87

Please sign in to comment.