1 /*
2 * Copyright 2010-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
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 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16 package com.amazonaws.metrics;
17 import static com.amazonaws.SDKGlobalConfiguration.DEFAULT_METRICS_SYSTEM_PROPERTY;
18
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.net.InetAddress;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashSet;
26 import java.util.Set;
27
28 import com.amazonaws.regions.Region;
29 import com.amazonaws.regions.RegionUtils;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32
33 import com.amazonaws.SDKGlobalConfiguration;
34 import com.amazonaws.auth.AWSCredentials;
35 import com.amazonaws.auth.AWSCredentialsProvider;
36 import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
37 import com.amazonaws.auth.PropertiesCredentials;
38 import com.amazonaws.jmx.spi.SdkMBeanRegistry;
39 import com.amazonaws.regions.Regions;
40 import com.amazonaws.util.AWSRequestMetrics;
41 import com.amazonaws.util.AWSRequestMetrics.Field;
42 import com.amazonaws.util.AWSServiceMetrics;
43
44 /**
45 * Used to control the default AWS SDK metric collection system.
46 * <p>
47 * The default metric collection of the Java AWS SDK is disabled by default. To
48 * enable it, simply specify the system property
49 * <b>"com.amazonaws.sdk.enableDefaultMetrics"</b> when starting up the JVM.
50 * When the system property is specified, a default metric collector will be
51 * started at the AWS SDK level. The default implementation uploads the
52 * request/response metrics captured to Amazon CloudWatch using AWS credentials
53 * obtained via the {@link DefaultAWSCredentialsProviderChain}.
54 * <p>
55 * By default, the metrics are uploaded to the us-east-1 region. You can
56 * change the region by changing the system property as
57 * -Dcom.amazonaws.sdk.enableDefaultMetrics=cloudwatchRegion={newregion}
58 * See #CLOUDWATCH_REGION
59 * </p>
60 * <p>
61 * For additional optional attributes that can be specified for the system
62 * property, please read the javadoc of the individual fields of
63 * this class for more details.
64 * <p>
65 * Instead of via system properties, the default AWS SDK metric collection can
66 * also be enabled programmatically via {@link #enableDefaultMetrics()}.
67 * Similarly, metric collection at the AWS SDK level can be disabled via
68 * {@link #disableMetrics()}.
69 * <p>
70 * Clients who needs to fully customize the metric collection can implement the
71 * SPI {@link MetricCollector}, and then replace the default AWS SDK
72 * implementation of the collector via
73 * {@link #setMetricCollector(MetricCollector)}.
74 * <p>
75 * Alternatively, for limited customization of the internal collector
76 * implementation provided by the AWS SDK, one can extend the internal Amazon
77 * CloudWatch metric collector. See the javadoc at
78 * com.amazonaws.metrics.internal.cloudwatch.CloudWatchMetricConfig for more
79 * details.
80 */
81 public enum AwsSdkMetrics {
82 ;
83 private static final Log log = LogFactory.getLog(AwsSdkMetrics.class);
84 public static final String DEFAULT_METRIC_NAMESPACE = "AWSSDK/Java";
85 private static final String MBEAN_OBJECT_NAME =
86 "com.amazonaws.management:type=" + AwsSdkMetrics.class.getSimpleName();
87
88 /**
89 * Object name under which the Admin Mbean of the current classloader is
90 * registered.
91 */
92 private static volatile String registeredAdminMbeanName;
93 /**
94 * Used to enable the use of a single metric namespace for all levels of SDK
95 * generated CloudWatch metrics such as JVM level, host level, etc.
96 *
97 * <pre>
98 * Example:
99 * -Dcom.amazonaws.sdk.enableDefaultMetrics=useSingleMetricNamespace
100 * </pre>
101 */
102 public static final String USE_SINGLE_METRIC_NAMESPACE = "useSingleMetricNamespace";
103 /**
104 * Used to exclude the generation of JVM metrics when the AWS SDK default
105 * metrics is enabled.
106 * By default, jvm metrics is included.
107 *
108 * <pre>
109 * Example:
110 * -Dcom.amazonaws.sdk.enableDefaultMetrics=excludeJvmMetrics
111 * </pre>
112 */
113 public static final String EXCLUDE_MACHINE_METRICS = "excludeMachineMetrics";
114
115 /**
116 * Used to generate per host level metrics when the AWS SDK default
117 * metrics is enabled.
118 * By default, per-host level metrics is excluded.
119 *
120 * <pre>
121 * Example:
122 * -Dcom.amazonaws.sdk.enableDefaultMetrics=includePerHostMetrics
123 * </pre>
124 */
125 public static final String INCLUDE_PER_HOST_METRICS = "includePerHostMetrics";
126
127 /**
128 * Used to specify an AWS credential property file.
129 * By default, the {@link DefaultAWSCredentialsProviderChain} is used.
130 *
131 * <pre>
132 * Example:
133 * -Dcom.amazonaws.sdk.enableDefaultMetrics=credentialFile=/path/aws.properties
134 * </pre>
135 */
136 public static final String AWS_CREDENTIAL_PROPERTIES_FILE= "credentialFile";
137
138 /**
139 * Used to specify an AWS credential property file.
140 * By default, the {@link DefaultAWSCredentialsProviderChain} is used.
141 *
142 * <pre>
143 * Example:
144 * -Dcom.amazonaws.sdk.enableDefaultMetrics=credentialFile=/path/aws.properties
145 * </pre>
146 * @deprecated in favor of {@link AWS_CREDENTIAL_PROPERTIES_FILE}
147 */
148 @Deprecated
149 public static final String AWS_CREDENTAIL_PROPERTIES_FILE = AWS_CREDENTIAL_PROPERTIES_FILE;
150
151 /**
152 * Used to specify the Amazon CloudWatch region for metrics uploading purposes.
153 * By default, metrics are uploaded to us-east-1.
154 *
155 * <pre>
156 * Example:
157 * -Dcom.amazonaws.sdk.enableDefaultMetrics=cloudwatchRegion=us-west-2
158 * </pre>
159 */
160 public static final String CLOUDWATCH_REGION = "cloudwatchRegion";
161
162 /**
163 * Used to specify the internal in-memory queue size for queuing metrics
164 * data points. The default size is 1,000.
165 *
166 * <pre>
167 * Example:
168 * -Dcom.amazonaws.sdk.enableDefaultMetrics=metricQueueSize=1000
169 * </pre>
170 */
171 public static final String METRIC_QUEUE_SIZE = "metricQueueSize";
172
173 /**
174 * Used to specify the internal queue polling timeout in millisecond.
175 * The default timeout is 1 minute, which is optimal for the default
176 * CloudWatch implementation.
177 *
178 * <pre>
179 * Example:
180 * -Dcom.amazonaws.sdk.enableDefaultMetrics=getQueuePollTimeoutMilli=60000
181 * </pre>
182 */
183 public static final String QUEUE_POLL_TIMEOUT_MILLI = "getQueuePollTimeoutMilli";
184
185 /**
186 * Used to specify a custom metric name space.
187 * The default name space is {@link #DEFAULT_METRIC_NAMESPACE}.
188 *
189 * <pre>
190 * Example:
191 * -Dcom.amazonaws.sdk.enableDefaultMetrics=metricNameSpace=MyNameSpace
192 * </pre>
193 */
194 public static final String METRIC_NAME_SPACE = "metricNameSpace";
195
196 /**
197 * Used to generate per JVM level metrics when the AWS SDK default
198 * metrics is enabled.
199 * By default, JVM level metrics are not generated.
200 *
201 * <pre>
202 * Example:
203 * -Dcom.amazonaws.sdk.enableDefaultMetrics=jvmMetricName=Tomcat1
204 * </pre>
205 */
206 public static final String JVM_METRIC_NAME = "jvmMetricName";
207
208 /**
209 * Used to explicitly specify the host name for metric purposes, instead of
210 * detecting the host name via {@link InetAddress} when the AWS SDK default
211 * metrics is enabled. Specifying the host name also has the side effecting
212 * of enabling per host level metrics.
213 *
214 * <pre>
215 * Example:
216 * -Dcom.amazonaws.sdk.enableDefaultMetrics=hostMetricName=MyHost
217 * </pre>
218 */
219 public static final String HOST_METRIC_NAME = "hostMetricName";
220
221 private static final String DEFAULT_METRIC_COLLECTOR_FACTORY =
222 "com.amazonaws.metrics.internal.cloudwatch.DefaultMetricCollectorFactory";
223
224 /**
225 * Used to explicitly enable {@link Field#HttpSocketReadTime} for recording socket read time.
226 *
227 * <pre>
228 * Example:
229 * -Dcom.amazonaws.sdk.enableDefaultMetrics=enableHttpSocketReadMetric
230 * </pre>
231 */
232 private static final String ENABLE_HTTP_SOCKET_READ_METRIC = "enableHttpSocketReadMetric";
233 /**
234 * True if the system property {@link #DEFAULT_METRICS_SYSTEM_PROPERTY} has
235 * been set; false otherwise.
236 */
237 private static final boolean defaultMetricsEnabled;
238 private static volatile AWSCredentialsProvider credentialProvider;
239 /**
240 * True if machine metrics is to be excluded; false otherwise.
241 */
242 private static volatile boolean machineMetricsExcluded;
243 /**
244 * True if per-host metrics is to be included; false if per-host metrics is
245 * to be excluded when {@link #hostMetricName} is not specified. In the
246 * absence of {@link #hostMetricName}, the host name will be automatically
247 * detected via {@link InetAddress}.
248 */
249 private static volatile boolean perHostMetricsIncluded;
250
251 /**
252 * True if socket read time metric is enabled; false otherwise. The {@link Field#HttpSocketReadTime}
253 * could have a big impact to performance, disabled by default.
254 */
255 private static volatile boolean httpSocketReadMetricEnabled;
256
257 private static volatile Region region;
258 private static volatile Integer metricQueueSize;
259 private static volatile Long queuePollTimeoutMilli;
260 private static volatile String metricNameSpace = DEFAULT_METRIC_NAMESPACE;
261 private static volatile String credentialFile;
262
263 /**
264 * No JVM level metrics is generated if this field is set to null or blank.
265 * Otherwise, the value in this field is used to compose the metric name
266 * space.
267 *
268 * Example:
269 * <ol>
270 * <li>If jvmMetricName="Tomcat1" and host-level metrics is disabled, the
271 * metric name space will be something like: "AWSSDK/Java/Tomcat1".</li>
272 * <li>If jvmMetricName="Tomcat1" and host-level metrics is enabled, the
273 * metric name space will be something like:
274 * "AWSSDK/Java/myhost.mycompany.com/Tomcat1".</li>
275 * <li>If jvmMetricName="Tomcat1" and host-level metrics is enabled and the
276 * metricNameSpace="MyNameSpace", the metric name space will be something
277 * like: "MyNameSpace/myhost.mycompany.com/Tomcat1".</li>
278 * </ol>
279 */
280 private static volatile String jvmMetricName;
281 private static volatile String hostMetricName;
282 /**
283 * True if the same metric namespace is to be used for all levels (such as
284 * JVM level, host-level, etc.) of AWS Cloudwatch Metrics for the Java SDK;
285 * false otherwise.
286 */
287 private static volatile boolean singleMetricNamespace;
288
289 static {
290 String defaultMetrics = System.getProperty(DEFAULT_METRICS_SYSTEM_PROPERTY);
291 defaultMetricsEnabled = defaultMetrics != null;
292 if (defaultMetricsEnabled) {
293 String[] values = defaultMetrics.split(",");
294 boolean excludeMachineMetrics = false;
295 boolean includePerHostMetrics = false;
296 boolean useSingleMetricNamespace = false;
297 boolean enableHttpSocketReadMetric = false;
298 for (String s: values) {
299 String part = s.trim();
300 if (!excludeMachineMetrics && EXCLUDE_MACHINE_METRICS.equals(part)) {
301 excludeMachineMetrics = true;
302 } else if (!includePerHostMetrics && INCLUDE_PER_HOST_METRICS.equals(part)) {
303 includePerHostMetrics = true;
304 } else if (!useSingleMetricNamespace && USE_SINGLE_METRIC_NAMESPACE.equals(part)) {
305 useSingleMetricNamespace = true;
306 } else if (!enableHttpSocketReadMetric && ENABLE_HTTP_SOCKET_READ_METRIC.equals(part)) {
307 enableHttpSocketReadMetric = true;
308 } else {
309 String[] pair = part.split("=");
310 if (pair.length == 2) {
311 String key = pair[0].trim();
312 String value = pair[1].trim();
313 try {
314 if (AWS_CREDENTIAL_PROPERTIES_FILE.equals(key)) {
315 setCredentialFile0(value);
316 } else if (CLOUDWATCH_REGION.equals(key)) {
317 region = RegionUtils.getRegion(value);
318 } else if (METRIC_QUEUE_SIZE.equals(key)) {
319 Integer i = Integer.valueOf(value);
320 if (i.intValue() < 1)
321 throw new IllegalArgumentException(METRIC_QUEUE_SIZE + " must be at least 1");
322 metricQueueSize = i;
323 } else if (QUEUE_POLL_TIMEOUT_MILLI.equals(key)) {
324 Long i = Long.valueOf(value);
325 if (i.intValue() < 1000)
326 throw new IllegalArgumentException(QUEUE_POLL_TIMEOUT_MILLI + " must be at least 1000");
327 queuePollTimeoutMilli = i;
328 } else if (METRIC_NAME_SPACE.equals(key)) {
329 metricNameSpace = value;
330 } else if (JVM_METRIC_NAME.equals(key)) {
331 jvmMetricName = value;
332 } else if (HOST_METRIC_NAME.equals(key)) {
333 hostMetricName = value;
334 } else {
335 LogFactory.getLog(AwsSdkMetrics.class).debug("Ignoring unrecognized parameter: " + part);
336 }
337 } catch (Exception e) {
338 LogFactory.getLog(AwsSdkMetrics.class).debug("Ignoring failure", e);
339 }
340 }
341 }
342 }
343 machineMetricsExcluded = excludeMachineMetrics;
344 perHostMetricsIncluded = includePerHostMetrics;
345 singleMetricNamespace = useSingleMetricNamespace;
346 httpSocketReadMetricEnabled = enableHttpSocketReadMetric;
347 }
348 }
349
350 private static final MetricRegistry registry = new MetricRegistry();
351 private static volatile MetricCollector mc;
352 /**
353 * Used to disallow re-entrancy in enabling the default metric collection system.
354 */
355 private static boolean dirtyEnabling;
356 /** Exports AwsSdkMetrics for JMX access. */
357 static {
358 try {
359 registerMetricAdminMBean();
360 } catch(Exception ex) {
361 LogFactory.getLog(AwsSdkMetrics.class).warn("", ex);
362 }
363 }
364
365 /**
366 * Returns true if the metric admin MBean is currently registered for JMX
367 * access; false otherwise.
368 */
369 public static boolean isMetricAdminMBeanRegistered() {
370 SdkMBeanRegistry registry = SdkMBeanRegistry.Factory.getMBeanRegistry();
371 return registeredAdminMbeanName != null
372 && registry.isMBeanRegistered(registeredAdminMbeanName);
373 }
374
375 /**
376 * Returns the name of the registered admin mbean; or null if the admin
377 * mbean is not currently registered.
378 */
379 public static String getRegisteredAdminMbeanName() {
380 return registeredAdminMbeanName;
381 }
382
383 /**
384 * Registers the metric admin MBean for JMX access for the current
385 * classloader. If an AdminMbean is found to have been registered under a
386 * different class loader, the AdminMBean of the current class loader would
387 * be registered under the same name {@link #MBEAN_OBJECT_NAME} but with an
388 * additional suffix in the format of "/<count>", where count is a counter
389 * incrementing from 1.
390 *
391 * @return true if the registeration succeeded; false otherwise.
392 */
393 public static boolean registerMetricAdminMBean() {
394 SdkMBeanRegistry registry = SdkMBeanRegistry.Factory.getMBeanRegistry();
395 synchronized(AwsSdkMetrics.class) {
396 if (registeredAdminMbeanName != null)
397 return false; // already registered
398 boolean registered = registry.registerMetricAdminMBean(MBEAN_OBJECT_NAME);
399 if (registered) {
400 registeredAdminMbeanName = MBEAN_OBJECT_NAME;
401 } else {
402 String mbeanName = MBEAN_OBJECT_NAME;
403 int count = 0;
404 while (registry.isMBeanRegistered(mbeanName)) {
405 mbeanName = MBEAN_OBJECT_NAME + "/" + ++count;
406 }
407 registered = registry.registerMetricAdminMBean(mbeanName);
408 if (registered)
409 registeredAdminMbeanName = mbeanName;
410 }
411 if (registered)
412 log.debug("Admin mbean registered under " + registeredAdminMbeanName);
413 return registered;
414 }
415 }
416
417 /**
418 * Unregisters the metric admin MBean from JMX for the current classloader.
419 *
420 * @return true if the unregistration succeeded or if there is no admin
421 * MBean registered; false otherwise.
422 */
423 public static boolean unregisterMetricAdminMBean() {
424 SdkMBeanRegistry registry = SdkMBeanRegistry.Factory.getMBeanRegistry();
425 synchronized(AwsSdkMetrics.class) {
426 if (registeredAdminMbeanName == null)
427 return true;
428 boolean success = registry.unregisterMBean(registeredAdminMbeanName);
429 if (success)
430 registeredAdminMbeanName = null;
431 return success;
432 }
433 }
434 /**
435 * Returns a non-null request metric collector for the SDK. If no custom
436 * request metric collector has previously been specified via
437 * {@link #setMetricCollector(MetricCollector)} and the
438 * {@link SDKGlobalConfiguration#DEFAULT_METRICS_SYSTEM_PROPERTY} has been set, then this method
439 * will initialize and return the default metric collector provided by the
440 * AWS SDK on a best-attempt basis.
441 */
442 public static <T extends RequestMetricCollector> T getRequestMetricCollector() {
443 if (mc == null) {
444 if (isDefaultMetricsEnabled())
445 enableDefaultMetrics();
446 }
447 @SuppressWarnings("unchecked")
448 T t = (T)(mc == null ? RequestMetricCollector.NONE : mc.getRequestMetricCollector());
449 return t;
450 }
451
452 public static <T extends ServiceMetricCollector> T getServiceMetricCollector() {
453 if (mc == null) {
454 if (isDefaultMetricsEnabled())
455 enableDefaultMetrics();
456 }
457 @SuppressWarnings("unchecked")
458 T t = (T)(mc == null ? ServiceMetricCollector.NONE : mc.getServiceMetricCollector());
459 return t;
460 }
461
462 /**
463 * This method should never be called by anyone except the JMX MBean used
464 * for administrative purposes only.
465 */
466 static MetricCollector getInternalMetricCollector() {
467 return mc;
468 }
469
470 public static <T extends MetricCollector> T getMetricCollector() {
471 if (mc == null) {
472 if (isDefaultMetricsEnabled())
473 enableDefaultMetrics();
474 }
475 @SuppressWarnings("unchecked")
476 T t = (T)(mc == null ? MetricCollector.NONE : mc);
477 return t;
478 }
479
480 /**
481 * Sets the metric collector to be used by the AWS SDK, and stop the
482 * previously running collector used by the AWS SDK, if any. Note, however,
483 * a request metric collector specified at the web service client level or
484 * request level, if any, always takes precedence over the one specified at
485 * the AWS SDK level.
486 * <p>
487 * Caller of this method is responsible for starting the new metric
488 * collector specified as the input parameter.
489 *
490 * @param mc
491 * the metric collector to be used by the AWS SDK; or
492 * null if no metric collection is to be performed
493 * at the AWS SDK level.
494 *
495 * @see RequestMetricCollector
496 * @see RequestMetricCollector#NONE
497 */
498 public static synchronized void setMetricCollector(MetricCollector mc) {
499 MetricCollector old = AwsSdkMetrics.mc;
500 AwsSdkMetrics.mc = mc;
501 if (old != null) {
502 old.stop();
503 }
504 }
505
506 /**
507 * Used to set whether the machine metrics is to be excluded.
508 *
509 * @param excludeMachineMetrics true if machine metrics is to be excluded;
510 * false otherwise.
511 */
512 public static void setMachineMetricsExcluded(boolean excludeMachineMetrics) {
513 AwsSdkMetrics.machineMetricsExcluded = excludeMachineMetrics;
514 }
515
516 /**
517 * Used to set whether the per-host metrics is to be included.
518 *
519 * @param includePerHostMetrics true if per-host metrics is to be included;
520 * false otherwise.
521 */
522 public static void setPerHostMetricsIncluded(boolean includePerHostMetrics) {
523 AwsSdkMetrics.perHostMetricsIncluded = includePerHostMetrics;
524 }
525
526 /**
527 * Used to enable {@link Field#HttpSocketReadTime} metric since by default it is disabled.
528 */
529 public static void enableHttpSocketReadMetric() {
530 AwsSdkMetrics.httpSocketReadMetricEnabled = true;
531 }
532
533 /**
534 * Returns true if the system property
535 * {@link SDKGlobalConfiguration#DEFAULT_METRICS_SYSTEM_PROPERTY} has been
536 * set; false otherwise.
537 */
538 public static boolean isDefaultMetricsEnabled() {
539 return defaultMetricsEnabled;
540 }
541
542 /**
543 * Returns true if a single metric name space is to be used for all
544 * levels of SDK generated CloudWatch metrics, including JVM level, host
545 * level, etc.; false otherwise.
546 */
547 public static boolean isSingleMetricNamespace() {
548 return singleMetricNamespace;
549 }
550
551 /**
552 * Used to set whether a single metric name space is to be used for all
553 * levels of SDK generated CloudWatch metrics, including JVM level, host
554 * level, etc.
555 *
556 * @param singleMetricNamespace
557 * true if single metric name is to be used; false otherwise.
558 */
559 public static void setSingleMetricNamespace(boolean singleMetricNamespace) {
560 AwsSdkMetrics.singleMetricNamespace = singleMetricNamespace;
561 }
562
563 /**
564 * Returns true if metrics at the AWS SDK level is enabled; false
565 * if disabled.
566 */
567 public static boolean isMetricsEnabled() {
568 MetricCollector mc = AwsSdkMetrics.mc;
569 return mc != null && mc.isEnabled();
570 }
571
572 /**
573 * Returns true if machine metrics is to be excluded.
574 */
575 public static boolean isMachineMetricExcluded() {
576 return machineMetricsExcluded;
577 }
578
579 /**
580 * Returns true if the per-host metrics flag has been set; false otherwise.
581 */
582 public static boolean isPerHostMetricIncluded() {
583 return perHostMetricsIncluded;
584 }
585
586 /**
587 * Returns true if per-host metrics is enabled; false otherwise.
588 */
589 public static boolean isPerHostMetricEnabled() {
590 if (perHostMetricsIncluded)
591 return true;
592 String host = hostMetricName;
593 host = host == null ? "" : host.trim();
594 return host.length() > 0;
595 }
596
597 /**
598 * Returns true if HttpSocketReadMetric is enabled; false otherwise.
599 */
600 public static boolean isHttpSocketReadMetricEnabled() {
601 return httpSocketReadMetricEnabled;
602 }
603
604 /**
605 * Starts the default AWS SDK metric collector, but
606 * only if no metric collector is currently in use at the AWS SDK
607 * level.
608 *
609 * @return true if the default AWS SDK metric collector has been
610 * successfully started by this call; false otherwise.
611 */
612 public static synchronized boolean enableDefaultMetrics() {
613 if (mc == null || !mc.isEnabled()) {
614 if (dirtyEnabling) {
615 throw new IllegalStateException("Reentrancy is not allowed");
616 }
617 dirtyEnabling = true;
618 try {
619 Class<?> c = Class.forName(DEFAULT_METRIC_COLLECTOR_FACTORY);
620 MetricCollector.Factory f = (MetricCollector.Factory)c.newInstance();
621 MetricCollector instance = f.getInstance();
622 if (instance != null) {
623 setMetricCollector(instance);
624 return true;
625 }
626 } catch (Exception e) {
627 LogFactory.getLog(AwsSdkMetrics.class)
628 .warn("Failed to enable the default metrics", e);
629 } finally {
630 dirtyEnabling = false;
631 }
632 }
633 return false;
634 }
635
636 /**
637 * Convenient method to disable the metric collector at the AWS SDK
638 * level.
639 */
640 public static void disableMetrics() {
641 setMetricCollector(MetricCollector.NONE);
642 }
643
644 /**
645 * Adds the given metric type to the registry of predefined metrics to be
646 * captured at the AWS SDK level.
647 *
648 * @return true if the set of predefined metric types gets changed as a
649 * result of the call
650 */
651 public static boolean add(MetricType type) {
652 return type == null ? false : registry.addMetricType(type);
653 }
654 /**
655 * Adds the given metric types to the registry of predefined metrics to be
656 * captured at the AWS SDK level.
657 *
658 * @return true if the set of predefined metric types gets changed as a
659 * result of the call
660 */
661 public static <T extends MetricType> boolean addAll(Collection<T> types) {
662 return types == null || types.size() == 0
663 ? false
664 : registry.addMetricTypes(types);
665 }
666 /**
667 * Sets the given metric types to replace the registry of predefined metrics
668 * to be captured at the AWS SDK level.
669 */
670 public static <T extends MetricType> void set(Collection<T> types) {
671 registry.setMetricTypes(types);
672 }
673 /**
674 * Removes the given metric type from the registry of predefined metrics to
675 * be captured at the AWS SDK level.
676 *
677 * @return true if the set of predefined metric types gets changed as a
678 * result of the call
679 */
680 public static boolean remove(MetricType type) {
681 return type == null ? false : registry.removeMetricType(type);
682 }
683 /**
684 * Returns an unmodifiable set of the current predefined metrics.
685 */
686 public static Set<MetricType> getPredefinedMetrics() {
687 return registry.predefinedMetrics();
688 }
689
690 /**
691 * Returns the credential provider for the default AWS SDK metric implementation.
692 * This method is restricted to calls from the default AWS SDK metric implementation.
693 *
694 * @throws SecurityException if called outside the default AWS SDK metric implementation.
695 */
696 public static AWSCredentialsProvider getCredentialProvider() {
697 StackTraceElement[] e = Thread.currentThread().getStackTrace();
698 for (int i=0; i < e.length; i++) {
699 if (e[i].getClassName().equals(DEFAULT_METRIC_COLLECTOR_FACTORY)) {
700 return credentialProvider;
701 }
702 }
703 SecurityException ex = new SecurityException();
704 LogFactory.getLog(AwsSdkMetrics.class).warn("Illegal attempt to access the credential provider", ex);
705 throw ex;
706 }
707
708 /**
709 * Sets the credential provider for the default AWS SDK metric
710 * implementation; or null if the default is to be used. Calling this method
711 * may result in the credential provider being different from the credential
712 * file property.
713 */
714 public static synchronized void setCredentialProvider(
715 AWSCredentialsProvider provider) {
716 credentialProvider = provider;
717 }
718
719 /**
720 * Returns the region configured for the default AWS SDK metric collector;
721 * or null if the default is to be used.
722 *
723 * @throws IllegalArgumentException when using a region not included in
724 * {@link Regions}
725 *
726 * @deprecated Use {@link #getRegionName()}
727 */
728 public static Regions getRegion() throws IllegalArgumentException {
729 return Regions.fromName(region.getName());
730 }
731
732 /**
733 * Returns the region name configured for the default AWS SDK metric collector;
734 * or null if the default is to be used.
735 */
736 public static String getRegionName() {
737 return region == null ? null : region.getName();
738 }
739
740 /**
741 * Sets the region to be used for the default AWS SDK metric collector;
742 * or null if the default is to be used.
743 */
744 public static void setRegion(Regions region) {
745 AwsSdkMetrics.region = RegionUtils.getRegion(region.getName());
746 }
747
748 /**
749 * Sets the region to be used for the default AWS SDK metric collector;
750 * or null if the default is to be used.
751 */
752 public static void setRegion(String region) {
753 AwsSdkMetrics.region = RegionUtils.getRegion(region);
754 }
755
756 /**
757 * Returns the last set AWS credential file, or null if there is none.
758 * @deprecated use {@link AwsSdkMetrics#getCredentialFile()}
759 */
760 @Deprecated
761 public static String getCredentailFile() {
762 return credentialFile;
763 }
764
765 /**
766 * Returns the last set AWS credential file, or null if there is none.
767 */
768 public static String getCredentialFile() {
769 return credentialFile;
770 }
771
772 /**
773 * Sets the AWS credential file to be used for accessing Amazon CloudWatch.
774 * Successfully calling this method would result in the AWS credential
775 * provider to make use of the given credential file.
776 */
777 public static void setCredentialFile(String filepath)
778 throws FileNotFoundException, IOException {
779 setCredentialFile0(filepath);
780 }
781
782 /**
783 * Internal method to implement the {@link #setCredentialFile(String)}.
784 */
785 private static void setCredentialFile0(String filepath)
786 throws FileNotFoundException, IOException {
787 final PropertiesCredentials cred =
788 new PropertiesCredentials(new File(filepath));
789 synchronized(AwsSdkMetrics.class) {
790 credentialProvider = new AWSCredentialsProvider() {
791 @Override public void refresh() {}
792 @Override public AWSCredentials getCredentials() {
793 return cred;
794 }
795 };
796 AwsSdkMetrics.credentialFile = filepath;
797 }
798 }
799
800 /**
801 * Returns the internal metric queue size to be used for the default AWS SDK
802 * metric collector; or null if the default is to be used.
803 */
804 public static Integer getMetricQueueSize() {
805 return metricQueueSize;
806 }
807
808 /**
809 * Sets the metric queue size to be used for the default AWS SDK metric collector;
810 * or null if the default is to be used.
811 */
812 public static void setMetricQueueSize(Integer size) {
813 metricQueueSize = size;
814 }
815
816 /**
817 * Returns the internal metric queue timeout in millisecond to be used for
818 * the default AWS SDK metric collector; or null if the default is to be
819 * used.
820 */
821 public static Long getQueuePollTimeoutMilli() {
822 return queuePollTimeoutMilli;
823 }
824
825 /**
826 * Sets the queue poll time in millisecond to be used for the default AWS
827 * SDK metric collector; or null if the default is to be used.
828 */
829 public static void setQueuePollTimeoutMilli(Long timeoutMilli) {
830 queuePollTimeoutMilli = timeoutMilli;
831 }
832
833 /**
834 * Returns the metric name space, which is never null or blank.
835 */
836 public static String getMetricNameSpace() {
837 return metricNameSpace;
838 }
839
840 /**
841 * Sets the metric name space.
842 *
843 * @param metricNameSpace
844 * metric name space which must neither be null or blank.
845 *
846 * @throws IllegalArgumentException
847 * if the specified metric name space is either null or blank.
848 */
849 public static void setMetricNameSpace(String metricNameSpace) {
850 if (metricNameSpace == null || metricNameSpace.trim().length() == 0)
851 throw new IllegalArgumentException();
852 AwsSdkMetrics.metricNameSpace = metricNameSpace;
853 }
854
855 /**
856 * Returns the name of the JVM for generating per-JVM level metrics;
857 * or null or blank if per-JVM level metrics are disabled.
858 */
859 public static String getJvmMetricName() {
860 return jvmMetricName;
861 }
862
863 /**
864 * Sets the name of the JVM for generating per-JVM level metrics.
865 *
866 * @param jvmMetricName
867 * name of the JVM for generating per-JVM level metrics; or null
868 * or blank if per-JVM level metrics are to be disabled.
869 */
870 public static void setJvmMetricName(String jvmMetricName) {
871 AwsSdkMetrics.jvmMetricName = jvmMetricName;
872 }
873
874 /**
875 * Returns the host name for generating per-host level metrics; or
876 * null or blank if the host is to be automatically detected via
877 * {@link InetAddress}.
878 */
879 public static String getHostMetricName() {
880 return hostMetricName;
881 }
882
883 /**
884 * Sets the host name for generating per-host level metrics.
885 *
886 * @param hostMetricName
887 * host name for generating per-host level metrics; or
888 * null or blank if the host is to be automatically detected via
889 * {@link InetAddress}.
890 */
891 public static void setHostMetricName(String hostMetricName) {
892 AwsSdkMetrics.hostMetricName = hostMetricName;
893 }
894
895 /**
896 * Used as a registry for the predefined metrics to be captured by the
897 * metric collector at the AWS SDK level.
898 */
899 private static class MetricRegistry {
900 private final Set<MetricType> metricTypes = new HashSet<MetricType>();
901 private volatile Set<MetricType> readOnly;
902
903 MetricRegistry() {
904 metricTypes.add(Field.ClientExecuteTime);
905 metricTypes.add(Field.Exception);
906 metricTypes.add(Field.ThrottleException);
907 metricTypes.add(Field.HttpClientRetryCount);
908 metricTypes.add(Field.HttpRequestTime);
909 metricTypes.add(Field.RequestCount);
910 // metricTypes.add(Field.RequestSigningTime);
911 // metricTypes.add(Field.ResponseProcessingTime);
912 metricTypes.add(Field.RetryCount);
913 metricTypes.add(Field.RetryCapacityConsumed);
914 metricTypes.add(Field.ThrottledRetryCount);
915 metricTypes.add(Field.HttpClientSendRequestTime);
916 metricTypes.add(Field.HttpClientReceiveResponseTime);
917 metricTypes.add(Field.HttpSocketReadTime);
918 metricTypes.add(Field.HttpClientPoolAvailableCount);
919 metricTypes.add(Field.HttpClientPoolLeasedCount);
920 metricTypes.add(Field.HttpClientPoolPendingCount);
921 metricTypes.add(AWSServiceMetrics.HttpClientGetConnectionTime);
922 syncReadOnly();
923 }
924
925 private void syncReadOnly() {
926 readOnly = Collections.unmodifiableSet(new HashSet<MetricType>(metricTypes));
927 }
928
929 public boolean addMetricType(MetricType type) {
930 synchronized(metricTypes) {
931 boolean added = metricTypes.add(type);
932 if (added)
933 syncReadOnly();
934 return added;
935 }
936 }
937 public <T extends MetricType> boolean addMetricTypes(Collection<T> types) {
938 synchronized(metricTypes) {
939 boolean added = metricTypes.addAll(types);
940 if (added)
941 syncReadOnly();
942 return added;
943 }
944 }
945 public <T extends MetricType> void setMetricTypes(Collection<T> types) {
946 synchronized(metricTypes) {
947 if (types == null || types.size() == 0) {
948 if (metricTypes.size() == 0)
949 return;
950 if (types == null)
951 types = Collections.emptyList();
952 }
953 metricTypes.clear();
954 if (!addMetricTypes(types)) {
955 syncReadOnly(); // avoid missing sync
956 }
957 }
958 }
959 public boolean removeMetricType(MetricType type) {
960 synchronized(metricTypes) {
961 boolean removed = metricTypes.remove(type);
962 if (removed)
963 syncReadOnly();
964 return removed;
965 }
966 }
967 public Set<MetricType> predefinedMetrics() {
968 return readOnly;
969 }
970 }
971 }
972