Skip to content

Commit

Permalink
Suspend beacon flushing (on low battery and/or cellular network) beco…
Browse files Browse the repository at this point in the history
…mes configurable to apps
  • Loading branch information
Hongyan Jiang authored and GitHub Enterprise committed Nov 9, 2023
1 parent 4e34124 commit 286d31a
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 9 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Fix unit test cases that failed in command line execution
- Fix unit test cases that failed in Xcode 15
- Change beacon id from 128 bit UUID to 64 bit hex string
- Suspend beacon flushing (on low battery and/or cellular network) becomes configurable

## 1.6.5
- Add crash to mobile feature list and send to Instana backend
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ To install the iOS agent, use Swift Package Manager (via Xcode) or CocoaPods.

1. Open Xcode.
2. Open your Xcode project.
2. Select menu File -> Add Package Dependencies...
3. In the window that pops up, enter repository https://github.com/instana/iOSAgent to top right edit field "Search or Enter Package URL".
2. Select menu **`File`** -> **`Add Package Dependencies...`**
3. In the window that pops up, enter the repository https://github.com/instana/iOSAgent in the **Search or Enter Package URL** field. Then, select **`iosagent`** from the search result and click **`Add Package`** button.

#### CocoaPods

Expand Down
4 changes: 2 additions & 2 deletions Sources/InstanaAgent/Beacons/Reporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public class Reporter {
internal var sendFirstBeacon = true // first beacon is sent all by itself, not in a batch
private var slowSendStartTime: Date?
private var inSlowModeBeforeFlush = false
private var lastFlushStartTime: Double?
private var flusher: BeaconFlusher?
internal var lastFlushStartTime: Double?
internal var flusher: BeaconFlusher?
internal var send: BeaconFlusher.Sender?
private let rateLimiter: ReporterRateLimiter
private let batterySafeForNetworking: () -> Bool
Expand Down
13 changes: 9 additions & 4 deletions Sources/InstanaAgent/Configuration/InstanaConfiguraton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,17 @@ class InstanaConfiguration {
var isValid: Bool { !key.isEmpty && !reportingURL.absoluteString.isEmpty }

required init(reportingURL: URL, key: String, httpCaptureConfig: HTTPCaptureConfig,
enableCrashReporting: Bool, slowSendInterval: Instana.Types.Seconds,
enableCrashReporting: Bool, suspendReporting: Set<SuspendReporting>? = nil,
slowSendInterval: Instana.Types.Seconds,
usiRefreshTimeIntervalInHrs: Double) {
self.reportingURL = reportingURL
self.key = key
self.httpCaptureConfig = httpCaptureConfig
suspendReporting = SuspendReporting.defaults
monitorTypes = MonitorTypes.current
if enableCrashReporting {
monitorTypes.insert(.crash)
}
self.suspendReporting = suspendReporting ?? SuspendReporting.defaults
self.slowSendInterval = slowSendInterval
self.usiRefreshTimeIntervalInHrs = usiRefreshTimeIntervalInHrs
reporterSendDebounce = Defaults.reporterSendDebounce
Expand All @@ -98,11 +99,15 @@ class InstanaConfiguration {
}

static func `default`(key: String, reportingURL: URL, httpCaptureConfig: HTTPCaptureConfig = .automatic,
enableCrashReporting: Bool, slowSendInterval: Instana.Types.Seconds = 0.0,
enableCrashReporting: Bool,
suspendReporting: Set<SuspendReporting>? = nil,
slowSendInterval: Instana.Types.Seconds = 0.0,
usiRefreshTimeIntervalInHrs: Double = defaultUsiRefreshTimeIntervalInHrs)
-> InstanaConfiguration {
self.init(reportingURL: reportingURL, key: key, httpCaptureConfig: httpCaptureConfig,
enableCrashReporting: enableCrashReporting, slowSendInterval: slowSendInterval,
enableCrashReporting: enableCrashReporting,
suspendReporting: suspendReporting,
slowSendInterval: slowSendInterval,
usiRefreshTimeIntervalInHrs: usiRefreshTimeIntervalInHrs)
}
}
12 changes: 12 additions & 0 deletions Sources/InstanaAgent/Instana.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,24 @@ import Foundation
var httpCaptureConfig = HTTPCaptureConfig.automatic
var collectionEnabled = true
var enableCrashReporting = false
var suspendReporting = InstanaConfiguration.SuspendReporting.defaults
var slowSendInterval = 0.0
var usiRefreshTimeIntervalInHrs = defaultUsiRefreshTimeIntervalInHrs
if let options = options {
httpCaptureConfig = options.httpCaptureConfig
collectionEnabled = options.collectionEnabled
enableCrashReporting = options.enableCrashReporting

let suspendReportingOnLowBattery = options.suspendReportingOnLowBattery
let suspendReportingOnCellular = options.suspendReportingOnCellular
suspendReporting = []
if suspendReportingOnLowBattery {
suspendReporting.insert(InstanaConfiguration.SuspendReporting.lowBattery)
}
if suspendReportingOnCellular {
suspendReporting.insert(InstanaConfiguration.SuspendReporting.cellularConnection)
}

let debounce = InstanaConfiguration.Defaults.reporterSendDebounce
if options.slowSendInterval != 0.0,
options.slowSendInterval < debounce || options.slowSendInterval > maxSlowSendInterval {
Expand All @@ -104,6 +115,7 @@ import Foundation
let config = InstanaConfiguration.default(key: key, reportingURL: reportingURL,
httpCaptureConfig: httpCaptureConfig,
enableCrashReporting: enableCrashReporting,
suspendReporting: suspendReporting,
slowSendInterval: slowSendInterval,
usiRefreshTimeIntervalInHrs: usiRefreshTimeIntervalInHrs)
let session = InstanaSession(configuration: config, propertyHandler: InstanaPropertyHandler(),
Expand Down
6 changes: 6 additions & 0 deletions Sources/InstanaAgent/InstanaSetupOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Foundation
public var httpCaptureConfig: HTTPCaptureConfig
public var collectionEnabled: Bool
public var enableCrashReporting: Bool
public var suspendReportingOnLowBattery: Bool
public var suspendReportingOnCellular: Bool
public var slowSendInterval: Instana.Types.Seconds
public var usiRefreshTimeIntervalInHrs: Double

Expand All @@ -22,11 +24,15 @@ import Foundation
@objc public
init(httpCaptureConfig: HTTPCaptureConfig = .automatic,
collectionEnabled: Bool = true, enableCrashReporting: Bool = false,
suspendReportingOnLowBattery: Bool = false,
suspendReportingOnCellular: Bool = false,
slowSendInterval: Instana.Types.Seconds = 0.0,
usiRefreshTimeIntervalInHrs: Double = defaultUsiRefreshTimeIntervalInHrs) {
self.httpCaptureConfig = httpCaptureConfig
self.collectionEnabled = collectionEnabled
self.enableCrashReporting = enableCrashReporting
self.suspendReportingOnLowBattery = suspendReportingOnLowBattery
self.suspendReportingOnCellular = suspendReportingOnCellular
self.slowSendInterval = slowSendInterval
self.usiRefreshTimeIntervalInHrs = usiRefreshTimeIntervalInHrs
}
Expand Down
81 changes: 81 additions & 0 deletions Tests/InstanaAgentTests/Beacons/ReporterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,26 @@ class ReporterTests: InstanaTestCase {
AssertTrue(didSubmit)
}

func test_collection_not_enabled() {
// Given
session = InstanaSession.mock(configuration: config)
session.collectionEnabled = false

var didSubmit = false
let submittedToQueue = expectation(description: "Submitted To Queue")
let reporter = Reporter(session, batterySafeForNetworking: { true }, networkUtility: .wifi)

// When
reporter.submit(AlertBeacon(alertType: .lowMemory)) {result in
didSubmit = result
submittedToQueue.fulfill()
}
wait(for: [submittedToQueue], timeout: 3.0)

// Then
AssertFalse(didSubmit)
}

func test_submit_rateLimit_two_NOT_exceeded() {
// Given
let submittedToQueue1 = expectation(description: "Submitted First Beacon")
Expand Down Expand Up @@ -1012,6 +1032,50 @@ class ReporterTests: InstanaTestCase {
AssertTrue(resultErrors.contains(instErr))
}

func test_canScheduleFlush_not_allowed_while_flushing() {
// Given
let reporter = Reporter(session, batterySafeForNetworking: { true }, networkUtility: .wifi)
let corebeacons = try! CoreBeaconFactory(session).map([HTTPBeacon.createMock()])
reporter.queue.add(corebeacons)

// When
reporter.scheduleFlush()

// Then
XCTAssertFalse(reporter.canScheduleFlush())
}

func test_canScheduleFlush_lastFlushStartTime_nil() {
// Given
let reporter = Reporter(session, batterySafeForNetworking: { true }, networkUtility: .wifi)
let corebeacons = try! CoreBeaconFactory(session).map([HTTPBeacon.createMock()])
reporter.queue.add(corebeacons)

// When
reporter.scheduleFlush()

reporter.lastFlushStartTime = nil // Force scheduling 2nd flushing

// Then
XCTAssertTrue(reporter.canScheduleFlush())
}


func test_canScheduleFlush_maxFlushingTimeExceeded() {
// Given
let reporter = Reporter(session, batterySafeForNetworking: { true }, networkUtility: .wifi)
let corebeacons = try! CoreBeaconFactory(session).map([HTTPBeacon.createMock()])
reporter.queue.add(corebeacons)

// When
reporter.scheduleFlush()

// last flush time is stale, force flushing
reporter.lastFlushStartTime = reporter.lastFlushStartTime! - (10.0+1) * 60
// Then
XCTAssertTrue(reporter.canScheduleFlush())
}

func test_submit_and_flush_shouldNotCause_RetainCycle() {
// Given
let waitForCompletion = expectation(description: "waitForSend")
Expand Down Expand Up @@ -1254,6 +1318,23 @@ class ReporterTests: InstanaTestCase {
}
AssertTrue(reporter.queue.isFull)
}

func test_runBackgroundFlush() {
// Given
let reporter = Reporter(session, batterySafeForNetworking: { true }, networkUtility: .wifi)

XCTAssertNil(reporter.flusher)

// When
let corebeacons = try! CoreBeaconFactory(session).map([HTTPBeacon.createMock()])
reporter.queue.add(corebeacons)

reporter.runBackgroundFlush()
Thread.sleep(forTimeInterval: 1)

// Then
XCTAssertNotNil(reporter.flusher)
}
}


Expand Down
77 changes: 77 additions & 0 deletions Tests/InstanaAgentTests/InstanaTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class InstanaTests: InstanaTestCase {
let options = InstanaSetupOptions(httpCaptureConfig: .automaticAndManual,
collectionEnabled: false)
options.enableCrashReporting = true
options.suspendReportingOnLowBattery = true
options.suspendReportingOnCellular = true
options.slowSendInterval = 20.0
let ret = Instana.setup(key: key, reportingURL: reportingURL, options: options)

Expand Down Expand Up @@ -87,6 +89,15 @@ class InstanaTests: InstanaTestCase {
AssertFalse(ret2)
}

func test_setup_invalid_configuration_empty_key() {
// Given
let reportingURL = URL(string: "http://www.instana.com")!
_ = Instana.setup(key: "", reportingURL: reportingURL, options: nil)

// Then
AssertFalse(Instana.current!.session.configuration.isValid)
}

func test_setup() {
// Given
let key = "KEY"
Expand Down Expand Up @@ -706,4 +717,70 @@ class InstanaTests: InstanaTestCase {
// Covers negative case for empty current of Instana
XCTAssertFalse(cancelled)
}

@available(*, deprecated)
func test_setup_deprecated1() {
// Given
let key = "KEY"
let reportingURL = URL(string: "http://www.instana.com")!

Instana.setup(key: key, reportingURL: reportingURL)

// Then
AssertEqualAndNotNil(Instana.key, key)
AssertEqualAndNotNil(Instana.reportingURL, reportingURL)
AssertTrue(Instana.collectionEnabled)
AssertTrue(Instana.current!.session.collectionEnabled)
AssertEqualAndNotNil(Instana.sessionID, Instana.current?.session.id.uuidString)

let config = Instana.current?.session.configuration
XCTAssertNotNil(config)
AssertEqualAndNotNil(config!.key, key)
AssertEqualAndNotNil(config!.reportingURL, reportingURL)
AssertEqualAndNotNil(config!.httpCaptureConfig, .automatic)
AssertEqualAndNotNil(config!.slowSendInterval, 0.0)
AssertEqualAndNotNil(config!.usiRefreshTimeIntervalInHrs, defaultUsiRefreshTimeIntervalInHrs)
AssertFalse(config!.monitorTypes.contains(.crash))

let session = Instana.current?.session
XCTAssertNotNil(session)
AssertTrue(session!.collectionEnabled)
}

@available(*, deprecated)
func test_setup_deprecated2() {
// Given
let key = "KEY"
let reportingURL = URL(string: "http://www.instana.com")!

Instana.setup(key: key, reportingURL: reportingURL,
httpCaptureConfig: .manual,
collectionEnabled: true,
enableCrashReporting: true)

// Then
AssertEqualAndNotNil(Instana.key, key)
AssertEqualAndNotNil(Instana.reportingURL, reportingURL)
AssertTrue(Instana.collectionEnabled)
AssertTrue(Instana.current!.session.collectionEnabled)
AssertEqualAndNotNil(Instana.sessionID, Instana.current?.session.id.uuidString)

let config = Instana.current?.session.configuration
XCTAssertNotNil(config)
AssertEqualAndNotNil(config!.key, key)
AssertEqualAndNotNil(config!.reportingURL, reportingURL)
AssertEqualAndNotNil(config!.httpCaptureConfig, .manual)
AssertEqualAndNotNil(config!.slowSendInterval, 0.0)
AssertEqualAndNotNil(config!.usiRefreshTimeIntervalInHrs, defaultUsiRefreshTimeIntervalInHrs)
AssertTrue(config!.monitorTypes.contains(.crash))

let session = Instana.current?.session
XCTAssertNotNil(session)
AssertTrue(session!.collectionEnabled)
}

func test_setup_not_called() {
Instana.current = nil
AssertFalse(Instana.collectionEnabled)
}
}
3 changes: 2 additions & 1 deletion Tests/InstanaAgentTests/Monitors/HTTP/HTTPMarkerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ class HTTPMarkerTests: InstanaTestCase {
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: ["KEY": "VALUE"])!
let marker = HTTPMarker(url: url, method: "GET", trigger: .automatic, delegate: Delegate())
let responseSize = HTTPMarker.Size(response)
let excpectedHeaderSize = Instana.Types.Bytes(NSKeyedArchiver.archivedData(withRootObject: response.allHeaderFields).count)
let excpectedHeaderSize = try! Instana.Types.Bytes(
NSKeyedArchiver.archivedData(withRootObject: response.allHeaderFields, requiringSecureCoding: false).count)

// When
marker.set(responseSize: responseSize)
Expand Down

0 comments on commit 286d31a

Please sign in to comment.