15

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?

mfaani
  • 33,269
  • 19
  • 164
  • 293
Offset
  • 609
  • 1
  • 5
  • 22
  • i guess you want to get the logs as file like a CrashReporter am I right? In that case there are a couple of pods which can archive this. [PLCrashReporter](https://www.plcrashreporter.org/) is one I am using in one of my projects. It prints the logfiles into a file and makes an report which will be sent to a specific server. – Marcel T Jun 14 '17 at 06:39
  • I want to logs parameters and runtime behavior with os.log like in the docs. In near future I want to collect those logs and send them elsewhere. But I´m looking for a clean swift in code approach and not a lib. – Offset Jun 14 '17 at 06:44

4 Answers4

26

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)
user3441734
  • 16,722
  • 2
  • 40
  • 59
  • 1
    okey yeah. this is a approach but why cant I use the os.log framework? I I mean it contains a LogLevel and so on. I have one open Question what means the '&' in front of Log.log? – Offset Jun 14 '17 at 11:37
  • 1
    @Offset OSLog is available only on iOS 10.0+, macOS 10.12+, tvOS 10.0+, watchOS 3.0+, Unknown SDK 8.0+. & works as an inout to make the variable an in-out parameter. To use OSLog import os to your files – user3441734 Jun 14 '17 at 11:43
  • okay just to be clear. OSLog is only for debugging? I cant write to file or can be used as a file? I Think its very confusing. Even more there´s a NSLog which is available for all versions. So to sum up : Either I write my own logger (like above) or use a third party lib? – Offset Jun 14 '17 at 11:50
  • 1
    @Offset up to you :-), there is not enough information (at least I don't have) to effectively use OSLog. For using NSLog/ NSLogv see 'man 3 asl'. If you want to direct output elsewhere, you need to use a custom logging facility. – user3441734 Jun 14 '17 at 11:58
  • @user3441734 the same way, like any other .txt files, there is a lot of examples around – user3441734 Nov 15 '18 at 05:08
  • 2
    Is there anyway to simply use logs created by calls to `os_log` and zip them and email them? Currently I'm only aware of the sysdiagnose approach... – mfaani Dec 14 '18 at 12:20
  • @user3441734 how to fetch log.txt file? – user2786 Jun 18 '19 at 12:28
  • You can find the log file here using this: https://stackoverflow.com/q/6121613/1759443 – Kyle Venn Jun 02 '20 at 01:50
  • I got the following text as a log: "started app: 2022-02-14 16:01:19 +0000". How can I get the following text "2022-02-14 16:01:19 +0000 - started app" ? I want to see the date in the first place. – fabiobh Feb 14 '22 at 16:06
  • On device, where file is saved? I wan to manage it anda send – doxsi Sep 29 '22 at 10:48
10

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")
Rajat Jain
  • 1,002
  • 1
  • 15
  • 24
3

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?)

Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • The OP is also asking about 'how to get them'. Your answer would benefit more if you discuss OSLogStore & Sysdiagnose as well – mfaani Dec 22 '21 at 18:36
  • @Honey your comment helped me, because I've been trying to figure that out, too. The documentation says the unified logging system is useful for "When you are unable to attach a debugger to the app, such as when you’re diagnosing problems on a user’s machine," but as far as I can tell, doesn't explain anywhere how to do this. I'll look into those two concepts and update my answer. – Daniel Kaplan Dec 22 '21 at 18:41
  • @Honey I've been searching for a whole hour and I can't figure out how to use OSLogStore. The most informative thing I've found is this article: https://steipete.com/posts/logging-in-swift but I still don't know how to use OSLogStore after reading it, nor do I understand why you'd need to: the documentation I linked to above doesn't mention it anywhere yet, as that table shows, it still logs to disk. – Daniel Kaplan Dec 22 '21 at 19:36
  • @Honey OSLogStore isn't mentioned in the official Logger docs either: https://developer.apple.com/documentation/os/logger – Daniel Kaplan Dec 22 '21 at 19:41
  • 1
    Click any instruction to see docs for trigger sysdiagnose from [here](https://developer.apple.com/bug-reporting/profiles-and-logs/). Though I recommend [this](https://stackoverflow.com/questions/42588673/how-can-i-retrieve-messages-logged-with-os-log-from-ipad-iphone) way. I haven't used OSLogStore, but its benefits is 1. you can query them with certain predicates as opposed to a sysdiagnose that queries ALL oslogs of all apps. 2. You can save them to disk and or ask users to email it I believe. 3. Docs are [here](https://developer.apple.com/documentation/oslog/oslogstore/3204125-getentries) – mfaani Dec 22 '21 at 20:13
  • @Honey cool thank you for that information. I'm still confused about how the `Logger` comes into play though. Do `Logger`s magically know where to write on disk and `OSLogStore` magically knows where to read it from? – Daniel Kaplan Dec 22 '21 at 20:22
  • 1
    I've used oslog/Logger before. And you never specify where it should log to. So my assumption is that Yes it magically logs and retrieves – mfaani Dec 22 '21 at 20:23
1

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.

Yano
  • 585
  • 3
  • 18