Skip to content

Commit

Permalink
Merge pull request #9 from MasterJ93/logging
Browse files Browse the repository at this point in the history
The important parts of the logging are there. This pull request will close for the moment. When everything is merged to `main`, then pull request will re-open (or a new one will be made).
  • Loading branch information
MasterJ93 authored Apr 13, 2024
2 parents a4a1f8b + 3777ac0 commit 865f781
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 26 deletions.
16 changes: 8 additions & 8 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"pins" : [
{
"identity" : "swift-docc-plugin",
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-docc-plugin",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "26ac5758409154cc448d7ab82389c520fa8a8247",
"version" : "1.3.0"
"revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5",
"version" : "1.5.4"
}
},
{
"identity" : "swift-docc-symbolkit",
"identity" : "swift-log-oslog",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-docc-symbolkit",
"location" : "https://github.com/MasterJ93/swift-log-oslog.git",
"state" : {
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
"version" : "1.0.0"
"branch" : "master",
"revision" : "b3d6886e0e236036f83b5b8966827b707e174e81"
}
},
{
Expand Down
10 changes: 7 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,20 @@ let package = Package(
targets: ["ATProtoKit"]),
],
dependencies: [
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.7.0"),
.package(url: "https://github.com/MasterJ93/swift-log-oslog.git", branch: "master"),
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.3.0"),
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.7.0")
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "ATProtoKit",
dependencies: ["SwiftSoup"]
),
dependencies: [
"SwiftSoup",
"Logging"
])
// .testTarget(
// name: "ATProtoKitTests",
// dependencies: ["ATProtoKit"]),
Expand Down
97 changes: 95 additions & 2 deletions Sources/ATProtoKit/ATProtoKit.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,49 @@
import Foundation
import Logging

