Swiftpack.co - Package - RougeWare/Swift-Simple-Logging

SimpleLogging

A simple, cross-platform, lightweight logging framework powered by Swift's built-in features (like print and FileHandle).

This framework was built for both out-of-the-box simplicity when you just need logging, and powerful extensibility for when your enterprise app has specific logging needs.

Examples

Severities

Six severity levels are defined in this package, but you can define more if you please:

  • đŸ’Ŧ verbose - The lowest builtin priority; anything and everything might be logged at this level
  • 👩🏾‍đŸ’ģ debug - Usually not included in user-facing logs, but helpful messages for debugging issues in the field
  • ℹī¸ info - Usually the lowest level that appears in user logs, information for power-users who look at logs
  • ⚠ī¸ warning - Describing potential future problems. "Future" might be the next line, the next release, etc.
  • 🆘 error - Problems that just happened. A server log might only print lines of this severity or higher.
  • 🚨 fatal - The only fatal lines in a log file should be the last lines, in the event of a crash

This package also pairs these with some convenient global-scope functions for quick logging:

log(verbose: "Starting...")
log(debug: "TODO: Implement startup procedure")
log(info: "Startup done")
log(warning: "Future versions won't support the `--magically-work` flag")

if username == "BenLeggiero" {
    log(error: "You aren't allowed in here")
    return
}

if system.environment.isInvalid {
    log(fatal: "Environment is invalid")
    fatalError()
}

Example output, with a channel which allows all messages:

2020-05-19 22:30:06.362Z đŸ’Ŧ Starting...
2020-05-19 22:30:06.362Z 👩🏾‍đŸ’ģ TODO: Implement statup procedure
2020-05-19 22:30:06.362Z ℹī¸ Startup done
2020-05-19 22:30:06.362Z ⚠ī¸ Future versions won't support the `--magically-work` flag
2020-05-19 22:30:06.362Z 🚨 Environment is invalid

If you want, you can also specify these (and any you make) explicitly:

log(severity: .trace, "\(i) iterations so far")
log(severity: .critical, "The system is down!")

By default, severities are represented by emoji (like ℹī¸ for info and 🆘 for error) so a human can more-easily skim a log. Each can also be represented by a plaintext character or long name (for example, debug's names are 👩🏾‍đŸ’ģ, "d", and "debug"). You can set this this per-channel, in case your channel can't handle UTF-16, or in case you just don't like that.

Channels

By default, this just logs to the same place as Swift's print statement. Because enterprise apps have different needs, it can also log to stdout, stderr, and any FileHandle. Arbitrarily many of these can operate simultaneously. You can also specify this per-log-call or for all log calls.

If none is specified, the lowest allowed severity is info, since that's the lowest built-in severity which users might care about if they're looking at the logs, but not debugging the code itself.

LogManager.defaultChannels += [
    try LogChannel(name: "General Log File", location: .file(path: "/tmp/com.acme.AwesomeApp/general.log")),
    try LogChannel(name: "Debug Log File", location: .file(path: "/tmp/com.acme.AwesomeApp/debug.log"), lowestAllowedSeverity: .debug),
    try LogChannel(name: "Error Log File", location: .file(path: "/tmp/com.acme.AwesomeApp/errors.log"), lowestAllowedSeverity: .error, logSeverityNameStyle: .short),
]

In the above example, info and up log messages will go to Swift's print target, and to a general log file, whereas error and up log messages will go to a specific error log file (which won't contain emoji), and debug log messages (and up) go to a specific debug log file. All channels will receive all error and up log messages. For example:

log(verbose: "This is thrown away")
log(debug: "This only goes to the debug log channel")
log(info: "This goes to most of the log channels")
log(warning: "Same here")
log(error: "This is the first item in the error log; everything gets this")
log(fatal: "Everything gets this too")

The above lines will result in these logs:

Logging scope entry/exit

When tracing through code in your logs, it's common to put log statements when a scope is entered and when it's exited. This framework makes such an action first-class and quite easy:

func notable() {
    logEntry(); defer { logExit() }
    
    MyApp.notableOperation()
}
func longAsyncOperation() {
    logEntry(); defer { logExit() }
    
    DispatchQueue.main.async {
        logEntry(); defer { logExit() }
        
        longOperation()
    }
}

Github

link
Stars: 0

Used By

Total: 0

Releases

0.5.0 - Better error logging and custom log channel locations -

Big changes this time!

  • New CombinedLoggable and CombinedLogMessage to combine two messages in one log line
  • Added new log channel location: .custom, which takes one function that you can use to implement your own location
  • Replaced the confusing minimum log severity with a semantic LogSeverityFilter
  • Removed a bunch of odd log severity names. If you want these, feel free to implement them yourself - it's just a couple lines 😁
  • Added LoggableError to aide in logging errors
  • It now depends on FunctionTools for the new custom log channel location

0.4.4 - New Licensing -

License is now split into two semantic licenses depending on the entity obtaining the license.


Non-licensing patch changes

  • 0.4.4
    • Added a dynamic library product whose name is a valid bundle identifier
    • Deprecated previous dynamic library product because its name caused problems when submitting to the App Store
  • 0.4.3
    • Added alternative dynamic library product

0.3.0 - Better error logging -

Logging errors works much better now! Not only does log(error:) have a special variant for taking an error, but there's also a new log function log(errorIfThrows:backup:), which gracefully handles errors, both with a log message iff the given call throws, and with a backup if it's needed:

myVeryGoodFunction(count: log(errorIfThrows: try readCountFromDisk(), backup: 0))

That's one line to both handle an error and provide a backup without ignoring the error! Here's what that same operation would look like without this:

do {
    myVeryGoodFunction(count: try readCountFromDisk())
}
catch {
    log(error: error)
    myVeryGoodFunction(count: 0)
}

And sure, you could use try? and ??, but then you ignore the error entirely. Personally, I see myself using this log function quite often 😄


Additional Fixes

This release also fixes #4, where the file/line/function were not actually being logged. Whoops!

0.2.1 - Non-specialized `log` functions no longer have labels on the message parameter -

Before:

log(severity: .critical, loggable: "Important message")
log(severity: .trace, any: notableObject)

Now:

log(severity: .critical, "Important message")
log(severity: .trace, notableObject)

Patch Changes

  • 0.2.1 - Added GitHub Actions, only imported CoreGraphics on non-desktop Apple platforms

0.1.0 - First idea -

A simple logging framework for Swift