Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jenkins Pipeline - how to get logs from parallel builds

Is it possible, and if yes: how?, to get the log output for each parallel step separately?

I.e.:

def projectBranches = [:]
        for (int i = 0; i < projects.size(); i++) {
            def _i = i
            projectBranches[_i] = {
                someFunction(_i)
            }
        }

        parallel projectBranches

Is it now possible to get the log for each of projectBranches[_i]?

like image 292
Matthias Avatar asked Jul 11 '16 10:07

Matthias


People also ask

Where do you check build logs in Jenkins?

Log files should be at /var/log/jenkins/jenkins.

How does Jenkins get parallel execution?

This plugin adds a tool that lets you easily execute tests in parallel. This is achieved by having Jenkins look at the test execution time of the last run, split tests into multiple units of roughly equal size, then execute them in parallel.

Can Jenkins run parallel builds?

Jenkins parallel builds can be based on static information and decisions, in which case the declarative approach works well. But when there is a need for dynamic decisions, a new approach is required for a more advanced way of doing Jenkins parallel builds.


2 Answers

I needed to access logs from within the pipeline code
so I implemented the algorithm proposed by キキジキ (really helpful) with a few adjustments (to add branchName prefix on each line to be able to get the whole log and still figure out which branch corresponds to each line ; and to support nested branches, which I needed) in https://github.com/gdemengin/pipeline-logparser :

  • to get logs programmatically

    • to get the full logs with branch prefix (similar to what currentBuild.rawBuild.log returned before version 2.2.5 of workflow-job plugin. but in version 2.26 they got rid of the branch information and I could not find any built-in function with the same information)
      String logs = logparser.getLogsWithBranchInfo()

      [Pipeline] Start of Pipeline
      [Pipeline] parallel
      [Pipeline] { (Branch: branch1)
      [Pipeline] { (Branch: branch2)
      [Pipeline] }
      [Pipeline] echo
      [branch1] in branch1
      [Pipeline] sleep
      [branch1] Sleeping for 1 sec
      [Pipeline] echo
      [branch2] in branch2
      [Pipeline] sleep
      [branch2] Sleeping for 1 sec

    • get the logs from 'branch2' only
      String logsBranch2 = logparser.getLogsWithBranchInfo(filter: ['branch2'])

      [branch2] in branch2
      [branch2] Sleeping for 1 sec

  • to archive logs (in as $JOB_URL/<runId>/artifacts) to have them available as a link for later use

    • to archive the full logs (with branch prefix)
      logparser.archiveLogsWithBranchInfo('consoleText.txt')

    • to archive the logs from branch2 only
      logparser.archiveLogsWithBranchInfo('logsBranch2.txt', [filter: ['branch2']])

like image 124
Guillaume Avatar answered Oct 20 '22 01:10

Guillaume


I found a way to achieve that, but you need to access the build folder directly (for example using currentBuild.rawBuild.getLogFile().getParent()).

  • Parse the xml files (or the single flowNodeStore.xml file) inside the workflow directory:
    • Build a hierarchy of nodes using the <id> and <parentIds> values.
    • If <branchName> is defined associate it to the current node and recursively to all nodes that have this node as parent. If a node has multiple parents assign no branch value to it.
  • Read the log file as byte[].
  • Read each line of log-index to find log ranges to assign to each node. The format of a line can be one of the following:
    • offset nodeId -> start of new node range, end of the previous (if present).
    • offset: end of current node range.
  • Convert the byte range back to a utf8 string (new String(range, "UTF-8")).
    • You might want to strip away all embedded codes with something like replaceAll("\u001B.*?\u001B\\[0m", "")
like image 35
キキジキ Avatar answered Oct 20 '22 00:10

キキジキ