001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import static java.util.logging.Level.*; 016import static org.apache.juneau.internal.StringUtils.*; 017 018import java.text.*; 019import java.util.*; 020import java.util.concurrent.*; 021import java.util.logging.*; 022 023import org.apache.juneau.json.*; 024import org.apache.juneau.serializer.*; 025import org.apache.juneau.transforms.*; 026 027/** 028 * Wraps and extends the {@link java.util.logging.Logger} class to provide some additional convenience methods. 029 */ 030public class JuneauLogger extends java.util.logging.Logger { 031 032 private static final WriterSerializer serializer = JsonSerializer.create() 033 .pojoSwaps( 034 CalendarSwap.ISO8601DTZ.class, 035 DateSwap.ISO8601DTZ.class, 036 EnumerationSwap.class, 037 IteratorSwap.class 038 ) 039 .ssq() 040 .build(); 041 042 private static final ConcurrentHashMap<Class<?>,String> rbMap = new ConcurrentHashMap<>(); 043 044 private final ResourceBundle rb; 045 private final java.util.logging.Logger innerLogger; 046 047 /** 048 * Get logger for specified class. 049 * 050 * @param forClass The class to create a logger for. 051 * @return A new <l>Logger</l>. 052 */ 053 public static JuneauLogger getLogger(Class<?> forClass) { 054 return new JuneauLogger(java.util.logging.Logger.getLogger(forClass.getName())); 055 } 056 057 /** 058 * Get logger for specified class using the specified resource bundle name. 059 * 060 * @param forClass The class to create a logger for. 061 * @param resourceBundleName 062 * The name of the resource bundle. 063 * Can be any of the following formats: 064 * <ol> 065 * <li>An absolute path. E.g. <js>"com/foo/nls/Messages"</js>. 066 * <li>A path relative to the package of the class. E.g. <js>"nls/Messages"</js>. 067 * </ol> 068 * Both <js>'.'</js> and <js>'/'</js> can be used as path delimiters. 069 * @return A new <l>Logger</l>. 070 */ 071 public static JuneauLogger getLogger(Class<?> forClass, String resourceBundleName) { 072 return new JuneauLogger(java.util.logging.Logger.getLogger(forClass.getName(), resolveResourceBundleName(forClass, resourceBundleName))); 073 } 074 075 /** 076 * Get logger with specified name using the specified resource bundle name. 077 * 078 * @param name The name of the logger to use. 079 * @param resourceBundleName 080 * The name of the resource bundle. 081 * Can be any of the following formats: 082 * <ol> 083 * <li>An absolute path. E.g. <js>"com/foo/nls/Messages"</js>. 084 * <li>A path relative to the package of the class. E.g. <js>"nls/Messages"</js>. 085 * </ol> 086 * Both <js>'.'</js> and <js>'/'</js> can be used as path delimiters. 087 * @return A new <l>Logger</l>. 088 */ 089 public static synchronized JuneauLogger getLogger(String name, String resourceBundleName) { 090 return new JuneauLogger(java.util.logging.Logger.getLogger(name, resourceBundleName)); 091 } 092 093 /** 094 * Constructor. 095 * 096 * @param innerLogger The wrapped logger. 097 */ 098 protected JuneauLogger(java.util.logging.Logger innerLogger) { 099 super(innerLogger.getName(), innerLogger.getResourceBundleName()); 100 this.innerLogger = innerLogger; 101 this.rb = getResourceBundle(); 102 } 103 104 /** 105 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#SEVERE} level. 106 * 107 * @param msg The message to log. 108 * @param args Optional {@link MessageFormat}-style arguments. 109 */ 110 public void severe(String msg, Object...args) { 111 if (isLoggable(SEVERE)) 112 log(SEVERE, msg, args); 113 } 114 115 /** 116 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#WARNING} level. 117 * 118 * @param msg The message to log. 119 * @param args Optional {@link MessageFormat}-style arguments. 120 */ 121 public void warning(String msg, Object...args) { 122 if (isLoggable(WARNING)) 123 log(WARNING, msg, args); 124 } 125 126 /** 127 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#INFO} level. 128 * 129 * @param msg The message to log. 130 * @param args Optional {@link MessageFormat}-style arguments. 131 */ 132 public void info(String msg, Object...args) { 133 if (isLoggable(INFO)) 134 log(INFO, msg, args); 135 } 136 137 /** 138 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#CONFIG} level. 139 * 140 * @param msg The message to log. 141 * @param args Optional {@link MessageFormat}-style arguments. 142 */ 143 public void config(String msg, Object...args) { 144 if (isLoggable(CONFIG)) 145 log(CONFIG, msg, args); 146 } 147 148 /** 149 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#FINE} level. 150 * 151 * @param msg The message to log. 152 * @param args Optional {@link MessageFormat}-style arguments. 153 */ 154 public void fine(String msg, Object...args) { 155 if (isLoggable(FINE)) 156 log(FINE, msg, args); 157 } 158 159 /** 160 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#FINER} level. 161 * 162 * @param msg The message to log. 163 * @param args Optional {@link MessageFormat}-style arguments. 164 */ 165 public void finer(String msg, Object...args) { 166 if (isLoggable(FINER)) 167 log(FINER, msg, args); 168 } 169 170 /** 171 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#FINEST} level. 172 * 173 * @param msg The message to log. 174 * @param args Optional {@link MessageFormat}-style arguments. 175 */ 176 public void finest(String msg, Object...args) { 177 if (isLoggable(FINEST)) 178 log(FINEST, msg, args); 179 } 180 181 /** 182 * Logs an exception as {@link Level#SEVERE} level. 183 * 184 * @param t The Throwable object to log. 185 */ 186 public void severe(Throwable t) { 187 if (isLoggable(SEVERE)) 188 log(SEVERE, t.getLocalizedMessage(), t); 189 } 190 191 /** 192 * Logs an exception as {@link Level#WARNING} level. 193 * 194 * @param t The Throwable object to log. 195 */ 196 public void warning(Throwable t) { 197 if (isLoggable(WARNING)) 198 log(WARNING, t.getLocalizedMessage(), t); 199 } 200 201 /** 202 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#SEVERE} level. 203 * 204 * @param t The Throwable object associated with the event that needs to be logged. 205 * @param msg The message to log. 206 * @param args Optional {@link MessageFormat}-style arguments. 207 */ 208 public void severe(Throwable t, String msg, Object...args) { 209 if (isLoggable(SEVERE)) 210 log(SEVERE, getMessage(msg, args), t); 211 } 212 213 /** 214 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#WARNING} level. 215 * 216 * @param t The Throwable object associated with the event that needs to be logged. 217 * @param msg The message to log. 218 * @param args Optional {@link MessageFormat}-style arguments. 219 */ 220 public void warning(Throwable t, String msg, Object...args) { 221 if (isLoggable(WARNING)) 222 log(WARNING, getMessage(msg, args), t); 223 } 224 225 /** 226 * Logs a message with the specified {@link MessageFormat}-style arguments at {@link Level#INFO} level. 227 * 228 * @param t The Throwable object associated with the event that needs to be logged. 229 * @param msg The message to log. 230 * @param args Optional {@link MessageFormat}-style arguments. 231 */ 232 public void info(Throwable t, String msg, Object...args) { 233 if (isLoggable(INFO)) 234 log(INFO, getMessage(msg, args), t); 235 } 236 237 @Override /* Logger */ 238 public void log(LogRecord record) { 239 innerLogger.log(record); 240 } 241 242 @Override /* Logger */ 243 public boolean isLoggable(Level level) { 244 return innerLogger.isLoggable(level); 245 } 246 247 /** 248 * Similar to {@link #log(Level, String, Object[])}, except arguments are converted to objects 249 * that are serialized using the {@link JsonSerializer#toStringObject(Object)} method. 250 * 251 * <p> 252 * This allows arbitrary POJOs to be serialized as message parameters. 253 * 254 * @param level The level of the given message. 255 * @param msg The message to log. 256 * @param args The POJO arguments. 257 */ 258 public void logObjects(Level level, String msg, Object...args) { 259 if (isLoggable(level)) { 260 for (int i = 0; i < args.length; i++) 261 args[i] = serializer.toStringObject(args[i]); 262 log(level, msg, args); 263 } 264 } 265 266 private String getMessage(String msg, Object...args) { 267 if (args.length == 0) 268 return msg; 269 if (rb != null && rb.containsKey(msg)) 270 msg = rb.getString(msg); 271 return format(msg, args); 272 } 273 274 private static String resolveResourceBundleName(Class<?> forClass, String path) { 275 if (StringUtils.isEmpty(path)) 276 return null; 277 String rb = rbMap.get(forClass); 278 if (rb == null) { 279 path = path.replace('/', '.'); 280 if (path.startsWith(".")) 281 path = path.substring(1); 282 ClassLoader cl = forClass.getClassLoader(); 283 try { 284 ResourceBundle.getBundle(path, Locale.getDefault(), cl); 285 rbMap.putIfAbsent(forClass, path); 286 } catch (MissingResourceException e) { 287 try { 288 path = forClass.getPackage().getName() + '.' + path; 289 ResourceBundle.getBundle(path, Locale.getDefault(), cl); 290 rbMap.putIfAbsent(forClass, path); 291 } catch (MissingResourceException e2) { 292 rbMap.putIfAbsent(forClass, ""); 293 } 294 } 295 rb = rbMap.get(forClass); 296 } 297 return ("".equals(rb) ? null : rb); 298 } 299}