java log4j 的一个bug
java项目中使用log4j记录日志几乎成了标配,
最近一个项目中出了个问题 现象是这样的: 不连vpn程序一切正常,连上VPN启动程序 直接异常退出,
错误日志直接指向了 log4j 库
org.apache.logging.log4j.core.util.UuidUtil.clinit
就是说在 UuidUtil 这个类初始化时出了问题
最终错误在此处
数组index超界
原因是在我的机器上不连vpn mac是一个长度为6的数组 连vpn后长度为8 了
mac长度为8 则 length=6 index=2
则等价于:
mac=new byte[8];
node=new byte[8];
System.arraycopy(mac, 2, node, 2+ 2, 6);
于是java.lang.ArrayIndexOutOfBoundsException
作者的意图我想是:
如果mac长度小于等6 则将其复制到node从2开始的后面几位
如果mac长度大于 6 则复制最后六位到node从2开始的后6位
则应该写成:
System.arraycopy(mac,index, node,2,length);
坑!
既然知道原因了 那么解决方案也很简单,直接修改UuidUtil.java 即可
最后附上异常信息和原类:
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.apache.logging.log4j.core.util.WatchManager.<init>(WatchManager.java:53)
at org.apache.logging.log4j.core.config.AbstractConfiguration.<init>(AbstractConfiguration.java:135)
at org.apache.logging.log4j.core.config.NullConfiguration.<init>(NullConfiguration.java:32)
at org.apache.logging.log4j.core.LoggerContext.<clinit>(LoggerContext.java:85)
at org.apache.logging.log4j.core.selector.ClassLoaderContextSelector.createContext(ClassLoaderContextSelector.java:179)
at org.apache.logging.log4j.core.selector.ClassLoaderContextSelector.locateContext(ClassLoaderContextSelector.java:153)
at org.apache.logging.log4j.core.selector.ClassLoaderContextSelector.getContext(ClassLoaderContextSelector.java:78)
at org.apache.logging.log4j.core.selector.ClassLoaderContextSelector.getContext(ClassLoaderContextSelector.java:65)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:148)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:45)
at org.apache.logging.log4j.LogManager.getContext(LogManager.java:194)
at org.apache.commons.logging.LogAdapterLog4jAdapter.createLog(LogAdapter.java:122)
at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:89)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
Caused by: java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at org.apache.logging.log4j.core.util.UuidUtil.<clinit>(UuidUtil.java:81)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache license, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the license for the specific language governing permissions and * limitations under the license. */ package org.apache.logging.log4j.core.util; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.security.SecureRandom; import java.util.Enumeration; import java.util.Random; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.PropertiesUtil; /** * Generates a unique ID. The generated UUID will be unique for approximately 8,925 years so long as * less than 10,000 IDs are generated per millisecond on the same device (as identified by its MAC address). */ public final class UuidUtil { /** * System property that may be used to seed the UUID generation with an integer value. */ public static final String UUID_SEQUENCE = "org.apache.logging.log4j.uuidSequence" ; private static final Logger LOGGER = StatusLogger.getLogger(); private static final String ASSIGNED_SEQUENCES = "org.apache.logging.log4j.assignedSequences" ; private static final AtomicInteger COUNT = new AtomicInteger( 0 ); private static final long TYPE1 = 0x1000L; private static final byte VARIANT = ( byte ) 0x80 ; private static final int SEQUENCE_MASK = 0x3FFF ; private static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L; private static final long INITIAL_UUID_SEQNO = PropertiesUtil.getProperties().getLongProperty(UUID_SEQUENCE, 0 ); private static final long LEAST; private static final long LOW_MASK = 0xffffffffL; private static final long MID_MASK = 0xffff00000000L; private static final long HIGH_MASK = 0xfff000000000000L; private static final int NODE_SIZE = 8 ; private static final int SHIFT_2 = 16 ; private static final int SHIFT_4 = 32 ; private static final int SHIFT_6 = 48 ; private static final int HUNDRED_NANOS_PER_MILLI = 10000 ; static { byte [] mac = NetUtils.getMacAddress(); final Random randomGenerator = new SecureRandom(); if (mac == null || mac.length == 0 ) { mac = new byte [ 6 ]; randomGenerator.nextBytes(mac); } final int length = mac.length >= 6 ? 6 : mac.length; final int index = mac.length >= 6 ? mac.length - 6 : 0 ; final byte [] node = new byte [NODE_SIZE]; node[ 0 ] = VARIANT; node[ 1 ] = 0 ; for ( int i = 2 ; i < NODE_SIZE; ++i) { node[i] = 0 ; } System.arraycopy(mac, index, node, index + 2 , length); final ByteBuffer buf = ByteBuffer.wrap(node); long rand = INITIAL_UUID_SEQNO; String assigned = PropertiesUtil.getProperties().getStringProperty(ASSIGNED_SEQUENCES); long [] sequences; if (assigned == null ) { sequences = new long [ 0 ]; } else { final String[] array = assigned.split(Patterns.COMMA_SEPARATOR); sequences = new long [array.length]; int i = 0 ; for ( final String value : array) { sequences[i] = Long.parseLong(value); ++i; } } if (rand == 0 ) { rand = randomGenerator.nextLong(); } rand &= SEQUENCE_MASK; boolean duplicate; do { duplicate = false ; for ( final long sequence : sequences) { if (sequence == rand) { duplicate = true ; break ; } } if (duplicate) { rand = (rand + 1 ) & SEQUENCE_MASK; } } while (duplicate); assigned = assigned == null ? Long.toString(rand) : assigned + ',' + Long.toString(rand); System.setProperty(ASSIGNED_SEQUENCES, assigned); LEAST = buf.getLong() | rand << SHIFT_6; } /* This class cannot be instantiated */ private UuidUtil() { } /** * Generates Type 1 UUID. The time contains the number of 100NS intervals that have occurred * since 00:00:00.00 UTC, 10 October 1582. Each UUID on a particular machine is unique to the 100NS interval * until they rollover around 3400 A.D. * <ol> * <li>Digits 1-12 are the lower 48 bits of the number of 100 ns increments since the start of the UUID * epoch.</li> * <li>Digit 13 is the version (with a value of 1).</li> * <li>Digits 14-16 are a sequence number that is incremented each time a UUID is generated.</li> * <li>Digit 17 is the variant (with a value of binary 10) and 10 bits of the sequence number</li> * <li>Digit 18 is final 16 bits of the sequence number.</li> * <li>Digits 19-32 represent the system the application is running on.</li> * </ol> * * @return universally unique identifiers (UUID) */ public static UUID getTimeBasedUuid() { final long time = ((System.currentTimeMillis() * HUNDRED_NANOS_PER_MILLI) + NUM_100NS_INTERVALS_SINCE_UUID_EPOCH) + (COUNT.incrementAndGet() % HUNDRED_NANOS_PER_MILLI); final long timeLow = (time & LOW_MASK) << SHIFT_4; final long timeMid = (time & MID_MASK) >> SHIFT_2; final long timeHi = (time & HIGH_MASK) >> SHIFT_6; final long most = timeLow | timeMid | TYPE1 | timeHi; return new UUID(most, LEAST); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】