001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community 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 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 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 org.kuali.rice.krad.uif.util; 017 018import java.text.DecimalFormat; 019import java.util.Date; 020import java.util.Deque; 021import java.util.Map; 022import java.util.Map.Entry; 023import java.util.concurrent.Callable; 024 025import org.apache.log4j.Logger; 026 027/** 028 * Performance monitoring log utility. 029 * 030 * @author Kuali Rice Team (rice.collab@kuali.org) 031 * @version 2.4 032 */ 033public class ProcessLogger { 034 035 private static final Logger LOG = Logger.getLogger(ProcessLogger.class); 036 037 /** 038 * Thread local mapping of process status trackers. 039 */ 040 private static final ThreadLocal<Map<String, ProcessStatus>> TL_STAT = new ThreadLocal<Map<String, ProcessStatus>>(); 041 042 /** 043 * Tracks statistics and execution count for common operations. 044 * 045 * <p> 046 * ProcessCounter is typically used to clock calls to external systems, such as database and web 047 * service requests. 048 * </p> 049 */ 050 private static class ProcessCounter { 051 052 /** 053 * The name of the counter. 054 */ 055 private final String name; 056 057 /** 058 * Stack of the start times for currently active executions. 059 */ 060 private Deque<Long> start = new java.util.LinkedList<Long>(); 061 062 /** 063 * The total count of all executions. 064 */ 065 private long count; 066 067 /** 068 * The minimum execution time, calculated among all executions within the same process. 069 */ 070 private long min; 071 072 /** 073 * The maximum execution time, calculated among all executions within the same process. 074 */ 075 private long max; 076 077 /** 078 * The average (mean) execution time, calculated among all executions within the same 079 * process. 080 */ 081 private long avg; 082 083 /** 084 * A detailed description of the execution responsible for the max execution time. 085 */ 086 private String longest; 087 088 /** 089 * Create a new counter for use within the current process. 090 * 091 * @param name The name of the counter. 092 */ 093 private ProcessCounter(String name) { 094 this.name = name; 095 } 096 } 097 098 /** 099 * Tracks the status of one more processes actively being monitoring on the current Thread. 100 * 101 * <p> 102 * This class represents the internal state of most of ProcessLogger's operations. 103 * </p> 104 */ 105 private static class ProcessStatus { 106 107 /** 108 * The name of this process trace. 109 */ 110 private final String name; 111 112 /** 113 * The start time the process started. 114 */ 115 private final long startTime; 116 117 /** 118 * Heap free space, recorded at the start of the process. 119 */ 120 private final long startFree; 121 122 /** 123 * Heap total available space, recorded at the start of the process. 124 */ 125 private final long startTot; 126 127 /** 128 * Heap max available space, recorded at the start of the process. 129 */ 130 private final long startMax; 131 132 /** 133 * The time of the last trace message reported on this process. 134 */ 135 private long lastTime; 136 137 /** 138 * Heap free space, recorded at the time the last trace message was reported on this 139 * process. 140 */ 141 private long lastFree; 142 143 /** 144 * Heap total available space, recorded at the time the last trace message was reported on 145 * this process. 146 */ 147 private long lastTot; 148 149 /** 150 * Heap max available space, recorded at the time the last trace message was reported on 151 * this process. 152 */ 153 private long lastMax; 154 155 /** 156 * The time, in milliseconds, elapsed between the last trace message reported and the 157 * previous report. 158 */ 159 private long diffTime; 160 161 /** 162 * The difference in heap free space between the last trace message reported and the 163 * previous report. 164 */ 165 private long diffFree; 166 167 /** 168 * The difference in heap total space between the last trace message reported and the 169 * previous report. 170 */ 171 private long diffTot; 172 173 /** 174 * Internal mapping of counters tracked on this process. 175 */ 176 private Map<String, ProcessCounter> counters = new java.util.LinkedHashMap<String, ProcessCounter>(); 177 178 /** 179 * Internal mapping of ntrace counters. 180 * 181 * <p> 182 * These counters are used for detecting excessive execution counts of operations that are 183 * typically fast, but may become expensive with a high number of executions. For example, 184 * ntrace counts may be used to track excessive object creation. 185 * </p> 186 */ 187 private Map<String, Long> ntraceCount = new java.util.TreeMap<String, Long>(); 188 189 /** 190 * Internal mapping of ntrace thresholds. 191 * 192 * <p> 193 * Associated with ntraceCount, this map defines thresholds below which not to report counts. 194 * </p> 195 */ 196 private Map<String, Long> ntraceThreshold = new java.util.HashMap<String, Long>(); 197 198 /** 199 * Verbose operation flag. 200 * 201 * <p> 202 * When true, all trace messages will be included regardless of elapsed time. When false, 203 * trace messages will only be included when elapsed time is 1 or more milliseconds. 204 * </p> 205 * 206 * <p> 207 * By default, verbose is true when debugging is enabled via {@link #LOG}. 208 * </p> 209 */ 210 private boolean verbose = LOG.isDebugEnabled(); 211 212 /** 213 * StringBuilder for constructing the trace message for reporting via log4j. 214 */ 215 private StringBuilder traceBuffer = new StringBuilder(); 216 217 /** 218 * StringBuilder for collecting extra information to included at the end of the trace. 219 */ 220 private StringBuilder extra = new StringBuilder(); 221 222 /** 223 * Create a status tracker for a new process. 224 * 225 * @param name The name of the process. 226 */ 227 private ProcessStatus(String name) { 228 this.name = name; 229 this.startTime = System.currentTimeMillis(); 230 this.startFree = Runtime.getRuntime().freeMemory(); 231 this.startTot = Runtime.getRuntime().totalMemory(); 232 this.startMax = Runtime.getRuntime().maxMemory(); 233 this.lastTime = startTime; 234 this.lastFree = startFree; 235 this.lastTot = startTot; 236 this.lastMax = startMax; 237 } 238 239 /** 240 * Report that time has elapsed on the process. 241 * <p> 242 * This method updates the following fields based on the current system runtime. 243 * <ul> 244 * <li>{@link #diffTime}</li> 245 * <li>{@link #diffFree}</li> 246 * <li>{@link #diffTot}</li> 247 * <li>{@link #lastTime}</li> 248 * <li>{@link #lastFree}</li> 249 * <li>{@link #lastTot}</li> 250 * <li>{@link #lastMax}</li> 251 * </ul> 252 */ 253 private void elapse() { 254 long nTime = System.currentTimeMillis(); 255 long nFree = Runtime.getRuntime().freeMemory(); 256 long nTot = Runtime.getRuntime().totalMemory(); 257 long nMax = Runtime.getRuntime().maxMemory(); 258 diffTime = nTime - lastTime; 259 diffFree = nFree - lastFree; 260 diffTot = nTot - lastTot; 261 lastTime = nTime; 262 lastFree = nFree; 263 lastTot = nTot; 264 lastMax = nMax; 265 } 266 267 /** 268 * Mark the start of a new countable operation in the current process. 269 * 270 * @param name The name of the process counter. 271 */ 272 private void countBegin(String name) { 273 ProcessCounter pc = counters.get(name); 274 if (pc == null) { 275 pc = new ProcessCounter(name); 276 counters.put(name, pc); 277 } 278 pc.start.push(new Long(System.currentTimeMillis())); 279 } 280 281 /** 282 * Mark the end of a countable operation previously reported via {@link #countBegin(String)} 283 * . 284 * 285 * @param name The name of the process counter. 286 * @param detail Details on the operation that just ended. 287 * @return The process counter. 288 */ 289 private ProcessCounter countEnd(String name, String detail) { 290 ProcessCounter pc = counters.get(name); 291 if (pc == null || pc.start.isEmpty()) { 292 return null; 293 } 294 295 long start = pc.start.pop(); 296 long elapsed = System.currentTimeMillis() - start; 297 298 if (elapsed < pc.min || pc.count == 0L) { 299 pc.min = elapsed; 300 } 301 302 if (elapsed > pc.max) { 303 pc.max = elapsed; 304 pc.longest = detail; 305 } 306 307 pc.count++; 308 pc.avg = (pc.avg * (pc.count - 1) + elapsed) / pc.count; 309 310 return pc; 311 } 312 313 /** 314 * Write a trace header on the process trace. 315 * 316 * @param name The name of the process trace. 317 * @param processDescription A description of the process trace. 318 */ 319 private void appendTraceHeader(String name, String processDescription) { 320 // Write a description of the process to the trace buffer. 321 traceBuffer.append("KRAD Process Trace ("); 322 traceBuffer.append(name); 323 traceBuffer.append("): "); 324 traceBuffer.append(processDescription); 325 326 // Write stack information related to method controlling the callable process. 327 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 328 String callerClassName = stackTrace[3].getClassName(); 329 int indexOfPeriod = callerClassName.lastIndexOf('.'); 330 String callerPackageName = ""; 331 if (indexOfPeriod != -1) { 332 callerPackageName = callerClassName.substring(0, indexOfPeriod); 333 } 334 int stackTraceIndex = 3; 335 while (stackTraceIndex < stackTrace.length - 1 336 && (stackTrace[stackTraceIndex].getClassName().startsWith("sun.") 337 || stackTrace[stackTraceIndex].getClassName().startsWith("java.") 338 || stackTrace[stackTraceIndex].getClassName().startsWith( 339 ProcessLogger.class.getPackage().getName()) || stackTrace[stackTraceIndex] 340 .getClassName().startsWith(callerPackageName))) { 341 if (!ProcessLogger.class.getName().equals(stackTrace[stackTraceIndex].getClassName())) { 342 traceBuffer.append("\n at ").append(stackTrace[stackTraceIndex]); 343 } 344 stackTraceIndex++; 345 } 346 traceBuffer.append("\n at ").append(stackTrace[stackTraceIndex]); 347 348 // Write initial heap state and start time. 349 traceBuffer.append("\nInitial Memory Usage: "); 350 traceBuffer.append(memoryToString(startFree, startTot, 351 startMax)); 352 353 if (LOG.isInfoEnabled() && verbose) { 354 LOG.debug("Processing Started\n" + traceBuffer.toString()); 355 } 356 } 357 358 /** 359 * Write a log message on this process trace. 360 * 361 * @param message The message to write. 362 */ 363 public void appendTraceMessage(String message) { 364 if (LOG.isDebugEnabled() && verbose) { 365 LOG.debug(message 366 + " (" 367 + name 368 + ")\nElapsed Time: " 369 + intervalToString(diffTime) 370 + "\nMemory Usage: " 371 + memoryToString(lastFree, lastTot, 372 lastMax) 373 + "\nMemory Delta: " 374 + memoryToString(diffFree, lastTot, 375 lastMax) + " - tot delta: " 376 + sizeToString(diffTot)); 377 } 378 379 if (LOG.isInfoEnabled() && (verbose || diffTime > 0L)) { 380 traceBuffer.append('\n'); 381 382 if (message.length() < 40) { 383 traceBuffer.append(message); 384 for (int i = message.length(); i < 40; i++) { 385 traceBuffer.append('.'); 386 } 387 } else { 388 traceBuffer.append(message.substring(0, 40)); 389 } 390 391 traceBuffer.append(intervalToString(diffTime)); 392 traceBuffer.append(' '); 393 traceBuffer.append(intervalToString(lastTime 394 - startTime)); 395 traceBuffer.append(' '); 396 traceBuffer.append(sizeToString(lastFree)); 397 traceBuffer.append(' '); 398 traceBuffer.append(sizeToString(diffFree)); 399 } 400 } 401 402 /** 403 * Write a trace footer on the process trace. 404 */ 405 public void appendTraceFooter() { 406 traceBuffer.append('\n'); 407 String message = "Processing Complete"; 408 traceBuffer.append(message); 409 for (int i = message.length(); i < 40; i++) { 410 traceBuffer.append('.'); 411 } 412 traceBuffer.append(intervalToString(diffTime)); 413 traceBuffer.append(' '); 414 traceBuffer.append(intervalToString(lastTime 415 - startTime)); 416 traceBuffer.append(' '); 417 traceBuffer.append(sizeToString(lastFree)); 418 traceBuffer.append(' '); 419 traceBuffer.append(sizeToString(diffFree)); 420 if (!ntraceCount.isEmpty()) { 421 traceBuffer.append("\nMonitors:"); 422 for (Entry<String, Long> ce : ntraceCount.entrySet()) { 423 424 Long threshold = ntraceThreshold.get(ce.getKey()); 425 if (threshold != null && threshold >= ce.getValue()) { 426 continue; 427 } 428 429 traceBuffer.append("\n "); 430 StringBuilder sb = new StringBuilder(ce.getKey()); 431 int iocc = sb.indexOf("::"); 432 if (iocc == -1) 433 sb.append(":" + ce.getValue()); 434 else 435 sb.insert(iocc + 1, ce.getValue()); 436 traceBuffer.append(sb); 437 } 438 } 439 if (!counters.isEmpty()) { 440 traceBuffer.append("\nCounters:"); 441 for (ProcessCounter pc : counters.values()) { 442 traceBuffer.append("\n "); 443 traceBuffer.append(pc.name); 444 traceBuffer.append(": "); 445 traceBuffer.append(pc.count); 446 traceBuffer.append(" ("); 447 traceBuffer.append(intervalToString(pc.min)); 448 traceBuffer.append("/"); 449 traceBuffer.append(intervalToString(pc.max)); 450 traceBuffer.append("/"); 451 traceBuffer.append(intervalToString(pc.avg)); 452 traceBuffer.append(")"); 453 if (pc.longest != null && !"".equals(pc.longest)) { 454 traceBuffer.append("\n longest : "); 455 traceBuffer.append(pc.longest); 456 } 457 } 458 } 459 traceBuffer.append("\nElapsed Time: "); 460 traceBuffer.append(intervalToString(lastTime 461 - startTime)); 462 traceBuffer.append("\nMemory Usage: "); 463 traceBuffer.append(memoryToString(lastFree, lastTot, 464 lastMax)); 465 traceBuffer.append("\nMemory Delta: "); 466 traceBuffer.append(memoryToString(lastFree 467 - startFree, lastTot, lastMax)); 468 traceBuffer.append(" - tot delta: "); 469 traceBuffer.append(sizeToString(lastTot - startTot)); 470 } 471 472 } 473 474 /** 475 * Print a human readable time duration. 476 * 477 * @param millis The number of milliseconds. 478 * @return A human readable representation of the time interval represented by millis. 479 */ 480 public static String intervalToString(long millis) { 481 DecimalFormat df = new DecimalFormat("000"); 482 StringBuilder sb = new StringBuilder(); 483 sb.append('.'); 484 sb.append(df.format(millis % 1000)); 485 df.applyPattern("00"); 486 long sec = millis / 1000; 487 sb.insert(0, df.format(sec % 60)); 488 long min = sec / 60; 489 sb.insert(0, ':'); 490 sb.insert(0, df.format(min % 60)); 491 long hours = min / 60; 492 if (hours > 0) { 493 sb.insert(0, ':'); 494 sb.insert(0, df.format(hours % 24)); 495 } 496 long days = hours / 24; 497 if (days > 0) { 498 sb.insert(0, " days, "); 499 sb.insert(0, days); 500 } 501 502 return sb.toString(); 503 } 504 505 /** 506 * The steps for printing sizes. 507 */ 508 private static final String[] SIZE_INTERVALS = new String[]{"k", "M", 509 "G", "T", "E",}; 510 511 /** 512 * Print a human readable size. 513 * 514 * @param bytes The number of bytes. 515 * @return A human readable representation of the size. 516 */ 517 public static String sizeToString(long bytes) { 518 DecimalFormat df = new DecimalFormat("000"); 519 StringBuilder sb = new StringBuilder(); 520 int i = -1; 521 int mod = 0; 522 523 if (bytes < 0) { 524 sb.append('-'); 525 bytes = Math.abs(bytes); 526 } 527 528 while (bytes / 1024 > 0 && i < SIZE_INTERVALS.length) { 529 i++; 530 mod = (int) (bytes % 1024); 531 bytes /= 1024; 532 } 533 sb.append(bytes); 534 535 if (mod > 0) { 536 sb.append('.'); 537 sb.append(df.format(mod * 1000 / 1024)); 538 } 539 540 if (i >= 0) { 541 sb.append(SIZE_INTERVALS[i]); 542 } 543 544 return sb.toString(); 545 } 546 547 /** 548 * Get a human readable representation of the system memory. 549 * 550 * @param free free heap memory, in bytes 551 * @param tot total heap memory, in bytes 552 * @param max maximum total heap memory, in bytes 553 * 554 * @return A human readable representation of the system memory. 555 */ 556 public static String memoryToString(long free, long tot, long max) { 557 StringBuilder sb = new StringBuilder(); 558 sb.append(sizeToString(free)); 559 sb.append('/'); 560 sb.append(sizeToString(tot)); 561 sb.append('/'); 562 sb.append(sizeToString(max)); 563 sb.append(" - "); 564 sb.append(free * 100 / tot); 565 sb.append("% free"); 566 return sb.toString(); 567 } 568 569 /** 570 * Follow a callable process using the system default verbose setting and checked exception 571 * handling. 572 * 573 * @param <T> callable return type 574 * @param name The name of the process. 575 * @param processDescription A message describing the process to report at the top of the trace. 576 * @param callableProcess The callable process. 577 * @return The result of calling the process. 578 * @throws IllegalStateException If checked exception occurs within the callable process. 579 */ 580 public static <T> T safeFollow(final String name, final String processDescription, 581 final Callable<T> callableProcess) { 582 try { 583 return follow(name, processDescription, null, callableProcess); 584 } catch (Exception e) { 585 if (e instanceof RuntimeException) { 586 throw (RuntimeException) e; 587 } else { 588 throw new IllegalStateException("Error in followed process " + name + " - " + processDescription, e); 589 } 590 } 591 } 592 593 /** 594 * Follow a callable process with checked exception handling. 595 * 596 * @param <T> callable return type 597 * @param name The name of the process. 598 * @param processDescription A message describing the process to report at the top of the trace. 599 * @param verbose Verbose operation flag, see {@link ProcessStatus#verbose}. 600 * @param callableProcess The callable process. 601 * @return The result of calling the process. 602 * @throws IllegalStateException If checked exception occurs within the callable process. 603 */ 604 public static <T> T safeFollow(final String name, final String processDescription, 605 final Boolean verbose, final Callable<T> callableProcess) { 606 try { 607 return follow(name, processDescription, verbose, callableProcess); 608 } catch (Exception e) { 609 if (e instanceof RuntimeException) { 610 throw (RuntimeException) e; 611 } else { 612 throw new IllegalStateException("Error in followed process " + name + " - " + processDescription, e); 613 } 614 } 615 } 616 617 /** 618 * Follow a callable process using the system default verbose setting. 619 * 620 * @param <T> callable return type 621 * @param name The name of the process. 622 * @param processDescription A message describing the process to report at the top of the trace. 623 * @param callableProcess The callable process. 624 * @return The result of calling the process. 625 * @throws Exception from {@link Callable#call()} 626 */ 627 public static <T> T follow(String name, String processDescription, Callable<T> callableProcess) 628 throws Exception { 629 return follow(name, processDescription, null, callableProcess); 630 } 631 632 /** 633 * Follow a callable process. 634 * 635 * @param <T> callable return type 636 * @param name The name of the process. 637 * @param processDescription A message describing the process to report at the top of the trace. 638 * @param verbose True to note every trace entry, false to only note those that take longer than 1ms. 639 * @param callableProcess The callable process. 640 * @return The result of calling the process. 641 * @throws Exception from {@link Callable#call()} 642 */ 643 public static <T> T follow(String name, String processDescription, Boolean verbose, 644 Callable<T> callableProcess) throws Exception { 645 // When logging is not at least info enabled, process tracing has no effect - short circuit. 646 if (!LOG.isInfoEnabled()) { 647 return callableProcess.call(); 648 } 649 650 // Ensure that statistics are not already active for a process with the same name. 651 assert TL_STAT.get() == null || TL_STAT.get().get(name) == null; 652 653 // Bind a new status tracking map to the current thread, if not already bound. 654 if (TL_STAT.get() == null) { 655 TL_STAT.set(new java.util.HashMap<String, ProcessStatus>()); 656 } 657 658 // Create a new status tracker for monitoring this process. 659 ProcessStatus processStatus = new ProcessStatus(name); 660 if (verbose != null) { 661 processStatus.verbose = verbose; 662 } 663 664 try { 665 // Bind the status tracker to the current thread. 666 TL_STAT.get().put(name, processStatus); 667 668 processStatus.appendTraceHeader(name, processDescription); 669 670 // Call the process. 671 return callableProcess.call(); 672 673 } finally { 674 // Clear the thread state to prevent memory growth. 675 if (TL_STAT.get() != null) { 676 TL_STAT.get().remove(name); 677 if (TL_STAT.get().isEmpty()) 678 TL_STAT.remove(); 679 } 680 681 // Calculate time and heap utilization since the last trace message. 682 processStatus.elapse(); 683 processStatus.appendTraceFooter(); 684 685 // Write process trace at INFO level 686 LOG.info(processStatus.traceBuffer.toString()); 687 } 688 } 689 690 /** 691 * Determine if any process traces are active on the current thread. 692 * 693 * @return True if any process traces are active on the current thread, false if not. 694 */ 695 public static boolean isTraceActive() { 696 return TL_STAT.get() != null && !TL_STAT.get().isEmpty(); 697 } 698 699 /** 700 * Determine if a process trace is active on the current thread. 701 * 702 * @param name The name of the process trace. 703 * @return True if the named process trace is active on the current thread, false if not. 704 */ 705 public static boolean isTraceActive(String name) { 706 return TL_STAT.get() != null && TL_STAT.get().containsKey(name); 707 } 708 709 /** 710 * Determine if the named process is active on the current thread with the verbose flag set to 711 * true. 712 * 713 * @param name The name of the process trace. 714 * @return True if the named process trace is active on the current thread with the verbose flag 715 * set to true, false if not. 716 */ 717 public static boolean isVerbose(String name) { 718 ProcessStatus processStatus = TL_STAT.get() == null ? null : TL_STAT.get().get( 719 name); 720 return processStatus != null && processStatus.verbose; 721 } 722 723 /** 724 * Modify the verbose flag on a process trace active on the current thread. 725 * 726 * <p> 727 * This method has no impact if a process trace with the given name is not active. 728 * </p> 729 * 730 * @param name The name of the process trace. 731 * @param verbose The verbose flag setting to apply to the named process trace. 732 */ 733 public static void setVerbose(String name, boolean verbose) { 734 ProcessStatus ps = TL_STAT.get() == null ? null : TL_STAT.get().get( 735 name); 736 if (ps != null) { 737 ps.verbose = verbose; 738 } 739 } 740 741 /** 742 * Report a trace message on all process traces active on the current thread. 743 * 744 * <p> 745 * The first 40 characters of the message will be printed on the traces along with timing and 746 * heap utilization statistics. 747 * </p> 748 * 749 * <p> 750 * When debug logging is enabled, the entire message will be printed via log4j at the DEBUG 751 * level. 752 * </p> 753 * 754 * @param message The message to report on the trace. 755 */ 756 public static void trace(String message) { 757 if (TL_STAT.get() != null) { 758 for (String k : TL_STAT.get().keySet()) { 759 trace(k, message); 760 } 761 } 762 } 763 764 /** 765 * Report a trace message on a process trace, if active on the current thread. 766 * 767 * <p> 768 * The first 40 characters of the message will be printed on the trace along with timing and 769 * heap utilization statistics. 770 * </p> 771 * 772 * <p> 773 * When debug logging is enabled, the entire message will be printed via log4j at the DEBUG 774 * level. 775 * </p> 776 * 777 * @param name The name of the process trace. 778 * @param message The message to report on the trace. 779 */ 780 public static void trace(String name, String message) { 781 ProcessStatus processStatus = TL_STAT.get() == null ? null : TL_STAT.get().get( 782 name); 783 if (processStatus != null) { 784 processStatus.elapse(); 785 processStatus.appendTraceMessage(message); 786 } 787 } 788 789 /** 790 * Count instances of a typically fast operation that may become expensive given a high number 791 * of executions. 792 * 793 * <p> 794 * When the specified number of instances of the same operation have been counted, then a 795 * message indicating the execution count will be added to the process trace. 796 * </p> 797 * 798 * @param prefix The message to report before the count. 799 * @param suffix The message to report after the count. 800 * @param interval The number of instances to count between reports on the process trace. 801 * @return The execution count of the operation on trace with the highest number of executions. 802 */ 803 public static long ntrace(String prefix, String suffix, long interval) { 804 return ntrace(prefix, suffix, interval, 0L); 805 } 806 807 /** 808 * Count instances of a typically fast operation that may become expensive given a high number 809 * of executions. 810 * 811 * <p> 812 * When the specified number of instances of the same operation have been counted, then a 813 * message indicating the execution count will be added to the process trace. 814 * </p> 815 * 816 * @param prefix The message to report before the count. 817 * @param suffix The message to report after the count. 818 * @param interval The number of instances to count between reports on the process trace. 819 * @param threshold The number of instances below which not to report monitored counts. 820 * @return The execution count of the operation on trace with the highest number of executions. 821 */ 822 public static long ntrace(String prefix, String suffix, long interval, long threshold) { 823 long rv = 0L; 824 if (TL_STAT.get() != null) { 825 for (String k : TL_STAT.get().keySet()) { 826 rv = Math.max(rv, ntrace(k, prefix, suffix, interval, threshold)); 827 } 828 } 829 830 return rv; 831 } 832 833 /** 834 * Count instances of a typically fast operation that may become expensive given a high number 835 * of executions. 836 * 837 * <p> 838 * When the specified number of instances of the same operation have been counted, then a 839 * message indicating the execution count will be added to the process trace. 840 * </p> 841 * 842 * @param name The name of the trace. 843 * @param prefix The message to report before the count. 844 * @param suffix The message to report after the count. 845 * @param interval The number of instances to count between reports on the process trace. 846 * @return The execution count of the operation on the named trace. 847 */ 848 public static long ntrace(String name, String prefix, String suffix, 849 long interval) { 850 return ntrace(name, prefix, suffix, interval, 0L); 851 } 852 853 /** 854 * Count instances of a typically fast operation that may become expensive given a high number 855 * of executions. 856 * 857 * <p> 858 * When the specified number of instances of the same operation have been counted, then a 859 * message indicating the execution count will be added to the process trace. 860 * </p> 861 * 862 * @param name The name of the trace. 863 * @param prefix The message to report before the count. 864 * @param suffix The message to report after the count. 865 * @param interval The number of instances to count between reports on the process trace. 866 * @param threshold The number of instances below which not to report monitored counts. 867 * @return The execution count of the operation on the named trace. 868 */ 869 public static long ntrace(String name, String prefix, String suffix, 870 long interval, long threshold) { 871 ProcessStatus processStatus = TL_STAT.get() == null ? null : TL_STAT.get().get( 872 name); 873 String nTraceCountKey = prefix + suffix; 874 Long nTraceCount = processStatus.ntraceCount.get(nTraceCountKey); 875 876 if (nTraceCount == null) { 877 nTraceCount = 0L; 878 } 879 880 processStatus.ntraceCount.put(nTraceCountKey, ++nTraceCount); 881 882 if (threshold > 0) { 883 processStatus.ntraceThreshold.put(nTraceCountKey, threshold); 884 } 885 886 if (nTraceCount % interval == 0) { 887 String msg = prefix + nTraceCount + suffix; 888 trace(msg); 889 if (LOG.isDebugEnabled()) { 890 LOG.debug(msg, new Throwable()); 891 } 892 } 893 894 return nTraceCount; 895 } 896 897 /** 898 * Mark the start of a new countable operation on all active process traces. 899 * 900 * @param name The name of the process counter. 901 */ 902 public static void countBegin(String name) { 903 if (TL_STAT.get() != null) { 904 for (String k : TL_STAT.get().keySet()) { 905 countBegin(k, name); 906 } 907 } 908 } 909 910 /** 911 * Mark the start of a new countable operation on an active process trace. 912 * 913 * @param traceName The name of the process trace. 914 * @param name The name of the process counter. 915 */ 916 public static void countBegin(String traceName, String name) { 917 ProcessStatus ps = TL_STAT.get() == null ? null : TL_STAT.get().get( 918 traceName); 919 if (ps != null) { 920 ps.countBegin(name); 921 } 922 } 923 924 /** 925 * Mark the end of a countable operation previously reported via {@link #countBegin(String)} . 926 * 927 * @param name The name of the process counter. 928 * @param detail Details on the operation that just ended. 929 */ 930 public static void countEnd(String name, String detail) { 931 if (TL_STAT.get() != null) { 932 for (String k : TL_STAT.get().keySet()) { 933 countEnd(k, name, detail); 934 } 935 } 936 } 937 938 /** 939 * Mark the end of a countable operation previously reported via 940 * {@link #countBegin(String, String)} . 941 * 942 * @param traceName The name of the process trace. 943 * @param name The name of the process counter. 944 * @param detail Details on the operation that just ended. 945 */ 946 public static void countEnd(String traceName, String name, 947 String detail) { 948 ProcessStatus processStatus = TL_STAT.get() == null ? null : TL_STAT.get().get( 949 traceName); 950 if (processStatus != null) { 951 processStatus.countEnd(name, detail); 952 } 953 } 954 955 /** 956 * Append an informational message to a process trace. 957 * 958 * <p> 959 * The message will additionally be logged at the INFO level. 960 * </p> 961 * 962 * @param traceName The name of the process trace. 963 * @param message The information message. 964 */ 965 public static void addExtra(String traceName, Object message) { 966 ProcessStatus processStatus = TL_STAT.get() == null ? null : TL_STAT.get().get( 967 traceName); 968 if (processStatus == null) { 969 return; 970 } 971 StringBuilder sb = new StringBuilder(); 972 sb.append("Information Message Reported at "); 973 sb.append(new Date()); 974 sb.append(":\n"); 975 sb.append(message); 976 LOG.info(sb.toString()); 977 processStatus.extra.append(sb); 978 processStatus.extra.append("\n\n"); 979 } 980 981}