Skip to content
This repository has been archived by the owner on Aug 12, 2022. It is now read-only.

Commit

Permalink
implementing logging once, closes #12
Browse files Browse the repository at this point in the history
  • Loading branch information
nilsvu committed Apr 6, 2016
1 parent 2eefe6f commit 3e3327d
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 42 deletions.
2 changes: 1 addition & 1 deletion Evergreen.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "Evergreen"
s.version = "0.8.1"
s.version = "0.9"
s.summary = "A Swift Logging Framework."
s.description = <<-DESC
Evergreen is a logging framework written in Swift.
Expand Down
4 changes: 2 additions & 2 deletions Evergreen.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.8.1;
CURRENT_PROJECT_VERSION = 0.9;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_DYNAMIC_NO_PIC = NO;
Expand Down Expand Up @@ -840,7 +840,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = 0.8.1;
CURRENT_PROJECT_VERSION = 0.9;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
Expand Down
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ log("Hello World!", forLevel: .Info)
```

```sh
2015-04-26 01:48:32.415 [AppDelegate.swift|INFO] Hello World!
[AppDelegate.swift|INFO] Hello World!
```

> Evergreen logging is great to use in any Swift project, but particularly useful when developing a framework. Give the users of your framework the opportunity to easily adjust the verbosity of the output your framework generates.
Expand Down Expand Up @@ -210,7 +210,7 @@ Every environment variable prefixed `Evergreen` is evaluated as a logger key pat
Valid environment variable declarations would be e.g. `Evergreen = Debug` or `Evergreen.MyLogger = Verbose`.


### Logging `ErrorType` errors alongside your events
### Logging `ErrorType` Errors alongside your Events

You can pass any error conforming to Swift's `ErrorType` (such as `NSError`) to Evergreen's logging functions, either as the message or in the separate `error:` argument:

Expand All @@ -225,10 +225,9 @@ debug("Something unexpected happened here!", error: error)
Easily measure the time between two events:

```swift
let logger = Evergreen.defaultLogger
logger.tic(andLog: "Starting expensive operation...", forLevel: .Debug)
tic(andLog: "Starting expensive operation...", forLevel: .Debug)
// ...
logger.toc(andLog: "Completed expensive operation!", forLevel: .Info)
toc(andLog: "Completed expensive operation!", forLevel: .Info)
```

```sh
Expand All @@ -239,14 +238,21 @@ logger.toc(andLog: "Completed expensive operation!", forLevel: .Info)
You can also use the `timerKey` argument for nested timing:

```swift
let logger = Evergreen.defaultLogger
logger.tic(andLog: "Starting expensive operation...", forLevel: .Debug, timerKey: "expensiveOperation")
tic(andLog: "Starting expensive operation...", forLevel: .Debug, timerKey: "expensiveOperation")
for var i=0; i<10; i++ {
logger.tic(andLog: "\(i+1). iteration...", forLevel: .Verbose, timerKey: "iteration")
tic(andLog: "\(i+1). iteration...", forLevel: .Verbose, timerKey: "iteration")
// ...
logger.toc(andLog: "Done!", forLevel: .Verbose, timerKey: "iteration")
toc(andLog: "Done!", forLevel: .Verbose, timerKey: "iteration")
}
logger.toc(andLog: "Completed expensive operation!", forLevel: .Info, timerKey: "expensiveOperation")
toc(andLog: "Completed expensive operation!", forLevel: .Info, timerKey: "expensiveOperation")
```

### Logging Events only once

You can keep similar events from being logged in excessive amounts by associating a `key` with them in any logging call, e.g.:

```swift
debug("Announcing this once!", onceForKey: "announcement")
```


Expand Down
4 changes: 4 additions & 0 deletions Sources/Evergreen/Formatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ public class Formatter {
string += " [ERROR: \(errorMessage)]"
}

if event.once {
string += " [ONLY LOGGED ONCE]"
}

return string
}

Expand Down
69 changes: 40 additions & 29 deletions Sources/Evergreen/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,40 +43,40 @@ public func getLoggerForFile(file: String = #file) -> Logger {

/// Logs the event using a logger that is appropriate for the caller.
/// - seealso: `Logger.log(_:, forLevel:)`
public func log<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, forLevel logLevel: LogLevel? = nil, function: String = #function, file: String = #file, line: Int = #line)
public func log<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, forLevel logLevel: LogLevel? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line)
{
Logger.loggerForFile(file).log(message, error: error, forLevel: logLevel, function: function, file: file, line: line)
Logger.loggerForFile(file).log(message, error: error, forLevel: logLevel, onceForKey: key, function: function, file: file, line: line)
}

