Source Code for Apache Log4j Core Implementation

Apache Log4j Core Implementation provides the functional components of the logging system. Users are free to create their own plugins and include them in the logging configuration. Apache Log4j Core is a required module to use Apache Log4j.

Bytecode (Java 8) for Apache Log4j Core Implementation is provided in a separate JAR file like log4j-core-2.14.1.jar.

Source Code files for Apache Log4j API are provided in both binary packge like apache-log4j-2.14.1-bin.zip and source package like apache-log4j-2.14.1-src.zip. You can download them at Apache Log4j Website.

You can also browse Source Code files for Apache Log4j Core Implementation 2.14.1 below.

✍: FYIcenter.com

org/apache/logging/log4j/core/pattern/CachedDateFormat.java

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

import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.Date;
import java.util.TimeZone;

import org.apache.logging.log4j.core.util.Constants;


/**
 * CachedDateFormat optimizes the performance of a wrapped
 * DateFormat.  The implementation is not thread-safe.
 * If the millisecond pattern is not recognized,
 * the class will only use the cache if the
 * same value is requested.
 */
final class CachedDateFormat extends DateFormat {

    /**
     * Constant used to represent that there was no change
     * observed when changing the millisecond count.
     */
    public static final int NO_MILLISECONDS = -2;

    /**
     * Constant used to represent that there was an
     * observed change, but was an expected change.
     */
    public static final int UNRECOGNIZED_MILLISECONDS = -1;

    private static final long serialVersionUID = -1253877934598423628L;

    /**
     * Supported digit set.  If the wrapped DateFormat uses
     * a different unit set, the millisecond pattern
     * will not be recognized and duplicate requests
     * will use the cache.
     */
    private static final String DIGITS = "0123456789";

    /**
     * First magic number used to detect the millisecond position.
     */
    private static final int MAGIC1 = 654;

    /**
     * Expected representation of first magic number.
     */
    private static final String MAGICSTRING1 = "654";

    /**
     * Second magic number used to detect the millisecond position.
     */
    private static final int MAGIC2 = 987;

    /**
     * Expected representation of second magic number.
     */
    private static final String MAGICSTRING2 = "987";

    /**
     * Expected representation of 0 milliseconds.
     */
    private static final String ZERO_STRING = "000";

    private static final int BUF_SIZE = 50;

    private static final int DEFAULT_VALIDITY = 1000;

    private static final int THREE_DIGITS = 100;

    private static final int TWO_DIGITS = 10;

    private static final long SLOTS = 1000L;

    /**
     * Wrapped formatter.
     */
    private final DateFormat formatter;

    /**
     * Index of initial digit of millisecond pattern or
     * UNRECOGNIZED_MILLISECONDS or NO_MILLISECONDS.
     */
    private int millisecondStart;

    /**
     * Integral second preceding the previous converted Date.
     */
    private long slotBegin;

    /**
     * Cache of previous conversion.
     */
    private final StringBuffer cache = new StringBuffer(BUF_SIZE);

    /**
     * Maximum validity period for the cache.
     * Typically 1, use cache for duplicate requests only, or
     * 1000, use cache for requests within the same integral second.
     */
    private final int expiration;

    /**
     * Date requested in previous conversion.
     */
    private long previousTime;

    /**
     * Scratch date object used to minimize date object creation.
     */
    private final Date tmpDate = new Date(0);

    /**
     * Creates a new CachedDateFormat object.
     *
     * @param dateFormat Date format, may not be null.
     * @param expiration maximum cached range in milliseconds.
     *                   If the dateFormat is known to be incompatible with the
     *                   caching algorithm, use a value of 0 to totally disable
     *                   caching or 1 to only use cache for duplicate requests.
     */
    public CachedDateFormat(final DateFormat dateFormat, final int expiration) {
        if (dateFormat == null) {
            throw new IllegalArgumentException("dateFormat cannot be null");
        }

        if (expiration < 0) {
            throw new IllegalArgumentException("expiration must be non-negative");
        }

        formatter = dateFormat;
        this.expiration = expiration;
        millisecondStart = 0;

        //
        //   set the previousTime so the cache will be invalid
        //        for the next request.
        previousTime = Long.MIN_VALUE;
        slotBegin = Long.MIN_VALUE;
    }

