Zealous Logger v1.0.0

This commit is contained in:
Francisco Gindre 2021-03-11 16:54:26 -03:00
parent 22f9c06308
commit b4d4156e29
7 changed files with 317 additions and 60 deletions

25
Package.resolved Normal file
View File

@ -0,0 +1,25 @@
{
"object": {
"pins": [
{
"package": "CocoaLumberjack",
"repositoryURL": "https://github.com/CocoaLumberjack/CocoaLumberjack.git",
"state": {
"branch": null,
"revision": "b06614d2373a9830c87817144d47ce654980a2d7",
"version": "3.7.0"
}
},
{
"package": "swift-log",
"repositoryURL": "https://github.com/apple/swift-log.git",
"state": {
"branch": null,
"revision": "5d66f7ba25daf4f94100e7022febf3c75e37a6c7",
"version": "1.4.2"
}
}
]
},
"version": 1
}

View File

@ -15,16 +15,17 @@ let package = Package(
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack.git", from: "3.7.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "zealous-logger",
dependencies: []),
dependencies: [.product(name: "CocoaLumberjackSwift", package: "CocoaLumberjack")]),
.testTarget(
name: "zealous-loggerTests",
dependencies: ["zealous-logger"]),
dependencies: ["zealous-logger",
.product(name: "CocoaLumberjackSwift", package: "CocoaLumberjack")]),
]
)

View File

