001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.microservice.jetty; 018 019import static java.util.logging.Level.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021import static org.apache.juneau.commons.utils.SystemUtils.*; 022 023import java.util.logging.*; 024import java.util.logging.Logger; 025 026import org.slf4j.*; 027import org.slf4j.helpers.*; 028import org.slf4j.spi.*; 029 030/** 031 * Implementation of Jetty {@link Logger} based on {@link Logger}. 032 * 033 * <p> 034 * Allows Jetty to log to the Java Util logging framework (and thus to the main log file defined in the 035 * <cc>[Logging]</cc> section). 036 * 037 * <p> 038 * Can be used by setting the following system property in the microservice config file. 039 * 040 * <p class='bini'> 041 * <cs>[SystemProperties]</cs> 042 * 043 * <cc># Configure Jetty to log using java-util logging</cc> 044 * <ck>org.eclipse.jetty.util.log.class</ck> = org.apache.juneau.microservice.jetty.JettyLogger 045 * </p> 046 * 047 * <h5 class='section'>See Also:</h5><ul> 048 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauMicroserviceJetty">juneau-microservice-jetty</a> 049 * </ul> 050 */ 051public class JettyLogger implements LocationAwareLogger { 052 private static final boolean SHOW_SOURCE = env("org.eclipse.jetty.util.log.SOURCE", env("org.eclipse.jetty.util.log.javautil.SOURCE", true)); 053 054 /** 055 * Formats a log message by replacing "{}" placeholders with the provided arguments. 056 * 057 * <p> 058 * Uses SLF4J's {@link MessageFormatter} so the SLF4J-style <c>{}</c> anchors used by Jetty's logging calls are 059 * expanded (a printf-style formatter would leave the <c>{}</c> literal and silently drop the arguments). 060 * 061 * @param msg The message template with "{}" placeholders. 062 * @param args The arguments to substitute into the message. 063 * @return The formatted message string. 064 */ 065 private static String format(String msg, Object...args) { 066 return MessageFormatter.arrayFormat(msg, args).getMessage(); 067 } 068 069 /** 070 * Maps an SLF4J integer log level to the equivalent {@link Level}. 071 * 072 * @param level The SLF4J level constant (e.g. {@link LocationAwareLogger#DEBUG_INT}). 073 * @return The equivalent Java Util Logging level. 074 */ 075 private static Level toLevel(int level) { 076 return switch (level) { 077 case LocationAwareLogger.TRACE_INT -> FINEST; 078 case LocationAwareLogger.DEBUG_INT -> FINE; 079 case LocationAwareLogger.INFO_INT -> INFO; 080 case LocationAwareLogger.WARN_INT -> WARNING; 081 default -> SEVERE; // ERROR_INT and any out-of-range value map to the highest severity. 082 }; 083 } 084 085 /** 086 * The originally configured log level, used to restore the level when debug mode is disabled. 087 */ 088 private Level configuredLevel; 089 090 /** 091 * The underlying Java Util Logger instance. 092 */ 093 private final Logger logger; 094 095 /** 096 * Default constructor. 097 * 098 * <p> 099 * Returns the logger with name <js>"org.eclipse.jetty.util.log.javautil"</js>. 100 */ 101 public JettyLogger() { 102 this("org.eclipse.jetty.util.log.javautil"); 103 } 104 105 /** 106 * Normal constructor. 107 * 108 * @param name The logger name. Must not be <jk>null</jk>. 109 */ 110 public JettyLogger(String name) { 111 logger = Logger.getLogger(name); 112 configuredLevel = logger.getLevel(); 113 } 114 115 @Override 116 public void debug(Marker marker, String msg) { 117 debug(msg); // Markers are not used by this JUL-backed implementation. 118 } 119 120 @Override 121 public void debug(Marker marker, String format, Object arg) { 122 debug(format, arg); // Markers are not used by this JUL-backed implementation. 123 } 124 125 @Override 126 public void debug(Marker marker, String format, Object...arguments) { 127 debug(format, arguments); // Markers are not used by this JUL-backed implementation. 128 } 129 130 @Override 131 public void debug(Marker marker, String format, Object arg1, Object arg2) { 132 debug(format, arg1, arg2); // Markers are not used by this JUL-backed implementation. 133 } 134 135 @Override 136 public void debug(Marker marker, String msg, Throwable t) { 137 debug(msg, t); // Markers are not used by this JUL-backed implementation. 138 } 139 140 @Override 141 public void debug(String msg) { 142 if (isLoggable(FINE)) 143 log(FINE, msg, null); 144 } 145 146 /** 147 * Logs a debug message with a long argument. 148 * 149 * @param msg The message template with "{}" placeholder. 150 * @param arg The long argument to substitute into the message. 151 */ 152 public void debug(String msg, long arg) { 153 if (isLoggable(FINE)) 154 log(FINE, format(msg, arg), null); 155 } 156 157 @Override 158 public void debug(String msg, Object...args) { 159 if (isLoggable(FINE)) 160 log(FINE, format(msg, args), null); 161 } 162 163 @Override 164 public void debug(String format, Object arg) { 165 if (isLoggable(FINE)) 166 log(FINE, format(format, arg), null); 167 } 168 169 @Override 170 public void debug(String format, Object arg1, Object arg2) { 171 if (isLoggable(FINE)) 172 log(FINE, format(format, arg1, arg2), null); 173 } 174 175 @Override 176 public void debug(String msg, Throwable thrown) { 177 if (isLoggable(FINE)) 178 log(FINE, msg, thrown); 179 } 180 181 /** 182 * Logs a debug message with only a throwable. 183 * 184 * @param thrown The throwable to log. 185 */ 186 public void debug(Throwable thrown) { 187 if (isLoggable(FINE)) 188 log(FINE, "", thrown); 189 } 190 191 @Override 192 public void error(Marker marker, String msg) { 193 error(msg); // Markers are not used by this JUL-backed implementation. 194 } 195 196 @Override 197 public void error(Marker marker, String format, Object arg) { 198 error(format, arg); // Markers are not used by this JUL-backed implementation. 199 } 200 201 @Override 202 public void error(Marker marker, String format, Object...arguments) { 203 error(format, arguments); // Markers are not used by this JUL-backed implementation. 204 } 205 206 @Override 207 public void error(Marker marker, String format, Object arg1, Object arg2) { 208 error(format, arg1, arg2); // Markers are not used by this JUL-backed implementation. 209 } 210 211 @Override 212 public void error(Marker marker, String msg, Throwable t) { 213 error(msg, t); // Markers are not used by this JUL-backed implementation. 214 } 215 216 @Override 217 public void error(String msg) { 218 if (isLoggable(SEVERE)) 219 log(SEVERE, msg, null); 220 } 221 222 @Override 223 public void error(String format, Object arg) { 224 if (isLoggable(SEVERE)) 225 log(SEVERE, format(format, arg), null); 226 } 227 228 @Override 229 public void error(String format, Object...arguments) { 230 if (isLoggable(SEVERE)) 231 log(SEVERE, format(format, arguments), null); 232 } 233 234 @Override 235 public void error(String format, Object arg1, Object arg2) { 236 if (isLoggable(SEVERE)) 237 log(SEVERE, format(format, arg1, arg2), null); 238 } 239 240 @Override 241 public void error(String msg, Throwable t) { 242 if (isLoggable(SEVERE)) 243 log(SEVERE, msg, t); 244 } 245 246 /** 247 * Creates a child logger with the specified name. 248 * 249 * @param name The logger name. Must not be <jk>null</jk>. 250 * @return A new JettyLogger instance for the specified name. 251 */ 252 public JettyLogger getLogger(String name) { 253 return new JettyLogger(name); 254 } 255 256 @Override 257 public String getName() { return logger.getName(); } 258 259 /** 260 * Logs an ignored exception at the FINEST level. 261 * 262 * @param ignored The exception to log as ignored. 263 */ 264 public void ignore(Throwable ignored) { 265 if (isLoggable(FINEST)) 266 log(FINEST, "IGNORED EXCEPTION ", ignored); 267 } 268 269 @Override 270 public void info(Marker marker, String msg) { 271 info(msg); // Markers are not used by this JUL-backed implementation. 272 } 273 274 @Override 275 public void info(Marker marker, String format, Object arg) { 276 info(format, arg); // Markers are not used by this JUL-backed implementation. 277 } 278 279 @Override 280 public void info(Marker marker, String format, Object...arguments) { 281 info(format, arguments); // Markers are not used by this JUL-backed implementation. 282 } 283 284 @Override 285 public void info(Marker marker, String format, Object arg1, Object arg2) { 286 info(format, arg1, arg2); // Markers are not used by this JUL-backed implementation. 287 } 288 289 @Override 290 public void info(Marker marker, String msg, Throwable t) { 291 info(msg, t); // Markers are not used by this JUL-backed implementation. 292 } 293 294 @Override 295 public void info(String msg) { 296 if (isLoggable(INFO)) 297 log(INFO, msg, null); 298 } 299 300 @Override 301 public void info(String msg, Object...args) { 302 if (isLoggable(INFO)) 303 log(INFO, format(msg, args), null); 304 } 305 306 @Override 307 public void info(String format, Object arg) { 308 if (isLoggable(INFO)) 309 log(INFO, format(format, arg), null); 310 } 311 312 @Override 313 public void info(String format, Object arg1, Object arg2) { 314 if (isLoggable(INFO)) 315 log(INFO, format(format, arg1, arg2), null); 316 } 317 318 @Override 319 public void info(String msg, Throwable thrown) { 320 if (isLoggable(INFO)) 321 log(INFO, msg, thrown); 322 } 323 324 /** 325 * Logs an info message with only a throwable. 326 * 327 * @param thrown The throwable to log. 328 */ 329 public void info(Throwable thrown) { 330 if (isLoggable(INFO)) 331 log(INFO, "", thrown); 332 } 333 334 @Override 335 public boolean isDebugEnabled() { return isLoggable(FINE); } 336 337 @Override 338 public boolean isDebugEnabled(Marker marker) { return isLoggable(FINE); } 339 340 @Override 341 public boolean isErrorEnabled() { return isLoggable(SEVERE); } 342 343 @Override 344 public boolean isErrorEnabled(Marker marker) { return isLoggable(SEVERE); } 345 346 @Override 347 public boolean isInfoEnabled() { return isLoggable(INFO); } 348 349 @Override 350 public boolean isInfoEnabled(Marker marker) { return isLoggable(INFO); } 351 352 @Override 353 public boolean isTraceEnabled() { return isLoggable(FINEST); } 354 355 @Override 356 public boolean isTraceEnabled(Marker marker) { return isLoggable(FINEST); } 357 358 @Override 359 public boolean isWarnEnabled() { return isLoggable(WARNING); } 360 361 @Override 362 public boolean isWarnEnabled(Marker marker) { return isLoggable(WARNING); } 363 364 @Override 365 public void log(Marker marker, String fqcn, int level, String message, Object[] argArray, Throwable t) { 366 var l = toLevel(level); 367 if (isLoggable(l)) 368 log(l, argArray == null ? message : format(message, argArray), t); 369 } 370 371 /** 372 * Enables or disables debug logging. 373 * 374 * <p> 375 * When enabled, sets the log level to FINE. When disabled, restores the previously configured log level. 376 * 377 * @param enabled <jk>true</jk> to enable debug logging, <jk>false</jk> to disable. 378 */ 379 public void setDebugEnabled(boolean enabled) { 380 if (enabled) { 381 configuredLevel = logger.getLevel(); 382 logger.setLevel(FINE); 383 } else { 384 logger.setLevel(configuredLevel); 385 } 386 } 387 388 @Override 389 public void trace(Marker marker, String msg) { 390 trace(msg); // Markers are not used by this JUL-backed implementation. 391 } 392 393 @Override 394 public void trace(Marker marker, String format, Object arg) { 395 trace(format, arg); // Markers are not used by this JUL-backed implementation. 396 } 397 398 @Override 399 public void trace(Marker marker, String format, Object...argArray) { 400 trace(format, argArray); // Markers are not used by this JUL-backed implementation. 401 } 402 403 @Override 404 public void trace(Marker marker, String format, Object arg1, Object arg2) { 405 trace(format, arg1, arg2); // Markers are not used by this JUL-backed implementation. 406 } 407 408 @Override 409 public void trace(Marker marker, String msg, Throwable t) { 410 trace(msg, t); // Markers are not used by this JUL-backed implementation. 411 } 412 413 @Override 414 public void trace(String msg) { 415 if (isLoggable(FINEST)) 416 log(FINEST, msg, null); 417 } 418 419 @Override 420 public void trace(String format, Object arg) { 421 if (isLoggable(FINEST)) 422 log(FINEST, format(format, arg), null); 423 } 424 425 @Override 426 public void trace(String format, Object...arguments) { 427 if (isLoggable(FINEST)) 428 log(FINEST, format(format, arguments), null); 429 } 430 431 @Override 432 public void trace(String format, Object arg1, Object arg2) { 433 if (isLoggable(FINEST)) 434 log(FINEST, format(format, arg1, arg2), null); 435 } 436 437 @Override 438 public void trace(String msg, Throwable t) { 439 if (isLoggable(FINEST)) 440 log(FINEST, msg, t); 441 } 442 443 @Override 444 public void warn(Marker marker, String msg) { 445 warn(msg); // Markers are not used by this JUL-backed implementation. 446 } 447 448 @Override 449 public void warn(Marker marker, String format, Object arg) { 450 warn(format, arg); // Markers are not used by this JUL-backed implementation. 451 } 452 453 @Override 454 public void warn(Marker marker, String format, Object...arguments) { 455 warn(format, arguments); // Markers are not used by this JUL-backed implementation. 456 } 457 458 @Override 459 public void warn(Marker marker, String format, Object arg1, Object arg2) { 460 warn(format, arg1, arg2); // Markers are not used by this JUL-backed implementation. 461 } 462 463 @Override 464 public void warn(Marker marker, String msg, Throwable t) { 465 warn(msg, t); // Markers are not used by this JUL-backed implementation. 466 } 467 468 @Override 469 public void warn(String msg) { 470 if (isLoggable(WARNING)) 471 log(WARNING, msg, null); 472 } 473 474 @Override 475 public void warn(String msg, Object...args) { 476 if (isLoggable(WARNING)) 477 log(WARNING, format(msg, args), null); 478 } 479 480 @Override 481 public void warn(String format, Object arg) { 482 if (isLoggable(WARNING)) 483 log(WARNING, format(format, arg), null); 484 } 485 486 @Override 487 public void warn(String format, Object arg1, Object arg2) { 488 if (isLoggable(WARNING)) 489 log(WARNING, format(format, arg1, arg2), null); 490 } 491 492 @Override 493 public void warn(String msg, Throwable thrown) { 494 if (isLoggable(WARNING)) 495 log(WARNING, msg, thrown); 496 } 497 498 /** 499 * Logs a warning message with only a throwable. 500 * 501 * @param thrown The throwable to log. 502 */ 503 public void warn(Throwable thrown) { 504 if (isLoggable(WARNING)) 505 log(WARNING, "", thrown); 506 } 507 508 /** 509 * Checks if a message at the specified level would be logged. 510 * 511 * @param level The log level to check. 512 * @return <jk>true</jk> if the level is loggable, <jk>false</jk> otherwise. 513 */ 514 private boolean isLoggable(Level level) { 515 return logger.isLoggable(level); 516 } 517 518 /** 519 * Internal log method that creates a LogRecord and logs it. 520 * 521 * <p> 522 * Optionally includes source class and method name in the log record if SHOW_SOURCE is enabled. 523 * 524 * @param level The log level. 525 * @param msg The log message. 526 * @param thrown The throwable to log, or <jk>null</jk> if none. 527 */ 528 private void log(Level level, String msg, Throwable thrown) { 529 var r = new LogRecord(level, msg); 530 if (nn(thrown)) 531 r.setThrown(thrown); 532 r.setLoggerName(logger.getName()); 533 if (SHOW_SOURCE) { // HTT - SHOW_SOURCE is a static final set at class load time from system property; false branch requires property set before JVM start 534 StackTraceElement[] stack = new Throwable().getStackTrace(); 535 for (var e : stack) { 536 if (! e.getClassName().equals(cn(this))) { 537 r.setSourceClassName(e.getClassName()); 538 r.setSourceMethodName(e.getMethodName()); 539 break; 540 } 541 } 542 } 543 logger.log(r); 544 } 545}