Skip to content

Commit ce3a7df

Browse files
Added more comprehensive logging metadata
1 parent a0e9471 commit ce3a7df

File tree

1 file changed

+54
-13
lines changed

1 file changed

+54
-13
lines changed

Sources/WebPush/WebPushManager.swift

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ public actor WebPushManager: Sendable {
104104
backgroundActivityLogger: Logger,
105105
executor: Executor
106106
) {
107+
var backgroundActivityLogger = backgroundActivityLogger
108+
backgroundActivityLogger[metadataKey: "vapidConfiguration"] = [
109+
"contactInformation" : "\(vapidConfiguration.contactInformation)",
110+
"primaryKey" : "\(vapidConfiguration.primaryKey?.id.description ?? "nil")",
111+
"keys" : .array(vapidConfiguration.keys.map { .string($0.id.description) }),
112+
"deprecatedKeys" : .array((vapidConfiguration.deprecatedKeys ?? []).map { .string($0.id.description) }),
113+
"validityDuration" : "\(vapidConfiguration.validityDuration)",
114+
"expirationDuration" : "\(vapidConfiguration.expirationDuration)",
115+
]
116+
107117
if vapidConfiguration.validityDuration > vapidConfiguration.expirationDuration {
108118
assertionFailure("The validity duration must be earlier than the expiration duration since it represents when the VAPID Authorization token will be refreshed ahead of it expiring.")
109119
backgroundActivityLogger.error("The validity duration must be earlier than the expiration duration since it represents when the VAPID Authorization token will be refreshed ahead of it expiring. Run your application server with the same configuration in debug mode to catch this.")
@@ -251,13 +261,15 @@ public actor WebPushManager: Sendable {
251261
) async throws {
252262
switch executor {
253263
case .httpClient(let httpClient):
264+
var logger = logger ?? backgroundActivityLogger
265+
logger[metadataKey: "message"] = "\(message)"
254266
try await execute(
255267
httpClient: httpClient,
256268
data: message,
257269
subscriber: subscriber,
258270
expiration: expiration,
259271
urgency: urgency,
260-
logger: logger ?? backgroundActivityLogger
272+
logger: logger
261273
)
262274
case .handler(let handler):
263275
try await handler(.data(Data(message)), Subscriber(subscriber), expiration, urgency)
@@ -330,6 +342,8 @@ public actor WebPushManager: Sendable {
330342
urgency: Urgency,
331343
logger: Logger
332344
) async throws {
345+
var logger = logger
346+
logger[metadataKey: "message"] = "\(message)"
333347
switch executor {
334348
case .httpClient(let httpClient):
335349
try await execute(
@@ -366,11 +380,19 @@ public actor WebPushManager: Sendable {
366380
urgency: Urgency,
367381
logger: Logger
368382
) async throws {
369-
guard let signingKey = vapidKeyLookup[subscriber.vapidKeyID]
370-
else {
371-
logger.warning("A key was not found for this subscriber.", metadata: [
372-
"vapidKeyID": "\(subscriber.vapidKeyID)"
373-
])
383+
var logger = logger
384+
logger[metadataKey: "subscriber"] = [
385+
"vapidKeyID" : "\(subscriber.vapidKeyID)",
386+
"endpoint" : "\(subscriber.endpoint)",
387+
]
388+
logger[metadataKey: "expiration"] = "\(expiration)"
389+
logger[metadataKey: "urgency"] = "\(urgency)"
390+
logger[metadataKey: "origin"] = "\(subscriber.endpoint.origin)"
391+
logger[metadataKey: "messageSize"] = "\(message.count)"
392+
logger.trace("Sending notification")
393+
394+
guard let signingKey = vapidKeyLookup[subscriber.vapidKeyID] else {
395+
logger.warning("A key was not found for this subscriber.")
374396
throw VAPID.ConfigurationError.matchingKeyNotFound
375397
}
376398

@@ -380,15 +402,20 @@ public actor WebPushManager: Sendable {
380402

381403
/// Perform key exchange between the user agent's public key and our private key, deriving a shared secret.
382404
let userAgent = subscriber.userAgentKeyMaterial
383-
guard let sharedSecret = try? applicationServerECDHPrivateKey.sharedSecretFromKeyAgreement(with: userAgent.publicKey)
384-
else { throw BadSubscriberError() }
405+
let sharedSecret: SharedSecret
406+
do {
407+
sharedSecret = try applicationServerECDHPrivateKey.sharedSecretFromKeyAgreement(with: userAgent.publicKey)
408+
} catch {
409+
logger.debug("A shared secret could not be derived from the subscriber's public key and the newly-generated private key.", metadata: ["error" : "\(error)"])
410+
throw BadSubscriberError()
411+
}
385412

386413
/// Generate a 16-byte salt.
387414
var salt: [UInt8] = Array(repeating: 0, count: 16)
388415
for index in salt.indices { salt[index] = .random(in: .min ... .max) }
389416

390417
if message.count > Self.maximumMessageSize {
391-
logger.warning("Push message is longer than the maximum guarantee made by the spec: \(Self.maximumMessageSize) bytes. Sending this message may fail, and its size will be leaked despite being encrypted. Please consider sending less data to keep your communications secure.", metadata: ["message": "\(message)"])
418+
logger.warning("Push message is longer than the maximum guarantee made by the spec: \(Self.maximumMessageSize) bytes. Sending this message may fail, and its size will be leaked despite being encrypted. Please consider sending less data to keep your communications secure.")
392419
}
393420

394421
/// Prepare the payload by padding it so the final message is 4KB.
@@ -438,9 +465,9 @@ public actor WebPushManager: Sendable {
438465
let requestContent = contentCodingHeader + encryptedRecord.ciphertext + encryptedRecord.tag
439466

440467
if expiration < Expiration.dropIfUndeliverable {
441-
logger.error("The message expiration must be greater than or equal to \(Expiration.dropIfUndeliverable) seconds.", metadata: ["expiration": "\(expiration)"])
468+
logger.error("The message expiration must be greater than or equal to \(Expiration.dropIfUndeliverable) seconds.")
442469
} else if expiration > Expiration.recommendedMaximum {
443-
logger.warning("The message expiration should be less than \(Expiration.recommendedMaximum) seconds.", metadata: ["expiration": "\(expiration)"])
470+
logger.warning("The message expiration should be less than \(Expiration.recommendedMaximum) seconds.")
444471
}
445472

446473
/// Add the VAPID authorization and corrent content encoding and type.
@@ -455,6 +482,9 @@ public actor WebPushManager: Sendable {
455482

456483
/// Send the request to the push endpoint.
457484
let response = try await httpClient.execute(request, deadline: .distantFuture, logger: logger)
485+
logger[metadataKey: "response"] = "\(response)"
486+
logger[metadataKey: "statusCode"] = "\(response.status)"
487+
logger.trace("Sent notification")
458488

459489
/// Check the response and determine if the subscription should be removed from our records, or if the notification should just be skipped.
460490
switch response.status {
@@ -464,7 +494,7 @@ public actor WebPushManager: Sendable {
464494
// TODO: 429 too many requests, 500 internal server error, 503 server shutting down - check config and perform a retry after a delay?
465495
default: throw HTTPError(response: response)
466496
}
467-
logger.trace("Sent \(message) notification to \(subscriber): \(response)")
497+
logger.trace("Successfully sent notification")
468498
}
469499
}
470500

@@ -645,7 +675,7 @@ extension WebPushManager.Urgency: Codable {
645675

646676
extension WebPushManager {
647677
/// An internal type representing a push message, accessible when using ``/WebPushTesting``.
648-
public enum _Message: Sendable {
678+
public enum _Message: Sendable, CustomStringConvertible {
649679
/// A message originally sent via ``WebPushManager/send(data:to:expiration:urgency:)``
650680
case data(Data)
651681

@@ -670,6 +700,17 @@ extension WebPushManager {
670700
}
671701
}
672702
}
703+
704+
public var description: String {
705+
switch self {
706+
case .data(let data):
707+
return ".data(\(data.base64URLEncodedString()))"
708+
case .string(let string):
709+
return ".string(\(string))"
710+
case .json(let json):
711+
return ".json(\(json))"
712+
}
713+
}
673714
}
674715

675716
/// An internal type representing the executor for a push message.

0 commit comments

Comments
 (0)