/// Defines a protocol for configurations in the `ATProtoKit` API library.
///
/// `ATProtoKitConfiguration` defines the basic requirements for any configuration class or structure
/// within `ATProtoKit`. Any class that conforms to this protocol must be geared for sending API calls to the AT Protocol. Creating a class
/// that conforms to this is useful if you have additional lexicons specific to the service you're running.
///
/// For logging-related tasks, make sure you set up the logging instide the `init()` method and attach it to the `logger` property.
/// ```swift
/// public init(session: UserSession? = nil, logIdentifier: String? = nil, logCategory: String?, logLevel: Logger.Level? = .info) {
/// self.session = session
/// self.logIdentifier = logIdentifier ?? Bundle.main.bundleIdentifier ?? "com.cjrriley.ATProtoKit"
/// self.logCategory = logCategory ?? "ATProtoKit"
/// self.logLevel = logLevel
///
/// #if canImport(os)
/// LoggingSystem.bootstrap { label in
/// ATLogHandler(subsystem: label, category: logCategory ?? "ATProtoKit")
/// }
/// #else
/// LoggingSystem.bootstrap(StreamLogHandler.standardOutput)
/// #endif
///
/// logger = Logger(label: logIdentifier ?? "com.cjrriley.ATProtoKit")
/// logger.logLevel = logLevel ?? .info
/// }
/// ```
public protocol ATProtoKitConfiguration {
/// Represents an authenticated user session within the AT Protocol. Optional.
var session: UserSession? { get }
/// Specifies the logger that will be used for emitting log messages.
///
/// - Note: Be sure to create an instance inside the `init()` method. This is important
var logger: Logger { get }
/// Specifies the identifier for managing log outputs. Optional.
///
/// This should default to the bundle identifier if it's in an Apple platform (`CFBundleIdentifier`).
var logIdentifier: String? { get }
/// Specifies the category name the logs in the logger within ATProtoKit will be in. Optional.
var logCategory: String? { get }
/// Specifies the highest level of logs that will be outputted. Optional.
///
/// This should default to `.info`
var logLevel: Logger.Level? { get }
/// Prepares an authorization value for API requests based on `session` and `pdsURL`.
///
/// This determines whether the "Authorization" header will be included in the request payload. It takes both `shouldAuthenticate` and `pdsURL` into account if
Expand Down Expand Up @@ -88,12 +124,42 @@ extension ATProtoKitConfiguration {
public class ATProtoKit: ATProtoKitConfiguration {
/// Represents an authenticated user session within the AT Protocol. Optional.
public let session: UserSession?
/// Specifies the logger that will be used for emitting log messages.
public private(set) var logger: Logger
/// Specifies the identifier for managing log outputs. Optional. Defaults to the project's `CFBundleIdentifier`.
public let logIdentifier: String?
/// Specifies the category name the logs in the logger within ATProtoKit will be in. Optional. Defaults to `ATProtoKit`.
///
/// - Note: This property is ignored if you're using `StreamLogHandler`.
public let logCategory: String?
/// Specifies the highest level of logs that will be outputted. Optional. Defaults to `.info`.
public let logLevel: Logger.Level?

/// Initializes a new instance of `ATProtoKit`.
///
/// This will also handle some of the logging-related setup. The identifier will either be your project's `CFBundleIdentifier` or an identifier named
/// `com.cjrriley.ATProtoKit`. However, you can manually override this.
/// - Parameters:
/// - session: The authenticated user session within the AT Protocol. Optional.
public init(session: UserSession? = nil) {
/// - logIdentifier: Specifies the identifier for managing log outputs. Optional. Defaults to the project's `CFBundleIdentifier`.
/// - logCategory: Specifies the category name the logs in the logger within ATProtoKit will be in. Optional. Defaults to `ATProtoKit`.
/// - logLevel: Specifies the highest level of logs that will be outputted. Optional. Defaults to `.info`.
public init(session: UserSession? = nil, logIdentifier: String? = nil, logCategory: String? = nil, logLevel: Logger.Level? = .info) {
self.session = session
self.logIdentifier = logIdentifier ?? Bundle.main.bundleIdentifier ?? "com.cjrriley.ATProtoKit"
self.logCategory = logCategory ?? "ATProtoKit"
self.logLevel = logLevel

#if canImport(os)
LoggingSystem.bootstrap { label in
ATLogHandler(subsystem: label, category: logCategory ?? "ATProtoKit")
}
#else
LoggingSystem.bootstrap(StreamLogHandler.standardOutput)
#endif

logger = Logger(label: logIdentifier ?? "com.cjrriley.ATProtoKit")
logger.logLevel = logLevel ?? .info
}

/// Determines the appropriate Personal Data Server (PDS) URL.
Expand Down Expand Up @@ -137,12 +203,39 @@ public class ATProtoKit: ATProtoKitConfiguration {
public class ATProtoAdmin: ATProtoKitConfiguration {
/// Represents an authenticated user session within the AT Protocol. Optional.
public let session: UserSession?
/// Specifies the logger that will be used for emitting log messages.
public private(set) var logger: Logger
/// Specifies the identifier for managing log outputs. Optional. Defaults to the project's `CFBundleIdentifier`.
public let logIdentifier: String?
/// Specifies the category name the logs in the logger within ATProtoKit will be in. Optional. Defaults to `ATProtoKit`.
///
/// - Note: This property is ignored if you're using `StreamLogHandler`.
public let logCategory: String?
/// Specifies the highest level of logs that will be outputted. Optional. Defaults to `.info`.
public let logLevel: Logger.Level?

/// Initializes a new instance of `ATProtoAdmin`.
/// - Parameters:
/// - session: The authenticated user session within the AT Protocol.
public init(session: UserSession? = nil) {
/// - logIdentifier: Specifies the identifier for managing log outputs. Optional. Defaults to the project's `CFBundleIdentifier`.
/// - logCategory: Specifies the category name the logs in the logger within ATProtoKit will be in. Optional. Defaults to `ATProtoKit`.
/// - logLevel: Specifies the highest level of logs that will be outputted. Optional. Defaults to `.info`.
public init(session: UserSession? = nil, logIdentifier: String? = nil, logCategory: String? = nil, logLevel: Logger.Level? = .info) {
self.session = session
self.logIdentifier = logIdentifier ?? Bundle.main.bundleIdentifier ?? "com.cjrriley.ATProtoKit"
self.logCategory = logCategory ?? "ATProtoKit"
self.logLevel = logLevel

#if canImport(os)
LoggingSystem.bootstrap { label in
ATLogHandler(subsystem: label, category: logCategory ?? "ATProtoKit")
}
#else
LoggingSystem.bootstrap(StreamLogHandler.standardOutput)
#endif

logger = Logger(label: logIdentifier ?? "com.cjrriley.ATProtoKit")
logger.logLevel = logLevel ?? .info
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public struct SelfLabel: Codable {
}
}

/// A data model definition for
/// A data model definition for labeler-created labels.
///
/// - Note: According to the AT Protocol specifications: "Declares a label value and its expected interpertations and behaviors."
///
Expand Down
4 changes: 2 additions & 2 deletions Sources/ATProtoKit/Networking/CoreAPI/CreateInviteCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import Foundation
extension ATProtoKit {
/// Creates an invite code.
///
/// - Note: If you need to create multiple invite codes at once, please use ``create`` instead.
///
/// - Note: If you need to create multiple invite codes at once, please use ``createInviteCodes(_:for:)`` instead.
///
/// - Note: According to the AT Protocol specifications: "Create an invite code."
///
/// - SeeAlso: This is based on the [`com.atproto.server.createInviteCode`][github] lexicon.
Expand Down
7 changes: 4 additions & 3 deletions Sources/ATProtoKit/Networking/PlatformAPI/CreatePost.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ extension ATProtoKit {
/// - swapCommit: Swaps out an operation based on the CID. Optional.
/// - Returns: A strong reference, which contains the newly-created record's URI and CID hash.
public func createPostRecord(text: String, locales: [Locale] = [], replyTo: String? = nil, embed: EmbedIdentifier? = nil,
labels: FeedLabelUnion? = nil, tags: [String]? = nil, creationDate: Date = Date.now, recordKey: String? = nil, shouldValidate: Bool? = true, swapCommit: String? = nil) async -> Result<StrongReference, Error> {
labels: FeedLabelUnion? = nil, tags: [String]? = nil, creationDate: Date = Date.now, recordKey: String? = nil,
shouldValidate: Bool? = true, swapCommit: String? = nil) async -> Result<StrongReference, Error> {

guard let session else {
return .failure(ATRequestPrepareError.missingActiveSession)
Expand Down Expand Up @@ -158,8 +159,8 @@ extension ATProtoKit {
///
/// `EmbedIdentifier` provides a unified interface for specifying embeddable content, simplifying the process of attaching
/// images, external links, other post records, or media to a post. By abstracting the details of each embed type, it allows methods
/// like ``createPostRecord(text:locales:replyTo:embed:labels:tags:creationDate:)`` to handle the
/// necessary operations (e.g., uploading, grabbing metadata, validation, etc.) behind the scenes, streamlining the embedding process.
/// like ``createPostRecord(text:locales:replyTo:embed:labels:tags:creationDate:recordKey:shouldValidate:swapCommit:)``
/// to handle the necessary operations (e.g., uploading, grabbing metadata, validation, etc.) behind the scenes, streamlining the embedding process.
public enum EmbedIdentifier {
/// Represents a set of images to be embedded in the post.
/// - Parameter images: An array of `ImageQuery` objects, each containing the image data, metadata, and filenames of the image.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extension ATProtoKit {
///
/// [github]: https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/describeFeedGenerator.json
///
/// - Returns: A `Result`, containing either a ``FeedDescribeFeedGenerator`` if successful, or an `Error` if not.
/// - Returns: A `Result`, containing either a ``FeedDescribeFeedGeneratorOutput`` if successful, or an `Error` if not.
public func describeFeedGenerator(pdsURL: String? = nil) async throws -> Result<FeedDescribeFeedGeneratorOutput, Error> {
guard let sessionURL = pdsURL != nil ? pdsURL : session?.pdsURL,
let requestURL = URL(string: "\(sessionURL)/app.bsky.feed.describeFeedGenerator") else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extension ATProtoKit {
/// - actorDID: The decentralized identifier (DID) of the user who created the feeds.
/// - limit: The number of items that can be in the list. Optional. Defaults to `50`.
/// - cursor: The mark used to indicate the starting point for the next set of result. Optional.
/// - Returns: A `Result`, containing either a ``FeedGetActorFeeds`` if successful, or an `Error` if not.
/// - Returns: A `Result`, containing either a ``FeedGetActorFeedsOutput`` if successful, or an `Error` if not.
public func getActorFeeds(by actorDID: String, limit: Int? = 50, cursor: String? = nil) async throws -> Result<FeedGetActorFeedsOutput, Error> {
guard session != nil,
let accessToken = session?.accessToken else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extension ATProtoKit {
/// (and therefore, the documentation is outdated) or unintentional (in this case, the underlying implementation is outdated). For now, this method will act as if auth is required until Bluesky clarifies their position.
///
/// - Important: This will only be able to get like records for the authenticated account. This won't work for any other user account. If you need to grab the like records for user accounts other than the
/// authenticated one, use ``listRecords`` instead.
/// authenticated one, use ``listRecords(from:collection:limit:cursor:isArrayReverse:pdsURL:)`` instead.
///
/// - Note: According to the AT Protocol specifications: "Get a list of posts liked by an actor. Does not require auth."
///
Expand Down
4 changes: 2 additions & 2 deletions Sources/ATProtoKit/Networking/PlatformAPI/GetProfile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ extension ATProtoKit {
/// - Note: If your Personal Data Server's (PDS) URL is something other than `https://bsky.social` and you're not using authentication, be sure to change it if the normal URL isn't used
/// for unauthenticated API calls.\
///\
/// If you need profiles of several users, it's best to use ``getProfiles(_:accessToken:pdsURL:)``.
///
/// If you need profiles of several users, it's best to use ``getProfiles(_:pdsURL:shouldAuthenticate:)``.
///
/// - Note: According to the AT Protocol specifications: "Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth."
///
/// - SeeAlso: This is based on the [`app.bsky.actor.getProfile`][github] lexicon.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extension ATProtoKit {
/// - Note: If your Personal Data Server's (PDS) URL is something other than `https://bsky.social` and you're not using authentication, be sure to change it if the normal URL isn't used
/// for unauthenticated API calls.\
/// \
/// If you need a profile of just one user, it's best to use ``getProfile(_:accessToken:pdsURL:)``
/// If you need a profile of just one user, it's best to use ``getProfile(_:pdsURL:shouldAuthenticate:)``.
///
/// - Note: According to the AT Protocol specifications: "Get detailed profile views of multiple actors."
///
Expand Down
2 changes: 1 addition & 1 deletion Sources/ATProtoKit/Utilities/ATImageProcessing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protocol ImageProtocol {
/// - Important: `stripMetadata(from:)` is an important method to create as, according to the AT Protocol documentation, the protocol may be more strict about stripping metadata in the future.\
/// \
/// Also, this should be an `internal` method, as it will be part of `convertToImageQuery(image:altText:targetFileSize)`. It's recommended that it's called before
/// ``convertToImageQuery(image:altText:targetFileSize)`` attempts to access the image.
/// ``convertToImageQuery(imagePath:altText:targetFileSize:)-2fma7`` attempts to access the image.
///
/// ### Example
/// Below is a sample implementation showcasing how to conform to `ATImageProcessable` for a custom image type:
Expand Down
46 changes: 46 additions & 0 deletions Sources/ATProtoKit/Utilities/ExtensionHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
//

import Foundation
//#if canImport(os)
//import Logging
//import os.log
//#endif

// MARK: - String Extension
extension String: Truncatable {
Expand Down Expand Up @@ -113,3 +117,45 @@ extension UInt64 {
return encoded
}
}

//// MARK: - Logging.Logger Extension
//#if canImport(os)
//extension Logging.Logger {
// enum PrivacyAwareMetadataValue {
// case string(String, OSLogPrivacy)
// case stringConvertible(CustomStringConvertible, OSLogPrivacy)
//
// var privacy: OSLogPrivacy {
// switch self {
// case .string(_, let privacy), .stringConvertible(_, let privacy):
// return privacy
// }
// }
//
// var value: String {
// switch self {
// case .string(let value, _):
// return value
// case .stringConvertible(let value, _):
// return value.description
// }
// }
// }
//}
//#endif
//
//// MARK: - DefaultStringInterpolation Extension
//extension DefaultStringInterpolation {
// mutating func appendInterpolation(_ message: OSLogMessage) {
// #if canImport(os)
// appendInterpolation(value)
// appendLiteral("")
// #else
// appendLiteral(value)
// #endif
// }
//
// private mutating func wrapMessage(_ value: OSLogMessage) {
// appendInterpolation(<#T##OSLogMessage#>)
// }
//}
Loading

0 comments on commit 865f781

Please sign in to comment.