5b44da8c3fd022544c0ee49c4ad4fcef1d3e9b3d
[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
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStreamWriter;
26 import java.io.PrintWriter;
27 import java.time.Duration;
28 import java.util.Calendar;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.SortedMap;
33 import java.util.concurrent.TimeUnit;
34
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 String DEFAULT_ENCODING = "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 Duration interval;
51
52     public MetricsFileReporter(MetricRegistry registry, Duration interval) {
53         super(registry, "file-reporter", MetricFilter.ALL, TimeUnit.SECONDS, TimeUnit.SECONDS);
54         this.parentDirectory = new File(DATA_DIRECTORY, COUNTERS_DIRECTORY);
55         this.interval = interval;
56     }
57
58     public void startReporter() {
59         start(interval.getSeconds(), TimeUnit.SECONDS);
60     }
61
62     Duration getInterval() {
63         return interval;
64     }
65
66     @Override
67     public void report(@SuppressWarnings("rawtypes")
68                        SortedMap<String, Gauge> gauges,
69                        SortedMap<String, Counter> counters,
70                        SortedMap<String, Histogram> histograms,
71                        SortedMap<String, Meter> meters,
72                        SortedMap<String, Timer> timers) {
73         try {
74             Calendar calendar = Calendar.getInstance();
75             int hourOfTheDay = calendar.get(Calendar.HOUR_OF_DAY);
76             int dayOfTheWeek = calendar.get(Calendar.DAY_OF_WEEK);
77             // retains one week worth of counters
78             rotateLastWeekFile(dayOfTheWeek, hourOfTheDay);
79             boolean append = true;
80             File file = createFile(dayOfTheWeek, hourOfTheDay);
81             PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file, append),
82                     DEFAULT_ENCODING));
83             pw.print("date,");
84             pw.print(new Date());
85             pw.println();
86
87             pw.println("Counters:");
88             for (Map.Entry<String, Counter> entry : counters.entrySet()) {
89                 Counter newCounter = entry.getValue();
90                 // avoid unnecessary write to report file
91                 // report the counter only if there is a change
92                 Long oldCounterObj = oldCounters.get(entry.getKey());
93                 long oldCounter = oldCounterObj != null ? oldCounterObj.longValue() : 0;
94                 if (newCounter.getCount() != oldCounter) {
95                     pw.print(entry.getKey());
96                     printWithSeparator(pw, "count", entry.getValue().getCount());
97                     printWithSeparator(pw, "diff",
98                             entry.getValue().getCount() - oldCounter);
99                     pw.println();
100                 }
101             }
102             pw.println("Gauges:");
103             for (@SuppressWarnings("rawtypes") Map.Entry<String, Gauge> entry : gauges.entrySet()) {
104                 pw.print(entry.getKey());
105                 pw.println(entry.getValue().getValue());
106             }
107             pw.println("Histograms:");
108             for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {
109                 pw.print(entry.getKey());
110                 printWithSeparator(pw, "count", entry.getValue().getCount());
111                 printSampling(pw, entry.getValue());
112                 pw.println();
113             }
114             pw.println("Meters:");
115             for (Map.Entry<String, Meter> entry : meters.entrySet()) {
116                 pw.print(entry.getKey());
117                 printMeter(pw, entry.getValue());
118             }
119             pw.println("Timers:");
120             for (Map.Entry<String, Timer> entry : timers.entrySet()) {
121                 pw.print(entry.getKey());
122                 printSampling(pw, entry.getValue());
123                 printMeter(pw, entry.getValue());
124             }
125             counters.forEach((key, value) -> oldCounters.put(key, value.getCount()));
126             pw.close();
127         } catch (IOException e) {
128             LOG.error("Failed to report counters to files", e);
129         }
130     }
131
132     private static void printSampling(PrintWriter pw, Sampling sampling) {
133         Snapshot snapshot = sampling.getSnapshot();
134         printWithSeparator(pw, "min", snapshot.getMin());
135         printWithSeparator(pw, "max", snapshot.getMax());
136         printWithSeparator(pw, "mean", snapshot.getMean());
137     }
138
139     private static void printMeter(PrintWriter pw, Metered meter) {
140         printWithSeparator(pw, "count", meter.getCount());
141         printWithSeparator(pw, "oneMinuteRate", meter.getOneMinuteRate());
142         printWithSeparator(pw, "fiveMinuteRate", meter.getFiveMinuteRate());
143         printWithSeparator(pw, "fifteenMinuteRate", meter.getFifteenMinuteRate());
144         pw.println();
145     }
146
147     private static void printWithSeparator(PrintWriter pw, String name, Object val) {
148         printSeparator(pw);
149         pw.print(name);
150         printSeparator(pw);
151         pw.print(val);
152     }
153
154     private static void printSeparator(PrintWriter pw) {
155         pw.print(SEPARATOR);
156     }
157
158     private static String getFileName(int dayOfTheWeek, int hourOfTheDay) {
159         return COUNTER_FILE_PREFIX + dayOfTheWeek + "." + hourOfTheDay;
160     }
161
162     public File createFile(int dayOfTheWeek, int hourOfTheDay) throws IOException {
163         if (!parentDirectory.exists()) {
164             LOG.info("Directory does not exist, creating it: {}", parentDirectory.getName());
165             if (!parentDirectory.mkdirs()) {
166                 throw new IOException("Failed to make directories: " + parentDirectory.toString());
167             }
168         }
169         File file = new File(parentDirectory, getFileName(dayOfTheWeek, hourOfTheDay));
170         if (!file.exists()) {
171             LOG.info("File does not exist, creating it: {}", file.getPath());
172             if (!file.createNewFile()) {
173                 throw new IOException("Failed to create file: " + file.toString());
174             }
175         }
176         return file;
177     }
178
179     private void rotateLastWeekFile(int dayOfTheWeek, int hourOfTheDay) throws IOException {
180         int nextHour = hourOfTheDay < 23 ? hourOfTheDay + 1 : 0;
181         File nextHourFile = new File(parentDirectory , getFileName(dayOfTheWeek, nextHour));
182         if (nextHourFile.exists()) {
183             boolean append = false;
184             PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(nextHourFile, append),
185                     DEFAULT_ENCODING));
186             pw.write(new Date().toString());
187             pw.close();
188         }
189     }
190 }