java.util.logging (JUL)

Built-in logging API included in Java standard library. Available without external dependencies, providing basic logging functionality. Equipped with standard features like log levels, handlers, and formatters. Suitable for small-scale applications.

loggingJavastandard-librarybuilt-inlightweight

Library

Java Util Logging (JUL)

Overview

Java Util Logging (JUL) is the official logging framework built into the Java standard library since Java 1.4. Provided through the java.util.logging package, it offers a lightweight solution for logging functionality without external dependencies. It provides flexible log control through a hierarchical structure of Logger, Handler, and Formatter components, supporting both configuration files and programmatic configuration. Despite its simple API, it includes features that meet basic logging requirements for enterprise applications.

Details

JUL has been provided as the standard logging solution for the Java platform since the Java 1.4 release in 2002, and continues to be used in many Java applications as of 2025. It offers basic yet comprehensive logging functionality including log level control through Logger hierarchy (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST), output destination control through various Handlers (file, console, memory, socket, etc.), log format control through custom Formatters, and centralized management through configuration files (logging.properties). As part of the Java standard library, it provides high compatibility and stability.

Key Features

  • Standard Library Integration: Built into the Java platform with no external dependencies
  • Hierarchical Logger Structure: Logger hierarchy management corresponding to package hierarchy
  • Seven Log Levels: SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST
  • Various Handlers: Support for Console, File, Memory, Socket, Stream, and other output destinations
  • Configuration File Management: Centralized configuration management through logging.properties
  • Internationalization Support: Message internationalization using ResourceBundle

Advantages and Disadvantages

Advantages

  • Easy integration as part of Java standard library with no external dependencies
  • Seamless JVM integration and high compatibility with the Java platform
  • Runtime log level changes possible through configuration files
  • Fine-grained log control through Logger hierarchy
  • Flexible output destination control through Handler combinations
  • Lightweight with sufficient functionality for basic logging requirements

Disadvantages

  • Limited functionality compared to specialized libraries like Log4j2 or Logback
  • Configuration complexity and difficulty can be a barrier for beginners
  • Performance inferior to other modern logging libraries
  • Basic log rotation functionality with difficult advanced control
  • Less community support and update frequency compared to other libraries
  • Operational difficulties in large-scale systems due to insufficient functionality

Reference Pages

Code Examples

Basic Setup

import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.SimpleFormatter;

public class BasicLoggingSetup {
    // Get Logger based on class name
    private static final Logger logger = Logger.getLogger(BasicLoggingSetup.class.getName());
    
    public static void main(String[] args) {
        try {
            // Configure console handler
            ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setLevel(Level.ALL);
            consoleHandler.setFormatter(new SimpleFormatter());
            
            // Configure file handler
            FileHandler fileHandler = new FileHandler("application.log", true);
            fileHandler.setLevel(Level.INFO);
            fileHandler.setFormatter(new SimpleFormatter());
            
            // Add handlers to Logger
            logger.addHandler(consoleHandler);
            logger.addHandler(fileHandler);
            logger.setLevel(Level.ALL);
            
            // Disable propagation to parent Logger (prevent duplicate output)
            logger.setUseParentHandlers(false);
            
            logger.info("Java Util Logging initialized successfully");
            
        } catch (Exception e) {
            System.err.println("Logging configuration error: " + e.getMessage());
        }
    }
}

Basic Log Output

import java.util.logging.Logger;
import java.util.logging.Level;

public class BasicLogging {
    private static final Logger logger = Logger.getLogger(BasicLogging.class.getName());
    
    public void demonstrateBasicLogging() {
        // Output using seven log levels
        logger.severe("SEVERE: A fatal error has occurred");
        logger.warning("WARNING: This is a warning message");
        logger.info("INFO: This is a general information message");
        logger.config("CONFIG: This is a configuration message");
        logger.fine("FINE: This is detailed debug information");
        logger.finer("FINER: This is more detailed debug information");
        logger.finest("FINEST: This is the most detailed debug information");
        
        // Level.OFF is not output
        logger.log(Level.OFF, "This message will not be output");
        
        // Parameterized logging
        String userName = "John Doe";
        int userId = 12345;
        logger.log(Level.INFO, "User {0} (ID: {1}) has logged in", 
                  new Object[]{userName, userId});
        
        // Logging with exception information
        try {
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            logger.log(Level.SEVERE, "Arithmetic error occurred", e);
        }
        
        // Conditional logging (performance optimization)
        if (logger.isLoggable(Level.FINE)) {
            String expensiveDebugInfo = calculateExpensiveDebugInfo();
            logger.fine("Detailed debug information: " + expensiveDebugInfo);
        }
    }
    
