sentry-cocoa icon indicating copy to clipboard operation
sentry-cocoa copied to clipboard

Capture Logs from OSLog as Breadcrumbs

Open philipphofmann opened this issue 4 years ago • 4 comments

Similar to https://github.com/getsentry/sentry-cocoa/issues/1247, but for os_log.

Useful resources:

  • https://steipete.com/posts/logging-in-swift/
  • https://useyourloaf.com/blog/fetching-oslog-messages-in-swift

philipphofmann avatar Nov 17 '21 14:11 philipphofmann

Is this implemented? Want to have my error OSLog messages hit sentry but if not possible will just switch em over

djmango avatar Mar 16 '24 19:03 djmango

Hello @djmango, this is not implemented yet.

The easiest way to make it happen right now is to create a global log function where you also create a breadcrumb.

Something like:

func log(_ message : String) {
    SentrySDK.addBreadcrumb(Breadcrumb(level: .debug, category: message))
    NSLog("%@", message)
}

brustolin avatar Mar 18 '24 13:03 brustolin

Got it. Heres a little utility struct I wrote, this is what I'm using as a drop in replacement for Logger to also log fatals and errors to sentry. It logs them as just Info level, but works well enough for me.

//
//  SentryLogger.swift
//  Invisibility
//
//  Created by Sulaiman Ghori on 3/24/24.
//  Copyright © 2024 Invisibility Inc. All rights reserved.
//

import Foundation
import OSLog
import Sentry

struct SentryLogger {
    private let logger: Logger
    private let subsystem: String
    private let category: String

    init(subsystem: String, category: String) {
        logger = Logger(subsystem: subsystem, category: category)
        self.subsystem = subsystem
        self.category = category
    }

    private func log(_ message: String, level: SentryLevel, file: String, function: String, line: Int) {
        let breadcrumb = Breadcrumb(level: level, category: category)
        let formattedMessage = "\(file):\(line) \(function) - \(message)"
        breadcrumb.message = formattedMessage
        SentrySDK.addBreadcrumb(breadcrumb)

        switch level {
        case .debug:
            logger.debug("\(message)")
        case .info:
            logger.info("\(message)")
        case .warning:
            logger.warning("\(message)")
        case .error:
            logger.error("\(message)")
            SentrySDK.capture(message: message)
        case .fatal:
            logger.fault("\(message)")
            SentrySDK.capture(message: message)
        default:
            logger.info("\(message)")
        }
    }

    func debug(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(message, level: .debug, file: file, function: function, line: line)
    }

    func info(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(message, level: .info, file: file, function: function, line: line)
    }

    func warning(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(message, level: .warning, file: file, function: function, line: line)
    }

    func error(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(message, level: .error, file: file, function: function, line: line)
    }

    func fault(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(message, level: .fatal, file: file, function: function, line: line)
    }
}

djmango avatar Mar 25 '24 19:03 djmango

@djmango thanks for sharing!

kahest avatar Mar 27 '24 10:03 kahest