Willow is a powerful, yet lightweight logging library written in Swift.

Related tags

Logging Willow
Overview

Willow

Build Status CocoaPods Compatible Carthage Compatible Platform

Willow is a powerful, yet lightweight logging library written in Swift.

Features

  • Default Log Levels
  • Custom Log Levels
  • Simple Logging Functions using Closures
  • Configurable Synchronous or Asynchronous Execution
  • Thread-Safe Logging Output (No Log Mangling)
  • Custom Writers through Dependency Injection
  • Custom Modifiers through Dependency Injection per Writer
  • Supports Multiple Simultaneous Writers
  • Shared Loggers Between Frameworks
  • Shared Locks or Queues Between Multiple Loggers
  • Comprehensive Unit Test Coverage
  • Complete Documentation

Requirements

  • iOS 9.0+ / Mac OS X 10.11+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 9.3+
  • Swift 4.1+

Migration Guides

Communication

  • Need help? Open an issue.
  • Have a feature request? Open an issue.
  • Find a bug? Open an issue.
  • Want to contribute? Fork the repo and submit a pull request.

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

[sudo] gem install cocoapods

CocoaPods 1.3+ is required.

To integrate Willow into your project, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '11.0'
use_frameworks!

pod 'Willow', '~> 5.0'

Then, run the following command:

$ pod install

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate Willow into your Xcode project using Carthage, specify it in your Cartfile:

github "Nike-Inc/Willow" ~> 5.0

Run carthage update to build the framework and drag the built Willow.framework into your Xcode project.

Swift Package Manager

The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler. It is in early development, but Willow does support its use on supported platforms.

Once you have your Swift package set up, adding Willow as a dependency is as easy as adding it to the dependencies value of your Package.swift.

dependencies: [
    .package(url: "https://github.com/Nike-Inc/Willow.git", majorVersion: 5)
]

Usage

Creating a Logger

import Willow

let defaultLogger = Logger(logLevels: [.all], writers: [ConsoleWriter()])

The Logger initializer takes three parameters to customize the behavior of the logger instance.

  • logLevels: [LogLevel] - The log message levels that should be processed. Messages that don't match the current log level are not processed.

  • writers: [LogWriter] - The array of writers to write to. Writers can be used to log output to a specific destination such as the console, a file, or an external service.

  • executionMethod: ExecutionMethod = .synchronous(lock: NSRecursiveLock()) - The execution method used when writing messages.

Logger objects can only be customized during initialization. If you need to change a Logger at runtime, it is advised to create an additional logger with a custom configuration to fit your needs. It is perfectly acceptable to have many different Logger instances running simultaneously.

Thread Safety

The print function does not guarantee that the String parameter will be fully logged to the console. If two print calls are happening simultaneously from two different queues (threads), the messages can get mangled, or intertwined. Willow guarantees that messages are completely finished writing before starting on the next one.

It is important to note that by creating multiple Logger instances, you can potentially lose the guarantee of thread-safe logging. If you want to use multiple Logger instances, you should create a NSRecursiveLock or DispatchQueue that is shared between both configurations. For more info, see the Advanced Usage section.

Logging Messages and String Messages

Willow can log two different types of objects: Messages and Strings.

Log Messages

Messages are structured data with a name and a dictionary of attributes. Willow declares the LogMessage protocol which frameworks and applications can use as the basis for concrete implementations. Messages are a good choice if you want to provide context information along with the log text (e.g. routing log information to an external system like New Relic).

enum Message: LogMessage {
    case requestStarted(url: URL)
    case requestCompleted(url: URL, response: HTTPURLResponse)

    var name: String {
        switch self {
        case .requestStarted:   return "Request started"
        case .requestCompleted: return "Request completed"
        }
    }

    var attributes: [String: Any] {
        switch self {
        case let .requestStarted(url):
            return ["url": url]

        case let .requestCompleted(url, response):
            return ["url": url, "response_code": response.statusCode]
        }
    }
}

let url = URL(string: "https://httpbin.org/get")!

log.debug(Message.requestStarted(url: url))
log.info(Message.requestStarted(url: url))
log.event(Message.requestStarted(url: url))
log.warn(Message.requestStarted(url: url))
log.error(Message.requestStarted(url: url))

Log Message Strings

Log message strings are just String instances with no additional data.

let url = URL(string: "https://httpbin.org/get")!

log.debugMessage("Request Started: \(url)")
log.infoMessage("Request Started: \(url)")
log.eventMessage("Request Started: \(url)")
log.warnMessage("Request Started: \(url)")
log.errorMessage("Request Started: \(url)")

The log message string APIs have the Message suffix on the end to avoid ambiguity with the log message APIs. The multi-line escaping closure APIs collide without the suffix.

Logging Messages with Closures

The logging syntax of Willow was optimized to make logging as lightweight and easy to remember as possible. Developers should be able to focus on the task at hand and not remembering how to write a log message.

Single Line Closures

let log = Logger()

// Option 1
log.debugMessage("Debug Message")    // Debug Message
log.infoMessage("Info Message")      // Info Message
log.eventMessage("Event Message")    // Event Message
log.warnMessage("Warn Message")      // Warn Message
log.errorMessage("Error Message")    // Error Message

// or

// Option 2
log.debugMessage { "Debug Message" } // Debug Message
log.infoMessage { "Info Message" }   // Info Message
log.eventMessage { "Event Message" } // Event Message
log.warnMessage { "Warn Message" }   // Warn Message
log.errorMessage { "Error Message" } // Error Message

