Skip to content

Commit f79438e

Browse files
authored
Доработал логи (#216)
- Логируем ошибки на девайсе - Обновил формат логирования
1 parent c1e41fc commit f79438e

File tree

9 files changed

+151
-85
lines changed

9 files changed

+151
-85
lines changed

SwiftUI-WorkoutApp.xcodeproj/project.pbxproj

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@
275275
67419AD8282E8E9E004F5339 /* Settings */ = {
276276
isa = PBXGroup;
277277
children = (
278-
674E704F2B24F032008AE9D0 /* Logger */,
278+
674E704D2B24D382008AE9D0 /* LoggerScreen.swift */,
279279
670CA19D280E8F09003914A3 /* SettingsView.swift */,
280280
6798AA72280B43FE00DB76F1 /* LoginView.swift */,
281281
);
@@ -302,14 +302,6 @@
302302
path = SportsGrounds;
303303
sourceTree = "<group>";
304304
};
305-
674E704F2B24F032008AE9D0 /* Logger */ = {
306-
isa = PBXGroup;
307-
children = (
308-
674E704D2B24D382008AE9D0 /* LoggerScreen.swift */,
309-
);
310-
path = Logger;
311-
sourceTree = "<group>";
312-
};
313305
67515697283FEC0B00501346 /* ImagePicker */ = {
314306
isa = PBXGroup;
315307
children = (

SwiftUI-WorkoutApp/SWNetworkClient/Sources/SWNetworkClient/ErrorFilter.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import Foundation
55
/// Заменяет `catch CancellationError { break }`
66
public enum ErrorFilter {
77
public static func message(from error: Error) -> String {
8-
(error as NSError).code == -999 ? "" : error.localizedDescription
8+
let errorCode = (error as NSError).code
9+
if errorCode == -999 {
10+
logger.debug("Отфильтровали ошибку с кодом -999")
11+
return ""
12+
} else {
13+
return error.localizedDescription
14+
}
915
}
1016
}

SwiftUI-WorkoutApp/SWNetworkClient/Sources/SWNetworkClient/SWClient+.swift

Lines changed: 85 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import Foundation
2-
import OSLog
32
import SWModels
43

5-
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "SWClient")
6-
74
extension SWClient {
85
private var successCode: Int { 200 }
96
private var forceLogoutCode: Int { 401 }
@@ -21,7 +18,16 @@ extension SWClient {
2118
/// - request: запрос, по которому нужно обратиться
2219
/// - Returns: Вся информация по запрошенному типу
2320
func makeResult<T: Decodable>(_ type: T.Type, for request: URLRequest?) async throws -> T {
24-
guard let request = await finalRequest(request) else { throw APIError.badRequest }
21+
guard let request = await finalRequest(request) else {
22+
let apiError = APIError.badRequest
23+
logger.error(
24+
"""
25+
\(apiError.localizedDescription, privacy: .public)
26+
\nURL запроса: \(request?.url?.absoluteString ?? "-", privacy: .public)
27+
"""
28+
)
29+
throw apiError
30+
}
2531
let (data, response) = try await urlSession.data(for: request)
2632
return try await handle(type, data, response)
2733
}
@@ -31,7 +37,14 @@ extension SWClient {
3137
/// - Returns: Статус действия
3238
func makeStatus(for request: URLRequest?) async throws -> Bool {
3339
guard let request = await finalRequest(request) else {
34-
throw APIError.badRequest
40+
let apiError = APIError.badRequest
41+
logger.error(
42+
"""
43+
\(apiError.localizedDescription, privacy: .public)
44+
\nURL запроса: \(request?.url?.absoluteString ?? "-", privacy: .public)
45+
"""
46+
)
47+
throw apiError
3548
}
3649
let response = try await urlSession.data(for: request).1
3750
return try await handle(response)
@@ -56,8 +69,16 @@ extension SWClient {
5669

5770
/// Обрабатывает ответ сервера и возвращает данные в нужном формате
5871
func handle<T: Decodable>(_ type: T.Type, _ data: Data?, _ response: URLResponse?) async throws -> T {
72+
let urlString = response?.url?.absoluteString ?? "-"
5973
guard let data, !data.isEmpty else {
60-
throw APIError.noData
74+
let apiError = APIError.noData
75+
logger.error(
76+
"""
77+
\(apiError.localizedDescription, privacy: .public)
78+
\nURL запроса: \(urlString, privacy: .public)
79+
"""
80+
)
81+
throw apiError
6182
}
6283
let responseCode = (response as? HTTPURLResponse)?.statusCode
6384
guard responseCode == successCode else {
@@ -66,31 +87,51 @@ extension SWClient {
6687
}
6788
throw handleError(from: data, response: response)
6889
}
69-
#if DEBUG
70-
let urlString = response?.url?.absoluteString ?? "unknown"
71-
logger.info("✅ Получили JSON по запросу: \(urlString)")
72-
logger.debug("\(data.prettyJson)")
7390
do {
74-
_ = try JSONDecoder().decode(type, from: data)
91+
logger.debug(
92+
"""
93+
Обработали ответ сервера
94+
\nURL запроса: \(urlString, privacy: .public)
95+
\nJSON в ответе: \(data.prettyJson, privacy: .public)
96+
"""
97+
)
98+
return try JSONDecoder().decode(type, from: data)
7599
} catch {
76-
logger.error("⛔️ Ошибка декодирования: \(error)")
100+
logger.error(
101+
"""
102+
Ошибка декодирования: \(error.localizedDescription, privacy: .public)
103+
\nURL запроса: \(urlString, privacy: .public)
104+
\nJSON в ответе: \(data.prettyJson, privacy: .public)
105+
"""
106+
)
107+
throw error
77108
}
78-
#endif
79-
return try JSONDecoder().decode(type, from: data)
80109
}
81110

82111
/// Обрабатывает ответ сервера, в котором важен только статус
83112
func handle(_ response: URLResponse?) async throws -> Bool {
84113
let responseCode = (response as? HTTPURLResponse)?.statusCode
85-
#if DEBUG
86-
let urlString = response?.url?.absoluteString ?? "unknown"
87-
logger.info("✅ Получили статус \(responseCode ?? 0) по запросу: \(urlString)")
88-
#endif
114+
let urlString = response?.url?.absoluteString ?? "-"
115+
logger.debug(
116+
"""
117+
Обработали ответ сервера
118+
\nURL запроса: \(urlString, privacy: .public)
119+
\nСтатус в ответе: \(responseCode ?? 0, privacy: .public)
120+
"""
121+
)
89122
guard responseCode == successCode else {
90123
if canForceLogout, responseCode == forceLogoutCode {
91124
await defaults.triggerLogout()
92125
}
93-
throw APIError(with: responseCode)
126+
let apiError = APIError(with: responseCode)
127+
logger.error(
128+
"""
129+
Ошибка обработки ответа сервера: \(apiError.localizedDescription, privacy: .public)
130+
\nURL запроса: \(urlString, privacy: .public)
131+
\nСтатус в ответе: \(responseCode ?? 0, privacy: .public)
132+
"""
133+
)
134+
throw apiError
94135
}
95136
return true
96137
}
@@ -102,20 +143,31 @@ extension SWClient {
102143
/// - Returns: Готовая к выводу ошибка `APIError`
103144
func handleError(from data: Data, response: URLResponse?) -> APIError {
104145
let errorCode = (response as? HTTPURLResponse)?.statusCode
105-
#if DEBUG
106-
let errorCodeMessage = if let errorCode {
107-
"Код ошибки \(errorCode)"
108-
} else {
109-
"Ошибка!"
110-
}
111-
let urlString = response?.url?.absoluteString ?? "unknown"
112-
logger.error("⛔️ \(errorCodeMessage)\nJSON с ошибкой по запросу: \(urlString)")
113-
logger.debug("\(data.prettyJson)")
114-
#endif
115-
if let errorInfo = try? JSONDecoder().decode(ErrorResponse.self, from: data) {
116-
return APIError(errorInfo, errorCode)
117-
} else {
118-
return APIError(with: errorCode)
146+
let urlString = response?.url?.absoluteString ?? "-"
147+
do {
148+
let errorInfo = try JSONDecoder().decode(ErrorResponse.self, from: data)
149+
let apiError = APIError(errorInfo, errorCode)
150+
logger.debug(
151+
"""
152+
Обработали ошибку в ответе
153+
\nКод ошибки: \(errorCode ?? 0, privacy: .public)
154+
\nURL запроса: \(urlString, privacy: .public)
155+
\nJSON с ошибкой: \(data.prettyJson, privacy: .public)
156+
\nПреобразованная ошибка: \(apiError.localizedDescription, privacy: .public)
157+
"""
158+
)
159+
return apiError
160+
} catch {
161+
let apiError = APIError(with: errorCode)
162+
logger.error(
163+
"""
164+
Ошибка декодирования: \(error.localizedDescription, privacy: .public)
165+
\nURL запроса: \(urlString, privacy: .public)
166+
\nJSON с ошибкой: \(data.prettyJson, privacy: .public)
167+
\nПреобразованная ошибка: \(apiError.localizedDescription, privacy: .public)
168+
"""
169+
)
170+
return apiError
119171
}
120172
}
121173
}

SwiftUI-WorkoutApp/SWNetworkClient/Sources/SWNetworkClient/SWClient.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import Foundation
2+
import OSLog
23
import SWModels
34

5+
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "SWClient")
6+
47
/// Сервис для обращений к серверу
58
public struct SWClient: Sendable {
69
/// Сервис, отвечающий за обновление `UserDefaults`

SwiftUI-WorkoutApp/Screens/Common/SportsGroundLocation/MapSnapshotView.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,8 @@ private extension MapSnapshotView {
9292
snapshotImage = image
9393
} else {
9494
snapshotImage = nil
95-
#if DEBUG
9695
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "MapSnapshotView")
97-
logger.error("Ошибка при создании снапшота карты: \(error)")
98-
#endif
96+
logger.error("Ошибка при создании снапшота карты: \(error, privacy: .public)")
9997
}
10098
}
10199
}

SwiftUI-WorkoutApp/Screens/Settings/Logger/LoggerScreen.swift renamed to SwiftUI-WorkoutApp/Screens/Settings/LoggerScreen.swift

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
#if DEBUG
2-
import Foundation
32
import OSLog
43
import SWDesignSystem
54
import SwiftUI
65

76
private final class LogStore: ObservableObject {
8-
private static let logger = Logger(
7+
private let logger = Logger(
98
subsystem: Bundle.main.bundleIdentifier!,
109
category: String(describing: LogStore.self)
1110
)
@@ -40,13 +39,15 @@ private final class LogStore: ObservableObject {
4039
message: $0.composedMessage
4140
)
4241
}
42+
let uniqueCategories = Array(Set(entries.map(\.category)))
43+
let uniqueLevels = Array(Set(entries.map(\.level)))
4344
await MainActor.run {
44-
categories = Array(Set(entries.map(\.category)))
45-
levels = Array(Set(entries.map(\.level)))
45+
categories = uniqueCategories
46+
levels = uniqueLevels
4647
state = .ready(entries)
4748
}
4849
} catch {
49-
Self.logger.warning("\(error.localizedDescription, privacy: .public)")
50+
logger.error("\(error.localizedDescription, privacy: .public)")
5051
await MainActor.run { state = .empty }
5152
}
5253
}
@@ -114,6 +115,32 @@ struct LoggerScreen: View {
114115
}
115116

116117
var body: some View {
118+
contentView
119+
.animation(.default, value: logStore.state)
120+
.loadingOverlay(if: logStore.state.isLoading)
121+
.frame(maxWidth: .infinity, maxHeight: .infinity)
122+
.navigationTitle("Логи")
123+
.background(Color.swBackground)
124+
.task { await logStore.getLogs() }
125+
.toolbar {
126+
ToolbarItem(placement: .topBarTrailing) {
127+
Button {
128+
showFilter = true
129+
} label: {
130+
Icons.Regular.filter.view
131+
.symbolVariant(isFilterOn ? .fill : .none)
132+
}
133+
.disabled(logStore.state.isLoading)
134+
}
135+
}
136+
.sheet(isPresented: $showFilter) {
137+
ContentInSheet(title: "Фильтр логов", spacing: 0) {
138+
filterView
139+
}
140+
}
141+
}
142+
143+
private var contentView: some View {
117144
ZStack {
118145
switch logStore.state {
119146
case .empty:
@@ -145,28 +172,6 @@ struct LoggerScreen: View {
145172
}
146173
}
147174
}
148-
.animation(.default, value: logStore.state)
149-
.loadingOverlay(if: logStore.state.isLoading)
150-
.frame(maxWidth: .infinity, maxHeight: .infinity)
151-
.navigationTitle("Логи")
152-
.background(Color.swBackground)
153-
.task { await logStore.getLogs() }
154-
.toolbar {
155-
ToolbarItem(placement: .topBarTrailing) {
156-
Button {
157-
showFilter = true
158-
} label: {
159-
Icons.Regular.filter.view
160-
.symbolVariant(isFilterOn ? .fill : .none)
161-
}
162-
.disabled(logStore.state.isLoading)
163-
}
164-
}
165-
.sheet(isPresented: $showFilter) {
166-
ContentInSheet(title: "Фильтр логов", spacing: 0) {
167-
filterView
168-
}
169-
}
170175
}
171176

172177
private var filterView: some View {

SwiftUI-WorkoutApp/Screens/SportsGrounds/Detail/SportsGroundDetailView.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import NetworkStatus
2+
import OSLog
23
import SWDesignSystem
34
import SwiftUI
45
import SWModels
56
import SWNetworkClient
67

78
/// Экран с детальной информацией о площадке
89
struct SportsGroundDetailView: View {
10+
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "SportsGroundDetailView")
911
@Environment(\.dismiss) private var dismiss
1012
@EnvironmentObject private var network: NetworkStatus
1113
@EnvironmentObject private var defaults: DefaultsService
@@ -400,8 +402,13 @@ private extension SportsGroundDetailView {
400402

401403
func process(_ error: Error) {
402404
if error.localizedDescription.contains("404") {
403-
// Похоже, был запрос данных о несуществующей площадке.
404-
// Удаляем её из памяти и закрываем экран
405+
logger.debug(
406+
"""
407+
Похоже, был запрос данных о несуществующей площадке
408+
id площадки: \(ground.id, privacy: .public)
409+
Удаляем ее из памяти и закрываем экран
410+
"""
411+
)
405412
onDeletion(ground.id)
406413
} else {
407414
setupErrorAlert(ErrorFilter.message(from: error))

SwiftUI-WorkoutApp/Services/SWAddress.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ extension SWAddress {
6969
do {
7070
return try city(with: cityID, in: countryID)?.name
7171
} catch {
72-
logger.error("Не смогли получить название города, ошибка: \(error)")
72+
logger.error("Не смогли получить название города, \(error.localizedDescription, privacy: .public)")
7373
return nil
7474
}
7575
}
@@ -81,10 +81,10 @@ extension SWAddress {
8181
func save(_ countries: [Country]) -> Bool {
8282
do {
8383
try storage.save(countries)
84-
logger.info("Успешно сохранили список стран")
84+
logger.debug("Успешно сохранили список стран в количестве \(countries.count, privacy: .public) шт.")
8585
return true
8686
} catch {
87-
logger.error("⛔️ Не смогли сохранить список стран, ошибка: \(error)")
87+
logger.error("Не смогли сохранить список стран, \(error.localizedDescription, privacy: .public)")
8888
return false
8989
}
9090
}
@@ -114,9 +114,7 @@ extension SWAddress {
114114
static func updateIfNeeded(_ oldAddress: inout String, placemark: CLPlacemark) {
115115
if let fullAddress = fullAddress(for: placemark), fullAddress != oldAddress {
116116
oldAddress = fullAddress
117-
#if DEBUG
118-
logger.info("Местоположение пользователя: \(fullAddress)")
119-
#endif
117+
logger.debug("Местоположение пользователя: \(fullAddress, privacy: .public)")
120118
}
121119
}
122120
}

0 commit comments

Comments
 (0)