001/* 002 * Copyright 2023 the original author or authors. 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.tools.logging; 017 018import static de.cuioss.tools.logging.CuiLoggerFactory.MARKER_CLASS_NAMES; 019import static de.cuioss.tools.string.MoreStrings.lenientFormat; 020import static de.cuioss.tools.string.MoreStrings.nullToEmpty; 021 022import java.util.Comparator; 023import java.util.List; 024import java.util.function.Supplier; 025import java.util.logging.Level; 026import java.util.logging.Logger; 027 028import de.cuioss.tools.collect.CollectionLiterals; 029import de.cuioss.tools.reflect.MoreReflection; 030import lombok.AccessLevel; 031import lombok.Getter; 032import lombok.NonNull; 033import lombok.RequiredArgsConstructor; 034 035/** 036 * Defines the log-levels with implicit mapping 037 * 038 * @author Oliver Wolff 039 */ 040@RequiredArgsConstructor(access = AccessLevel.PRIVATE) 041public enum LogLevel { 042 043 /** 044 * Trace Level, maps to {@link Level#FINER}. 045 * <p> 046 * Attention: This is a derivation to 047 * http://www.slf4j.org/apidocs/org/slf4j/bridge/SLF4JBridgeHandler.html. But in 048 * fact this works... 049 */ 050 TRACE(Level.FINER), 051 052 /** 053 * Debug Level, maps to {@link Level#FINE}. 054 */ 055 DEBUG(Level.FINE), 056 057 /** 058 * Info Level, maps to {@link Level#INFO}. 059 */ 060 INFO(Level.INFO), 061 062 /** 063 * Warn Level, maps to {@link Level#WARNING}. 064 */ 065 WARN(Level.WARNING), 066 067 /** 068 * Error Level, maps to {@link Level#SEVERE}. 069 */ 070 ERROR(Level.SEVERE), 071 072 /** 073 * Off Level, maps to {@link Level#OFF} 074 */ 075 OFF(Level.OFF); 076 077 @Getter(AccessLevel.PACKAGE) 078 private final Level juliLevel; 079 080 /** 081 * @param logger to be checked, must not be null 082 * @return {@code true} if the log-level is enabled on the logger, false 083 * otherwise 084 */ 085 boolean isEnabled(final Logger logger) { 086 return logger.isLoggable(getJuliLevel()); 087 } 088 089 /** 090 * Logs the message 091 * 092 * @param logger to be used, must not be null 093 * @param message must not be null 094 * @param throwable to be logged, may be null 095 */ 096 void handleActualLog(final Logger logger, final String message, final Throwable throwable) { 097 if (!isEnabled(logger)) { 098 return; 099 } 100 doLog(logger, message, throwable); 101 } 102 103 /** 104 * @param juliLevel 105 * @return CUI log level 106 */ 107 public static LogLevel from(@NonNull final Level juliLevel) { 108 // highest value first, i.e. OFF, ERROR, WARN, INFO, DEBUG, TRACE 109 List<LogLevel> sortedCuiLevels = CollectionLiterals.mutableList(values()); 110 sortedCuiLevels.sort(Comparator.comparing(logLevel -> logLevel.getJuliLevel().intValue())); 111 sortedCuiLevels.sort(Comparator.reverseOrder()); 112 113 final var juliIntLevel = juliLevel.intValue(); 114 for (LogLevel cuiLevel : sortedCuiLevels) { 115 final var cuiIntLevel = cuiLevel.getJuliLevel().intValue(); 116 if (cuiIntLevel <= juliIntLevel) { 117 return cuiLevel; 118 } 119 } 120 return TRACE; 121 } 122 123 private void doLog(final Logger logger, final String message, final Throwable throwable) { 124 // We go up the stack-trace until we found the call from CuiLogger. 125 final var caller = MoreReflection.findCallerElement(throwable, MARKER_CLASS_NAMES); 126 if (caller.isPresent()) { 127 // This is needed because otherwise LogRecord will assume this class and this 128 // method as 129 // the source of the log-statement 130 logger.logp(getJuliLevel(), caller.get().getClassName(), caller.get().getMethodName(), message, throwable); 131 } else { 132 logger.log(getJuliLevel(), message, throwable); 133 } 134 } 135 136 @SuppressWarnings("squid:S2629") 137 // False positive, logger state explicitly checked 138 void log(final Logger logger, final String template, final Object... parameter) { 139 if (isEnabled(logger)) { 140 final var replacedTemplate = de.cuioss.tools.logging.CuiLogger.SLF4J_PATTERN.matcher(nullToEmpty(template)) 141 .replaceAll("%s"); 142 doLog(logger, lenientFormat(replacedTemplate, parameter), null); 143 } 144 } 145 146 @SuppressWarnings("squid:S2629") 147 // False positive, logger state explicitly checked 148 void log(final Logger logger, Supplier<String> message, final Throwable throwable) { 149 if (isEnabled(logger)) { 150 doLog(logger, message.get(), throwable); 151 } 152 } 153 154 @SuppressWarnings("squid:S2629") 155 // False positive, logger state explicitly checked 156 void log(final Logger logger, final Throwable throwable, final String template, final Object... parameter) { 157 if (isEnabled(logger)) { 158 final var replacedTemplate = de.cuioss.tools.logging.CuiLogger.SLF4J_PATTERN.matcher(nullToEmpty(template)) 159 .replaceAll("%s"); 160 doLog(logger, lenientFormat(replacedTemplate, parameter), throwable); 161 } 162 } 163}