blob: bf39f2b71ef54c4b6d43ac86580eaf73fb79ec0e [file] [log] [blame]
Flavio Castro41b1f3a2015-07-31 13:51:32 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Flavio Castro41b1f3a2015-07-31 13:51:32 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Aaron Kruglikov1110b2c2016-02-02 16:24:37 -080016package org.onosproject.utils;
Flavio Castro41b1f3a2015-07-31 13:51:32 -070017
Flavio Castro6e044612015-08-13 14:13:58 -070018import com.codahale.metrics.Counter;
Flavio Castro41b1f3a2015-07-31 13:51:32 -070019import com.codahale.metrics.Timer;
20import com.google.common.collect.Maps;
21import org.onlab.metrics.MetricsComponent;
22import org.onlab.metrics.MetricsFeature;
23import org.onlab.metrics.MetricsService;
24import org.onlab.osgi.DefaultServiceDirectory;
25
26import java.util.Map;
27import java.util.concurrent.TimeUnit;
28
29import static com.google.common.base.Preconditions.checkNotNull;
30
31/**
32 * Agent that implements usage and performance monitoring via the metrics service.
33 */
34public class MeteringAgent {
35
Flavio Castro6e044612015-08-13 14:13:58 -070036 private Counter exceptionCounter;
37 private Counter perObjExceptionCounter;
Flavio Castro41b1f3a2015-07-31 13:51:32 -070038 private MetricsService metricsService;
39 private MetricsComponent metricsComponent;
40 private MetricsFeature metricsFeature;
41 private final Map<String, Timer> perObjOpTimers = Maps.newConcurrentMap();
42 private final Map<String, Timer> perOpTimers = Maps.newConcurrentMap();
43 private Timer perPrimitiveTimer;
44 private Timer perObjTimer;
45 private MetricsFeature wildcard;
46 private final boolean activated;
47 private Context nullTimer;
48
49 /**
50 * Constructs a new MeteringAgent for a given distributed primitive.
51 * Instantiates the metrics service
52 * Initializes all the general metrics for that object
53 *
54 * @param primitiveName Type of primitive to be metered
55 * @param objName Global name of the primitive
Madan Jampanif97edc12015-08-31 14:41:01 -070056 * @param activated boolean flag for whether metering is enabled or not
Flavio Castro41b1f3a2015-07-31 13:51:32 -070057 */
58 public MeteringAgent(String primitiveName, String objName, boolean activated) {
59 checkNotNull(objName, "Object name cannot be null");
60 this.activated = activated;
61 nullTimer = new Context(null, "");
62 if (this.activated) {
63 this.metricsService = DefaultServiceDirectory.getService(MetricsService.class);
64 this.metricsComponent = metricsService.registerComponent(primitiveName);
65 this.metricsFeature = metricsComponent.registerFeature(objName);
66 this.wildcard = metricsComponent.registerFeature("*");
67 this.perObjTimer = metricsService.createTimer(metricsComponent, metricsFeature, "*");
68 this.perPrimitiveTimer = metricsService.createTimer(metricsComponent, wildcard, "*");
Flavio Castro6e044612015-08-13 14:13:58 -070069 this.perObjExceptionCounter = metricsService.createCounter(metricsComponent, metricsFeature, "exceptions");
70 this.exceptionCounter = metricsService.createCounter(metricsComponent, wildcard, "exceptions");
Flavio Castro41b1f3a2015-07-31 13:51:32 -070071 }
72 }
73
74 /**
75 * Initializes a specific timer for a given operation.
76 *
77 * @param op Specific operation being metered
Madan Jampanif97edc12015-08-31 14:41:01 -070078 * @return timer context
Flavio Castro41b1f3a2015-07-31 13:51:32 -070079 */
80 public Context startTimer(String op) {
81 if (!activated) {
82 return nullTimer;
83 }
84 // Check if timer exists, if it doesn't creates it
Yuta HIGUCHIebf88d62017-06-08 11:11:50 -070085 final Timer currTimer = getObjectOperationTimer(op);
Flavio Castro41b1f3a2015-07-31 13:51:32 -070086 // Starts timer
87 return new Context(currTimer.time(), op);
88 }
89
90 /**
Yuta HIGUCHIebf88d62017-06-08 11:11:50 -070091 * Get or creates operation timer specific to this agent's object.
92 *
93 * @param operation name
94 * @return Timer
95 */
96 private Timer getObjectOperationTimer(String operation) {
97 Timer t = perObjOpTimers.get(operation);
98 if (t != null) {
99 return t;
100 }
101 return perObjOpTimers.computeIfAbsent(operation,
102 this::createObjectOperationTimer);
103 }
104
105 /**
106 * Creates operation timer specific to this agent's object.
107 * <p>
108 * Intended to be called from {@code perObjOpTimers.computeIfAbsent(..)}
109 *
110 * @param operation name
111 * @return Timer
112 */
113 private Timer createObjectOperationTimer(String operation) {
114 return metricsService.createTimer(metricsComponent, metricsFeature, operation);
115 }
116
117 /**
118 * Get or creates operation timer common for all objects.
119 *
120 * @param operation name
121 * @return Timer
122 */
123 private Timer getOperationTimer(String operation) {
124 Timer t = perOpTimers.get(operation);
125 if (t != null) {
126 return t;
127 }
128 return perOpTimers.computeIfAbsent(operation,
129 this::createOperationTimer);
130 }
131
132 /**
133 * Creates operation timer common for all objects.
134 * <p>
135 * Intended to be called from {@code perOpTimers.computeIfAbsent(..)}
136 *
137 * @param operation name
138 * @return Timer
139 */
140 private Timer createOperationTimer(String operation) {
141 return metricsService.createTimer(metricsComponent, wildcard, operation);
142 }
143
144 /**
Flavio Castro41b1f3a2015-07-31 13:51:32 -0700145 * Timer.Context with a specific operation.
146 */
147 public class Context {
148 private final Timer.Context context;
149 private final String operation;
150
151 /**
152 * Constructs Context.
153 *
Madan Jampanif97edc12015-08-31 14:41:01 -0700154 * @param context context
155 * @param operation operation name
Flavio Castro41b1f3a2015-07-31 13:51:32 -0700156 */
157 public Context(Timer.Context context, String operation) {
158 this.context = context;
159 this.operation = operation;
160 }
161
162 /**
163 * Stops timer given a specific context and updates all related metrics.
Madan Jampanif97edc12015-08-31 14:41:01 -0700164 * @param e throwable
Flavio Castro41b1f3a2015-07-31 13:51:32 -0700165 */
Flavio Castro6e044612015-08-13 14:13:58 -0700166 public void stop(Throwable e) {
Flavio Castro41b1f3a2015-07-31 13:51:32 -0700167 if (!activated) {
168 return;
169 }
Flavio Castro6e044612015-08-13 14:13:58 -0700170 if (e == null) {
171 //Stop and updates timer with specific measurements per map, per operation
172 final long time = context.stop();
173 //updates timer with aggregated measurements per map
Yuta HIGUCHIebf88d62017-06-08 11:11:50 -0700174 getOperationTimer(operation).update(time, TimeUnit.NANOSECONDS);
Flavio Castro6e044612015-08-13 14:13:58 -0700175 //updates timer with aggregated measurements per map
176 perObjTimer.update(time, TimeUnit.NANOSECONDS);
177 //updates timer with aggregated measurements per all Consistent Maps
178 perPrimitiveTimer.update(time, TimeUnit.NANOSECONDS);
179 } else {
180 exceptionCounter.inc();
181 perObjExceptionCounter.inc();
182 }
Flavio Castro41b1f3a2015-07-31 13:51:32 -0700183 }
184 }
185
186}