To do that you need to write a regex which then extracts the number from the log. However, Sonarqube doesn’t log it, so I can’t use this feature, though it would be very handy.
I use Sonarqube to combine two coverage reports, otherwise I could have just taken the coverage directly from the original test run.
Is there a way to get the coverage in % in the logs?
I’m using SonarQube Server Enterprise Edition v2025.1.1.
SonarQube processes the coverage files during analysis, but doesn’t compute coverage until the background task processes server-side. So what you’re asking for, unfortunately isn’t possible.
Thank you for your answer. We set the flag (sonar.qualitygate.wait=true) to wait for the quality gate , so at this point there should be the total coverage available, as it’s needed for the QualityGate. Is it possible to log the QualityGate metrics (like “86.78% is more than 80%: Passed”)? Or to easily get the coverage through an API call?
There’s a couple APIs you could call – the one that comes to mind (and makes sure you can reference a specific analysis ID) is GET api/qualitygates/project_status. You can get the analysisID by parsing the .scannerwork/report-task.txt file.
This will make sure you can see all the measures in your Quality Gate. If you want to query a measure outside your Quality Gate, GET api/measures/component is another option. And, this will always return the latest measure. It’s possible that many analyses processing at once could lead to the wrong measure being returned.
I now used the GET api/measures/component endpoint as the risk of multiple analyses on the same branch isn’t that high, also it wouldn’t change the result.
Here’s my Node.JS script for anyone interested:
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';
// Read sonar-project.properties and extract sonar.projectKey
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const propsPath = path.resolve(__dirname, '../sonar-project.properties');
const propsContent = fs.readFileSync(propsPath, 'utf-8');
const projectKeyMatch = propsContent.match(/^sonar\.projectKey\s*=\s*(.+)$/m);
if (!projectKeyMatch) {
console.error('sonar.projectKey not found in sonar-project.properties');
process.exit(1);
}
const projectKey = projectKeyMatch[1].trim();
// Get current git branch
let branch = process.env.CI_COMMIT_BRANCH;
if (!branch) {
try {
branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
} catch (e) {
console.error('Failed to get current git branch');
process.exit(1);
}
}
// Get token from env
const token = process.env.SONARQUBE_TOKEN;
if (!token) {
console.error('SONARQUBE_TOKEN env variable not set');
process.exit(1);
}
// Get SonarQube host from env
const host = process.env.SONARQUBE_HOST;
if (!host) {
console.error('SONARQUBE_HOST env variable not set');
process.exit(1);
}
// Fetch coverage
(async () => {
try {
const url = new URL('/api/measures/component', host);
url.searchParams.set('component', projectKey);
url.searchParams.set('metricKeys', 'coverage');
url.searchParams.set('branch', branch);
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${token}`
}
});
if (!res.ok) {
const errText = await res.text();
throw new Error(`HTTP ${res.status}: ${errText}`);
}
const data = await res.json();
const coverage = data?.component?.measures?.find(m => m.metric === 'coverage')?.value;
if (coverage !== undefined) {
console.log(`Coverage received from SonarQube: ${coverage}%`);
} else {
console.error('Coverage metric not found in response');
process.exit(1);
}
} catch (err) {
console.error('Failed to fetch SonarQube coverage:', err.message);
process.exit(1);
}
})();
Extract the coverage from the log (.gitlab-ci.yml):
coverage: '/Coverage received from SonarQube: ([\d.]+)%/'
It works, however it would be nice to have an official intergration for Gitlab, which would be easier to use and could also display the Code Quality directly in Gitlab.