Building Echo - Customizable log format

With Echo I want that we can customize the way our logs are print to the console. Therefor I need a way to describe/define a log format.


Observing a log I start by define Echo format as a string in which we have datetime, flag, filename, function name, line number, log message, ... We will name them Echo components to be coherent with the acoustic vocabulary I used for Echo.

struct Echo {
    var format = "[flag] [[datetime]] [[filename]:[line]] [message]"
}

We could make this nicer using enum and Printable protocol.

enum EchoComponent {
    case DateTime
    case Flag
    case Filename
    case Function
    case Line
    case case Message
}
extension EchoComponent: Printable {
    public var description: String {
        switch self {
            case .DateTime: return "[datetime]"
            case .Flag: return "[flag]"
            case .Filename: return "[filename]"
            case .Function: return "[function]"
            case .Line: return "[line]"
            case .Message: return "[message]"
        }
    }
}

Well! Now we just have to call format.stringByReplacingOccurrencesOfString() for each values of EchoComponent and then print the result to the console.

Ok! It works, but I think we can do better.

Last I said that Echo format is a string in which we have Echo components. Finally, I'm not agree with the word string.
First it doesn't provide any information on what composes Echo format (on this subject I encourage you to throw a glance at Scott Wlaschin talk about Domain Driven Design, F# and Types, most concepts can be applied to Swift).
Next we call stringByReplacingOccurrencesOfString() too many times to obtain the string to print, and that sucks ๐Ÿ‘Ž.


Let's restart.

What is left if we remove Echo components from Echo format previous definition? Separators.
So Separator is a good candidate to join Echo components. However Separator must have different value each time it is used (because we don't use the same separator between Filename and Line as between Line and Message).
Hopefully we have Swift stuff for that.

It is sometimes useful to be able to store associated values of other types alongside these member values. This enables you to store additional custom information along with the member value, and permits this information to vary each time you use that member in your code.The Swift Programming Language, Apple.

Our Echo format now look like this:

enum EchoComponent {
    case Datetime
    case Flag
    case Filename
    case Function
    case Line
    case Message
    case Separator(String)
}
struct Echo {
    var format: [EchoComponent] = [
        .Flag,
        .Separator(" ["),
        .Datetime,
        .Separator("] ["),
        .Filename,
        .Separator(":"),
        .Line,
        .Separator("] "),
        .Message
    ]
}

Wow! This code is more readable. Defining Echo format as an array of EchoComponent make more sense, reading its declaration we axacly know how it is composed and how we can use it.

We can go further and add the possibility to modify flags and datetime format using the same way, ๐Ÿ˜Ž.

enum EchoComponent {
    case Datetime(format: String)
    case Flag(flags: [EchoLevel: EchoFlag])
    case Filename
    case Function
    case Line
    case Message
    case Separator(String)
}
struct Echo {
    var format: [EchoComponent] = [
        .Flag(flags: [.Trace: "๐Ÿ’Š", .Debug:  "โ˜•๏ธ", .Info: "๐Ÿ’ก", .Warn: "โš ๏ธ", .Error: "โŒ", .Fatal: "๐Ÿ’ฃ", .Off: "๐Ÿ˜ถ"]),
        .Separator(" ["),
        .Datetime(format: "HH:mm:ss.SSS"),
        .Separator("] ["),
        .Filename,
        .Separator(":"),
        .Line,
        .Separator("] "),
        .Message
    ]
}

Now I think we can't do better. This code is concise, readable and we can add others EchoComponent if we need. Moreover, work with Echo format to obtain the log to print is nicer and easier, we just have to print the result of format.map({...}).reduce("", combine: +) (cf. Echo log method on GitHub).


This is the end of my first post of the « Building Echo » series, I hope you liked it and appreciate that you have read it. If you have any comments or suggestions, please feel free to contact me on my Twitter.

You can find Echo project sources on GitHub.

See you soon for the next episode. !)