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

import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.logging.log4j.util.PerformanceSensitive;
import org.apache.logging.log4j.util.StringBuilderFormattable;

Applications create Markers by using the Marker Manager. All Markers created by this Manager are immutable.
/** * Applications create Markers by using the Marker Manager. All Markers created by this Manager are immutable. */
public final class MarkerManager { private static final ConcurrentMap<String, Marker> MARKERS = new ConcurrentHashMap<>(); private MarkerManager() { // do nothing }
Clears all markers.
/** * Clears all markers. */
public static void clear() { MARKERS.clear(); }
Tests existence of the given marker.
Params:
  • key – the marker name
Returns:true if the marker exists.
Since:2.4
/** * Tests existence of the given marker. * * @param key the marker name * @return true if the marker exists. * @since 2.4 */
public static boolean exists(final String key) { return MARKERS.containsKey(key); }
Retrieves a Marker or create a Marker that has no parent.
Params:
  • name – The name of the Marker.
Throws:
Returns:The Marker with the specified name.
/** * Retrieves a Marker or create a Marker that has no parent. * * @param name The name of the Marker. * @return The Marker with the specified name. * @throws IllegalArgumentException if the argument is {@code null} */
public static Marker getMarker(final String name) { Marker result = MARKERS.get(name); if (result == null) { MARKERS.putIfAbsent(name, new Log4jMarker(name)); result = MARKERS.get(name); } return result; }
Retrieves or creates a Marker with the specified parent. The parent must have been previously created.
Params:
  • name – The name of the Marker.
  • parent – The name of the parent Marker.
Throws:
Returns:The Marker with the specified name.
Deprecated:Use the Marker add or set methods to add parent Markers. Will be removed by final GA release.
/** * Retrieves or creates a Marker with the specified parent. The parent must have been previously created. * * @param name The name of the Marker. * @param parent The name of the parent Marker. * @return The Marker with the specified name. * @throws IllegalArgumentException if the parent Marker does not exist. * @deprecated Use the Marker add or set methods to add parent Markers. Will be removed by final GA release. */
@Deprecated public static Marker getMarker(final String name, final String parent) { final Marker parentMarker = MARKERS.get(parent); if (parentMarker == null) { throw new IllegalArgumentException("Parent Marker " + parent + " has not been defined"); } return getMarker(name, parentMarker); }
Retrieves or creates a Marker with the specified parent.
Params:
  • name – The name of the Marker.
  • parent – The parent Marker.
Throws:
Returns:The Marker with the specified name.
Deprecated:Use the Marker add or set methods to add parent Markers. Will be removed by final GA release.
/** * Retrieves or creates a Marker with the specified parent. * * @param name The name of the Marker. * @param parent The parent Marker. * @return The Marker with the specified name. * @throws IllegalArgumentException if any argument is {@code null} * @deprecated Use the Marker add or set methods to add parent Markers. Will be removed by final GA release. */
@Deprecated public static Marker getMarker(final String name, final Marker parent) { return getMarker(name).addParents(parent); }
Consider this class private, it is only public to satisfy Jackson for XML and JSON IO.

The actual Marker implementation.

Internal note: We could make this class package private instead of public if the class org.apache.logging.log4j.core.jackson.MarkerMixIn is moved to this package and would of course stay in its current module.

/** * <em>Consider this class private, it is only public to satisfy Jackson for XML and JSON IO.</em> * <p> * The actual Marker implementation. * </p> * <p> * <em>Internal note: We could make this class package private instead of public if the class * {@code org.apache.logging.log4j.core.jackson.MarkerMixIn} * is moved to this package and would of course stay in its current module.</em> * </p> */
public static class Log4jMarker implements Marker, StringBuilderFormattable { private static final long serialVersionUID = 100L; private final String name; private volatile Marker[] parents;
Required by JAXB and Jackson for XML and JSON IO.
/** * Required by JAXB and Jackson for XML and JSON IO. */
@SuppressWarnings("unused") private Log4jMarker() { this.name = null; this.parents = null; }
Constructs a new Marker.
Params:
  • name – the name of the Marker.
Throws:
/** * Constructs a new Marker. * * @param name the name of the Marker. * @throws IllegalArgumentException if the argument is {@code null} */
public Log4jMarker(final String name) { // we can't store null references in a ConcurrentHashMap as it is, not to mention that a null Marker // name seems rather pointless. To get an "anonymous" Marker, just use an empty string. requireNonNull(name, "Marker name cannot be null."); this.name = name; this.parents = null; } // TODO: use java.util.concurrent @Override public synchronized Marker addParents(final Marker... parentMarkers) { requireNonNull(parentMarkers, "A parent marker must be specified"); // It is not strictly necessary to copy the variable here but it should perform better than // Accessing a volatile variable multiple times. final Marker[] localParents = this.parents; // Don't add a parent that is already in the hierarchy. int count = 0; int size = parentMarkers.length; if (localParents != null) { for (final Marker parent : parentMarkers) { if (!(contains(parent, localParents) || parent.isInstanceOf(this))) { ++count; } } if (count == 0) { return this; } size = localParents.length + count; } final Marker[] markers = new Marker[size]; if (localParents != null) { // It's perfectly OK to call arraycopy in a synchronized context; it's still faster // noinspection CallToNativeMethodWhileLocked System.arraycopy(localParents, 0, markers, 0, localParents.length); } int index = localParents == null ? 0 : localParents.length; for (final Marker parent : parentMarkers) { if (localParents == null || !(contains(parent, localParents) || parent.isInstanceOf(this))) { markers[index++] = parent; } } this.parents = markers; return this; } @Override public synchronized boolean remove(final Marker parent) { requireNonNull(parent, "A parent marker must be specified"); final Marker[] localParents = this.parents; if (localParents == null) { return false; } final int localParentsLength = localParents.length; if (localParentsLength == 1) { if (localParents[0].equals(parent)) { parents = null; return true; } return false; } int index = 0; final Marker[] markers = new Marker[localParentsLength - 1]; // noinspection ForLoopReplaceableByForEach for (int i = 0; i < localParentsLength; i++) { final Marker marker = localParents[i]; if (!marker.equals(parent)) { if (index == localParentsLength - 1) { // no need to swap array return false; } markers[index++] = marker; } } parents = markers; return true; } @Override public Marker setParents(final Marker... markers) { if (markers == null || markers.length == 0) { this.parents = null; } else { final Marker[] array = new Marker[markers.length]; System.arraycopy(markers, 0, array, 0, markers.length); this.parents = array; } return this; } @Override public String getName() { return this.name; } @Override public Marker[] getParents() { if (this.parents == null) { return null; } return Arrays.copyOf(this.parents, this.parents.length); } @Override public boolean hasParents() { return this.parents != null; } @Override @PerformanceSensitive({"allocation", "unrolled"}) public boolean isInstanceOf(final Marker marker) { requireNonNull(marker, "A marker parameter is required"); if (this == marker) { return true; } final Marker[] localParents = parents; if (localParents != null) { // With only one or two parents the for loop is slower. final int localParentsLength = localParents.length; if (localParentsLength == 1) { return checkParent(localParents[0], marker); } if (localParentsLength == 2) { return checkParent(localParents[0], marker) || checkParent(localParents[1], marker); } // noinspection ForLoopReplaceableByForEach for (int i = 0; i < localParentsLength; i++) { final Marker localParent = localParents[i]; if (checkParent(localParent, marker)) { return true; } } } return false; } @Override @PerformanceSensitive({"allocation", "unrolled"}) public boolean isInstanceOf(final String markerName) { requireNonNull(markerName, "A marker name is required"); if (markerName.equals(this.getName())) { return true; } // Use a real marker for child comparisons. It is faster than comparing the names. final Marker marker = MARKERS.get(markerName); if (marker == null) { return false; } final Marker[] localParents = parents; if (localParents != null) { final int localParentsLength = localParents.length; if (localParentsLength == 1) { return checkParent(localParents[0], marker); } if (localParentsLength == 2) { return checkParent(localParents[0], marker) || checkParent(localParents[1], marker); } // noinspection ForLoopReplaceableByForEach for (int i = 0; i < localParentsLength; i++) { final Marker localParent = localParents[i]; if (checkParent(localParent, marker)) { return true; } } } return false; } @PerformanceSensitive({"allocation", "unrolled"}) private static boolean checkParent(final Marker parent, final Marker marker) { if (parent == marker) { return true; } final Marker[] localParents = parent instanceof Log4jMarker ? ((Log4jMarker) parent).parents : parent .getParents(); if (localParents != null) { final int localParentsLength = localParents.length; if (localParentsLength == 1) { return checkParent(localParents[0], marker); } if (localParentsLength == 2) { return checkParent(localParents[0], marker) || checkParent(localParents[1], marker); } // noinspection ForLoopReplaceableByForEach for (int i = 0; i < localParentsLength; i++) { final Marker localParent = localParents[i]; if (checkParent(localParent, marker)) { return true; } } } return false; } /* * Called from add while synchronized. */ @PerformanceSensitive("allocation") private static boolean contains(final Marker parent, final Marker... localParents) { // performance tests showed a normal for loop is slightly faster than a for-each loop on some platforms // noinspection ForLoopReplaceableByForEach for (int i = 0, localParentsLength = localParents.length; i < localParentsLength; i++) { final Marker marker = localParents[i]; if (marker == parent) { return true; } } return false; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || !(o instanceof Marker)) { return false; } final Marker marker = (Marker) o; return name.equals(marker.getName()); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { // FIXME: might want to use an initial capacity; the default is 16 (or str.length() + 16) final StringBuilder sb = new StringBuilder(); formatTo(sb); return sb.toString(); } @Override public void formatTo(final StringBuilder sb) { sb.append(name); final Marker[] localParents = parents; if (localParents != null) { addParentInfo(sb, localParents); } } @PerformanceSensitive("allocation") private static void addParentInfo(final StringBuilder sb, final Marker... parents) { sb.append("[ "); boolean first = true; // noinspection ForLoopReplaceableByForEach for (int i = 0, parentsLength = parents.length; i < parentsLength; i++) { final Marker marker = parents[i]; if (!first) { sb.append(", "); } first = false; sb.append(marker.getName()); final Marker[] p = marker instanceof Log4jMarker ? ((Log4jMarker) marker).parents : marker.getParents(); if (p != null) { addParentInfo(sb, p); } } sb.append(" ]"); } } // this method wouldn't be necessary if Marker methods threw an NPE instead of an IAE for null values ;) private static void requireNonNull(final Object obj, final String message) { if (obj == null) { throw new IllegalArgumentException(message); } } }