Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filename to LoggerMessageEntity #49

Merged
merged 3 commits into from
Oct 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ let package = Package(
targets: [
.target(name: "PulseUI", dependencies: ["PulseCore"], path: "Pulse/Sources/PulseUI"),
.target(name: "Pulse", dependencies: [.product(name: "Logging", package: "swift-log"), "PulseCore"], path: "Pulse/Sources/Pulse"),
.target(name: "PulseCore", path: "Pulse/Sources/PulseCore"),
.target(name: "PulseCore", dependencies: ["PulseInternal"], path: "Pulse/Sources/PulseCore"),
.target(name: "PulseInternal", path: "Pulse/Sources/PulseInternal"),
.testTarget(name: "PulseTests", dependencies: ["Pulse"], path: "Pulse/Tests/PulseTests", resources: [.process("Resources")])
]
)
3 changes: 2 additions & 1 deletion Pulse/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ let package = Package(
targets: [
.target(name: "PulseUI", dependencies: ["PulseCore"]),
.target(name: "Pulse", dependencies: [.product(name: "Logging", package: "swift-log"), "PulseCore"]),
.target(name: "PulseCore"),
.target(name: "PulseCore", dependencies: ["PulseInternal"]),
.target(name: "PulseInternal"),
.testTarget(name: "PulseTests", dependencies: ["Pulse"], resources: [.process("Resources")])
]
)
4 changes: 3 additions & 1 deletion Pulse/Sources/PulseCore/LoggerStore+MOM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ public extension LoggerStore {
let text = NSAttributeDescription(name: "text", type: .stringAttributeType)
let metadata = NSRelationshipDescription.make(name: "metadata", type: .oneToMany, entity: metadata)
let file = NSAttributeDescription(name: "file", type: .stringAttributeType)
let filename = NSAttributeDescription(name: "filename", type: .stringAttributeType)
let function = NSAttributeDescription(name: "function", type: .stringAttributeType)
let line = NSAttributeDescription(name: "line", type: .integer32AttributeType)
let isPinned = NSAttributeDescription(name: "isPinned", type: .booleanAttributeType)
let requestState = NSAttributeDescription(name: "requestState", type: .integer16AttributeType)
let request = NSRelationshipDescription.make(name: "request", type: .oneToOne(isOptional: true), entity: request)
message.properties = [createdAt, level, levelOrder, label, session, text, metadata, file, function, line, isPinned, requestState, request]
message.properties = [createdAt, level, levelOrder, label, session, text, metadata, file, filename, function, line, isPinned, requestState, request]
}

