From cb1162110e23e152c7d1f7b50651706b6d4e5b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ne=CC=8Cmec?= Date: Tue, 14 Apr 2026 10:06:01 +0200 Subject: [PATCH] Remove WebLogger --- README.md | 31 --- Sources/Logger/Loggers/WebLogger/WebLog.swift | 42 ---- .../Logger/Loggers/WebLogger/WebLogger.swift | 115 ---------- .../LoggerTests/Loggers/WebLoggerTests.swift | 214 ------------------ 4 files changed, 402 deletions(-) delete mode 100644 Sources/Logger/Loggers/WebLogger/WebLog.swift delete mode 100644 Sources/Logger/Loggers/WebLogger/WebLogger.swift delete mode 100644 Tests/LoggerTests/Loggers/WebLoggerTests.swift diff --git a/README.md b/README.md index 198e0bd..a015ab7 100644 --- a/README.md +++ b/README.md @@ -33,37 +33,6 @@ Wraps the native ```Logger``` to log messages both in the Xcode console and the Enables logging to a file. Each log file relates to a single day data. Another day, another log file is used. `numberOfLogFiles` specifies the number of log files that are stored. In other words, how many days back (log files) should be kept. If the last log file is filled, the first one gets overriden using the simplest Round-robin strategy. -#### `WebLogger` - -Enables logging via REST API to a target server. To reduce the traffic, logs are grouped into so-called batches when sent. A user can set the max size of such batches and also a max time interval between individual batches being sent. - - -The integrator is responsible for the creation of `URLRequest` with the log batches & firing the request. -Target server that receives logs is independent on the `WebLogger`. Thus the integrator is responsible for the implementation of a target server. The target server is to receive / parse / display the incoming log batches. If the does not wish to implement a customized server, we also provide [a simple server solution](https://github.com/Qase/LoggingServer/) written in Node.js. - -Here is an example of log batch in JSON: -``` -[ - {"severity": "VERBOSE", - "sessionName": "E598B4C1-2B08-4563-81C0-2A77E5CE0C3C", - "message":"/some/path/LoggerTests.swift - testWebLogger() - line 165: Test verbose", - "timestamp": 1529668897318.845}, - {"severity": "INFO", - "sessionName":"E598B4C1-2B08-4563-81C0-2A77E5CE0C3C", - "message": "/some/path/LoggerTests.swift - testWebLogger() - line 166: Test system", - "timestamp":1529668897319.6549}, - {"severity":"INFO", - "sessionName":"E598B4C1-2B08-4563-81C0-2A77E5CE0C3C", - "message":"/some/path/LoggerTests.swift - testWebLogger() - line 167: Test process", - "timestamp":1529668897319.6959} -] -``` - - -Here is the set of properties a user can customize: - - `sessionID` which can be used on a server to filter logs for a specific application instance - - `batchConfiguration` max batch size & time interval of batches - #### `ApplicationCallbackLogger` A special type of logger, that automatically logs all received UIApplication notifications, further called application callbacks. Here is a complete list of supported application callbacks: diff --git a/Sources/Logger/Loggers/WebLogger/WebLog.swift b/Sources/Logger/Loggers/WebLogger/WebLog.swift deleted file mode 100644 index ad76340..0000000 --- a/Sources/Logger/Loggers/WebLogger/WebLog.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// LogEntry.swift -// -// -// Created by Martin Troup on 29.09.2021. -// - -import Foundation - -struct WebLog { - let level: Level - let timestamp: Double - let message: CustomStringConvertible - let sessionID: UUID -} - -// MARK: - WebLog + Encodable - -extension WebLog: Encodable { - enum CodingKeys: String, CodingKey { - case level = "severity" - case timestamp - case message - case sessionID = "sessionName" - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(serverLevelName(for: level), forKey: .level) - try container.encode(timestamp, forKey: .timestamp) - try container.encode(message.description, forKey: .message) - try container.encode(sessionID.uuidString, forKey: .sessionID) - } - - private func serverLevelName(for level: Level) -> String { - level.rawValue.uppercased() - } -} - -// MARK: - LogEntryBatch - -typealias LogEntryBatch = Array diff --git a/Sources/Logger/Loggers/WebLogger/WebLogger.swift b/Sources/Logger/Loggers/WebLogger/WebLogger.swift deleted file mode 100644 index 5e2caba..0000000 --- a/Sources/Logger/Loggers/WebLogger/WebLogger.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// WebLogger.swift -// -// -// Created by Martin Troup on 24.09.2021. -// - -// TODO: Cover with unit tests - -import Foundation -import Combine - -public struct BatchConfiguration { - // Maximum size of a batch (a bag of logs) which is sent to a server - let maxSize: Int - // Maximum time window after which a batch (a bag of logs) is sent to a server - let timeWindow: S.SchedulerTimeType.Stride - // Queue on which batches (bags of logs) are being collected - let queue: S - - public init( - maxSize: Int, - timeWindow: S.SchedulerTimeType.Stride, - queue: S - ) { - self.maxSize = maxSize - self.timeWindow = timeWindow - self.queue = queue - } -} - -public class WebLogger: Logging { - // `sessionID` is used on a server to filter logs for a specific application instance. - // - each application may provide or is provided with a `sessionID` - // - `sessionID` may be persisted or renewed after each application run (implementation responsibility of `WebLogger` integrator) - let sessionID: UUID - // Configuration for batching individual logs (time, size & queue on which the batching happens). - let batchConfiguration: BatchConfiguration - // A function that is handling the log batch server sending. - // - passed is the log batch as an `Encodable` instance - // - returning `Void` if the request happens successfully, an instance of `Error` otherwise - // - the caller is responsible for creating & firing an instance of `URLRequest`. The `URLRequest` needs to attach - // the log batch (`Decodable` instance) passed in as a parameter - let requestPerformer: (Encodable) -> AnyPublisher - - private let logSubject = PassthroughSubject() - private var subscriptions = Set() - - public var levels: [Level] = [.info] - - /// `WebLogger` enables to configure and send logs to a specific server. - /// The integrator is responsible for providing a running server, that is able to receive the log batch and present it. - /// - Parameters: - /// - sessionID: can be used on a server to filter logs for a specific application instance - /// - batchConfiguration: configuration for batching individual logs - /// - requestPerformer: a function that is handling the log batch server sending - public init( - sessionID: UUID = UUID(), - batchConfiguration: BatchConfiguration, - requestPerformer: @escaping (Encodable) -> AnyPublisher - ) { - self.sessionID = sessionID - self.batchConfiguration = batchConfiguration - self.requestPerformer = requestPerformer - } - - public func configure() { - logSubject - .collect(.byTimeOrCount(batchConfiguration.queue, batchConfiguration.timeWindow, batchConfiguration.maxSize)) - .filter { $0.count > 0 } - .flatMap { [weak self] logsBatch -> AnyPublisher in - guard let self = self else { - print("WebLogger is nil while trying to reach it within a closure!") - - return Empty().eraseToAnyPublisher() - } - - return self.requestPerformer(logsBatch) - .catch { error -> Empty in - print("[WebLogger] Failing to send logs to a server with error: \(error)!") - - return Empty() - } - .eraseToAnyPublisher() - } - .sink(receiveValue: { _ in }) - .store(in: &subscriptions) - } - - public func log(_ logEntry: LogEntry) { - let entry = WebLog( - level: logEntry.header.level, - timestamp: Date().timeIntervalSince1970 * 1000, - message: "\(logEntry)", - sessionID: sessionID - ) - - logSubject.send(entry) - } -} - -// MARK: - Default init for `WebLogger` - -extension WebLogger where S == DispatchQueue { - public convenience init( - sessionID: UUID = UUID(), - requestPerformer: @escaping (Encodable) -> AnyPublisher - ) { - self.init( - sessionID: sessionID, - batchConfiguration: .init(maxSize: 5, timeWindow: 4, queue: .global(qos: .utility)), - requestPerformer: requestPerformer - ) - } -} diff --git a/Tests/LoggerTests/Loggers/WebLoggerTests.swift b/Tests/LoggerTests/Loggers/WebLoggerTests.swift deleted file mode 100644 index 66b97fc..0000000 --- a/Tests/LoggerTests/Loggers/WebLoggerTests.swift +++ /dev/null @@ -1,214 +0,0 @@ -// -// WebLoggerTests.swift -// -// -// Created by Martin Troup on 30.09.2021. -// - -import Combine -@testable import Logger -import XCTest - -class WebLoggerTests: XCTestCase { - - private var subscriptions: Set! - - override func setUp() { - super.setUp() - - subscriptions = Set() - } - - override func tearDown() { - subscriptions = nil - - super.tearDown() - } - - func test_WebLogger_init() { - let testUUID = UUID() - let webLogger = WebLogger( - sessionID: testUUID, - batchConfiguration: .init( - maxSize: 10, - timeWindow: 15, - queue: .main - ), - requestPerformer: { _ in Just(()).setFailureType(to: Error.self).eraseToAnyPublisher() } - ) - - XCTAssertEqual(webLogger.sessionID, testUUID) - XCTAssertEqual(webLogger.batchConfiguration.maxSize, 10) - XCTAssertEqual(webLogger.batchConfiguration.timeWindow, 15) - XCTAssertEqual(webLogger.batchConfiguration.queue, DispatchQueue.main) - - let expectation = self.expectation(description: "") - - var valueReceived = false - - webLogger.requestPerformer(Data()) - .sink( - receiveCompletion: { completion in - switch completion { - case let .failure(error): - XCTFail("Unexpected event received - error: \(error).") - case .finished: - expectation.fulfill() - } - }, - receiveValue: { - valueReceived = true - } - ) - .store(in: &subscriptions) - - waitForExpectations(timeout: 0.01) - XCTAssertTrue(valueReceived) - } - - func test_WebLogger_DispatchQueue_batchConfiguration_default_init() { - let testUUID = UUID() - let webLogger = WebLogger( - sessionID: testUUID, - requestPerformer: { _ in Just(()).setFailureType(to: Error.self).eraseToAnyPublisher() } - ) - - XCTAssertEqual(webLogger.sessionID, testUUID) - XCTAssertEqual(webLogger.batchConfiguration.maxSize, 5) - XCTAssertEqual(webLogger.batchConfiguration.timeWindow, 4) - XCTAssertEqual(webLogger.batchConfiguration.queue, .global(qos: .utility)) - - let expectation = self.expectation(description: "") - - var valueReceived = false - - webLogger.requestPerformer(Data()) - .sink( - receiveCompletion: { completion in - switch completion { - case let .failure(error): - XCTFail("Unexpected event received - error: \(error).") - case .finished: - expectation.fulfill() - } - }, - receiveValue: { - valueReceived = true - } - ) - .store(in: &subscriptions) - - waitForExpectations(timeout: 0.01) - XCTAssertTrue(valueReceived) - } - - func test_batching_by_size() { - var requestPerformerCount = 0 - - let webLogger = WebLogger( - sessionID: UUID(), - batchConfiguration: .init( - maxSize: 10, - timeWindow: 10, - queue: .main - ), - requestPerformer: { batch in - XCTAssertEqual((batch as! LogEntryBatch).count, 10) - - return Just(()).setFailureType(to: Error.self) - .handleEvents(receiveSubscription: { _ in - requestPerformerCount += 1 - }) - .eraseToAnyPublisher() - } - ) - - webLogger.configure() - - (0..<50).forEach { index in - webLogger.log(.mock("random-message-index-\(index)")) - } - } - - func test_batching_by_time() { - let expectation = self.expectation(description: "") - - let webLogger = WebLogger( - sessionID: UUID(), - batchConfiguration: .init( - maxSize: 10, - timeWindow: 0.05, - queue: .main - ), - requestPerformer: { batch in - XCTAssertEqual((batch as! LogEntryBatch).count, 5) - - return Just(()).setFailureType(to: Error.self) - .handleEvents(receiveSubscription: { _ in - expectation.fulfill() - }) - .eraseToAnyPublisher() - } - ) - - webLogger.configure() - - (0..<5).forEach { index in - webLogger.log(.mock("random-message-index-\(index)")) - } - - waitForExpectations(timeout: 0.1) - } - - func test_batching_by_size_and_time() { - let expectation = self.expectation(description: "") - - var requestPerformerCount = 0 - - let webLogger = WebLogger( - sessionID: UUID(), - batchConfiguration: .init( - maxSize: 10, - timeWindow: 0.1, - queue: .main - ), - requestPerformer: { batch in - switch requestPerformerCount { - case 0: - XCTAssertEqual((batch as! LogEntryBatch).count, 10) - case 1: - XCTAssertEqual((batch as! LogEntryBatch).count, 1) - case 2: - XCTAssertEqual((batch as! LogEntryBatch).count, 5) - default: - () - } - - return Just(()).setFailureType(to: Error.self) - .handleEvents(receiveSubscription: { _ in - requestPerformerCount += 1 - - if requestPerformerCount == 3 { - expectation.fulfill() - } - }) - .eraseToAnyPublisher() - } - ) - - webLogger.configure() - - (0..<11).forEach { - webLogger.log(.mock("random-message-index-\($0)")) - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - (0..<5).forEach { - webLogger.log(.mock("random-message-index-\($0)")) - } - } - - waitForExpectations(timeout: 1) - } -} -