getsentry / sentry-cocoa

The official Sentry SDK for iOS, tvOS, macOS, watchOS.
https://sentry.io/for/cocoa/
MIT License
815 stars 330 forks source link

Capture Logs from OSLog as Breadcrumbs #1461

Open philipphofmann opened 3 years ago

philipphofmann commented 3 years ago

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

Useful resources:

djmango commented 8 months ago

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

brustolin commented 8 months ago

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)
}
djmango commented 8 months ago

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)
    }
}
kahest commented 8 months ago

@djmango thanks for sharing!