001package io.jstach.rainbowgum.systemlogger; 002 003import java.lang.System.Logger; 004import java.util.Locale; 005import java.util.function.Supplier; 006 007import org.eclipse.jdt.annotation.Nullable; 008 009import io.jstach.rainbowgum.LogProperties; 010import io.jstach.rainbowgum.LogRouter; 011import io.jstach.rainbowgum.MetaLog; 012import io.jstach.rainbowgum.RainbowGum; 013import io.jstach.rainbowgum.spi.RainbowGumServiceProvider; 014import io.jstach.rainbowgum.LogProperty.Property; 015 016/** 017 * Abstract System Logger Finder to allow users to create their own custom 018 * System.LoggerFinder. <strong>This implementation does not cache System Loggers</strong> 019 * by name! 020 * 021 * @see #INITIALIZE_RAINBOW_GUM_PROPERTY 022 */ 023public abstract class RainbowGumSystemLoggerFinder extends System.LoggerFinder { 024 025 /** 026 * Initialization flag. 027 * @see InitOption 028 */ 029 public static final String INITIALIZE_RAINBOW_GUM_PROPERTY = LogProperties.ROOT_PREFIX + "systemlogger.initialize"; 030 031 private final RouterProvider routerProvider; 032 033 /** 034 * Values (case is ignored) for {@value #INITIALIZE_RAINBOW_GUM_PROPERTY}. 035 */ 036 public enum InitOption { 037 038 /** 039 * Will not initialize rainbow gum. 040 */ 041 FALSE, 042 /** 043 * Will initialize rainbow gum. 044 */ 045 TRUE, 046 /** 047 * (default) Will check if there are implementations of 048 * {@link RainbowGumServiceProvider.RainbowGumEagerLoad} and if there are not will 049 * load rainbow gum. 050 */ 051 CHECK, 052 /** 053 * Will reuse an existing rainbow gum or fail. 054 */ 055 REUSE; 056 057 /** 058 * Parses an init option from a property value. 059 * @param input from properties. 060 * @return init option. 061 */ 062 public static InitOption parse(String input) { 063 if (input.isBlank()) 064 return FALSE; 065 return InitOption.valueOf(input.toUpperCase(Locale.ROOT)); 066 } 067 068 } 069 070 /** 071 * Creates the logger inder based on init option. 072 * @param optSupplier can be resolved with {@link #initOption(LogProperties)}. 073 */ 074 protected RainbowGumSystemLoggerFinder(Supplier<? extends InitOption> optSupplier) { 075 try { 076 var opt = optSupplier.get(); 077 this.routerProvider = switch (opt) { 078 case FALSE -> n -> LogRouter.global(); 079 case TRUE -> new InitRouterProvider(RainbowGum::of); 080 case CHECK -> { 081 if (RainbowGumServiceProvider.RainbowGumEagerLoad.exists()) { 082 yield n -> LogRouter.global(); 083 } 084 yield new InitRouterProvider(RainbowGum::of); 085 } 086 case REUSE -> new InitRouterProvider(() -> { 087 var gum = RainbowGum.getOrNull(); 088 if (gum == null) { 089 throw new IllegalStateException( 090 "SystemLogging was configured to reuse a loaded Rainbow Gum but none was found. " 091 + INITIALIZE_RAINBOW_GUM_PROPERTY + "=" + opt); 092 } 093 return gum; 094 }); 095 }; 096 } 097 catch (Exception e) { 098 // We have to do this because it because very difficult 099 // to determine why the System Logging fails as it does not even print the 100 // exception. 101 MetaLog.error(getClass(), "Failed to create System.LoggerFinder", e); 102 throw e; 103 } 104 } 105 106 @Override 107 public Logger getLogger(String name, Module module) { 108 var router = routerProvider.router(name); 109 if (!router.isChangeable(name)) { 110 var level = router.levelResolver().resolveLevel(name); 111 return LevelSystemLogger.of(name, level, router.route(name, level)); 112 } 113 return RainbowGumSystemLogger.of(name, router); 114 } 115 116 /** 117 * Gets the init option from properties. 118 * @param properties usually system properties. 119 * @return initialization option. 120 */ 121 protected static InitOption initOption(LogProperties properties) { 122 return Property.builder() // 123 .map(InitOption::parse) 124 .build(INITIALIZE_RAINBOW_GUM_PROPERTY) // 125 .get(properties) // 126 .value(InitOption.CHECK); 127 } 128 129 private interface RouterProvider { 130 131 LogRouter.RootRouter router(String loggerName); 132 133 } 134 135 private class InitRouterProvider implements RouterProvider { 136 137 private final Supplier<RainbowGum> supplier; 138 139 private volatile @Nullable RainbowGum gum = null; 140 141 public InitRouterProvider(Supplier<RainbowGum> supplier) { 142 super(); 143 this.supplier = supplier; 144 } 145 146 @Override 147 public LogRouter.RootRouter router(String loggerName) { 148 LogRouter.RootRouter router; 149 RainbowGum gum = this.gum; 150 if (gum == null) { 151 gum = this.gum = supplier.get(); 152 } 153 if (gum.config().changePublisher().isEnabled(loggerName)) { 154 router = LogRouter.global(); 155 } 156 else { 157 // var rootRouter = gum.router(); 158 // var levelResolver = rootRouter.levelResolver(); 159 // var level = levelResolver.resolveLevel(loggerName); 160 // var logger = rootRouter.route(loggerName, level); 161 // router = LogRouter.ofLevel(logger, level); 162 router = gum.router(); 163 } 164 return router; 165 } 166 167 } 168 169}