    private String calculateExpensiveDebugInfo() {
        // Simulation of expensive processing
        return "Detailed debug data";
    }
}

Advanced Configuration (Custom Formatter and Handler)

import java.util.logging.*;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

// Custom formatter implementation
class CustomFormatter extends Formatter {
    private static final DateTimeFormatter DATE_FORMAT = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
    
    @Override
    public String format(LogRecord record) {
        StringBuilder sb = new StringBuilder();
        
        // Timestamp
        LocalDateTime dateTime = LocalDateTime.ofInstant(
            record.getInstant(), java.time.ZoneId.systemDefault());
        sb.append(dateTime.format(DATE_FORMAT));
        
        // Log level
        sb.append(" [").append(record.getLevel()).append("]");
        
        // Thread information
        sb.append(" [Thread-").append(record.getThreadID()).append("]");
        
        // Class name and method name
        if (record.getSourceClassName() != null) {
            sb.append(" ").append(record.getSourceClassName());
            if (record.getSourceMethodName() != null) {
                sb.append(".").append(record.getSourceMethodName());
            }
        }
        
        // Message
        sb.append(" - ").append(formatMessage(record));
        
        // Exception information
        if (record.getThrown() != null) {
            sb.append("\n");
            java.io.StringWriter sw = new java.io.StringWriter();
            java.io.PrintWriter pw = new java.io.PrintWriter(sw);
            record.getThrown().printStackTrace(pw);
            sb.append(sw.toString());
        }
        
        sb.append("\n");
        return sb.toString();
    }
}

// Custom filter implementation
class CustomFilter implements Filter {
    @Override
    public boolean isLoggable(LogRecord record) {
        // Allow only logs from specific classes
        String sourceClass = record.getSourceClassName();
        if (sourceClass != null && sourceClass.contains("Security")) {
            return record.getLevel().intValue() >= Level.WARNING.intValue();
        }
        return true;
    }
}

// File handler with rotation functionality
class RotatingFileHandler extends FileHandler {
    public RotatingFileHandler(String pattern, int limit, int count) 
            throws IOException, SecurityException {
        super(pattern, limit, count, true);
    }
}

public class AdvancedLoggingConfiguration {
    private static final Logger logger = Logger.getLogger(
        AdvancedLoggingConfiguration.class.getName());
    
