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.LogAdapter$Log4jLog.<clinit>(LogAdapter.java:155)
at org.apache.commons.logging.LogAdapter$Log4jAdapter.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)
/* * 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); } }