Skip to content

Commit

Permalink
Merge pull request lsjostro#2 from markyjackson-taulia/documentation
Browse files Browse the repository at this point in the history
Documentation
  • Loading branch information
Marky Jackson authored Jan 20, 2019
2 parents bd34bb9 + ff8c208 commit b708b60
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 51 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Jenkins Prometheus Metrics Plugin

[![Join the chat at https://gitter.im/jenkinsci/prometheus-plugin](https://badges.gitter.im/jenkinsci/prometheus-plugin.svg)](https://gitter.im/jenkinsci/prometheus-plugin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

## About
Jenkins Prometheus Plugin expose an endpoint (default `/prometheus`) with metrics where a Prometheus Server can scrape.

Documentation can be found [here](https://plugins.jenkins.io/prometheus)

Please note that the documentation is a WIP.

## Metrics exposed
Currently only metrics from the [Metrics-plugin](https://github.com/jenkinsci/metrics-plugin) and summary of build
duration of jobs and pipeline stages
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</parent>

<artifactId>prometheus</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.4-SNAPSHOT</version>
<packaging>hpi</packaging>
<name>Prometheus metrics plugin</name>
<description>Expose Jenkins metrics in prometheus format</description>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.jenkinsci.plugins.prometheus;

import hudson.model.Computer;
import io.prometheus.client.Collector;
import io.prometheus.client.Gauge;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.prometheus.util.ConfigurationUtils;

import java.util.ArrayList;
import java.util.List;

public class JenkinsStatusCollector extends Collector {

@Override
public List<MetricFamilySamples> collect() {
final String subsystem = "jenkins";
final String namespace = ConfigurationUtils.getNamespace();
final List<MetricFamilySamples> samples = new ArrayList<>();
Gauge jenkinsUp = Gauge.build().
name("up").
labelNames().
subsystem(subsystem).
namespace(namespace).
help("Is Jenkins ready to receive requests").
create();
Jenkins jenkins = Jenkins.getInstance();
if (jenkins == null) {
return null;
}
jenkinsUp.set(jenkins.getInitLevel() == hudson.init.InitMilestone.COMPLETED ?
1 : 0);
samples.addAll(jenkinsUp.collect());
Gauge jenkinsUptime = Gauge.build().
name("uptime").
labelNames().
subsystem(subsystem).
namespace(namespace).
help("Time since Jenkins machine was initialized").
create();
Computer computer = jenkins.toComputer();
if (computer != null) {
long upTime = computer.getConnectTime();
jenkinsUptime.set(System.currentTimeMillis() - upTime);
samples.addAll(jenkinsUptime.collect());
}
return samples;
}
}
121 changes: 71 additions & 50 deletions src/main/java/org/jenkinsci/plugins/prometheus/JobCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import java.util.ArrayList;
import java.util.List;

import io.prometheus.client.Counter;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.prometheus.util.Callback;
import org.jenkinsci.plugins.prometheus.util.ConfigurationUtils;
import org.jenkinsci.plugins.prometheus.util.FlowNodes;
import org.jenkinsci.plugins.prometheus.util.Jobs;
import org.jenkinsci.plugins.prometheus.util.Runs;
Expand All @@ -24,11 +26,12 @@
import io.prometheus.client.Gauge;
import org.jenkinsci.plugins.prometheus.config.PrometheusConfiguration;

public class JobCollector extends Collector {
public class JobCollector extends Collector {
private static final Logger logger = LoggerFactory.getLogger(JobCollector.class);

private String lastNamespace;
private Summary summary;
private Counter jobSuccessCount;
private Counter jobFailedCount;
private Gauge jobBuildResultOrdinal;
private Gauge jobBuildResult;
private Gauge jobStartMillis;
Expand All @@ -46,21 +49,25 @@ public JobCollector() {
public List<MetricFamilySamples> collect() {
logger.debug("Collecting metrics for prometheus");

final String namespace = getNamespace();
final String namespace = ConfigurationUtils.getNamespace();
final List<MetricFamilySamples> samples = new ArrayList<>();
final List<Job> jobs = new ArrayList<>();
final String fullname = "builds";
final String subsystem = "jenkins";
final String jobAttribute = PrometheusConfiguration.get().getJobAttributeName();
String[] labelNameArray = {jobAttribute};
String[] labelStageNameArray = {jobAttribute,"stage"};
String[] labelNameArray = {jobAttribute, "repo"};
String[] labelStageNameArray = {jobAttribute, "repo", "stage"};
final boolean ignoreDisabledJobs = PrometheusConfiguration.get().isProcessingDisabledBuilds();
final boolean ignoreBuildMetrics =
!PrometheusConfiguration.get().isCountAbortedBuilds() &&
!PrometheusConfiguration.get().isCountFailedBuilds() &&
!PrometheusConfiguration.get().isCountNotBuiltBuilds() &&
!PrometheusConfiguration.get().isCountSuccessfulBuilds() &&
!PrometheusConfiguration.get().isCountUnstableBuilds();
!PrometheusConfiguration.get().isCountAbortedBuilds() &&
!PrometheusConfiguration.get().isCountFailedBuilds() &&
!PrometheusConfiguration.get().isCountNotBuiltBuilds() &&
!PrometheusConfiguration.get().isCountSuccessfulBuilds() &&
!PrometheusConfiguration.get().isCountUnstableBuilds();

if (ignoreBuildMetrics) {
return samples;
}

logger.debug("getting summary of build times in milliseconds by Job");
summary = Summary.build().
Expand All @@ -69,7 +76,18 @@ public List<MetricFamilySamples> collect() {
labelNames(labelNameArray).
help("Summary of Jenkins build times in milliseconds by Job").
create();

jobSuccessCount = Counter.build().
name(fullname + "_success_build_count").
subsystem(subsystem).namespace(namespace).
labelNames(labelNameArray).
help("Successful build count").
create();
jobFailedCount = Counter.build().
name(fullname + "_failed_build_count").
subsystem(subsystem).namespace(namespace).
labelNames(labelNameArray).
help("Failed build count").
create();
jobBuildResultOrdinal = Gauge.build().
name(fullname + "_last_build_result_ordinal").
subsystem(subsystem).namespace(namespace).
Expand Down Expand Up @@ -155,39 +173,50 @@ public void invoke(Job job) {
appendJobMetrics(job, ignoreBuildMetrics);
}
});
if (summary.collect().get(0).samples.size() > 0){
if (summary.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from summary", summary.collect().get(0).samples.size());
samples.addAll(summary.collect());
}
if (jobBuildResultOrdinal.collect().get(0).samples.size() > 0){
if (jobSuccessCount.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from counter", jobSuccessCount.collect().get(0).samples.size());
samples.addAll(jobSuccessCount.collect());
}
if (jobFailedCount.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from counter", jobFailedCount.collect().get(0).samples.size());
samples.addAll(jobFailedCount.collect());
}
if (jobBuildResultOrdinal.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from summary", jobBuildResultOrdinal.collect().get(0).samples.size());
samples.addAll(jobBuildResultOrdinal.collect());
}
if (jobBuildResult.collect().get(0).samples.size() > 0){
if (jobBuildResult.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from summary", jobBuildResult.collect().get(0).samples.size());
samples.addAll(jobBuildResult.collect());
}
if (jobDuration.collect().get(0).samples.size() > 0){
if (jobDuration.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from summary", jobDuration.collect().get(0).samples.size());
samples.addAll(jobDuration.collect());
}

if (jobTestsTotal.collect().get(0).samples.size() > 0){
if (jobStartMillis.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from summary", jobStartMillis.collect().get(0).samples.size());
samples.addAll(jobStartMillis.collect());
}
if (jobTestsTotal.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from stage summary", jobTestsTotal.collect().get(0).samples.size());
samples.addAll(jobTestsTotal.collect());
}

if (jobTestsSkipped.collect().get(0).samples.size() > 0){
if (jobTestsSkipped.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from stage summary", jobTestsSkipped.collect().get(0).samples.size());
samples.addAll(jobTestsSkipped.collect());
}

if (jobTestsFailing.collect().get(0).samples.size() > 0){
if (jobTestsFailing.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from stage summary", jobTestsFailing.collect().get(0).samples.size());
samples.addAll(jobTestsFailing.collect());
}

if (stageSummary.collect().get(0).samples.size() > 0){
if (stageSummary.collect().get(0).samples.size() > 0) {
logger.debug("Adding [{}] samples from stage summary", stageSummary.collect().get(0).samples.size());
samples.addAll(stageSummary.collect());
}
Expand All @@ -196,12 +225,12 @@ public void invoke(Job job) {
}

protected void appendJobMetrics(Job job, Boolean ignoreBuildMetrics) {
// Add this to the repo as well so I can group by Github Repository
// Add this to the repo as well so I can group by Github Repository
String repoName = StringUtils.substringBetween(job.getFullName(), "/");
if (repoName == null) {
repoName="NA";
repoName = "NA";
}
String[] labelValueArray = {job.getFullName(),};
String[] labelValueArray = {job.getFullName(), repoName};

Run run = job.getLastBuild();
// Never built
Expand All @@ -212,7 +241,7 @@ protected void appendJobMetrics(Job job, Boolean ignoreBuildMetrics) {

/*
* _last_build_result _last_build_result_ordinal
*
*
* SUCCESS 0 true - The build had no errors.
* UNSTABLE 1 true - The build had some errors but they were not fatal. For example, some tests failed.
* FAILURE 2 false - The build had a fatal error.
Expand All @@ -235,7 +264,7 @@ protected void appendJobMetrics(Job job, Boolean ignoreBuildMetrics) {
jobDuration.labels(labelValueArray).set(duration);
jobScore.labels(labelValueArray).set(score);

if(PrometheusConfiguration.get().isFetchTestResults() && hasTestResults(run)) {
if (PrometheusConfiguration.get().isFetchTestResults() && hasTestResults(run)) {
int testsTotal = run.getAction(AbstractTestResultAction.class).getTotalCount();
int testsFail = run.getAction(AbstractTestResultAction.class).getFailCount();
int testsSkipped = run.getAction(AbstractTestResultAction.class).getSkipCount();
Expand All @@ -245,18 +274,21 @@ protected void appendJobMetrics(Job job, Boolean ignoreBuildMetrics) {
jobTestsFailing.labels(labelValueArray).set(testsFail);
}

if(ignoreBuildMetrics) {
return;
}

while (run != null) {
logger.debug("getting metrics for run [{}] from job [{}]", run.getNumber(), job.getName());
if (Runs.includeBuildInMetrics(run)) {
logger.debug("getting build duration for run [{}] from job [{}]", run.getNumber(), job.getName());
long buildDuration = run.getDuration();
logger.debug("duration is [{}] for run [{}] from job [{}]", buildDuration, run.getNumber(), job.getName());
summary.labels(labelValueArray).observe(buildDuration);

Result result = run.getResult();
if (result != null) {
if (result.ordinal == 0 || result.ordinal == 1) {
jobSuccessCount.labels(labelValueArray).inc();
} else if (result.ordinal > 1) {
jobFailedCount.labels(labelValueArray).inc();
}
}
if (run instanceof WorkflowRun) {
logger.debug("run [{}] from job [{}] is of type workflowRun", run.getNumber(), job.getName());
WorkflowRun workflowRun = (WorkflowRun) run;
Expand All @@ -270,17 +302,24 @@ protected void appendJobMetrics(Job job, Boolean ignoreBuildMetrics) {
for (FlowNode stage : stages) {
observeStage(job, run, stage);
}
} catch (final NullPointerException e){}
} catch (final NullPointerException e) {
}
}
}
run = run.getPreviousBuild();
}
}

private void observeStage(Job job, Run run, FlowNode stage) {
logger.debug("Observing stage[{}] in run [{}] from job [{}]", stage.getDisplayName(), run.getNumber(), job.getName());
// Add this to the repo as well so I can group by Github Repository
String repoName = StringUtils.substringBetween(job.getFullName(), "/");
if (repoName == null) {
repoName = "NA";
}
String jobName = job.getFullName();
String stageName = stage.getDisplayName();
String[] labelValueArray = {jobName, stageName};
String[] labelValueArray = {jobName, repoName, stageName};

logger.debug("getting duration for stage[{}] in run [{}] from job [{}]", stage.getDisplayName(), run.getNumber(), job.getName());
long duration = FlowNodes.getStageDuration(stage);
Expand All @@ -291,22 +330,4 @@ private void observeStage(Job job, Run run, FlowNode stage) {
private boolean hasTestResults(Run<?, ?> job) {
return job.getAction(AbstractTestResultAction.class) != null;
}

private String getNamespace() {
// get the namespace from the environment first
String namespace = System.getenv("PROMETHEUS_NAMESPACE");
if (StringUtils.isEmpty(namespace)) {
// when the environment variable isn't set, try the system configuration
namespace = PrometheusConfiguration.get().getDefaultNamespace();
if (!namespace.equals(lastNamespace)) {
logger.debug("Since the environment variable 'PROMETHEUS_NAMESPACE' is empty, using the value [{}] from the master configuration (empty strings are allowed)"+namespace);
}
}
if (!namespace.equals(lastNamespace)) {
logger.info("The prometheus namespace is now [{}]", namespace);
}
lastNamespace = namespace;
return namespace;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import jenkins.metrics.api.Metrics;
import jenkins.model.Jenkins;
import org.acegisecurity.Authentication;
import org.jenkinsci.plugins.prometheus.JenkinsStatusCollector;
import org.jenkinsci.plugins.prometheus.JobCollector;
import org.jenkinsci.plugins.prometheus.MetricsRequest;
import org.jenkinsci.plugins.prometheus.config.PrometheusConfiguration;
Expand All @@ -20,6 +21,7 @@
public class PrometheusAction implements UnprotectedRootAction {
private CollectorRegistry collectorRegistry;
private JobCollector jobCollector = new JobCollector();
private JenkinsStatusCollector jenkinsStatusCollector = new JenkinsStatusCollector();

@Override
public String getIconFileName() {
Expand All @@ -42,6 +44,7 @@ public Object doDynamic(StaplerRequest request) {
if (collectorRegistry == null) {
collectorRegistry = CollectorRegistry.defaultRegistry;
collectorRegistry.register(jobCollector);
collectorRegistry.register(jenkinsStatusCollector);
if (Metrics.metricRegistry() != null) {
collectorRegistry.register(new DropwizardExports(Metrics.metricRegistry()));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.jenkinsci.plugins.prometheus.util;

import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.prometheus.config.PrometheusConfiguration;

public class ConfigurationUtils {
public static String getNamespace() {
// get the namespace from the environment first
String namespace = System.getenv("PROMETHEUS_NAMESPACE");
if (StringUtils.isEmpty(namespace)) {
// when the environment variable isn't set, try the system configuration
namespace = PrometheusConfiguration.get().getDefaultNamespace();
}
return namespace;
}
}

0 comments on commit b708b60

Please sign in to comment.