Skip to content

Commit

Permalink
rewrite the logging utility and make a launchdaemon
Browse files Browse the repository at this point in the history
  • Loading branch information
erikng committed Feb 15, 2021
1 parent bcbc257 commit 70bd820
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 151 deletions.
123 changes: 8 additions & 115 deletions Nudge/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,129 +2,22 @@
// Logger.swift
// Nudge
//
// Created by Rory Murdock on 10/2/21.
// Created by Rory Murdock on 2/10/21.
//

import Foundation
import os

let bundleID = Bundle.main.bundleIdentifier

func getLibraryDirectory() -> URL {
let paths = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)
let LibraryDirectory = paths[0]
return LibraryDirectory
}
let bundleID = Bundle.main.bundleIdentifier ?? "com.github.macadmins.Nudge"
let utilsLog = Logger(subsystem: bundleID, category: "utilities")
let osLog = Logger(subsystem: bundleID, category: "operating-system")
let loggingLog = Logger(subsystem: bundleID, category: "logging")
let prefsLog = Logger(subsystem: bundleID, category: "preferences")
let uiLog = Logger(subsystem: bundleID, category: "user-interface")

class NudgeLogger {

// Get the default filemanager
let fileManager = FileManager.default

let logdirectory = getLibraryDirectory().appendingPathComponent("Logs/Nudge/")
let logfile = getLibraryDirectory().appendingPathComponent("Logs/Nudge/Nudge.log")

// Create date formatter
let formatter = DateFormatter()

var logToSystem = false

init() {

os_log("Starting", type: .info)

var isDir : ObjCBool = false

// Note, have to remove file:// from url for filemanager
if fileManager.fileExists(atPath: logdirectory.absoluteString.replacingOccurrences(of: "file://", with: ""), isDirectory:&isDir) {
os_log("Log dir %s exists", type: .info, logdirectory.absoluteString)
} else {
// Directory doesn't exist, create it
os_log("Log dir %s does not exist, creating it", type: .error, logdirectory.absoluteString)

do
{
// Create Nudge directory
try fileManager.createDirectory(atPath: logdirectory.path, withIntermediateDirectories: true, attributes: nil)
}
catch let error as NSError
{
// Can't create the directory, log to built-in logger instead
os_log("Unable to create directory %s", type: .error, error.debugDescription)
logToSystem = true
}

}

if fileManager.fileExists(atPath: logfile.absoluteString.replacingOccurrences(of: "file://", with: "")){
os_log("Log file %s exists", type: .error, logfile.absoluteString.replacingOccurrences(of: "file://", with: ""))
} else {
os_log("Creating file %s", type: .info, logfile.absoluteString.replacingOccurrences(of: "file://", with: ""))
fileManager.createFile(atPath: logfile.absoluteString.replacingOccurrences(of: "file://", with: ""), contents: nil)
}

// Configure date style
formatter.timeStyle = .medium
formatter.dateStyle = .long

// Logging should be started now
writeToLog(message:"Nudge launched", logLevel:"Info")
}

func writeToLog(message:String, logLevel:String) {

if !logToSystem {
do {
// https://medium.com/@vhart/read-write-and-delete-file-handling-from-xcode-playground-abf57e445b4
// Add Time, Log Level, and add a new line at the end
// let message = String(format: "%s\t%s\t%s\n", self.formatter.string(from: Date()), logLevel, message)
let message = self.formatter.string(from: Date()) + "\t" + logLevel + "\t\t" + message + "\n"

let fileUpdater = try FileHandle(forUpdating: logfile)
fileUpdater.seekToEndOfFile()
fileUpdater.write(message.data(using: .utf8)!)
fileUpdater.closeFile() // Is closing the file all the time efficent

} catch {
// Can't write to the file
os_log("Unable to write to %s log: %s", type: .error, logfile.absoluteString, error.localizedDescription)

// Start logging to the built-in logger instead
logToSystem = true
}

} else {
// Can't log to file, use built-in logger
if logLevel == "Error" {
os_log("%s", type: .error, message)
} else if logLevel == "Debug" {
os_log("%s", type: .debug, message)
} else if logLevel == "Warning" {
os_log("%s", type: .error, message) // TODO Find a way to log as warning
} else {
// Write anything else to info
os_log("%s", type: .info, message)
}
}
}

func error(message:String) {
writeToLog(message:message, logLevel:"Error")
}

func warning(message:String) {
writeToLog(message:message, logLevel:"Warning")
}


func info(message:String) {
writeToLog(message:message, logLevel:"Info")
}

func debug(message:String) {
writeToLog(message:message, logLevel:"Debug")
loggingLog.info("Starting log events, privacy: .public)")
}
}

// Lets it be used throughout Nudge via Log.info etc.
let Log = NudgeLogger()
18 changes: 11 additions & 7 deletions Nudge/UILogic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ func nudgeStartLogic() {
return
} else {
if Utils().demoModeEnabled() {
Log.info(message: "Device in demo mode")
uiLog.info("Device in demo mode, privacy: .public)")
if Utils().simpleModeEnabled() {
Log.info(message: "Device in simple mode")
uiLog.info("Device in simple mode, privacy: .public)")
}
} else {
Log.info(message: "Device fully up-to-date.")
uiLog.info("Current operating system is greater than or equal to required operating system, privacy: .public)")
AppKit.NSApp.terminate(nil)
}
}
Expand All @@ -35,6 +35,7 @@ func nudgeStartLogic() {
var lastRefreshTime = Utils().getInitialDate()
var afterFirstRun = false
var deferralCount = 0
var hasLoggedDeferralCountPastThreshold = false

func needToActivateNudge(deferralCountVar: Int, lastRefreshTimeVar: Date) -> Bool {
// If noTimers is true, just bail
Expand All @@ -47,7 +48,7 @@ func needToActivateNudge(deferralCountVar: Int, lastRefreshTimeVar: Date) -> Boo

// The first time the main timer contoller hits we don't care
if !afterFirstRun {
Log.info(message: "First run detected")
uiLog.info("Initilizing nudgeRefreshCycle, privacy: .public)")
_ = afterFirstRun = true
_ = lastRefreshTime = Date()
return false
Expand All @@ -63,14 +64,14 @@ func needToActivateNudge(deferralCountVar: Int, lastRefreshTimeVar: Date) -> Boo
// Don't nudge if major upgrade is frontmostApplication
if FileManager.default.fileExists(atPath: majorUpgradeAppPath) {
if NSURL.fileURL(withPath: majorUpgradeAppPath) == frontmostApplication?.bundleURL {
Log.info(message: "Upgrade app is currently frontmostApplication")
uiLog.info("majorUpgradeApp is currently the frontmostApplication, privacy: .public)")
return false
}
}

// Don't nudge if acceptable apps are frontmostApplication
if acceptableApps.contains((frontmostApplication?.bundleIdentifier!)!) {
Log.info(message: "An acceptable app is currently frontmostApplication")
uiLog.info("acceptableApp is currently the frontmostApplication, privacy: .public)")
return false
}

Expand All @@ -80,7 +81,10 @@ func needToActivateNudge(deferralCountVar: Int, lastRefreshTimeVar: Date) -> Boo
_ = lastRefreshTime = Date()
Utils().activateNudge()
if deferralCountVar > allowedDeferrals {
Log.warning(message: "Nudge deferral count over threshold")
if !hasLoggedDeferralCountPastThreshold {
uiLog.info("allowedDeferrals has been passed, privacy: .public)")
}
_ = hasLoggedDeferralCountPastThreshold = true
// Loop through all the running applications and hide them
for runningApplication in runningApplications {
let appName = runningApplication.bundleIdentifier ?? ""
Expand Down
59 changes: 33 additions & 26 deletions Nudge/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,42 @@ import SystemConfiguration

struct Utils {
func activateNudge() {
Log.info(message: "Re-activating Nudge")
utilsLog.info("Re-activating Nudge, privacy: .public)")
NSApp.activate(ignoringOtherApps: true)
}

func bringNudgeToFront() {
Log.info(message: "Bringing nudge to front")
utilsLog.info("Bringing nudge to front, privacy: .public)")
NSApp.activate(ignoringOtherApps: true)
NSApp.mainWindow?.makeKeyAndOrderFront(self)
}

func createImageData(fileImagePath: String) -> NSImage {
Log.info(message: "Creating image path for fileImagePath")
utilsLog.info("Creating image path for fileImagePath, privacy: .public)")
let urlPath = NSURL(fileURLWithPath: fileImagePath)
let imageData:NSData = NSData(contentsOf: urlPath as URL)!
return NSImage(data: imageData as Data)!
}

func demoModeEnabled() -> Bool {
let demoModeEnable = CommandLine.arguments.contains("-demo-mode")
Log.info(message: "ARG: Demo mode enabled command line argument: \(demoModeEnable)")
return demoModeEnable
let demoModeArgumentPassed = CommandLine.arguments.contains("-demo-mode")
if demoModeArgumentPassed {
uiLog.info("-demo-mode argument passed, privacy: .public)")
}
return demoModeArgumentPassed
}

func forceScreenShotIconModeEnabled() -> Bool {
let forceScreenShotIconMode = CommandLine.arguments.contains("-force-screenshot-icon")
Log.info(message: "ARG: Force screenshot icon mode: \(forceScreenShotIconMode)")
if forceScreenShotIconMode {
uiLog.info("-force-screenshot-icon argument passed, privacy: .public)")
}
return forceScreenShotIconMode
}

func fullyUpdated() -> Bool {
let fullyUpdated = versionGreaterThanOrEqual(current_version: OSVersion(ProcessInfo().operatingSystemVersion).description, new_version: requiredMinimumOSVersion)
Log.info(message: "Device is fulled updated: \(fullyUpdated)")
utilsLog.info("Device is fulled updated: \(fullyUpdated), privacy: .public)")
return fullyUpdated
}

Expand All @@ -69,14 +73,14 @@ struct Utils {

let cpu_arch = type & 0xff // mask for architecture bits
if cpu_arch == cpu_type_t(7){
Log.info(message: "CPU Type is Intel")
utilsLog.info("CPU Type is Intel, privacy: .public)")
return "Intel"
}
if cpu_arch == cpu_type_t(12){
Log.info(message: "CPU Type is Apple Silicon")
utilsLog.info("CPU Type is Apple Silicon, privacy: .public)")
return "Apple Silicon"
}
Log.warning(message: "Unknown CPU Type")
utilsLog.error("Unknown CPU Type, privacy: .public)")
return "unknown"
}

Expand All @@ -87,7 +91,7 @@ struct Utils {
func getJSONUrl() -> String {
// let jsonURL = UserDefaults.standard.volatileDomain(forName: UserDefaults.argumentDomain)
let jsonURL = UserDefaults.standard.string(forKey: "json-url") ?? "file:///Library/Preferences/com.github.macadmins.Nudge.json" // For Greg Neagle
Log.info(message: "JSON url: \(jsonURL)")
utilsLog.info("JSON url: \(jsonURL, privacy: .public)")
return jsonURL
}

Expand All @@ -99,19 +103,20 @@ struct Utils {

func getMajorOSVersion() -> Int {
let MajorOSVersion = ProcessInfo().operatingSystemVersion.majorVersion
Log.info(message: "OS Version: \(MajorOSVersion)")
utilsLog.info("OS Version: \(MajorOSVersion, privacy: .public)")
return MajorOSVersion
}

func getMajorRequiredNudgeOSVersion() -> Int {
let parts = requiredMinimumOSVersion.split(separator: ".", omittingEmptySubsequences: false)
Log.info(message: "Major required OS version: \(Int((parts[0]))!)")
return Int((parts[0]))!
let majorRequiredNudgeOSVersion = Int((parts[0]))!
utilsLog.info("Major required OS version: \(majorRequiredNudgeOSVersion, privacy: .public)")
return majorRequiredNudgeOSVersion
}

func getMinorOSVersion() -> Int {
let MinorOSVersion = ProcessInfo().operatingSystemVersion.minorVersion
Log.info(message: "Minor OS Version: \(MinorOSVersion)")
utilsLog.info("Minor OS Version: \(MinorOSVersion, privacy: .public)")
return MinorOSVersion
}

Expand All @@ -129,7 +134,7 @@ struct Utils {

func getPatchOSVersion() -> Int {
let PatchOSVersion = ProcessInfo().operatingSystemVersion.patchVersion
Log.info(message: "Patch OS Version: \(PatchOSVersion)")
utilsLog.info("Patch OS Version: \(PatchOSVersion, privacy: .public)")
return PatchOSVersion
}

Expand All @@ -151,7 +156,7 @@ struct Utils {

IOObjectRelease(platformExpert)

Log.info(message: "Serial is \(serialNumber)")
utilsLog.info("Serial Number: \(serialNumber, privacy: .public)")
return serialNumber
}

Expand All @@ -163,14 +168,14 @@ struct Utils {
var uid: uid_t = 0
var gid: gid_t = 0
let SystemConsoleUsername = SCDynamicStoreCopyConsoleUser(nil, &uid, &gid) as String? ?? ""
Log.info(message: "System console username: \(SystemConsoleUsername)")
utilsLog.info("System console username: \(SystemConsoleUsername, privacy: .public)")
return SystemConsoleUsername
}

func getTimerController() -> Int {
let timerCycle = getTimerControllerInt()
// print("Timer Cycle:", String(timerCycle)) // Easy way to debug the timerController logic
Log.info(message: "Timer cycle: \(timerCycle)")
utilsLog.info("Timer cycle: \(timerCycle, privacy: .public)")
return timerCycle
}

Expand All @@ -190,35 +195,37 @@ struct Utils {
guard let url = URL(string: aboutUpdateURL) else {
return
}
Log.info(message: "User clicked moreInfo button.")
uiLog.info("User clicked moreInfo button, privacy: .public)")
NSWorkspace.shared.open(url)
}

func pastRequiredInstallationDate() -> Bool {
let pastRequiredInstallationDate = getCurrentDate() > requiredInstallationDate
Log.info(message: "Installation date has passed: \(pastRequiredInstallationDate)")
utilsLog.info("Device pastRequiredInstallationDate: \(pastRequiredInstallationDate, privacy: .public)")
return pastRequiredInstallationDate
}

func requireDualQuitButtons() -> Bool {
let requireDualQuitButtons = (approachingWindowTime / 24) >= getNumberOfDaysBetween()
Log.info(message: "Require dual quit buttons: \(requireDualQuitButtons)")
uiLog.info("Device requireDualQuitButtons: \(requireDualQuitButtons, privacy: .public)")
return requireDualQuitButtons
}

func requireMajorUpgrade() -> Bool {
if requiredMinimumOSVersion == "0.0" {
Log.info(message: "Required major update: false")
utilsLog.info("Device requireMajorUpgrade: false, privacy: .public)")
return false
}
let requireMajorUpdate = versionGreaterThanOrEqual(current_version: OSVersion(ProcessInfo().operatingSystemVersion).description, new_version: requiredMinimumOSVersion)
Log.info(message: "Require major update: \(requireMajorUpdate)")
utilsLog.info("Device requireMajorUpgrade: \(requireMajorUpdate, privacy: .public)")
return requireMajorUpdate
}

func simpleModeEnabled() -> Bool {
let simpleModeEnabled = CommandLine.arguments.contains("-simple-mode")
Log.info(message: "Simple mode enabled: \(simpleModeEnabled)")
if simpleModeEnabled {
uiLog.info("-simple-mode argument passed, privacy: .public)")
}
return simpleModeEnabled
}

Expand Down
Loading

0 comments on commit 70bd820

Please sign in to comment.