Both of these approaches are equivalent. The first set of APIs accept autoclosures and the second set accept closures.

Feel free to use whichever syntax you prefer for your project. Also, by default, only the String returned by the closure will be logged. See the Log Modifiers section for more information about customizing log message formats.

The reason both sets of APIs use closures to extract the log message is performance.

There are some VERY important performance considerations when designing a logging solution that are described in more detail in the Closure Performance section.

Multi-Line Closures

Logging a message is easy, but knowing when to add the logic necessary to build a log message and tune it for performance can be a bit tricky. We want to make sure logic is encapsulated and very performant. Willow log level closures allow you to cleanly wrap all the logic to build up the message.

log.debugMessage {
    // First let's run a giant for loop to collect some info
    // Now let's scan through the results to get some aggregate values
    // Now I need to format the data
    return "Computed Data Value: \(dataValue)"
}

log.infoMessage {
    let countriesString = ",".join(countriesArray)
    return "Countries: \(countriesString)"
}

Unlike the Single Line Closures, the Multi-Line Closures require a return declaration.

Closure Performance

Willow works exclusively with logging closures to ensure the maximum performance in all situations. Closures defer the execution of all the logic inside the closure until absolutely necessary, including the string evaluation itself. In cases where the Logger instance is disabled, log execution time was reduced by 97% over the traditional log message methods taking a String parameter. Additionally, the overhead for creating a closure was measured at 1% over the traditional method making it negligible. In summary, closures allow Willow to be extremely performant in all situations.

Disabling a Logger

The Logger class has an enabled property to allow you to completely disable logging. This can be helpful for turning off specific Logger objects at the app level, or more commonly to disable logging in a third-party library.

let log = Logger()
log.enabled = false

// No log messages will get sent to the registered Writers

log.enabled = true

// We're back in business...

Synchronous and Asynchronous Logging

Logging can greatly affect the runtime performance of your application or library. Willow makes it very easy to log messages synchronously or asynchronously. You can define this behavior when creating the LoggerConfiguration for your Logger instance.

let queue = DispatchQueue(label: "serial.queue", qos: .utility)
let log = Logger(logLevels: [.all], writers: [ConsoleWriter()], executionMethod: .asynchronous(queue: queue))

Synchronous Logging

Synchronous logging is very helpful when you are developing your application or library. The log operation will be completed before executing the next line of code. This can be very useful when stepping through the debugger. The downside is that this can seriously affect performance if logging on the main thread.

Asynchronous Logging

Asynchronous logging should be used for deployment builds of your application or library. This will offload the logging operations to a separate dispatch queue that will not affect the performance of the main thread. This allows you to still capture logs in the manner that the Logger is configured, yet not affect the performance of the main thread operations.

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.

Log Writers

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

public protocol LogWriter {
    func writeMessage(_ message: String, logLevel: LogLevel)
    func writeMessage(_ message: Message, logLevel: LogLevel)
}

Again, this is an extremely lightweight design to allow for ultimate flexibility. As long as your LogWriter 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 a simple write that writes to the console.

open class ConsoleWriter: LogMessageWriter {
    open func writeMessage(_ message: String, logLevel: LogLevel) {
        print(message)
    }

    open func writeMessage(_ message: LogMessage, logLevel: LogLevel) {
        let message = "\(message.name): \(message.attributes)"
        print(message)
    }
}

Log 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 emoji! There's no way to predict all the types of custom formatting teams are going to want to use. This is where LogModifier objects come in.

public protocol LogModifier {
    func modifyMessage(_ message: String, with logLevel: LogLevel) -> String
}

The LogModifier 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.

As an added layer of convenience, writers intending to output strings (e.g. writing to the console, files, etc.) can conform to the LogModifierWritier protocol. The LogModifierWriter protocol adds an array of LogModifier objects to the LogWriter that can be applied to the message before it is output using the modifyMessage(_:logLevel) API in the extension.

Let's walk through a simple example for adding a prefix to a logger for the debug and info log levels.

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

let prefixModifiers = [PrefixModifier()]
let writers = [ConsoleWriter(modifiers: prefixModifiers)]
let log = Logger(logLevels: [.debug, .info], writers: writers)

To apply modifiers consistently to strings, LogModifierWriter objects should call modifyMessage(_:logLevel) to create a new string based on the original string with all the modifiers applied in order.

open func writeMessage(_ message: String, logLevel: LogLevel) {
    let message = modifyMessage(message, logLevel: logLevel)
    print(message)
}

Multiple Modifiers

Multiple LogModifier 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 an EmojiModifier.

class EmojiModifier: LogModifier {
    func modifyMessage(_ message: String, with logLevel: LogLevel) -> String {
        return "🚀🚀🚀 \(message)"
    }
}

let writers: = [ConsoleWriter(modifiers: [EmojiModifier(), TimestampModifier()])]
let log = Logger(logLevels: [.all], writers: writers)

Willow doesn't have any hard limits on the total number of LogModifier 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 TimestampModifier was inserted before the EmojiModifier.

OSLog

The OSLogWriter class allows you to use the os_log APIs within the Willow system. In order to use it, all you need to do is to create the LogModifier instance and add it to the Logger.

let writers = [OSLogWriter(subsystem: "com.nike.willow.example", category: "testing")]
let log = Logger(logLevels: [.all], writers: writers)

log.debugMessage("Hello world...coming to your from the os_log APIs!")

Multiple Writers

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

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

    public func writeMessage(_ message: LogMessage, logLevel: LogLevel) {
        let message = "\(message.name): \(message.attributes)"
        // Write the formatted message to a file (We'll leave this to you!)
    }
}

let writers: [LogMessageWriter] = [FileWriter(), ConsoleWriter()]
let log = Logger(logLevels: [.all], writers: writers)

LogWriter 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.


Advanced Usage

Creating Custom Log Levels

Depending upon the situation, the need to support additional log levels may arise. Willow can easily support additional log levels through the art of bitmasking. Since the internal RawValue of a LogLevel is a UInt, Willow can support up to 32 log levels simultaneously for a single Logger. Since there are 7 default log levels, Willow can support up to 27 custom log levels for a single logger. That should be more than enough to handle even the most complex of logging solutions.

Creating custom log levels is very simple. Here's a quick example of how to do so. First, you must create a LogLevel extension and add your custom values.

extension LogLevel {
    private static var verbose = LogLevel(rawValue: 0b00000000_00000000_00000001_00000000)
}

It's a good idea to make the values for custom log levels var instead of let. In the event of two frameworks using the same custom log level bit mask, the application can re-assign one of the frameworks to a new value.

Now that we have a custom log level called verbose, we need to extend the Logger class to be able to easily call it.

extension Logger {
    public func verboseMessage(_ message: @autoclosure @escaping () -> String) {
    	logMessage(message, with: .verbose)
    }

    public func verboseMessage(_ message: @escaping () -> String) {
    	logMessage(message, with: .verbose)
    }
}

Finally, using the new log level is a simple as...

let log = Logger(logLevels: [.all], writers: [ConsoleWriter()])
log.verboseMessage("My first verbose log message!")

The all log level contains a bitmask where all bits are set to 1. This means that the all log level will contain all custom log levels automatically.

Shared Loggers between Frameworks

Defining a single Logger and sharing that instance several frameworks can be very advantageous, especially with the addition of Frameworks in iOS 8. Now that we're going to be creating more frameworks inside our own apps to be shared between apps, extensions and third party libraries, wouldn't it be nice if we could share Logger instances?

Let's walk through a quick example of a Math framework sharing a Logger with it's parent Calculator app.

//=========== Inside Math.swift ===========
public var log: Logger?

//=========== Calculator.swift ===========
import Math

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

// Set the Math.log instance to the Calculator.log to share the same Logger instance
Math.log = log

It's very simple to swap out a pre-existing Logger with a new one.

Multiple Loggers, One Queue

The previous example showed how to share Logger instances between multiple frameworks. Something more likely though is that you would want to have each third party library or internal framework to have their own Logger with their own configuration. The one thing that you really want to share is the NSRecursiveLock or DispatchQueue that they run on. This will ensure all your logging is thread-safe. Here's the previous example demonstrating how to create multiple Logger instances and still share the queue.

//=========== Inside Math.swift ===========
public var log: Logger?

//=========== Calculator.swift ===========
import Math

// Create a single queue to share
let sharedQueue = DispatchQueue(label: "com.math.logger", qos: .utility)

// Create the Calculator.log with multiple writers and a .Debug log level
let writers: [LogMessageWriter] = [FileWriter(), ConsoleWriter()]

var log = Logger(
    logLevels: [.all],
    writers: writers,
    executionMethod: .asynchronous(queue: sharedQueue)
)

// Replace the Math.log with a new instance with all the same configuration values except a shared queue
Math.log = Logger(
    logLevels: log.logLevels,
    writers: [ConsoleWriter()],
    executionMethod: .asynchronous(queue: sharedQueue)
)

Willow is a very lightweight library, but its flexibility allows it to become very powerful if you so wish.


FAQ

Why 5 default log levels? And why are they so named?

Simple...simplicity and elegance. Contextually it gets difficult to understand which log level you need if you have too many. However, that doesn't mean that this is always the perfect solution for everyone or every use case. This is why there are 5 default log levels, with support for easily adding additional ones.

As for the naming, here's our mental breakdown of each log level for an iOS app (obviously it depends on your use case).

  • debug - Highly detailed information of a context
  • info - Summary information of a context
  • event - User driven interactions such as button taps, view transitions, selecting a cell
  • warn - An error occurred but it is recoverable
  • error - A non-recoverable error occurred

When should I use Willow?

If you are starting a new iOS project in Swift and want to take advantage of many new conventions and features of the language, Willow would be a great choice. If you are still working in Objective-C, a pure Objective-C library such as CocoaLumberjack would probably be more appropriate.

Where did the name Willow come from?

Willow is named after the one, the only, Willow tree.


License

Willow is released under the MIT license. See LICENSE for details.

Creators