do {
Expand Down Expand Up @@ -127,6 +128,7 @@ public final class LoggerMessageEntity: NSManagedObject {
@NSManaged public var text: String
@NSManaged public var metadata: Set<LoggerMetadataEntity>
@NSManaged public var file: String
@NSManaged public var filename: String
@NSManaged public var function: String
@NSManaged public var line: Int32
@NSManaged public var isPinned: Bool
Expand Down
119 changes: 61 additions & 58 deletions Pulse/Sources/PulseCore/LoggerStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import Foundation
import CoreData
import PulseInternal

/// `LoggerStore` persistently stores all of the logged messages, network
/// requests, and blobs. Use `.default` store to log messages.
Expand All @@ -27,7 +28,7 @@ public final class LoggerStore {
private let document: PulseDocument

/// Determines how often the messages are saved to the database. By default,
/// 100 milliseconds.
/// 100 milliseconds - quickly enough, but avoiding too many individual writes.
public var saveInterval: DispatchTimeInterval = .milliseconds(100)

/// Size limit in bytes. `30 Mb` by default. The limit is approximate.
Expand All @@ -48,7 +49,8 @@ public final class LoggerStore {

var onEvent: ((LoggerStoreEvent) -> Void)?

private let encodingQueue = DispatchQueue(label: "com.github.kean.logger-store")
// Marked internal only for testing purposes.
let encodingQueue = DispatchQueue(label: "com.github.kean.logger-store")

private var isSaveScheduled = false

Expand Down Expand Up @@ -190,10 +192,16 @@ public final class LoggerStore {
private func setNeedsSave() {
guard !isSaveScheduled else { return }
isSaveScheduled = true
DispatchQueue.main.asyncAfter(deadline: .now() + saveInterval) {
self.backgroundContext.perform {
DispatchQueue.main.asyncAfter(deadline: .now() + saveInterval) { [weak self] in
guard let self = self else { return }
self.backgroundContext.perform { [weak self] in
guard let self = self else { return }
if self.backgroundContext.hasChanges {
try? _ExceptionCatcher.catchException {
try? self.backgroundContext.save()
}
}
self.isSaveScheduled = false
try? self.backgroundContext.save()
}
}
}
Expand All @@ -205,8 +213,6 @@ public final class LoggerStore {
store.setValue("DELETE" as NSString, forPragmaNamed: "journal_mode")
}
container.persistentStoreDescriptions = [store]
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
return container
}

Expand All @@ -221,6 +227,9 @@ public final class LoggerStore {
if let error = loadError {
throw error
}

container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
}

private static func makeBackgroundContext(for container: NSPersistentContainer) -> NSManagedObjectContext {
Expand All @@ -235,70 +244,55 @@ public final class LoggerStore {
extension LoggerStore {
/// Stores the given message.
public func storeMessage(label: String, level: Level, message: String, metadata: [String: MetadataValue]?, file: String = #file, function: String = #function, line: UInt = #line) {
let message = Message(createdAt: makeCurrentDate(), label: label, level: level, message: message, metadata: metadata?.unpack(), session: LoggerSession.current.id.uuidString, file: file, function: function, line: line)
storeMessage(message)
let date = makeCurrentDate()
perform {
let message = Message(createdAt: date, label: label, level: level, message: message, metadata: metadata?.unpack(), session: LoggerSession.current.id.uuidString, file: file, function: function, line: line)
self.makeMessageEntity(with: message)
self.onEvent?(.saveMessage(message))
}
}

/// Stores the given message.
func storeMessage(_ message: Message) {
let context = backgroundContext
context.perform {
self.makeMessageEntity(with: message)
self.setNeedsSave()
perform {
self._storeMessage(message)
}

onEvent?(.saveMessage(message))
}

private func _storeMessage(_ message: Message) {
makeMessageEntity(with: message)
}

/// Stores the network request.
///
/// - note: If you want to store incremental updates to the task, use
/// `NetworkLogger` instead.
public func storeRequest(_ request: URLRequest, response: URLResponse?, error: Error?, data: Data?, metrics: URLSessionTaskMetrics? = nil) {
let context = NetworkLogger.TaskContext()
context.request = request
context.response = response
context.error = error
context.data = data ?? Data()
context.metrics = metrics.map(NetworkLoggerMetrics.init)

storeNetworkRequest(context)
storeRequest(request, response: response, error: error, data: data, metrics: metrics.map(NetworkLoggerMetrics.init))
}

func storeNetworkRequest(_ context: NetworkLogger.TaskContext) {
guard let urlRequest = context.request else { return }


func storeRequest(_ request: URLRequest, response: URLResponse?, error: Error?, data: Data?, metrics: NetworkLoggerMetrics?) {
let date = makeCurrentDate()
encodingQueue.async {
self.storeNetworkRequest(urlRequest: urlRequest, context, date: date)
perform {
let message = NetworkMessage(
createdAt: date,
request: NetworkLoggerRequest(urlRequest: request),
response: response.map(NetworkLoggerResponse.init),
error: error.map(NetworkLoggerError.init),
requestBody: request.httpBody ?? request.httpBodyStreamData(),
responseBody: data,
metrics: metrics,
session: LoggerSession.current.id.uuidString
)
self._storeNetworkRequest(message)
self.onEvent?(.saveNetworkMessage(message))
}
}

private func storeNetworkRequest(urlRequest: URLRequest, _ context: NetworkLogger.TaskContext, date: Date) {
// This is pretty expensive because it perform JSON encoding, we do
// this on the encoding queue.
let message = NetworkMessage(
createdAt: date,
request: NetworkLoggerRequest(urlRequest: urlRequest),
response: context.response.map(NetworkLoggerResponse.init),
error: context.error.map(NetworkLoggerError.init),
requestBody: urlRequest.httpBody ?? urlRequest.httpBodyStreamData(),
responseBody: context.data,
metrics: context.metrics,
session: LoggerSession.current.id.uuidString
)
storeRequest(message)
}

/// Stores the network request.
func storeRequest(_ message: NetworkMessage) {
let context = backgroundContext
context.perform {
self._storeNetworkRequest(message)
self.setNeedsSave()
func storeRequest(_ request: NetworkMessage) {
perform {
self._storeNetworkRequest(request)
}

onEvent?(.saveNetworkMessage(message))
}

private func _storeNetworkRequest(_ summary: NetworkMessage) {
Expand All @@ -321,7 +315,7 @@ extension LoggerStore {
let messageObject = Message(createdAt: summary.createdAt, label: "network", level: level, message: message, metadata: nil, session: summary.session, file: "", function: "", line: 0)

let messageEntity = self.makeMessageEntity(with: messageObject)
let requestEntity = self.makeRequest(summary, createdAt: summary.createdAt)
let requestEntity = self.makeNetworkRequestEntity(summary, createdAt: summary.createdAt)
messageEntity.request = requestEntity
messageEntity.requestState = requestEntity.requestState
requestEntity.message = messageEntity
Expand All @@ -345,13 +339,14 @@ extension LoggerStore {
})
}
entity.file = message.file
entity.filename = (message.file as NSString).lastPathComponent
entity.function = message.function
entity.line = Int32(message.line)
return entity
}

@discardableResult
private func makeRequest(_ summary: LoggerStore.NetworkMessage, createdAt: Date) -> LoggerNetworkRequestEntity {
private func makeNetworkRequestEntity(_ summary: LoggerStore.NetworkMessage, createdAt: Date) -> LoggerNetworkRequestEntity {
let entity = LoggerNetworkRequestEntity(context: backgroundContext)
// Primary
entity.createdAt = createdAt
Expand Down Expand Up @@ -396,14 +391,14 @@ extension LoggerStore {

/// Toggles pin for the give message.
public func togglePin(for message: LoggerMessageEntity) {
performChanges { _ in
performChangesOnMain { _ in
message.isPinned.toggle()
}
}

/// Removes all pins.
public func removeAllPins() {
performChanges { context in
performChangesOnMain { context in
let request = NSFetchRequest<LoggerMessageEntity>(entityName: "\(LoggerMessageEntity.self)")
request.fetchBatchSize = 250
request.predicate = NSPredicate(format: "isPinned == YES")
Expand All @@ -418,12 +413,20 @@ extension LoggerStore {
// MARK: Direct Modifiction

/// Perform and save changes on the main queue.
func performChanges(_ closure: (NSManagedObjectContext) -> Void) {
private func performChangesOnMain(_ closure: (NSManagedObjectContext) -> Void) {
precondition(Thread.isMainThread)
closure(container.viewContext)
try? container.viewContext.save()
}

private func perform(_ changes: @escaping () -> Void) {
guard !isReadonly else { return }
backgroundContext.perform {
changes()
self.setNeedsSave()
}
}

// MARK: Accessing Data

/// Returns blob data for the given key.
Expand Down
11 changes: 4 additions & 7 deletions Pulse/Sources/PulseCore/NetworkLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,13 @@ public final class NetworkLogger {
defer { lock.unlock() }

let context = self.context(for: task)
tasks[ObjectIdentifier(task)] = nil

if let response = task.response {
context.response = response
guard let request = context.request, let response = context.response else {
return // This should never happen
}
context.error = error

store.storeNetworkRequest(context)

tasks[ObjectIdentifier(task)] = nil
store.storeRequest(request, response: response, error: error, data: context.data, metrics: context.metrics)
}

/// Logs the task metrics (optional).
Expand All @@ -90,7 +88,6 @@ public final class NetworkLogger {
var request: URLRequest?
var response: URLResponse?
lazy var data = Data()
var error: Error?
var metrics: NetworkLoggerMetrics?
}

Expand Down
11 changes: 11 additions & 0 deletions Pulse/Sources/PulseCore/include/CatchExceptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface _ExceptionCatcher: NSObject

+ (BOOL)catchException:(__attribute__((noescape)) void(^)(void))tryBlock error:(__autoreleasing NSError **)error;

@end

NS_ASSUME_NONNULL_END
20 changes: 20 additions & 0 deletions Pulse/Sources/PulseInternal/ExceptionCatcher.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#import "ExceptionCatcher.h"

@implementation _ExceptionCatcher: NSObject

+ (BOOL)catchException:(__attribute__((noescape)) void(^)(void))tryBlock error:(__autoreleasing NSError **)error {
@try {
tryBlock();
return YES;
} @catch (NSException *exception) {
*error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:@{
NSUnderlyingErrorKey: exception,
NSLocalizedDescriptionKey: exception.reason,
@"CallStackSymbols": exception.callStackSymbols
}];

return NO;
}
}

@end
11 changes: 11 additions & 0 deletions Pulse/Sources/PulseInternal/include/ExceptionCatcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface _ExceptionCatcher: NSObject

+ (BOOL)catchException:(__attribute__((noescape)) void(^)(void))tryBlock error:(__autoreleasing NSError **)error;

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ final class NetworkInspectorSummaryViewModel: ObservableObject {
title: "Summary",
color: tintColor,
items: [
("URL", summary.request?.url?.absoluteString ?? "–"),
("URL", "https://www.google.ru/search?q=very+long+google+url&newwindow=1&hl=en&source=hp&ei=r_RqYfnxB7X59AP8z6Qg&iflsig=ALs-wAMAAAAAYWsCv804bF6Tig_Q0HLznCCxGCmznP-v&ved=0ahUKEwi5hdWgpM_zAhW1PH0KHfwnCQQQ4dUDCAw&uact=5&oq=very+long+google+url&gs_lcp=Cgdnd3Mtd2l6EAMyBQghEKABMgUIIRCgAToRCC4QgAQQsQMQxwEQowIQkwI6DgguEIAEELEDEMcBENEDOgUIABCABDoLCC4QgAQQxwEQowI6CAgAELEDEIMBOgsILhCABBDHARCvAToOCC4QgAQQsQMQxwEQowI6CAguELEDEIMBOggIABCABBCxAzoRCC4QgAQQsQMQgwEQxwEQrwE6CAguEIAEELEDOgsIABCABBCxAxDJAzoFCAAQkgM6BwguELEDEAo6DQguELEDEMcBENEDEAo6CgguEMcBEK8BEAo6BAguEAo6BAgAEAo6DQguELEDEMcBEKMCEAo6EAguEIAEEMcBEKMCEAoQkwI6BwgAEIAEEAo6CwguELEDEMcBEKMCOgsILhCABBCxAxCDAToLCC4QgAQQxwEQ0QM6CAgAEIAEEMkDOhEILhCABBCxAxCDARDHARDRAzoLCAAQgAQQsQMQgwE6CwguEIAEELEDEJMCOg4ILhCABBCxAxDHARCvAToGCAAQFhAeOggIABAWEAoQHjoFCAAQhgM6BQghEKsCOgcIIRAKEKABUMkGWNAgYKAhaABwAHgAgAGLAYgB9g-SAQQyMC41mAEAoAEB&sclient=gws-wiz"),
("Method", summary.request?.httpMethod ?? "–"),
("Status Code", summary.response?.statusCode.map(StatusCodeFormatter.string) ?? "–")
])
Expand Down
8 changes: 5 additions & 3 deletions Pulse/Sources/PulseUI/Views/KeyValueSectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ private struct KeyValueListView: View {
var body: some View {
if model.items.isEmpty {
HStack {
Text("Empty")
.foregroundColor(actualTintColor)
.font(.system(size: fontSize, weight: .medium))
Text("Empty")
.foregroundColor(actualTintColor)
.font(.system(size: fontSize, weight: .medium))
}
} else {
Label(text: text)
Expand Down Expand Up @@ -185,6 +185,8 @@ private struct Label: NSViewRepresentable {
label.isSelectable = true
label.attributedStringValue = text
label.allowsEditingTextAttributes = true
label.lineBreakMode = .byCharWrapping
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return label
}

Expand Down
2 changes: 1 addition & 1 deletion Pulse/Sources/PulseUI/Views/SearchBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ struct SearchBar: NSViewRepresentable {

func makeNSView(context: Context) -> NSSearchField {
let searchField = FocusAwareSearchField()
searchField.placeholderString = title
searchField.delegate = context.coordinator
searchField.translatesAutoresizingMaskIntoConstraints = false
searchField.onFocusChange = onEditingChanged
Expand All @@ -138,6 +137,7 @@ struct SearchBar: NSViewRepresentable {
}

func updateNSView(_ nsView: NSSearchField, context: Context) {
nsView.placeholderString = title
nsView.stringValue = text
}
}
Expand Down
Loading