Skip to content

Commit 3aac2b6

Browse files
committed
[Vertex AI] Migrate logging to FirebaseLogger
# Conflicts: # FirebaseVertexAI/Sources/Safety.swift
1 parent 3d61c54 commit 3aac2b6

File tree

5 files changed

+136
-87
lines changed

5 files changed

+136
-87
lines changed

FirebaseVertexAI/Sources/GenerateContentResponse.swift

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ public struct GenerateContentResponse: Sendable {
4242
/// The response's content as text, if it exists.
4343
public var text: String? {
4444
guard let candidate = candidates.first else {
45-
Logging.default
46-
.error("[FirebaseVertexAI] Could not get text from a response that had no candidates.")
45+
Logging.logEvent(
46+
level: .error,
47+
message: "Could not get text from a response that had no candidates.",
48+
messageCode: .generateContentResponseNoCandidates
49+
)
4750
return nil
4851
}
4952
let textValues: [String] = candidate.content.parts.compactMap { part in
@@ -53,8 +56,11 @@ public struct GenerateContentResponse: Sendable {
5356
return text
5457
}
5558
guard textValues.count > 0 else {
56-
Logging.default
57-
.error("[FirebaseVertexAI] Could not get a text part from the first candidate.")
59+
Logging.logEvent(
60+
level: .error,
61+
message: "Could not get a text part from the first candidate.",
62+
messageCode: .generateContentResponseNoText
63+
)
5864
return nil
5965
}
6066
return textValues.joined(separator: " ")
@@ -338,8 +344,11 @@ extension FinishReason: Decodable {
338344
public init(from decoder: Decoder) throws {
339345
let value = try decoder.singleValueContainer().decode(String.self)
340346
guard let decodedFinishReason = FinishReason(rawValue: value) else {
341-
Logging.default
342-
.error("[FirebaseVertexAI] Unrecognized FinishReason with value \"\(value)\".")
347+
Logging.logEvent(
348+
level: .error,
349+
message: "Unrecognized FinishReason with value \"\(value)\".",
350+
messageCode: .generateContentResponseUnrecognizedFinishReason
351+
)
343352
self = .unknown
344353
return
345354
}
@@ -353,8 +362,11 @@ extension PromptFeedback.BlockReason: Decodable {
353362
public init(from decoder: Decoder) throws {
354363
let value = try decoder.singleValueContainer().decode(String.self)
355364
guard let decodedBlockReason = PromptFeedback.BlockReason(rawValue: value) else {
356-
Logging.default
357-
.error("[FirebaseVertexAI] Unrecognized BlockReason with value \"\(value)\".")
365+
Logging.logEvent(
366+
level: .error,
367+
message: "Unrecognized BlockReason with value \"\(value)\".",
368+
messageCode: .generateContentResponseUnrecognizedBlockReason
369+
)
358370
self = .unknown
359371
return
360372
}

FirebaseVertexAI/Sources/GenerativeAIService.swift

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import FirebaseAppCheckInterop
1616
import FirebaseAuthInterop
1717
import FirebaseCore
1818
import Foundation
19+
import os.log
1920

2021
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
2122
struct GenerativeAIService {
@@ -60,9 +61,17 @@ struct GenerativeAIService {
6061

6162
// Verify the status code is 200
6263
guard response.statusCode == 200 else {
63-
Logging.network.error("[FirebaseVertexAI] The server responded with an error: \(response)")
64+
Logging.logEvent(
65+
level: .error,
66+
message: "The server responded with an error: \(response)",
67+
messageCode: .loadRequestResponseError
68+
)
6469
if let responseString = String(data: data, encoding: .utf8) {
65-
Logging.default.error("[FirebaseVertexAI] Response payload: \(responseString)")
70+
Logging.logEvent(
71+
level: .error,
72+
message: "Response payload: \(responseString)",
73+
messageCode: .loadRequestResponseErrorPayload
74+
)
6675
}
6776

6877
throw parseError(responseData: data)
@@ -108,14 +117,21 @@ struct GenerativeAIService {
108117

109118
// Verify the status code is 200
110119
guard response.statusCode == 200 else {
111-
Logging.network
112-
.error("[FirebaseVertexAI] The server responded with an error: \(response)")
120+
Logging.logEvent(
121+
level: .error,
122+
message: "The server responded with an error: \(response)",
123+
messageCode: .loadRequestStreamResponseError
124+
)
113125
var responseBody = ""
114126
for try await line in stream.lines {
115127
responseBody += line + "\n"
116128
}
117129

118-
Logging.default.error("[FirebaseVertexAI] Response payload: \(responseBody)")
130+
Logging.logEvent(
131+
level: .error,
132+
message: "Response payload: \(responseBody)",
133+
messageCode: .loadRequestStreamResponseErrorPayload
134+
)
119135
continuation.finish(throwing: parseError(responseBody: responseBody))
120136

121137
return
@@ -127,7 +143,11 @@ struct GenerativeAIService {
127143
let decoder = JSONDecoder()
128144
decoder.keyDecodingStrategy = .convertFromSnakeCase
129145
for try await line in stream.lines {
130-
Logging.network.debug("[FirebaseVertexAI] Stream response: \(line)")
146+
Logging.logEvent(
147+
level: .debug,
148+
message: "Stream response: \(line)",
149+
messageCode: .loadRequestStreamResponseLine
150+
)
131151

132152
if line.hasPrefix("data:") {
133153
// We can assume 5 characters since it's utf-8 encoded, removing `data:`.
@@ -179,8 +199,11 @@ struct GenerativeAIService {
179199
let tokenResult = await appCheck.getToken(forcingRefresh: false)
180200
urlRequest.setValue(tokenResult.token, forHTTPHeaderField: "X-Firebase-AppCheck")
181201
if let error = tokenResult.error {
182-
Logging.default
183-
.debug("[FirebaseVertexAI] Failed to fetch AppCheck token. Error: \(error)")
202+
Logging.logEvent(
203+
level: .error,
204+
message: "Failed to fetch AppCheck token. Error: \(error)",
205+
messageCode: .appCheckTokenFetchFailed
206+
)
184207
}
185208
}
186209

@@ -202,10 +225,11 @@ struct GenerativeAIService {
202225
private func httpResponse(urlResponse: URLResponse) throws -> HTTPURLResponse {
203226
// Verify the status code is 200
204227
guard let response = urlResponse as? HTTPURLResponse else {
205-
Logging.default
206-
.error(
207-
"[FirebaseVertexAI] Response wasn't an HTTP response, internal error \(urlResponse)"
208-
)
228+
Logging.logEvent(
229+
level: .error,
230+
message: "Response wasn't an HTTP response, internal error \(urlResponse)",
231+
messageCode: .generativeAIServiceNonHTTPResponse
232+
)
209233
throw NSError(
210234
domain: "com.google.generative-ai",
211235
code: -1,
@@ -253,14 +277,14 @@ struct GenerativeAIService {
253277
// These errors do not produce specific GenerateContentError or CountTokensError cases.
254278
private func logRPCError(_ error: RPCError) {
255279
if error.isFirebaseMLServiceDisabledError() {
256-
Logging.default.error("""
280+
Logging.logEvent(level: .error, message: """
257281
The Vertex AI for Firebase SDK requires the Firebase ML API `firebaseml.googleapis.com` to \
258282
be enabled for your project. Get started in the Firebase Console \
259283
(https://console.firebase.google.com/project/\(projectID)/genai/vertex) or verify that the \
260284
API is enabled in the Google Cloud Console \
261285
(https://console.developers.google.com/apis/api/firebaseml.googleapis.com/overview?project=\
262286
\(projectID)).
263-
""")
287+
""", messageCode: .firebaseMLAPIDisabled)
264288
}
265289
}
266290

@@ -269,9 +293,17 @@ struct GenerativeAIService {
269293
return try JSONDecoder().decode(type, from: data)
270294
} catch {
271295
if let json = String(data: data, encoding: .utf8) {
272-
Logging.network.error("[FirebaseVertexAI] JSON response: \(json)")
296+
Logging.logEvent(
297+
level: .error,
298+
message: "JSON response: \(json)",
299+
messageCode: .loadRequestParseResponseFailedJSON
300+
)
273301
}
274-
Logging.default.error("[FirebaseVertexAI] Error decoding server JSON: \(error)")
302+
Logging.logEvent(
303+
level: .error,
304+
message: "Error decoding server JSON: \(error)",
305+
messageCode: .loadRequestParseResponseFailedJSONError
306+
)
275307
throw error
276308
}
277309
}
@@ -297,9 +329,12 @@ struct GenerativeAIService {
297329
}
298330

299331
private func printCURLCommand(from request: URLRequest) {
332+
guard Logging.additionalLoggingEnabled() else {
333+
return
334+
}
300335
let command = cURLCommand(from: request)
301-
Logging.verbose.debug("""
302-
[FirebaseVertexAI] Creating request with the equivalent cURL command:
336+
os_log(.debug, log: Logging.logObject, """
337+
\(Logging.service) Creating request with the equivalent cURL command:
303338
----- cURL command -----
304339
\(command, privacy: .private)
305340
------------------------

FirebaseVertexAI/Sources/GenerativeModel.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,22 @@ public final class GenerativeModel {
8686
self.requestOptions = requestOptions
8787

8888
if Logging.additionalLoggingEnabled() {
89-
if ProcessInfo.processInfo.arguments.contains(Logging.migrationEnableArgumentKey) {
90-
Logging.verbose.debug("""
91-
[FirebaseVertexAI] Verbose logging enabled with the \
92-
\(Logging.migrationEnableArgumentKey, privacy: .public) launch argument; please migrate to \
93-
the \(Logging.enableArgumentKey, privacy: .public) argument to ensure future compatibility.
94-
""")
95-
} else {
96-
Logging.verbose.debug("[FirebaseVertexAI] Verbose logging enabled.")
97-
}
89+
Logging.logEvent(
90+
level: .debug,
91+
message: "Verbose logging enabled.",
92+
messageCode: .verboseLoggingEnabled
93+
)
9894
} else {
99-
Logging.default.info("""
95+
Logging.logEvent(level: .info, message: """
10096
[FirebaseVertexAI] To enable additional logging, add \
101-
`\(Logging.enableArgumentKey, privacy: .public)` as a launch argument in Xcode.
102-
""")
97+
`\(Logging.enableArgumentKey)` as a launch argument in Xcode.
98+
""", messageCode: .verboseLoggingDisabled)
10399
}
104-
Logging.default.debug("[FirebaseVertexAI] Model \(name, privacy: .public) initialized.")
100+
Logging.logEvent(
101+
level: .debug,
102+
message: "Model \(name) initialized.",
103+
messageCode: .generativeModelInitialized
104+
)
105105
}
106106

107107
/// Generates content from String and/or image inputs, given to the model as a prompt, that are

FirebaseVertexAI/Sources/Logging.swift

Lines changed: 40 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,59 +13,55 @@
1313
// limitations under the License.
1414

1515
import Foundation
16-
import OSLog
16+
import os.log
1717

18-
@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
19-
struct Logging {
20-
/// Subsystem that should be used for all Loggers.
21-
static let subsystem = "com.google.firebase.vertex-ai"
22-
23-
/// Default category used for most loggers, unless specialized.
24-
static let defaultCategory = ""
18+
@_implementationOnly import FirebaseCoreExtension
2519

26-
/// The argument required to enable additional logging.
27-
static let enableArgumentKey = "-FIRDebugEnabled"
20+
/// Enum of log messages.
21+
enum LoggerMessageCode: Int {
22+
case unknown = 0
23+
case verboseLoggingEnabled
24+
case verboseLoggingDisabled
25+
case firebaseMLAPIDisabled
26+
case generativeModelInitialized
27+
case generativeAIServiceNonHTTPResponse
28+
case loadRequestResponseError
29+
case loadRequestResponseErrorPayload
30+
case loadRequestStreamResponseError
31+
case loadRequestStreamResponseErrorPayload
32+
case loadRequestStreamResponseLine
33+
case loadRequestParseResponseFailedJSON
34+
case loadRequestParseResponseFailedJSONError
35+
case generateContentResponseNoCandidates
36+
case generateContentResponseNoText
37+
case generateContentResponseUnrecognizedFinishReason
38+
case generateContentResponseUnrecognizedBlockReason
39+
case generateContentResponseUnrecognizedBlockThreshold
40+
case generateContentResponseUnrecognizedHarmProbability
41+
case generateContentResponseUnrecognizedHarmCategory
42+
case appCheckTokenFetchFailed
43+
}
2844

29-
/// The argument required to enable additional logging in the Google AI SDK; used for migration.
30-
///
31-
/// To facilitate migration between the SDKs, this launch argument is also accepted to enable
32-
/// additional logging at this time, though it is expected to be removed in the future.
33-
static let migrationEnableArgumentKey = "-GoogleGenerativeAIDebugLogEnabled"
45+
enum Logging {
46+
/// Log identifier.
47+
static let service = "[FirebaseVertexAI]"
3448

35-
// No initializer available.
36-
@available(*, unavailable)
37-
private init() {}
49+
/// Subsystem that should be used for all Loggers.
50+
static let subsystem = "com.google.firebase"
3851

39-
/// The default logger that is visible for all users. Note: we shouldn't be using anything lower
40-
/// than `.notice`.
41-
static let `default` = Logger(subsystem: subsystem, category: defaultCategory)
52+
static let logObject = OSLog(subsystem: subsystem, category: service)
4253

43-
/// A non default
44-
static let network: Logger = {
45-
if additionalLoggingEnabled() {
46-
return Logger(subsystem: subsystem, category: "NetworkResponse")
47-
} else {
48-
// Return a valid logger that's using `OSLog.disabled` as the logger, hiding everything.
49-
return Logger(.disabled)
50-
}
51-
}()
54+
/// The argument required to enable additional logging.
55+
static let enableArgumentKey = "-FIRDebugEnabled"
5256

53-
///
54-
static let verbose: Logger = {
55-
if additionalLoggingEnabled() {
56-
return Logger(subsystem: subsystem, category: defaultCategory)
57-
} else {
58-
// Return a valid logger that's using `OSLog.disabled` as the logger, hiding everything.
59-
return Logger(.disabled)
60-
}
61-
}()
57+
static func logEvent(level: FirebaseLoggerLevel, message: String,
58+
messageCode: LoggerMessageCode) {
59+
let code = String(format: "I-VTX%06d", messageCode.rawValue)
60+
FirebaseLogger.log(level: level, service: Logging.service, code: code, message: message)
61+
}
6262

6363
/// Returns `true` if additional logging has been enabled via a launch argument.
6464
static func additionalLoggingEnabled() -> Bool {
65-
let arguments = ProcessInfo.processInfo.arguments
66-
if arguments.contains(enableArgumentKey) || arguments.contains(migrationEnableArgumentKey) {
67-
return true
68-
}
69-
return false
65+
return ProcessInfo.processInfo.arguments.contains(enableArgumentKey)
7066
}
7167
}

FirebaseVertexAI/Sources/Safety.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,11 @@ extension SafetyRating.HarmProbability: Decodable {
125125
public init(from decoder: Decoder) throws {
126126
let value = try decoder.singleValueContainer().decode(String.self)
127127
guard let decodedProbability = SafetyRating.HarmProbability(rawValue: value) else {
128-
Logging.default
129-
.error("[FirebaseVertexAI] Unrecognized HarmProbability with value \"\(value)\".")
128+
Logging.logEvent(
129+
level: .error,
130+
message: "Unrecognized HarmProbability with value \"\(value)\".",
131+
messageCode: .generateContentResponseUnrecognizedHarmProbability
132+
)
130133
self = .unknown
131134
return
132135
}
@@ -143,8 +146,11 @@ extension SafetySetting.HarmCategory: Codable {
143146
public init(from decoder: Decoder) throws {
144147
let value = try decoder.singleValueContainer().decode(String.self)
145148
guard let decodedCategory = SafetySetting.HarmCategory(rawValue: value) else {
146-
Logging.default
147-
.error("[FirebaseVertexAI] Unrecognized HarmCategory with value \"\(value)\".")
149+
Logging.logEvent(
150+
level: .error,
151+
message: "Unrecognized HarmCategory with value \"\(value)\".",
152+
messageCode: .generateContentResponseUnrecognizedHarmCategory
153+
)
148154
self = .unknown
149155
return
150156
}

0 commit comments

Comments
 (0)