A flexible logging library written in Swift

Overview

Puppy

Swift5.0+ CocoaPods Carthage SwiftPM platforms platforms

release CI codecov license

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.

Features

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

Examples

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")
let fileURL = URL(fileURLWithPath: "./foo.log").absoluteURL
let file = FileLogger("com.example.yourapp.file", fileURL: fileURL)

let log = Puppy()
// Set the logging level.
log.add(console, withLevel: .warning)
log.add(file, withLevel: .warning)

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

Use with apple/swift-log

Logging to mutliple 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")

let puppy = Puppy.default
puppy.add(console)
puppy.add(syslog)

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

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

Use file log rotation

Logging to file and use log rotation feature.

import Puppy

class ViewController: UIViewController {
    let delegate = SampleFileRotationDelegate()
    override func viewDidLoad() {
        super.viewDidLoad()
        let fileRotation = try! FileRotationLogger("com.example.yourapp.filerotation",
                                                   fileURL: "./rotation/foo.log")
        fileRotation.maxFileSize = 10 * 1024 * 1024
        fileRotation.maxArchivedFilesCount = 5
        fileRotation.delegate = delegate
        let log = Puppy()
        log.add(fileRotation)
        log.info("INFO message")
        log.warning("WARNING message")
    }
}

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

Customize the log format

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

import Puppy

class ViewController: UIViewController {    
    override func viewDidLoad() {
        super.viewDidLoad()
        let oslog = OSLogger("com.yourapp.oslog")
        oslog.format = LogFormatter()
        let log = Puppy()
        log.add(oslog)
        log.info("INFO message")
        log.warning("WARNING message")
    }
}

class 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 file = shortFileName(file)
        return "\(date) \(threadID) [\(level.emoji) \(level)] \(file)#L.\(line) \(function) \(message)"
    }
}

License

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

