mirror of
https://github.com/google/nomulus.git
synced 2025-05-14 16:37:13 +02:00
Add metrics for the command used in the registry CLI tool
Puts the metric in <project>/tools/commands_called It counts the use of the tool, with the following labels: - environment - tool (nomulus/gtech) - command called (class name) - success true/false - from the shell true/false ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=212879670
This commit is contained in:
parent
e72f5c09a2
commit
414b2e4db1
6 changed files with 145 additions and 4 deletions
|
@ -70,6 +70,7 @@ java_library(
|
||||||
"@com_google_api_client",
|
"@com_google_api_client",
|
||||||
"@com_google_apis_google_api_services_bigquery",
|
"@com_google_apis_google_api_services_bigquery",
|
||||||
"@com_google_apis_google_api_services_dns",
|
"@com_google_apis_google_api_services_dns",
|
||||||
|
"@com_google_apis_google_api_services_monitoring",
|
||||||
"@com_google_appengine_api_1_0_sdk",
|
"@com_google_appengine_api_1_0_sdk",
|
||||||
"@com_google_appengine_remote_api",
|
"@com_google_appengine_remote_api",
|
||||||
"@com_google_appengine_remote_api//:link",
|
"@com_google_appengine_remote_api//:link",
|
||||||
|
@ -81,6 +82,8 @@ java_library(
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
"@com_google_http_client",
|
"@com_google_http_client",
|
||||||
"@com_google_http_client_jackson2",
|
"@com_google_http_client_jackson2",
|
||||||
|
"@com_google_monitoring_client_metrics",
|
||||||
|
"@com_google_monitoring_client_stackdriver",
|
||||||
"@com_google_oauth_client",
|
"@com_google_oauth_client",
|
||||||
"@com_google_oauth_client_java6",
|
"@com_google_oauth_client_java6",
|
||||||
"@com_google_oauth_client_jetty",
|
"@com_google_oauth_client_jetty",
|
||||||
|
|
53
java/google/registry/tools/MetricToolModule.java
Normal file
53
java/google/registry/tools/MetricToolModule.java
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2018 The Nomulus 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.tools;
|
||||||
|
|
||||||
|
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||||
|
import com.google.api.services.monitoring.v3.Monitoring;
|
||||||
|
import com.google.api.services.monitoring.v3.model.MonitoredResource;
|
||||||
|
import com.google.monitoring.metrics.MetricWriter;
|
||||||
|
import com.google.monitoring.metrics.stackdriver.StackdriverWriter;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import google.registry.config.CredentialModule.DefaultCredential;
|
||||||
|
import google.registry.config.RegistryConfig.Config;
|
||||||
|
|
||||||
|
/** Dagger module for metrics on the client tool. */
|
||||||
|
@Module
|
||||||
|
public final class MetricToolModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
static Monitoring provideMonitoring(
|
||||||
|
@DefaultCredential GoogleCredential credential, @Config("projectId") String projectId) {
|
||||||
|
return new Monitoring.Builder(
|
||||||
|
credential.getTransport(), credential.getJsonFactory(), credential)
|
||||||
|
.setApplicationName(projectId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
static MetricWriter provideMetricWriter(
|
||||||
|
Monitoring monitoringClient,
|
||||||
|
@Config("projectId") String projectId,
|
||||||
|
@Config("stackdriverMaxQps") int maxQps,
|
||||||
|
@Config("stackdriverMaxPointsPerRequest") int maxPointsPerRequest) {
|
||||||
|
return new StackdriverWriter(
|
||||||
|
monitoringClient,
|
||||||
|
projectId,
|
||||||
|
new MonitoredResource().setType("global"),
|
||||||
|
maxQps,
|
||||||
|
maxPointsPerRequest);
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,10 +26,19 @@ import com.beust.jcommander.Parameter;
|
||||||
import com.beust.jcommander.ParameterException;
|
import com.beust.jcommander.ParameterException;
|
||||||
import com.beust.jcommander.Parameters;
|
import com.beust.jcommander.Parameters;
|
||||||
import com.beust.jcommander.ParametersDelegate;
|
import com.beust.jcommander.ParametersDelegate;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.monitoring.metrics.IncrementableMetric;
|
||||||
|
import com.google.monitoring.metrics.LabelDescriptor;
|
||||||
|
import com.google.monitoring.metrics.Metric;
|
||||||
|
import com.google.monitoring.metrics.MetricPoint;
|
||||||
|
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||||
|
import com.google.monitoring.metrics.MetricWriter;
|
||||||
import google.registry.model.ofy.ObjectifyService;
|
import google.registry.model.ofy.ObjectifyService;
|
||||||
import google.registry.tools.params.ParameterFactory;
|
import google.registry.tools.params.ParameterFactory;
|
||||||
|
import java.io.IOException;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
@ -38,6 +47,11 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
@Parameters(separators = " =", commandDescription = "Command-line interface to the registry")
|
@Parameters(separators = " =", commandDescription = "Command-line interface to the registry")
|
||||||
final class RegistryCli implements AutoCloseable, CommandRunner {
|
final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
|
|
||||||
|
// The environment parameter is parsed twice: once here, and once with {@link
|
||||||
|
// RegistryToolEnvironment#parseFromArgs} in the {@link RegistryTool#main} or {@link
|
||||||
|
// GtechTool#main} functions.
|
||||||
|
//
|
||||||
|
// The flag names must be in sync between the two, and also - this is ugly and we should feel bad.
|
||||||
@Parameter(
|
@Parameter(
|
||||||
names = {"-e", "--environment"},
|
names = {"-e", "--environment"},
|
||||||
description = "Sets the default environment to run the command.")
|
description = "Sets the default environment to run the command.")
|
||||||
|
@ -48,6 +62,9 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
description = "Returns all command names.")
|
description = "Returns all command names.")
|
||||||
private boolean showAllCommands;
|
private boolean showAllCommands;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean uploadMetrics = true;
|
||||||
|
|
||||||
// Do not make this final - compile-time constant inlining may interfere with JCommander.
|
// Do not make this final - compile-time constant inlining may interfere with JCommander.
|
||||||
@ParametersDelegate
|
@ParametersDelegate
|
||||||
private AppEngineConnectionFlags appEngineConnectionFlags =
|
private AppEngineConnectionFlags appEngineConnectionFlags =
|
||||||
|
@ -68,6 +85,24 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
// "shell".
|
// "shell".
|
||||||
private boolean isFirstUse = true;
|
private boolean isFirstUse = true;
|
||||||
|
|
||||||
|
private static final ImmutableSet<LabelDescriptor> LABEL_DESCRIPTORS_FOR_COMMANDS =
|
||||||
|
ImmutableSet.of(
|
||||||
|
LabelDescriptor.create("program", "The program used - e.g. nomulus or gtech_tool"),
|
||||||
|
LabelDescriptor.create("environment", "The environment used - e.g. sandbox"),
|
||||||
|
LabelDescriptor.create("command", "The command used"),
|
||||||
|
LabelDescriptor.create("success", "Whether the command succeeded"),
|
||||||
|
LabelDescriptor.create("shell", "Whether the command was called from the nomulus shell"));
|
||||||
|
|
||||||
|
private static final IncrementableMetric commandsCalledCount =
|
||||||
|
MetricRegistryImpl.getDefault()
|
||||||
|
.newIncrementableMetric(
|
||||||
|
"/tools/commands_called",
|
||||||
|
"Count of tool commands called",
|
||||||
|
"count",
|
||||||
|
LABEL_DESCRIPTORS_FOR_COMMANDS);
|
||||||
|
|
||||||
|
private MetricWriter metricWriter = null;
|
||||||
|
|
||||||
Map<String, ? extends Class<? extends Command>> commands;
|
Map<String, ? extends Class<? extends Command>> commands;
|
||||||
String programName;
|
String programName;
|
||||||
|
|
||||||
|
@ -89,9 +124,13 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
// http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ104
|
// http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ104
|
||||||
@Override
|
@Override
|
||||||
public void run(String[] args) throws Exception {
|
public void run(String[] args) throws Exception {
|
||||||
|
boolean inShell = !isFirstUse;
|
||||||
|
isFirstUse = false;
|
||||||
|
|
||||||
// Create the JCommander instance.
|
// Create the JCommander instance.
|
||||||
JCommander jcommander = new JCommander(this);
|
// If we're in the shell, we don't want to update the RegistryCli's parameters (so we give a
|
||||||
|
// dummy object to update)
|
||||||
|
JCommander jcommander = new JCommander(inShell ? new Object() : this);
|
||||||
jcommander.addConverterFactory(new ParameterFactory());
|
jcommander.addConverterFactory(new ParameterFactory());
|
||||||
jcommander.setProgramName(programName);
|
jcommander.setProgramName(programName);
|
||||||
|
|
||||||
|
@ -110,8 +149,8 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
// Create the "help" and "shell" commands (these are special in that they don't have a default
|
// Create the "help" and "shell" commands (these are special in that they don't have a default
|
||||||
// constructor).
|
// constructor).
|
||||||
jcommander.addCommand("help", new HelpCommand(jcommander));
|
jcommander.addCommand("help", new HelpCommand(jcommander));
|
||||||
if (isFirstUse) {
|
if (!inShell) {
|
||||||
isFirstUse = false;
|
// If we aren't inside a shell, then we want to add the shell command.
|
||||||
ShellCommand shellCommand = new ShellCommand(this);
|
ShellCommand shellCommand = new ShellCommand(this);
|
||||||
// We have to build the completions based on the jcommander *before* we add the "shell"
|
// We have to build the completions based on the jcommander *before* we add the "shell"
|
||||||
// command - to avoid completion for the "shell" command itself.
|
// command - to avoid completion for the "shell" command itself.
|
||||||
|
@ -153,17 +192,31 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
jcommander.getCommands().get(jcommander.getParsedCommand()).getObjects());
|
jcommander.getCommands().get(jcommander.getParsedCommand()).getObjects());
|
||||||
loggingParams.configureLogging(); // Must be called after parameters are parsed.
|
loggingParams.configureLogging(); // Must be called after parameters are parsed.
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
runCommand(command);
|
runCommand(command);
|
||||||
|
success = true;
|
||||||
} catch (AuthModule.LoginRequiredException ex) {
|
} catch (AuthModule.LoginRequiredException ex) {
|
||||||
System.err.println("===================================================================");
|
System.err.println("===================================================================");
|
||||||
System.err.println("You must login using 'nomulus login' prior to running this command.");
|
System.err.println("You must login using 'nomulus login' prior to running this command.");
|
||||||
System.err.println("===================================================================");
|
System.err.println("===================================================================");
|
||||||
|
} finally {
|
||||||
|
commandsCalledCount.increment(
|
||||||
|
programName,
|
||||||
|
environment.toString(),
|
||||||
|
command.getClass().getSimpleName(),
|
||||||
|
String.valueOf(success),
|
||||||
|
String.valueOf(inShell));
|
||||||
|
exportMetrics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
exportMetrics();
|
||||||
|
if (metricWriter != null) {
|
||||||
|
metricWriter = null;
|
||||||
|
}
|
||||||
if (installer != null) {
|
if (installer != null) {
|
||||||
installer.uninstall();
|
installer.uninstall();
|
||||||
installer = null;
|
installer = null;
|
||||||
|
@ -180,6 +233,9 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
|
|
||||||
private void runCommand(Command command) throws Exception {
|
private void runCommand(Command command) throws Exception {
|
||||||
injectReflectively(RegistryToolComponent.class, component, command);
|
injectReflectively(RegistryToolComponent.class, component, command);
|
||||||
|
if (metricWriter == null && uploadMetrics) {
|
||||||
|
metricWriter = component.metricWriter();
|
||||||
|
}
|
||||||
|
|
||||||
if (command instanceof CommandWithConnection) {
|
if (command instanceof CommandWithConnection) {
|
||||||
((CommandWithConnection) command).setConnection(getConnection());
|
((CommandWithConnection) command).setConnection(getConnection());
|
||||||
|
@ -211,6 +267,25 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||||
command.run();
|
command.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void exportMetrics() {
|
||||||
|
if (metricWriter == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (Metric<?> metric : MetricRegistryImpl.getDefault().getRegisteredMetrics()) {
|
||||||
|
for (MetricPoint<?> point : metric.getTimestampedValues()) {
|
||||||
|
metricWriter.write(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metricWriter.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.format("Failed to export metrics. Got error:\n%s\n\n", e);
|
||||||
|
System.err.println("Maybe you need to login? Try calling:");
|
||||||
|
System.err.println(" gcloud auth application-default login");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
void setEnvironment(RegistryToolEnvironment environment) {
|
void setEnvironment(RegistryToolEnvironment environment) {
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
|
import com.google.monitoring.metrics.MetricWriter;
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
import google.registry.config.CredentialModule;
|
import google.registry.config.CredentialModule;
|
||||||
import google.registry.config.RegistryConfig.ConfigModule;
|
import google.registry.config.RegistryConfig.ConfigModule;
|
||||||
|
@ -27,6 +28,7 @@ import google.registry.request.Modules.AppIdentityCredentialModule;
|
||||||
import google.registry.request.Modules.DatastoreServiceModule;
|
import google.registry.request.Modules.DatastoreServiceModule;
|
||||||
import google.registry.request.Modules.GoogleCredentialModule;
|
import google.registry.request.Modules.GoogleCredentialModule;
|
||||||
import google.registry.request.Modules.Jackson2Module;
|
import google.registry.request.Modules.Jackson2Module;
|
||||||
|
import google.registry.request.Modules.NetHttpTransportModule;
|
||||||
import google.registry.request.Modules.URLFetchServiceModule;
|
import google.registry.request.Modules.URLFetchServiceModule;
|
||||||
import google.registry.request.Modules.UrlFetchTransportModule;
|
import google.registry.request.Modules.UrlFetchTransportModule;
|
||||||
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
||||||
|
@ -63,6 +65,7 @@ import javax.inject.Singleton;
|
||||||
Jackson2Module.class,
|
Jackson2Module.class,
|
||||||
KeyModule.class,
|
KeyModule.class,
|
||||||
KmsModule.class,
|
KmsModule.class,
|
||||||
|
NetHttpTransportModule.class,
|
||||||
RdeModule.class,
|
RdeModule.class,
|
||||||
RegistryToolModule.class,
|
RegistryToolModule.class,
|
||||||
SystemClockModule.class,
|
SystemClockModule.class,
|
||||||
|
@ -74,6 +77,7 @@ import javax.inject.Singleton;
|
||||||
UserServiceModule.class,
|
UserServiceModule.class,
|
||||||
VoidDnsWriterModule.class,
|
VoidDnsWriterModule.class,
|
||||||
WhoisModule.class,
|
WhoisModule.class,
|
||||||
|
MetricToolModule.class,
|
||||||
})
|
})
|
||||||
interface RegistryToolComponent {
|
interface RegistryToolComponent {
|
||||||
void inject(CheckDomainClaimsCommand command);
|
void inject(CheckDomainClaimsCommand command);
|
||||||
|
@ -111,4 +115,6 @@ interface RegistryToolComponent {
|
||||||
void inject(WhoisQueryCommand command);
|
void inject(WhoisQueryCommand command);
|
||||||
|
|
||||||
AppEngineConnection appEngineConnection();
|
AppEngineConnection appEngineConnection();
|
||||||
|
|
||||||
|
MetricWriter metricWriter();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,7 @@ handlers = java.util.logging.ConsoleHandler
|
||||||
.level = INFO
|
.level = INFO
|
||||||
com.google.wrappers.base.GoogleInit.level = WARNING
|
com.google.wrappers.base.GoogleInit.level = WARNING
|
||||||
com.google.monitoring.metrics.MetricRegistryImpl.level = WARNING
|
com.google.monitoring.metrics.MetricRegistryImpl.level = WARNING
|
||||||
|
com.google.monitoring.metrics.MetricReporter.level = WARNING
|
||||||
|
com.google.monitoring.metrics.MetricExporter.level = WARNING
|
||||||
|
com.google.monitoring.metrics.stackdriver.StackdriverWriter.level = WARNING
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ public class ShellCommandTest {
|
||||||
public void testMultipleCommandInvocations() throws Exception {
|
public void testMultipleCommandInvocations() throws Exception {
|
||||||
try (RegistryCli cli =
|
try (RegistryCli cli =
|
||||||
new RegistryCli("unittest", ImmutableMap.of("test_command", TestCommand.class))) {
|
new RegistryCli("unittest", ImmutableMap.of("test_command", TestCommand.class))) {
|
||||||
|
cli.uploadMetrics = false;
|
||||||
RegistryToolEnvironment.UNITTEST.setup();
|
RegistryToolEnvironment.UNITTEST.setup();
|
||||||
cli.setEnvironment(RegistryToolEnvironment.UNITTEST);
|
cli.setEnvironment(RegistryToolEnvironment.UNITTEST);
|
||||||
cli.run(new String[] {"test_command", "-x", "xval", "arg1", "arg2"});
|
cli.run(new String[] {"test_command", "-x", "xval", "arg1", "arg2"});
|
||||||
|
@ -169,7 +170,7 @@ public class ShellCommandTest {
|
||||||
public void testNonExistentCommand() {
|
public void testNonExistentCommand() {
|
||||||
try (RegistryCli cli =
|
try (RegistryCli cli =
|
||||||
new RegistryCli("unittest", ImmutableMap.of("test_command", TestCommand.class))) {
|
new RegistryCli("unittest", ImmutableMap.of("test_command", TestCommand.class))) {
|
||||||
|
cli.uploadMetrics = false;
|
||||||
cli.setEnvironment(RegistryToolEnvironment.UNITTEST);
|
cli.setEnvironment(RegistryToolEnvironment.UNITTEST);
|
||||||
assertThrows(MissingCommandException.class, () -> cli.run(new String[] {"bad_command"}));
|
assertThrows(MissingCommandException.class, () -> cli.run(new String[] {"bad_command"}));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue