add modernizer-maven-plugin to infrautils' parent
[serviceutils.git] / metrics / impl / src / main / java / org / opendaylight / infrautils / metrics / internal / MetricsFileReporter.java
1 /*
2  * Copyright (c) 2018 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.infrautils.metrics.internal;
9
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;
21 import java.io.File;
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;
32 import java.util.Map;
33 import java.util.SortedMap;
34 import java.util.concurrent.TimeUnit;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 public class MetricsFileReporter extends ScheduledReporter {
39
40     private static final Logger LOG = LoggerFactory.getLogger(MetricsFileReporter.class);
41
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 = ",";
47
48     private final File parentDirectory;
49     private final Map<String, Long> oldCounters = new HashMap<>();
50     private final MetricRegistry registry;
51     private final Duration interval;
52
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;
58     }
59
60     public void startReporter() {
61         start(interval.getSeconds(), TimeUnit.SECONDS);
62     }
63
64     Duration getInterval() {
65         return interval;
66     }
67
68     public void report(PrintWriter pw) {
69         report(pw, registry.getGauges(), registry.getCounters(),
70                 registry.getHistograms(), registry.getMeters(), registry.getTimers());
71     }
72
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) {
76         pw.print("date,");
77         pw.print(new Date());
78         pw.println();
79
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);
92                 pw.println();
93             }
94         }
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());
99         }
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());
105             pw.println();
106         }
107         pw.println("Meters:");
108         for (Map.Entry<String, Meter> entry : meters.entrySet()) {
109             pw.print(entry.getKey());
110             printMeter(pw, entry.getValue());
111         }
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());
117         }
118         counters.forEach((key, value) -> oldCounters.put(key, value.getCount()));
119     }
120
121     @Override
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) {
128         try {
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),
137                     DEFAULT_ENCODING));
138             report(pw, gauges, counters, histograms, meters, timers);
139             pw.close();
140         } catch (IOException e) {
141             LOG.error("Failed to report counters to files", e);
142         }
143     }
144
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());
150     }
151
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());
157         pw.println();
158     }
159
160     private static void printWithSeparator(PrintWriter pw, String name, Object val) {
161         printSeparator(pw);
162         pw.print(name);
163         printSeparator(pw);
164         pw.print(val);
165     }
166
167     private static void printSeparator(PrintWriter pw) {
168         pw.print(SEPARATOR);
169     }
170
171     private static String getFileName(int dayOfTheWeek, int hourOfTheDay) {
172         return COUNTER_FILE_PREFIX + dayOfTheWeek + "." + hourOfTheDay;
173     }
174
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());
180             }
181         }
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());
187             }
188         }
189         return file;
190     }
191
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),
198                     DEFAULT_ENCODING));
199             pw.write(new Date().toString());
200             pw.close();
201         }
202     }
203 }