Comments
  • Add instructions for setting up with Vapor web framework

    Add instructions for setting up with Vapor web framework

    There is a bit of a gap with Vapor logging to a file on the server. If you search logging in the Vapor Discord, there are quite a few people over the years asking for a way to log to a file and the answer is usually to use any swift-log backend (Vapor's logging is built on top of swift-log)... Unfortunately most of that list is only built for iOS or is too big a dependency to add for simple file logging. After some trial and error with different packages, Puppy was the package that was straight forward and just worked. This has been tested on Xcode 14.1 and Ubuntu 18.04.6 with no issues.

    In a Vapor 4 project, add the following to your Sources/Run/main.swift file:

    import App
    import Vapor
    import Puppy
    
    class fileRotationDelegate: FileRotationLoggerDelegate {
        func fileRotationLogger(
            _ fileRotationLogger: FileRotationLogger,
            didArchiveFileURL: URL,
            toFileURL: URL
        ) {
            print("didArchiveFileURL: \(didArchiveFileURL), toFileURL: \(toFileURL)")
        }
    
        func fileRotationLogger(
            _ fileRotationLogger: FileRotationLogger,
            didRemoveArchivedFileURL: URL
        ) {
            print("didRemoveArchivedFileURL: \(didRemoveArchivedFileURL)")
        }
    }
    
    let fileURL = URL(fileURLWithPath: "./Logs/info.log").absoluteURL
    let fileRotation = try FileRotationLogger(
        "com.example.yourapp.file",
        fileURL: fileURL,
        filePermission: "600"
    )
    
    let delegate = fileRotationDelegate()
    fileRotation.delegate = delegate
    
    var puppy = Puppy.default
    puppy.add(fileRotation)
    
    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()
    
    opened by neilhamilton 4
  • Puppy tries to delete all files in directory

    Puppy tries to delete all files in directory

    When using an existing directory for log files, Puppy will try to delete all files in the directory when rotating logs, not just the Puppy logs. To solve this I put my logs into a specific directory. This is fine, but generally people may want to put logs into a generic 'log' directory.

    But .... puppy is great. File rotation logging is why I use it. Thanks.

    opened by keniwhat 4
  • FileRotationLogger crashes when archiving full log files

    FileRotationLogger crashes when archiving full log files

    Hi!

    In FileRotationLogger.swift the function rotateFiles() fails at line 109 during the following instruction:

    try FileManager.default.moveItem(at: fileURL, to: archivedFileURL)
    

    This happens because in Formatter.swift the function dateFormatter(_:, locale:, dateFormat:, timeZone:) creates a timestamp that includes colons (:) which the file system does not like.

    One potential solution is changing the dateFormat in line 13 of Formatter.swift from "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" to "yyyy-MM-dd'T'HH-mm-ss.SSS". This removes the explicit colons in the original date format string, as well as an implicit colon which is added by ZZZZZ which expands to an HH:MM style presentation of the timezone.

    You might also consider reducing the complexity of this string further, since I'd argue if a detailed timestamp is necessary then it can be placed inside the log file, rather than in the file name.

    One alternative could be to structure like this:

    myLog.log
    myLog.log.1
    myLog.log.2
    

    This proposal would simplify the implementation of the rest of the rotateFiles method that is concerned with removing old archived files.

    opened by tucknology 4
  • Empty permissions create on log rotation

    Empty permissions create on log rotation

    On macOS (Big Sur) with Apple Silicon chipset (m1), when a log file is rotated, when the new empty log file is created, it is created with empty permissions (------------). This causes the application to crash with an unexpected nil optional for the file handle (failed to open file for writing?).

    NOTE: This looks like a macOS bug where the 'umask' is not taken into account, or is lost at the point of log rotation, because initially a file with proper permissions is created (if not, we could not get to the first point of log rotation).

    NOTE: THIS ONLY HAPPENS ON APPLE SILICON.

    Changing the openFile function as follows fixes the issue:

    let successful = FileManager.default.createFile(atPath: fileURL.path, contents: nil, attributes: [FileAttributeKey.posixPermissions: 0o660])

    Obviously a better solution would be to allow the attributes to be specified via some other means, eg: FileLoggerDelegate protocol.

    Thanks. Still a great framework :-)

    opened by keniwhat 3
  • On swift 5.7 I get could not build Objective-C module 'Puppy' - version 0.5.1

    On swift 5.7 I get could not build Objective-C module 'Puppy' - version 0.5.1

    When

    I add puppy like private var puppy = Puppy.default

    Then

    This only happens when I do swift build -c release with xcode 14.0.1 or in terminal with swift-driver version: 1.62.8 Apple Swift version 5.7 (swiftlang-5.7.0.127.4 clang-1400.0.29.50) Target: arm64-apple-macosx12.0

    The whole output

    ~/.build/arm64-apple-macosx/release/Puppy.build/module.modulemap:2:12: error: header '~/.build/arm64-apple-macosx/release/Puppy.build/Puppy-Swift.h' not found
        header "~/.build/arm64-apple-macosx/release/Puppy.build/Puppy-Swift.h"
               ^
    ~/Sources/Utilities/Logging.swift:3:8: error: could not build Objective-C module 'Puppy'
    import Puppy
           ^
    [504/510] Compiling Utilities Logging.swift
    

    It does not happen when I build Puppy on its own.

    I have mac with info

    Software:
    
        System Software Overview:
    
          System Version: macOS 12.6 (21G115)
          Kernel Version: Darwin 21.6.0
          Boot Volume: Macintosh HD
          Boot Mode: Normal
          User Name: Stijn Willems (stijnwillems)
          Secure Virtual Memory: Enabled
          System Integrity Protection: Enabled
          Time since boot: 47 minutes
    
    Hardware:
    
        Hardware Overview:
    
          Model Name: MacBook Pro
          Model Identifier: MacBookPro18,2
          Chip: Apple M1 Max
          Total Number of Cores: 10 (8 performance and 2 efficiency)
          Memory: 64 GB
          System Firmware Version: 7459.141.1
          OS Loader Version: 7459.141.1
          Serial Number (system): Y63LWPG9VF
          Hardware UUID: 1B4FA833-0112-5EC9-B392-86A2D549DBE0
          Provisioning UDID: 00006001-0008712E3402801E
          Activation Lock Status: Enabled
    

    Workaround

    adding puppy like private var puppy = Puppy() fixes the issue 🥴

    opened by doozMen 2
  • How to make a custom logger?

    How to make a custom logger?

    I'd like to make a custom logger that would log to a new target, specifically in my case Firebase Crashlytics. Looking at ConsoleLogger which inherits from BaseLogger, I would think that all I needed to do would be to define my own CrashlyticsLogger with an overridden log() function.

    However, since the BaseLogger class is defined as public instead of open I'm not able to do so, with the compiler error: Cannot inherit from non-open class 'ConsoleLogger' outside of its defining module.

    Is there some other mechanism that I should be using to make custom loggers? 🤔

    opened by dhiraj 2
  • Update Package.swift

    Update Package.swift

    XCode sometimes gets confused and doesn't resolve the dependency correctly, if the .git is omitted from the dependency url. When it happens, XCode will complain about missing package Logging

    opened by WarWithinMe 2
  • Changing the logLevel centrally

    Changing the logLevel centrally

    For some reason I have to create my own copy of PuppyLogHandler so I can enable/disable debug logging centrally. Whenever I try to use PuppyLogHandler (a single instance for all Loggers) and then set the logLevel, it's not taken into account. However, If I try my cloned class, it works. Pretty strange.

    The only issue is that I can't use the puppy.logMessage(...) method because it's not public.

    Is it possible to expose it? Or alternatively allow the individual methods (error, trace,...) to also pass swiftLogInfo?

    Here's how I'm using it:

    private static var handler: PuppyLogHandler?
    
    private static func initLogging() {
    // ... (more code)
            let puppy = Puppy(loggers: loggers)
            handler = PuppyLogHandler(label: "\(LOGGING_PREFIX).PuppyLogHandler", puppy: puppy)
    
            // Set the default logging level.
            #if DEBUG
            handler?.logLevel = .debug
            #else
            handler?.logLevel = .info
            #endif
    
            LoggingSystem.bootstrap { _ in
                return handler!
            }
        }
        
        static func enableDebugLogging(_ enableDebugLogging: Bool) {
            handler?.logLevel = enableDebugLogging ? .debug : .info
        }
    
    opened by drallgood 0
  • Problem with module compilation in swift

    Problem with module compilation in swift

    Hi, I have problem after adding the swift package with Puppy to my application. Could anyone please tell me what I am doing wrong? I am trying on macOS on Intel machine with XCode Version 13.4 (13F17a).

    Undefined symbols for architecture x86_64: "type metadata accessor for Puppy.FileLogger", referenced from: Action.metoda() -> () in Logger.o "Puppy.FileLogger.__allocating_init(_: Swift.String, fileURL: Foundation.URL, filePermission: Swift.String, flushMode: Puppy.FileLogger.FlushMode) throws -> Puppy.FileLogger", referenced from: Action.metoda() -> () in Logger.o "Puppy.Puppy.__allocating_init() -> Puppy.Puppy", referenced from: one-time initialization function for log in Logger.o "type metadata accessor for Puppy.Puppy", referenced from: one-time initialization function for log in Logger.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)

    question 
    opened by klukaspl 2