    public static void setupAdvancedLogging() {
        try {
            // Clear root logger configuration
            Logger rootLogger = Logger.getLogger("");
            Handler[] handlers = rootLogger.getHandlers();
            for (Handler handler : handlers) {
                rootLogger.removeHandler(handler);
            }
            
            // Custom console handler
            ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setLevel(Level.WARNING);
            consoleHandler.setFormatter(new CustomFormatter());
            consoleHandler.setFilter(new CustomFilter());
            
            // File handler with rotation functionality
            // 10MB limit, rotate up to 5 files
            RotatingFileHandler fileHandler = new RotatingFileHandler(
                "logs/app-%g.log", 10 * 1024 * 1024, 5);
            fileHandler.setLevel(Level.ALL);
            fileHandler.setFormatter(new CustomFormatter());
            
            // Error-specific file handler
            FileHandler errorHandler = new FileHandler("logs/error.log", true);
            errorHandler.setLevel(Level.WARNING);
            errorHandler.setFormatter(new CustomFormatter());
            errorHandler.setFilter(record -> 
                record.getLevel().intValue() >= Level.WARNING.intValue());
            
            // Add each handler to Logger
            logger.addHandler(consoleHandler);
            logger.addHandler(fileHandler);
            logger.addHandler(errorHandler);
            logger.setLevel(Level.ALL);
            logger.setUseParentHandlers(false);
            
            logger.info("Advanced logging configuration completed");
            
        } catch (IOException e) {
            System.err.println("Logging configuration error: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        setupAdvancedLogging();
        
        // Test log output
        logger.finest("Finest log");
        logger.fine("Fine log");
        logger.info("Info log");
        logger.warning("Warning log");
        logger.severe("Severe error log");
    }
}

Error Handling and Configuration File Management

import java.util.logging.*;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.Properties;

public class ConfigurationBasedLogging {
    private static final Logger logger = Logger.getLogger(
        ConfigurationBasedLogging.class.getName());
    
    // Load logging configuration from configuration file
    public static void loadLoggingConfiguration() {
        try {
            // Load logging.properties file
            LogManager.getLogManager().readConfiguration(
                new FileInputStream("config/logging.properties"));
            
            logger.info("Logging configuration file loaded successfully");
            
        } catch (IOException e) {
            System.err.println("Failed to load logging configuration file: " + e.getMessage());
            
            // Fallback configuration
            setupFallbackLogging();
        } catch (SecurityException e) {
            System.err.println("Logging configuration security error: " + e.getMessage());
        }
    }
    
    // Fallback configuration
    private static void setupFallbackLogging() {
        try {
            ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setLevel(Level.INFO);
            consoleHandler.setFormatter(new SimpleFormatter());
            
            logger.addHandler(consoleHandler);
            logger.setLevel(Level.INFO);
            logger.setUseParentHandlers(false);
            
            logger.warning("Fallback logging configuration applied");
            
        } catch (Exception e) {
            System.err.println("Fallback configuration error: " + e.getMessage());
        }
    }
    
    // Application-specific log methods
    public static class ApplicationLogger {
        private final Logger appLogger;
        
        public ApplicationLogger(String name) {
            this.appLogger = Logger.getLogger(name);
        }
        
        public void logUserAction(String userId, String action) {
            appLogger.info(String.format("USER_ACTION: %s performed %s", userId, action));
        }
        
        public void logSystemEvent(String event, Object... params) {
            appLogger.log(Level.INFO, "SYSTEM_EVENT: " + event, params);
        }
        
        public void logError(String component, String errorMessage, Throwable throwable) {
            appLogger.log(Level.SEVERE, 
                String.format("ERROR in %s: %s", component, errorMessage), throwable);
        }
        
        public void logPerformanceMetric(String metricName, double value) {
            if (appLogger.isLoggable(Level.CONFIG)) {
                appLogger.config(String.format("METRIC: %s = %.3f", metricName, value));
            }
        }
        
        public void logSecurityEvent(String event, String details) {
            Logger securityLogger = Logger.getLogger("SECURITY." + appLogger.getName());
            securityLogger.warning(String.format("SECURITY_EVENT: %s - %s", event, details));
        }
    }
    
    public static void main(String[] args) {
        // Load from configuration file
        loadLoggingConfiguration();
        
        // Application logger usage example
        ApplicationLogger appLogger = new ApplicationLogger("MyApplication");
        
        try {
            appLogger.logSystemEvent("Application started");
            appLogger.logUserAction("user123", "login");
            appLogger.logPerformanceMetric("response_time", 0.125);
            
            // Some business logic
            performBusinessLogic();
            
        } catch (Exception e) {
            appLogger.logError("MainApplication", "Unexpected error", e);
        } finally {
            appLogger.logSystemEvent("Application shutdown");
        }
    }
    
    private static void performBusinessLogic() throws Exception {
        // Business logic simulation
        if (Math.random() > 0.7) {
            throw new Exception("Random business exception");
        }
    }
}

Practical Example (Web Application Integration)

import java.util.logging.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// logging.properties configuration file example (as comments)
/*
# logging.properties
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Root logger configuration
.level=INFO

# File handler configuration
java.util.logging.FileHandler.pattern=logs/webapp_%g.log
java.util.logging.FileHandler.limit=50000000
java.util.logging.FileHandler.count=5
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.level=ALL

# Console handler configuration
java.util.logging.ConsoleHandler.level=WARNING
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

# Individual logger configuration
com.example.webapp.level=FINE
com.example.webapp.security.level=ALL
*/

public class WebApplicationLogging {
    
    // Loggers for each component
    private static final Logger ACCESS_LOGGER = Logger.getLogger("webapp.access");
    private static final Logger SECURITY_LOGGER = Logger.getLogger("webapp.security");
    private static final Logger PERFORMANCE_LOGGER = Logger.getLogger("webapp.performance");
    private static final Logger ERROR_LOGGER = Logger.getLogger("webapp.error");
    
    static {
        // Perform log configuration in static initialization
        initializeLogging();
    }
    
    private static void initializeLogging() {
        try {
            // Load log configuration from system properties
            String configFile = System.getProperty("java.util.logging.config.file");
            if (configFile != null) {
                LogManager.getLogManager().readConfiguration(
                    new FileInputStream(configFile));
            }
        } catch (Exception e) {
            System.err.println("Log configuration initialization error: " + e.getMessage());
        }
    }
    
    // HTTP request logging
    public static void logHttpRequest(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    long processingTime) {
        String clientIp = getClientIpAddress(request);
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String query = request.getQueryString();
        int statusCode = response.getStatus();
        
        String logMessage = String.format(
            "HTTP_REQUEST: %s %s %s%s %d %dms",
            clientIp, method, uri, 
            (query != null ? "?" + query : ""), 
            statusCode, processingTime
        );
        
        // Access log
        ACCESS_LOGGER.info(logMessage);
        
        // Performance warning
        if (processingTime > 1000) {
            PERFORMANCE_LOGGER.warning(
                String.format("Slow request detected: %s took %dms", uri, processingTime));
        }
        
        // Error log
        if (statusCode >= 500) {
            ERROR_LOGGER.severe(String.format("Server error: %d for %s", statusCode, uri));
        } else if (statusCode >= 400) {
            ERROR_LOGGER.warning(String.format("Client error: %d for %s", statusCode, uri));
        }
    }
    
    // Security event logging
    public static void logSecurityEvent(String eventType, String details, 
                                      HttpServletRequest request) {
        String clientIp = getClientIpAddress(request);
        String userAgent = request.getHeader("User-Agent");
        String sessionId = request.getSession(false) != null ? 
            request.getSession().getId() : "no-session";
        
        String logMessage = String.format(
            "SECURITY_EVENT: %s | IP: %s | Session: %s | UA: %s | Details: %s",
            eventType, clientIp, sessionId, userAgent, details
        );
        
        SECURITY_LOGGER.warning(logMessage);
    }
    
    // Error logging
    public static void logApplicationError(String component, String operation, 
                                         Throwable error, Object... context) {
        StringBuilder contextStr = new StringBuilder();
        for (int i = 0; i < context.length; i += 2) {
            if (i + 1 < context.length) {
                contextStr.append(context[i]).append("=").append(context[i + 1]).append(" ");
            }
        }
        
        String logMessage = String.format(
            "APP_ERROR: [%s.%s] %s | Context: %s",
            component, operation, error.getMessage(), contextStr.toString().trim()
        );
        
        ERROR_LOGGER.log(Level.SEVERE, logMessage, error);
    }
    
    // Business event logging
    public static void logBusinessEvent(String eventType, String userId, 
                                      String details) {
        String logMessage = String.format(
            "BUSINESS_EVENT: %s | User: %s | Details: %s",
            eventType, userId, details
        );
        
        ACCESS_LOGGER.info(logMessage);
    }
    
    // Performance metrics logging
    public static void logPerformanceMetric(String metricName, double value, 
                                          String unit) {
        if (PERFORMANCE_LOGGER.isLoggable(Level.CONFIG)) {
            String logMessage = String.format(
                "PERFORMANCE_METRIC: %s = %.3f %s",
                metricName, value, unit
            );
            PERFORMANCE_LOGGER.config(logMessage);
        }
    }
    
    private static String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
    
    // Usage example (servlet usage)
    public static void main(String[] args) {
        // Sample usage example
        
        // System startup log
        ACCESS_LOGGER.info("SYSTEM_STARTUP: Web application started successfully");
        
        // Business event
        logBusinessEvent("USER_REGISTRATION", "user123", "New user registration completed");
        
        // Security event (mock)
        // logSecurityEvent("INVALID_LOGIN", "5 consecutive failures", mockRequest);
        
        // Performance metrics
        logPerformanceMetric("database_connection_pool_usage", 0.75, "ratio");
        
        // Error event
        try {
            throw new RuntimeException("Database connection error");
        } catch (Exception e) {
            logApplicationError("DatabaseService", "getConnection", e, 
                              "database", "primary", "retry_count", 3);
        }
        
        // System shutdown log
        ACCESS_LOGGER.info("SYSTEM_SHUTDOWN: Web application shutting down gracefully");
    }
}