Skip to content

Commit

Permalink
Nr 331577: Log common block (#337)
Browse files Browse the repository at this point in the history
* NR-331557: Common Block impl

* NR-331577:  Impl Log common block

* NR-233726: gzip logs
  • Loading branch information
cdillard-NewRelic authored Jan 7, 2025
1 parent 925dbff commit 9b5f166
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 53 deletions.
64 changes: 48 additions & 16 deletions Agent/Utilities/NRLogger.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import "NRMASupportMetricHelper.h"
#import "NRMAFlags.h"
#import "NRAutoLogCollector.h"
#import "NRMAHarvesterConnection+GZip.h"

NRLogger *_nr_logger = nil;

Expand Down Expand Up @@ -286,10 +287,14 @@ - (void)addLogMessage:(NSDictionary *)message : (BOOL) agentLogsOn {
});
}

- (NSData*) jsonDictionary:(NSDictionary*)message {
- (NSMutableDictionary*) commonBlockDict {
NSString* NRSessionId = [[[NewRelicAgentInternal sharedInstance] currentSessionId] copy];
NRMAHarvesterConfiguration *configuration = [NRMAHarvestController configuration];

// The following line generates the native platform name which is used as "collector.name" in the log payload.
NSString* nativePlatform = [NewRelicInternalUtils agentName];

// The following code generates the variable name that is chosen between native platform name or the Hybrid platform name which is used as "instrumentation.name" in the log payload.
NSString* platform = [NewRelicInternalUtils stringFromNRMAApplicationPlatform:[NRMAAgentConfiguration connectionInformation].deviceInformation.platform];
NSString* name = [NRMAAgentConfiguration connectionInformation].deviceInformation.platform == NRMAPlatform_Native ? nativePlatform : platform;

Expand All @@ -316,6 +321,20 @@ - (NSData*) jsonDictionary:(NSDictionary*)message {
entityGuid = @"";
}

NSMutableDictionary *commonAttributes = [NSMutableDictionary dictionary];
[commonAttributes setObject:entityGuid forKey:NRLogMessageEntityGuidKey];
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];
if (nativePlatform) [commonAttributes setObject:nativePlatform forKey:NRLogMessageInstrumentationCollectorKey];
if (nrAppId) [commonAttributes setObject:nrAppId forKey:NRLogMessageAppIdKey];

return commonAttributes;
}

