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}