Skip to content

Commit

Permalink
Logger Implemented.
Browse files Browse the repository at this point in the history
  • Loading branch information
shivankkiwitech committed Jan 8, 2019
1 parent cbc9e5d commit d6d75b6
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 0 deletions.
12 changes: 12 additions & 0 deletions KiwiPods.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
D69C64D121E334FF00E22013 /* NetworkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69C64D021E334FF00E22013 /* NetworkModel.swift */; };
D6B031612189D172003431C6 /* KiwiPods.h in Headers */ = {isa = PBXBuildFile; fileRef = D6B0315F2189D172003431C6 /* KiwiPods.h */; settings = {ATTRIBUTES = (Public, ); }; };
D6B031692189D2C9003431C6 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B031682189D2C9003431C6 /* NetworkManager.swift */; };
D6E90B7421E4D5BA0072876C /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E90B7321E4D5B90072876C /* Logger.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -50,6 +51,7 @@
D6B0315F2189D172003431C6 /* KiwiPods.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KiwiPods.h; sourceTree = "<group>"; };
D6B031602189D172003431C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D6B031682189D2C9003431C6 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = "<group>"; };
D6E90B7321E4D5B90072876C /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -153,6 +155,7 @@
isa = PBXGroup;
children = (
D6B0315F2189D172003431C6 /* KiwiPods.h */,
D6E90B7221E4D5B90072876C /* Logger */,
D66ADF5E21D504E5004499F2 /* ImagePicker */,
D66ADF5D21D504D8004499F2 /* Social */,
D66ADF3D21D50488004499F2 /* HashTextView */,
Expand All @@ -172,6 +175,14 @@
path = Networking;
sourceTree = "<group>";
};
D6E90B7221E4D5B90072876C /* Logger */ = {
isa = PBXGroup;
children = (
D6E90B7321E4D5B90072876C /* Logger.swift */,
);
path = Logger;
sourceTree = "<group>";
};
E7DA49B519516020E9531073 /* Frameworks */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -310,6 +321,7 @@
D66ADF4121D50488004499F2 /* HashTextView.swift in Sources */,
D66ADF5821D504B0004499F2 /* ImagePicker.swift in Sources */,
D66ADF4F21D504B0004499F2 /* ImagePickerController.swift in Sources */,
D6E90B7421E4D5BA0072876C /* Logger.swift in Sources */,
D66ADF5421D504B0004499F2 /* TwitterHelper.swift in Sources */,
D66ADF5521D504B0004499F2 /* ImagePickerCell.swift in Sources */,
);
Expand Down
234 changes: 234 additions & 0 deletions KiwiPods/Logger/Logger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
//
// Logger.swift
//
// Created by KiwiTech on 08/01/19.
// Copyright © 2019 KiwiTech. All rights reserved.
//

import Foundation
import UIKit

/**
* Singleton class to debugPrint custom log messages easier to read and analize. Based in glyphs and log levels
*/
public final class Logger {

private static let kLogDirectoryName = "Logs"

// MARK: Singleton Instance
class var shared: Logger {
struct Singleton {
static let instance = Logger()
}
return Singleton.instance
}

// Enum defining our log levels
public enum Level: Int {
case verbose
case debug
case info
case warning
case error
case severe

public var description: String {
switch self {
case .verbose:
return "Verbose"
case .debug:
return "Debug"
case .info:
return "Info"
case .warning:
return "Warning"
case .error:
return "Error"
case .severe:
return "Severe"
}
}

public static let all: [Level] = [.verbose, .debug, .info, .warning, .error, .severe]

func atLeast(_ level: Level) -> Bool {
return level.rawValue >= rawValue
}
}

// Configuration settings
private struct Configuration {
var logLevel: Level

var showLogLevel: Bool
var showThreadName: Bool
var showFunctionName: Bool
var showFileName: Bool
var showLineNumber: Bool

var writeToFile: Bool
}

private var configuration: Configuration!

private init() {
// Set the defaut level to error
configuration = Configuration(logLevel: .error, showLogLevel: true, showThreadName: false, showFunctionName: true, showFileName: true, showLineNumber: true, writeToFile: false)
}

// A shortcut method to configure the logger.
public class func setup(_ logLevel: Level, showLogLevel: Bool = true, showThreadName: Bool = false, showFunctionName: Bool = true, showFileName: Bool = true, showLineNumber: Bool = true, writeToFile: Bool = false) {

Logger.shared.configuration = Configuration(logLevel: logLevel, showLogLevel: showLogLevel, showThreadName: showThreadName, showFunctionName: showFunctionName, showFileName: showFileName, showLineNumber: showLineNumber, writeToFile: writeToFile)
}

// Log a message if the logger's log level is equal to or lower than the specified level.
private class func log(_ message: String, properties: [String: Any]? = nil, level: Level = .debug, fileName: String = #file, line: Int = #line, column: Int = #column, functionName: String = #function, writeToFile: Bool! = Logger.shared.configuration.writeToFile) {

let config = Logger.shared.configuration!

if config.logLevel.atLeast(level) {

var appendContents = Logger.formattedMessage(message, properties: properties, level: level, fileName: fileName, line: line, column: column, functionName: functionName)
debugPrint(appendContents)

if writeToFile {

let logFilePath = Logger.logFilePath()
if !FileManager.default.fileExists(atPath: logFilePath) {
appendContents = Logger.headerContent() + appendContents
debugPrint("Log file created")
} else {
appendContents = logdebugPrint() + appendContents
}

appendContents += "\n\n"

do {
try appendContents.write(toFile: logFilePath, atomically: true, encoding: String.Encoding.utf8)
} catch let error as NSError {
debugPrint("Unable to write : \(error.debugDescription)")
}
}
}
}

private class func formattedMessage(_ message: String, properties: [String: Any]?, level: Level, fileName: String, line: Int, column: Int, functionName: String) -> String {

let config = Logger.shared.configuration!
var appendContents = String()
appendContents += "\(Date().toString())"
appendContents += config.showLogLevel ? " [\(level.description)]" : ""
appendContents += config.showFileName ? " [\(sourceFileName(filePath: fileName))]" : ""
appendContents += config.showLineNumber ? " [\(line)]" : ""

if config.showThreadName {
let name = __dispatch_queue_get_label(nil)
let queuename = String(cString: name, encoding: .utf8)
appendContents += config.showThreadName ? " [\(String(describing: queuename))]" : ""
}

appendContents += config.showFunctionName ? " \(functionName)" : ""
appendContents += " -> \(message)"

if let properties = properties {

appendContents += "\nPROPERTIES ¬ \n"

for (key, _) in properties {
assert(properties[key] != nil, "Event property cannot be null")
appendContents += "\(key): \"\(String(describing: properties[key]))\"\n"
}
}

return appendContents
}

private class func sourceFileName(filePath: String) -> String {
let components = filePath.components(separatedBy: "/")
return components.isEmpty ? "" : components.last!
}

private class func logDirectoryPath() -> URL {
let url = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent(Logger.kLogDirectoryName)

if !FileManager.default.fileExists(atPath: url.path) {

do {
try FileManager.default.createDirectory(atPath: url.path, withIntermediateDirectories: false, attributes: nil)
debugPrint("Log directory created")
} catch let error as NSError {
debugPrint("Unable to create directory : \(error.debugDescription)")
}
}

return url
}

private class func todayDate() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.locale = Locale.current
formatter.timeZone = TimeZone.current
return "\(formatter.string(from: Date()))"
}