- (NSData*) jsonDictionary:(NSDictionary*)message {

NSMutableDictionary *requiredAttributes = [NSMutableDictionary dictionary];

id value = [message objectForKey:NRLogMessageLevelKey];
Expand All @@ -336,19 +355,6 @@ - (NSData*) jsonDictionary:(NSDictionary*)message {
value = [[message objectForKey:NRLogMessageMessageKey]stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
if (value) [requiredAttributes setObject:value forKey:NRLogMessageMessageKey]; // 6

if (NRSessionId) [requiredAttributes setObject:NRSessionId forKey:NRLogMessageSessionIdKey]; // 7
if (nrAppId) [requiredAttributes setObject:nrAppId forKey:NRLogMessageAppIdKey]; // 8
if (entityGuid) [requiredAttributes setObject:entityGuid forKey:NRLogMessageEntityGuidKey]; // 9

[requiredAttributes setObject:NRLogMessageMobileValue forKey:NRLogMessageInstrumentationProviderKey]; // 10
[requiredAttributes setObject:name forKey:NRLogMessageInstrumentationNameKey]; // 11

value = [NRMAAgentConfiguration connectionInformation].deviceInformation.agentVersion;
if (value) [requiredAttributes setObject:value forKey:NRLogMessageInstrumentationVersionKey]; // 12

[requiredAttributes setObject:nativePlatform forKey:NRLogMessageInstrumentationCollectorKey]; // 13


NSMutableDictionary *providedAttributes = [message mutableCopy];
[providedAttributes removeObjectsForKeys:@[NRLogMessageLevelKey,NRLogMessageFileKey,NRLogMessageLineNumberKey,NRLogMessageMethodKey,NRLogMessageTimestampKey,NRLogMessageMessageKey]];
[providedAttributes addEntriesFromDictionary:requiredAttributes];
Expand Down Expand Up @@ -531,8 +537,30 @@ - (void)enqueueLogUpload {
return;
}

NSString* logMessagesJson = [NSString stringWithFormat:@"[ %@ ]", [[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];
NSData* formattedData = [logMessagesJson dataUsingEncoding:NSUTF8StringEncoding];
// the text of the file contents is just comma separated dict objects
// Add the user provided attributes to the message.
NSMutableDictionary *commonBlock = [self commonBlockDict];

NSError* error = nil;

NSData *json = [NRMAJSON dataWithJSONObject:commonBlock
options:0
error:&error];

if (error) {
NRLOG_AGENT_ERROR(@"Failed to create log payload w error = %@", error);
}

// New version of the line
NSString* logMessagesJson = [NSString stringWithFormat:@"[{ \"common\": { \"attributes\": %@}, \"logs\": [ %@ ] }]",
[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding],
[[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];

// Old version of the line
// NSString* logMessagesJson = [NSString stringWithFormat:@"[ %@ ]", [[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];

// Here we add gzip compression to the log data.
NSData* formattedData = [NRMAHarvesterConnection gzipData:[logMessagesJson dataUsingEncoding:NSUTF8StringEncoding]];

// We clear the log when we save the existing logs to uploadQueue.
[self clearLog];
Expand Down Expand Up @@ -582,6 +610,10 @@ - (void) processNextUploadTask {
[req setValue:self->logIngestKey forHTTPHeaderField:@"X-App-License-Key"];
[req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

//NSString* contentEncoding = message.length <= 512 ? kNRMAIdentityHeader : kNRMAGZipHeader;

[req setValue:kNRMAGZipHeader forHTTPHeaderField:kNRMAContentEncodingHeader];

req.HTTPMethod = @"POST";

NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:req fromData:formattedData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
Expand Down
162 changes: 125 additions & 37 deletions Tests/Unit-Tests/NewRelicAgentTests/Uncategorized/NRLoggerTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
#import "NRAutoLogCollector.h"
#import <os/log.h>

@interface NRLogger()
+ (NRLogger *)logger;
- (NSMutableDictionary*) commonBlockDict;
@end

@implementation NRLoggerTests
- (void) setUp
{
Expand Down Expand Up @@ -69,8 +74,6 @@ - (void) setUp
weakSelf.fileDescriptor = 0;
}
});


}
- (void) tearDown
{
Expand Down Expand Up @@ -108,20 +111,40 @@ - (void) testNRLogger {

sleep(5);

NSError* error;
NSError* error = nil;
NSData* logData = [NRLogger logFileData:&error];
if(error){
NSLog(@"%@", error.localizedDescription);
}

NSString* logMessagesJson = [NSString stringWithFormat:@"[ %@ ]", [[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];
NSMutableDictionary *commonBlock = [[NRLogger logger] commonBlockDict];

NSData *json = [NRMAJSON dataWithJSONObject:commonBlock
options:0
error:&error];

if (error) {
NRLOG_AGENT_ERROR(@"Failed to create log payload w error = %@", error);
XCTAssertNil(error, @"Error creating log payload");
return;
}

error = nil;
// New version of the line
NSString* logMessagesJson = [NSString stringWithFormat:@"[{ \"common\": { \"attributes\": %@}, \"logs\": [ %@ ] }]",
[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding],
[[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];

NSData* formattedData = [logMessagesJson dataUsingEncoding:NSUTF8StringEncoding];

NSArray* decode = [NSJSONSerialization JSONObjectWithData:formattedData
NSDictionary* decode = [NSJSONSerialization JSONObjectWithData:formattedData
options:0
error:nil];
error:&error];
NSLog(@"decode=%@", decode);

NSArray *decodedArray = [[decode valueForKey:@"logs"] objectAtIndex:0];
NSDictionary *decodedCommonBlock = [[[decode valueForKey:@"common"] objectAtIndex:0] valueForKey:@"attributes"];

NSArray * expectedValues = @[
@{@"message": @"Info Log..."},
@{@"message": @"Error Log..."},
Expand All @@ -131,31 +154,19 @@ - (void) testNRLogger {
@{@"message": @"Debug Log..."},
@{@"message": @"This is a test message for the New Relic logging system."},
];

// check for existence of 6 logs.
int foundCount = 0;
// For each expected message.
for (NSDictionary *dict in expectedValues) {
// Iterate through the collected message logs.
for (NSDictionary *dict2 in decode) {
for (NSDictionary *dict2 in decodedArray) {
//
NSString* currentMessage = [dict objectForKey:@"message"];

// Check the logs entries
if ([[dict2 objectForKey:@"message"] isEqualToString: currentMessage]) {
foundCount += 1;
XCTAssertTrue([[dict2 objectForKey:@"entity.guid"] isEqualToString:@"Entity-Guid-XXXX"],@"entity.guid set incorrectly");
XCTAssertTrue([[dict2 objectForKey:NRLogMessageInstrumentationProviderKey] isEqualToString:NRLogMessageMobileValue],@"instrumentation provider set incorrectly");
XCTAssertTrue([[dict2 objectForKey:NRLogMessageInstrumentationVersionKey] isEqualToString:@"DEV"],@"instrumentation name set incorrectly");

#if TARGET_OS_WATCH
XCTAssertTrue([[dict2 objectForKey:NRLogMessageInstrumentationNameKey] isEqualToString:@"watchOSAgent"],@"instrumentation name set incorrectly");
#else
if ([[[UIDevice currentDevice] systemName] isEqualToString:@"tvOS"]) {
XCTAssertTrue([[dict2 objectForKey:NRLogMessageInstrumentationNameKey] isEqualToString:@"tvOSAgent"],@"instrumentation name set incorrectly");

}
else {
XCTAssertTrue([[dict2 objectForKey:NRLogMessageInstrumentationNameKey] isEqualToString:@"iOSAgent"],@"instrumentation name set incorrectly");
}
#endif
}
// Verify added attributes with logAttributes.
if ([[dict2 objectForKey:@"message"] isEqualToString:@"This is a test message for the New Relic logging system."]) {
Expand All @@ -166,6 +177,22 @@ - (void) testNRLogger {
}

XCTAssertEqual(foundCount, 7, @"Seven messages should be found.");

// Verify Common Block
XCTAssertTrue([[decodedCommonBlock objectForKey:@"entity.guid"] isEqualToString:@"Entity-Guid-XXXX"],@"entity.guid set incorrectly");
XCTAssertTrue([[decodedCommonBlock objectForKey:NRLogMessageInstrumentationProviderKey] isEqualToString:NRLogMessageMobileValue],@"instrumentation provider set incorrectly");
XCTAssertTrue([[decodedCommonBlock objectForKey:NRLogMessageInstrumentationVersionKey] isEqualToString:@"DEV"],@"instrumentation name set incorrectly");

#if TARGET_OS_WATCH
XCTAssertTrue([[decodedCommonBlock objectForKey:NRLogMessageInstrumentationNameKey] isEqualToString:@"watchOSAgent"],@"instrumentation name set incorrectly");
#else
if ([[[UIDevice currentDevice] systemName] isEqualToString:@"tvOS"]) {
XCTAssertTrue([[decodedCommonBlock objectForKey:NRLogMessageInstrumentationNameKey] isEqualToString:@"tvOSAgent"],@"instrumentation name set incorrectly");
}
else {
XCTAssertTrue([[decodedCommonBlock objectForKey:NRLogMessageInstrumentationNameKey] isEqualToString:@"iOSAgent"],@"instrumentation name set incorrectly");
}
#endif
}


Expand Down Expand Up @@ -223,13 +250,34 @@ - (void) testRemoteLogLevels {
if(error){
NSLog(@"%@", error.localizedDescription);
}
NSString* logMessagesJson = [NSString stringWithFormat:@"[ %@ ]", [[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];
NSMutableDictionary *commonBlock = [[NRLogger logger] commonBlockDict];

NSData *json = [NRMAJSON dataWithJSONObject:commonBlock
options:0
error:&error];

if (error) {
NRLOG_AGENT_ERROR(@"Failed to create log payload w error = %@", error);
XCTAssertNil(error, @"Error creating log payload");
return;
}

error = nil;
// New version of the line
NSString* logMessagesJson = [NSString stringWithFormat:@"[{ \"common\": { \"attributes\": %@}, \"logs\": [ %@ ] }]",
[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding],
[[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];

NSData* formattedData = [logMessagesJson dataUsingEncoding:NSUTF8StringEncoding];
NSArray* decode = [NSJSONSerialization JSONObjectWithData:formattedData

NSDictionary* decode = [NSJSONSerialization JSONObjectWithData:formattedData
options:0
error:nil];
error:&error];
NSLog(@"decode=%@", decode);

NSArray *decodedArray = [[decode valueForKey:@"logs"] objectAtIndex:0];
NSDictionary *decodedCommonBlock = [[[decode valueForKey:@"common"] objectAtIndex:0] valueForKey:@"attributes"];

NSArray * expectedValues = @[
@{@"message": @"Info Log..."},
@{@"message": @"Error Log..."},
Expand All @@ -244,12 +292,11 @@ - (void) testRemoteLogLevels {
// For each expected message.
for (NSDictionary *dict in expectedValues) {
// Iterate through the collected message logs.
for (NSDictionary *dict2 in decode) {
for (NSDictionary *dict2 in decodedArray) {
//
NSString* currentMessage = [dict objectForKey:@"message"];
if ([[dict2 objectForKey:@"message"] isEqualToString: currentMessage]) {
foundCount += 1;
XCTAssertTrue([[dict2 objectForKey:@"entity.guid"] isEqualToString:@"Entity-Guid-XXXX"],@"entity.guid set incorrectly");
}
// Verify added attributes with logAttributes.
if ([[dict2 objectForKey:@"message"] isEqualToString:@"This is a test message for the New Relic logging system."]) {
Expand Down Expand Up @@ -314,13 +361,34 @@ - (void) testLocalLogLevels {
if(error){
NSLog(@"%@", error.localizedDescription);
}
NSString* logMessagesJson = [NSString stringWithFormat:@"[ %@ ]", [[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];
NSMutableDictionary *commonBlock = [[NRLogger logger] commonBlockDict];

NSData *json = [NRMAJSON dataWithJSONObject:commonBlock
options:0
error:&error];

if (error) {
NRLOG_AGENT_ERROR(@"Failed to create log payload w error = %@", error);
XCTAssertNil(error, @"Error creating log payload");
return;
}

error = nil;
// New version of the line
NSString* logMessagesJson = [NSString stringWithFormat:@"[{ \"common\": { \"attributes\": %@}, \"logs\": [ %@ ] }]",
[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding],
[[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];

NSData* formattedData = [logMessagesJson dataUsingEncoding:NSUTF8StringEncoding];
NSArray* decode = [NSJSONSerialization JSONObjectWithData:formattedData

NSDictionary* decode = [NSJSONSerialization JSONObjectWithData:formattedData
options:0
error:nil];
error:&error];
NSLog(@"decode=%@", decode);

NSArray *decodedArray = [[decode valueForKey:@"logs"] objectAtIndex:0];
NSDictionary *decodedCommonBlock = [[[decode valueForKey:@"common"] objectAtIndex:0] valueForKey:@"attributes"];

NSArray * expectedValues = @[
@{@"message": @"Info Log..."},
@{@"message": @"Error Log..."},
Expand All @@ -335,12 +403,11 @@ - (void) testLocalLogLevels {
// For each expected message.
for (NSDictionary *dict in expectedValues) {
// Iterate through the collected message logs.
for (NSDictionary *dict2 in decode) {
for (NSDictionary *dict2 in decodedArray) {
//
NSString* currentMessage = [dict objectForKey:@"message"];
if ([[dict2 objectForKey:@"message"] isEqualToString: currentMessage]) {
foundCount += 1;
XCTAssertTrue([[dict2 objectForKey:@"entity.guid"] isEqualToString:@"Entity-Guid-XXXX"],@"entity.guid set incorrectly");
}
// Verify added attributes with logAttributes.
if ([[dict2 objectForKey:@"message"] isEqualToString:@"This is a test message for the New Relic logging system."]) {
Expand Down Expand Up @@ -400,13 +467,35 @@ - (void) testAutoCollectedLogs {
if(error){
NSLog(@"%@", error.localizedDescription);
}
NSString* logMessagesJson = [NSString stringWithFormat:@"[ %@ ]", [[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];
NSMutableDictionary *commonBlock = [[NRLogger logger] commonBlockDict];

NSData *json = [NRMAJSON dataWithJSONObject:commonBlock
options:0
error:&error];

if (error) {
NRLOG_AGENT_ERROR(@"Failed to create log payload w error = %@", error);
XCTAssertNil(error, @"Error creating log payload");
return;
}

error = nil;
// New version of the line
NSString* logMessagesJson = [NSString stringWithFormat:@"[{ \"common\": { \"attributes\": %@}, \"logs\": [ %@ ] }]",
[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding],
[[NSString alloc] initWithData:logData encoding:NSUTF8StringEncoding]];

NSData* formattedData = [logMessagesJson dataUsingEncoding:NSUTF8StringEncoding];
NSArray* decode = [NSJSONSerialization JSONObjectWithData:formattedData

NSDictionary* decode = [NSJSONSerialization JSONObjectWithData:formattedData
options:0
error:nil];
error:&error];
NSLog(@"decode=%@", decode);

NSArray *decodedArray = [[decode valueForKey:@"logs"] objectAtIndex:0];
NSDictionary *decodedCommonBlock = [[[decode valueForKey:@"common"] objectAtIndex:0] valueForKey:@"attributes"];


NSArray * expectedValues = @[
@{@"message": @"NSLog Test"},
@{@"message": @"This is a default os_log message."},
Expand All @@ -419,12 +508,11 @@ - (void) testAutoCollectedLogs {
// For each expected message.
for (NSDictionary *dict in expectedValues) {
// Iterate through the collected message logs.
for (NSDictionary *dict2 in decode) {
for (NSDictionary *dict2 in decodedArray) {
//
NSString* currentMessage = [dict objectForKey:@"message"];
if ([[dict2 objectForKey:@"message"] containsString: currentMessage]) {
foundCount += 1;
XCTAssertTrue([[dict2 objectForKey:@"entity.guid"] isEqualToString:@"Entity-Guid-XXXX"],@"entity.guid set incorrectly");
}
// Verify added attributes with logAttributes.
if ([[dict2 objectForKey:@"message"] isEqualToString:@"This is a test message for the New Relic logging system."]) {
Expand Down

0 comments on commit 9b5f166

Please sign in to comment.