/*
 * 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.util;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.status.StatusLogger;

Utility for preventing primitive parameter values from being auto-boxed. Auto-boxing creates temporary objects which contribute to pressure on the garbage collector. With this utility users can convert primitive values directly into text without allocating temporary objects.

Example usage:

import static org.apache.logging.log4j.util.Unbox.box;
...
long longValue = 123456L;
double doubleValue = 3.14;
// prevent primitive values from being auto-boxed
logger.debug("Long value={}, double value={}", box(longValue), box(doubleValue));

This class manages a small thread-local ring buffer of StringBuilders. Each time one of the box() methods is called, the next slot in the ring buffer is used, until the ring buffer is full and the first slot is reused. By default the Unbox ring buffer has 32 slots, so user code can have up to 32 boxed primitives in a single logger call.

If more slots are required, set system property log4j.unbox.ringbuffer.size to the desired ring buffer size. Note that the specified number will be rounded up to the nearest power of 2.

Since:2.6
/** * Utility for preventing primitive parameter values from being auto-boxed. Auto-boxing creates temporary objects * which contribute to pressure on the garbage collector. With this utility users can convert primitive values directly * into text without allocating temporary objects. * <p> * Example usage: * </p><pre> * import static org.apache.logging.log4j.util.Unbox.box; * ... * long longValue = 123456L; * double doubleValue = 3.14; * // prevent primitive values from being auto-boxed * logger.debug("Long value={}, double value={}", box(longValue), box(doubleValue)); * </pre> * <p> * This class manages a small thread-local ring buffer of StringBuilders. * Each time one of the {@code box()} methods is called, the next slot in the ring buffer is used, until the ring * buffer is full and the first slot is reused. By default the Unbox ring buffer has 32 slots, so user code can * have up to 32 boxed primitives in a single logger call. * </p> * <p> * If more slots are required, set system property {@code log4j.unbox.ringbuffer.size} to the desired ring buffer size. * Note that the specified number will be rounded up to the nearest power of 2. * </p> * @since 2.6 */
@PerformanceSensitive("allocation") public class Unbox { private static final Logger LOGGER = StatusLogger.getLogger(); private static final int BITS_PER_INT = 32; private static final int RINGBUFFER_MIN_SIZE = 32; private static final int RINGBUFFER_SIZE = calculateRingBufferSize("log4j.unbox.ringbuffer.size"); private static final int MASK = RINGBUFFER_SIZE - 1;
State implementation that only puts JDK classes in ThreadLocals, so this is safe to be used from web applications. Web application containers have thread pools that may hold on to ThreadLocal objects after the application was stopped. This may prevent the classes of the application from being unloaded, causing memory leaks.

Such memory leaks will not occur if only JDK classes are stored in ThreadLocals.

/** * State implementation that only puts JDK classes in ThreadLocals, so this is safe to be used from * web applications. Web application containers have thread pools that may hold on to ThreadLocal objects * after the application was stopped. This may prevent the classes of the application from being unloaded, * causing memory leaks. * <p> * Such memory leaks will not occur if only JDK classes are stored in ThreadLocals. * </p> */
private static class WebSafeState { private final ThreadLocal<StringBuilder[]> ringBuffer = new ThreadLocal<>(); private final ThreadLocal<int[]> current = new ThreadLocal<>(); public StringBuilder getStringBuilder() { StringBuilder[] array = ringBuffer.get(); if (array == null) { array = new StringBuilder[RINGBUFFER_SIZE]; for (int i = 0; i < array.length; i++) { array[i] = new StringBuilder(21); } ringBuffer.set(array); current.set(new int[1]); } final int[] index = current.get(); final StringBuilder result = array[MASK & index[0]++]; result.setLength(0); return result; } public boolean isBoxedPrimitive(final StringBuilder text) { final StringBuilder[] array = ringBuffer.get(); if (array == null) { return false; } for (int i = 0; i < array.length; i++) { if (text == array[i]) { return true; } } return false; } } private static class State { private final StringBuilder[] ringBuffer = new StringBuilder[RINGBUFFER_SIZE]; private int current; State() { for (int i = 0; i < ringBuffer.length; i++) { ringBuffer[i] = new StringBuilder(21); } } public StringBuilder getStringBuilder() { final StringBuilder result = ringBuffer[MASK & current++]; result.setLength(0); return result; } public boolean isBoxedPrimitive(final StringBuilder text) { for (int i = 0; i < ringBuffer.length; i++) { if (text == ringBuffer[i]) { return true; } } return false; } } private static ThreadLocal<State> threadLocalState = new ThreadLocal<>(); private static WebSafeState webSafeState = new WebSafeState(); private Unbox() { // this is a utility } private static int calculateRingBufferSize(final String propertyName) { final String userPreferredRBSize = PropertiesUtil.getProperties().getStringProperty(propertyName, String.valueOf(RINGBUFFER_MIN_SIZE)); try { int size = Integer.parseInt(userPreferredRBSize); if (size < RINGBUFFER_MIN_SIZE) { size = RINGBUFFER_MIN_SIZE; LOGGER.warn("Invalid {} {}, using minimum size {}.", propertyName, userPreferredRBSize, RINGBUFFER_MIN_SIZE); } return ceilingNextPowerOfTwo(size); } catch (final Exception ex) { LOGGER.warn("Invalid {} {}, using default size {}.", propertyName, userPreferredRBSize, RINGBUFFER_MIN_SIZE); return RINGBUFFER_MIN_SIZE; } }
Calculate the next power of 2, greater than or equal to x.

From Hacker's Delight, Chapter 3, Harry S. Warren Jr.

Params:
  • x – Value to round up
Returns:The next power of 2 from x inclusive
/** * Calculate the next power of 2, greater than or equal to x. * <p> * From Hacker's Delight, Chapter 3, Harry S. Warren Jr. * * @param x Value to round up * @return The next power of 2 from x inclusive */
private static int ceilingNextPowerOfTwo(final int x) { return 1 << (BITS_PER_INT - Integer.numberOfLeadingZeros(x - 1)); }
Returns a StringBuilder containing the text representation of the specified primitive value. This method will not allocate temporary objects.
Params:
  • value – the value whose text representation to return
Returns:a StringBuilder containing the text representation of the specified primitive value
/** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */
@PerformanceSensitive("allocation") public static StringBuilder box(final float value) { return getSB().append(value); }
Returns a StringBuilder containing the text representation of the specified primitive value. This method will not allocate temporary objects.
Params:
  • value – the value whose text representation to return
Returns:a StringBuilder containing the text representation of the specified primitive value
/** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */
@PerformanceSensitive("allocation") public static StringBuilder box(final double value) { return getSB().append(value); }
Returns a StringBuilder containing the text representation of the specified primitive value. This method will not allocate temporary objects.
Params:
  • value – the value whose text representation to return
Returns:a StringBuilder containing the text representation of the specified primitive value
/** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */
@PerformanceSensitive("allocation") public static StringBuilder box(final short value) { return getSB().append(value); }
Returns a StringBuilder containing the text representation of the specified primitive value. This method will not allocate temporary objects.
Params:
  • value – the value whose text representation to return
Returns:a StringBuilder containing the text representation of the specified primitive value
/** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */
@PerformanceSensitive("allocation") public static StringBuilder box(final int value) { return getSB().append(value); }
Returns a StringBuilder containing the text representation of the specified primitive value. This method will not allocate temporary objects.
Params:
  • value – the value whose text representation to return
Returns:a StringBuilder containing the text representation of the specified primitive value
/** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */
@PerformanceSensitive("allocation") public static StringBuilder box(final char value) { return getSB().append(value); }
Returns a StringBuilder containing the text representation of the specified primitive value. This method will not allocate temporary objects.
Params:
  • value – the value whose text representation to return
Returns:a StringBuilder containing the text representation of the specified primitive value
/** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */
@PerformanceSensitive("allocation") public static StringBuilder box(final long value) { return getSB().append(value); }
Returns a StringBuilder containing the text representation of the specified primitive value. This method will not allocate temporary objects.
Params:
  • value – the value whose text representation to return
Returns:a StringBuilder containing the text representation of the specified primitive value
/** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */
@PerformanceSensitive("allocation") public static StringBuilder box(final byte value) { return getSB().append(value); }
Returns a StringBuilder containing the text representation of the specified primitive value. This method will not allocate temporary objects.
Params:
  • value – the value whose text representation to return
Returns:a StringBuilder containing the text representation of the specified primitive value
/** * Returns a {@code StringBuilder} containing the text representation of the specified primitive value. * This method will not allocate temporary objects. * * @param value the value whose text representation to return * @return a {@code StringBuilder} containing the text representation of the specified primitive value */
@PerformanceSensitive("allocation") public static StringBuilder box(final boolean value) { return getSB().append(value); } private static State getState() { State state = threadLocalState.get(); if (state == null) { state = new State(); threadLocalState.set(state); } return state; } private static StringBuilder getSB() { return Constants.ENABLE_THREADLOCALS ? getState().getStringBuilder() : webSafeState.getStringBuilder(); }
For testing.
/** For testing. */
static int getRingbufferSize() { return RINGBUFFER_SIZE; } }