Skip to content

Instantly share code, notes, and snippets.

@kraigspear
Created May 18, 2018 07:52
Show Gist options
  • Save kraigspear/27fd34d45f648362479222b9474dd10e to your computer and use it in GitHub Desktop.
Save kraigspear/27fd34d45f648362479222b9474dd10e to your computer and use it in GitHub Desktop.
Logger Using CosmosDB
//
// Logger.swift
// SpearSwiftLib
//
// Created by Kraig Spear on 4/22/18.
// Copyright © 2018 spearware. All rights reserved.
//
import Foundation
// Enum for showing the type of Log Types
public enum LogEvent: String {
case error = "[‼️]" // error
case info = "[ℹ️]" // info
case debug = "[💬]" // debug
case verbose = "[🔬]" // verbose
case warning = "[⚠️]" // warning
case severe = "[🔥]" // severe
var name: String {
switch self {
case .error:
return "Error"
case .info:
return "Info"
case .debug:
return "Debug"
case .verbose:
return "Verbose"
case .warning:
return "Warning"
case .severe:
return "Severe"
}
}
}
// MARK: - Log
struct Log: CustomStringConvertible {
let time: Date
let deviceID: String
let level: String
let sourceFile: String
let lineNumber: Int
let column: Int
let functionName: String
let message: String
enum CodingKeys: String, CodingKey {
case time = "Time"
case deviceID = "DeviceID"
case level = "Level"
case sourceFile = "SourceFile"
case lineNumber = "LineNumber"
case column = "Column"
case functionName = "FunctionName"
case message = "Message"
}
var description: String {
return "\(time.toString()) \(level)[\(sourceFile)]:\(lineNumber) \(column) \(functionName) -> \(message)"
}
}
extension Log: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
let GMT = TimeZone(abbreviation: "GMT")!
let options: ISO8601DateFormatter.Options = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone, .withFractionalSeconds]
let timeAsJSONString = ISO8601DateFormatter.string(from: time, timeZone: GMT, formatOptions: options)
try container.encode(timeAsJSONString, forKey: Log.CodingKeys.time)
try container.encode(deviceID, forKey: Log.CodingKeys.deviceID)
try container.encode(level, forKey: Log.CodingKeys.level)
try container.encode(sourceFile, forKey: Log.CodingKeys.sourceFile)
try container.encode(lineNumber, forKey: Log.CodingKeys.lineNumber)
try container.encode(column, forKey: Log.CodingKeys.column)
try container.encode(functionName, forKey: Log.CodingKeys.functionName)
try container.encode(message, forKey: Log.CodingKeys.message)
}
}
public final class Logger {
static var dateFormat = "yyyy-MM-dd hh:mm:ssSSS"
static var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = dateFormat
formatter.locale = Locale.current
formatter.timeZone = TimeZone.current
return formatter
}
public static var deviceID: String?
public static func log(message: String,
event: LogEvent,
fileName: String = #file,
line: Int = #line,
column: Int = #column,
funcName: String = #function) {
let log = Log(time: Date(),
deviceID: Logger.deviceID ?? "undefined",
level: event.name,
sourceFile: sourceFileName(filePath: fileName),
lineNumber: line,
column: column,
functionName: funcName,
message: message)
#if IOS_SIMULATOR
// Logger.send(log)
#else
Logger.send(log)
#endif
print("\(Date().toString()) \(event.rawValue)[\(sourceFileName(filePath: fileName))]:\(line) \(column) \(funcName) -> \(message)")
}
private static func sourceFileName(filePath: String) -> String {
let components = filePath.components(separatedBy: "/")
return components.isEmpty ? "" : components.last!
}
private static func send(_ log: Log) {
guard let apiKey = Logger.apiKey,
let logURL = Logger.logURL else {
return
}
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
let baseUrl = URL(string: logURL)!
var networkPrameters = NetworkParameters()
_ = networkPrameters.addParam("code", value: apiKey)
let url = networkPrameters.NSURLByAppendingQueryParameters(baseUrl)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = try! JSONEncoder().encode(log)
let task = session.dataTask(with: request) { _, _, _ in
}
task.resume()
session.finishTasksAndInvalidate()
}
private static var apiKeyValue: String?
static var apiKey: String? {
if let apiKeyValue = Logger.apiKeyValue {
return apiKeyValue
}
let bundle = Bundle(for: Logger.self)
guard let filePath = bundle.path(forResource: "AppKeys", ofType: "plist"),
let plist = NSDictionary(contentsOfFile: filePath) as? [String: AnyObject],
let apiKey = plist["Log"] as? String
else {
assertionFailure("Missing API Key")
return nil
}
Logger.apiKeyValue = apiKey
return Logger.apiKeyValue
}
private static var logURLValue: String?
static var logURL: String? {
if let logURLValue = Logger.logURLValue {
return logURLValue
}
let bundle = Bundle(for: Logger.self)
guard let filePath = bundle.path(forResource: "AppKeys", ofType: "plist"),
let plist = NSDictionary(contentsOfFile: filePath) as? [String: AnyObject],
let logURL = plist["LogURL"] as? String
else {
assertionFailure("Missing API Key")
return nil
}
Logger.logURLValue = logURL
return Logger.logURLValue
}
}
private extension Date {
func toString() -> String {
return Logger.dateFormatter.string(from: self as Date)
}
}
@kraigspear
Copy link
Author

Swift Logger Class

This is part of my generic util project https://github.com/kraigspear/SpearSwiftLib, however, this code isn't pushed up yet

Notes

  1. API Keys + URL to a Azure function come from a plist in the project that isn't included.
  2. This being a purely static class is a bit questionable.
  3. IOS_SIMULATOR is defined as an 'Other Swift Flag' under Any iOS Simulator SDK. Only device builds are logged to the Azure function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment