diff --git a/java/google/registry/dns/BUILD b/java/google/registry/dns/BUILD index be0fd98ac..eb3af1047 100644 --- a/java/google/registry/dns/BUILD +++ b/java/google/registry/dns/BUILD @@ -38,6 +38,7 @@ java_library( "//java/google/registry/config", "//java/google/registry/dns/writer", "//java/google/registry/model", + "//java/google/registry/monitoring/metrics", "//java/google/registry/request", "//java/google/registry/util", ], diff --git a/java/google/registry/dns/DnsMetrics.java b/java/google/registry/dns/DnsMetrics.java new file mode 100644 index 000000000..93c75fa30 --- /dev/null +++ b/java/google/registry/dns/DnsMetrics.java @@ -0,0 +1,68 @@ +// Copyright 2016 The Domain Registry Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.dns; + +import com.google.common.collect.ImmutableSet; +import google.registry.monitoring.metrics.IncrementableMetric; +import google.registry.monitoring.metrics.LabelDescriptor; +import google.registry.monitoring.metrics.MetricRegistryImpl; + +/** + * DNS instrumentation. + */ +public class DnsMetrics { + + private static final ImmutableSet LABEL_DESCRIPTORS = + ImmutableSet.of( + LabelDescriptor.create("tld", "TLD"), + LabelDescriptor.create( + "status", "Whether the publish request was accepted or rejected.")); + private static final IncrementableMetric publishDomainRequests = + MetricRegistryImpl.getDefault() + .newIncrementableMetric( + "/dns/publish_domain_requests", + "count of publishDomain requests", + "count", + LABEL_DESCRIPTORS); + private static final IncrementableMetric publishHostRequests = + MetricRegistryImpl.getDefault() + .newIncrementableMetric( + "/dns/publish_host_requests", + "count of publishHost requests", + "count", + LABEL_DESCRIPTORS); + + /** + * Increment a monotonic counter that tracks calls to {@link + * google.registry.dns.writer.DnsWriter#publishDomain(String)}, per TLD. + */ + public void incrementPublishDomainRequests(String tld, Status status) { + publishDomainRequests.increment(tld, status.name()); + } + + /** + * Increment a monotonic counter that tracks calls to {@link + * google.registry.dns.writer.DnsWriter#publishHost(String)}, per TLD. + */ + public void incrementPublishHostRequests(String tld, Status status) { + publishHostRequests.increment(tld, status.name()); + } + + /** Enum to encode the disposition of a publish request. */ + public enum Status { + ACCEPTED, + REJECTED + } +} diff --git a/java/google/registry/dns/PublishDnsUpdatesAction.java b/java/google/registry/dns/PublishDnsUpdatesAction.java index 3766503f3..31e635464 100644 --- a/java/google/registry/dns/PublishDnsUpdatesAction.java +++ b/java/google/registry/dns/PublishDnsUpdatesAction.java @@ -20,6 +20,7 @@ import static google.registry.util.CollectionUtils.nullToEmpty; import com.google.common.net.InternetDomainName; import google.registry.config.ConfigModule.Config; +import google.registry.dns.DnsMetrics.Status; import google.registry.dns.writer.DnsWriter; import google.registry.request.Action; import google.registry.request.HttpException.ServiceUnavailableException; @@ -44,6 +45,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable { @Inject DnsQueue dnsQueue; @Inject DnsWriterProxy dnsWriterProxy; + @Inject DnsMetrics dnsMetrics; @Inject @Config("dnsWriteLockTimeout") Duration timeout; @Inject @Parameter(RequestParameters.PARAM_TLD) String tld; @Inject @Parameter(DOMAINS_PARAM) Set domains; @@ -76,16 +78,20 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable { for (String domain : nullToEmpty(domains)) { if (!DomainNameUtils.isUnder( InternetDomainName.from(domain), InternetDomainName.from(tld))) { + dnsMetrics.incrementPublishDomainRequests(tld, Status.REJECTED); logger.severefmt("%s: skipping domain %s not under tld", tld, domain); } else { + dnsMetrics.incrementPublishDomainRequests(tld, Status.ACCEPTED); writer.publishDomain(domain); } } for (String host : nullToEmpty(hosts)) { if (!DomainNameUtils.isUnder( InternetDomainName.from(host), InternetDomainName.from(tld))) { + dnsMetrics.incrementPublishHostRequests(tld, Status.REJECTED); logger.severefmt("%s: skipping host %s not under tld", tld, host); } else { + dnsMetrics.incrementPublishHostRequests(tld, Status.ACCEPTED); writer.publishHost(host); } } diff --git a/java/google/registry/module/backend/BackendComponent.java b/java/google/registry/module/backend/BackendComponent.java index ac12306e7..4ec783f28 100644 --- a/java/google/registry/module/backend/BackendComponent.java +++ b/java/google/registry/module/backend/BackendComponent.java @@ -47,6 +47,7 @@ import javax.inject.Singleton; @Component( modules = { AppIdentityCredentialModule.class, + BackendMetricsModule.class, BigqueryModule.class, ConfigModule.class, DatastoreServiceModule.class, diff --git a/java/google/registry/module/backend/BackendMetricsModule.java b/java/google/registry/module/backend/BackendMetricsModule.java new file mode 100644 index 000000000..adc64ccc8 --- /dev/null +++ b/java/google/registry/module/backend/BackendMetricsModule.java @@ -0,0 +1,34 @@ +// Copyright 2016 The Domain Registry Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.module.backend; + +import dagger.Module; +import dagger.Provides; +import google.registry.dns.DnsMetrics; +import javax.inject.Singleton; + +/** + * Dagger module for injecting metrics. All metrics should have {@link Singleton} scope to avoid + * being recreated per-request, as the metrics generally track cumulative values. + */ +@Module +public class BackendMetricsModule { + + @Provides + @Singleton + static DnsMetrics provideDnsMetrics() { + return new DnsMetrics(); + } +} diff --git a/javatests/google/registry/dns/PublishDnsUpdatesActionTest.java b/javatests/google/registry/dns/PublishDnsUpdatesActionTest.java index 3f6792659..b788a1348 100644 --- a/javatests/google/registry/dns/PublishDnsUpdatesActionTest.java +++ b/javatests/google/registry/dns/PublishDnsUpdatesActionTest.java @@ -19,11 +19,13 @@ import static google.registry.testing.DatastoreHelper.persistActiveDomain; import static google.registry.testing.DatastoreHelper.persistActiveSubordinateHost; import static google.registry.testing.DatastoreHelper.persistResource; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import google.registry.dns.DnsMetrics.Status; import google.registry.dns.writer.DnsWriter; import google.registry.model.domain.DomainResource; import google.registry.model.ofy.Ofy; @@ -54,6 +56,7 @@ public class PublishDnsUpdatesActionTest { private final FakeClock clock = new FakeClock(DateTime.parse("1971-01-01TZ")); private final DnsWriter dnsWriter = mock(DnsWriter.class); + private final DnsMetrics dnsMetrics = mock(DnsMetrics.class); private PublishDnsUpdatesAction action; @Before @@ -76,6 +79,7 @@ public class PublishDnsUpdatesActionTest { action.hosts = ImmutableSet.of(); action.domains = ImmutableSet.of(); action.dnsWriterProxy = new DnsWriterProxy(ImmutableMap.of("mock", dnsWriter)); + action.dnsMetrics = dnsMetrics; return action; } @@ -84,9 +88,13 @@ public class PublishDnsUpdatesActionTest { action = createAction("xn--q9jyb4c"); action.hosts = ImmutableSet.of("ns1.example.xn--q9jyb4c"); action.run(); + verify(dnsWriter).publishHost("ns1.example.xn--q9jyb4c"); verify(dnsWriter).close(); verifyNoMoreInteractions(dnsWriter); + + verify(dnsMetrics).incrementPublishHostRequests("xn--q9jyb4c", Status.ACCEPTED); + verifyNoMoreInteractions(dnsMetrics); } @Test @@ -94,9 +102,13 @@ public class PublishDnsUpdatesActionTest { action = createAction("xn--q9jyb4c"); action.domains = ImmutableSet.of("example.xn--q9jyb4c"); action.run(); + verify(dnsWriter).publishDomain("example.xn--q9jyb4c"); verify(dnsWriter).close(); verifyNoMoreInteractions(dnsWriter); + + verify(dnsMetrics).incrementPublishDomainRequests("xn--q9jyb4c", Status.ACCEPTED); + verifyNoMoreInteractions(dnsMetrics); } @Test @@ -106,6 +118,7 @@ public class PublishDnsUpdatesActionTest { action.hosts = ImmutableSet.of( "ns1.example.xn--q9jyb4c", "ns2.example.xn--q9jyb4c", "ns1.example2.xn--q9jyb4c"); action.run(); + verify(dnsWriter).publishDomain("example.xn--q9jyb4c"); verify(dnsWriter).publishDomain("example2.xn--q9jyb4c"); verify(dnsWriter).publishHost("ns1.example.xn--q9jyb4c"); @@ -113,6 +126,10 @@ public class PublishDnsUpdatesActionTest { verify(dnsWriter).publishHost("ns1.example2.xn--q9jyb4c"); verify(dnsWriter).close(); verifyNoMoreInteractions(dnsWriter); + + verify(dnsMetrics, times(2)).incrementPublishDomainRequests("xn--q9jyb4c", Status.ACCEPTED); + verify(dnsMetrics, times(3)).incrementPublishHostRequests("xn--q9jyb4c", Status.ACCEPTED); + verifyNoMoreInteractions(dnsMetrics); } @Test @@ -121,7 +138,12 @@ public class PublishDnsUpdatesActionTest { action.domains = ImmutableSet.of("example.com", "example2.com"); action.hosts = ImmutableSet.of("ns1.example.com", "ns2.example.com", "ns1.example2.com"); action.run(); + verify(dnsWriter).close(); verifyNoMoreInteractions(dnsWriter); + + verify(dnsMetrics, times(2)).incrementPublishDomainRequests("xn--q9jyb4c", Status.REJECTED); + verify(dnsMetrics, times(3)).incrementPublishHostRequests("xn--q9jyb4c", Status.REJECTED); + verifyNoMoreInteractions(dnsMetrics); } }