Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/available_metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ All measurements share the following metrics:
| Metric | Type | Description | Introduced in |
| --- | --- | --- | --- |
| build_number | integer | Build number | |
| instance | string | Jenkins instance url | 3.4 |
| project_name | string | Build name | |
| project_namespace | string | Root folder of the project | 3.4 |
| project_path | string | Build path | 1.15 |

All measurements share the following tags:
Expand All @@ -28,6 +30,7 @@ All measurements share the following tags:
| --- | --- | --- | --- |
| build_agent_name | string | Name of the executor node | 1.15 |
| build_branch_name | string | Branch name of multibranch pipeline | 2.4 |
| build_cause | string | Trigger type | 3.4 |
| build_causer | string | Short description of build causer| 2.4 |
| build_exec_time | integer | Start time of the build | 1.17 |
| build_measured_time | integer | Time when InfluxDB plugin is called | 1.17 |
Expand All @@ -37,6 +40,7 @@ All measurements share the following tags:
| build_status_message | string | Status message (stable, back to normal, broken since #50, etc.) | |
| build_successful | boolean | Boolean whether build succeeded | 1.10 |
| build_time | integer | Build execution time | |
| build_user | string | User who launch the build | 3.4 |
| last_stable_build | integer | Build number of the last stable build (0 if never) | 1.10 |
| last_successful_build | integer | Build number of the last successful build (0 if never) | 1.10 |
| project_build_health | integer | Health score from build | |
Expand Down
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ THE SOFTWARE.
<slf4j.version>1.7.30</slf4j.version>
<spotbugs-annotations.version>4.2.0</spotbugs-annotations.version>
<spotbugs.failOnError>false</spotbugs.failOnError>
<sshd.version>3.0</sshd.version>
<structs.version>308.v852b473a2b8c</structs.version>
<symbol-annotation.version>1.10</symbol-annotation.version>
<workflow-api.version>2.40</workflow-api.version>
Expand Down Expand Up @@ -233,6 +234,12 @@ THE SOFTWARE.
<artifactId>workflow-step-api</artifactId>
<version>${workflow-step-api.version}</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.modules</groupId>
<artifactId>sshd</artifactId>
<version>${sshd.version}</version>
<scope>test</scope>
</dependency>

<!-- Optional dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@

public abstract class AbstractPointGenerator implements PointGenerator {

public static final String PROJECT_NAMESPACE = "project_namespace";
public static final String PROJECT_NAME = "project_name";
public static final String PROJECT_PATH = "project_path";
public static final String INSTANCE = "instance";
public static final String BUILD_NUMBER = "build_number";
public static final String CUSTOM_PREFIX = "prefix";

Expand All @@ -41,8 +43,9 @@ public AbstractPointGenerator(Run<?, ?> build, TaskListener listener, ProjectNam

@Override
public Point buildPoint(String name, String customPrefix, Run<?, ?> build, long timestamp) {
Jenkins instance = Jenkins.getInstanceOrNull();
String projectName = projectNameRenderer.render(build);
String projectPath = build.getParent().getRelativeNameFrom(Jenkins.getInstanceOrNull());
String projectPath = build.getParent().getRelativeNameFrom(instance);

Point point = Point
.measurement(name)
Expand All @@ -57,6 +60,8 @@ public Point buildPoint(String name, String customPrefix, Run<?, ?> build, long

point.addTag(PROJECT_NAME, projectName);
point.addTag(PROJECT_PATH, projectPath);
point.addTag(INSTANCE, instance != null ? instance.getRootUrl() : "");
point.addTag(PROJECT_NAMESPACE, projectPath.split("/")[0]);


if (StringUtils.isNotBlank(jenkinsEnvParameterTag)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.Cause.UserIdCause;
import hudson.tasks.test.AbstractTestResultAction;
import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JenkinsBasePointGenerator extends AbstractPointGenerator {

Expand All @@ -37,6 +43,8 @@ public class JenkinsBasePointGenerator extends AbstractPointGenerator {
public static final String BUILD_AGENT_NAME = "build_agent_name";
public static final String BUILD_BRANCH_NAME = "build_branch_name";
public static final String BUILD_CAUSER = "build_causer";
public static final String BUILD_USER = "build_user";
public static final String BUILD_CAUSE = "build_cause";

public static final String PROJECT_BUILD_HEALTH = "project_build_health";
public static final String PROJECT_LAST_SUCCESSFUL = "last_successful_build";
Expand All @@ -45,6 +53,8 @@ public class JenkinsBasePointGenerator extends AbstractPointGenerator {
public static final String TESTS_SKIPPED = "tests_skipped";
public static final String TESTS_TOTAL = "tests_total";

public static final String AGENT_LOG_PATTERN = "Running on ";

private final Run<?, ?> build;
private final String customPrefix;
private final String jenkinsEnvParameterField;
Expand Down Expand Up @@ -87,6 +97,8 @@ public Point[] generate() {
ordinal = buildResult.ordinal;
}

String[] buildCause = getCauseDatas();

Point point = buildPoint(measurementName, customPrefix, build);

point.addField(BUILD_TIME, build.getDuration() == 0 ? dt : build.getDuration())
Expand All @@ -97,12 +109,14 @@ public Point[] generate() {
.addField(BUILD_RESULT, result)
.addField(BUILD_RESULT_ORDINAL, ordinal)
.addField(BUILD_IS_SUCCESSFUL, ordinal < 2)
.addField(BUILD_AGENT_NAME, getBuildEnv("NODE_NAME"))
.addField(BUILD_AGENT_NAME, getNodeName())
.addField(BUILD_BRANCH_NAME, getBuildEnv("BRANCH_NAME"))
.addField(PROJECT_BUILD_HEALTH, build.getParent().getBuildHealth().getScore())
.addField(PROJECT_LAST_SUCCESSFUL, getLastSuccessfulBuild())
.addField(PROJECT_LAST_STABLE, getLastStableBuild())
.addField(BUILD_CAUSER , getCauseShortDescription())
.addField(BUILD_USER, buildCause[0])
.addField(BUILD_CAUSE, buildCause[1])
.addTag(BUILD_RESULT, result);

if (hasTestResults(build)) {
Expand Down Expand Up @@ -151,6 +165,24 @@ private String getCauseShortDescription() {
}
}

private String[] getCauseDatas() {
String userCause = "";
StringJoiner triggers = new StringJoiner(", ");
try {
for (Cause cause : build.getCauses()) {
triggers.add(cause.getClass().getSimpleName());
if (cause instanceof UserIdCause) {
userCause = ((UserIdCause) cause).getUserId();
} else if (cause.getClass().getName().contains("GitlabWebhookCause")) {
userCause = build.getEnvironment(listener).get("gitlabUserUsername");
}
}
return new String[] { userCause != null ? userCause : "", triggers.toString() };
} catch (IOException | InterruptedException e) {
return new String[] { "", "" };
}
}

private int getLastSuccessfulBuild() {
Run<?, ?> lastSuccessfulBuild = build.getParent().getLastSuccessfulBuild();
return lastSuccessfulBuild != null ? lastSuccessfulBuild.getNumber() : 0;
Expand All @@ -160,4 +192,33 @@ private int getLastStableBuild() {
Run<?, ?> lastStableBuild = build.getParent().getLastStableBuild();
return lastStableBuild != null ? lastStableBuild.getNumber() : 0;
}

private String getNodeName() {
String nodeName = getBuildEnv("NODE_NAME");
if(StringUtils.isEmpty(nodeName)) {
nodeName = getNodeNameFromLogs();
}
return nodeName;
}

/**
* Retrieve agent name in the log of the build
*
* @return agent name
*/
private String getNodeNameFromLogs() {
String agentName = "";
try (BufferedReader br = new BufferedReader(build.getLogReader())) {
String line;
String[] splitLine;
while ((line = br.readLine()) != null) {
if (line.startsWith(AGENT_LOG_PATTERN)) {
splitLine = line.split(" ");
agentName = splitLine.length >= 3 ? splitLine[2] : "";
break;
}
}
} catch (IOException e) {}
return agentName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void generate() {
lineProtocol1 = pointsToWrite[1].toLineProtocol();
lineProtocol2 = pointsToWrite[0].toLineProtocol();
}
assertTrue(lineProtocol1.startsWith("series1,build_result=SUCCESS,prefix=test_prefix,project_name=test_prefix_master,project_path=folder/master build_number=11i,project_name=\"test_prefix_master\",project_path=\"folder/master\",test1=11i,test2=22i"));
assertTrue(lineProtocol2.startsWith("series2,prefix=test_prefix,project_name=test_prefix_master,project_path=folder/master build_number=11i,project_name=\"test_prefix_master\",project_path=\"folder/master\",test3=33i,test4=44i"));
assertTrue(lineProtocol1.startsWith("series1,build_result=SUCCESS,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master build_number=11i,project_name=\"test_prefix_master\",project_path=\"folder/master\",test1=11i,test2=22i"));
assertTrue(lineProtocol2.startsWith("series2,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master build_number=11i,project_name=\"test_prefix_master\",project_path=\"folder/master\",test3=33i,test4=44i"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public void generate() {
Point[] pointsToWrite = cdGen.generate();

String lineProtocol = pointsToWrite[0].toLineProtocol();
assertTrue(lineProtocol.startsWith("jenkins_custom_data,prefix=test_prefix,project_name=test_prefix_master,project_path=folder/master,tag1=myTag build_number=11i,build_time="));
assertTrue(lineProtocol.contains("project_name=\"test_prefix_master\",project_path=\"folder/master\",test1=11i,test2=22i"));
assertTrue(lineProtocol.startsWith("jenkins_custom_data,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master,tag1=myTag build_number=11i,build_time="));
assertTrue(lineProtocol.contains("jenkins_custom_data,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master,tag1=myTag build_number=11i"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

import static org.junit.Assert.*;

import java.io.Reader;
import java.io.StringReader;

/**
* @author Damien Coraboeuf <[email protected]>
*/
Expand Down Expand Up @@ -68,6 +71,7 @@ public void before() throws Exception {
Mockito.when(mockedEnvVars.get(JENKINS_ENV_VALUE_TAG)).thenReturn(JENKINS_ENV_RESOLVED_VALUE_TAG);
Mockito.when(mockedEnvVars.get("NODE_NAME")).thenReturn(null);
Mockito.when(mockedEnvVars.get("BRANCH_NAME")).thenReturn(null);
Mockito.when(build.getLogReader()).thenReturn(new StringReader(""));

currTime = System.currentTimeMillis();
}
Expand All @@ -84,6 +88,19 @@ public void agent_present() {
assertTrue(lineProtocol.contains("project_path=\"folder/master\""));
}

public void agent_present_in_log() throws Exception {
Mockito.when(build.getExecutor()).thenReturn(executor);
Mockito.when(mockedEnvVars.get("NODE_NAME")).thenReturn("");
Reader reader = new StringReader(JenkinsBasePointGenerator.AGENT_LOG_PATTERN + "slave-1");
Mockito.when(build.getLogReader()).thenReturn(reader);
JenkinsBasePointGenerator generator = new JenkinsBasePointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, StringUtils.EMPTY, CUSTOM_PREFIX, MEASUREMENT_NAME, mockedEnvVars);
Point[] points = generator.generate();
String lineProtocol = points[0].toLineProtocol();

assertTrue(lineProtocol.contains("build_agent_name=\"slave-1\""));
assertTrue(lineProtocol.contains("project_path=\"folder/master\""));
}

@Test
public void agent_not_present() {
Mockito.when(build.getExecutor()).thenReturn(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import hudson.model.Run;
import hudson.model.TaskListener;
import jenkins.metrics.impl.TimeInQueueAction;
import jenkins.model.Jenkins;
import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
import org.apache.commons.lang.StringUtils;
import org.junit.Before;
Expand Down Expand Up @@ -38,6 +39,7 @@ public void before() {
Mockito.when(build.getParent()).thenReturn(job);
Mockito.when(job.getName()).thenReturn(JOB_NAME);
Mockito.when(build.getAction(TimeInQueueAction.class)).thenReturn(timeInQueueAction);
Mockito.when(job.getRelativeNameFrom(Mockito.nullable(Jenkins.class))).thenReturn("folder/" + JOB_NAME);

currTime = System.currentTimeMillis();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ public void generate() {
PerfPublisherPointGenerator generator = new PerfPublisherPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, CUSTOM_PREFIX);
Point[] points = generator.generate();

assertTrue(points[0].toLineProtocol().startsWith("perfpublisher_summary,prefix=test_prefix,project_name=test_prefix_master,project_path=folder/master build_number=11i,number_of_executed_tests=1i"));
assertTrue(points[1].toLineProtocol().startsWith("perfpublisher_metric,prefix=test_prefix,project_name=test_prefix_master,project_path=folder/master average=50.0,best=50.0,build_number=11i,metric_name=\"metric1\",project_name=\"test_prefix_master\",project_path=\"folder/master\",worst=50.0"));
assertTrue(points[2].toLineProtocol().startsWith("perfpublisher_test,prefix=test_prefix,project_name=test_prefix_master,project_path=folder/master,test_name=test.txt build_number=11i,executed=true,project_name=\"test_prefix_master\",project_path=\"folder/master\",successful=false,test_name=\"test.txt\""));
assertTrue(points[3].toLineProtocol().startsWith("perfpublisher_test_metric,prefix=test_prefix,project_name=test_prefix_master,project_path=folder/master,test_name=test.txt build_number=11i,metric_name=\"metric1\",project_name=\"test_prefix_master\",project_path=\"folder/master\",relevant=true,test_name=\"test.txt\",unit=\"ms\",value=50.0"));
assertTrue(points[0].toLineProtocol().startsWith("perfpublisher_summary,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master build_number=11i,number_of_executed_tests=1i"));
assertTrue(points[1].toLineProtocol().startsWith("perfpublisher_metric,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master average=50.0,best=50.0,build_number=11i,metric_name=\"metric1\",project_name=\"test_prefix_master\",project_path=\"folder/master\",worst=50.0"));
assertTrue(points[2].toLineProtocol().startsWith("perfpublisher_test,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master,test_name=test.txt build_number=11i,executed=true,project_name=\"test_prefix_master\",project_path=\"folder/master\",successful=false,test_name=\"test.txt\""));
assertTrue(points[3].toLineProtocol().startsWith("perfpublisher_test_metric,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master,test_name=test.txt build_number=11i,metric_name=\"metric1\",project_name=\"test_prefix_master\",project_path=\"folder/master\",relevant=true,test_name=\"test.txt\",unit=\"ms\",value=50.0"));
}
}