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.RainbowGum; 012import io.jstach.rainbowgum.LogProperty.Property; 013 014/** 015 * Abstract System Logger Finder to allow users to create their own custom 016 * System.LoggerFinder. <strong>This implementation does not cache System Loggers</strong> 017 * by name! 018 * 019 * @see #INTIALIZE_RAINBOW_GUM_PROPERTY 020 */ 021public abstract class RainbowGumSystemLoggerFinder extends System.LoggerFinder { 022 023 /** 024 * Initialization flag. 025 * @see InitOption 026 */ 027 public static final String INTIALIZE_RAINBOW_GUM_PROPERTY = LogProperties.ROOT_PREFIX + "systemlogger.intialize"; 028 029 private final RouterProvider routerProvider; 030 031 /** 032 * Values (case is ignored) for {@value #INTIALIZE_RAINBOW_GUM_PROPERTY}. 033 */ 034 public enum InitOption { 035 036 /** 037 * Will not initialize rainbow gum. 038 */ 039 FALSE, 040 /** 041 * Will initialize rainbow gum. 042 */ 043 TRUE, 044 /** 045 * Will reuse an existing rainbow gum or fail. 046 */ 047 REUSE; 048 049 /** 050 * Parses an init option from a property value. 051 * @param input from properties. 052 * @return init option. 053 */ 054 public static InitOption parse(String input) { 055 if (input.isBlank()) 056 return FALSE; 057 return InitOption.valueOf(input.toUpperCase(Locale.ROOT)); 058 } 059 060 } 061 062 /** 063 * Creates the logger inder based on init option. 064 * @param opt can be resolved with {@link #initOption(LogProperties)}. 065 */ 066 protected RainbowGumSystemLoggerFinder(InitOption opt) { 067 this.routerProvider = switch (opt) { 068 case FALSE -> n -> LogRouter.global(); 069 case TRUE -> new InitRouterProvider(RainbowGum::of); 070 case REUSE -> new InitRouterProvider(() -> { 071 var gum = RainbowGum.getOrNull(); 072 if (gum == null) { 073 throw new IllegalStateException( 074 "SystemLogging was configured to reuse a loaded Rainbow Gum but none was found. " 075 + INTIALIZE_RAINBOW_GUM_PROPERTY + "=" + opt); 076 } 077 return gum; 078 }); 079 }; 080 } 081 082 @Override 083 public Logger getLogger(String name, Module module) { 084 var router = routerProvider.router(name); 085 return RainbowGumSystemLogger.of(name, router); 086 } 087 088 /** 089 * Gets the init option from properties. 090 * @param properties usually system properties. 091 * @return intialization option. 092 */ 093 protected static InitOption initOption(LogProperties properties) { 094 return Property.builder() // 095 .map(InitOption::parse) 096 .build(INTIALIZE_RAINBOW_GUM_PROPERTY) // 097 .get(properties) // 098 .value(InitOption.FALSE); 099 } 100 101 private interface RouterProvider { 102 103 LogRouter router(String loggerName); 104 105 } 106 107 private class InitRouterProvider implements RouterProvider { 108 109 private final Supplier<RainbowGum> supplier; 110 111 private volatile @Nullable RainbowGum gum = null; 112 113 public InitRouterProvider(Supplier<RainbowGum> supplier) { 114 super(); 115 this.supplier = supplier; 116 } 117 118 @Override 119 public LogRouter router(String loggerName) { 120 LogRouter router; 121 RainbowGum gum = this.gum; 122 if (gum == null) { 123 gum = this.gum = supplier.get(); 124 } 125 if (gum.config().changePublisher().isEnabled(loggerName)) { 126 router = LogRouter.global(); 127 } 128 else { 129 var rootRouter = gum.router(); 130 var levelResolver = rootRouter.levelResolver(); 131 var level = levelResolver.resolveLevel(loggerName); 132 var logger = rootRouter.route(loggerName, level); 133 router = LogRouter.ofLevel(logger, level); 134 } 135 return router; 136 } 137 138 } 139 140}