2 * Copyright (c) 2018 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.infrautils.metrics.internal;
10 import com.codahale.metrics.Counter;
11 import com.codahale.metrics.Gauge;
12 import com.codahale.metrics.Histogram;
13 import com.codahale.metrics.Meter;
14 import com.codahale.metrics.Metered;
15 import com.codahale.metrics.MetricFilter;
16 import com.codahale.metrics.MetricRegistry;
17 import com.codahale.metrics.Sampling;
18 import com.codahale.metrics.ScheduledReporter;
19 import com.codahale.metrics.Snapshot;
20 import com.codahale.metrics.Timer;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStreamWriter;
25 import java.io.PrintWriter;
26 import java.nio.charset.Charset;
27 import java.nio.charset.StandardCharsets;
28 import java.time.Duration;
29 import java.util.Calendar;
30 import java.util.Date;
31 import java.util.HashMap;
33 import java.util.SortedMap;
34 import java.util.concurrent.TimeUnit;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 public class MetricsFileReporter extends ScheduledReporter {
40 private static final Logger LOG = LoggerFactory.getLogger(MetricsFileReporter.class);
42 private static final String DATA_DIRECTORY = "data";
43 private static final String COUNTERS_DIRECTORY = "metrics";
44 private static final String COUNTER_FILE_PREFIX = "metrics.";
45 private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
46 private static final String SEPARATOR = ",";
48 private final File parentDirectory;
49 private final Map<String, Long> oldCounters = new HashMap<>();
50 private final MetricRegistry registry;
51 private final Duration interval;
53 public MetricsFileReporter(MetricRegistry registry, Duration interval) {
54 super(registry, "file-reporter", MetricFilter.ALL, TimeUnit.SECONDS, TimeUnit.SECONDS);
55 this.parentDirectory = new File(DATA_DIRECTORY, COUNTERS_DIRECTORY);
56 this.registry = registry;
57 this.interval = interval;
60 public void startReporter() {
61 start(interval.getSeconds(), TimeUnit.SECONDS);
64 Duration getInterval() {
68 public void report(PrintWriter pw) {
69 report(pw, registry.getGauges(), registry.getCounters(),
70 registry.getHistograms(), registry.getMeters(), registry.getTimers());
73 private void report(PrintWriter pw, @SuppressWarnings("rawtypes") SortedMap<String, Gauge> gauges,
74 SortedMap<String, Counter> counters, SortedMap<String, Histogram> histograms,
75 SortedMap<String, Meter> meters, SortedMap<String, Timer> timers) {
80 pw.println("Counters:");
81 for (Map.Entry<String, Counter> entry : counters.entrySet()) {
82 Counter newCounter = entry.getValue();
83 // avoid unnecessary write to report file
84 // report the counter only if there is a change
85 Long oldCounterObj = oldCounters.get(entry.getKey());
86 long oldCounter = oldCounterObj != null ? oldCounterObj.longValue() : 0;
87 if (newCounter.getCount() != oldCounter) {
88 pw.print(entry.getKey());
89 printWithSeparator(pw, "count", entry.getValue().getCount());
90 printWithSeparator(pw, "diff",
91 entry.getValue().getCount() - oldCounter);
95 pw.println("Gauges:");
96 for (@SuppressWarnings("rawtypes") Map.Entry<String, Gauge> entry : gauges.entrySet()) {
97 pw.print(entry.getKey());
98 pw.println(entry.getValue().getValue());
100 pw.println("Histograms:");
101 for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {
102 pw.print(entry.getKey());
103 printWithSeparator(pw, "count", entry.getValue().getCount());
104 printSampling(pw, entry.getValue());
107 pw.println("Meters:");
108 for (Map.Entry<String, Meter> entry : meters.entrySet()) {
109 pw.print(entry.getKey());
110 printMeter(pw, entry.getValue());
112 pw.println("Timers:");
113 for (Map.Entry<String, Timer> entry : timers.entrySet()) {
114 pw.print(entry.getKey());
115 printSampling(pw, entry.getValue());
116 printMeter(pw, entry.getValue());
118 counters.forEach((key, value) -> oldCounters.put(key, value.getCount()));
122 public void report(@SuppressWarnings("rawtypes")
123 SortedMap<String, Gauge> gauges,
124 SortedMap<String, Counter> counters,
125 SortedMap<String, Histogram> histograms,
126 SortedMap<String, Meter> meters,
127 SortedMap<String, Timer> timers) {
129 Calendar calendar = Calendar.getInstance();
130 int hourOfTheDay = calendar.get(Calendar.HOUR_OF_DAY);
131 int dayOfTheWeek = calendar.get(Calendar.DAY_OF_WEEK);
132 // retains one week worth of counters
133 rotateLastWeekFile(dayOfTheWeek, hourOfTheDay);
134 boolean append = true;
135 File file = createFile(dayOfTheWeek, hourOfTheDay);
136 PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file, append),
138 report(pw, gauges, counters, histograms, meters, timers);
140 } catch (IOException e) {
141 LOG.error("Failed to report counters to files", e);
145 private static void printSampling(PrintWriter pw, Sampling sampling) {
146 Snapshot snapshot = sampling.getSnapshot();
147 printWithSeparator(pw, "min", snapshot.getMin());
148 printWithSeparator(pw, "max", snapshot.getMax());
149 printWithSeparator(pw, "mean", snapshot.getMean());
152 private static void printMeter(PrintWriter pw, Metered meter) {
153 printWithSeparator(pw, "count", meter.getCount());
154 printWithSeparator(pw, "oneMinuteRate", meter.getOneMinuteRate());
155 printWithSeparator(pw, "fiveMinuteRate", meter.getFiveMinuteRate());
156 printWithSeparator(pw, "fifteenMinuteRate", meter.getFifteenMinuteRate());
160 private static void printWithSeparator(PrintWriter pw, String name, Object val) {
167 private static void printSeparator(PrintWriter pw) {
171 private static String getFileName(int dayOfTheWeek, int hourOfTheDay) {
172 return COUNTER_FILE_PREFIX + dayOfTheWeek + "." + hourOfTheDay;
175 public File createFile(int dayOfTheWeek, int hourOfTheDay) throws IOException {
176 if (!parentDirectory.exists()) {
177 LOG.info("Directory does not exist, creating it: {}", parentDirectory.getName());
178 if (!parentDirectory.mkdirs()) {
179 throw new IOException("Failed to make directories: " + parentDirectory.toString());
182 File file = new File(parentDirectory, getFileName(dayOfTheWeek, hourOfTheDay));
183 if (!file.exists()) {
184 LOG.info("File does not exist, creating it: {}", file.getPath());
185 if (!file.createNewFile()) {
186 throw new IOException("Failed to create file: " + file.toString());
192 private void rotateLastWeekFile(int dayOfTheWeek, int hourOfTheDay) throws IOException {
193 int nextHour = hourOfTheDay < 23 ? hourOfTheDay + 1 : 0;
194 File nextHourFile = new File(parentDirectory , getFileName(dayOfTheWeek, nextHour));
195 if (nextHourFile.exists()) {
196 boolean append = false;
197 PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(nextHourFile, append),
199 pw.write(new Date().toString());