@ -8,65 +8,95 @@
import Foundation
import os
#if COCOAPODS
import CocoaLumberjack
#else
import CocoaLumberjackSwift
#endif
protocol ConcreteLogger {
func log(level: LogLevel, message: String, file: StaticString, function: StaticString, line: Int)
var level: LogLevel { get }
}
class ConsoleLogger: Logger {
enum LogLevel: Int {
case debug
case error
case warning
case event
case info
var concreteLogger: ConcreteLogger
init(concreteLogger: ConcreteLogger) {
self.concreteLogger = concreteLogger
}
enum LoggerType {
case osLog
case printerLog
func debug(_ message: String, file: StaticString, function: StaticString, line: Int) {
guard concreteLogger.level.rawValue == LogLevel.debug.rawValue else { return }
concreteLogger.log(level: concreteLogger.level, message: message, file: file, function: function, line: line)
}
var level: LogLevel
var loggerType: LoggerType
init(logLevel: LogLevel, type: LoggerType = .osLog) {
self.level = logLevel
self.loggerType = type
func error(_ message: String, file: StaticString, function: StaticString, line: Int) {
guard concreteLogger.level.rawValue <= LogLevel.error.rawValue else { return }
concreteLogger.log(level: concreteLogger.level, message: message, file: file, function: function, line: line)
}
private static let subsystem = Bundle.main.bundleIdentifier!
@available(OSX 10.12, *)
static let oslog = OSLog(subsystem: subsystem, category: "logs")
func debug(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue == LogLevel.debug.rawValue else { return }
log(level: "DEBUG 🐞", message: message, file: file, function: function, line: line)
}
func error(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.error.rawValue else { return }
log(level: "ERROR 💥", message: message, file: file, function: function, line: line)
}
func warn(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.warning.rawValue else { return }
log(level: "WARNING ⚠️", message: message, file: file, function: function, line: line)
func warn(_ message: String, file: StaticString, function: StaticString, line: Int) {
guard concreteLogger.level.rawValue <= LogLevel.warning.rawValue else { return }
concreteLogger.log(level: concreteLogger.level, message: message, file: file, function: function, line: line)
}
func event(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.event.rawValue else { return }
log(level: "EVENT ⏱", message: message, file: file, function: function, line: line)
func event(_ message: String, file: StaticString, function: StaticString, line: Int) {
guard concreteLogger.level.rawValue <= LogLevel.event.rawValue else { return }
concreteLogger.log(level: concreteLogger.level, message: message, file: file, function: function, line: line)
}
func info(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.info.rawValue else { return }
log(level: "INFO ", message: message, file: file, function: function, line: line)
}
private func log(level: String, message: String, file: String, function: String, line: Int) {
let fileName = (file as NSString).lastPathComponent
switch loggerType {
case .printerLog:
print("[\(level)] \(fileName) - \(function) - line: \(line) -> \(message)")
default:
os_log("[%{public}@] %{public}@ - %{public}@ - Line: %{public}d -> %{public}@", level, fileName, function, line, message)
}
func info(_ message: String, file: StaticString, function: StaticString, line: Int) {
guard concreteLogger.level.rawValue <= LogLevel.info.rawValue else { return }
concreteLogger.log(level: concreteLogger.level, message: message, file: file, function: function, line: line)
}
}
class OsLogLogger: ConcreteLogger {
var level: LogLevel
var oslog: OSLog
init(subsystem: String, level: LogLevel) {
self.level = level
self.oslog = OSLog(subsystem: subsystem, category: "logs")
}
private func describeLevel(_ level: LogLevel) -> String {
switch level {
case .debug:
return "DEBUG 🐞"
case .error:
return "ERROR 💥"
case .event:
return "EVENT ⏱"
case .info:
return "INFO "
case .warning:
return "WARNING ⚠️"
}
}
func log(level: LogLevel, message: String, file: StaticString, function: StaticString, line: Int) {
let fileName = (String(describing: file) as NSString).lastPathComponent
os_log("[%{public}@] %{public}@ - %{public}@ - Line: %{public}d -> %{public}@", log: oslog, describeLevel(level), fileName, String(describing: fileName), line, message)
}
}
class PrinterLogger: ConcreteLogger {
var level: LogLevel
init(level: LogLevel) {
self.level = level
}
func log(level: LogLevel, message: String, file: StaticString, function: StaticString, line: Int) {
let filename = (String(describing: file) as NSString).lastPathComponent
print("[\(level)] \(filename) - \(function) - line: \(line) -> \(message)")
}
}

View File

@ -0,0 +1,109 @@
//
// FileLogger.swift
//
//
// Created by Francisco Gindre on 3/9/21.
//
import Foundation
#if COCOAPODS
import CocoaLumberjack
#else
import CocoaLumberjackSwift
#endif
class DDLogger: Logger {
var concreteLogger: ConcreteLogger
init(concreteLogger: ConcreteLogger) {
self.concreteLogger = concreteLogger
}
public func debug(_ message: String, file: StaticString, function: StaticString, line: Int) {
DDLogDebug(message, level: toDDLogLevel(.debug), file: file, function: function, line: UInt(line))
}
public func info(_ message: String, file: StaticString, function: StaticString, line: Int) {
DDLogInfo(message, level: toDDLogLevel(.debug), file: file, function: function, line: UInt(line))
}
public func event(_ message: String, file: StaticString, function: StaticString, line: Int) {
DDLogInfo(message, level: toDDLogLevel(.debug), file: file, function: function, line: UInt(line))
}
public func warn(_ message: String, file: StaticString, function: StaticString, line: Int) {
DDLogWarn(message, level: toDDLogLevel(.debug), file: file, function: function, line: UInt(line))
}
public func error(_ message: String, file: StaticString, function: StaticString, line: Int) {
DDLogError(message, level: toDDLogLevel(.debug), file: file, function: function, line: UInt(line))
}
}
class LumberjackLogger: ConcreteLogger {
init(logDirectory: URL, level: LogLevel, alsoPrint: Bool) {
self.level = level
let manager = DDLogFileManagerDefault(logsDirectory: logDirectory.path)
manager.maximumNumberOfLogFiles = 2
let l = DDFileLogger(logFileManager: manager)
l.maximumFileSize = 1024 * 1024
l.rollingFrequency = 60 * 60 * 24 * 7 // weekly rollout
DDLog.add(l, with: toDDLogLevel(level))
self.fileLogger = l
if alsoPrint {
DDLog.add(DDOSLogger(subsystem: Bundle.main.bundleIdentifier, category: "application logs") , with: toDDLogLevel(level))
}
}
var fileLogger: DDFileLogger
var level: LogLevel
/**
This function does nothing because DDLog uses a Shared instance architecture where everything goes through the DDLog class shared instance.
*/
func log(level: LogLevel, message: String, file: StaticString, function: StaticString, line: Int) {
// This is fine
}
}
@inlinable
func levelToDDLogFlag(_ level: LogLevel) -> DDLogFlag {
switch level {
case .debug:
return .debug
case .error:
return .error
case .info:
return .info
case .event:
return .info
case .warning:
return .warning
}
}
@inlinable
func toDDLogLevel(_ level: LogLevel) -> DDLogLevel {
switch level {
case .debug:
return .all
case .error:
return .error
case .event:
return .info
case .info:
return .info
case .warning:
return .warning
}
}

View File

@ -4,14 +4,43 @@ import Foundation
*/
public protocol Logger {
func debug(_ message: String, file: String, function: String, line: Int)
func debug(_ message: String, file: StaticString, function: StaticString, line: Int)
func info(_ message: String, file: String, function: String, line: Int)
func info(_ message: String, file: StaticString, function: StaticString, line: Int)
func event(_ message: String, file: String, function: String, line: Int)
func event(_ message: String, file: StaticString, function: StaticString, line: Int)
func warn(_ message: String, file: String, function: String, line: Int)
func warn(_ message: String, file: StaticString, function: StaticString, line: Int)
func error(_ message: String, file: String, function: String, line: Int)
func error(_ message: String, file: StaticString, function: StaticString, line: Int)
}
public enum LogLevel: Int {
case debug
case error
case warning
case event
case info
}
public enum LoggerType {
case osLog(subsystem: String) // prints to OSLog
case printerLog // prints to console
case fileLogger(logsDirectory: URL, alsoPrint: Bool) // prints logs to files
}
public class ZealousLogger {
public static func logger(_ type: LoggerType, level: LogLevel) -> Logger {
switch type {
case .fileLogger(let url, let alsoPrint):
return DDLogger(concreteLogger: LumberjackLogger(logDirectory: url, level: level, alsoPrint: alsoPrint))
case .osLog(let subsystem):
return ConsoleLogger(concreteLogger: OsLogLogger(subsystem: subsystem, level: level))
case .printerLog:
return ConsoleLogger(concreteLogger: PrinterLogger(level: level))
}
}
}

View File

@ -2,14 +2,40 @@ import XCTest
@testable import zealous_logger
final class zealous_loggerTests: XCTestCase {
func testExample() {
func testFileLogger() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
//XCTAssertEqual(zealous_logger().text, "Hello, World!")
XCTAssertNotNil(ZealousLogger.logger(.printerLog, level: .debug) as? ConsoleLogger)
let url = FileManager.default.temporaryDirectory
let fileLogger = ZealousLogger.logger(.fileLogger(logsDirectory: url, alsoPrint: true), level: .debug)
fileLogger.debug("test 123", file: #file, function: #function, line: #line)
guard let logfile = try FileManager.default.contentsOfDirectory(atPath: url.path).first(where: { $0.contains(Bundle.main.bundleIdentifier!)})
else {
XCTFail("no logfile found! in \(url)")
return
}
let logfilePath = url.appendingPathComponent(logfile).path
let logAttributes = try FileManager.default.attributesOfItem(atPath: logfilePath)
let firstLogfileSize = logAttributes[FileAttributeKey.size] as! NSNumber
fileLogger.debug("test 123", file: #file, function: #function, line: #line)
let logAttributes2 = try FileManager.default.attributesOfItem(atPath: logfilePath)
let secondLogfileSize = logAttributes2[FileAttributeKey.size] as! NSNumber
XCTAssertGreaterThan(secondLogfileSize.uint64Value,firstLogfileSize.uint64Value)
}
static var allTests = [
("testExample", testExample),
("testFileLogger", testFileLogger),
]
}

37
zealous-logger.podspec Normal file
View File

@ -0,0 +1,37 @@
#
# Be sure to run `pod lib lint zealous-logger.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'zealous-logger'
s.version = '1.0.0'
s.summary = 'Zealous Logger is a small, simple, discrete but yet enthusiastic logger'
s.description = <<-DESC
Zealous Logger is a small, simple, discrete but yet enthusiastic logger. Designed for those who need logs, but not snitches. We acknowledge that Application Logs are needed for troubleshooting and development purposes. That doesn't mean that you should choose between logging and your users' privacy. We believe that Privacy is a Human Right, so logs shouldn't leave your device unless you explicitly want to. We write this small library with that in mind.
DESC
s.homepage = 'https://github.com/zcash-hackworks/zealous-logger'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Francisco \'Pacu\' Gindre' => 'francisco.gindre@gmail.com' }
s.source = { :git => 'https://github.com/zcash-hackworks/zealous-logger', :tag => s.version.to_s }
s.ios.deployment_target = '12.0'
s.osx.deployment_target = '10.12'
s.source_files = 'Sources/zealous-logger/**/*.{swift}'
s.swift_version = '5.1'
s.dependency 'CocoaLumberjack/Swift', '~> 3.7.0'
s.frameworks = 'OSLog'
s.test_spec 'Tests' do | test_spec |
test_spec.source_files = 'Tests/zealous-loggerTests/**/*.{swift}'
test_spec.dependency 'CocoaLumberjack/Swift', '~> 3.7.0'
end
end