001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.util; 020 021import org.HdrHistogram.Histogram; 022 023import java.io.*; 024import java.util.Map; 025import java.util.concurrent.ConcurrentHashMap; 026import java.util.stream.Collectors; 027 028public class Metrics implements Loggeable { 029 private Histogram template; 030 private Map<String,Histogram> metrics = new ConcurrentHashMap<>(); 031 private double conversion = 1; 032 033 public Metrics(Histogram template) { 034 super(); 035 this.template = template; 036 if (template != null && template.getStartTimeStamp() == Long.MAX_VALUE) { 037 template.setStartTimeStamp(System.currentTimeMillis()); 038 } 039 } 040 041 public Map<String, Histogram> metrics() { 042 return metrics.entrySet() 043 .stream() 044 .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().copy())); 045 } 046 047 public Map<String, Histogram> metrics(String prefix) { 048 return metrics.entrySet() 049 .stream() 050 .filter(e -> e.getKey().startsWith(prefix)) 051 .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().copy())); 052 } 053 054 public void record(String name, long elapsed) { 055 Histogram h = getHistogram(name); 056 long l = Math.min(elapsed, h.getHighestTrackableValue()); 057 if (l > 0) 058 h.recordValue(l); 059 } 060 061 private Histogram getHistogram(String p) { 062 Histogram h = metrics.get(p); 063 if (h == null) { 064 Histogram hist = new Histogram(template); 065 hist.setTag(p); 066 metrics.putIfAbsent(p, hist); 067 h = metrics.get(p); 068 } 069 return h; 070 } 071 072 public void dump(PrintStream ps, String indent) { 073 metrics.entrySet() 074 .stream() 075 .sorted(Map.Entry.comparingByKey()) 076 .forEach(e -> dumpPercentiles(ps, indent, e.getKey(), e.getValue().copy())); 077 } 078 079 private void dumpPercentiles (PrintStream ps, String indent, String key, Histogram h) { 080 ps.printf("%s%s min=%.7f, max=%.7f, mean=%.7f stddev=%.7f P50=%.7f, P90=%.7f, P99=%.7f, P99.9=%.7f, P99.99=%.7f tot=%d size=%d%n", 081 indent, 082 key, 083 h.getMinValue()/conversion, 084 h.getMaxValue()/conversion, 085 h.getMean()/conversion, 086 h.getStdDeviation()/conversion, 087 h.getValueAtPercentile(50.0)/conversion, 088 h.getValueAtPercentile(90.0)/conversion, 089 h.getValueAtPercentile(99.0)/conversion, 090 h.getValueAtPercentile(99.9)/conversion, 091 h.getValueAtPercentile(99.99)/conversion, 092 h.getTotalCount(), 093 h.getEstimatedFootprintInBytes() 094 ); 095 } 096 097 private void dumpHistogram(File dir, String key, Histogram h) { 098 try (FileOutputStream fos = new FileOutputStream(new File(dir, key + ".hgrm"))) { 099 h.outputPercentileDistribution(new PrintStream(fos), 1.0); 100 } catch (IOException e) { 101 e.printStackTrace(); 102 } 103 } 104 105 public void dumpHistograms(File dir, String prefix) { 106 metrics.entrySet() 107 .stream() 108 .sorted(Map.Entry.comparingByKey()) 109 .forEach(e -> dumpHistogram(dir, prefix + e.getKey(), e.getValue().copy())); 110 } 111 112 /** 113 * @param conversion 114 * This is used to divide the percentile values while dumping. 115 * If you are using nano seconds to record and want to display the numbers in millis then conversion can be set to 1000000. 116 * By default conversion is set to 1. 117 */ 118 public void setConversion(double conversion) { 119 this.conversion = conversion; 120 } 121}