Skip to content

Conversation

denrase
Copy link
Collaborator

@denrase denrase commented Jul 16, 2025

#skip-changelog

📜 Description

Structured Logs: String templating for structured logs

💡 Motivation and Context

Closes #5642

💚 How did you test it?

Unit tests

📝 Checklist

You have to check all boxes before merging:

  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

@denrase denrase marked this pull request as ready for review July 16, 2025 11:49
Copy link

codecov bot commented Jul 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.678%. Comparing base (13bc1aa) to head (80f64cf).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files

Impacted file tree graph

@@              Coverage Diff              @@
##              main     #5643       +/-   ##
=============================================
+ Coverage   86.604%   86.678%   +0.073%     
=============================================
  Files          423       424        +1     
  Lines        36221     36324      +103     
  Branches     17046     17089       +43     
=============================================
+ Hits         31369     31485      +116     
+ Misses        4807      4793       -14     
- Partials        45        46        +1     
Files with missing lines Coverage Δ
Sources/Swift/Protocol/SentryLogMessage.swift 100.000% <100.000%> (ø)
Sources/Swift/Tools/SentryLogger.swift 99.378% <100.000%> (+0.219%) ⬆️

... and 10 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 13bc1aa...80f64cf. Read the comment docs.

Copy link
Member

@philipphofmann philipphofmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have one important comment to address before I give this a full review.

@denrase denrase requested a review from philipphofmann July 18, 2025 12:25
@denrase
Copy link
Collaborator Author

denrase commented Jul 18, 2025

import Foundation

public struct SentryInterpolatedString: ExpressibleByStringInterpolation {
    public let message: String
    public let attributes: [String: SentryLog.Attribute]
    
    public init(stringLiteral value: String) {
        self.message = value
        self.attributes = [:]
    }
    
    public init(stringInterpolation: StringInterpolation) {
        self.message = stringInterpolation.message
        self.attributes = stringInterpolation.attributes
    }
    
    public struct StringInterpolation: StringInterpolationProtocol {
        var message: String = ""
        var attributes: [String: SentryLog.Attribute] = [:]
        private var interpolationCounter = 0
        
        public init(literalCapacity: Int, interpolationCount: Int) {
            message.reserveCapacity(literalCapacity * 2)
            attributes.reserveCapacity(interpolationCount)
        }
        
        public mutating func appendLiteral(_ literal: String) {
            message.append(literal)
        }
        
        // MARK: - Supported SentryLog.Attribute types only
        
        /// Append String interpolation
        public mutating func appendInterpolation(_ value: String) {
            let key = "interpolation_\(interpolationCounter)"
            interpolationCounter += 1
            
            message.append(value)
            attributes[key] = .string(value)
        }
        
        // Other attibute types
        ...
        
        /// Append interpolation without tracking (for any other type or sensitive data)
        public mutating func appendInterpolation<T>(untracked value: T) {
            message.append(String(describing: value))
        }
    }
}

// MARK: - SentryLogger Extensions

extension SentryLogger {
    /// Logs a trace-level message using interpolated string that automatically captures attributes
    public func trace(_ interpolatedString: SentryInterpolatedString) {
        captureLog(level: .trace, body: interpolatedString.message, attributes: interpolatedString.attributes.mapValues { $0.value })
    }

    ...
}

// Automatic attribute tracking for supported types
let userName = "Henry"
logger.info("User \(userName) just logged in...")

Which would generate:

Message: "User 12345 completed request in 1.5s with success: true"
Attributes: "sentry.message.parameter.0": .string("12345")

For the template we'd have to do something along the lines of:

Tempalte: "sentry.message.template": "User ("12345") completed request in 1.5s with success: true"

This of course would be Swift only.

Copy link
Member

@philipphofmann philipphofmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, we're getting close. Thanks for doing this. TIL about custom string interpolation in Swift 😄

Copy link
Member

@philprime philprime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of comments to consider in addition to the feedback of @philipphofmann

Copy link
Contributor

github-actions bot commented Jul 28, 2025

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1214.44 ms 1245.84 ms 31.40 ms
Size 23.75 KiB 912.61 KiB 888.86 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
d3f650a 1225.45 ms 1241.86 ms 16.41 ms
e64d3d4 1241.90 ms 1260.10 ms 18.20 ms
0759f32 1237.10 ms 1257.35 ms 20.25 ms
aa96485 1215.37 ms 1234.04 ms 18.67 ms
2bddb03 1214.11 ms 1246.78 ms 32.67 ms
fc0757d 1231.83 ms 1248.98 ms 17.15 ms
99104c9 1224.84 ms 1247.08 ms 22.24 ms
f8029e2 1245.16 ms 1261.32 ms 16.16 ms
8ad303c 1220.02 ms 1231.79 ms 11.77 ms
079bcc8 1217.88 ms 1234.88 ms 17.00 ms

App size

Revision Plain With Sentry Diff
d3f650a 23.75 KiB 902.48 KiB 878.73 KiB
e64d3d4 23.75 KiB 855.37 KiB 831.62 KiB
0759f32 23.75 KiB 880.20 KiB 856.46 KiB
aa96485 23.75 KiB 874.46 KiB 850.71 KiB
2bddb03 23.75 KiB 891.01 KiB 867.26 KiB
fc0757d 23.75 KiB 850.73 KiB 826.98 KiB
99104c9 23.75 KiB 894.83 KiB 871.09 KiB
f8029e2 23.75 KiB 893.72 KiB 869.97 KiB
8ad303c 23.75 KiB 879.24 KiB 855.49 KiB
079bcc8 23.74 KiB 874.07 KiB 850.33 KiB

Previous results on branch: feat/structured-logs-string-templating

Startup times

Revision Plain With Sentry Diff
3b683b5 1224.45 ms 1249.24 ms 24.80 ms
16ca22a 1227.90 ms 1239.63 ms 11.73 ms
c5ed6dc 1222.35 ms 1242.58 ms 20.23 ms
867fd34 1214.88 ms 1230.11 ms 15.23 ms

App size

Revision Plain With Sentry Diff
3b683b5 23.75 KiB 911.79 KiB 888.04 KiB
16ca22a 23.75 KiB 912.04 KiB 888.29 KiB
c5ed6dc 23.75 KiB 911.79 KiB 888.04 KiB
867fd34 23.75 KiB 911.91 KiB 888.16 KiB

Copy link
Member

@philipphofmann philipphofmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for including all the feedback. LGTM for the first version.

@denrase denrase requested a review from philprime July 29, 2025 08:29
Copy link
Member

@philprime philprime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@denrase denrase merged commit b5ef4c0 into main Jul 30, 2025
172 of 177 checks passed
@denrase denrase deleted the feat/structured-logs-string-templating branch July 30, 2025 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Structured Logs: String templating for structured logs
4 participants