001/* 002 * SPDX-License-Identifier: Apache-2.0 003 * 004 * Copyright 2025-2026 The Enola <https://enola.dev> Authors 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package dev.enola.common.logging; 019 020import java.util.logging.Handler; 021import java.util.logging.Level; 022import java.util.logging.Logger; 023 024/** 025 * Configures {@link java.util.logging} (JUL). 026 * 027 * <p>This is intended to be called once at application startup (main method). It has global side 028 * effects, and is not meant to be used from libraries. 029 */ 030public final class JavaUtilLogging { 031 private JavaUtilLogging() {} 032 033 /** 034 * Configures logging. 035 * 036 * <p>This sets two {@link System#setProperty(String, String)} properties to work around other 037 * logging frameworks, and to configure the format. 038 * 039 * <p>This removes all existing handlers from the root logger, and adds a single new handler 040 * which logs all messages at or above the given {@link Level} to standard error, with colors. 041 * 042 * @param level the {@link Level} for the root logger and its new handler. 043 */ 044 public static void configure(Level level) { 045 // We don't want Spring Boot, which is used in ADK Web, to do configure logging: 046 System.setProperty("org.springframework.boot.logging.LoggingSystem", "none"); 047 048 System.setProperty( 049 "java.util.logging.SimpleFormatter.format", 050 "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s%n"); 051 052 // Root Logger, so for dev.enola.* but also e.g. ch.vorburger.exec or gRPC, etc. 053 var rootLogger = Logger.getLogger(""); 054 rootLogger.setLevel(level); 055 056 // Remove all existing handlers 057 Handler[] handlers = rootLogger.getHandlers(); 058 for (Handler handler : handlers) { 059 rootLogger.removeHandler(handler); 060 } 061 062 // Add (only) our fancy colouring handler 063 var handler = new LoggingColorConsoleHandler(); 064 handler.setLevel(level); 065 rootLogger.addHandler(handler); 066 067 // ============================= 068 // Configure specific frameworks 069 // TODO Make these modular and injectable! 070 071 // Disable Java WebSockets logging entirely. The problem is that it, at FINEST, prints 072 // BINARY data, which BREAKS subsequent logging, and that's (very) confusing. 073 // Our own LoggingWebSocketServer already logs what we are interested in. 074 Logger.getLogger("org.java_websocket").setLevel(Level.OFF); 075 } 076}