apple/swift-logコンパチのSwiftログライブラリ「Puppy」をつくった

先日、[Puppy](https://github.com/sushichop/Puppy)というSwiftログライブラリをつくりました。本ライブラリは、[apple/swift-log](https://github.com/apple/swift-log/)のREADMEにも**apple/swift-logのバックエンドコンパチログライブラリの1つ**としても記載されています。また、**単体の(例えばiOS向けの)ログライブラリとしても使うことができる**ようになっており、LinuxとDarwin(macOS/iOS/tvOS/watchOS)のいずれの環境でも動作します。サーバーサイドSwiftに興味のある方はもちろんのことiOSアプリ等を開発をする方にも一度使ってみてもらえればと思います。
https://github.com/sushichop/Puppy
**※ STARをもらえると励みになります**🙂 

# なぜつくったのか？

私はObjective-C全盛時代によく使われていたログライブラリ[CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack)のメンテナの1人として今現在もメンテナ活動を続けています。~~少なくとも昔は~~よくできたライブラリだとは思いますが、Swift環境では使いにくい、Linux環境では使えない等の問題があります。
私が必要としていたのは、1つのライブラリでLinux、Dwarinのいずれの環境でも十分に使えること、及びログローテーションをサポートしていることでした。**特に必要としていたのはLinux環境におけるファイルログローテーション機能です。**
既存のOSSのSwiftログライブラリでは私の需要を満たせなかったので、自ら作ることにしました。

|ライブラリ名|Linuxサポート|syslog(Linux)サポート|ログローテーション機能|apple/swift-logサポート|
|:---:|:---:|:---:|:---:|:---:|
|SwiftyBeaver|✅|N/A|N/A|N/A|
|XCGLogger|N/A|N/A|✅|N/A|
|**Puppy**|✅|✅|✅|✅|

# 動作環境

- Swift 5.0以上(Swift 5.3以上推奨)
- CocoaPods、Carthage、Swift Package Manager

# 使い方

ロギング先としてコンソール、ファイル、syslog(Linux)等をサポートしています。カスタムログフォーマットにも対応しています。詳しくは[sushichop/Puppy](https://github.com/sushichop/Puppy)のREADMEを参照してもらえればと思いますが、以下に使い方の例を2つ簡単に書いておきます。

## 使い方その1(ファイルログローテーション)

下記はファイルへロギングしつつ、10MBのログファイルを5つまでログローテーションする例です。`FileRotationLogger`の第1引数にはIDとなる逆FQDN形式の文字列を指定してください。第2引数にはファイル名を指定します。ログローテーション完了通知については`delegate`で実現しています。

```swift
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)")
    }
}
```

## 使い方その2(apple/swift-logバックエンド)

apple/swift-logバックエンドとして使う場合は、Carthageは利用できません。これはapple/swift-logがCarthageをサポートしていないためです。CocoaPodsまたはSwift Package Managerを使ってインテグレーションしてください。
下記はコンソールとsyslogへロギングする例です。`Puppy`に加えて`Logging`もインポートする必要がある点に注意ください。

```swift
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.
```

# その他(補足)

本ライブラリでは、あえて実装していない機能等もあります。その1つが`__dispatch_queue_get_label(nil)`関数によるDispatchキューラベル取得機能です。これは、Linux環境で使えないこと、及び[ここ](https://github.com/apple/swift-log/issues/18#issuecomment-474867384)にもあるように本関数を呼び出すことにより、潜在的にクラッシュする可能性があるためです。
どうしても使いたい場合は、本ライブラリでは`LogFormattable`プロトコルによるカスタムログフォーマットに対応していますので、ライブラリ利用者は必要に応じて本プロトコル実装の中で、上記関数を呼び出すことにより対応できます。

# 最後に

今回つくったSwiftログライブラリ[Puppy](https://github.com/sushichop/Puppy)については、Linux/Darwinの両環境で動作することを重視しており、今後も適宜改版していく予定です。サーバサイドSwift盛り上がっていくといいですね🙂