Chapter 27Lesson 02~180 minutes

Server Logs, Monitoring, Health, Elasticsearch, and Operational Diagnostics: Guided Hands-On Workflow

Build a timestamped Community Build baseline, trace one successful analysis end to end, create a safe server-side task failure in a disposable project topology, preserve first-failure evidence, and recover at the owning layer.

OperationsLogsCompute EngineMonitoringElasticsearch

Learning objectives

  • Create a disposable Community Build 26.9 operational baseline with timestamped system, resource, log, and queue evidence.
  • Trace one successful analysis from scanner revision to ceTaskId, CE completion, search/UI visibility, and gate evidence.
  • Create a safe project/component ownership conflict that can produce a failed CE task without corrupting database or Elasticsearch state.
  • Preserve the first failed task, scanner context, CE log, health, and project topology before applying a correction.
  • Recover by changing only the conflicting disposable project topology, then rerun the same source revision.
  • Explain the fallback when an exact scanner version detects the conflict before upload rather than in CE.

1. Disposable lab contract

Local lab only. This exercise intentionally creates a component/project-key ownership conflict. Use only the two synthetic Maven projects named below on an isolated SonarQube instance. Never delete or re-key production projects to imitate the failure.
Item Assumption
SonarQube Community Build 26.9.0.129388, preferably exact Docker image sonarqube:26.9.0.129388-community
Scanner CLI 8.1.0.6389 for the simple successful baseline project
Maven scanner SonarScanner for Maven 5.7.0.6970 for the component-key conflict experiment
Java/Maven JDK 21 and Maven 3.9.x for the tiny synthetic reactor
Database Disposable evaluation DB is acceptable for this operations lab; production must use a supported external DB
Credentials Disposable project-analysis tokens; one local admin/operator identity only for project creation/deletion and system-health evidence

2. Establish a timestamped operational baseline

export SONAR_HOST_URL="http://localhost:9000"
export LAB_TS="$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "evidence/$LAB_TS"/{system,logs,success,conflict,recovery}

date -u +%FT%TZ | tee "evidence/$LAB_TS/system/timestamp.txt"
df -h | tee "evidence/$LAB_TS/system/disk.txt"
free -h 2>/dev/null | tee "evidence/$LAB_TS/system/memory.txt" || true

curl -fsS "$SONAR_HOST_URL/api/system/status" \
  | tee "evidence/$LAB_TS/system/status.json"

# Requires system passcode or an Administer System identity.
curl -fsS -H "X-Sonar-Passcode: $SONAR_SYSTEM_PASSCODE" \
  "$SONAR_HOST_URL/api/system/health" \
  | tee "evidence/$LAB_TS/system/health.json"

# Container deployment: capture without deleting/rotating logs.
docker cp sonarqube:/opt/sonarqube/logs/. "evidence/$LAB_TS/logs/"

Record the log time zone and container/host time. The baseline should say whether health is GREEN/YELLOW/RED, how much disk/RAM is free, and whether the server already had pending/failed tasks. Do not induce a failure on top of an unexplained unhealthy baseline.

3. Trace one normal analysis end to end

Create a tiny standalone project sq-ch27-success, create a project-analysis token, and scan one source file. Keep the token in the environment, not the command line.

mkdir -p sq-ch27-success/src
cd sq-ch27-success
cat > src/math.py <<'PY'
def total(values):
    return sum(values)
PY
cat > sonar-project.properties <<'EOF'
sonar.projectKey=sq-ch27-success
sonar.projectName=Chapter 27 Successful Task
sonar.sources=src
sonar.sourceEncoding=UTF-8
EOF

git init
git config user.email "learner@example.invalid"
git config user.name "SQ Learner"
git add . && git commit -m "chapter27 success baseline"
git rev-parse HEAD | tee "../evidence/$LAB_TS/success/revision.txt"

export SONAR_TOKEN="$SUCCESS_PROJECT_TOKEN"
sonar-scanner -X 2>&1 | tee "../evidence/$LAB_TS/success/scanner.log"
cp .scannerwork/report-task.txt "../evidence/$LAB_TS/success/report-task.txt"
CE_TASK_ID="$(sed -n 's/^ceTaskId=//p' .scannerwork/report-task.txt)"
printf '%s\n' "$CE_TASK_ID" | tee "../evidence/$LAB_TS/success/ceTaskId.txt"

curl -fsS -H "Authorization: Bearer $SONAR_API_TOKEN" \
  "$SONAR_HOST_URL/api/ce/task?id=$CE_TASK_ID" \
  | tee "../evidence/$LAB_TS/success/ce-task.json"

Poll deliberately until the task is terminal; do not infer completion from scanner exit 0. After SUCCESS, capture project measures/gate and the matching ce.log lines by task ID. This becomes the known-good control for the failure experiment.

4. Safe failed-task experiment: a project/component ownership clash

Sonar’s current background-task documentation explicitly lists a clash between an existing project/module key and a component in a new report as a cause of failed background processing. We can use that failure class without memory exhaustion, disk filling, database corruption, or Elasticsearch mutation.

Create these two disposable projects in the local SonarQube UI before scanning:

  • Parent project key: academy:sq-ch27-parent
  • Standalone child project key: academy:shared

Create separate project-analysis tokens for each. The source tree uses Maven’s natural groupId:artifactId keys, so no artificial sonar.projectKey override is necessary.

mkdir -p sq-ch27-reactor/shared/src/main/java/academy
cd sq-ch27-reactor
cat > pom.xml <<'XML'
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>academy</groupId>
  <artifactId>sq-ch27-parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>
  <modules><module>shared</module></modules>
