Swiftpack.co - sushichop/Puppy as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by sushichop.
sushichop/Puppy 0.6.0
A flexible logging library written in Swift
⭐️ 97
🕓 10 weeks ago
iOS macOS watchOS tvOS linux
.package(url: "https://github.com/sushichop/Puppy.git", from: "0.6.0")


Swift5.6+ release CocoaPods CI codecov license

platforms SwiftPM|CMake|Bazel|Carthage

Puppy is a flexible logging library written in Swift 🐶

It supports multiple transports(console, file, syslog, and oslog) as loggers. It not only works alone, but also as a backend for apple/swift-log.

Furthermore, it has file log rotation feature and you can also customize the log format as you like. And it supports cross-platform(Darwin, Linux, and Windows).


  • Written in Swift.
  • Supports cross-platform(Darwin, Linux, and Windows).
  • Supports console, file, syslog, and oslog as loggers.
  • Supports automatic log rotation about file logger.
  • Also Works as a backend for apple/swift-log.


Basic Usage

Logging to mutliple transports(e.g. console and file). It is recommended that the first argument of each logger be a unique reverse-order FQDN since it is also used internally for a DispatchQueue's label.

import Puppy

let console = ConsoleLogger("com.example.yourapp.console", logLevel: .info)
let fileURL = URL(fileURLWithPath: "./foo.log").absoluteURL
let file = FileLogger("com.example.yourapp.file",
                      logLevel: .info,
                      fileURL: fileURL,
                      filePermission: "600")  // Default permission is "640". 

var log = Puppy()

log.debug("DEBUG message")  // Will NOT be logged.
log.info("INFO message")    // Will be logged.
log.error("ERROR message")  // Will be logged.

Use file log rotation

Logging to file and use log rotation feature.

import Puppy

class ViewController: UIViewController {
    let fileURL = URL(fileURLWithPath: "./logs/foo.log").absoluteURL
    let rotationConfig = RotationConfig(suffixExtension: .date_uuid,
                                        maxFileSize: 10 * 1024 * 1024,
                                        maxArchivedFilesCount: 3)
    let delegate = SampleFileRotationDelegate()
    let fileRotation = try! FileRotationLogger("com.example.yourapp.filerotation",
                                                fileURL: fileURL,
                                                rotationConfig: rotationConfig,
                                                delegate: delegate)

    override func viewDidLoad() {
        var log = Puppy()
        log.info("INFO message")
        log.warning("WARNING message")

class SampleFileRotationDelegate: FileRotationLoggerDelegate {
    func fileRotationLogger(_ fileRotationLogger: FileRotationLogger,
                            didArchiveFileURL: URL, toFileURL: URL) {
        print("didArchiveFileURL: \(didArchiveFileURL), toFileURL: \(toFileURL)")
    func fileRotationLogger(_ fileRotationLogger: FileRotationLogger,
                            didRemoveArchivedFileURL: URL) {
        print("didRemoveArchivedFileURL: \(didRemoveArchivedFileURL)")

Use with apple/swift-log

Logging to multiple transports(e.g. console and syslog) as a backend for apple/swift-log.

import Puppy
import Logging

let console = ConsoleLogger("com.example.yourapp.console")
let syslog = SystemLogger("com.example.yourapp.syslog")

var puppy = Puppy()

LoggingSystem.bootstrap {
    var handler = PuppyLogHandler(label: $0, puppy: puppy)
    // Set the logging level.
    handler.logLevel = .trace
    return handler

var log = Logger(label: "com.example.yourapp.swiftlog")

log.trace("TRACE message")  // Will be logged.
log.debug("DEBUG message")  // Will be logged.

Here is a practical example of using Puppy with Vapor, which uses apple/swift-log internally.

import App
import Vapor  // Vapor 4.67.4
import Puppy

let fileURL = URL(fileURLWithPath: "./server-logs/bar.log").absoluteURL
let rotationConfig = RotationConfig(suffixExtension: .numbering,
                                    maxFileSize: 30 * 1024 * 1024,
                                    maxArchivedFilesCount: 5)
let fileRotation = try FileRotationLogger("com.example.yourapp.server",
                                          fileURL: fileURL,
                                          rotationConfig: rotationConfig)
var puppy = Puppy()

// https://docs.vapor.codes/basics/logging/
var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env) { (logLevel) -> (String) -> LogHandler in
    return { label -> LogHandler in
        var handler = PuppyLogHandler(label: label, puppy: puppy)
        handler.logLevel = .info
        return handler
let app = Application(env)
defer { app.shutdown() }
try configure(app)
try app.run()

Customize the logging format

Customize the log format using Formattable protocol. Logging to oslog for example.

import Puppy

class ViewController: UIViewController {
    let logFormat = LogFormatter()
    let oslog = OSLogger("com.yourapp.oslog", logFormat: logFormat)

    override func viewDidLoad() {
        var log = Puppy()
        log.info("INFO message")
        log.warning("WARNING message")

struct LogFormatter: LogFormattable {
    func formatMessage(_ level: LogLevel, message: String, tag: String, function: String,
                       file: String, line: UInt, swiftLogInfo: [String : String],
                       label: String, date: Date, threadID: UInt64) -> String {
        let date = dateFormatter(date)
        let fileName = fileName(file)
        let moduleName = moduleName(file)
        return "\(date) \(threadID) [\(level.emoji) \(level)] \(swiftLogInfo) \(moduleName)/\(fileName)#L.\(line) \(function) \(message)".colorize(level.color)

Create a custom logger

You can also create your own custom logger. The custom logger needs to conform to Loggerable protocol.

@preconcurrency import Dispatch
import Puppy

public struct CustomLogger: Loggerable {
    public let label: String
    public let queue: DispatchQueue
    public let logLevel: LogLevel
    public let logFormat: LogFormattable?

    public init(_ label: String, logLevel: LogLevel = .trace, logFormat: LogFormattable? = nil) {
        self.label = label
        self.queue = DispatchQueue(label: label)
        self.logLevel = logLevel
        self.logFormat = logFormat

    public func log(_ level: LogLevel, string: String) {
        // Implements the logging feature here.


Puppy is available under the MIT license. See the LICENSE file for details.


Stars: 97
Last commit: 9 minutes ago
jonrohan Something's broken? Yell at me @ptrpavlik. Praise and feedback (and money) is also welcome.


Related Packages

Release Notes

10 weeks ago

Supports Swift 5.6 or later.

There are several breaking changes. Please check README for details.

  • Disable bitcode. #54
  • Update platform versions. #55
  • Update to Xcode 14.0.1 and Swift 5.6 or later. #56
  • Still use Xcode 13.4.1 for executing pod lib lint. #57
  • Remove a property of type FileHandle. #58
  • Remove the argument named asynchronous. #59
  • Remove and change the properties. #60
  • Change minimum platform versions. #61
  • Adopt Sendable and Loggerable. #62
  • Remove the dependency in podspec. #66
  • Update to Xcode 14.1. #69
  • Use #fileID instead of #file. #70
  • Add another example of using Puppy with Vapor. #71
  • Use struct instead of class. #72

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics