1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.uif.util;
17
18 import java.text.DecimalFormat;
19 import java.util.Date;
20 import java.util.Deque;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.concurrent.Callable;
24
25 import org.apache.log4j.Logger;
26
27
28
29
30
31
32
33 public class ProcessLogger {
34
35 private static final Logger LOG = Logger.getLogger(ProcessLogger.class);
36
37
38
39
40 private static final ThreadLocal<Map<String, ProcessStatus>> TL_STAT = new ThreadLocal<Map<String, ProcessStatus>>();
41
42
43
44
45
46
47
48
49
50 private static class ProcessCounter {
51
52
53
54
55 private final String name;
56
57
58
59
60 private Deque<Long> start = new java.util.LinkedList<Long>();
61
62
63
64
65 private long count;
66
67
68
69
70 private long min;
71
72
73
74
75 private long max;
76
77
78
79
80
81 private long avg;
82
83
84
85
86 private String longest;
87
88
89
90
91
92
93 private ProcessCounter(String name) {
94 this.name = name;
95 }
96 }
97
98
99
100
101
102
103
104
105 private static class ProcessStatus {
106
107
108
109
110 private final String name;
111
112
113
114
115 private final long startTime;
116
117
118
119
120 private final long startFree;
121
122
123
124
125 private final long startTot;
126
127
128
129
130 private final long startMax;
131
132
133
134
135 private long lastTime;
136
137
138
139
140
141 private long lastFree;
142
143
144
145
146
147 private long lastTot;
148
149
150
151
152
153 private long lastMax;
154
155
156
157
158
159 private long diffTime;
160
161
162
163
164
165 private long diffFree;
166
167
168
169
170
171 private long diffTot;
172
173
174
175
176 private Map<String, ProcessCounter> counters = new java.util.LinkedHashMap<String, ProcessCounter>();
177
178
179
180
181
182
183
184
185
186
187 private Map<String, Long> ntraceCount = new java.util.TreeMap<String, Long>();
188
189
190
191
192
193
194
195
196 private Map<String, Long> ntraceThreshold = new java.util.HashMap<String, Long>();
197
198
199
200
201
202
203
204
205
206
207
208
209
210 private boolean verbose = LOG.isDebugEnabled();
211
212
213
214
215 private StringBuilder traceBuffer = new StringBuilder();
216
217
218
219
220 private StringBuilder extra = new StringBuilder();
221
222
223
224
225
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
241
242
243
244
245
246
247
248
249
250
251
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
269
270
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
283
284
285
286
287
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
315
316
317
318
319 private void appendTraceHeader(String name, String processDescription) {
320
321 traceBuffer.append("KRAD Process Trace (");
322 traceBuffer.append(name);
323 traceBuffer.append("): ");
324 traceBuffer.append(processDescription);
325
326
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
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
360
361
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
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
476
477
478
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
507
508 private static final String[] SIZE_INTERVALS = new String[]{"k", "M",
509 "G", "T", "E",};
510
511
512
513
514
515
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
549
550
551
552
553
554
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
571
572
573
574
575
576
577
578
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
595
596
597
598
599
600
601
602
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
619
620
621
622
623
624
625
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
634
635
636
637
638
639
640
641
642
643 public static <T> T follow(String name, String processDescription, Boolean verbose,
644 Callable<T> callableProcess) throws Exception {
645
646 if (!LOG.isInfoEnabled()) {
647 return callableProcess.call();
648 }
649
650
651 assert TL_STAT.get() == null || TL_STAT.get().get(name) == null;
652
653
654 if (TL_STAT.get() == null) {
655 TL_STAT.set(new java.util.HashMap<String, ProcessStatus>());
656 }
657
658
659 ProcessStatus processStatus = new ProcessStatus(name);
660 if (verbose != null) {
661 processStatus.verbose = verbose;
662 }
663
664 try {
665
666 TL_STAT.get().put(name, processStatus);
667
668 processStatus.appendTraceHeader(name, processDescription);
669
670
671 return callableProcess.call();
672
673 } finally {
674
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
682 processStatus.elapse();
683 processStatus.appendTraceFooter();
684
685
686 LOG.info(processStatus.traceBuffer.toString());
687 }
688 }
689
690
691
692
693
694
695 public static boolean isTraceActive() {
696 return TL_STAT.get() != null && !TL_STAT.get().isEmpty();
697 }
698
699
700
701
702
703
704
705 public static boolean isTraceActive(String name) {
706 return TL_STAT.get() != null && TL_STAT.get().containsKey(name);
707 }
708
709
710
711
712
713
714
715
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
725
726
727
728
729
730
731
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
743
744
745
746
747
748
749
750
751
752
753
754
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
766
767
768
769
770
771
772
773
774
775
776
777
778
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
791
792
793
794
795
796
797
798
799
800
801
802
803 public static long ntrace(String prefix, String suffix, long interval) {
804 return ntrace(prefix, suffix, interval, 0L);
805 }
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
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
835
836
837
838
839
840
841
842
843
844
845
846
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
855
856
857
858
859
860
861
862
863
864
865
866
867
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
899
900
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
912
913
914
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
926
927
928
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
940
941
942
943
944
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
957
958
959
960
961
962
963
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 }