The logging API of the Log4j project providing a comprehensive and flexible logging framework for Java applications.
—
Extension points for custom logger implementations, context factories, and integration with different logging backends. Enables Log4j API to work with various implementations.
Anchor point for logging implementations that manages loggers and their lifecycle.
/**
* Anchor point for logging implementations; manages loggers
*/
public interface LoggerContext {
/** Get logger by name */
ExtendedLogger getLogger(String name);
/** Get logger with custom message factory */
ExtendedLogger getLogger(String name, MessageFactory messageFactory);
/** Get logger by class */
ExtendedLogger getLogger(Class<?> cls);
/** Check if logger exists */
boolean hasLogger(String name);
/** Check if logger exists with specific message factory */
boolean hasLogger(String name, MessageFactory messageFactory);
/** Check if logger exists by class */
boolean hasLogger(Class<?> cls);
/** Get external context object */
Object getExternalContext();
/** Get stored object by key */
Object getObject(String key);
/** Store object with key */
Object putObject(String key, Object value);
/** Remove stored object */
Object removeObject(String key);
/** Empty array constant */
LoggerContext[] EMPTY_ARRAY = new LoggerContext[0];
}Usage Examples:
public void demonstrateLoggerContext() {
// Get current logger context
LoggerContext context = LogManager.getContext();
// Get loggers from context
ExtendedLogger logger1 = context.getLogger("com.example.MyClass");
ExtendedLogger logger2 = context.getLogger(MyClass.class);
// Check logger existence
boolean exists = context.hasLogger("com.example.MyClass");
// Store and retrieve context-specific objects
context.putObject("config", new MyConfiguration());
MyConfiguration config = (MyConfiguration) context.getObject("config");
// External context (implementation-specific)
Object externalCtx = context.getExternalContext();
}
// Custom logger context implementation
public class CustomLoggerContext implements LoggerContext {
private final Map<String, ExtendedLogger> loggers = new ConcurrentHashMap<>();
private final Map<String, Object> objects = new ConcurrentHashMap<>();
private final Object externalContext;
public CustomLoggerContext(Object externalContext) {
this.externalContext = externalContext;
}
@Override
public ExtendedLogger getLogger(String name) {
return loggers.computeIfAbsent(name, this::createLogger);
}
@Override
public ExtendedLogger getLogger(String name, MessageFactory messageFactory) {
String key = name + "#" + messageFactory.getClass().getName();
return loggers.computeIfAbsent(key, k -> createLogger(name, messageFactory));
}
@Override
public boolean hasLogger(String name) {
return loggers.containsKey(name);
}
@Override
public Object getExternalContext() {
return externalContext;
}
@Override
public Object getObject(String key) {
return objects.get(key);
}
@Override
public Object putObject(String key, Object value) {
return objects.put(key, value);
}
@Override
public Object removeObject(String key) {
return objects.remove(key);
}
private ExtendedLogger createLogger(String name) {
return new CustomExtendedLogger(name);
}
private ExtendedLogger createLogger(String name, MessageFactory messageFactory) {
return new CustomExtendedLogger(name, messageFactory);
}
}Factory for creating and managing LoggerContext instances across different classloaders and configurations.
/**
* Factory for creating LoggerContext instances
*/
public interface LoggerContextFactory {
/**
* Get context with full control over selection criteria
* @param fqcn Fully qualified class name of the caller
* @param loader ClassLoader to associate with context
* @param externalContext External context object
* @param currentContext Whether to return current context
*/
LoggerContext getContext(String fqcn, ClassLoader loader,
Object externalContext, boolean currentContext);
/**
* Get context with configuration location and name
* @param fqcn Fully qualified class name of the caller
* @param loader ClassLoader to associate with context
* @param externalContext External context object
* @param currentContext Whether to return current context
* @param configLocation Configuration file location
* @param name Context name
*/
LoggerContext getContext(String fqcn, ClassLoader loader,
Object externalContext, boolean currentContext,
URI configLocation, String name);
/** Remove a context */
void removeContext(LoggerContext context);
/** Check if context exists */
boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext);
/**
* Shutdown contexts
* @param fqcn Fully qualified class name
* @param loader ClassLoader
* @param currentContext Whether to shutdown current context
* @param allContexts Whether to shutdown all contexts
*/
void shutdown(String fqcn, ClassLoader loader,
boolean currentContext, boolean allContexts);
/** Check if factory is dependent on ClassLoader */
boolean isClassLoaderDependent();
}Usage Examples:
// Custom logger context factory
public class CustomLoggerContextFactory implements LoggerContextFactory {
private final Map<String, LoggerContext> contexts = new ConcurrentHashMap<>();
@Override
public LoggerContext getContext(String fqcn, ClassLoader loader,
Object externalContext, boolean currentContext) {
String key = createContextKey(loader, externalContext);
return contexts.computeIfAbsent(key, k -> createContext(externalContext));
}
@Override
public LoggerContext getContext(String fqcn, ClassLoader loader,
Object externalContext, boolean currentContext,
URI configLocation, String name) {
String key = createContextKey(loader, externalContext, configLocation, name);
return contexts.computeIfAbsent(key, k -> createContext(externalContext, configLocation, name));
}
@Override
public void removeContext(LoggerContext context) {
contexts.values().remove(context);
}
@Override
public boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {
String key = createContextKey(loader, null);
return contexts.containsKey(key);
}
@Override
public void shutdown(String fqcn, ClassLoader loader,
boolean currentContext, boolean allContexts) {
if (allContexts) {
contexts.clear();
} else {
String key = createContextKey(loader, null);
contexts.remove(key);
}
}
@Override
public boolean isClassLoaderDependent() {
return true; // This factory creates different contexts per ClassLoader
}
private String createContextKey(ClassLoader loader, Object externalContext) {
return loader.toString() + "#" + (externalContext != null ? externalContext.toString() : "null");
}
private String createContextKey(ClassLoader loader, Object externalContext,
URI configLocation, String name) {
return createContextKey(loader, externalContext) + "#" + configLocation + "#" + name;
}
private LoggerContext createContext(Object externalContext) {
return new CustomLoggerContext(externalContext);
}
private LoggerContext createContext(Object externalContext, URI configLocation, String name) {
return new CustomLoggerContext(externalContext, configLocation, name);
}
}
// Usage in application
public class ContextFactoryUsage {
public void demonstrateContextFactory() {
LoggerContextFactory factory = new CustomLoggerContextFactory();
// Get context for current classloader
LoggerContext context1 = factory.getContext(
this.getClass().getName(),
Thread.currentThread().getContextClassLoader(),
null,
true
);
// Get context with specific configuration
URI configUri = URI.create("classpath:log4j2-test.xml");
LoggerContext context2 = factory.getContext(
this.getClass().getName(),
getClass().getClassLoader(),
"test-app",
false,
configUri,
"test-context"
);
// Check context existence
boolean exists = factory.hasContext(
this.getClass().getName(),
getClass().getClassLoader(),
true
);
// Shutdown specific context
factory.removeContext(context1);
// Shutdown all contexts
factory.shutdown(null, null, false, true);
}
}Extended logger interface providing additional methods for logger implementations.
/**
* Extended logger interface for implementations
*/
public interface ExtendedLogger extends Logger {
/**
* Log message if the specified level is enabled
* @param fqcn Fully qualified class name of the caller
* @param level Log level
* @param marker Marker
* @param message Message
* @param throwable Throwable
*/
void logIfEnabled(String fqcn, Level level, Marker marker,
String message, Throwable throwable);
/** Log with message supplier if level enabled */
void logIfEnabled(String fqcn, Level level, Marker marker,
Supplier<?> messageSupplier, Throwable throwable);
/** Log with message object if level enabled */
void logIfEnabled(String fqcn, Level level, Marker marker,
Object message, Throwable throwable);
/** Log with Message object if level enabled */
void logIfEnabled(String fqcn, Level level, Marker marker,
Message message, Throwable throwable);
/** Log parameterized message if level enabled */
void logIfEnabled(String fqcn, Level level, Marker marker,
String message, Object... params);
/** Log parameterized message with specific parameter count if level enabled */
void logIfEnabled(String fqcn, Level level, Marker marker,
String message, Object p0);
void logIfEnabled(String fqcn, Level level, Marker marker,
String message, Object p0, Object p1);
// ... additional overloads for performance
/** Always log regardless of level (for internal use) */
void logMessage(String fqcn, Level level, Marker marker,
Message message, Throwable throwable);
}Usage Examples:
// Custom extended logger implementation
public class CustomExtendedLogger extends AbstractLogger implements ExtendedLogger {
private final String name;
private final Level level;
public CustomExtendedLogger(String name) {
this.name = name;
this.level = Level.INFO; // Default level
}
@Override
public void logIfEnabled(String fqcn, Level level, Marker marker,
String message, Throwable throwable) {
if (isEnabled(level, marker)) {
logMessage(fqcn, level, marker,
getMessageFactory().newMessage(message), throwable);
}
}
@Override
public void logIfEnabled(String fqcn, Level level, Marker marker,
String message, Object... params) {
if (isEnabled(level, marker)) {
logMessage(fqcn, level, marker,
getMessageFactory().newMessage(message, params), null);
}
}
@Override
public void logMessage(String fqcn, Level level, Marker marker,
Message message, Throwable throwable) {
// Actual logging implementation
StringBuilder sb = new StringBuilder();
sb.append("[").append(level).append("] ");
sb.append(name).append(" - ");
if (marker != null) {
sb.append(marker.getName()).append(" ");
}
sb.append(message.getFormattedMessage());
System.out.println(sb.toString());
if (throwable != null) {
throwable.printStackTrace(System.out);
}
}
@Override
public boolean isEnabled(Level level, Marker marker) {
return level.isMoreSpecificThan(this.level);
}
@Override
public Level getLevel() {
return level;
}
@Override
public String getName() {
return name;
}
}
// Usage of extended logger features
public class ExtendedLoggerUsage {
private static final String FQCN = ExtendedLoggerUsage.class.getName();
public void demonstrateExtendedLogger() {
ExtendedLogger logger = (ExtendedLogger) LogManager.getLogger();
// Use extended logger methods directly
logger.logIfEnabled(FQCN, Level.INFO, null, "Direct extended logging", (Throwable) null);
// Parameterized logging with FQCN
logger.logIfEnabled(FQCN, Level.DEBUG, null, "User {} performed action {}", "alice", "login");
// With marker and throwable
Marker securityMarker = MarkerManager.getMarker("SECURITY");
Exception ex = new RuntimeException("test");
logger.logIfEnabled(FQCN, Level.ERROR, securityMarker, "Security violation", ex);
// Force logging regardless of level (use with caution)
Message forceMsg = new SimpleMessage("Forced log message");
logger.logMessage(FQCN, Level.TRACE, null, forceMsg, null);
}
}Abstract base class providing common logger functionality that implementations can extend.
/**
* Abstract base class for Logger implementations
*/
public abstract class AbstractLogger implements ExtendedLogger, Serializable {
/** Default message factory */
public static final MessageFactory DEFAULT_MESSAGE_FACTORY = ParameterizedMessageFactory.INSTANCE;
/** Default flow message factory */
public static final FlowMessageFactory DEFAULT_FLOW_MESSAGE_FACTORY = DefaultFlowMessageFactory.INSTANCE;
/** Get message factory for this logger */
@Override
public MessageFactory getMessageFactory() {
return DEFAULT_MESSAGE_FACTORY;
}
/** Get flow message factory for this logger */
@Override
public FlowMessageFactory getFlowMessageFactory() {
return DEFAULT_FLOW_MESSAGE_FACTORY;
}
// Abstract methods that implementations must provide
@Override
public abstract Level getLevel();
@Override
public abstract boolean isEnabled(Level level, Marker marker, String message, Throwable throwable);
@Override
public abstract boolean isEnabled(Level level, Marker marker, String message);
@Override
public abstract boolean isEnabled(Level level, Marker marker, String message, Object... params);
@Override
public abstract boolean isEnabled(Level level, Marker marker, Object message, Throwable throwable);
@Override
public abstract boolean isEnabled(Level level, Marker marker, Message message, Throwable throwable);
@Override
public abstract void logMessage(String fqcn, Level level, Marker marker,
Message message, Throwable throwable);
}Usage Examples:
// Implementing a custom logger by extending AbstractLogger
public class FileLogger extends AbstractLogger {
private final String name;
private final Level level;
private final PrintWriter writer;
public FileLogger(String name, Level level, String filename) throws IOException {
this.name = name;
this.level = level;
this.writer = new PrintWriter(new FileWriter(filename, true));
}
@Override
public String getName() {
return name;
}
@Override
public Level getLevel() {
return level;
}
@Override
public boolean isEnabled(Level level, Marker marker, String message, Throwable throwable) {
return level.isMoreSpecificThan(this.level);
}
@Override
public boolean isEnabled(Level level, Marker marker, String message) {
return level.isMoreSpecificThan(this.level);
}
@Override
public boolean isEnabled(Level level, Marker marker, String message, Object... params) {
return level.isMoreSpecificThan(this.level);
}
@Override
public boolean isEnabled(Level level, Marker marker, Object message, Throwable throwable) {
return level.isMoreSpecificThan(this.level);
}
@Override
public boolean isEnabled(Level level, Marker marker, Message message, Throwable throwable) {
return level.isMoreSpecificThan(this.level);
}
@Override
public void logMessage(String fqcn, Level level, Marker marker,
Message message, Throwable throwable) {
synchronized (writer) {
writer.printf("[%s] %s %s - %s%n",
new Date(), level, name, message.getFormattedMessage());
if (throwable != null) {
throwable.printStackTrace(writer);
}
writer.flush();
}
}
public void close() {
writer.close();
}
}
// Usage of custom logger
public class CustomLoggerUsage {
public void demonstrateCustomLogger() throws IOException {
// Create custom file logger
FileLogger fileLogger = new FileLogger("com.example.FileLogger", Level.DEBUG, "app.log");
// Use it like any other logger
fileLogger.info("Application started");
fileLogger.debug("Debug information: {}", "debug data");
fileLogger.error("An error occurred", new RuntimeException("test error"));
// Clean up
fileLogger.close();
}
}Service provider interfaces for custom ThreadContext implementations.
/**
* Interface for ThreadContext map implementations
*/
public interface ThreadContextMap {
/** Clear the context */
void clear();
/** Check if key exists */
boolean containsKey(String key);
/** Get value by key */
String get(String key);
/** Get copy of context as Map */
Map<String, String> getCopy();
/** Get immutable view of context */
Map<String, String> getImmutableMapOrNull();
/** Check if context is empty */
boolean isEmpty();
/** Put key-value pair */
void put(String key, String value);
/** Remove key */
void remove(String key);
}
/**
* Interface for ThreadContext stack implementations
*/
public interface ThreadContextStack extends ThreadContext.ContextStack {
/** Clear the stack */
void clear();
/** Check if stack contains value */
boolean contains(Object o);
/** Copy the stack */
ThreadContextStack copy();
/** Check if stack equals another */
boolean equals(Object obj);
/** Get stack depth */
int getDepth();
/** Get hash code */
int hashCode();
/** Check if stack is empty */
boolean isEmpty();
/** Get iterator */
Iterator<String> iterator();
/** Peek at top element */
String peek();
/** Pop top element */
String pop();
/** Push element */
void push(String message);
/** Get stack size */
int size();
/** Trim to specified depth */
void trim(int depth);
}Usage Examples:
// Custom ThreadContext map implementation
public class DatabaseThreadContextMap implements ThreadContextMap {
private final ThreadLocal<Map<String, String>> localMap =
ThreadLocal.withInitial(HashMap::new);
@Override
public void clear() {
localMap.get().clear();
// Could also persist to database here
}
@Override
public boolean containsKey(String key) {
return localMap.get().containsKey(key);
}
@Override
public String get(String key) {
return localMap.get().get(key);
}
@Override
public Map<String, String> getCopy() {
return new HashMap<>(localMap.get());
}
@Override
public Map<String, String> getImmutableMapOrNull() {
Map<String, String> map = localMap.get();
return map.isEmpty() ? null : Collections.unmodifiableMap(map);
}
@Override
public boolean isEmpty() {
return localMap.get().isEmpty();
}
@Override
public void put(String key, String value) {
localMap.get().put(key, value);
// Could also persist to database here
persistToDatabase(key, value);
}
@Override
public void remove(String key) {
localMap.get().remove(key);
// Could also remove from database here
removeFromDatabase(key);
}
private void persistToDatabase(String key, String value) {
// Database persistence logic
}
private void removeFromDatabase(String key) {
// Database removal logic
}
}
// Provider class for SPI registration
public class CustomProvider extends Provider {
public CustomProvider() {
super(10, "2.25.1"); // Priority and version
// Register custom implementations
put("LoggerContextFactory", "com.example.CustomLoggerContextFactory");
put("ThreadContextMap", "com.example.DatabaseThreadContextMap");
}
@Override
public String getVersionStr() {
return "2.25.1";
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-logging-log4j--log4j-api