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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Logging API open source project
//
// Copyright (c) 2018-2019 Apple Inc. and the Swift Logging API project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Logging API project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import Logging
import XCTest
class LocalLoggerTest: XCTestCase {
func test1() throws {
// bootstrap with our test logging impl
let logging = TestLogging()
LoggingSystem.bootstrapInternal(logging.make)
// change test logging config to log traces and above
logging.config.set(value: Logger.Level.debug)
// run our program
let context = Context()
Struct1().doSomething(context: context)
// test results
logging.history.assertExist(level: .debug, message: "Struct1::doSomething", source: "LoggingTests")
logging.history.assertNotExist(level: .debug, message: "Struct1::doSomethingElse")
logging.history.assertExist(level: .info, message: "Struct2::doSomething")
logging.history.assertExist(level: .info, message: "Struct2::doSomethingElse")
logging.history.assertExist(level: .error, message: "Struct3::doSomething", metadata: ["bar": "baz"])
logging.history.assertExist(level: .error, message: "Struct3::doSomethingElse", metadata: ["bar": "baz"])
logging.history.assertExist(level: .warning, message: "Struct3::doSomethingElseAsync", metadata: ["bar": "baz"])
logging.history.assertExist(level: .info, message: "TestLibrary::doSomething")
logging.history.assertExist(level: .info, message: "TestLibrary::doSomethingAsync")
logging.history.assertExist(level: .debug, message: "Struct3::doSomethingElse::Local", metadata: ["bar": "baz", "baz": "qux"])
logging.history.assertExist(level: .debug, message: "Struct3::doSomethingElse::end", metadata: ["bar": "baz"])
logging.history.assertExist(level: .debug, message: "Struct3::doSomething::end", metadata: ["bar": "baz"])
logging.history.assertExist(level: .debug, message: "Struct2::doSomethingElse::end")
logging.history.assertExist(level: .debug, message: "Struct1::doSomethingElse::end")
logging.history.assertExist(level: .debug, message: "Struct1::doSomething::end")
}
func test2() throws {
// bootstrap with our test logging impl
let logging = TestLogging()
LoggingSystem.bootstrapInternal(logging.make)
// change test logging config to log errors and above
logging.config.set(value: Logger.Level.error)
// run our program
let context = Context()
Struct1().doSomething(context: context)
// test results
logging.history.assertNotExist(level: .debug, message: "Struct1::doSomething") // global context
logging.history.assertNotExist(level: .debug, message: "Struct1::doSomethingElse") // global context
logging.history.assertExist(level: .info, message: "Struct2::doSomething") // local context
logging.history.assertExist(level: .info, message: "Struct2::doSomethingElse") // local context
logging.history.assertExist(level: .error, message: "Struct3::doSomething", metadata: ["bar": "baz"]) // local context
logging.history.assertExist(level: .error, message: "Struct3::doSomethingElse", metadata: ["bar": "baz"]) // local context
logging.history.assertExist(level: .warning, message: "Struct3::doSomethingElseAsync", metadata: ["bar": "baz"]) // local context
logging.history.assertNotExist(level: .info, message: "TestLibrary::doSomething") // global context
logging.history.assertNotExist(level: .info, message: "TestLibrary::doSomethingAsync") // global context
logging.history.assertExist(level: .debug, message: "Struct3::doSomethingElse::Local", metadata: ["bar": "baz", "baz": "qux"]) // hyper local context
logging.history.assertExist(level: .debug, message: "Struct3::doSomethingElse::end", metadata: ["bar": "baz"]) // local context
logging.history.assertExist(level: .debug, message: "Struct2::doSomethingElse::end") // local context
logging.history.assertExist(level: .debug, message: "Struct3::doSomething::end", metadata: ["bar": "baz"]) // local context
logging.history.assertNotExist(level: .debug, message: "Struct1::doSomethingElse::end") // global context
logging.history.assertNotExist(level: .debug, message: "Struct1::doSomething::end") // global context
}
}
// systems that follow the context pattern need to implement something like this
private struct Context {
var logger = Logger(label: "LocalLoggerTest::ContextLogger")
// since logger is a value type, we can reuse our copy to manage logLevel
var logLevel: Logger.Level {
get { return self.logger.logLevel }
set { self.logger.logLevel = newValue }
}
// since logger is a value type, we can reuse our copy to manage metadata
subscript(metadataKey: String) -> Logger.Metadata.Value? {
get { return self.logger[metadataKey: metadataKey] }
set { self.logger[metadataKey: metadataKey] = newValue }
}
}
private struct Struct1 {
func doSomething(context: Context) {
context.logger.debug("Struct1::doSomething")
self.doSomethingElse(context: context)
context.logger.debug("Struct1::doSomething::end")
}
private func doSomethingElse(context: Context) {
let originalContext = context
var context = context
context.logger.logLevel = .warning
context.logger.debug("Struct1::doSomethingElse")
Struct2().doSomething(context: context)
originalContext.logger.debug("Struct1::doSomethingElse::end")
}
}
private struct Struct2 {
func doSomething(context: Context) {
var c = context
c.logLevel = .info // only effects from this point on
c.logger.info("Struct2::doSomething")
self.doSomethingElse(context: c)
c.logger.debug("Struct2::doSomething::end")
}
private func doSomethingElse(context: Context) {
var c = context
c.logLevel = .debug // only effects from this point on
c.logger.info("Struct2::doSomethingElse")
Struct3().doSomething(context: c)
c.logger.debug("Struct2::doSomethingElse::end")
}
}
private struct Struct3 {
private let queue = DispatchQueue(label: "LocalLoggerTest::Struct3")
func doSomething(context: Context) {
var c = context
c["bar"] = "baz" // only effects from this point on
c.logger.error("Struct3::doSomething")
self.doSomethingElse(context: c)
c.logger.debug("Struct3::doSomething::end")
}
private func doSomethingElse(context: Context) {
context.logger.error("Struct3::doSomethingElse")
let group = DispatchGroup()
group.enter()
self.queue.async {
context.logger.warning("Struct3::doSomethingElseAsync")
let library = TestLibrary()
library.doSomething()
library.doSomethingAsync {
group.leave()
}
}
group.wait()
// only effects the logger instance
var l = context.logger
l[metadataKey: "baz"] = "qux"
l.debug("Struct3::doSomethingElse::Local")
context.logger.debug("Struct3::doSomethingElse::end")
}
}
|