Skip to content

Commit

Permalink
Re-added UUID as a private inner class as a fallback transaction ID (#…
Browse files Browse the repository at this point in the history
…170)

- The UUID class is used as a secondary transaction ID in the event that System.Request.getCurrent().getRequestId() is null/blank.
  • Loading branch information
jongpie authored Jun 20, 2021
1 parent 29d8e9b commit 5acd00c
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 1 deletion.
98 changes: 97 additions & 1 deletion nebula-logger/main/logger-engine/classes/Logger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
global with sharing class Logger {
private static final LoggingLevel DEFAULT_LOGGING_LEVEL = LoggingLevel.DEBUG;
private static final List<LogEntryEventBuilder> LOG_ENTRIES_BUFFER = new List<LogEntryEventBuilder>();
private static final String TRANSACTION_ID = System.Request.getCurrent().getRequestId();
private static final String TRANSACTION_ID = setTransactionId();
private static final Quiddity TRANSACTION_QUIDDITY = System.Request.getCurrent().getQuiddity();

private static Integer currentTransactionEntryNumber = 1;
Expand Down Expand Up @@ -2636,6 +2636,11 @@ global with sharing class Logger {
return logEntryEventBuilder;
}

private static String setTransactionId() {
String transactionId = System.Request.getCurrent().getRequestId();
return String.isNotBlank(transactionId) ? transactionId : new Uuid().getValue();
}

private static SaveMethod getDefaultSaveMethod() {
SaveMethod defaultSaveMethod;

Expand Down Expand Up @@ -2756,4 +2761,95 @@ global with sharing class Logger {
this.records = records;
}
}

/******************************************************************************************
* This code is based on the Apex UUID project, released under the MIT License. *
* See LICENSE file or go to https://github.com/jongpie/ApexUuid for full license details. *
******************************************************************************************/
@testVisible
private without sharing class Uuid {

private final String HEX_CHARACTERS = '0123456789abcdef';
private final String HEX_PREFIX = '0x';
private final List<String> HEX_CHARACTER_LIST = HEX_CHARACTERS.split('');
private final Integer UUID_V4_LENGTH = 36;
private final String UUID_V4_REGEX = '[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}';


private final String value;

public Uuid() {
this.value = this.generateValue();
}

public String getValue() {
return this.value;
}

private String generateValue() {
String hexValue = EncodingUtil.convertToHex(Crypto.generateAesKey(128));

// Version Calculation: (i & 0x0f) | 0x40
// Version Format: Always begins with 4
String versionShiftedHexBits = this.getShiftedHexBits(hexValue.substring(14, 16), this.convertHexToInteger('0x0f'), this.convertHexToInteger('0x40'));

// Variant Calculation: (i & 0x3f) | 0x80
// Variant Format: Always begins with 8, 9, A or B
String variantShiftedHexBits = this.getShiftedHexBits(hexValue.substring(18, 20), this.convertHexToInteger('0x3f'), this.convertHexToInteger('0x80'));

String uuidValue = hexValue.substring(0, 8) // time-low
+ hexValue.substring(8, 12) // time-mid
+ versionShiftedHexBits + hexValue.substring(14, 16) // time-high-and-version
+ variantShiftedHexBits + hexValue.substring(18, 20) // clock-seq-and-reserved + clock-seq-low
+ hexValue.substring(20); // node

return formatValue(uuidValue);
}

private String formatValue(String unformattedValue) {
final String invalidValueError = unformattedValue + ' is not a valid UUID value';

// Remove any non-alphanumeric characters
unformattedValue = unformattedValue.replaceAll('[^a-zA-Z0-9]', '');

// UUID Pattern: 8-4-4-4-12
String formattedValue = unformattedValue.substring(0, 8)
+ '-' + unformattedValue.substring(8, 12)
+ '-' + unformattedValue.substring(12, 16)
+ '-' + unformattedValue.substring(16, 20)
+ '-' + unformattedValue.substring(20);

return formattedValue.toLowerCase();
}

private String getShiftedHexBits(String hexSubstring, Integer lowerThreshold, Integer upperThreshold) {
Integer shiftedIntegerBits = (this.convertHexToInteger(hexSubstring) & lowerThreshold) | upperThreshold;
return this.convertIntegerToHex(shiftedIntegerBits);
}

private Integer convertHexToInteger(String hexValue) {
hexValue = hexValue.toLowerCase();

if(hexValue.startsWith(HEX_PREFIX)) hexValue = hexValue.substringAfter(HEX_PREFIX);

Integer integerValue = 0;
for(String hexCharacter : hexValue.split('')) {
Integer hexCharacterIndex = HEX_CHARACTERS.indexOf(hexCharacter);

integerValue = HEX_CHARACTERS.length() * integerValue + hexCharacterIndex;
}
return integerValue;
}

private String convertIntegerToHex(Integer integerValue) {
String hexValue = '';
while(integerValue > 0) {
Integer hexCharacterIndex = Math.mod(integerValue, HEX_CHARACTERS.length());

hexValue = HEX_CHARACTER_LIST[hexCharacterIndex] + hexValue;
integerValue = integerValue / HEX_CHARACTERS.length();
}
return hexValue;
}
}
}
10 changes: 10 additions & 0 deletions nebula-logger/tests/logger-engine/classes/Logger_Tests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,16 @@ private class Logger_Tests {
System.assertEquals(System.Request.getCurrent().getRequestId(), transactionId);
}

@isTest
static void it_should_generate_a_valid_uuid() {
Pattern pattern = Pattern.compile('[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}');

Logger.Uuid uuid = new Logger.Uuid();
System.assertEquals(36, uuid.getValue().length());
Matcher matcher = pattern.matcher(uuid.getValue());
System.assert(matcher.matches(), 'Generated UUID=' + uuid.getValue());
}

@isTest
static void it_should_set_transaction_entry_number() {
for (Integer i = 0; i < 10; i++) {
Expand Down

0 comments on commit 5acd00c

Please sign in to comment.