TelemetryLogger.swift 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright 2021 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 Foundation
  15. import FirebaseCore
  16. import GoogleDataTransport
  17. /// Extension to set Firebase app info.
  18. extension SystemInfo {
  19. mutating func setAppInfo(apiKey: String?, projectID: String?) {
  20. appID = Bundle.main.bundleIdentifier ?? "unknownBundleID"
  21. let appVersionKey = "CFBundleShortVersionString"
  22. appVersion = Bundle.main.infoDictionary?[appVersionKey] as? String ?? "unknownAppVersion"
  23. mlSdkVersion = FirebaseVersion()
  24. self.apiKey = apiKey ?? "unknownAPIKey"
  25. firebaseProjectID = projectID ?? "unknownProjectID"
  26. }
  27. }
  28. /// Extension to set model options.
  29. extension ModelOptions {
  30. mutating func setModelOptions(model: CustomModel, isModelUpdateEnabled: Bool? = nil) {
  31. if let updateEnabled = isModelUpdateEnabled {
  32. self.isModelUpdateEnabled = updateEnabled
  33. }
  34. modelInfo.name = model.name
  35. modelInfo.hash = model.hash
  36. modelInfo.modelType = .custom
  37. }
  38. }
  39. /// Extension to build model download log event.
  40. extension ModelDownloadLogEvent {
  41. mutating func setEvent(status: DownloadStatus, errorCode: ErrorCode,
  42. roughDownloadDuration: UInt64? = nil, exactDownloadDuration: UInt64? = nil,
  43. downloadFailureStatus: Int64? = nil, modelOptions: ModelOptions) {
  44. downloadStatus = status
  45. self.errorCode = errorCode
  46. if let roughDuration = roughDownloadDuration {
  47. roughDownloadDurationMs = roughDuration
  48. }
  49. if let exactDuration = exactDownloadDuration {
  50. exactDownloadDurationMs = exactDuration
  51. }
  52. if let failureStatus = downloadFailureStatus {
  53. self.downloadFailureStatus = failureStatus
  54. }
  55. options = modelOptions
  56. }
  57. }
  58. /// Extension to build Firebase ML log event.
  59. extension FirebaseMlLogEvent {
  60. mutating func setEvent(eventName: EventName, systemInfo: SystemInfo,
  61. modelDownloadLogEvent: ModelDownloadLogEvent) {
  62. self.eventName = eventName
  63. self.systemInfo = systemInfo
  64. self.modelDownloadLogEvent = modelDownloadLogEvent
  65. }
  66. }
  67. /// Data object for Firelog event.
  68. class FBMLDataObject: NSObject, GDTCOREventDataObject {
  69. private let event: FirebaseMlLogEvent
  70. init(event: FirebaseMlLogEvent) {
  71. self.event = event
  72. }
  73. /// Encode Firelog event for transport.
  74. func transportBytes() -> Data {
  75. do {
  76. // TODO: Should this be binary or json serialized?
  77. let data = try event.serializedData()
  78. return data
  79. } catch {
  80. DeviceLogger.logEvent(level: .debug,
  81. message: TelemetryLogger.ErrorDescription.encodeEvent,
  82. messageCode: .analyticsEventEncodeError)
  83. return Data()
  84. }
  85. }
  86. }
  87. /// Firelog logger.
  88. class TelemetryLogger {
  89. /// Mapping ID for the log source.
  90. private let mappingID = "1326"
  91. /// Current Firebase app.
  92. private let app: FirebaseApp
  93. /// Transport for Firelog events.
  94. private let fllTransport: GDTCORTransport
  95. /// Init logger, could be nil if unable to get event transport.
  96. init?(app: FirebaseApp) {
  97. self.app = app
  98. guard let fllTransport = GDTCORTransport(
  99. mappingID: mappingID,
  100. transformers: nil,
  101. target: GDTCORTarget.FLL
  102. ) else {
  103. DeviceLogger.logEvent(level: .debug,
  104. message: TelemetryLogger.ErrorDescription.initTelemetryLogger,
  105. messageCode: .telemetryInitError)
  106. return nil
  107. }
  108. self.fllTransport = fllTransport
  109. }
  110. /// Log events to Firelog.
  111. private func logModelEvent(event: FirebaseMlLogEvent) {
  112. let eventForTransport: GDTCOREvent = fllTransport.eventForTransport()
  113. eventForTransport.dataObject = FBMLDataObject(event: event)
  114. fllTransport.sendTelemetryEvent(eventForTransport)
  115. }
  116. /// Log model info retrieval event to Firelog.
  117. func logModelInfoRetrievalEvent(eventName: EventName,
  118. status: ModelDownloadLogEvent.DownloadStatus,
  119. model: CustomModel? = nil, errorCode: ModelInfoErrorCode) {
  120. guard app.isDataCollectionDefaultEnabled else { return }
  121. var systemInfo = SystemInfo()
  122. let apiKey = app.options.apiKey
  123. let projectID = app.options.projectID
  124. systemInfo.setAppInfo(apiKey: apiKey, projectID: projectID)
  125. let modelDownloadLogEvent = ModelDownloadLogEvent()
  126. var fbmlEvent = FirebaseMlLogEvent()
  127. fbmlEvent.setEvent(
  128. eventName: eventName,
  129. systemInfo: systemInfo,
  130. modelDownloadLogEvent: modelDownloadLogEvent
  131. )
  132. logModelEvent(event: fbmlEvent)
  133. }
  134. /// Log model download event to Firelog.
  135. func logModelDownloadEvent(eventName: EventName, status: ModelDownloadLogEvent.DownloadStatus,
  136. model: CustomModel? = nil, downloadErrorCode: ModelDownloadErrorCode) {
  137. guard app.isDataCollectionDefaultEnabled else { return }
  138. var modelOptions = ModelOptions()
  139. if let model = model {
  140. modelOptions.setModelOptions(model: model)
  141. }
  142. var systemInfo = SystemInfo()
  143. let apiKey = app.options.apiKey
  144. let projectID = app.options.projectID
  145. systemInfo.setAppInfo(apiKey: apiKey, projectID: projectID)
  146. var errorCode = ErrorCode()
  147. switch downloadErrorCode {
  148. case .noError:
  149. errorCode = .noError
  150. case .urlExpired:
  151. errorCode = .uriExpired
  152. case .noConnection:
  153. errorCode = .noNetworkConnection
  154. case .downloadFailed:
  155. errorCode = .downloadFailed
  156. case let .httpError(code):
  157. errorCode = ErrorCode(rawValue: code) ?? .unknownError
  158. }
  159. var modelDownloadLogEvent = ModelDownloadLogEvent()
  160. modelDownloadLogEvent.setEvent(
  161. status: status,
  162. errorCode: errorCode,
  163. modelOptions: modelOptions
  164. )
  165. var fbmlEvent = FirebaseMlLogEvent()
  166. fbmlEvent.setEvent(
  167. eventName: eventName,
  168. systemInfo: systemInfo,
  169. modelDownloadLogEvent: modelDownloadLogEvent
  170. )
  171. logModelEvent(event: fbmlEvent)
  172. }
  173. }
  174. /// Possible error messages while logging telemetry.
  175. extension TelemetryLogger {
  176. /// Error descriptions.
  177. fileprivate enum ErrorDescription {
  178. static let encodeEvent = "Unable to encode event for Firelog."
  179. static let initTelemetryLogger = "Unable to create telemetry logger."
  180. }
  181. }