private class func logFilePath() -> String {
return "\(Logger.logDirectoryPath().appendingPathComponent(Logger.todayDate()).path).txt"
}

private class func headerContent() -> String {
return "Log Created: \(Logger.todayDate())\nApp Name: \(String(describing: Bundle.main.object(forInfoDictionaryKey: "CFBundleName")))\nApp Bundle Identifier: \(Bundle.main.bundleIdentifier ?? "Not available")\nDevice: \(UIDevice.current.name) (\(UIDevice.current.systemName) \(UIDevice.current.systemVersion))\nLocalization: \(String(describing: NSLocale.current.languageCode))\n\n"
}

private class func logdebugPrint() -> String {
var content: String?
let path = Logger.logFilePath()

if FileManager.default.fileExists(atPath: path) {
do {
content = try? String(contentsOfFile: path, encoding: String.Encoding.utf8)
}
}

return content ?? ""
}
}

// MARK: Logging methods
extension Logger {

public class func verbose(_ message: String, properties: [String: Any]? = nil, fileName: String = #file, line: Int = #line, column: Int = #column, functionName: String = #function, writeToFile: Bool? = nil) {
Logger.log(message, properties: properties, level: .verbose, fileName: fileName, line: line, column: column, functionName: functionName, writeToFile: writeToFile)
}

public class func debug(_ message: String, properties: [String: Any]? = nil, fileName: String = #file, line: Int = #line, column: Int = #column, functionName: String = #function) {
Logger.log(message, properties: properties, level: .debug, fileName: fileName, line: line, column: column, functionName: functionName)
}

public class func info(_ message: String, properties: [String: Any]? = nil, fileName: String = #file, line: Int = #line, column: Int = #column, functionName: String = #function, writeToFile: Bool? = nil) {
Logger.log(message, properties: properties, level: .info, fileName: fileName, line: line, column: column, functionName: functionName, writeToFile: writeToFile)
}

public class func warning(_ message: String, properties: [String: Any]? = nil, fileName: String = #file, line: Int = #line, column: Int = #column, functionName: String = #function) {
Logger.log(message, properties: properties, level: .warning, fileName: fileName, line: line, column: column, functionName: functionName)
}

public class func error(_ message: String, properties: [String: Any]? = nil, fileName: String = #file, line: Int = #line, column: Int = #column, functionName: String = #function, writeToFile: Bool? = nil) {
Logger.log(message, properties: properties, level: .error, fileName: fileName, line: line, column: column, functionName: functionName, writeToFile: writeToFile)
}

public class func severe(_ message: String, properties: [String: Any]? = nil, fileName: String = #file, line: Int = #line, column: Int = #column, functionName: String = #function) {
Logger.log(message, properties: properties, level: .severe, fileName: fileName, line: line, column: column, functionName: functionName, writeToFile: true)
}
}

internal extension Date {

func toString(format: String? = "MMM dd, yyyy") -> String {
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone.current
dateFormatter.dateFormat = format
return dateFormatter.string(from: self)
}
}

0 comments on commit d6d75b6

Please sign in to comment.