Logging DSL

A small set of helpers in vars/utils.groovy gives every *Step.groovy a uniform, scannable log format. The goal: a dense "thread of the process" on the Jenkins console, with verbose details routed to a file that is archived only when the build fails.

Why there is a DSL

Ad-hoc echo + sh calls produce noisy, inconsistent logs that are hard to read, hard to diff, and hard to follow when three stages fail in a row. The DSL enforces a single visual shape across every step, so users can skim the status column (› ✓ ⚠ ✗ ▸) without reading every line.

Cheat sheet

Helper Purpose

utils.logInit()

Normalise env.QA_LOG_VERBOSE; must be called once at the top of call().

utils.stage(num, total, title)

60-char -padded header — "Stage N/M — Title".

utils.step(msg)

Sub-step marker › msg.

utils.ok(msg) / utils.warn(msg) / utils.err(msg)

Status lines ✓ msg / ⚠ msg / ✗ msg.

utils.detail(msg)

Verbose-only; silent unless QA_LOG_VERBOSE=true.

utils.silentSh(label, script)

sh wrapper. Quiet mode: stdout only to .qa-build.log. Verbose mode: prepends set -x and tees to console.

utils.silentShOut(label, script)

Same as silentSh but returns stdout to caller; console stays clean even in verbose.

utils.group(name, closure)

Collapsible block. Quiet mode: prints the header, suppresses body console output. Verbose mode: prints both.

utils.archiveLogOnFailure()

Archives .qa-build.log as a build artifact. Call from catch blocks.

utils.exists(path) (@NonCPS)

Master-side file check; do not use inside node(runner) { …​ } blocks — use fileExists there instead.

Step integration pattern

Every *Step.groovy main body follows this shape:

def call(Map config = [:]) {
    ansiColor('xterm') {
        utils.logInit()
        try {
            utils.stage(1, 4, 'Resolve parameters')
            utils.step('Loading dependencies.yaml')
            utils.ok('Dependencies resolved')

            utils.stage(2, 4, 'Render compose bundle')
            utils.silentSh('yq filter', "yq '...' compose-mysql.yml > target.yml")
            utils.detail('Service variables overlaid')          // verbose-only

            utils.stage(3, 4, 'Write meta.json')
            utils.atomicWriteMeta(metaPath, metaData)
            utils.ok("Env ${envName} configured")

            utils.stage(4, 4, 'Done')
            // ...
        } catch (err) {
            utils.archiveLogOnFailure()
            utils.failureHints(err.message, hints, context)
            throw err
        }
    }
}

The try / catch around the body is not optional — it is what ensures .qa-build.log survives a failure as a build artifact.

Visual style

  • Single-char prefixes (› ✓ ⚠ ✗ ▸) line up into a readable column. Users read the column, not the sentence.

  • Emojis are reserved for the stage-7 summary box in createEnvStep and the image-override listings — do not sprinkle them elsewhere.

  • Stage headers are -padded to 60 chars for consistent width across stages.

QA_LOG_VERBOSE — runtime switch

Global Jenkins env var that toggles verbose mode for every pipeline step at runtime. No restart required; the next build picks up the change.

Value Effect

(unset) or anything but true

Quiet mode (default). utils.detail() is silent; utils.silentSh / silentShOut only write to .qa-build.log; utils.group prints the header and suppresses the body.

true

Verbose mode. utils.detail() prints. utils.silentSh prepends set -x and tees stdout to the console. utils.group prints the body inline.

How to enable:

  1. Manage Jenkins → System → Global properties → Environment variables.

  2. Add QA_LOG_VERBOSE = true. Save.

  3. Next build runs verbose.

QA_LOG_VERBOSE and DEBUG (the Create-Environment parameter that keeps failed envs on disk) are orthogonal switches. Either, both, or neither can be on.

Switch Scope What it does

QA_LOG_VERBOSE

global env var

Verbose log output.

DEBUG

Create-Environment parameter

Preserve the env directory on failure for investigation.

.qa-build.log

The logging DSL always writes the full verbose trace to .qa-build.log in the job workspace, regardless of QA_LOG_VERBOSE. The file is:

  • Created at the top of every step via silentSh / silentShOut.

  • Appended to as stages progress.

  • Archived only on failure by utils.archiveLogOnFailure() so the build’s Artifacts panel holds the post-mortem trace.

  • Removed by the next successful workspace cleanup — it is not a permanent log.

Download it from the build page’s Artifacts link to see the full trace when QA_LOG_VERBOSE is off.

Master-side vs runner-side

utils.exists is master-side only (@NonCPS + new File(…​)). For runner-side paths (/opt/qa-envs/<env>/…​) use fileExists inside the node(runner) { …​ } block. Getting this wrong typically looks like an ok("File present") followed by a compose up that says "no such file".

Failure hints

utils.failureHints(errorMessage, baseHints, context) prints a diagnostic block right before the exception propagates. Keep the hints list short and actionable ("check Harbor credential", "run with `QA_LOG_VERBOSE=true`", "verify port availability") — operators are usually scanning it in a rush.