    /**
     * Finds start of millisecond field in formatted time.
     *
     * @param time      long time, must be integral number of seconds
     * @param formatted String corresponding formatted string
     * @param formatter DateFormat date format
     * @return int position in string of first digit of milliseconds,
     *         -1 indicates no millisecond field, -2 indicates unrecognized
     *         field (likely RelativeTimeDateFormat)
     */
    public static int findMillisecondStart(final long time, final String formatted, final DateFormat formatter) {
        long slotBegin = (time / Constants.MILLIS_IN_SECONDS) * Constants.MILLIS_IN_SECONDS;

        if (slotBegin > time) {
            slotBegin -= Constants.MILLIS_IN_SECONDS;
        }

        final int millis = (int) (time - slotBegin);

        int magic = MAGIC1;
        String magicString = MAGICSTRING1;

        if (millis == MAGIC1) {
            magic = MAGIC2;
            magicString = MAGICSTRING2;
        }

        final String plusMagic = formatter.format(new Date(slotBegin + magic));

        /**
         *   If the string lengths differ then
         *      we can't use the cache except for duplicate requests.
         */
        if (plusMagic.length() != formatted.length()) {
            return UNRECOGNIZED_MILLISECONDS;
        }
        // find first difference between values
        for (int i = 0; i < formatted.length(); i++) {
            if (formatted.charAt(i) != plusMagic.charAt(i)) {
                //
                //   determine the expected digits for the base time
                final StringBuffer formattedMillis = new StringBuffer("ABC");
                millisecondFormat(millis, formattedMillis, 0);

                final String plusZero = formatter.format(new Date(slotBegin));

                //   If the next 3 characters match the magic
                //      string and the expected string
                if (
                    (plusZero.length() == formatted.length())
                        && magicString.regionMatches(
                        0, plusMagic, i, magicString.length())
                        && formattedMillis.toString().regionMatches(
                        0, formatted, i, magicString.length())
                        && ZERO_STRING.regionMatches(
                        0, plusZero, i, ZERO_STRING.length())) {
                    return i;
                }
                return UNRECOGNIZED_MILLISECONDS;
            }
        }

        return NO_MILLISECONDS;
    }

    /**
     * Formats a Date into a date/time string.
     *
     * @param date          the date to format.
     * @param sbuf          the string buffer to write to.
     * @param fieldPosition remains untouched.
     * @return the formatted time string.
     */
    @Override
    public StringBuffer format(final Date date, final StringBuffer sbuf, final FieldPosition fieldPosition) {
        format(date.getTime(), sbuf);

        return sbuf;
    }

