TelemetryLogger.swift 5.7 KB

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