1
18 package net.bull.javamelody.internal.model;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Calendar;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Date;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.concurrent.ConcurrentHashMap;
33
34 import net.bull.javamelody.JdbcWrapper;
35 import net.bull.javamelody.internal.common.LOG;
36 import net.bull.javamelody.internal.common.Parameters;
37 import net.bull.javamelody.internal.model.Counter.CounterRequestContextComparator;
38 import net.bull.javamelody.internal.model.SamplingProfiler.SampledMethod;
39 import net.bull.javamelody.internal.publish.MetricsPublisher;
40
41
45 public class Collector {
46 private static final long NOT_A_NUMBER = Long.MIN_VALUE;
47
48
49 private final int periodMillis;
50 private final String application;
51 private final List<Counter> counters;
52 private final SamplingProfiler samplingProfiler;
53 private final Map<String, JRobin> requestJRobinsById = new ConcurrentHashMap<>();
54
55 private final Map<String, JRobin> counterJRobins = new LinkedHashMap<>();
56 private final Map<String, JRobin> otherJRobins = new LinkedHashMap<>();
57
58
59
60
61 private final Map<Counter, CounterRequest> globalRequestsByCounter = new HashMap<>();
62 private final Map<String, CounterRequest> requestsById = new HashMap<>();
63 private final Map<Counter, Counter> dayCountersByCounter = new LinkedHashMap<>();
64 private final Map<Counter, Boolean> firstCollectDoneByCounter = new HashMap<>();
65 private long transactionCount = NOT_A_NUMBER;
66 private long cpuTimeMillis = NOT_A_NUMBER;
67 private long gcTimeMillis = NOT_A_NUMBER;
68 private long tomcatBytesReceived = NOT_A_NUMBER;
69 private long tomcatBytesSent = NOT_A_NUMBER;
70 private long lastCollectDuration;
71 private Throwable lastCollectorException;
72 private long estimatedMemorySize;
73 private long diskUsage;
74 private Date lastDateOfDeletedObsoleteFiles = new Date();
75 private boolean stopped;
76 private final boolean noDatabase = Parameters.isNoDatabase();
77 private List<MetricsPublisher> metricsPublishers;
78 private final WebappVersions webappVersions;
79 private final StorageLock storageLock;
80
81
86 public Collector(String application, List<Counter> counters) {
87 this(application, counters, null);
88 }
89
90
96 public Collector(String application, List<Counter> counters,
97 SamplingProfiler samplingProfiler) {
98 super();
99 assert application != null;
100 assert counters != null;
101 this.application = application;
102 this.counters = Collections.unmodifiableList(new ArrayList<>(counters));
103 this.samplingProfiler = samplingProfiler;
104
105 for (final Counter counter : counters) {
106 for (final Counter otherCounter : counters) {
107
108 assert counter == otherCounter || !counter.getName().equals(otherCounter.getName());
109 }
110 counter.setApplication(application);
111 final Counter dayCounter = new PeriodCounterFactory(counter)
112 .createDayCounterAtDate(new Date());
113 dayCountersByCounter.put(counter, dayCounter);
114 }
115 periodMillis = Parameters.getResolutionSeconds() * 1000;
116
117 try {
118
119
120 for (final Counter counter : counters) {
121 counter.readFromFile();
122 }
123
124 for (final Counter counter : counters) {
125 dayCountersByCounter.get(counter).readFromFile();
126 }
127 LOG.debug("counters data read from files in "
128 + Parameters.getStorageDirectory(application));
129 } catch (final IOException e) {
130
131
132 LOG.warn("exception while reading counters data from files in "
133 + Parameters.getStorageDirectory(application), e);
134 }
135
136
137 this.storageLock = new StorageLock(application);
138
139 this.webappVersions = new WebappVersions(application);
140 }
141
142
146 public String getApplication() {
147 return application;
148 }
149
150
154 public SamplingProfiler getSamplingProfiler() {
155 return samplingProfiler;
156 }
157
158 public List<SampledMethod> getHotspots() {
159 if (samplingProfiler == null) {
160 throw new IllegalStateException("Hotspots sampling is not enabled in this server");
161 }
162 return samplingProfiler.getHotspots(1000);
163 }
164
165 public Map<String, Date> getDatesByWebappVersions() {
166 return webappVersions.getDatesByVersions();
167 }
168
169
172 public List<Counter> getCounters() {
173 return counters;
174 }
175
176
180 public Counter getCounterByName(String counterName) {
181 for (final Counter counter : counters) {
182 if (counter.getName().equals(counterName)) {
183 return counter;
184 }
185 }
186 return null;
187 }
188
189 public Counter getCounterByRequestId(CounterRequest request) {
190 final String requestId = request.getId();
191 for (final Counter counter : counters) {
192 if (counter.isRequestIdFromThisCounter(requestId)) {
193 return counter;
194 }
195 }
196 return null;
197 }
198
199 public List<CounterRequestContext> getRootCurrentContexts(List<Counter> newParentCounters) {
200 final List<CounterRequestContext> rootCurrentContexts = new ArrayList<>();
201 for (final Counter counter : counters) {
202 if (counter.isDisplayed()) {
203
204
205 rootCurrentContexts.addAll(counter.getOrderedRootCurrentContexts());
206 }
207 }
208 if (rootCurrentContexts.size() > 1) {
209 Collections.sort(rootCurrentContexts, Collections
210 .reverseOrder(new CounterRequestContextComparator(System.currentTimeMillis())));
211
212 CounterRequestContext.replaceParentCounters(rootCurrentContexts, newParentCounters);
213 }
214 return rootCurrentContexts;
215 }
216
217 public long getLastCollectDuration() {
218 return lastCollectDuration;
219 }
220
221 public Throwable getLastCollectorException() {
222 return lastCollectorException;
223 }
224
225 public long getEstimatedMemorySize() {
226 return estimatedMemorySize;
227 }
228
229 public long getDiskUsage() {
230 if (diskUsage == 0) {
231
232
233
234 final File storageDir = Parameters.getStorageDirectory(application);
235 long sum = 0;
236 final File[] files = storageDir.listFiles();
237 if (files != null) {
238 for (final File file : files) {
239 sum += file.length();
240 }
241 }
242 diskUsage = sum;
243 }
244 return diskUsage;
245 }
246
247 public List<Counter> getRangeCounters(Range range) throws IOException {
248 if (range.getPeriod() == Period.TOUT) {
249 return new ArrayList<>(counters);
250 }
251 final Collection<Counter> currentDayCounters = dayCountersByCounter.values();
252 final List<Counter> result = new ArrayList<>(currentDayCounters.size());
253 for (final Counter dayCounter : currentDayCounters) {
254 final Counter counter = getRangeCounter(range, dayCounter);
255 result.add(counter);
256 }
257 return result;
258 }
259
260 private Counter getRangeCounter(Range range, Counter dayCounter) throws IOException {
261 final PeriodCounterFactory periodCounterFactory = new PeriodCounterFactory(dayCounter);
262 final Counter counter;
263 if (range.getPeriod() == null) {
264 counter = periodCounterFactory.getCustomCounter(range);
265 } else {
266 switch (range.getPeriod()) {
267 case JOUR:
268 counter = periodCounterFactory.getDayCounter();
269 break;
270 case SEMAINE:
271 counter = periodCounterFactory.getWeekCounter();
272 break;
273 case MOIS:
274 counter = periodCounterFactory.getMonthCounter();
275 break;
276 case ANNEE:
277 counter = periodCounterFactory.getYearCounter();
278 break;
279 case TOUT:
280 throw new IllegalStateException(range.getPeriod().toString());
281 default:
282 throw new IllegalArgumentException(range.getPeriod().toString());
283 }
284 }
285 return counter;
286 }
287
288 public List<Counter> getRangeCountersToBeDisplayed(Range range) throws IOException {
289 final List<Counter> result = new ArrayList<>(getRangeCounters(range));
290 final Iterator<Counter> it = result.iterator();
291 while (it.hasNext()) {
292 final Counter counter = it.next();
293 if (!counter.isDisplayed() || counter.isJobCounter()) {
294 it.remove();
295 }
296 }
297 return Collections.unmodifiableList(result);
298 }
299
300 public Counter getRangeCounter(Range range, String counterName) throws IOException {
301 final Counter counter = getCounterByName(counterName);
302 if (counter == null) {
303 throw new IllegalArgumentException(counterName);
304 }
305 if (range.getPeriod() == Period.TOUT) {
306 return counter;
307 }
308 return getRangeCounter(range, dayCountersByCounter.get(counter));
309 }
310
311 public void collectLocalContextWithoutErrors() {
312
313
314 try {
315 final JavaInformations javaInformations = new JavaInformations(
316 Parameters.getServletContext(), false);
317
318 collectWithoutErrors(Collections.singletonList(javaInformations));
319 } catch (final Throwable t) {
320
321 LOG.warn("exception while collecting data: " + t, t);
322 }
323 }
324
325 public void collectWithoutErrors(List<JavaInformations> javaInformationsList) {
326 assert javaInformationsList != null;
327 final long start = System.currentTimeMillis();
328 try {
329 estimatedMemorySize = collect(javaInformationsList);
330 lastCollectorException = null;
331 } catch (final Throwable t) {
332 lastCollectorException = t;
333
334 LOG.warn("exception while collecting data: " + t, t);
335 }
336
337
338 lastCollectDuration = Math.max(0, System.currentTimeMillis() - start);
339 }
340
341 private synchronized long collect(List<JavaInformations> javaInformationsList)
342 throws IOException {
343 long memorySize = 0;
344 try {
345
346 if (!javaInformationsList.isEmpty()) {
347 if (metricsPublishers == null) {
348 metricsPublishers = MetricsPublisher.getMetricsPublishers(javaInformationsList);
349 }
350 collectJavaInformations(javaInformationsList);
351 collectOtherJavaInformations(javaInformationsList);
352 collectTomcatInformations(javaInformationsList);
353 }
354 for (final Counter counter : counters) {
355
356 dayCountersByCounter.get(counter).setDisplayed(counter.isDisplayed());
357
358
359
360 if (counter.isDisplayed()) {
361
362
363 memorySize += collectCounterData(counter);
364 }
365 }
366 } finally {
367 if (metricsPublishers != null) {
368 for (final MetricsPublisher metricsPublisher : metricsPublishers) {
369 metricsPublisher.send();
370 }
371 }
372 }
373
374 final Calendar calendar = Calendar.getInstance();
375 final int currentDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
376 calendar.setTime(lastDateOfDeletedObsoleteFiles);
377 if (calendar.get(Calendar.DAY_OF_YEAR) != currentDayOfYear) {
378
379
380 try {
381 deleteObsoleteFiles();
382 } finally {
383 lastDateOfDeletedObsoleteFiles = new Date();
384 }
385 }
386
387 if (!javaInformationsList.isEmpty()) {
388 final String webappVersion = javaInformationsList.get(0).getWebappVersion();
389 webappVersions.addVersionIfNeeded(webappVersion);
390 }
391
392 return memorySize;
393 }
394
395 private void collectJavaInformations(List<JavaInformations> javaInformationsList)
396 throws IOException {
397 long usedMemory = 0;
398 long processesCpuTimeMillis = 0;
399 int availableProcessors = 0;
400 int sessionCount = 0;
401 int activeThreadCount = 0;
402 int activeConnectionCount = 0;
403 int usedConnectionCount = 0;
404
405 for (final JavaInformations javaInformations : javaInformationsList) {
406 final MemoryInformations memoryInformations = javaInformations.getMemoryInformations();
407 usedMemory = add(memoryInformations.getUsedMemory(), usedMemory);
408 sessionCount = add(javaInformations.getSessionCount(), sessionCount);
409 activeThreadCount = add(javaInformations.getActiveThreadCount(), activeThreadCount);
410 activeConnectionCount = add(javaInformations.getActiveConnectionCount(),
411 activeConnectionCount);
412 usedConnectionCount = add(javaInformations.getUsedConnectionCount(),
413 usedConnectionCount);
414
415
416 availableProcessors = add(Math.max(javaInformations.getAvailableProcessors(), 1),
417 availableProcessors);
418
419 processesCpuTimeMillis = add(javaInformations.getProcessCpuTimeMillis(),
420 processesCpuTimeMillis);
421 }
422 collectJRobinValues(usedMemory, processesCpuTimeMillis, availableProcessors, sessionCount,
423 activeThreadCount, activeConnectionCount, usedConnectionCount);
424 }
425
426
427 private void collectOtherJavaInformations(List<JavaInformations> javaInformationsList)
428 throws IOException {
429
430 long usedNonHeapMemory = 0;
431 long usedBufferedMemory = 0;
432 int loadedClassesCount = 0;
433 long garbageCollectionTimeMillis = 0;
434 long usedPhysicalMemorySize = 0;
435 long usedSwapSpaceSize = 0;
436 int availableProcessors = 0;
437 int sessionCount = 0;
438 long sessionAgeSum = 0;
439 int threadCount = 0;
440 long databaseTransactionCount = 0;
441 double systemLoadAverage = 0;
442 long unixOpenFileDescriptorCount = 0;
443 long freeDiskSpaceInTemp = Long.MAX_VALUE;
444 long usableDiskSpaceInTemp = Long.MAX_VALUE;
445 double systemCpuLoad = 0;
446
447 for (final JavaInformations javaInformations : javaInformationsList) {
448 final MemoryInformations memoryInformations = javaInformations.getMemoryInformations();
449 sessionCount = add(javaInformations.getSessionCount(), sessionCount);
450 sessionAgeSum = add(javaInformations.getSessionAgeSum(), sessionAgeSum);
451 threadCount = add(javaInformations.getThreadCount(), threadCount);
452 databaseTransactionCount = add(javaInformations.getTransactionCount(),
453 databaseTransactionCount);
454
455 availableProcessors = add(Math.max(javaInformations.getAvailableProcessors(), 1),
456 availableProcessors);
457 usedNonHeapMemory = add(memoryInformations.getUsedNonHeapMemory(), usedNonHeapMemory);
458 usedBufferedMemory = add(memoryInformations.getUsedBufferedMemory(),
459 usedBufferedMemory);
460 loadedClassesCount = add(memoryInformations.getLoadedClassesCount(),
461 loadedClassesCount);
462 usedPhysicalMemorySize = add(memoryInformations.getUsedPhysicalMemorySize(),
463 usedPhysicalMemorySize);
464 usedSwapSpaceSize = add(memoryInformations.getUsedSwapSpaceSize(), usedSwapSpaceSize);
465 garbageCollectionTimeMillis = add(memoryInformations.getGarbageCollectionTimeMillis(),
466 garbageCollectionTimeMillis);
467
468 systemLoadAverage = add(javaInformations.getSystemLoadAverage(), systemLoadAverage);
469
470 unixOpenFileDescriptorCount = add(javaInformations.getUnixOpenFileDescriptorCount(),
471 unixOpenFileDescriptorCount);
472
473 freeDiskSpaceInTemp = Math.min(javaInformations.getFreeDiskSpaceInTemp(),
474 freeDiskSpaceInTemp);
475 usableDiskSpaceInTemp = Math.min(javaInformations.getUsableDiskSpaceInTemp(),
476 usableDiskSpaceInTemp);
477 systemCpuLoad = add(javaInformations.getSystemCpuLoad(), systemCpuLoad);
478 }
479
480
481 if (garbageCollectionTimeMillis >= 0) {
482 if (this.gcTimeMillis != NOT_A_NUMBER) {
483
484 final int gcPercentage = Math
485 .min((int) ((garbageCollectionTimeMillis - this.gcTimeMillis) * 100
486 / periodMillis / availableProcessors), 100);
487 addJRobinValue(getOtherJRobin("gc"), gcPercentage);
488 } else {
489 addJRobinValue(getOtherJRobin("gc"), 0d);
490 }
491 this.gcTimeMillis = garbageCollectionTimeMillis;
492 }
493
494 final Map<String, Double> otherJRobinsValues = new LinkedHashMap<>();
495 otherJRobinsValues.put("threadCount", (double) threadCount);
496 otherJRobinsValues.put("loadedClassesCount", (double) loadedClassesCount);
497 otherJRobinsValues.put("usedBufferedMemory", (double) usedBufferedMemory);
498 otherJRobinsValues.put("usedNonHeapMemory", (double) usedNonHeapMemory);
499 otherJRobinsValues.put("usedPhysicalMemorySize", (double) usedPhysicalMemorySize);
500 otherJRobinsValues.put("usedSwapSpaceSize", (double) usedSwapSpaceSize);
501 otherJRobinsValues.put("systemLoad", systemLoadAverage);
502 otherJRobinsValues.put("systemCpuLoad", systemCpuLoad / javaInformationsList.size());
503 otherJRobinsValues.put("fileDescriptors", (double) unixOpenFileDescriptorCount);
504 for (final Map.Entry<String, Double> entry : otherJRobinsValues.entrySet()) {
505 if (entry.getValue() >= 0) {
506 addJRobinValue(getOtherJRobin(entry.getKey()), entry.getValue());
507 }
508 }
509
510 collectSessionsMeanAge(sessionAgeSum, sessionCount);
511
512 if (!noDatabase) {
513
514 if (this.transactionCount != NOT_A_NUMBER) {
515 final double periodMinutes = periodMillis / 60000d;
516 addJRobinValue(getOtherJRobin("transactionsRate"),
517 (databaseTransactionCount - this.transactionCount) / periodMinutes);
518 } else {
519 addJRobinValue(getOtherJRobin("transactionsRate"), 0d);
520 }
521 this.transactionCount = databaseTransactionCount;
522 }
523
524 if (freeDiskSpaceInTemp != Long.MAX_VALUE) {
525 addJRobinValue(getOtherJRobin("Free_disk_space"), freeDiskSpaceInTemp);
526 }
527 if (usableDiskSpaceInTemp != Long.MAX_VALUE) {
528 addJRobinValue(getOtherJRobin("Usable_disk_space"), usableDiskSpaceInTemp);
529 }
530
531
532
533
534 }
535
536 private void collectTomcatInformations(List<JavaInformations> javaInformationsList)
537 throws IOException {
538 int tomcatBusyThreads = 0;
539 long bytesReceived = 0;
540 long bytesSent = 0;
541 boolean tomcatUsed = false;
542
543 for (final JavaInformations javaInformations : javaInformationsList) {
544 for (final TomcatInformations tomcatInformations : javaInformations
545 .getTomcatInformationsList()) {
546 tomcatBusyThreads = add(tomcatInformations.getCurrentThreadsBusy(),
547 tomcatBusyThreads);
548 bytesReceived = add(tomcatInformations.getBytesReceived(), bytesReceived);
549 bytesSent = add(tomcatInformations.getBytesSent(), bytesSent);
550 tomcatUsed = true;
551 }
552 }
553
554 if (tomcatUsed) {
555
556 collectTomcatValues(tomcatBusyThreads, bytesReceived, bytesSent);
557 }
558 }
559
560 private void collectJRobinValues(long usedMemory, long processesCpuTimeMillis,
561 int availableProcessors, int sessionCount, int activeThreadCount,
562 int activeConnectionCount, int usedConnectionCount) throws IOException {
563
564 addJRobinValue(getCounterJRobin("usedMemory"), usedMemory);
565
566
567 if (processesCpuTimeMillis >= 0) {
568
569
570
571
572
573
574
575
576 if (this.cpuTimeMillis != NOT_A_NUMBER) {
577
578 final int cpuPercentage = Math
579 .min((int) ((processesCpuTimeMillis - this.cpuTimeMillis) * 100
580 / periodMillis / availableProcessors), 100);
581 addJRobinValue(getCounterJRobin("cpu"), cpuPercentage);
582 } else {
583 addJRobinValue(getCounterJRobin("cpu"), 0d);
584 }
585 this.cpuTimeMillis = processesCpuTimeMillis;
586 }
587
588
589
590 if (getCounterByName(Counter.HTTP_COUNTER_NAME) != null) {
591
592 if (sessionCount >= 0) {
593 addJRobinValue(getCounterJRobin("httpSessions"), sessionCount);
594 }
595
596 addJRobinValue(getCounterJRobin("activeThreads"), activeThreadCount);
597 }
598 if (!noDatabase) {
599
600 addJRobinValue(getCounterJRobin("activeConnections"), activeConnectionCount);
601 addJRobinValue(getCounterJRobin("usedConnections"), usedConnectionCount);
602 }
603
604
605
606 if (getCounterByName(Counter.BUILDS_COUNTER_NAME) != null) {
607 addJRobinValue(getCounterJRobin("runningBuilds"), JdbcWrapper.getRunningBuildCount());
608 addJRobinValue(getCounterJRobin("buildQueueLength"), JdbcWrapper.getBuildQueueLength());
609 addJRobinValue(getCounterJRobin("buildQueueWaiting"),
610 JdbcWrapper.getBuildQueueWaitingDurationsSum() / 1000d);
611 }
612 }
613
614 private void collectTomcatValues(int tomcatBusyThreads, long bytesReceived, long bytesSent)
615 throws IOException {
616 addJRobinValue(getOtherJRobin("tomcatBusyThreads"), tomcatBusyThreads);
617 if (this.tomcatBytesSent != NOT_A_NUMBER) {
618 final double periodMinutes = periodMillis / 60000d;
619 addJRobinValue(getOtherJRobin("tomcatBytesReceived"),
620 (bytesReceived - this.tomcatBytesReceived) / periodMinutes);
621 addJRobinValue(getOtherJRobin("tomcatBytesSent"),
622 (bytesSent - this.tomcatBytesSent) / periodMinutes);
623 } else {
624 addJRobinValue(getOtherJRobin("tomcatBytesReceived"), 0d);
625 addJRobinValue(getOtherJRobin("tomcatBytesSent"), 0d);
626 }
627 this.tomcatBytesReceived = bytesReceived;
628 this.tomcatBytesSent = bytesSent;
629 }
630
631 private void collectSessionsMeanAge(long sessionAgeSum, int sessionCount) throws IOException {
632 if (sessionCount >= 0 && getCounterByName(Counter.HTTP_COUNTER_NAME) != null) {
633 final long sessionAgeMeanInMinutes;
634 if (sessionCount > 0) {
635 sessionAgeMeanInMinutes = sessionAgeSum / sessionCount / 60000;
636 } else {
637 sessionAgeMeanInMinutes = -1;
638 }
639 addJRobinValue(getOtherJRobin("httpSessionsMeanAge"), sessionAgeMeanInMinutes);
640 }
641 }
642
643 private void addJRobinValue(JRobin jRobin, double value) throws IOException {
644 jRobin.addValue(value);
645
646
647 if (value >= 0 && metricsPublishers != null) {
648 for (final MetricsPublisher metricsPublisher : metricsPublishers) {
649 metricsPublisher.addValue(jRobin.getName(), value);
650 }
651 }
652 }
653
654 private static double add(double t1, double t2) {
655
656
657
658
659
660
661
662
663 if (t1 < 0d && t2 > 0d) {
664 return t2;
665 } else if (t1 > 0d && t2 < 0d) {
666 return t1;
667 }
668 return t1 + t2;
669 }
670
671 private static long add(long t1, long t2) {
672 if (t1 < 0L && t2 > 0L) {
673 return t2;
674 } else if (t1 > 0L && t2 < 0L) {
675 return t1;
676 }
677 return t1 + t2;
678 }
679
680 private static int add(int t1, int t2) {
681 if (t1 < 0 && t2 > 0) {
682 return t2;
683 } else if (t1 > 0 && t2 < 0) {
684 return t1;
685 }
686 return t1 + t2;
687 }
688
689 private long collectCounterData(Counter counter) throws IOException {
690
691 final String counterName = counter.getName();
692 final List<CounterRequest> requests = counter.getRequests();
693 if (!counter.isErrorCounter()) {
694
695 final CounterRequest newGlobalRequest = new CounterRequest(counterName + " global",
696 counterName);
697 for (final CounterRequest request : requests) {
698
699 newGlobalRequest.addHits(request);
700 }
701
702
703
704 final JRobin hitsJRobin;
705 final JRobin meanTimesJRobin;
706 final JRobin systemErrorsJRobin;
707 if (!counter.isJspOrStrutsCounter()) {
708 hitsJRobin = getCounterJRobin(counterName + "HitsRate");
709 meanTimesJRobin = getCounterJRobin(counterName + "MeanTimes");
710 systemErrorsJRobin = getCounterJRobin(counterName + "SystemErrors");
711 } else {
712 hitsJRobin = getOtherJRobin(counterName + "HitsRate");
713 meanTimesJRobin = getOtherJRobin(counterName + "MeanTimes");
714 systemErrorsJRobin = getOtherJRobin(counterName + "SystemErrors");
715 }
716
717 final CounterRequest globalRequest = globalRequestsByCounter.get(counter);
718 if (globalRequest != null) {
719
720
721
722
723
724
725 final CounterRequest lastPeriodGlobalRequest = newGlobalRequest.clone();
726 lastPeriodGlobalRequest.removeHits(globalRequest);
727
728 final long hits = lastPeriodGlobalRequest.getHits();
729 final long hitsParMinute = hits * 60 * 1000 / periodMillis;
730
731
732 addJRobinValue(hitsJRobin, hitsParMinute);
733
734 if (hits > 0) {
735 addJRobinValue(meanTimesJRobin, lastPeriodGlobalRequest.getMean());
736 addJRobinValue(systemErrorsJRobin,
737 lastPeriodGlobalRequest.getSystemErrorPercentage());
738
739
740
741
742 counter.writeToFile();
743 }
744 }
745
746
747 globalRequestsByCounter.put(counter, newGlobalRequest);
748 }
749
750
751 final long dayCounterEstimatedMemorySize = collectCounterRequestsAndErrorsData(counter,
752 requests);
753 return counter.getEstimatedMemorySize() + dayCounterEstimatedMemorySize;
754 }
755
756 private long collectCounterRequestsAndErrorsData(Counter counter, List<CounterRequest> requests)
757 throws IOException {
758 final Counter dayCounter = getCurrentDayCounter(counter);
759 final boolean firstCollectDoneForCounter = Boolean.TRUE
760 .equals(firstCollectDoneByCounter.get(counter));
761 final List<CounterRequest> filteredRequests = filterRequestsIfOverflow(counter, requests);
762 for (final CounterRequest newRequest : filteredRequests) {
763 collectCounterRequestData(dayCounter, newRequest, firstCollectDoneForCounter);
764 }
765 if (dayCounter.getRequestsCount() > dayCounter.getMaxRequestsCount()) {
766
767 filterRequestsIfOverflow(dayCounter, dayCounter.getRequests());
768 }
769 if (dayCounter.isErrorCounter()) {
770 dayCounter.addErrors(getDeltaOfErrors(counter, dayCounter));
771 }
772 dayCounter.writeToFile();
773 if (!firstCollectDoneForCounter) {
774 firstCollectDoneByCounter.put(counter, Boolean.TRUE);
775 }
776 return dayCounter.getEstimatedMemorySize();
777 }
778
779 private List<CounterRequest> filterRequestsIfOverflow(Counter counter,
780 List<CounterRequest> requests) {
781 final int maxRequestsCount = counter.getMaxRequestsCount();
782 if (requests.size() <= maxRequestsCount) {
783 return requests;
784 }
785 final List<CounterRequest> result = new ArrayList<>(requests);
786 for (final CounterRequest request : requests) {
787 if (result.size() > maxRequestsCount && request.getHits() < 10) {
788
789
790
791
792
793
794 removeRequest(counter, request);
795 result.remove(request);
796 }
797 }
798 while (result.size() > maxRequestsCount && !result.isEmpty()) {
799
800
801 final CounterRequest request = result.get(0);
802 removeRequest(counter, request);
803 result.remove(request);
804 }
805 return result;
806 }
807
808 private void collectCounterRequestData(Counter dayCounter, CounterRequest newRequest,
809 boolean firstCollectDoneForCounter) throws IOException {
810 final String requestStorageId = newRequest.getId();
811
812 final CounterRequest request = requestsById.get(requestStorageId);
813 if (request != null) {
814
815
816 final CounterRequest lastPeriodRequest = newRequest.clone();
817 lastPeriodRequest.removeHits(request);
818
819
820
821
822
823 if (lastPeriodRequest.getHits() > 1 && !dayCounter.isJspOrStrutsCounter()
824 && (!dayCounter.isErrorCounter() || dayCounter.isJobCounter())) {
825
826
827 final JRobin requestJRobin = getRequestJRobin(requestStorageId,
828 newRequest.getName());
829
830
831
832 requestJRobin.addValue(lastPeriodRequest.getMean());
833 }
834
835
836 dayCounter.addHits(lastPeriodRequest);
837 } else if (firstCollectDoneForCounter) {
838
839
840
841
842
843
844
845 dayCounter.addHits(newRequest);
846 }
847 requestsById.put(requestStorageId, newRequest);
848 }
849
850 private List<CounterError> getDeltaOfErrors(Counter counter, Counter dayCounter) {
851 final List<CounterError> errors = counter.getErrors();
852 if (errors.isEmpty()) {
853 return Collections.emptyList();
854 }
855 final long lastErrorTime;
856 final List<CounterError> dayErrors = dayCounter.getErrors();
857 if (dayErrors.isEmpty()) {
858 lastErrorTime = dayCounter.getStartDate().getTime();
859 } else {
860 lastErrorTime = dayErrors.get(dayErrors.size() - 1).getTime();
861 }
862 final List<CounterError> errorsOfDay = new ArrayList<>();
863 for (final CounterError error : errors) {
864
865
866
867 if (error.getTime() > lastErrorTime) {
868 errorsOfDay.add(error);
869 }
870 }
871 return errorsOfDay;
872 }
873
874 private Counter getCurrentDayCounter(Counter counter) throws IOException {
875 Counter dayCounter = dayCountersByCounter.get(counter);
876 final Calendar calendar = Calendar.getInstance();
877 final int currentDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
878 calendar.setTime(dayCounter.getStartDate());
879 if (calendar.get(Calendar.DAY_OF_YEAR) != currentDayOfYear) {
880
881 dayCounter = new PeriodCounterFactory(dayCounter).buildNewDayCounter();
882 dayCountersByCounter.put(counter, dayCounter);
883 }
884 return dayCounter;
885 }
886
887 void deleteObsoleteFiles() {
888 final long rrdDiskUsage = CounterStorage.deleteObsoleteCounterFiles(getApplication());
889 final long serGzDiskUsage = JRobin.deleteObsoleteJRobinFiles(getApplication());
890 diskUsage = rrdDiskUsage + serGzDiskUsage;
891
892 LOG.debug("Obsolete files deleted. JavaMelody disk usage: " + diskUsage / 1024 + " KB");
893 }
894
895 private void removeRequest(Counter counter, CounterRequest newRequest) {
896 counter.removeRequest(newRequest.getName());
897 requestsById.remove(newRequest.getId());
898 final JRobin requestJRobin = requestJRobinsById.remove(newRequest.getId());
899 if (requestJRobin != null) {
900 requestJRobin.deleteFile();
901 }
902 }
903
904 private JRobin getRequestJRobin(String requestId, String requestName) throws IOException {
905 JRobin jrobin = requestJRobinsById.get(requestId);
906 if (jrobin == null) {
907 jrobin = JRobin.createInstance(getApplication(), requestId, requestName);
908 requestJRobinsById.put(requestId, jrobin);
909 }
910 return jrobin;
911 }
912
913 private JRobin getCounterJRobin(String name) throws IOException {
914 JRobin jrobin = counterJRobins.get(name);
915 if (jrobin == null) {
916 jrobin = JRobin.createInstance(getApplication(), name, null);
917 counterJRobins.put(name, jrobin);
918 }
919 return jrobin;
920 }
921
922 private JRobin getOtherJRobin(String name) throws IOException {
923 JRobin jrobin = otherJRobins.get(name);
924 if (jrobin == null) {
925 jrobin = JRobin.createInstance(getApplication(), name, null);
926 otherJRobins.put(name, jrobin);
927 }
928 return jrobin;
929 }
930
931 public JRobin getJRobin(String graphName) throws IOException {
932 JRobin jrobin = counterJRobins.get(graphName);
933 if (jrobin == null) {
934 jrobin = otherJRobins.get(graphName);
935 if (jrobin == null) {
936 jrobin = requestJRobinsById.get(graphName);
937 if (jrobin == null) {
938
939
940
941 jrobin = JRobin.createInstanceIfFileExists(getApplication(), graphName,
942 graphName);
943 }
944 }
945 }
946 return jrobin;
947 }
948
949 public Collection<JRobin> getCounterJRobins() {
950 return Collections.unmodifiableCollection(counterJRobins.values());
951 }
952
953 Collection<JRobin> getOtherJRobins() {
954 return Collections.unmodifiableCollection(otherJRobins.values());
955 }
956
957 public Collection<JRobin> getDisplayedCounterJRobins() {
958 return getDisplayedJRobins(counterJRobins.values());
959 }
960
961 public Collection<JRobin> getDisplayedOtherJRobins() {
962 return getDisplayedJRobins(otherJRobins.values());
963 }
964
965 private Collection<JRobin> getDisplayedJRobins(Collection<JRobin> jrobins) {
966 final List<JRobin> displayedJRobins = new ArrayList<>(jrobins.size());
967 for (final JRobin jrobin : jrobins) {
968 final String jrobinName = jrobin.getName();
969 boolean displayed = true;
970
971
972
973
974 for (final Counter counter : getCounters()) {
975 if (jrobinName.startsWith(counter.getName())) {
976 displayed = counter.isDisplayed();
977 break;
978 }
979 }
980 if (displayed) {
981 displayedJRobins.add(jrobin);
982 }
983 }
984 return Collections.unmodifiableCollection(displayedJRobins);
985 }
986
987
991 void clearCounter(String counterName) {
992 final Counter counter = getCounterByName(counterName);
993 if (counter != null) {
994 final List<CounterRequest> requests = counter.getRequests();
995
996 counter.clear();
997
998 globalRequestsByCounter.remove(counter);
999 for (final CounterRequest request : requests) {
1000 requestsById.remove(request.getId());
1001 requestJRobinsById.remove(request.getId());
1002 }
1003 }
1004 }
1005
1006 public boolean isStorageUsedByMultipleInstances() {
1007 return !storageLock.isAcquired();
1008 }
1009
1010 public void stop() {
1011 try {
1012 try {
1013
1014 for (final Counter counter : counters) {
1015 counter.writeToFile();
1016 }
1017 } finally {
1018 storageLock.release();
1019 }
1020 } catch (final IOException e) {
1021
1022 LOG.warn("exception while writing counters data to files", e);
1023 } finally {
1024 try {
1025 for (final Counter counter : counters) {
1026 counter.clear();
1027 }
1028 } finally {
1029 if (metricsPublishers != null) {
1030 for (final MetricsPublisher metricsPublisher : metricsPublishers) {
1031 metricsPublisher.stop();
1032 }
1033 }
1034 }
1035 stopped = true;
1036
1037
1038
1039
1040
1041
1042
1043 }
1044 }
1045
1046 public boolean isStopped() {
1047 return stopped;
1048 }
1049
1050 public static void stopJRobin() {
1051 try {
1052 JRobin.stop();
1053 } catch (final Throwable t) {
1054 LOG.warn("stopping jrobin failed", t);
1055 }
1056 }
1057
1058 public static void detachVirtualMachine() {
1059 try {
1060 VirtualMachine.detach();
1061 } catch (final Throwable t) {
1062 LOG.warn("exception while detaching virtual machine", t);
1063 }
1064 }
1065
1066
1067 @Override
1068 public String toString() {
1069 return getClass().getSimpleName() + "[application=" + getApplication() + ", periodMillis="
1070 + periodMillis + ", counters=" + getCounters() + ']';
1071 }
1072 }
1073