    /**
     * Formats a millisecond count into a date/time string.
     *
     * @param now Number of milliseconds after midnight 1 Jan 1970 GMT.
     * @param buf the string buffer to write to.
     * @return the formatted time string.
     */
    public StringBuffer format(final long now, final StringBuffer buf) {
        //
        // If the current requested time is identical to the previously
        //     requested time, then append the cache contents.
        //
        if (now == previousTime) {
            buf.append(cache);

            return buf;
        }

        //
        //   If millisecond pattern was not unrecognized
        //     (that is if it was found or milliseconds did not appear)
        //
        if (millisecondStart != UNRECOGNIZED_MILLISECONDS &&
            //    Check if the cache is still valid.
            //    If the requested time is within the same integral second
            //       as the last request and a shorter expiration was not requested.
            (now < (slotBegin + expiration)) && (now >= slotBegin) && (now < (slotBegin + SLOTS))) {
            //
            //    if there was a millisecond field then update it
            //
            if (millisecondStart >= 0) {
                millisecondFormat((int) (now - slotBegin), cache, millisecondStart);
            }

            //
            //   update the previously requested time
            //      (the slot begin should be unchanged)
            previousTime = now;
            buf.append(cache);

            return buf;
        }

        //
        //  could not use previous value.
        //    Call underlying formatter to format date.
        cache.setLength(0);
        tmpDate.setTime(now);
        cache.append(formatter.format(tmpDate));
        buf.append(cache);
        previousTime = now;
        slotBegin = (previousTime / Constants.MILLIS_IN_SECONDS) * Constants.MILLIS_IN_SECONDS;

        if (slotBegin > previousTime) {
            slotBegin -= Constants.MILLIS_IN_SECONDS;
        }

        //
        //    if the milliseconds field was previous found
        //       then reevaluate in case it moved.
        //
        if (millisecondStart >= 0) {
            millisecondStart =
                findMillisecondStart(now, cache.toString(), formatter);
        }

        return buf;
    }

    /**
     * Formats a count of milliseconds (0-999) into a numeric representation.
     *
     * @param millis Millisecond count between 0 and 999.
     * @param buf    String buffer, may not be null.
     * @param offset Starting position in buffer, the length of the
     *               buffer must be at least offset + 3.
     */
    private static void millisecondFormat(
        final int millis, final StringBuffer buf, final int offset) {
        buf.setCharAt(offset, DIGITS.charAt(millis / THREE_DIGITS));
        buf.setCharAt(offset + 1, DIGITS.charAt((millis / TWO_DIGITS) % TWO_DIGITS));
        buf.setCharAt(offset + 2, DIGITS.charAt(millis % TWO_DIGITS));
    }

    /**
     * Sets the time zone.
     * <p>
     * Setting the time zone using getCalendar().setTimeZone() will likely cause caching to misbehave.
     * </p>
     *
     * @param timeZone
     *        TimeZone new time zone
     */
    @Override
    public void setTimeZone(final TimeZone timeZone) {
        formatter.setTimeZone(timeZone);
        previousTime = Long.MIN_VALUE;
        slotBegin = Long.MIN_VALUE;
    }

    /**
     * This method is delegated to the formatter which most
     * likely returns null.
     *
     * @param s   string representation of date.
     * @param pos field position, unused.
     * @return parsed date, likely null.
     */
    @Override
    public Date parse(final String s, final ParsePosition pos) {
        return formatter.parse(s, pos);
    }

    /**
     * Gets number formatter.
     *
     * @return NumberFormat number formatter
     */
    @Override
    public NumberFormat getNumberFormat() {
        return formatter.getNumberFormat();
    }

    /**
     * Gets maximum cache validity for the specified SimpleDateTime
     * conversion pattern.
     *
     * @param pattern conversion pattern, may not be null.
     * @return Duration in milliseconds from an integral second
     *         that the cache will return consistent results.
     */
    public static int getMaximumCacheValidity(final String pattern) {
        //
        //   If there are more "S" in the pattern than just one "SSS" then
        //      (for example, "HH:mm:ss,SSS SSS"), then set the expiration to
        //      one millisecond which should only perform duplicate request caching.
        //
        final int firstS = pattern.indexOf('S');

        if ((firstS >= 0) && (firstS != pattern.lastIndexOf("SSS"))) {
            return 1;
        }

        return DEFAULT_VALIDITY;
    }
}

org/apache/logging/log4j/core/pattern/CachedDateFormat.java

 

Or download all of them as a single archive file:

File name: log4j-core-2.14.1-sources.jar
File size: 1281358 bytes
Release date: 2021-03-06
Download 

 

Source Code for Apache Log4j JDK Logging Adapter

Source Code for Apache Log4j API

Downloading and Reviewing Apache Log4j Packages

⇑⇑ FAQ for Apache Log4j

2015-11-03, 92342👍, 0💬