Skip to content

Commit abc21c7

Browse files
committed
Add SentrySwiftyBeaver Integration
1 parent 6106fab commit abc21c7

File tree

5 files changed

+615
-0
lines changed

5 files changed

+615
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# --- macOS ---
2+
3+
# General
4+
.DS_Store
5+
__MACOSX/
6+
.AppleDouble
7+
.LSOverride
8+
Icon[]
9+
10+
# Thumbnails
11+
._*
12+
13+
# Files that might appear in the root of a volume
14+
.DocumentRevisions-V100
15+
.fseventsd
16+
.Spotlight-V100
17+
.TemporaryItems
18+
.Trashes
19+
.VolumeIcon.icns
20+
.com.apple.timemachine.donotpresent
21+
22+
# Directories potentially created on remote AFP share
23+
.AppleDB
24+
.AppleDesktop
25+
Network Trash Folder
26+
Temporary Items
27+
.apdisk
28+
29+
# --- Swift ---
30+
31+
# Xcode
32+
#
33+
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
34+
35+
## User settings
36+
xcuserdata/
37+
38+
## Obj-C/Swift specific
39+
*.hmap
40+
41+
## App packaging
42+
*.ipa
43+
*.dSYM.zip
44+
*.dSYM
45+
46+
## Playgrounds
47+
timeline.xctimeline
48+
playground.xcworkspace
49+
50+
# Swift Package Manager
51+
#
52+
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
53+
# Packages/
54+
# Package.pins
55+
# Package.resolved
56+
# *.xcodeproj
57+
#
58+
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
59+
# hence it is not needed unless you have added a package configuration file to your project
60+
# .swiftpm
61+
62+
.build/
63+
64+
# CocoaPods
65+
#
66+
# We recommend against adding the Pods directory to your .gitignore. However
67+
# you should judge for yourself, the pros and cons are mentioned at:
68+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
69+
#
70+
# Pods/
71+
#
72+
# Add this line if you want to avoid checking in source code from the Xcode workspace
73+
# *.xcworkspace
74+
75+
# Carthage
76+
#
77+
# Add this line if you want to avoid checking in source code from Carthage dependencies.
78+
# Carthage/Checkouts
79+
80+
Carthage/Build/
81+
82+
# fastlane
83+
#
84+
# It is recommended to not store the screenshots in the git repo.
85+
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
86+
# For more information about the recommended setup visit:
87+
# https://docs.fastlane.tools/best-practices/source-control/#source-control
88+
89+
fastlane/report.xml
90+
fastlane/Preview.html
91+
fastlane/screenshots/**/*.png
92+
fastlane/test_output
93+
94+
# --- Xcode ---
95+
96+
## User settings
97+
xcuserdata/
98+
99+
# Archive
100+
*.xcarchive
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// swift-tools-version:6.0
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "SentrySwiftyBeaver",
6+
platforms: [.iOS(.v15), .macOS(.v10_14), .tvOS(.v15), .watchOS(.v8), .visionOS(.v1)],
7+
products: [
8+
.library(
9+
name: "SentrySwiftyBeaver",
10+
targets: ["SentrySwiftyBeaver"]
11+
)
12+
],
13+
dependencies: [
14+
.package(url: "https://github.com/getsentry/sentry-cocoa", from: "9.0.0"),
15+
.package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver.git", from: "2.0.0")
16+
],
17+
targets: [
18+
.target(
19+
name: "SentrySwiftyBeaver",
20+
dependencies: [
21+
.product(name: "Sentry", package: "sentry-cocoa"),
22+
.product(name: "SwiftyBeaver", package: "SwiftyBeaver")
23+
]
24+
),
25+
.testTarget(
26+
name: "SentrySwiftyBeaverTests",
27+
dependencies: [
28+
"SentrySwiftyBeaver",
29+
.product(name: "Sentry", package: "sentry-cocoa"),
30+
.product(name: "SwiftyBeaver", package: "SwiftyBeaver")
31+
]
32+
)
33+
]
34+
)
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Sentry SwiftyBeaver Integration
2+
3+
A [SwiftyBeaver](https://github.com/SwiftyBeaver/SwiftyBeaver) destination that forwards log entries to Sentry's structured logging system, automatically capturing application logs with full context including metadata, source location, and log levels.
4+
5+
> [!NOTE]
6+
> This repo is a mirror of [github.com/getsentry/sentry-cocoa](https://github.com/getsentry/sentry-cocoa). The source code lives in `3rd-party-integrations/SentrySwiftyBeaver/`. This allows users to import only what they need via SPM while keeping all integration code in the main repository.
7+
8+
## Installation
9+
10+
### Swift Package Manager
11+
12+
Add the following dependencies to your `Package.swift` or Xcode package dependencies:
13+
14+
```swift
15+
dependencies: [
16+
.package(url: "https://github.com/getsentry/sentry-cocoa-swiftybeaver", from: "1.0.0")
17+
]
18+
```
19+
20+
## Quick Start
21+
22+
```swift
23+
import Sentry
24+
import SwiftyBeaver
25+
26+
SentrySDK.start { options in
27+
options.dsn = "YOUR_DSN"
28+
options.logsEnabled = true
29+
}
30+
31+
let log = SwiftyBeaver.self
32+
let sentryDestination = SentryDestination()
33+
log.addDestination(sentryDestination)
34+
35+
log.info("User logged in", context: ["userId": "12345", "sessionId": "abc"])
36+
```
37+
38+
## Configuration
39+
40+
### Log Level Threshold
41+
42+
Set the minimum log level for messages to be sent to Sentry using SwiftyBeaver's built-in `minLevel` property. Messages below the configured threshold will be filtered out and not sent to Sentry.
43+
44+
```swift
45+
let sentryDestination = SentryDestination()
46+
sentryDestination.minLevel = .info
47+
log.addDestination(sentryDestination)
48+
```
49+
50+
## Log Level Mapping
51+
52+
SwiftyBeaver levels are automatically mapped to Sentry log levels:
53+
54+
| SwiftyBeaver Level | Sentry Log Level |
55+
| ------------------ | ---------------- |
56+
| `.verbose` | `.trace` |
57+
| `.debug` | `.debug` |
58+
| `.info` | `.info` |
59+
| `.warning` | `.warn` |
60+
| `.error` | `.error` |
61+
| `.critical` | `.fatal` |
62+
| `.fault` | `.fatal` |
63+
64+
## Context Handling
65+
66+
The destination supports SwiftyBeaver's `context` parameter for additional metadata:
67+
68+
### Dictionary Context
69+
70+
When `context` is provided as a `[String: Any]` dictionary, each key-value pair is added as an individual Sentry log attribute with the prefix `swiftybeaver.context.{key}`.
71+
72+
```swift
73+
log.info("User action", context: [
74+
"userId": "12345",
75+
"action": "purchase",
76+
"isActive": true,
77+
"errorCode": 500,
78+
"amount": 99.99
79+
])
80+
```
81+
82+
Supported types in dictionary context:
83+
84+
- Strings
85+
- Numbers (Int, Double, Float)
86+
- Booleans
87+
- Arrays (converted to string representation)
88+
89+
### Non-Dictionary Context
90+
91+
For non-dictionary contexts, the entire context is converted to a string attribute `swiftybeaver.context`.
92+
93+
```swift
94+
log.info("Test message", context: "simple string context")
95+
```
96+
97+
## Automatic Attributes
98+
99+
The destination automatically includes the following attributes with every log entry:
100+
101+
- `sentry.origin`: `"auto.logging.swiftybeaver"`
102+
- `swiftybeaver.level`: The original SwiftyBeaver level (as raw value)
103+
- `swiftybeaver.thread`: The thread identifier
104+
- `swiftybeaver.file`: The source file name
105+
- `swiftybeaver.function`: The function name
106+
- `swiftybeaver.line`: The line number
107+
108+
## Documentation
109+
110+
- [Sentry Cocoa SDK Documentation](https://docs.sentry.io/platforms/apple/)
111+
- [Sentry Logs Documentation](https://docs.sentry.io/platforms/apple/logs/)
112+
- [SwiftyBeaver Repo](https://github.com/SwiftyBeaver/SwiftyBeaver)
113+
114+
## License
115+
116+
This integration follows the same license as the Sentry Cocoa SDK. See the [LICENSE](https://github.com/getsentry/sentry-cocoa/blob/main/LICENSE.md) file for details.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import Sentry
2+
import SwiftyBeaver
3+
4+
/// A SwiftyBeaver destination that forwards log entries to Sentry's structured logging system.
5+
///
6+
/// `SentryDestination` extends SwiftyBeaver's `BaseDestination`, allowing you to integrate
7+
/// Sentry's structured logging capabilities with SwiftyBeaver. This enables you to capture
8+
/// application logs from SwiftyBeaver and send them to Sentry for analysis and monitoring.
9+
///
10+
/// ## Level Mapping
11+
/// SwiftyBeaver levels are mapped to Sentry log levels:
12+
/// - `.verbose` → `.trace`
13+
/// - `.debug` → `.debug`
14+
/// - `.info` → `.info`
15+
/// - `.warning` → `.warn`
16+
/// - `.error` → `.error`
17+
/// - `.critical` → `.fatal`
18+
/// - `.fault` → `.fatal`
19+
///
20+
/// ## Context Handling
21+
/// When `context` is provided as a `[String: Any]` dictionary, each key-value pair is added
22+
/// as an individual Sentry log attribute with the prefix `swiftybeaver.context.{key}`. For non-dictionary
23+
/// contexts, the entire context is converted to a string attribute `swiftybeaver.context`.
24+
///
25+
/// ## Usage
26+
/// ```swift
27+
/// import Sentry
28+
/// import SwiftyBeaver
29+
///
30+
/// SentrySDK.start { options in
31+
/// options.dsn = "YOUR_DSN"
32+
/// options.logsEnabled = true
33+
/// }
34+
///
35+
/// let log = SwiftyBeaver.self
36+
/// let sentryDestination = SentryDestination()
37+
/// log.addDestination(sentryDestination)
38+
///
39+
/// log.info("User logged in", context: ["userId": "12345", "sessionId": "abc"])
40+
/// ```
41+
public class SentryDestination: BaseDestination {
42+
43+
/// Creates a new SentryDestination.
44+
///
45+
/// Log level filtering should be configured on the destination itself using SwiftyBeaver's
46+
/// built-in `minLevel` property after initialization.
47+
public override init() {
48+
super.init()
49+
}
50+
51+
/// Sends a log message to Sentry's structured logging system.
52+
///
53+
/// - Parameters:
54+
/// - level: The SwiftyBeaver log level
55+
/// - msg: The log message
56+
/// - thread: The thread identifier
57+
/// - file: The source file name
58+
/// - function: The function name
59+
/// - line: The line number
60+
/// - context: Additional context information
61+
/// - Returns: Always returns `nil` as per SwiftyBeaver convention
62+
public override func send(
63+
_ level: SwiftyBeaver.Level,
64+
msg: String,
65+
thread: String,
66+
file: String,
67+
function: String,
68+
line: Int,
69+
context: Any? = nil
70+
) -> String? {
71+
// Additional guard for level filtering.
72+
guard level.rawValue >= minLevel.rawValue else {
73+
return nil
74+
}
75+
var attributes: [String: Any] = [:]
76+
attributes["sentry.origin"] = "auto.logging.swiftybeaver"
77+
attributes["swiftybeaver.level"] = "\(level.rawValue)"
78+
attributes["swiftybeaver.thread"] = thread
79+
attributes["swiftybeaver.file"] = file
80+
attributes["swiftybeaver.function"] = function
81+
attributes["swiftybeaver.line"] = "\(line)"
82+
83+
if let context = context {
84+
addContextToAttributes(&attributes, context: context)
85+
}
86+
87+
logToSentry(level: level, message: msg, attributes: attributes)
88+
return nil
89+
}
90+
91+
private func addContextToAttributes(_ attributes: inout [String: Any], context: Any) {
92+
if let contextDict = context as? [String: Any] {
93+
for (key, value) in contextDict {
94+
attributes["swiftybeaver.context.\(key)"] = value
95+
}
96+
} else {
97+
attributes["swiftybeaver.context"] = "\(context)"
98+
}
99+
}
100+
101+
private func logToSentry(level: SwiftyBeaver.Level, message: String, attributes: [String: Any]) {
102+
switch level {
103+
case .verbose:
104+
SentrySDK.logger.trace(message, attributes: attributes)
105+
case .debug:
106+
SentrySDK.logger.debug(message, attributes: attributes)
107+
case .info:
108+
SentrySDK.logger.info(message, attributes: attributes)
109+
case .warning:
110+
SentrySDK.logger.warn(message, attributes: attributes)
111+
case .error:
112+
SentrySDK.logger.error(message, attributes: attributes)
113+
case .critical:
114+
SentrySDK.logger.fatal(message, attributes: attributes)
115+
case .fault:
116+
SentrySDK.logger.fatal(message, attributes: attributes)
117+
@unknown default:
118+
SentrySDK.logger.error(message, attributes: attributes)
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)