|
21 | 21 | import java.util.Random;
|
22 | 22 | import java.util.UUID;
|
23 | 23 | import java.util.concurrent.atomic.AtomicInteger;
|
| 24 | +import org.apache.logging.log4j.core.LogEvent; |
24 | 25 | import org.apache.logging.log4j.core.impl.CoreProperties.UuidProperties;
|
25 | 26 | import org.apache.logging.log4j.kit.env.PropertyEnvironment;
|
26 | 27 |
|
@@ -50,6 +51,7 @@ public final class UuidUtil {
|
50 | 51 | private static final int HUNDRED_NANOS_PER_MILLI = 10000;
|
51 | 52 |
|
52 | 53 | private static final long LEAST = initialize(NetUtils.getMacAddress());
|
| 54 | + private static final long SALT = new SecureRandom().nextLong(); |
53 | 55 |
|
54 | 56 | /* This class cannot be instantiated */
|
55 | 57 | private UuidUtil() {}
|
@@ -140,4 +142,47 @@ public static UUID getTimeBasedUuid() {
|
140 | 142 | final long most = timeLow | timeMid | TYPE1 | timeHi;
|
141 | 143 | return new UUID(most, LEAST);
|
142 | 144 | }
|
| 145 | + |
| 146 | + /** |
| 147 | + * Generates a Type 4 UUID based on the deterministic LogEvent hash. |
| 148 | + * Meant for generating consistent, correlatable UUID values across multiple Appenders for the same LogEvent. |
| 149 | + * |
| 150 | + * @param logEvent |
| 151 | + * @return universally unique identifiers (UUID) |
| 152 | + */ |
| 153 | + public static UUID getLogEventBasedUuid(LogEvent logEvent) { |
| 154 | + // TODO: better hashing algorithm - include other LogEvent fields? |
| 155 | + long epochSecond = logEvent.getInstant().getEpochSecond(); |
| 156 | + // Enable 'log4j.configuration.usePreciseClock' system property otherwise will be truncated to millis |
| 157 | + long nanoOfSecond = logEvent.getInstant().getNanoOfSecond(); |
| 158 | + // Thread IDs typically increment from 0 producing a narrow range |
| 159 | + long threadId = logEvent.getThreadId(); |
| 160 | + |
| 161 | + // Increase entropy |
| 162 | + long most = mix(epochSecond, nanoOfSecond, threadId); |
| 163 | + long least = mix(threadId, ~nanoOfSecond, epochSecond); |
| 164 | + // Set UUID v4 bits |
| 165 | + most &= 0xFFFFFFFFFFFF0FFFL; |
| 166 | + most |= 0x0000000000004000L; |
| 167 | + least &= 0x3FFFFFFFFFFFFFFFL; |
| 168 | + least |= 0x8000000000000000L; |
| 169 | + |
| 170 | + return new UUID(most, least); |
| 171 | + } |
| 172 | + |
| 173 | + private static long mix(long v1, long v2, long v3) { |
| 174 | + // XOR with large primes |
| 175 | + long hash = v1 * 0x9E3779B97F4A7C15L; |
| 176 | + hash ^= (v2 * 0xC6BC279692B5C323L); |
| 177 | + hash ^= (v3 * 0x3243F6A8885A308DL); |
| 178 | + // Scramble |
| 179 | + hash ^= (hash >>> 33); |
| 180 | + hash *= 0xff51afd7ed558ccdL; |
| 181 | + hash ^= (hash >>> 33); |
| 182 | + hash *= 0xc4ceb9fe1a85ec53L; |
| 183 | + hash ^= (hash >>> 33); |
| 184 | + // Add salt |
| 185 | + hash ^= SALT; |
| 186 | + return hash; |
| 187 | + } |
143 | 188 | }
|
0 commit comments