/// Logs the event with the Verbose log level using a logger that is appropriate for the caller.
/// - seealso: `Logger.log(_:, forLevel:)`
public func verbose<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Verbose, function: function, file: file, line: line)
public func verbose<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Verbose, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Debug log level using a logger that is appropriate for the caller.
/// - seealso: `Logger.log(_:, forLevel:)`
public func debug<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Debug, function: function, file: file, line: line)
public func debug<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Debug, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Info log level using a logger that is appropriate for the caller.
/// - seealso: `Logger.log(_:, forLevel:)`
public func info<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Info, function: function, file: file, line: line)
public func info<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Info, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Warning log level using a logger that is appropriate for the caller.
/// - seealso: `Logger.log(_:, forLevel:)`
public func warning<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Warning, function: function, file: file, line: line)
public func warning<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Warning, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Error log level using a logger that is appropriate for the caller.
/// - seealso: `Logger.log(_:, forLevel:)`
public func error<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Error, function: function, file: file, line: line)
public func error<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Error, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Critical log level using a logger that is appropriate for the caller.
/// - seealso: `Logger.log(_:, forLevel:)`
public func critical<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Critical, function: function, file: file, line: line)
public func critical<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
Evergreen.log(message, error: error, forLevel: .Critical, onceForKey: key, function: function, file: file, line: line)
}


Expand Down Expand Up @@ -244,44 +244,54 @@ public final class Logger {
- parameter message: The message to be logged, provided by an autoclosure. The closure will not be evaluated if the event is not going to be emitted, so it can contain expensive operations only needed for logging purposes.
- parameter error: An error that occured and should be logged alongside the event.
- parameter logLevel: If the event's log level is lower than the receiving logger's `effectiveLogLevel`, the event will not be logged. The event will always be logged, if no log level is provided for either the event or the logger's `effectiveLogLevel`.
- parameter key: Only logs the message if no logging calls with the same `key` have occured before.
*/
public func log<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, forLevel logLevel: LogLevel? = nil, function: String = #function, file: String = #file, line: Int = #line)
public func log<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, forLevel logLevel: LogLevel? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line)
{
let event = Event(logger: self, message: message, error: error, logLevel: logLevel, date: NSDate(), elapsedTime: nil, function: function, file: file, line: line)
if let key = key {
if keysLoggedOnce.contains(key) {
return
} else {
keysLoggedOnce.insert(key)
}
}
let event = Event(logger: self, message: message, error: error, logLevel: logLevel, date: NSDate(), elapsedTime: nil, once: key != nil, function: function, file: file, line: line)
self.logEvent(event)
}

/// Logs the event with the Verbose log level.
/// - seealso: `log(_:, forLevel:)`
public func verbose<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Verbose, function: function, file: file, line: line)
public func verbose<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Verbose, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Debug log level.
/// - seealso: `log(_:, forLevel:)`
public func debug<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Debug, function: function, file: file, line: line)
public func debug<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Debug, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Info log level.
/// - seealso: `log(_:, forLevel:)`
public func info<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Info, function: function, file: file, line: line)
public func info<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Info, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Warning log level.
/// - seealso: `log(_:, forLevel:)`
public func warning<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Warning, function: function, file: file, line: line)
public func warning<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Warning, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Error log level.
/// - seealso: `log(_:, forLevel:)`
public func error<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Error, function: function, file: file, line: line)
public func error<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Error, onceForKey: key, function: function, file: file, line: line)
}
/// Logs the event with the Critical log level.
/// - seealso: `log(_:, forLevel:)`
public func critical<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Critical, function: function, file: file, line: line)
public func critical<M>(@autoclosure(escaping) message: () -> M, error: ErrorType? = nil, onceForKey key: String? = nil, function: String = #function, file: String = #file, line: Int = #line) {
self.log(message, error: error, forLevel: .Critical, onceForKey: key, function: function, file: file, line: line)
}

var keysLoggedOnce = Set<String>()

/// Logs the given event. Use `log(_:, forLevel:)` instead for convenience.
public func logEvent<M>(event: Event<M>)
{
Expand Down Expand Up @@ -353,7 +363,7 @@ public final class Logger {
}
if let startDate = startDate {
let elapsedTime = NSDate().timeIntervalSinceDate(startDate)
let event = Event(logger: self, message: message, error: error, logLevel: logLevel, date: NSDate(), elapsedTime: elapsedTime, function: function, file: file, line: line)
let event = Event(logger: self, message: message, error: error, logLevel: logLevel, date: NSDate(), elapsedTime: elapsedTime, once: false, function: function, file: file, line: line)
self.logEvent(event)
}
}
Expand Down Expand Up @@ -554,6 +564,7 @@ public struct Event<M> {
let logLevel: LogLevel?
let date: NSDate
let elapsedTime: NSTimeInterval?
let once: Bool

let function: String
let file: String
Expand Down

0 comments on commit 3e3327d

Please sign in to comment.