Skip to content
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
2 changes: 2 additions & 0 deletions Agent/Analytics/NRMAAnalytics.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1127,10 +1127,12 @@ - (void) clearLastSessionsAnalytics {
if([NRMAFlags shouldEnableNewEventSystem]){
[_sessionAttributeManager removeAllSessionAttributes];
[_eventManager empty];
[_eventManager resetTimestamp];
} else {
try {
_analyticsController->clearAttributesDuplicationStore();
_analyticsController->clearEventsDuplicationStore();
_analyticsController->resetEventTimestamp();
} catch (std::exception& e) {
NRLOG_AGENT_VERBOSE(@"Failed to clear last sessions' analytcs, %s",e.what());
} catch (...) {
Expand Down
1 change: 1 addition & 0 deletions Agent/Analytics/NRMAEventManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)didReachMaxQueueTime:(NSTimeInterval)currentTimeMilliseconds;
- (BOOL)addEvent:(id<NRMAAnalyticEventProtocol>)event;
- (void)empty;
- (void)resetTimestamp;
- (nullable NSString *)getEventJSONStringWithError:(NSError *__autoreleasing *)error clearEvents:(BOOL)clearEvents;

+ (nullable NSString *)getLastSessionEventsFromFilename:(NSString *)filename;
Expand Down
10 changes: 7 additions & 3 deletions Agent/Analytics/NRMAEventManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
static const NSUInteger kDefaultBufferTimeSeconds = 60; // 60 seconds
static const NSUInteger kMinBufferTimeSeconds = 60; // 60 seconds
static const NSUInteger kBufferTimeSecondsLeeway = 60; // 60 seconds
static const double kMillisecondsPerSecond = 1000.0; // milliseconds to seconds conversion

// Event Key Format String: TimeStamp|SessionElapsedTime|EventType
static NSString* const eventKeyFormat = @"%f|%f|%@";
Expand Down Expand Up @@ -74,9 +75,9 @@ - (BOOL)didReachMaxQueueTime:(NSTimeInterval)currentTimeMilliseconds {
if(oldestEventTimestamp == 0) {
return false;
}

NSTimeInterval oldestEventAge = currentTimeMilliseconds - oldestEventTimestamp;
return (oldestEventAge / kDefaultBufferSize) + kBufferTimeSecondsLeeway >= maxBufferTimeSeconds;
return (oldestEventAge / kMillisecondsPerSecond) + kBufferTimeSecondsLeeway >= maxBufferTimeSeconds;
}

- (NSUInteger)getEvictionIndex {
Expand Down Expand Up @@ -122,11 +123,14 @@ - (void)empty {
@synchronized (events) {
[events removeAllObjects];
[_persistentStore clearAll];
oldestEventTimestamp = 0;
totalAttemptedInserts = 0;
}
}

- (void)resetTimestamp {
oldestEventTimestamp = 0;
}

- (nullable NSString *)getEventJSONStringWithError:(NSError *__autoreleasing *)error clearEvents:(BOOL)clearEvents {
NSString *eventJsonString = nil;
@synchronized (events) {
Expand Down
22 changes: 19 additions & 3 deletions Agent/SessionReplay/SessionReplayReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,25 @@ public class SessionReplayReporter: NSObject {
"replay.lastTimestamp": String(Int(lastTimestamp)),
"appVersion": appVersion,
"instrumentation.provider": "mobile",
"instrumentation.name": NewRelicInternalUtils.osName(),
"instrumentation.version": NewRelicInternalUtils.agentVersion(),
"collector.name": NewRelicInternalUtils.osName()
"instrumentation.name": {
guard let connectionInfo = NRMAAgentConfiguration.connectionInformation(),
let deviceInfo = connectionInfo.deviceInformation else {
return NewRelicInternalUtils.agentName()
}
let platform = deviceInfo.platform
return platform.rawValue == 0 // NRMAPlatform_Native
? NewRelicInternalUtils.agentName()
: NewRelicInternalUtils.string(from: platform)
}(),
"instrumentation.version": {
guard let connectionInfo = NRMAAgentConfiguration.connectionInformation(),
let deviceInfo = connectionInfo.deviceInformation,
let platformVersion = deviceInfo.platformVersion as String? else {
return NewRelicInternalUtils.agentVersion()
}
return platformVersion
}(),
"collector.name": NewRelicInternalUtils.agentName()
]
if isGZipped {
attributes["content_encoding"] = "gzip"
Expand Down
2 changes: 1 addition & 1 deletion Agent/Utilities/NRLogger.m
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ - (NSMutableDictionary*) commonBlockDict {
if (NRSessionId) [commonAttributes setObject:NRSessionId forKey:NRLogMessageSessionIdKey];
[commonAttributes setObject:NRLogMessageMobileValue forKey:NRLogMessageInstrumentationProviderKey];
if (name) [commonAttributes setObject:name forKey:NRLogMessageInstrumentationNameKey];
[commonAttributes setObject:[NRMAAgentConfiguration connectionInformation].deviceInformation.agentVersion forKey:NRLogMessageInstrumentationVersionKey];
[commonAttributes setObject:[NRMAAgentConfiguration connectionInformation].deviceInformation.platformVersion ?: [NRMAAgentConfiguration connectionInformation].deviceInformation.agentVersion forKey:NRLogMessageInstrumentationVersionKey];
if (nativePlatform) [commonAttributes setObject:nativePlatform forKey:NRLogMessageInstrumentationCollectorKey];
if (nrAppId) [commonAttributes setObject:nrAppId forKey:NRLogMessageAppIdKey];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,32 +187,65 @@ - (void)testEmptyEvents {
XCTAssertEqual(emptyDecode.count, 0);
}

- (void)testEmptyEventsResetOldestEventTime {
- (void)testEmptyEventsDoesNotResetOldestEventTime {
// Given
NRMACustomEvent *customEventOne = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 1"
timestamp:1000
sessionElapsedTimeInSeconds:20
withAttributeValidator:agreeableAttributeValidator];

NRMACustomEvent *customEventTwo = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 2"
timestamp:1000
sessionElapsedTimeInSeconds:20
withAttributeValidator:agreeableAttributeValidator];

NRMACustomEvent *customEventThree = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 3"
timestamp:1000
sessionElapsedTimeInSeconds:20
withAttributeValidator:agreeableAttributeValidator];
[sut setMaxEventBufferTimeInSeconds:1];

[sut addEvent:customEventOne];
[sut addEvent:customEventTwo];
[sut addEvent:customEventThree];

XCTAssertTrue([sut didReachMaxQueueTime:2000]);

// When - empty() is called (during normal harvest)
[sut empty];

// Then - timestamp should persist (matching Android behavior)
XCTAssertTrue([sut didReachMaxQueueTime:2000]);
}

- (void)testResetTimestampResetsOldestEventTime {
// Given
NRMACustomEvent *customEventOne = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 1"
timestamp:1000
sessionElapsedTimeInSeconds:20
withAttributeValidator:agreeableAttributeValidator];

NRMACustomEvent *customEventTwo = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 2"
timestamp:1000
sessionElapsedTimeInSeconds:20
withAttributeValidator:agreeableAttributeValidator];

NRMACustomEvent *customEventThree = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 3"
timestamp:1000
sessionElapsedTimeInSeconds:20
withAttributeValidator:agreeableAttributeValidator];
[sut setMaxEventBufferTimeInSeconds:1];

[sut addEvent:customEventOne];
[sut addEvent:customEventTwo];
[sut addEvent:customEventThree];

XCTAssertTrue([sut didReachMaxQueueTime:2000]);

// When - resetTimestamp() is explicitly called (during session clear)
[sut resetTimestamp];

// Then - timestamp should be reset
XCTAssertFalse([sut didReachMaxQueueTime:2000]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ namespace NewRelic {

void clearAttributesDuplicationStore();

void resetEventTimestamp();

static std::shared_ptr <NRJSON::JsonArray> fetchDuplicatedEvents(
PersistentStore<std::string, AnalyticEvent> &eventStore,
bool shouldClearStore);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ namespace NewRelic {
void setMaxBufferSize(unsigned int size); //sets max buffer size
bool didReachMaxQueueTime(unsigned long long currentTimestamp_ms); //checks if oldest event timestamp exceededs max queue time
void empty(); //removes all events in _events;
void resetTimestamp(); //resets _oldest_event_timestamp_ms to 0 (for session clear)
};
}
#endif
4 changes: 4 additions & 0 deletions libMobileAgent/src/Analytics/src/AnalyticsController.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,10 @@ namespace NewRelic {
_attributeDuplicationStore.clear();
}

void AnalyticsController::resetEventTimestamp() {
_eventManager.resetTimestamp();
}

const char *AnalyticsController::getPersistentAttributeStoreName() {
return ATTRIBUTE_STORE_DB_FILENAME;
}
Expand Down
5 changes: 4 additions & 1 deletion libMobileAgent/src/Analytics/src/EventManager.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@ void EventManager::empty() {
lock1.lock();
_events.clear();
_eventDuplicationStore.clear();
_oldest_event_timestamp_ms = 0;
//we're empty so let's reset the total number of attempted inserts.
_total_attempted_inserts = 0;
}

void EventManager::resetTimestamp() {
_oldest_event_timestamp_ms = 0;
}

std::string EventManager::createKey(std::shared_ptr<AnalyticEvent> event) {
std::stringstream ss;
ss << *event;
Expand Down
50 changes: 50 additions & 0 deletions libMobileAgent/test/AnalyticsTest/EventManager_test.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -286,5 +286,55 @@ TEST_F(EventManagerTest, testBadSerializedEvent) {

ASSERT_THROW(manager.newEvent(strs2), std::runtime_error);
}

Copy link
Member

Choose a reason for hiding this comment

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

Nice! I forgot about libMobileAgent C++ tests, they will go away once we make NewEventSystem default and remove the new event system flag. Excellent work getting coverage.

TEST_F(EventManagerTest, testEmptyDoesNotResetTimestamp) {
EventManager manager{store};

// Set max buffer time to 1 second
manager.setMaxBufferTime(1);

// Add events at timestamp 1000ms
auto event1 = manager.newCustomMobileEvent("custom1", 1000, 1, validator);
auto event2 = manager.newCustomMobileEvent("custom2", 1000, 1, validator);
auto event3 = manager.newCustomMobileEvent("custom3", 1000, 1, validator);

manager.addEvent(event1);
manager.addEvent(event2);
manager.addEvent(event3);

// Check at 2000ms - should have reached max queue time (1 second elapsed)
ASSERT_TRUE(manager.didReachMaxQueueTime(2000));

// Call empty() (simulating normal harvest)
manager.empty();

// Timestamp should persist (matching Android behavior)
ASSERT_TRUE(manager.didReachMaxQueueTime(2000));
}

TEST_F(EventManagerTest, testResetTimestampResetsTimestamp) {
EventManager manager{store};

// Set max buffer time to 1 second
manager.setMaxBufferTime(1);

// Add events at timestamp 1000ms
auto event1 = manager.newCustomMobileEvent("custom1", 1000, 1, validator);
auto event2 = manager.newCustomMobileEvent("custom2", 1000, 1, validator);
auto event3 = manager.newCustomMobileEvent("custom3", 1000, 1, validator);

manager.addEvent(event1);
manager.addEvent(event2);
manager.addEvent(event3);

// Check at 2000ms - should have reached max queue time (1 second elapsed)
ASSERT_TRUE(manager.didReachMaxQueueTime(2000));

// Call resetTimestamp() (simulating session clear)
manager.resetTimestamp();

// Timestamp should be reset
ASSERT_FALSE(manager.didReachMaxQueueTime(2000));
}
} // namespace NewRelic