1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Crypto
import Foundation
/// An object that can printed for logging and also offers a redacted description
/// when logging in contexts in which private information shouldn't be captured.
public protocol CustomLogStringConvertible: CustomStringConvertible, Sendable {
/// A full description of the object.
var description: String { get }
/// A description of the object that doesn't contain any private information.
var redactedDescription: String { get }
}
/// When an NSObject is logged with OSLog in private mode and the object
/// implements `redactedDescription`, OSLog will log that information instead of
/// just logging `<private>`.
///
/// There currently is no way to get equivalent functionality in pure Swift. We
/// thus pass this object to OSLog, which just forwards to `description` or
/// `redactedDescription` of an object that implements `CustomLogStringConvertible`.
public final class CustomLogStringConvertibleWrapper: NSObject, Sendable {
private let underlyingObject: any CustomLogStringConvertible
fileprivate init(_ underlyingObject: any CustomLogStringConvertible) {
self.underlyingObject = underlyingObject
}
public override var description: String {
return underlyingObject.description
}
#if canImport(os)
// When using OSLog mark redactedDescription as @objc so that OSLog can find it via the Objective-C runtime.
// We can't unconditionally mark it as @objc because eg. Linux doesn't have the Objective-C runtime.
@objc
#endif
public var redactedDescription: String {
underlyingObject.redactedDescription
}
}
extension CustomLogStringConvertible {
/// Returns an object that can be passed to OSLog, which will print the
/// `redactedDescription` if logging of private information is disabled and
/// will log `description` otherwise.
public var forLogging: CustomLogStringConvertibleWrapper {
return CustomLogStringConvertibleWrapper(self)
}
}
extension String {
/// A hash value that can be logged in a redacted description without
/// disclosing any private information about the string.
public var hashForLogging: String {
return Insecure.MD5.hash(data: Data(self.utf8)).description
}
}
private struct OptionalWrapper<Wrapped>: CustomLogStringConvertible where Wrapped: CustomLogStringConvertible {
let optional: Optional<Wrapped>
public var description: String {
return optional?.description ?? "<nil>"
}
public var redactedDescription: String {
return optional?.redactedDescription ?? "<nil>"
}
}
extension Optional where Wrapped: CustomLogStringConvertible {
public var forLogging: CustomLogStringConvertibleWrapper {
return CustomLogStringConvertibleWrapper(OptionalWrapper(optional: self))
}
}
|