Signpost.swift 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import os.log
  2. import os.signpost
  3. protocol RangeSignpost {
  4. func end(_ format: @autoclosure () -> StaticString, _ arguments: @autoclosure () -> [String])
  5. func end(_ format: @autoclosure () -> StaticString)
  6. }
  7. enum Signpost {
  8. static func point(
  9. _ category: Logger.Category,
  10. name: StaticString,
  11. format: StaticString? = nil,
  12. arguments: @autoclosure () -> [String] = []
  13. ) {
  14. guard Config.withSignPosts else { return }
  15. if #available(iOS 12.0, *) {
  16. let log = category.logger.log
  17. let signpostID = OSSignpostID(log: log)
  18. set(.event, log: log, name: name, signpostID: signpostID, format, arguments())
  19. }
  20. }
  21. static func perform<T>(
  22. _: Logger.Category,
  23. name: StaticString,
  24. block: () throws -> T
  25. ) rethrows -> T {
  26. let signpost = Signpost.range(.businessLogic, name: name)
  27. defer { signpost.end(name) }
  28. return try block()
  29. }
  30. static func range(
  31. _ category: Logger.Category,
  32. name: StaticString
  33. ) -> RangeSignpost {
  34. guard Config.withSignPosts else { return EmptySignpost() }
  35. if #available(iOS 12.0, *) {
  36. return BaseRangeSignpost(category, name)
  37. }
  38. return EmptySignpost()
  39. }
  40. static func range(
  41. _ category: Logger.Category,
  42. name: StaticString,
  43. format: StaticString,
  44. arguments: @autoclosure () -> [String] = []
  45. ) -> RangeSignpost {
  46. guard Config.withSignPosts else { return EmptySignpost() }
  47. if #available(iOS 12.0, *) {
  48. return BaseRangeSignpost(category, name, format, arguments())
  49. }
  50. return EmptySignpost()
  51. }
  52. @available(iOS 12.0, *) fileprivate static func set(
  53. _ type: OSSignpostType,
  54. log: OSLog,
  55. name: StaticString,
  56. signpostID: OSSignpostID,
  57. _ format: StaticString? = nil,
  58. _ arguments: [String] = []
  59. ) {
  60. if let format = format {
  61. switch arguments.count {
  62. case 0: os_signpost(type, log: log, name: name, signpostID: signpostID, format)
  63. case 1: os_signpost(type, log: log, name: name, signpostID: signpostID, format, arguments[0])
  64. case 2: os_signpost(type, log: log, name: name, signpostID: signpostID, format, arguments[0], arguments[1])
  65. case 3: os_signpost(
  66. type,
  67. log: log,
  68. name: name,
  69. signpostID: signpostID,
  70. format,
  71. arguments[0],
  72. arguments[1],
  73. arguments[2]
  74. )
  75. case 4: os_signpost(
  76. type,
  77. log: log,
  78. name: name,
  79. signpostID: signpostID,
  80. format,
  81. arguments[0],
  82. arguments[1],
  83. arguments[2],
  84. arguments[3]
  85. )
  86. case 5: os_signpost(
  87. type,
  88. log: log,
  89. name: name,
  90. signpostID: signpostID,
  91. format,
  92. arguments[0],
  93. arguments[1],
  94. arguments[2],
  95. arguments[3],
  96. arguments[4]
  97. )
  98. default: error(.service, "Signpost.set is not implemented for size.", description: "\(arguments.count)")
  99. }
  100. } else {
  101. os_signpost(type, log: log, name: name, signpostID: signpostID)
  102. }
  103. }
  104. }
  105. struct EmptySignpost: RangeSignpost {
  106. func end(_: @autoclosure () -> StaticString, _: @autoclosure () -> [String]) {}
  107. func end(_: @autoclosure () -> StaticString) {}
  108. }
  109. @available(iOS 12.0, *) final class BaseRangeSignpost: RangeSignpost {
  110. private let log: OSLog
  111. private let signpostID: OSSignpostID
  112. private let name: StaticString
  113. private var endFormat: StaticString?
  114. private var endArguments: [String] = []
  115. init(_ category: Logger.Category, _ name: StaticString) {
  116. log = category.logger.log
  117. signpostID = OSSignpostID(log: log)
  118. self.name = name
  119. Signpost.set(.begin, log: log, name: name, signpostID: signpostID)
  120. }
  121. init(_ category: Logger.Category, _ name: StaticString, _ format: StaticString, _ arguments: [String]) {
  122. log = category.logger.log
  123. signpostID = OSSignpostID(log: log)
  124. self.name = name
  125. Signpost.set(.begin, log: log, name: name, signpostID: signpostID, format, arguments)
  126. }
  127. deinit {
  128. Signpost.set(.end, log: log, name: name, signpostID: signpostID, endFormat, endArguments)
  129. }
  130. func end(_ format: @autoclosure () -> StaticString, _ arguments: @autoclosure () -> [String]) {
  131. endFormat = format()
  132. endArguments = arguments()
  133. }
  134. func end(_ format: @autoclosure () -> StaticString) {
  135. end(format(), [])
  136. }
  137. }