FirebaseLogger.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Copyright 2024 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import FirebaseCore
  15. import Foundation
  16. import os
  17. @_implementationOnly import FirebaseCoreExtension
  18. public class FirebaseLogger {
  19. let category: String
  20. let categoryIdentifier: String
  21. /// Initializes a `FirebaseLogger`.
  22. ///
  23. /// - Parameters:
  24. /// - category: The product or component name to use as an OSLog category to differentiate logs
  25. /// within the Firebase subsystem.
  26. /// - categoryIdentifier: A three-character identifier for the `category` that is unique within
  27. /// Firebase, e.g., "COR" for the "FirebaseCore" category.
  28. public init(category: String, categoryIdentifier: String) {
  29. self.category = category
  30. self.categoryIdentifier = categoryIdentifier
  31. }
  32. // MARK: - Logging Methods
  33. /// Writes a log message with the log level `notice`.
  34. ///
  35. /// - Parameters:
  36. /// - messageID: A six digit integer message identifier that is unique within the category.
  37. /// - message: The message to log; may be a format string.
  38. /// - arguments: A comma-separated list of arguments to substitute into `message`, if it is a
  39. /// format string.
  40. public func notice(messageID: Int, _ message: String, _ arguments: any CVarArg...) {
  41. log(level: .notice, messageID: messageID, message, arguments)
  42. }
  43. /// Writes a log message with the log level `info`.
  44. ///
  45. /// - Parameters:
  46. /// - messageID: A six digit integer message identifier that is unique within the category.
  47. /// - message: The message to log; may be a format string.
  48. /// - arguments: A comma-separated list of arguments to substitute into `message`, if it is a
  49. /// format string.
  50. public func info(messageID: Int, _ message: String, _ arguments: any CVarArg...) {
  51. log(level: .info, messageID: messageID, message, arguments)
  52. }
  53. /// Writes a log message with the log level `debug`.
  54. ///
  55. /// - Parameters:
  56. /// - messageID: A six digit integer message identifier that is unique within the category.
  57. /// - message: The message to log; may be a format string.
  58. /// - arguments: A comma-separated list of arguments to substitute into `message`, if it is a
  59. /// format string.
  60. public func debug(messageID: Int, _ message: String, _ arguments: any CVarArg...) {
  61. log(level: .debug, messageID: messageID, message, arguments)
  62. }
  63. /// Writes a log message with the log level `warning`.
  64. ///
  65. /// - Parameters:
  66. /// - messageID: A six digit integer message identifier that is unique within the category.
  67. /// - message: The message to log; may be a format string.
  68. /// - arguments: A comma-separated list of arguments to substitute into `message`, if it is a
  69. /// format string.
  70. public func warning(messageID: Int, _ message: String, _ arguments: any CVarArg...) {
  71. log(level: .warning, messageID: messageID, message, arguments)
  72. }
  73. /// Writes a log message with the log level `error`.
  74. ///
  75. /// - Parameters:
  76. /// - messageID: A six digit integer message identifier that is unique within the category.
  77. /// - message: The message to log; may be a format string.
  78. /// - arguments: A comma-separated list of arguments to substitute into `message`, if it is a
  79. /// format string.
  80. public func error(messageID: Int, _ message: String, _ arguments: any CVarArg...) {
  81. log(level: .error, messageID: messageID, message, arguments)
  82. }
  83. // MARK: - Manual Logging Helpers
  84. /// Returns true if the specified logging level should be logged.
  85. ///
  86. /// - Parameters:
  87. /// - level: The logging level to check.
  88. public static func isLoggableLevel(_ level: FirebaseLoggerLevel) -> Bool {
  89. FIRIsLoggableLevel(level, false)
  90. }
  91. /// Returns the `OSLogType` equivalent of the specified `FirebaseLoggerLevel`.
  92. ///
  93. /// - Parameters:
  94. /// - level: The desired Firebase logging level.
  95. public static func osLogType(_ level: FirebaseLoggerLevel) -> OSLogType {
  96. return FIRLoggerLevelToOSLogType(level)
  97. }
  98. /// Returns the prefix that should be prepended to log messages when using OSLog directly.
  99. ///
  100. /// - Important: This prefix is added automatically when using the logging methods `notice`,
  101. /// `info`, etc., in this class and should not be added manually to avoid duplication.
  102. ///
  103. /// - Parameters:
  104. /// - messageID: A six digit integer message identifier that is unique within the category.
  105. public func messagePrefix(messageID: Int) -> String {
  106. return FIRLogPrefix(category, categoryIdentifier, messageID)
  107. }
  108. /// Returns the log object that may be used when using OSLog directly.
  109. ///
  110. /// ## Example Usage
  111. /// ```swift
  112. /// // Create a FirebaseLogger for the product.
  113. /// let logger = FirebaseLogger(category: "FirebaseProduct", categoryIdentifier: "FBP")
  114. ///
  115. /// // Check if messages with the chosen log level (severity) should be logged.
  116. /// if FirebaseLogger.isLoggableLevel(FirebaseLoggerLevel.debug) {
  117. /// os_log(
  118. /// // Get the equivalent OSLogType for the chosen log level.
  119. /// FirebaseLogger.osLogType(FirebaseLoggerLevel.debug),
  120. /// // Write to the OSLog log object for your FirebaseLogger.
  121. /// log: logger.logObject(),
  122. /// // Add the standard Firebase log message prefix with `%{public}@` since the contents of
  123. /// // dynamic strings are redacted by default.
  124. /// "%{public}@ Logged in - User name: %@, ID: %{private}ld, admin: %{BOOL}d",
  125. /// // The standard Firebase log message prefix does not contain sensitive user data but
  126. /// // dynamic string values are redacted by default; use the `%{public}@` format specifier to
  127. /// // prevent redacting.
  128. /// logger.messagePrefix(messageID: 1),
  129. /// // The user name is sensitive user data but is redacted by default; the `%@` format
  130. /// // specifier is sufficient.
  131. /// getUserName(),
  132. /// // The user ID is sensitive user data but integer values are not redacted by default;
  133. /// // redact using the `%{private}ld` format specifier.
  134. /// getUserID(),
  135. /// // The user's administrator status is not sensitive user data and is not redacted by
  136. /// // default; the `%{BOOL}d` format specifier is sufficient.
  137. /// getUserAdmin()
  138. /// )
  139. /// }
  140. /// ```
  141. ///
  142. /// - Important: When writing log messages that **do not** contain sensitive data, it is
  143. /// preferable to use the convenience logging methods ``notice(messageID:_:_:)``,
  144. /// ``info(messageID:_:_:)``, ``debug(messageID:_:_:)``, ``warning(messageID:_:_:)`` or
  145. /// ``error(messageID:_:_:)``; these methods default to `OSLogPrivacy.public`.
  146. ///
  147. /// - Tip: The `Logger` struct returned by ``osLogger()`` provides a nicer API for SDKs
  148. /// targeting iOS 14+ and equivalent.
  149. public func logObject() -> OSLog {
  150. return FIRLogOSLogObject(category)
  151. }
  152. /// Returns a Swift Logger struct that may be used when using OSLog directly.
  153. @available(iOS 14.0, macOS 11.0, macCatalyst 14.0, tvOS 14.0, watchOS 7.0, *)
  154. public func osLogger() -> Logger {
  155. return Logger(logObject())
  156. }
  157. // MARK: - Private Helpers
  158. private func log(level: FirebaseLoggerLevel, messageID: Int, _ message: String,
  159. _ arguments: any CVarArg...) {
  160. withVaList(arguments) { va_list in
  161. let code = FIRLogMessageCode(self.categoryIdentifier, messageID)
  162. FIRLogBasic(level, category, code, message, va_list)
  163. }
  164. }
  165. }