Files
e-party-iOS/yana/APIs/APILogger.swift
edwinQQQ 007c10daaf feat: 添加Swift Package管理和API功能模块
新增Package.swift和Package.resolved文件以支持Swift Package管理,创建API相关文件(API.swift、APICaller.swift、APIConstants.swift、APIEndpoints.swift、APIService.swift、APILogger.swift、APIModels.swift、Integration-Guide.md)以实现API请求管理和网络交互功能,增强项目的功能性和可扩展性。同时更新.gitignore以排除构建文件和临时文件。
2025-06-04 17:25:21 +08:00

214 lines
8.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Foundation
// MARK: - API Logger
class APILogger {
enum LogLevel {
case none
case basic
case detailed
}
#if DEBUG
static var logLevel: LogLevel = .detailed
#else
static var logLevel: LogLevel = .none
#endif
private static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss.SSS"
return formatter
}()
// MARK: - Request Logging
static func logRequest<T: APIRequestProtocol>(_ request: T, url: URL, body: Data?, finalHeaders: [String: String]? = nil) {
guard logLevel != .none else { return }
let timestamp = dateFormatter.string(from: Date())
print("\n🚀 [API Request] [\(timestamp)] ==================")
print("📍 Endpoint: \(request.endpoint)")
print("🔗 Full URL: \(url.absoluteString)")
print("📝 Method: \(request.method.rawValue)")
print("⏰ Timeout: \(request.timeout)s")
// headers headers headers
if let headers = finalHeaders, !headers.isEmpty {
if logLevel == .detailed {
print("📋 Final Headers (包括默认 + 自定义):")
for (key, value) in headers.sorted(by: { $0.key < $1.key }) {
print(" \(key): \(value)")
}
} else if logLevel == .basic {
print("📋 Headers: \(headers.count) 个 headers")
// headers
let importantHeaders = ["Content-Type", "Accept", "User-Agent", "Authorization"]
for key in importantHeaders {
if let value = headers[key] {
print(" \(key): \(value)")
}
}
}
} else if let customHeaders = request.headers, !customHeaders.isEmpty {
print("📋 Custom Headers:")
for (key, value) in customHeaders.sorted(by: { $0.key < $1.key }) {
print(" \(key): \(value)")
}
} else {
print("📋 Headers: 使用默认 headers")
}
if let queryParams = request.queryParameters, !queryParams.isEmpty {
print("🔍 Query Parameters:")
for (key, value) in queryParams.sorted(by: { $0.key < $1.key }) {
print(" \(key): \(value)")
}
}
if logLevel == .detailed {
if let body = body {
print("📦 Request Body (\(body.count) bytes):")
if let jsonObject = try? JSONSerialization.jsonObject(with: body, options: []),
let prettyData = try? JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted),
let prettyString = String(data: prettyData, encoding: .utf8) {
print(prettyString)
} else if let rawString = String(data: body, encoding: .utf8) {
print(rawString)
} else {
print("Binary data")
}
} else {
print("📦 Request Body: No body")
}
//
if request.includeBaseParameters {
print("📱 Base Parameters: 自动注入设备和应用信息")
let baseParams = BaseRequest()
print(" Device: \(baseParams.model), OS: \(baseParams.os) \(baseParams.osVersion)")
print(" App: \(baseParams.app) v\(baseParams.appVersion)")
print(" Language: \(baseParams.acceptLanguage)")
}
} else if logLevel == .basic {
if let body = body {
print("📦 Request Body: \(formatBytes(body.count))")
} else {
print("📦 Request Body: No body")
}
//
if request.includeBaseParameters {
print("📱 Base Parameters: 已自动注入")
}
}
print("=====================================")
}
// MARK: - Response Logging
static func logResponse(data: Data, response: HTTPURLResponse, duration: TimeInterval) {
guard logLevel != .none else { return }
let timestamp = dateFormatter.string(from: Date())
let statusEmoji = response.statusCode < 400 ? "" : ""
print("\n\(statusEmoji) [API Response] [\(timestamp)] ===================")
print("⏱️ Duration: \(String(format: "%.3f", duration))s")
print("📊 Status Code: \(response.statusCode)")
print("🔗 URL: \(response.url?.absoluteString ?? "Unknown")")
print("📏 Data Size: \(formatBytes(data.count))")
if logLevel == .detailed {
print("📋 Response Headers:")
for (key, value) in response.allHeaderFields.sorted(by: { "\($0.key)" < "\($1.key)" }) {
print(" \(key): \(value)")
}
print("📦 Response Data:")
if data.isEmpty {
print(" Empty response")
} else if let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []),
let prettyData = try? JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted),
let prettyString = String(data: prettyData, encoding: .utf8) {
print(prettyString)
} else if let rawString = String(data: data, encoding: .utf8) {
print(rawString)
} else {
print(" Binary data (\(data.count) bytes)")
}
}
print("=====================================")
}
// MARK: - Error Logging
static func logError(_ error: Error, url: URL?, duration: TimeInterval) {
guard logLevel != .none else { return }
let timestamp = dateFormatter.string(from: Date())
print("\n❌ [API Error] [\(timestamp)] ======================")
print("⏱️ Duration: \(String(format: "%.3f", duration))s")
if let url = url {
print("🔗 URL: \(url.absoluteString)")
}
if let apiError = error as? APIError {
print("🚨 API Error: \(apiError.localizedDescription)")
} else {
print("🚨 System Error: \(error.localizedDescription)")
}
if logLevel == .detailed {
if let urlError = error as? URLError {
print("🔍 URLError Code: \(urlError.code.rawValue)")
print("🔍 URLError Localized: \(urlError.localizedDescription)")
//
switch urlError.code {
case .timedOut:
print("💡 建议:检查网络连接或增加超时时间")
case .notConnectedToInternet:
print("💡 建议:检查网络连接")
case .cannotConnectToHost:
print("💡 建议:检查服务器地址和端口")
case .resourceUnavailable:
print("💡 建议:检查 API 端点是否正确")
default:
break
}
}
print("🔍 Full Error: \(error)")
}
print("=====================================\n")
}
// MARK: - Decoded Response Logging
static func logDecodedResponse<T>(_ response: T, type: T.Type) {
guard logLevel == .detailed else { return }
let timestamp = dateFormatter.string(from: Date())
print("🎯 [Decoded Response] [\(timestamp)] Type: \(type)")
print("=====================================\n")
}
// MARK: - Helper Methods
private static func formatBytes(_ bytes: Int) -> String {
let formatter = ByteCountFormatter()
formatter.allowedUnits = [.useKB, .useMB]
formatter.countStyle = .file
return formatter.string(fromByteCount: Int64(bytes))
}
// MARK: - Performance Logging
static func logPerformanceWarning(duration: TimeInterval, threshold: TimeInterval = 5.0) {
guard logLevel != .none && duration > threshold else { return }
let timestamp = dateFormatter.string(from: Date())
print("\n⚠️ [Performance Warning] [\(timestamp)] ============")
print("🐌 Request took \(String(format: "%.3f", duration))s (threshold: \(threshold)s)")
print("💡 建议:检查网络条件或优化 API 响应")
print("================================================\n")
}
}