Skip to content

Commit eede6a4

Browse files
committed
Initial plugin prototype
The initial version of the plugin only contains a single metric collector. At startup numberOfMetrics * numberOFProjects metrics are statically registered. It would be better to dynamically register them when sample come in. Change-Id: I529c42d5cda9602a421ecba6dc18b3449716a70c
0 parents  commit eede6a4

17 files changed

+885
-0
lines changed

.gitignore

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Compiled class file
2+
*.class
3+
4+
# Log file
5+
*.log
6+
7+
# Package Files #
8+
*.jar
9+
*.war
10+
*.nar
11+
*.ear
12+
*.zip
13+
*.tar.gz
14+
*.rar

BUILD

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
2+
load("//tools/bzl:junit.bzl", "junit_tests")
3+
load(
4+
"//tools/bzl:plugin.bzl",
5+
"PLUGIN_DEPS",
6+
"PLUGIN_TEST_DEPS",
7+
"gerrit_plugin",
8+
)
9+
10+
gerrit_plugin(
11+
name = "git-repo-metrics",
12+
srcs = glob(
13+
["src/main/java/**/*.java"],
14+
),
15+
manifest_entries = [
16+
"Gerrit-PluginName: git-repo-metrics",
17+
"Gerrit-Module: com.googlesource.gerrit.plugins.gitrepometrics.Module",
18+
"Implementation-Title: git-repo-metrics plugin",
19+
"Implementation-URL: https://review.gerrithub.io/admin/repos/GerritForge/git-repo-metrics",
20+
"Implementation-Vendor: GerritForge",
21+
],
22+
resources = glob(
23+
["src/main/resources/**/*"],
24+
)
25+
)
26+
27+
junit_tests(
28+
name = "git-repo-metrics_tests",
29+
srcs = glob(["src/test/java/**/*.java"]),
30+
resources = glob(["src/test/resources/**/*"]),
31+
tags = [
32+
"git-repo-metrics",
33+
],
34+
deps = [
35+
":git-repo-metrics__plugin_test_deps",
36+
],
37+
)
38+
39+
java_library(
40+
name = "git-repo-metrics__plugin_test_deps",
41+
testonly = 1,
42+
visibility = ["//visibility:public"],
43+
exports = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
44+
":git-repo-metrics__plugin",
45+
],
46+
)
47+
48+
java_library(
49+
name = "git-repo-metrics__plugin_deps",
50+
visibility = ["//visibility:public"],
51+
exports = PLUGIN_DEPS
52+
)