</project>
XML
cat > shared/pom.xml <<'XML'
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>academy</groupId>
    <artifactId>sq-ch27-parent</artifactId>
    <version>1.0.0</version>
  </parent>
  <artifactId>shared</artifactId>
</project>
XML
cat > shared/src/main/java/academy/Shared.java <<'JAVA'
package academy;
public final class Shared { public static int twice(int x) { return x * 2; } }
JAVA

git init
git config user.email "learner@example.invalid"
git config user.name "SQ Learner"
git add . && git commit -m "chapter27 conflict fixture"
git rev-parse HEAD | tee "../evidence/$LAB_TS/conflict/revision.txt"

5. First make the child key belong to its standalone project

cd shared
export SONAR_TOKEN="$SHARED_PROJECT_TOKEN"
mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:5.7.0.6970:sonar \
  2>&1 | tee "../../evidence/$LAB_TS/conflict/shared-standalone-scanner.log"
cp target/sonar/report-task.txt \
  "../../evidence/$LAB_TS/conflict/shared-standalone-report-task.txt" 2>/dev/null || \
cp .scannerwork/report-task.txt \
  "../../evidence/$LAB_TS/conflict/shared-standalone-report-task.txt"
cd ..

Wait for this standalone child task to complete successfully. Now the server owns academy:shared as a project. Preserve the project page/API evidence before creating the conflict.

6. Submit the parent reactor and preserve the first failure

export SONAR_TOKEN="$PARENT_PROJECT_TOKEN"
set +e
mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:5.7.0.6970:sonar \
  2>&1 | tee "../evidence/$LAB_TS/conflict/parent-scanner.log"
SCAN_RC=${PIPESTATUS[0]}
set -e
printf 'scanner_exit=%s\n' "$SCAN_RC" \
  | tee "../evidence/$LAB_TS/conflict/scanner-exit.txt"

# If report-task.txt exists, the report was uploaded and CE owns the next step.
find . -name report-task.txt -print \
  | tee "../evidence/$LAB_TS/conflict/report-task-locations.txt"

Expected current behavior: the scanner can upload a parent report that contains the academy:shared component, and Compute Engine rejects the ownership clash. Preserve the task ID, failed task JSON/error details, scanner context, and matching ce.log lines.

Version-safe fallback. If your exact Maven/scanner version detects the duplicate/component-key conflict locally and refuses to upload, that is a scanner/configuration failure—not a failed CE task. Preserve it honestly. Do not corrupt the DB/search engine merely to manufacture FAILED. For the required CE-failure practice, use a training instance/version where the documented server-side key-clash behavior is reproducible or use the sanitized failed-task fixture provided by your instructor. The diagnostic sequence remains the same.

7. Repair only the ownership conflict

After exporting all evidence, delete only the disposable standalone project academy:shared. This releases that component key so it can belong to the parent reactor. Do not change the parent project key, deactivate rules, or alter the database/search engine.

# Perform deletion in the UI after verifying exact key academy:shared.
# Then rerun the SAME source revision from the parent root.
git status --porcelain
git rev-parse HEAD | tee "../evidence/$LAB_TS/recovery/revision.txt"
export SONAR_TOKEN="$PARENT_PROJECT_TOKEN"
mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:5.7.0.6970:sonar \
  2>&1 | tee "../evidence/$LAB_TS/recovery/scanner.log"

find . -name report-task.txt -print \
  | tee "../evidence/$LAB_TS/recovery/report-task-locations.txt"

The repaired parent task should complete successfully at the same Git SHA. That is the causal proof: source did not change; component ownership did.

8. Challenge: choose the owner, not the loudest symptom

You see all of the following at once: the UI works, api/system/health is GREEN, scanner exit code is 0, and one CE task is FAILED with “component already belongs to another project.” Which layer owns the correction?

Answer before revealing: the server/project topology represented by project/component keys. A server restart, more heap, lower gate, or Elasticsearch edit cannot resolve an ownership conflict.

Knowledge check

Why is the successful task captured before the failed-task experiment?

What makes a scanner-side duplicate-key rejection different from a CE failure?

Why not change the parent project key to make the failure disappear?

What evidence proves the recovery is causal?

Should you force an Elasticsearch error if the key clash is caught by the scanner in your version?

Next lesson

Design operational signals and response boundaries

Lesson 3 compares health, queue, logs, metrics, verbosity, restart, and infrastructure/project ownership as separate operational choices.

Official references and version notes

Version and compatibility note

Rechecked 2026-09-08. Mandatory examples target SonarQube Community Build 26.9.0.129388 and SonarScanner CLI 8.1.0.6389; the Maven failed-task fixture uses SonarScanner for Maven 5.7.0.6970. Current commercial reference is SonarQube Server 2026 Release 4.1 with 2026.1.5 LTA. Current Community Build logs are sonar.log, web.log, ce.log, es.log, access and API-deprecation logs. api/system/health requires Administer System or system passcode; /api/monitoring/metrics exposes OpenMetrics under its monitoring-auth boundary. Community Build has Web, CE and embedded Elasticsearch JVMs; production databases must use supported PostgreSQL/SQL Server/Oracle rather than the evaluation H2 database. The project/component key-clash failure is documented as a CE failure class, but exact scanner versions may prevalidate the conflict before upload; the lessons explicitly preserve that layer distinction rather than forcing unsafe database/search failures.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.