Skip to content

Commit 06eba62

Browse files
committed
logging support
Motivation: AsyncHTTPClient is not a simple piece of software and nowadays also quite stateful. To debug issues, the user may want logging. Modification: Support passing a logger to the request methods. Result: Debugging simplified.
1 parent 070c1e5 commit 06eba62

12 files changed

+1116
-414
lines changed

Diff for: Package.swift

+5-2
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,18 @@ let package = Package(
2525
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.7.0"),
2626
.package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.3.0"),
2727
.package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.5.1"),
28+
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
2829
],
2930
targets: [
3031
.target(
3132
name: "AsyncHTTPClient",
32-
dependencies: ["NIO", "NIOHTTP1", "NIOSSL", "NIOConcurrencyHelpers", "NIOHTTPCompression", "NIOFoundationCompat", "NIOTransportServices"]
33+
dependencies: ["NIO", "NIOHTTP1", "NIOSSL", "NIOConcurrencyHelpers", "NIOHTTPCompression",
34+
"NIOFoundationCompat", "NIOTransportServices", "Logging"]
3335
),
3436
.testTarget(
3537
name: "AsyncHTTPClientTests",
36-
dependencies: ["NIO", "NIOConcurrencyHelpers", "NIOSSL", "AsyncHTTPClient", "NIOFoundationCompat", "NIOTestUtils"]
38+
dependencies: ["NIO", "NIOConcurrencyHelpers", "NIOSSL", "AsyncHTTPClient", "NIOFoundationCompat",
39+
"NIOTestUtils", "Logging"]
3740
),
3841
]
3942
)

Diff for: Sources/AsyncHTTPClient/ConnectionPool.swift

+97-35
Large diffs are not rendered by default.

Diff for: Sources/AsyncHTTPClient/HTTPClient.swift

+225-25
Large diffs are not rendered by default.

Diff for: Sources/AsyncHTTPClient/HTTPHandler.swift

