MobileCoin-Swift/Sources/Common/MobileCoinLogging.swift
2021-05-10 19:51:21 -07:00

498 lines
15 KiB
Swift

//
// Copyright (c) 2020-2021 MobileCoin. All rights reserved.
//
// swiftlint:disable function_parameter_count prefixed_toplevel_constant
import Logging
public enum MobileCoinLogging {
public static var logSensitiveData = false {
willSet {
guard logSensitiveDataInternal.set(newValue) else {
logger.preconditionFailure(
"logSensitiveData can only be set prior to using the MobileCoin SDK.")
}
}
}
}
internal let logger = Logger(label: "com.mobilecoin", factory: ContextPrefixLogHandler.init)
private struct ContextPrefixLogHandler: LogHandler {
static let ENABLE_LOG_FUNCTION_METADATA_KEY = "LOG_FUNCTION_METADATA_KEY"
private var logger: Logger
subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
get { logger[metadataKey: metadataKey] }
set { logger[metadataKey: metadataKey] = newValue }
}
init(label: String) {
self.logger = Logger(label: label)
}
// `metadata` isn't currently accessible via `logger`, so there's not much we can do.
// Fortunately, it's not accessed by `Logger` either, so we're just going to ignore it.
var metadata: Logger.Metadata {
get { [:] }
set { _ = newValue }
}
var logLevel: Logger.Level {
get { logger.logLevel }
set { logger.logLevel = newValue }
}
func log(
level: Logger.Level,
message: Logger.Message,
metadata: Logger.Metadata?,
source: String,
file: String,
function: String,
line: UInt
) {
var (message, metadata) = (message, metadata)
// Remove log function metadata entry and add file/line/function name if it was enabled.
if metadata?
.removeValue(forKey: ContextPrefixLogHandler.ENABLE_LOG_FUNCTION_METADATA_KEY) != nil
{
let filename = URL(fileURLWithPath: file, isDirectory: false).lastPathComponent
message = "\(filename):\(line):\(function) - \(message)"
}
logger.log(
level: level,
message,
metadata: metadata,
source: source,
file: file,
function: function,
line: line)
}
}
// The value of `logSensitiveDataInternal` gets locked in place upon first read.
private let logSensitiveDataInternal = ImmutableOnceReadLock(false)
extension Logger {
func assert(
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String = String(),
file: StaticString = #file,
function: String = #function,
line: UInt = #line
) {
if condition() {
assertionFailure(message(), file: file, function: function, line: line)
}
}
func precondition(
_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String = String(),
file: StaticString = #file,
function: String = #function,
line: UInt = #line
) {
if condition() {
preconditionFailure(message(), file: file, function: function, line: line)
}
}
func assertionFailure(
_ message: @autoclosure () -> String = String(),
file: StaticString = #file,
function: String = #function,
line: UInt = #line
) {
let message = message()
error("\(message)", file: "\(file)", function: function, line: line)
Swift.assertionFailure(message, file: file, line: line)
}
func preconditionFailure(
_ message: @autoclosure () -> String = String(),
file: StaticString = #file,
function: String = #function,
line: UInt = #line
) -> Never {
let message = message()
critical("\(message)", file: "\(file)", function: function, line: line)
return Swift.preconditionFailure(message, file: file, line: line)
}
func fatalError(
_ message: @autoclosure () -> String = String(),
file: StaticString = #file,
function: String = #function,
line: UInt = #line
) -> Never {
let message = message()
critical("\(message)", file: "\(file)", function: function, line: line)
return Swift.fatalError(message, file: file, line: line)
}
}
extension Logger {
func trace(
_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
guard !sensitive || logSensitiveDataInternal.get() else { return }
let metadata = logFunction ? { Self.addingLogFunctionKey(metadata()) } : metadata
trace(
message(),
metadata: metadata(),
source: source(),
file: file,
function: function,
line: line)
}
func debug(
_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
guard !sensitive || logSensitiveDataInternal.get() else { return }
let metadata = logFunction ? { Self.addingLogFunctionKey(metadata()) } : metadata
debug(
message(),
metadata: metadata(),
source: source(),
file: file,
function: function,
line: line)
}
func info(
_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
guard !sensitive || logSensitiveDataInternal.get() else { return }
let metadata = logFunction ? { Self.addingLogFunctionKey(metadata()) } : metadata
info(
message(),
metadata: metadata(),
source: source(),
file: file,
function: function,
line: line)
}
func notice(
_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
guard !sensitive || logSensitiveDataInternal.get() else { return }
let metadata = logFunction ? { Self.addingLogFunctionKey(metadata()) } : metadata
notice(
message(),
metadata: metadata(),
source: source(),
file: file,
function: function,
line: line)
}
func warning(
_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
guard !sensitive || logSensitiveDataInternal.get() else { return }
let metadata = logFunction ? { Self.addingLogFunctionKey(metadata()) } : metadata
warning(
message(),
metadata: metadata(),
source: source(),
file: file,
function: function,
line: line)
}
func error(
_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
guard !sensitive || logSensitiveDataInternal.get() else { return }
let metadata = logFunction ? { Self.addingLogFunctionKey(metadata()) } : metadata
error(
message(),
metadata: metadata(),
source: source(),
file: file,
function: function,
line: line)
}
func critical(
_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
guard !sensitive || logSensitiveDataInternal.get() else { return }
let metadata = logFunction ? { Self.addingLogFunctionKey(metadata()) } : metadata
critical(
message(),
metadata: metadata(),
source: source(),
file: file,
function: function,
line: line)
}
private static func addingLogFunctionKey(_ metadata: Logger.Metadata?) -> Logger.Metadata {
var metadata = metadata ?? Logger.Metadata()
metadata[ContextPrefixLogHandler.ENABLE_LOG_FUNCTION_METADATA_KEY] = "1"
return metadata
}
}
extension Logger {
func trace(
_ message: @autoclosure () -> String,
metadata: @autoclosure () -> Logging.Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
trace(
Message(stringLiteral: message()),
metadata: metadata(),
source: source(),
sensitive: sensitive,
logFunction: logFunction,
file: file,
function: function,
line: line)
}
func debug(
_ message: @autoclosure () -> String,
metadata: @autoclosure () -> Logging.Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
debug(
Message(stringLiteral: message()),
metadata: metadata(),
source: source(),
sensitive: sensitive,
logFunction: logFunction,
file: file,
function: function,
line: line)
}
func info(
_ message: @autoclosure () -> String,
metadata: @autoclosure () -> Logging.Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
info(
Message(stringLiteral: message()),
metadata: metadata(),
source: source(),
sensitive: sensitive,
logFunction: logFunction,
file: file,
function: function,
line: line)
}
func notice(
_ message: @autoclosure () -> String,
metadata: @autoclosure () -> Logging.Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
notice(
Message(stringLiteral: message()),
metadata: metadata(),
source: source(),
sensitive: sensitive,
logFunction: logFunction,
file: file,
function: function,
line: line)
}
func warning(
_ message: @autoclosure () -> String,
metadata: @autoclosure () -> Logging.Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
warning(
Message(stringLiteral: message()),
metadata: metadata(),
source: source(),
sensitive: sensitive,
logFunction: logFunction,
file: file,
function: function,
line: line)
}
func error(
_ message: @autoclosure () -> String,
metadata: @autoclosure () -> Logging.Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
error(
Message(stringLiteral: message()),
metadata: metadata(),
source: source(),
sensitive: sensitive,
logFunction: logFunction,
file: file,
function: function,
line: line)
}
func critical(
_ message: @autoclosure () -> String,
metadata: @autoclosure () -> Logging.Logger.Metadata? = nil,
source: @autoclosure () -> String? = nil,
sensitive: Bool = false,
logFunction: Bool = true,
file: String = #file,
function: String = #function,
line: UInt = #line
) {
critical(
Message(stringLiteral: message()),
metadata: metadata(),
source: source(),
sensitive: sensitive,
logFunction: logFunction,
file: file,
function: function,
line: line)
}
}
extension DefaultStringInterpolation {
mutating func appendInterpolation<T>(redacting value: T)
where T: CustomStringConvertible & TextOutputStreamable
{
if logSensitiveDataInternal.get() {
appendInterpolation(value)
} else {
appendInterpolation("<redacted>")
}
}
mutating func appendInterpolation<T: TextOutputStreamable>(redacting value: T) {
if logSensitiveDataInternal.get() {
appendInterpolation(value)
} else {
appendInterpolation("<redacted>")
}
}
mutating func appendInterpolation<T: CustomStringConvertible>(redacting value: T) {
if logSensitiveDataInternal.get() {
appendInterpolation(value)
} else {
appendInterpolation("<redacted>")
}
}
mutating func appendInterpolation<T>(redacting value: T) {
if logSensitiveDataInternal.get() {
appendInterpolation(value)
} else {
appendInterpolation("<redacted>")
}
}
mutating func appendInterpolation(redacting value: Any.Type) {
if logSensitiveDataInternal.get() {
appendInterpolation(value)
} else {
appendInterpolation("<redacted>")
}
}
mutating func appendInterpolation<T: CustomRedactingStringConvertible>(redacting value: T) {
appendInterpolation(value.redactingDescription)
}
}