Comments
  • OSLogWriter sends os logs with same type for both warn and error levels

    OSLogWriter sends os logs with same type for both warn and error levels

    It seems OSLogWriter sends os logs with type error for both warn and error levels. I think it would be better to have different types for these 2 levels assigning fault type to error level.

    In LogWriter.swift at line 186 (on master branch) we have:

            case LogLevel.warn:  return .error
            case LogLevel.error: return .error
    

    we could just modify it in this way:

            case LogLevel.warn:  return .error
            case LogLevel.error: return .fault
    
    log level needs feedback os_log 
    opened by MarcoFilosi 9
  • I have some suggestions

    I have some suggestions

    Recently, I try to found a logging library written in Swift.
    Such as XCGLogger, SwiftyBeaver can`t satisfy me.

    Talk about Willow, I must say, your shoe is very good , so your code must be good.

    First Willow dont have a way to log to file.
    Second, I think func info(message: () -> String) is not very good, I suggest this:

    func lInfo(items: Any..., separator: String = " ") {
        let str = items.reduce("") { return $0 + "\($1)" + separator }
        info (str)
    }
    

    So it will use like print(1, 2, "21", [1, 2, 3])

    feature request 
    opened by harryzjm 7
  • Fixes #47 Add Dispatch Group Execution Method

    Fixes #47 Add Dispatch Group Execution Method

    Adding Dispatch Group solves this issue but it will require calling group.wait() before the main() method terminates.

    class AarKayLogger {
    
       private static let `default`: Logger = {
            return Logger(
                logLevels: [.all],
                writers: [ConsoleWriter()],
                executionMethod: .asynchronous(
                    queue: DispatchQueue(label: "me.RahulKatariya.AarKay.outputQueue", qos: .utility)
                )
            )
        }()
        
        static func waitForCompletion() {
            AarKayLogger.default.waitForAllLogsCompletion()
        }
    
    }
    

    Reference:

    • https://github.com/RahulKatariya/AarKay/blob/master/Sources/AarKay/AarKayLogger.swift#L26
    execution method needs feedback 
    opened by rahul0x24 3
  • Bugfix - Ambiguity in Escaping Closure APIs

    Bugfix - Ambiguity in Escaping Closure APIs

    This PR fixes an issue we identified in the Willow 4.0.0 release where the escaping closure APIs are ambiguous when the closure is multiline. The compiler can't infer what the return type of the closure is unfortunately. You can declare the return type of the closure to fix the ambiguity error, but that's less than ideal.

    See the migration guide for examples of the issue.

    These changes fix the ambiguity by changing the log message string APIs to include the Message suffix. This fixes the ambiguity properly. The only question is whether this is the best way to name the APIs. I think this makes the usage the most readable.

    log.debug(Message.requestStarted)
    log.debugMessage("This is a test message")
    

    I'm open to suggestions if anyone thinks there's a better way. If someone can come up with a backwards compatible solution that doesn't require declaring the return type to the compiler, I'm all ears.

    logger 
    opened by cnoon 2
  • Willow 2.0 not available on cocoapods

    Willow 2.0 not available on cocoapods

    Hey guys, I'm super excited to start using willow, however I just ran into a small roadblock I thought you might like to know about.

    Podfile:

    source 'https://github.com/CocoaPods/Specs.git'
    project 'CCRewards'
    
    target 'CCRewards' do
        platform :ios, '8.0'
        use_frameworks!
    
        pod 'Willow', '~> 2.0'
        pod 'Reveal-iOS-SDK', :configurations => ['Debug']
    
    end
    

    Cocoapods (1.0.1) output:

    Analyzing dependencies
    [!] Unable to satisfy the following requirements:
    
    - `Willow (~> 2.0)` required by `Podfile`
    
    None of your spec sources contain a spec satisfying the dependency: `Willow (~> 2.0)`.
    
    You have either:
     * out-of-date source repos which you can update with `pod repo update`.
     * mistyped the name or version.
     * not added the source repo that hosts the Podspec to your Podfile.
    
    Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by default.
    

    I ran pod repo update but that did not fix the issue

    Willow on cocoapods.org: https://cocoapods.org/pods/Willow Shows version is 1.1.0

    Thanks in advance for your help and all your hard work!

    question cocoapods 
    opened by scottdelly 2
  • Support of the new Unified Logging and Tracing System for iOS and macOS

    Support of the new Unified Logging and Tracing System for iOS and macOS

    Is there any plans to support the new Unified Logging and Tracing System for iOS and macOS, see https://developer.apple.com/videos/play/wwdc2016/721/.

    I assume that there's a branch feature/os_log_writer that is adding this support.

    I'm considering the library to use for the new app and I would like having this support. I event can take a part of the implementation.

    feature request question 
    opened by larryonoff 2
  • Feature - Refactoring Modifiers and Writers

    Feature - Refactoring Modifiers and Writers

    This PR refactors the Writer and Modifier protocols to include a LogMessage prefix. There are two main reasons for these changes.

    1. We wanted to be more explicit with the protocol names to make their intent more clear. The previous names were so vague that they didn't really express intent clearly.
    2. We all agreed that Formatter was better than Modifier, but that LogMessageModifier was much better than Formatter. We also agreed that LogMessageModifier was much better than Willow.Formatter everywhere. In the end, we decided that additional verbosity to avoid conflicting with Foundation APIs was probably the best decision.
    modifier writer 
    opened by cnoon 2
  • Impossible to compile on linux

    Impossible to compile on linux

    Hello,

    The LogWriter imports without condition the os module, and uses %@ to print strings to NSLog.

    The os module and the %@ formatter are not available in Foundation, therefore it is not possible to build on Linux and do some server-side swift using Willow on Linux platform.

    Here is the kind of errors you get when you try:

    /src/Lambda/.build/checkouts/Willow.git-2002761449145563439/Source/LogWriter.swift:26:8: error: no such module 'os'
    import os
           ^
    

    and

    /src/Lambda/.build/checkouts/Willow.git--433062434768024296/Source/LogWriter.swift:124:34: error: argument type 'String' does not conform to expected type 'CVarArg'
            case .nslog: NSLog("%@", message)
                                     ^~~~~~~
                                             as! CVarArg
    

    I suggest to add a condition on the os import using canImport() and around the OSLogWriter class.

    ~~About the string formatter, we can use withCString and %s.~~ Bad solution, destroys unicode char because %s forces roman charset. Replaced with the usage of NSLog without the formatter, it works.

    I made a pull request (#52) implementing this, and built it on both MacOS and Linux, so these few changes make it entirely usable on Linux.

    I hope I respected all the requirements, do not hesitate to tell me if I did anything wrong. Also I'm not a native english speaker and there may be some language mistakes, sorry about that.

    opened by qraimbault 1
  • Vulnerability to deadlocks with synchronous execution method

    Vulnerability to deadlocks with synchronous execution method

    I just came across https://github.com/JohnEstropia/CoreStore/issues/304 issue, where Willow is partly to blame, specifically these lines:

    lock.lock() ; defer { lock.unlock() }
    logMessage(message(), with: logLevel)
    

    The message shouldn't be acquired within the lock. While the recursive lock handles deadlocking, there're a many cases where people somehow manage to get them. The aforementioned case is another example.

    bug logger log message 
    opened by iby 1
  • Ambiguity fix

    Ambiguity fix

    I believe that the original rational for rejecting this PR was perhaps unfounded, if I understood the objection correctly.

    The objection was passing the LogMessage as a CustomStringConvertible would prevent any types adopting the LogWriter protocol from introspecting into the LogMessage's attributes property.

    However, as is demonstrated in the new test case I added in this PR, this is an incorrect assumption.

    In fact, the test case's TestConsoleLogWriter is able to see the LogMessage's attributes without any problems at all. See the LogWriterTests file, ll. 96-103 for hard proof of this:

        func testThatLogMessageAttributesCanBeExposedToWriters() {
            // Given
            let message: LogMessage = TestMessage("testMessage", attributes: ["aCode": 1337, "bCode": 4000, "cCode": 9001, "dCode": 42])
            let logLevel = LogLevel.info
            let writer = TestConsoleWriter(method: .print)
    
            // When, Then
            writer.writeMessage(message, logLevel: logLevel)
            XCTAssert(Set(writer.codes) == Set([1337, 4000, 9001, 42]))
    

    All that's required is that inside the adopter of LogWriter to do:

             if let logMessage = message as? LogMessage { 
                 //access logMessage.attributes
             }
    

    https://github.com/Nike-Inc/Willow/compare/master...gistya:ambiguityFix?expand=1#diff-d80f49e5d44c4f93b3d49edf426d1a46

    opened by gistya 1
  • Ambiguity fix

    Ambiguity fix

    In reference to the problems described PR 32, it appears I may have found a way to make the compiler happy while not requiring two different method names for log and logMessage etc.

    In the process I have also allowed logging any type that adopts CustomStringConvertible, so for example:

    log.error { displayNyanCat() return someNSError.userInfo }

    I have updated Willow and its unit tests, which all pass. There are some places in the Documentation that will need info to be inserted (like what PR it was merged with). As well, the migration guide to 6.0 should be reviewed. I don't know if that's how you'd want to handle versioning, so let me know if it should be different.

    Hope this works for you. If not, then let me know what could be done differently. Thanks.

    enhancement log message 
    opened by gistya 1
  • SPM Package via Fastlane in Xcode does not work with Willow

    SPM Package via Fastlane in Xcode does not work with Willow

    Hey Nike,

    I know I'm not using SPM incorrectly, because some of the packages/products to get installed and I obviously have higher than macOS.11.

    This is the error:

    error: the product 'Kingfisher' requires minimum platform version 10.12 for macos platform
    error: the product 'RxSwiftExt' requires minimum platform version 10.11 for macos platform
    error: the product 'Willow' requires minimum platform version 10.11 for macos platform
    

    Thank you, Montana Mendy

    opened by Montana 0
  • Bump tzinfo from 1.2.5 to 1.2.10

    Bump tzinfo from 1.2.5 to 1.2.10

    Bumps tzinfo from 1.2.5 to 1.2.10.

    Release notes

    Sourced from tzinfo's releases.

    v1.2.10

    TZInfo v1.2.10 on RubyGems.org

    v1.2.9

    • Fixed an incorrect InvalidTimezoneIdentifier exception raised when loading a zoneinfo file that includes rules specifying an additional transition to the final defined offset (for example, Africa/Casablanca in version 2018e of the Time Zone Database). #123.

    TZInfo v1.2.9 on RubyGems.org

    v1.2.8

    • Added support for handling "slim" format zoneinfo files that are produced by default by zic version 2020b and later. The POSIX-style TZ string is now used calculate DST transition times after the final defined transition in the file. The 64-bit section is now always used regardless of whether Time has support for 64-bit times. #120.
    • Rubinius is no longer supported.

    TZInfo v1.2.8 on RubyGems.org

    v1.2.7

    • Fixed 'wrong number of arguments' errors when running on JRuby 9.0. #114.
    • Fixed warnings when running on Ruby 2.8. #112.

    TZInfo v1.2.7 on RubyGems.org

    v1.2.6

    • Timezone#strftime('%s', time) will now return the correct number of seconds since the epoch. #91.
    • Removed the unused TZInfo::RubyDataSource::REQUIRE_PATH constant.
    • Fixed "SecurityError: Insecure operation - require" exceptions when loading data with recent Ruby releases in safe mode.
    • Fixed warnings when running on Ruby 2.7. #106 and #111.

    TZInfo v1.2.6 on RubyGems.org

    Changelog

    Sourced from tzinfo's changelog.

    Version 1.2.10 - 19-Jul-2022

    Version 1.2.9 - 16-Dec-2020

    • Fixed an incorrect InvalidTimezoneIdentifier exception raised when loading a zoneinfo file that includes rules specifying an additional transition to the final defined offset (for example, Africa/Casablanca in version 2018e of the Time Zone Database). #123.

    Version 1.2.8 - 8-Nov-2020

    • Added support for handling "slim" format zoneinfo files that are produced by default by zic version 2020b and later. The POSIX-style TZ string is now used calculate DST transition times after the final defined transition in the file. The 64-bit section is now always used regardless of whether Time has support for 64-bit times. #120.
    • Rubinius is no longer supported.

    Version 1.2.7 - 2-Apr-2020

    • Fixed 'wrong number of arguments' errors when running on JRuby 9.0. #114.
    • Fixed warnings when running on Ruby 2.8. #112.

    Version 1.2.6 - 24-Dec-2019

    • Timezone#strftime('%s', time) will now return the correct number of seconds since the epoch. #91.
    • Removed the unused TZInfo::RubyDataSource::REQUIRE_PATH constant.
    • Fixed "SecurityError: Insecure operation - require" exceptions when loading data with recent Ruby releases in safe mode.
    • Fixed warnings when running on Ruby 2.7. #106 and #111.
    Commits
    • 0814dcd Fix the release date.
    • fd05e2a Preparing v1.2.10.
    • b98c32e Merge branch 'fix-directory-traversal-1.2' into 1.2
    • ac3ee68 Remove unnecessary escaping of + within regex character classes.
    • 9d49bf9 Fix relative path loading tests.
    • 394c381 Remove private_constant for consistency and compatibility.
    • 5e9f990 Exclude Arch Linux's SECURITY file from the time zone index.
    • 17fc9e1 Workaround for 'Permission denied - NUL' errors with JRuby on Windows.
    • 6bd7a51 Update copyright years.
    • 9905ca9 Fix directory traversal in Timezone.get when using Ruby data source
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump cocoapods-downloader from 1.2.2 to 1.6.3

    Bump cocoapods-downloader from 1.2.2 to 1.6.3

    Bumps cocoapods-downloader from 1.2.2 to 1.6.3.

    Release notes

    Sourced from cocoapods-downloader's releases.

    1.6.3

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.2

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.1

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.0

    Enhancements
    • None.
    Bug Fixes
    • Adds a check for command injections in the input for hg and git.
      orta #124

    1.5.1

    Enhancements
    • None.
    Bug Fixes
    • Fix "can't modify frozen string" errors when pods are integrated using the branch option
      buju77 #10920

    1.5.0

    ... (truncated)

    Changelog

    Sourced from cocoapods-downloader's changelog.

    1.6.3 (2022-04-01)

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.2 (2022-03-28)

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.1 (2022-03-23)

    Enhancements
    • None.
    Bug Fixes
    • None.

    1.6.0 (2022-03-22)

    Enhancements
    • None.
    Bug Fixes
    • Adds a check for command injections in the input for hg and git.
      orta #124

    1.5.1 (2021-09-07)

    Enhancements
    • None.

    ... (truncated)

    Commits
    • c03e2ed Release 1.6.3
    • f75bccc Disable Bazaar tests due to macOS 12.3 not including python2
    • 52a0d54 Merge pull request #128 from CocoaPods/validate_before_dl
    • d27c983 Ensure that the git pre-processor doesn't accidentally bail also
    • 3adfe1f [CHANGELOG] Add empty Master section
    • 591167a Release 1.6.2
    • d2564c3 Merge pull request #127 from CocoaPods/validate_before_dl
    • 99fec61 Switches where we check for invalid input, to move it inside the download fun...
    • 96679f2 [CHANGELOG] Add empty Master section
    • 3a7c54b Release 1.6.1
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Type not displayed in Console output on Mac for OSLogWriter

    Type not displayed in Console output on Mac for OSLogWriter

    I am using Willow v6.0.0. In it, I am creating a release level logger like this:

    private static func buildReleaseLogger(name: String) -> Logger {
            guard let bundleIdentifier =  Bundle.main.bundleIdentifier else {
                return buildDebugLogger(name: name)
            }
            
            let osLogWriter = OSLogWriter(subsystem: bundleIdentifier, category: name)
            
            let appLogLevels: LogLevel = [.event, .info, .warn, .error]
            let asynchronousExecution: Logger.ExecutionMethod = .asynchronous(
                queue: DispatchQueue(label: "speech-drill.logging", qos: .utility)
            )
            
            return Logger(logLevels: appLogLevels, writers: [osLogWriter], executionMethod: asynchronousExecution)
    }
    

    But when I open the console app on my Mac and change the build configuration to release, I see the type field empty when filtering by subsystem.

    image

    What am I missing in this setup? I have followed this guide: Convenient Logging in Swift and used the exact same code. The only changes are made in subsystem and queue label names. In the output for the tutorial, you can see the type of log which gets decided by log level. What am I missing?

    This configure function is called in application(_:didFinishLaunchingWithOptions:).

    static func configure() {
            
            let name = "Logger"
           
            #if DEBUG
                willow_logger = buildDebugLogger(name: name)
            #else
                willow_logger = buildReleaseLogger(name: name)
            #endif
            
            willow_logger?.enabled = true
    }
    
    opened by parthv21 0
  • Suggesting converting private to fileprivate to avoid inaccessible error

    Suggesting converting private to fileprivate to avoid inaccessible error

    Due to private level restriction, the following code causes verbose defined in LogLevel, to be inaccessible in Logger. At least it should be made fileprivate so the logger extension can access it if defined in the same file. The other option is a public protection level.

    extension LogLevel {
        private static var verbose = LogLevel(rawValue: 0b00000000_00000000_00000001_00000000)
    }
    
    extension Logger {
        public func verboseMessage(_ message: @autoclosure @escaping () -> String) {
        	logMessage(message, with: .verbose)
        }
    
        public func verboseMessage(_ message: @escaping () -> String) {
        	logMessage(message, with: .verbose)
        }
    }
    
    opened by parthv21 1
Releases(5.2.1)
  • 5.2.1(May 2, 2019)

  • 5.2.0(Apr 30, 2019)

    Released on 2019-04-30. All issues associated with this milestone can be found using this filter.

    Updated

    • To Swift 5 with backwards compatability with 4.2 and Xcode 10.1.
    • OSLogLevel to now be overridable and the default mapping.
    Source code(tar.gz)
    Source code(zip)
  • 5.1.0(Sep 17, 2018)

    Released on 2018-09-17. All issues associated with this milestone can be found using this filter.

    Added

    • A Logger.disabled static variable that can be assigned to a Logger variable. This Logger will not write any messages sent to it.

    Updated

    • The Xcode workspace to be compatible with Xcode 10 and Swift 4.2.
    • The podspec swift-version to 4.2.
    • The Travis-CI yaml file to build with Xcode 10 by leveraging bundler and a Gemfile.

    Deprecated

    • The Optional<Logger> extensions are now deprecated. Use a non-optional Logger variable: var log: Logger? = nilvar log: Logger = .disabled.
    Source code(tar.gz)
    Source code(zip)
  • 5.0.2(Apr 10, 2018)

    Released on 2018-04-10. All issues associated with this milestone can be found using this filter.

    Updated

    • The Xcode project to support Xcode 9.3.
    • The Travis-CI YAML file to support Xcode 9.3.

    Fixed

    • Compiler warnings in the test suite on Xcode 9.3 for the characters property on String.
    Source code(tar.gz)
    Source code(zip)
  • 5.0.1(Jan 3, 2018)

  • 5.0.0(Sep 27, 2017)

    Released on 2017-09-20. All issues associated with this milestone can be found using this filter.

    Added

    • Migration Guide for Willow 5 and added it to the README.

    Updated

    • Log message string APIs to include Message suffix to remove ambiguity with LogMessage APIs.
    • The README to match the updated APIs.
    • The Package file to be compatible with SPM v4.
    Source code(tar.gz)
    Source code(zip)
  • 4.0.0(Aug 30, 2017)

    Released on 2017-08-30. All issues associated with this milestone can be found using this filter.

    Added

    • Support for structured messages and simplified Logger setup.
    • New package file for Willow to support the Swift Package Manager.

    Updated

    • The Xcode project and source code to Swift 4.0.
    • The log level enabled check to run prior to acquiring the lock or async dispatch queue resulting in a small performance gain.
    • The example frameworks to not have a default logger instance.
    • The example app configuration logic to match the new APIs.
    • The LogMessageWriter protocol by splitting it into two new protocols: LogWriter and LogModifierWriter. The former is a basic writer. The latter is a writer that also accepts an array of modifiers to apply to incoming messages.
    • Example frameworks to show intended usage of the new LogMessage APIs.

    Removed

    • LoggerConfiguration entirely. Logger construction now takes a log level, writers array, and execution method which greatly simplifies setup and usage.
    Source code(tar.gz)
    Source code(zip)
  • 3.0.2(Aug 30, 2017)

  • 3.0.1(Jan 3, 2018)

    Released on 2017-08-17. All issues associated with this milestone can be found using this filter.

    Updated

    • Network example framework to WebServices to avoid a name collision with an iOS 11 private framework with the same name.
    • SWIFT_VERSION to 3.2 in all targets. All targets still build with Swift 3.1/Xcode 8.3.x.
    • Copyright dates to 2017.
    • ExecutionMethod enum casing in example.
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(Jan 14, 2017)

    All issues associated with this milestone can be found using this filter.

    Added

    • A new .swift-version file for CocoaPods pointed at Swift 3.0.
    • A migration guide for the Willow 3.0 release.
    • Support for OSLogWriter on macOS 10.12+ by removing preprocessor guards.

    Updated

    • The Travis-CI YAML file to Xcode 8.2 and the latest SDKs and destinations.
    • The Travis-CI YAML file by re-enabling pod lib lint since lint issue is resolved.
    • The Xcode projects to Xcode 8.2 and disabled automatic signing on frameworks.
    • Instances of OSX with macOS including the framework and target names.
    • ExecutionMethod enum cases to be lowercased to match Swift API Design Guidelines.

    Fixed

    • Crash in WriterTests on iOS and tvOS 9 where os_log APIs are not available.
    • Compiler warnings in the example app around private and fileprivate ACLs.
    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(Sep 8, 2016)

    All issues associated with this milestone can be found using this filter.

    Added

    • OSLogWriter to use the os_log APIs indirectly through a Logger instance.
    • The Method enumeration on ConsoleWriter to switch between print and NSLog functions.
    • A Willow 2.0 Migration Guide detailing all breaking changes between 1.x and 2.0.

    Updated

    • All source, test and example logic and project settings to compile against Swift 3.0.
    • The Formatter protocol to be LogMessageModifier to avoid naming collisions with Foundation.
    • The Writer protocol to be LogMessageWriter to match LogMessageModifier naming convention.
    • The README and all sample code to match the new APIs and conventions.

    Removed

    • Code generation from all framework targets by default due to instability issues.
    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Sep 7, 2016)

    All issues associated with this milestone can be found using this filter.

    Updated

    • All source, test and example logic to compile against Swift 2.3 and Xcode 8.
    • The Travis CI yaml file to build against iOS 10 and the new suite of simulators.
    • The iOS Example app to use emoticons in the PrefixFormatter.

    Removed

    • Slather reporting from the test suite due to instability issues with Xcode and Travis CI.
    • CocoaPods linting from the Travis CI yaml file due to current instabilities with Xcode 8.
    • The ColorFormatter and all logic associated with it since plugins are no longer supported.
    • Removed the color formatting section and examples from the README.
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Jul 12, 2016)

    All issues associated with this milestone can be found using this filter.

    Added

    • New autoclosure(escaping) variants of the logging methods.

    Updated

    • The README to explain the differences between autoclosure and closure APIs.
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Jun 27, 2016)

Owner
Nike Inc.
Nike Inc.
A logging backend for swift-log that sends logging messages to Logstash (eg. the ELK stack)

LoggingELK LoggingELK is a logging backend library for Apple's swift-log The LoggingELK library provides a logging backend for Apple's apple/swift-log

null 17 Nov 15, 2022
JustLog brings logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support for logz.io available.

JustLog JustLog takes logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support f

Just Eat 509 Dec 10, 2022
Twitter Logging Service is a robust and performant logging framework for iOS clients

Twitter Logging Service Background Twitter created a framework for logging in order to fulfill the following requirements: fast (no blocking the main

Twitter 290 Nov 15, 2022
Simple, lightweight and flexible debug logging framework written in Swift

AELog Simple, lightweight and flexible debug logging minion written in Swift If you find yourself in upcoming statements, then you probably want to us

Marko Tadić 28 Jul 6, 2022
Spy is a flexible, lightweight, multiplatform logging utility written in pure Swift.

Spy is a flexible, lightweight, multiplatform logging utility written in pure Swift. It allows to log with different levels and on different channels. You can define what levels and channels actually are.

AppUnite Sp. z o.o. Spk. 12 Jul 28, 2021
A powerful input-agnostic swift logging framework made to speed up development with maximum readability.

The Swift logging framework. Atlantis is an extremely powerful logging framework that I've created for everyday use, including enterprise development

Andrew Aquino 199 Jan 2, 2023
CleanroomLogger provides an extensible Swift-based logging API that is simple, lightweight and performant

CleanroomLogger CleanroomLogger provides an extensible Swift-based logging API that is simple, lightweight and performant. The API provided by Cleanro

null 1.3k Dec 8, 2022
A lightweight logging framework for Swift

HeliumLogger Provides a lightweight logging implementation for Swift which logs to standard output. Features Logs output to stdout by default. You can

Kitura 174 Nov 30, 2022
A lightweight logging framework for Swift

HeliumLogger Provides a lightweight logging implementation for Swift which logs to standard output. Features Logs output to stdout by default. You can

Kitura 174 Nov 30, 2022
A fancy logger yet lightweight, and configurable. 🖨

?? ?? Important: Printer can only print console logs if you're running an app in the Simulator. If you're running in a real device it will not print a

Hemang 66 Dec 7, 2022
A flexible logging library written in Swift

Puppy Puppy is a flexible logging library written in Swift ?? It supports multiple transports(console, file, syslog, and oslog) as loggers. It not onl

Koichi Yokota 92 Dec 29, 2022
Convenient & secure logging during development & release in Swift 3, 4 & 5

Colorful, flexible, lightweight logging for Swift 3, Swift 4 & Swift 5. Great for development & release with support for Console, File & cloud platfor

SwiftyBeaver 5.6k Jan 4, 2023
Swift Logging Utility for Xcode & Google Docs

QorumLogs Swift Logging Utility in Xcode & Google Docs

Goktug Yilmaz 777 Jul 15, 2022
TraceLog is a highly configurable, flexible, portable, and simple to use debug logging system for Swift and Objective-C applications running on Linux, macOS, iOS, watchOS, and tvOS.

Please star this github repository to stay up to date. TraceLog Introduction TraceLog is a highly configurable, flexible, portable, and simple to use

Tony Stone 52 Oct 28, 2022
An extensible logging framework for Swift

Log is a powerful logging framework that provides built-in themes and formatters, and a nice API to define your owns. Get the most out of Log by insta

Damien 825 Nov 6, 2022
Logging utility for Swift and Objective C

Swell - Swift Logging A logging utility for Swift and Objective C. ##Features Turn on logging during development, turn them off when building for the

Hubert Rabago 361 Jun 29, 2022
Most natural Swift logging

Evergreen Most natural Swift logging Evergreen is a logging framework written in Swift. It is designed to work just as you would expect, yet so versat

Nils Leif Fischer 72 Aug 12, 2022
A simple logging package for Swift

OhMyLog OhMyLog is a simple logging package for Swift. It supports the following features: Six logging levels ( ?? , ?? , ?? , ⚠️ , ?? , ?? ) Display

Junhao Wang 1 Jan 10, 2022
Simple logging for simples needs.

Simple logging for simples needs.

native.dev.br 0 May 30, 2022