001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.metrics; 018 019import org.apache.wicket.Application; 020import org.apache.wicket.MetaDataKey; 021import org.apache.wicket.WicketRuntimeException; 022import org.aspectj.lang.ProceedingJoinPoint; 023 024import com.codahale.metrics.Counter; 025import com.codahale.metrics.MetricRegistry; 026import com.codahale.metrics.Timer.Context; 027 028/** 029 * Base aspect provides access to the metric registry 030 * 031 * @author Tobias Soloschenko 032 * 033 */ 034public class WicketMetrics 035{ 036 037 /** 038 * The name of the filter the metrics are going to collect of 039 */ 040 private static String filterName; 041 042 private static final String APPLICATION_ERROR = "The application couldn't be resolved, please ensure to apply \"<aspect name=\"org.apache.wicket.metrics.aspects.WicketFilterInitAspect\" />\" to your aspects"; 043 044 /** The key for metrics registry **/ 045 public static final MetaDataKey<MetricRegistry> METRIC_REGISTRY = new MetaDataKey<>() 046 { 047 private static final long serialVersionUID = 1L; 048 }; 049 050 /** The key for metrics registry **/ 051 public static final MetaDataKey<WicketMetricsSettings> METRIC_SETTINGS = new MetaDataKey<>() 052 { 053 private static final long serialVersionUID = 1L; 054 }; 055 056 /** 057 * Simply measure the time for a {@literal @}around 058 * 059 * @param name 060 * the name of the timer context 061 * @param joinPoint 062 * the joinPoint to be proceed 063 * 064 * @return the value of the join point 065 * @throws Throwable 066 * if there is an exception while execution 067 * @see org.apache.wicket.metrics.WicketMetrics#measureTime(String, ProceedingJoinPoint, 068 * boolean) 069 */ 070 public Object measureTime(String name, ProceedingJoinPoint joinPoint) throws Throwable 071 { 072 return this.measureTime(name, joinPoint, true); 073 } 074 075 /** 076 * Simply measure the time for a {@literal @}around 077 * 078 * @param name 079 * the name of the timer context 080 * @param joinPoint 081 * the joinPoint to be proceed 082 * @param renderClass 083 * if the class name should be rendered behind the metric path 084 * 085 * @return the value of the join point 086 * @throws Throwable 087 * if there is an exception while execution 088 */ 089 public Object measureTime(String name, ProceedingJoinPoint joinPoint, boolean renderClass) 090 throws Throwable 091 { 092 WicketMetricsSettings settings = getSettings(); 093 MetricRegistry registry = getMetricRegistry(); 094 095 if (settings.isEnabled()) 096 { 097 Context context = registry 098 .timer( 099 settings.getPrefix() + name + (renderClass ? renderClassName(joinPoint) : "")) 100 .time(); 101 try 102 { 103 return proceedSilent(joinPoint); 104 } 105 finally 106 { 107 stopQuietly(context); 108 } 109 } 110 else 111 { 112 return proceedSilent(joinPoint); 113 } 114 } 115 116 /** 117 * Stops the context quietly 118 * 119 * @param context 120 * the context to stop 121 */ 122 public void stopQuietly(Context context) 123 { 124 if (context != null) 125 { 126 context.stop(); 127 } 128 } 129 130 /** 131 * 132 * @author Tobias Soloschenko 133 * 134 */ 135 public enum CounterOperation { 136 /** 137 * Increments 138 */ 139 INC, 140 /** 141 * Decrements 142 */ 143 DEC 144 } 145 146 /** 147 * Creates a count of the given arguments 148 * 149 * @param name 150 * the name of the meter to be marked 151 * @param joinPoint 152 * the join point 153 * @param counterOperation 154 * the operation 155 * @param value 156 * the value to update the counter 157 * @return the result of the proceeded join point 158 * @throws Throwable 159 */ 160 public Object count(String name, ProceedingJoinPoint joinPoint, 161 CounterOperation counterOperation, Long value) throws Throwable 162 { 163 WicketMetricsSettings settings = getSettings(); 164 MetricRegistry registry = getMetricRegistry(); 165 166 if (settings.isEnabled()) 167 { 168 Counter counter = registry 169 .counter(settings.getPrefix() + name + renderClassName(joinPoint)); 170 if (counterOperation == CounterOperation.INC) 171 { 172 counter.inc(value); 173 } 174 else 175 { 176 counter.dec(value); 177 } 178 } 179 return proceedSilent(joinPoint); 180 } 181 182 183 /** 184 * Marks the meter with the given name 185 * 186 * @param name 187 * the name of the meter to be marked 188 * @param joinPoint 189 * the join point 190 * @return the result of the proceeded join point 191 * @throws Throwable 192 */ 193 public Object mark(String name, ProceedingJoinPoint joinPoint) throws Throwable 194 { 195 WicketMetricsSettings settings = getSettings(); 196 MetricRegistry registry = getMetricRegistry(); 197 198 if (settings.isEnabled()) 199 { 200 registry.meter(settings.getPrefix() + name + renderClassName(joinPoint)).mark(); 201 } 202 return proceedSilent(joinPoint); 203 } 204 205 /** 206 * Proceed the join point silent 207 * 208 * @param joinPoint 209 * the join point to proceed 210 * @return the result of the proceeded join point 211 * @throws Throwable 212 * if the invocation throws an error 213 */ 214 private Object proceedSilent(ProceedingJoinPoint joinPoint) throws Throwable 215 { 216 return joinPoint != null ? joinPoint.proceed() : null; 217 } 218 219 /** 220 * Renders the class name of the given join point 221 * 222 * @param joinPoint 223 * the join point to get the class of 224 * @return the class name representation 225 */ 226 public String renderClassName(ProceedingJoinPoint joinPoint) 227 { 228 return joinPoint != null && joinPoint.getTarget() != null 229 ? "/" + joinPoint.getTarget().getClass().getName().replace('.', '_') : ""; 230 } 231 232 /** 233 * Gets the metric registry 234 * 235 * @return the metric registry 236 */ 237 public static synchronized MetricRegistry getMetricRegistry() 238 { 239 Application application = Application.get(getFilterName()); 240 if (application == null) 241 { 242 throw new WicketRuntimeException(APPLICATION_ERROR); 243 } 244 MetricRegistry metricRegistry = application.getMetaData(METRIC_REGISTRY); 245 if (metricRegistry == null) 246 { 247 metricRegistry = new MetricRegistry(); 248 application.setMetaData(METRIC_REGISTRY, metricRegistry); 249 } 250 return metricRegistry; 251 } 252 253 /** 254 * Gets the wicket metrics settings 255 * 256 * @return the wicket metrics settings 257 */ 258 public static synchronized WicketMetricsSettings getSettings() 259 { 260 Application application = Application.get(getFilterName()); 261 if (application == null) 262 { 263 throw new WicketRuntimeException(APPLICATION_ERROR); 264 } 265 WicketMetricsSettings wicketMetricsSettings = application.getMetaData(METRIC_SETTINGS); 266 if (wicketMetricsSettings == null) 267 { 268 wicketMetricsSettings = new WicketMetricsSettings(); 269 wicketMetricsSettings.setPrefix(getFilterName()); 270 application.setMetaData(METRIC_SETTINGS, wicketMetricsSettings); 271 } 272 return wicketMetricsSettings; 273 } 274 275 /** 276 * Gets the filter name the application should be resolved with 277 * 278 * @return the filter name 279 */ 280 public static String getFilterName() 281 { 282 return filterName; 283 } 284 285 /** 286 * Sets the filter name the application should be resolved with 287 * 288 * @param filterName 289 * the filter name 290 */ 291 public static void setFilterName(String filterName) 292 { 293 WicketMetrics.filterName = filterName; 294 } 295}