Releases(0.6.0)
  • 0.6.0(Nov 29, 2022)

    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
    Source code(tar.gz)
    Source code(zip)
  • 0.5.1(Sep 24, 2022)

  • 0.5.0(Feb 28, 2022)

    Puppy 0.5.0 supports Windows!

    • Remove concurrency features. #39
    • Fix the default rotation type to numbering. #40
    • Change the type of intPermission to UInt16. #41
    • Output a message prompting to override. #42
    • Use String type for filePermission. #43
    • Add error descriptions to FileError. #44
    • Support Windows. #45
    • Update GitHub Actions. #46
    Source code(tar.gz)
    Source code(zip)
  • 0.4.0(Jan 31, 2022)

    [NOTICE] The specifications of FileLogger and FileRotationLogger have been changed(#32, #36). Please check README as well.

    • FileRotatonLogger inherits FileLogger. #29
    • Add asynchronous methods for delete and flush. #30
    • Add test_spec in podspec. #31
    • Add suffix extension types for FileRotationLogger. #32
    • FileRotationLoggerDelegate Fix Spelling. #34
    • Add option for file permission. #36
    • Import more precisely. #38
    Source code(tar.gz)
    Source code(zip)
  • 0.3.1(Aug 18, 2021)

  • 0.3.0(Aug 7, 2021)

    • Update cmake-build for Linux. #19
    • Workaround for carthage build in both Xcode 12 and 13. #20
    • Follow up Integrate modules into Xcode. #22
    • Make BaseLogger inheritable outside of the module(Puppy). #23
    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Jun 16, 2021)

    • Instantiate DispatchQueue and add an argument named of asynchronous. #9
    • Remove colons from log rotation file name(extension). #11
    • Use AnyObject for protocol inheritance instead of class. #13
    • Add carthage-build-xcframeworks. #14
    • Specify Linux platform. #15
    • Integrate modules in Xcode and Podspec. #16
    • Add bazel-build. #17
    • Add cmake/ninja-build. #18
    Source code(tar.gz)
    Source code(zip)
  • 0.1.2(Dec 5, 2020)

  • 0.1.1(Oct 20, 2020)

  • 0.1.0(Oct 18, 2020)

Owner
Koichi Yokota
Koichi Yokota
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
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
A fast & simple, yet powerful & flexible logging framework for Mac and iOS

CocoaLumberjack CocoaLumberjack is a fast & simple, yet powerful & flexible logging framework for macOS, iOS, tvOS and watchOS. How to get started Fir

null 12.9k Jan 9, 2023
A modern, flexible logging tool

NSLogger NSLogger is a high performance logging utility which displays traces emitted by client applications running on macOS, iOS and Android. It rep

Florent Pillet 5k Dec 22, 2022
Willow is a powerful, yet lightweight logging library written in Swift.

Willow Willow is a powerful, yet lightweight logging library written in Swift. Features Requirements Migration Guides Communication Installation Cocoa

Nike Inc. 1.3k Nov 16, 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
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
Swift Logging Utility for Xcode & Google Docs

QorumLogs Swift Logging Utility in Xcode & Google Docs

Goktug Yilmaz 777 Jul 15, 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
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
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
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