Offset
Offset

Reputation: 649

How to write Application Logs to File and get them

I´m taking baby steps in iOS development and searching for a method to use logging in iOS.

I found these docs about logging with swift 3 : https://developer.apple.com/documentation/os/logging#1682426

The Docs say that the logs aren´t saved on disk. What is the typical way to get the logs and process the file?

Upvotes: 18

Views: 33356

Answers (4)

Yano
Yano

Reputation: 652

Simple way!

func createLogFile() {
   guard let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else {
            return
   }
   let fileName = "\(Date()).log"
   let logFilePath = (documentsDirectory as NSString).appendingPathComponent(fileName)
   freopen(logFilePath.cString(using: String.Encoding.ascii), "a+", stderr)
}

Now call this method on viewDidLoad

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        createLogFile()
        NSLog("First Log")
        NSLog("Second Log")
    }
}

Goto the simulator application directory and check the logs.

enter image description here

NOTE: Only NSLog will work this way. Print, debugPrint will not work.

Upvotes: 2

Daniel Kaplan
Daniel Kaplan

Reputation: 67300

The Docs say that the logs aren´t saved on disk. What is the typical way to get the logs and process the file?

I'm not sure if this was true at some point and changed, but the current documentation you linked to does say logs are saved on disk:

This system centralizes the storage of log data in memory and on disk, rather than writing that data to a text-based log file.

Specifically, you can find this table in a recommended article:

enter image description here

You can override the default storage behavior of each log level using tools or custom configuration profiles. For more information on how to do so, see Customizing Logging Behavior While Debugging

Here's an example in the article of how to use the logger (swift):

if #available(OSX 11.0, *) {
   // Log a message to the default log and default log level.
   let defaultLog = Logger()
   defaultLog.log("This is a default message.")
            
   // Log a message to the default log and debug log level
   defaultLog.debug("This is a debug message.")


   // Log an error to a custom log object.
   let customLog = Logger(subsystem: "com.your_company.your_subsystem", 
          category: "your_category_name")
   customLog.error("An error occurred!")
} else {
   // Log a message to the default log and default log level.
   os_log("This is a default message.")
            
   // Log a message to the default log and debug log level
   os_log("This is a debug message.", log: OSLog.default, type: .debug)
            
   // Log an error to a custom log object.
   let customLog = OSLog(subsystem: "com.your_company.your_subsystem", 
            category: "your_category_name")
   os_log("An error occurred!", log: customLog, type: .error)
}

As far as I can tell, there's no need to use the other answers (anymore?)

Upvotes: 2

Rajat Jain
Rajat Jain

Reputation: 1022

Swift 3.0 version

Create a new swift file "TextLog.swift" in your project

import Foundation

struct TextLog: TextOutputStream {

    /// Appends the given string to the stream.
    mutating func write(_ string: String) {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .allDomainsMask)
        let documentDirectoryPath = paths.first!
        let log = documentDirectoryPath.appendingPathComponent("log.txt")

        do {
            let handle = try FileHandle(forWritingTo: log)
            handle.seekToEndOfFile()
            handle.write(string.data(using: .utf8)!)
            handle.closeFile()
        } catch {
            print(error.localizedDescription)
            do {
                try string.data(using: .utf8)?.write(to: log)
            } catch {
                print(error.localizedDescription)
            }
        }

    }

}

Initialize the logger at bottom in the AppDelegate.swift file

var textLog = TextLog()

Use it anywhere in the application like below

textLog.write("hello")

Upvotes: 11

user3441734
user3441734

Reputation: 17534

put this file to your project

//
//  log.swift
//  logtest
//

import Foundation

struct Log: TextOutputStream {

    func write(_ string: String) {
        let fm = FileManager.default
        let log = fm.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("log.txt")
        if let handle = try? FileHandle(forWritingTo: log) {
            handle.seekToEndOfFile()
            handle.write(string.data(using: .utf8)!)
            handle.closeFile()
        } else {
            try? string.data(using: .utf8)?.write(to: log)
        }
    }
}

var logger = Log()

and if you need something to be logged, just use print function like

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        print("started:", Date(), to: &logger)
        return true
    }

or

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    print(#file, #function, "my own text", 1, [1,2], to: &logger)
}

in your application's 'Documents' folder you could find 'log.txt' file which you could examine later.

while running my test application twice, the content looks like

started: 2017-06-14 09:58:58 +0000
/Users/ivo_vacek/Documents/logtest/logtest/ViewController.swift viewDidLoad() my own text 1 [1, 2]
started: 2017-06-14 09:59:15 +0000
/Users/ivo_vacek/Documents/logtest/logtest/ViewController.swift viewDidLoad() my own text 1 [1, 2] 

if you don't like 'globals' define Log as singletone class

class Log: TextOutputStream {

    func write(_ string: String) {
        let fm = FileManager.default
        let log = fm.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("log.txt")
        if let handle = try? FileHandle(forWritingTo: log) {
            handle.seekToEndOfFile()
            handle.write(string.data(using: .utf8)!)
            handle.closeFile()
        } else {
            try? string.data(using: .utf8)?.write(to: log)
        }
    }
    static var log: Log = Log()
    private init() {} // we are sure, nobody else could create it
}

and use it like

print("started:", Date(), to: &Log.log)

Upvotes: 28

Related Questions