+25-10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import Foundation
16+
import Logging
1617
import NIO
1718
import NIOConcurrencyHelpers
1819
import NIOFoundationCompat
@@ -494,17 +495,19 @@ extension HTTPClient {
494495
var connection: Connection?
495496
var cancelled: Bool
496497
let lock: Lock
498+
let logger: Logger // We are okay to store the logger here because a Task is for only ond request.
497499

498-
init(eventLoop: EventLoop) {
500+
init(eventLoop: EventLoop, logger: Logger) {
499501
self.eventLoop = eventLoop
500502
self.promise = eventLoop.makePromise()
501503
self.completion = self.promise.futureResult.map { _ in }
502504
self.cancelled = false
503505
self.lock = Lock()
506+
self.logger = logger
504507
}
505508

506-
static func failedTask(eventLoop: EventLoop, error: Error) -> Task<Response> {
507-
let task = self.init(eventLoop: eventLoop)
509+
static func failedTask(eventLoop: EventLoop, error: Error, logger: Logger) -> Task<Response> {
510+
let task = self.init(eventLoop: eventLoop, logger: logger)
508511
task.promise.fail(error)
509512
return task
510513
}
@@ -546,13 +549,18 @@ extension HTTPClient {
546549
}
547550
}
548551

549-
func succeed<Delegate: HTTPClientResponseDelegate>(promise: EventLoopPromise<Response>?, with value: Response, delegateType: Delegate.Type, closing: Bool) {
550-
self.releaseAssociatedConnection(delegateType: delegateType, closing: closing).whenSuccess {
552+
func succeed<Delegate: HTTPClientResponseDelegate>(promise: EventLoopPromise<Response>?,
553+
with value: Response,
554+
delegateType: Delegate.Type,
555+
closing: Bool) {
556+
self.releaseAssociatedConnection(delegateType: delegateType,
557+
closing: closing).whenSuccess {
551558
promise?.succeed(value)
552559
}
553560
}
554561

555-
func fail<Delegate: HTTPClientResponseDelegate>(with error: Error, delegateType: Delegate.Type) {
562+
func fail<Delegate: HTTPClientResponseDelegate>(with error: Error,
563+
delegateType: Delegate.Type) {
556564
if let connection = self.connection {
557565
self.releaseAssociatedConnection(delegateType: delegateType, closing: true)
558566
.whenSuccess {
@@ -562,13 +570,14 @@ extension HTTPClient {
562570
}
563571
}
564572

565-
func releaseAssociatedConnection<Delegate: HTTPClientResponseDelegate>(delegateType: Delegate.Type, closing: Bool) -> EventLoopFuture<Void> {
573+
func releaseAssociatedConnection<Delegate: HTTPClientResponseDelegate>(delegateType: Delegate.Type,
574+
closing: Bool) -> EventLoopFuture<Void> {
566575
if let connection = self.connection {
567576
// remove read timeout handler
568577
return connection.removeHandler(IdleStateHandler.self).flatMap {
569578
connection.removeHandler(TaskHandler<Delegate>.self)
570579
}.map {
571-
connection.release(closing: closing)
580+
connection.release(closing: closing, logger: self.logger)
572581
}.flatMapError { error in
573582
fatalError("Couldn't remove taskHandler: \(error)")
574583
}
@@ -600,6 +609,7 @@ internal class TaskHandler<Delegate: HTTPClientResponseDelegate>: RemovableChann
600609
let delegate: Delegate
601610
let redirectHandler: RedirectHandler<Delegate.Response>?
602611
let ignoreUncleanSSLShutdown: Bool
612+
let logger: Logger // We are okay to store the logger here because a TaskHandler is just for one request.
603613

604614
var state: State = .idle
605615
var pendingRead = false
@@ -617,12 +627,14 @@ internal class TaskHandler<Delegate: HTTPClientResponseDelegate>: RemovableChann
617627
kind: HTTPClient.Request.Kind,
618628
delegate: Delegate,
619629
redirectHandler: RedirectHandler<Delegate.Response>?,
620-
ignoreUncleanSSLShutdown: Bool) {
630+
ignoreUncleanSSLShutdown: Bool,
631+
logger: Logger) {
621632
self.task = task
622633
self.delegate = delegate
623634
self.redirectHandler = redirectHandler
624635
self.ignoreUncleanSSLShutdown = ignoreUncleanSSLShutdown
625636
self.kind = kind
637+
self.logger = logger
626638
}
627639
}
628640

@@ -678,7 +690,10 @@ extension TaskHandler {
678690
do {
679691
let result = try body(self.task)
680692

681-
self.task.succeed(promise: promise, with: result, delegateType: Delegate.self, closing: self.closing)
693+
self.task.succeed(promise: promise,
694+
with: result,
695+
delegateType: Delegate.self,
696+
closing: self.closing)
682697
} catch {
683698
self.task.fail(with: error, delegateType: Delegate.self)
684699
}

Diff for: Sources/AsyncHTTPClient/NoOpLogHandler.swift

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Logging
16+
17+
internal struct NoOpLogHandler: LogHandler {
18+
func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) {}
19+
20+
subscript(metadataKey _: String) -> Logger.Metadata.Value? {
21+
get {
22+
return nil
23+
}
24+
set {}
25+
}
26+
27+
var metadata: Logger.Metadata {
28+
get {
29+
return [:]
30+
}
31+
set {}
32+
}
33+
34+
var logLevel: Logger.Level {
35+
get {
36+
return .critical
37+
}
38+
set {}
39+
}
40+
}
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
extension Connection: CustomStringConvertible {
16+
var description: String {
17+
return "\(self.channel)"
18+
}
19+
}
20+
21+
extension HTTP1ConnectionProvider.Waiter: CustomStringConvertible {
22+
var description: String {
23+
return "HTTP1ConnectionProvider.Waiter(\(self.preference))"
24+
}
25+
}
26+
27+
extension HTTPClient.EventLoopPreference: CustomStringConvertible {
28+
public var description: String {
29+
return "\(self.preference)"
30+
}
31+
}

0 commit comments

Comments
 (0)