README.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Plugin to collect Git repository metrics
2+
3+
This plugin allows a systematic collection of repository metrics.
4+
Metrics are updated upon a `ref-update` receive.
5+
6+
## How to build
7+
8+
Clone or link this plugin to the plugins directory of Gerrit's source tree, and then run bazel build
9+
on the plugin's directory.
10+
11+
Example:
12+
13+
```
14+
git clone --recursive https://gerrit.googlesource.com/gerrit
15+
git clone https://gerrit.googlesource.com/plugins/git-repo-metrics
16+
pushd gerrit/plugins && ln -s ../../git-repo-metrics . && popd
17+
cd gerrit && bazel build plugins/git-repo-metrics
18+
```
19+
20+
The output plugin jar is created in:
21+
22+
```
23+
bazel-genfiles/plugins/git-repo-metrics/git-repo-metrics.jar
24+
```
25+
26+
## How to install
27+
28+
Copy the git-repo-metrics.jar into the Gerrit's /plugins directory and wait for the plugin to be automatically
29+
loaded.
30+
31+
## Configuration
32+
33+
More information about the plugin configuration can be found in the [config.md](resources/Documentation/config.md)
34+
file.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright (C) 2022 The Android Open Source Project
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.googlesource.gerrit.plugins.gitrepometrics;
16+
17+
import com.google.common.base.Supplier;
18+
import com.google.gerrit.metrics.Description;
19+
import com.google.gerrit.metrics.MetricMaker;
20+
import com.google.inject.Inject;
21+
import com.google.inject.Singleton;
22+
import com.googlesource.gerrit.plugins.gitrepometrics.collectors.GitRepoMetric;
23+
import com.googlesource.gerrit.plugins.gitrepometrics.collectors.GitStats;
24+
import java.util.ArrayList;
25+
import java.util.Collections;
26+
import java.util.HashMap;
27+
import java.util.List;
28+
import java.util.Locale;
29+
import java.util.Map;
30+
31+
@Singleton
32+
public class GitRepoMetricsCacheModule {
33+
public static List<GitRepoMetric> metricsNames = new ArrayList<>(GitStats.availableMetrics());
34+
public List<String> projects;
35+
36+
public static Map<String, Long> metrics = new HashMap<>(Collections.emptyMap());;
37+
38+
public final MetricMaker metricMaker;
39+
public final GitRepoMetricsConfig config;
40+
41+
@Inject
42+
GitRepoMetricsCacheModule(MetricMaker metricMaker, GitRepoMetricsConfig config) {
43+
this.metricMaker = metricMaker;
44+
this.config = config;
45+
this.projects = config.getRepositoryNames();
46+
}
47+
48+
public void initCache() {
49+
metricsNames.forEach(
50+
gitRepoMetric -> {
51+
projects.forEach(
52+
projectName -> {
53+
String name =
54+
GitRepoMetricsCacheModule.getMetricName(gitRepoMetric.getName(), projectName);
55+
Supplier<Long> supplier =
56+
new Supplier<Long>() {
57+
public Long get() {
58+
// TODO Blaah! Initializing all the values to zero!? Would be better
59+
// registering
60+
// dynamically the metrics
61+
// TODO add grace period!!
62+
return GitRepoMetricsCacheModule.metrics.getOrDefault(name, 0L);
63+
}
64+
};
65+
66+
metricMaker.newCallbackMetric(
67+
name,
68+
Long.class,
69+
new Description(gitRepoMetric.getDescription())
70+
.setRate()
71+
.setUnit(gitRepoMetric.getUnit()),
72+
supplier);
73+
});
74+
});
75+
}
76+
77+
public static String getMetricName(String metricName, String projectName) {
78+
return String.format("%s_%s", metricName, projectName).toLowerCase(Locale.ROOT);
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (C) 2022 The Android Open Source Project
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.googlesource.gerrit.plugins.gitrepometrics;
16+
17+
import static java.util.stream.Collectors.toList;
18+
19+
import com.google.gerrit.extensions.annotations.PluginName;
20+
import com.google.gerrit.server.config.PluginConfigFactory;
21+
import com.google.inject.Inject;
22+
import com.google.inject.Singleton;
23+
import java.util.Arrays;
24+
import java.util.List;
25+
import org.eclipse.jgit.lib.Config;
26+
27+
@Singleton
28+
public class GitRepoMetricsConfig {
29+
30+
private final Config config;
31+
32+
@Inject
33+
public GitRepoMetricsConfig(PluginConfigFactory configFactory, @PluginName String pluginName) {
34+
config = configFactory.getGlobalPluginConfig(pluginName);
35+
}
36+
37+
public List<String> getRepositoryNames() {
38+
return Arrays.stream(config.getStringList("git-repo-metrics", null, "project"))
39+
.collect(toList());
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (C) 2022 The Android Open Source Project
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.googlesource.gerrit.plugins.gitrepometrics;
16+
17+
import com.google.common.flogger.FluentLogger;
18+
import com.google.gerrit.entities.Project;
19+
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
20+
import com.google.gerrit.server.git.GitRepositoryManager;
21+
import com.google.inject.Inject;
22+
import java.io.IOException;
23+
import java.util.concurrent.ExecutorService;
24+
import org.eclipse.jgit.errors.RepositoryNotFoundException;
25+
import org.eclipse.jgit.lib.Repository;
26+
27+
public class GitRepoUpdateListener implements GitReferenceUpdatedListener {
28+
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
29+
private final GitRepositoryManager repoManager;
30+
private final ExecutorService executor;
31+
32+
@Inject
33+
GitRepoUpdateListener(
34+
GitRepositoryManager repoManager, @UpdateGitMetricsExecutor ExecutorService executor) {
35+
this.repoManager = repoManager;
36+
this.executor = executor;
37+
}
38+
39+
@Override
40+
public void onGitReferenceUpdated(Event event) {
41+
String projectName = event.getProjectName();
42+
Project.NameKey projectNameKey = Project.nameKey(projectName);
43+
logger.atFine().log("Got an update for project %s", projectName);
44+
try (Repository repository = repoManager.openRepository(projectNameKey)) {
45+
UpdateGitMetricsTask updateGitMetricsTask =
46+
new UpdateGitMetricsTask(repository, Project.builder(projectNameKey).build());
47+
executor.execute(updateGitMetricsTask);
48+
} catch (RepositoryNotFoundException e) {
49+
logger.atSevere().withCause(e).log("Cannot find repository for %s", projectName);
50+
} catch (IOException e) {
51+
logger.atSevere().withCause(e).log(
52+
"Something went wrong when reading from the repository for %s", projectName);
53+
}
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (C) 2022 The Android Open Source Project
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.googlesource.gerrit.plugins.gitrepometrics;
16+
17+
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
18+
import com.google.gerrit.extensions.registration.DynamicSet;
19+
import com.google.gerrit.lifecycle.LifecycleModule;
20+
import java.util.concurrent.ExecutorService;
21+
22+
public class Module extends LifecycleModule {
23+
24+
@Override
25+
protected void configure() {
26+
bind(ExecutorService.class)
27+
.annotatedWith(UpdateGitMetricsExecutor.class)
28+
.toProvider(UpdateGitMetricsExecutorProvider.class);
29+
bind(GitRepoUpdateListener.class);
30+
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(GitRepoUpdateListener.class);
31+
listener().to(PluginStartup.class);
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (C) 2022 The Android Open Source Project
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.googlesource.gerrit.plugins.gitrepometrics;
16+
17+
import com.google.gerrit.extensions.events.LifecycleListener;
18+
import com.google.gerrit.metrics.MetricMaker;
19+
import com.google.inject.Inject;
20+
21+
public class PluginStartup implements LifecycleListener {
22+
public final MetricMaker metricMaker;
23+
public final GitRepoMetricsConfig config;
24+
25+
@Inject
26+
public PluginStartup(MetricMaker metricMaker, GitRepoMetricsConfig config) {
27+
this.metricMaker = metricMaker;
28+
this.config = config;
29+
}
30+
31+
@Override
32+
public void start() {
33+
new GitRepoMetricsCacheModule(metricMaker, config).initCache();
34+
}
35+
36+
@Override
37+
public void stop() {}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (C) 2022 The Android Open Source Project
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.googlesource.gerrit.plugins.gitrepometrics;
16+
17+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
18+
19+
import com.google.inject.BindingAnnotation;
20+
import java.lang.annotation.Retention;
21+
22+
@Retention(RUNTIME)
23+
@BindingAnnotation
24+
@interface UpdateGitMetricsExecutor {}

0 commit comments

Comments
 (0)