├── .github
├── CODEOWNERS
├── CONTRIBUTING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ └── feature_request.yml
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
├── renovate.json
└── workflows
│ ├── build.yml
│ ├── jenkins-security-scan.yml
│ ├── m2.settings.xml
│ └── release.yml
├── .gitignore
├── .mvn
└── wrapper
│ └── maven-wrapper.properties
├── CHANGELOG.md
├── Dockerfile
├── Jenkinsfile
├── LICENSE
├── Makefile
├── README.md
├── docker-compose.yml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
├── java
│ └── hudson
│ │ └── plugins
│ │ └── logparser
│ │ ├── ClassicParsingStrategy.java
│ │ ├── CompiledPatterns.java
│ │ ├── LineToStatus.java
│ │ ├── LogParserAction.java
│ │ ├── LogParserBuildStep.java
│ │ ├── LogParserColumn.java
│ │ ├── LogParserConsts.java
│ │ ├── LogParserDisplayConsts.java
│ │ ├── LogParserLogPart.java
│ │ ├── LogParserParser.java
│ │ ├── LogParserPublisher.java
│ │ ├── LogParserReader.java
│ │ ├── LogParserResult.java
│ │ ├── LogParserStatusComputer.java
│ │ ├── LogParserThread.java
│ │ ├── LogParserUtils.java
│ │ ├── LogParserWriter.java
│ │ ├── ParserRuleFile.java
│ │ ├── ParsingInput.java
│ │ ├── ParsingRulePattern.java
│ │ ├── ParsingStrategy.java
│ │ ├── ParsingStrategyLocator.java
│ │ ├── ReadWriteTextFile.java
│ │ ├── StreamParsingStrategy.java
│ │ └── action
│ │ └── LogParserProjectAction.java
├── resources
│ ├── hudson
│ │ └── plugins
│ │ │ └── logparser
│ │ │ ├── LogParserAction
│ │ │ ├── index.jelly
│ │ │ └── summary.jelly
│ │ │ ├── LogParserColumn
│ │ │ ├── column.jelly
│ │ │ ├── column.properties
│ │ │ ├── columnHeader.jelly
│ │ │ └── columnHeader.properties
│ │ │ ├── LogParserPublisher
│ │ │ ├── config.jelly
│ │ │ ├── global.jelly
│ │ │ └── help-projectRulePath.html
│ │ │ ├── Messages.properties
│ │ │ ├── ParserRuleFile
│ │ │ └── config.jelly
│ │ │ └── action
│ │ │ └── LogParserProjectAction
│ │ │ └── floatingBox.jelly
│ └── index.jelly
└── webapp
│ ├── fail_on_error.html
│ ├── global_config_help.html
│ ├── global_legacy_formatting.html
│ ├── help.html
│ ├── images
│ ├── blue.gif
│ ├── gray.gif
│ ├── red.gif
│ └── yellow.gif
│ ├── js
│ └── log-parser-behaviour.js
│ ├── parse_rule_choice.html
│ ├── parser_graphs.html
│ └── unstable_on_warning.html
├── spotbugs
└── excludesFilter.xml
└── test
├── java
├── hudson
│ └── plugins
│ │ └── logparser
│ │ ├── LineToStatusTest.java
│ │ ├── LogParserPublisherTest.java
│ │ └── ParsingStrategyLocatorTest.java
└── org
│ └── jenkinsci
│ └── plugins
│ └── logparser
│ ├── ConfigurationAsCodeTest.java
│ └── LogParserWorkflowTest.java
└── resources
└── org
└── jenkinsci
└── plugins
└── logparser
├── configuration-as-code-legacy-formatting.yaml
├── configuration-as-code-parsing-rules.yaml
└── maven-project1
├── .gitignore
├── Jenkinsfile
├── doc.txt
├── logparser-rules.txt
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── cloudbees
│ └── manticore
│ └── App.java
└── test
└── java
└── com
└── cloudbees
└── manticore
└── AppTest.java
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @jenkinsci/log-parser-plugin-developers
2 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ### Submitting Pull Requests
2 |
3 | We'd love for you to contribute to our source code and to make this package even better than it is
4 | today! Here are the guidelines we'd like you to follow:
5 |
6 | - [Issues and Bugs](#issue)
7 | - [Feature Requests](#feature)
8 | - [Coding Rules](#rules)
9 | - [Commit Message Guidelines](#commit)
10 |
11 | ## Found an Issue?
12 |
13 | If you find a bug in the source code or a mistake in the documentation, you can help us by
14 | submitting an issue to our GitHub Repository. Even better you can submit a Pull Request
15 | with a fix. But first search if the issue is already described!
16 |
17 | If not create a new issue:
18 |
19 | * Tell about your environment:
20 | * node version
21 | * nativescript version
22 | * used platform and versionÍ
23 | * Describe your issue
24 | * describe your steps leading to the issue
25 | * attach error logs or screenshots
26 | * if possible provide test case or screenshots
27 |
28 | ## Want a Feature?
29 |
30 | You can request a new feature by submitting an issue to our [GitHub Repository][github].
31 |
32 | Please follow these basic steps to simplify pull request reviews - if you don't you'll probably just be asked to anyway.**
33 |
34 | * Please rebase your branch against the current develop, use the **develop** for pull requests
35 | * Please ensure that the test suite passes **and** that code is lint free before submitting a PR by running:
36 | * ```./mvnw test```
37 | * Verify plugin is working via docker test ```docker compose up```
38 | * If you've added new functionality, **please** include tests which validate its behaviour
39 | * Make reference to possible [issues](https://github.com/jenkinsci/log-parser-plugin/issues) on PR comment
40 |
41 | ### Resulting from long experience
42 |
43 | * To the largest extent possible, all fields shall be private. Use an IDE to generate the getters and setters.
44 | * If a class has more than one `volatile` member field, it is probable that there are subtle race conditions. Please consider where appropriate encapsulation of the multiple fields into an immutable value object replace the multiple `volatile` member fields with a single `volatile` reference to the value object (or perhaps better yet an `AtomicReference` to allow for `compareAndSet` - if compare-and-set logic is appropriate).
45 | * If it is `Serializable` it shall have a `serialVersionUID` field. Unless code has shipped to users, the initial value of the `serialVersionUID` field shall be `1L`.
46 |
47 | ### Indentation
48 |
49 | 1. **Use spaces.** Tabs are banned.
50 | 2. **Java blocks are 4 spaces.** JavaScript blocks as for Java. **XML nesting is 2 spaces**
51 |
52 | ### Field Naming Conventions
53 |
54 | 1. "hungarian"-style notation is banned (i.e. instance variable names preceded by an 'm', etc)
55 | 2. If the field is `static final` then it shall be named in `ALL_CAPS_WITH_UNDERSCORES`.
56 | 3. Start variable names with a lowercase letter and use camelCase rather than under_scores.
57 | 4. Spelling and abreviations: If the word is widely used in the JVM runtime, stick with the spelling/abreviation in the JVM runtime, e.g. `color` over `colour`, `sync` over `synch`, `async` over `asynch`, etc.
58 | 5. It is acceptable to use `i`, `j`, `k` for loop indices and iterators. If you need more than three, you are likely doing something wrong and as such you shall either use full descriptive names or refactor.
59 | 6. It is acceptable to use `e` for the exception in a `try...catch` block.
60 | 7. You shall never use `l` (i.e. lower case `L`) as a variable name.
61 |
62 | ### Line Length
63 |
64 | To the greatest extent possible, please wrap lines to ensure that they do not exceed 120 characters.
65 |
66 | ### Maven POM file layout
67 |
68 | * The `pom.xml` file shall use the sequencing of elements as defined by the `mvn tidy:pom` command (after any indenting fix-up).
69 | * If you are introducing a property to the `pom.xml` the property must be used in at least two distinct places in the model or a comment justifying the use of a property shall be provided.
70 | * If the `` is in the groupId `org.apache.maven.plugins` you shall omit the ``.
71 | * All `` entries shall have an explicit version defined unless inherited from the parent.
72 |
73 | ### Java code style
74 |
75 | #### Modifiers
76 |
77 | * For fields, the order is:
78 | - public / protected / private
79 | - static
80 | - final
81 | - transient
82 | - volatile
83 | * For methods, the order is:
84 | - public / protected / private
85 | - abstract
86 | - static
87 | - final
88 | - synchronized
89 | - native
90 | - strictfp
91 | * For classes, the order is:
92 | - public / protected / private
93 | - abstract
94 | - static
95 | - final
96 | - strictfp
97 |
98 | #### Imports
99 |
100 | * For code in `src/main`:
101 | - `*` imports are banned.
102 | - `static` imports are strongly discouraged.
103 | - `static` `*` imports are discouraged unless code readability is significantly enhanced and the import is restricted to a single class.
104 | * For code in `src/test`:
105 | - `*` imports of anything other than JUnit classes and Hamcrest matchers are banned.
106 | - `static` imports of anything other than JUnit classes and Hamcrest matchers are strongly discouraged.
107 | - `import static org.hamcrest.Matchers.*`, `import static org.junit.Assert.*` are expressly permitted. Any other `static` `*` imports are discouraged unless code readability is significantly enhanced and the import is restricted to a single class.
108 |
109 | #### Annotation placement
110 |
111 | * Annotations on classes, interfaces, annotations, enums, methods, fields and local variables shall be on the lines immediately preceding the line where modifier(s) (e.g. `public` / `protected` / `private` / `final`, etc) would be appropriate.
112 | * Annotations on method arguments shall, to the largest extent possible, be on the same line as the method argument (and, if present, before the `final` modifier)
113 |
114 | #### Javadoc
115 |
116 | * Each class shall have a Javadoc comment.
117 | * Each field shall have a Javadoc comment.
118 | * Unless the method is `private`, it shall have a Javadoc comment.
119 | * When a method is overriding a method from a super-class / interface, unless the semantics of the method have changed it is sufficient to document the intent of implementing the super-method's contract with:
120 | ```
121 | /**
122 | * {@inheritDoc}
123 | */
124 | @Override
125 | ```
126 | * Getters and Setters shall have a Javadoc comment. The following is prefered
127 | ```
128 | /**
129 | * The count of widgets
130 | */
131 | private int widgetCount;
132 |
133 | /**
134 | * Returns the count of widgets.
135 | *
136 | * @return the count of widgets.
137 | */
138 | public int getWidgetCount() {
139 | return widgetCount;
140 | }
141 |
142 | /**
143 | * Sets the count of widgets.
144 | *
145 | * @param widgetCount the count of widgets.
146 | */
147 | public void setWidgetCount(int widgetCount) {
148 | this.widgetCount = widgetCount;
149 | }
150 | ```
151 | * When adding a new class / interface / etc, it shall have a `@since` doc comment. The version shall be `FIXME` to indicate that the person merging the change should replace the `FIXME` with the next release version number. The fields and methods within a class/interface (but not nested classes) will be assumed to have the `@since` annotation of their class/interface unless a different `@since` annotation is present.
152 |
153 | ## Coding Rules
154 |
155 | To ensure consistency throughout the source code, keep these rules in mind as you are working:
156 |
157 | * All features or bug fixes **must be tested** by one or more [specs][unit-testing].
158 | * All public API methods **must be documented** with jsdoc.
159 |
160 |
161 | * To the largest extent possible, all fields shall be private. Use an IDE to generate the getters and setters.
162 | * If a class has more than one `volatile` member field, it is probable that there are subtle race conditions. Please consider where appropriate encapsulation of the multiple fields into an immutable value object replace the multiple `volatile` member fields with a single `volatile` reference to the value object (or perhaps better yet an `AtomicReference` to allow for `compareAndSet` - if compare-and-set logic is appropriate).
163 | * If it is `Serializable` it shall have a `serialVersionUID` field. Unless code has shipped to users, the initial value of the `serialVersionUID` field shall be `1L`.
164 |
165 |
166 | ## Git Commit Guidelines
167 |
168 | We're using [Angular Commit Guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#-git-commit-guidelines)
169 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | # github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | #patreon: continuoussecuritytooling
5 | #open_collective: moleculer
6 | #ko_fi: # Replace with a single Ko-fi username
7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H8TR8246RCDJG
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: 🐛Bug Report
2 | description: File a bug report here
3 | title: "[BUG] "
4 | labels: ["bug"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Please make sure to enter all needed information.
10 |
11 | - type: textarea
12 | id: prerequisites
13 | attributes:
14 | label: Prerequisites
15 | description: Please answer the following questions for yourself before submitting an issue.
16 | placeholder: |
17 | - [ ] I am running the latest version
18 | - [ ] I checked the documentation and found no answer
19 | - [ ] I checked to make sure that this issue has not already been filed
20 | - [ ] I'm reporting the issue to the correct repository
21 | validations:
22 | required: true
23 |
24 | - type: textarea
25 | id: bug-description
26 | attributes:
27 | label: Description of the bug
28 | description: Give us a brief description of what happened and what should have happened
29 | validations:
30 | required: true
31 |
32 | - type: textarea
33 | id: steps-to-reproduce
34 | attributes:
35 | label: Steps To Reproduce
36 | description: Steps to reproduce the behavior.
37 | placeholder: |
38 | 1. Go to '...'
39 | 2. Click on '...'
40 | 3. Scroll down to '...'
41 | 4. See error
42 | validations:
43 | required: true
44 |
45 | - type: textarea
46 | id: additional-information
47 | attributes:
48 | label: Additional Information
49 | description: |
50 | Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
51 | placeholder: |
52 | * Java version
53 | * Jenkins version
54 | * Operating System
55 | * Log files
56 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: ✨Feature Request
2 | description: Request a new feature or enhancement
3 | labels: ["enhancement"]
4 | title: "Add a meaningful name"
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Please describe the desired feature as clear as possible
10 |
11 | - type: textarea
12 | id: description
13 | attributes:
14 | label: Description
15 | description: Give us a brief description of the feature or enhancement you would like
16 | validations:
17 | required: true
18 |
19 | - type: textarea
20 | id: acceptance-criteria
21 | attributes:
22 | label: Acceptance Criteria
23 | description: All thinks that must be fullfilled due the change.
24 | placeholder: |
25 | [ ] List item
26 | validations:
27 | required: true
28 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## :memo: Description
2 |
3 |
4 |
5 | ### :dart: Relevant issues
6 |
7 |
8 | ### :gem: Type of change
9 |
10 |
11 |
12 | - [ ] Bug fix (non-breaking change which fixes an issue)
13 | - [ ] New feature (non-breaking change which adds functionality)
14 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
15 | - [ ] This change requires a documentation update
16 |
17 | ### :scroll: Example code
18 | ```js
19 |
20 | ```
21 |
22 | ## :vertical_traffic_light: How Has This Been Tested?
23 |
24 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
25 |
26 | - [ ] Test A
27 | - [ ] Test B
28 |
29 | ## :checkered_flag: Checklist:
30 |
31 | - [ ] My code follows the style guidelines of this project
32 | - [ ] I have performed a self-review of my own code
33 | - [ ] **I have added tests that prove my fix is effective or that my feature works**
34 | - [ ] **New and existing unit tests pass locally with my changes**
35 | - [ ] I have commented my code, particularly in hard-to-understand areas
36 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: maven
9 | directory: "/"
10 | schedule:
11 | interval: daily
12 | open-pull-requests-limit: 10
13 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:recommended",
5 | ":semanticCommitsDisabled",
6 | "schedule:daily"
7 | ],
8 | "automerge": true,
9 | "ignorePaths": [
10 | ],
11 | "ignoreDeps": [
12 | "org.apache.maven:maven-resolver-provider",
13 | "org.apache.maven:maven-settings-builder",
14 | "org.apache.maven.resolver:maven-resolver-api",
15 | "org.apache.maven.resolver:maven-resolver-connector-basic",
16 | "org.apache.maven.resolver:maven-resolver-impl",
17 | "org.apache.maven.resolver:maven-resolver-transport-file",
18 | "org.apache.maven.resolver:maven-resolver-transport-http",
19 | "org.apache.maven.resolver:maven-resolver-util",
20 | "org.eclipse.sisu:org.eclipse.sisu.inject"
21 | ],
22 | "packageRules": [
23 | {
24 | "allowedVersions": "<7.0.0",
25 | "matchPackageNames": [
26 | "com.google.inject:guice"
27 | ],
28 | "description": "We focus on Guice 6 until core adopts 7"
29 | },
30 | {
31 | "groupName": "Selenium",
32 | "matchPackageNames": [
33 | "/selenium/"
34 | ]
35 | },
36 | {
37 | "matchDepNames": [
38 | "docker",
39 | "docker/buildx",
40 | "firefox",
41 | "mozilla/geckodriver",
42 | "org.apache.maven:maven-core"
43 | ],
44 | "labels": [
45 | "dependencies",
46 | "build-image"
47 | ]
48 | }
49 | ],
50 | "labels": [
51 | "dependencies"
52 | ],
53 | "rebaseWhen": "conflicted"
54 | }
55 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - develop
8 | pull_request:
9 | merge_group:
10 |
11 | permissions:
12 | # This is required for requesting the JWT
13 | id-token: write
14 | security-events: write
15 | contents: read
16 | # This is required for actions/checkout
17 | actions: read
18 | issues: write
19 | # For reporting
20 | attestations: write
21 | checks: write
22 | # For Junit Reporting commenting
23 | pull-requests: write
24 |
25 | jobs:
26 | build:
27 | strategy:
28 | matrix:
29 | os: [ ubuntu-latest, windows-latest ]
30 | java: [ 17, 21 ]
31 | fail-fast: false
32 |
33 | runs-on: ${{ matrix.os }}
34 |
35 | steps:
36 | - name: Checkout
37 | uses: actions/checkout@v3
38 |
39 | - name: Set up cache for ~./m2/repository
40 | uses: actions/cache@v3
41 | with:
42 | path: ~/.m2/repository
43 | key: maven-${{ matrix.os }}-java${{ matrix.java }}-${{ hashFiles('**/pom.xml') }}-${{ hashFiles('.mvn/wrapper/maven-wrapper.properties') }}
44 | restore-keys: |
45 | maven-${{ matrix.os }}-java${{ matrix.java }}-
46 | maven-${{ matrix.os }}-
47 |
48 | - name: Set up JDK
49 | uses: actions/setup-java@v3
50 | with:
51 | java-version: ${{ matrix.java }}
52 | distribution: "adopt"
53 |
54 | - name: Build with Maven
55 | run: ./mvnw --batch-mode clean install
56 |
57 | - name: Publish Test Report
58 | uses: mikepenz/action-junit-report@v5
59 | if: success() || failure() # always run even if the previous step fails
60 | with:
61 | report_paths: '**/target/*/TEST-*.xml'
62 |
63 | - name: Run PMD checks
64 | run: ./mvnw pmd:check
65 |
66 | build-results:
67 | name: Build results
68 | if: ${{ always() }}
69 | runs-on: ubuntu-latest
70 | needs:
71 | - build
72 | steps:
73 | - name: Check out
74 | uses: actions/checkout@v3
75 | with:
76 | fetch-depth: 0
77 | - name: Set up JDK 17
78 | uses: actions/setup-java@v3
79 | with:
80 | distribution: 'adopt'
81 | java-version: 17
82 | - run: exit 1
83 | # see https://stackoverflow.com/a/67532120/4907315
84 | if: >-
85 | ${{
86 | contains(needs.*.result, 'failure')
87 | || contains(needs.*.result, 'cancelled')
88 | || contains(needs.*.result, 'skipped')
89 | }}
90 |
--------------------------------------------------------------------------------
/.github/workflows/jenkins-security-scan.yml:
--------------------------------------------------------------------------------
1 | name: Jenkins Security Scan
2 |
3 | on:
4 | push:
5 | branches:
6 | - develop
7 | pull_request:
8 | types: [ opened, synchronize, reopened ]
9 | workflow_dispatch:
10 |
11 | permissions:
12 | security-events: write
13 | contents: read
14 | actions: read
15 |
16 | jobs:
17 | security-scan:
18 | uses: jenkins-infra/jenkins-security-scan/.github/workflows/jenkins-security-scan.yaml@v2
19 | with:
20 | java-cache: 'maven' # Optionally enable use of a build dependency cache. Specify 'maven' or 'gradle' as appropriate.
21 | # java-version: 21 # Optionally specify what version of Java to set up for the build, or remove to use a recent default.
22 |
--------------------------------------------------------------------------------
/.github/workflows/m2.settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | central
7 | ${JENKINS_MAVEN_USER}
8 | ${JENKINS_MAVEN_PASS}
9 |
10 |
11 | snapshots
12 | ${JENKINS_MAVEN_USER}
13 | ${JENKINS_MAVEN_PASS}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | false
22 |
23 | central
24 | remote-snapshot-repos
25 | https://repo.jenkins-ci.org/artifactory/remote-snapshot-repos
26 |
27 |
28 |
29 | snapshots
30 | remote-snapshot-repos
31 | https://repo.jenkins-ci.org/artifactory/remote-snapshot-repos
32 |
33 |
34 |
35 |
36 |
37 | false
38 |
39 | central
40 | remote-snapshot-repos
41 | https://repo.jenkins-ci.org/artifactory/remote-snapshot-repos
42 |
43 |
44 |
45 | snapshots
46 | remote-snapshot-repos
47 | https://repo.jenkins-ci.org/artifactory/remote-snapshot-repos
48 |
49 |
50 | artifactory
51 |
52 |
53 |
54 | artifactory
55 |
56 |
57 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: create release
2 | run-name: "Release ${{ inputs.releaseversion }}"
3 |
4 | on:
5 | workflow_dispatch:
6 | inputs:
7 | releaseversion:
8 | description: 'Release version (..)'
9 | required: true
10 | type: string
11 | default: "X.Y.Z"
12 |
13 | jobs:
14 | release:
15 | runs-on: ubuntu-latest
16 | permissions:
17 | packages: write
18 | contents: write
19 | attestations: write
20 | id-token: write
21 |
22 | steps:
23 | - name: Check out
24 | uses: actions/checkout@v3
25 | with:
26 | fetch-depth: 0
27 |
28 | - name: set git config
29 | env:
30 | GH_TOKEN: ${{ github.token }}
31 | run: |
32 | git config --global user.email "${GITHUB_ACTOR_ID}+${GITHUB_ACTOR}@users.noreply.github.com"
33 | git config --global user.name "$(gh api /users/${GITHUB_ACTOR} | jq .name -r)"
34 |
35 | - name: Set up JDK 17
36 | uses: actions/setup-java@v3
37 | with:
38 | distribution: 'adopt'
39 | java-version: 17
40 | cache: 'maven'
41 |
42 | - name: Setup settings.xml
43 | uses: s4u/maven-settings-action@v3.1.0
44 | with:
45 | override: true
46 | servers: |
47 | [{
48 | "id": "repo.jenkins-ci.org",
49 | "username": "${{ vars.OSS_JENKINS_USER }}",
50 | "password": "${{ secrets.OSS_JENKINS_PASS }}"
51 | }]
52 |
53 | - name: Create release
54 | id: create-release
55 | env:
56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57 | run: |
58 | export TZ="Europe/Berlin"
59 | git checkout -b release/${{ inputs.releaseversion }}
60 | mvn release:prepare release:perform -B -DreleaseVersion=${{ inputs.releaseversion }} -DskipTests=true -DskipITs=true -DlocalCheckout=true -D"arguments=-DskipTests=true -DskipITs=true"
61 | # manually push
62 | git push --tags
63 | git push origin release/${{ inputs.releaseversion }}
64 | git branch --set-upstream-to=origin/release/${{ inputs.releaseversion }} release/${{ inputs.releaseversion }} || true
65 | # write version info
66 | cat <target/config.json
67 | {
68 | "version": "${{ inputs.releaseversion }}"
69 | }
70 | EOF
71 |
72 | - name: Conventional Changelog Action
73 | id: create-changelog
74 | uses: TriPSs/conventional-changelog-action@v5
75 | with:
76 | input-file: CHANGELOG.md
77 | github-token: ${{ secrets.GITHUB_TOKEN }}
78 | version-file: target/config.json
79 | pre-release: true
80 | skip-bump: true
81 | skip-tag: true
82 | skip-on-empty: true
83 | tag-prefix: 'v'
84 |
85 | - name: tag-and-release
86 | id: tag-and-release
87 | uses: avakar/tag-and-release@v1
88 | with:
89 | release_name: ${{ github.event.inputs.releaseversion }}
90 | tag_name: v${{ github.event.inputs.releaseversion }}
91 | draft: true
92 | env:
93 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
94 |
95 | - name: Create Pull Request
96 | id: create-pr
97 | uses: peter-evans/create-pull-request@v7
98 | with:
99 | commit-message: Update report
100 | committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
101 | author: ${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com>
102 | branch: release/${{ inputs.releaseversion }}
103 | base: develop
104 | title: '[Release] ${{ inputs.releaseversion }}'
105 | body: |
106 | Release Plugin v${{ inputs.releaseversion }}
107 | labels: |
108 | release
109 | assignees: hypery2k
110 | draft: false
111 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS stuff
2 | *.icloud
3 | ._*
4 | .DS_Store
5 | Thumbs.db
6 | Desktop.ini
7 | .Spotlight-V100
8 | .Trashes
9 |
10 | # IDE stuff
11 | .idea/*
12 | *.iml
13 | *.ipr
14 | .metadata
15 | bin/
16 | tmp/
17 | *.tmp
18 | *.bak
19 | *.swp
20 | *~.nib
21 | local.properties
22 | .settings/
23 | .loadpath
24 | .recommenders
25 |
26 | # External tool builders
27 | .externalToolBuilders/
28 |
29 | # Locally stored "Eclipse launch configurations"
30 | *.launch
31 |
32 | # PyDev specific (Python IDE for Eclipse)
33 | *.pydevproject
34 |
35 | # CDT-specific (C/C++ Development Tooling)
36 | .cproject
37 |
38 | # CDT- autotools
39 | .autotools
40 |
41 | # Java annotation processor (APT)
42 | .factorypath
43 |
44 | # PDT-specific (PHP Development Tools)
45 | .buildpath
46 |
47 | # sbteclipse plugin
48 | .target
49 |
50 | # Tern plugin
51 | .tern-project
52 |
53 | # TeXlipse plugin
54 | .texlipse
55 |
56 | # STS (Spring Tool Suite)
57 | .springBeans
58 |
59 | # Code Recommenders
60 | .recommenders/
61 |
62 | # Annotation Processing
63 | .apt_generated/
64 |
65 | # Scala IDE specific (Scala & Java development for Eclipse)
66 | .cache-main
67 | .scala_dependencies
68 | .worksheet
69 |
70 | ### Eclipse Patch ###
71 | # Eclipse Core
72 | .project
73 |
74 | # JDT-specific (Eclipse Java Development Tools)
75 | .classpath
76 |
77 | # Annotation Processing
78 | .apt_generated
79 |
80 | .sts4-cache/
81 |
82 | ### Intellij ###
83 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
84 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
85 |
86 | # User-specific stuff
87 | .idea/**/workspace.xml
88 | .idea/**/tasks.xml
89 | .idea/**/usage.statistics.xml
90 | .idea/**/dictionaries
91 | .idea/**/shelf
92 |
93 | # Generated files
94 | .idea/**/contentModel.xml
95 |
96 | # Sensitive or high-churn files
97 | .idea/**/dataSources/
98 | .idea/**/dataSources.ids
99 | .idea/**/dataSources.local.xml
100 | .idea/**/sqlDataSources.xml
101 | .idea/**/dynamic.xml
102 | .idea/**/uiDesigner.xml
103 | .idea/**/dbnavigator.xml
104 |
105 | # Gradle
106 | .idea/**/gradle.xml
107 | .idea/**/libraries
108 |
109 | # Gradle and Maven with auto-import
110 | # When using Gradle or Maven with auto-import, you should exclude module files,
111 | # since they will be recreated, and may cause churn. Uncomment if using
112 | # auto-import.
113 | # .idea/modules.xml
114 | # .idea/*.iml
115 | # .idea/modules
116 |
117 | # CMake
118 | cmake-build-*/
119 |
120 | # Mongo Explorer plugin
121 | .idea/**/mongoSettings.xml
122 |
123 | # File-based project format
124 | *.iws
125 |
126 | # IntelliJ
127 | out/
128 |
129 | # mpeltonen/sbt-idea plugin
130 | .idea_modules/
131 |
132 | # JIRA plugin
133 | atlassian-ide-plugin.xml
134 |
135 | # Cursive Clojure plugin
136 | .idea/replstate.xml
137 |
138 | # Crashlytics plugin (for Android Studio and IntelliJ)
139 | com_crashlytics_export_strings.xml
140 | crashlytics.properties
141 | crashlytics-build.properties
142 | fabric.properties
143 |
144 | # Editor-based Rest Client
145 | .idea/httpRequests
146 |
147 | # Android studio 3.1+ serialized cache file
148 | .idea/caches/build_file_checksums.ser
149 |
150 | ### Intellij Patch ###
151 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
152 |
153 | *.iml
154 | # modules.xml
155 | # .idea/misc.xml
156 | # *.ipr
157 |
158 | # Sonarlint plugin
159 | .idea/sonarlint
160 |
161 | ### Java ###
162 | # Compiled class file
163 | *.class
164 |
165 | # Log file
166 | *.log
167 |
168 | # BlueJ files
169 | *.ctxt
170 |
171 | # Mobile Tools for Java (J2ME)
172 | .mtj.tmp/
173 |
174 | # Package Files #
175 | *.jar
176 | *.war
177 | *.nar
178 | *.ear
179 | *.zip
180 | *.tar.gz
181 | *.rar
182 |
183 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
184 | hs_err_pid*
185 |
186 | ### Maven ###
187 | target/
188 | pom.xml.tag
189 | pom.xml.releaseBackup
190 | pom.xml.versionsBackup
191 | pom.xml.next
192 | release.properties
193 | dependency-reduced-pom.xml
194 | buildNumber.properties
195 | .mvn/timing.properties
196 | .mvn/wrapper/maven-wrapper.jar
197 |
198 | ### NetBeans ###
199 | **/nbproject/private/
200 | build/
201 | nbbuild/
202 | dist/
203 | nbdist/
204 | .nb-gradle/
205 |
206 |
207 | # End of https://www.gitignore.io/api/java,maven,netbeans,intellij,eclipse
208 |
209 | .idea/
210 | work/
211 | nbactions.xml
212 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
20 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [](https://github.com/jenkinsci/log-parser-plugin/compare/v2.5.0...v) (2025-05-16)
2 |
3 | ## [2.5.0](https://github.com/jenkinsci/log-parser-plugin/compare/v2.3.6...v2.5.0) (2025-05-16)
4 |
5 |
6 | ### Features
7 |
8 | * Add column to jobs list ([d4c87ef](https://github.com/jenkinsci/log-parser-plugin/commit/d4c87efc9939aa2c2d79a1fa275b2d035f0c48ef))
9 | * **java:** Require Jenkins core 2.492.3 and Java 17 ([2d96d37](https://github.com/jenkinsci/log-parser-plugin/commit/2d96d37d72bc7991659a6ca7115590d21f1e0e1b))
10 | * **Timestamper:** Adding support for Timestamper ([dcce20a](https://github.com/jenkinsci/log-parser-plugin/commit/dcce20ad6bb80a7b04daa56a929feb1618da4901))
11 | * upgrade to Jenkins LTS Core 2.462.3 for Java 11 support ([#150](https://github.com/jenkinsci/log-parser-plugin/issues/150)) ([5d5057e](https://github.com/jenkinsci/log-parser-plugin/commit/5d5057e0b52377af8e08d94f12f1a047c6eba280))
12 |
13 |
14 | ### Bug Fixes
15 |
16 | * **Escpaing:** Remove xterm escape codes from log file ([1b40ade](https://github.com/jenkinsci/log-parser-plugin/commit/1b40ade9af7e6b6b9deb87cea6b4920866b9fb31))
17 |
18 | ## [2.3.6](https://github.com/jenkinsci/log-parser-plugin/compare/v2.3.5...v2.3.6) (2024-11-25)
19 |
20 | ## [2.3.5](https://github.com/jenkinsci/log-parser-plugin/compare/v2.3.4...v2.3.5) (2024-08-07)
21 |
22 | ## [2.3.4](https://github.com/jenkinsci/log-parser-plugin/compare/v2.3.3...v2.3.4) (2024-06-03)
23 |
24 | ## [2.3.3](https://github.com/jenkinsci/log-parser-plugin/compare/v2.3.2...v2.3.3) (2023-12-23)
25 |
26 | ## [2.3.2](https://github.com/jenkinsci/log-parser-plugin/compare/v2.3.1...v2.3.2) (2023-10-15)
27 |
28 |
29 | ### Bug Fixes
30 |
31 | * **JENKINS-72048:** Correcting NPE ([16f069e](https://github.com/jenkinsci/log-parser-plugin/commit/16f069e6faea50630b4869337aa1703188b1e52c))
32 |
33 | ## [2.3.1](https://github.com/jenkinsci/log-parser-plugin/compare/d613f5ef8a166edc518f580165a8367746a463c0...v2.3.1) (2023-09-20)
34 |
35 |
36 | ### Features
37 |
38 | * Introduce `StreamParsingStrategy` to support builds with large logs ([#40](https://github.com/jenkinsci/log-parser-plugin/issues/40)) ([6b4b6e6](https://github.com/jenkinsci/log-parser-plugin/commit/6b4b6e6c95a7da4eaf49d6fe4be937da95ffc11b))
39 | * rebaseline Jenkins to 2.387.3 ([d6b4cc8](https://github.com/jenkinsci/log-parser-plugin/commit/d6b4cc8e995221d354fd4d5cf09a5787c23af73b))
40 |
41 |
42 | ### Bug Fixes
43 |
44 | * Colors & hints on trend graphs do not match to the data ([413b828](https://github.com/jenkinsci/log-parser-plugin/commit/413b828a52855a6345f72e869ba803544393160e))
45 | * docker-compose up doesn't support --rm ([d613f5e](https://github.com/jenkinsci/log-parser-plugin/commit/d613f5ef8a166edc518f580165a8367746a463c0))
46 | * Jenkins Log Parser Debug Icon not shown ([1e930e0](https://github.com/jenkinsci/log-parser-plugin/commit/1e930e00df73337ea75d8fe8d9caa38e3d7a792a))
47 | * **Memory:** Correct potential OOM when parsing logs in workflows ([#36](https://github.com/jenkinsci/log-parser-plugin/issues/36)) ([bff7f9f](https://github.com/jenkinsci/log-parser-plugin/commit/bff7f9f53820aade452a4c44441bbfabc905931e)), closes [/github.com/jenkinsci/workflow-job-plugin/blob/1551f82/src/main/java/org/jenkinsci/plugins/workflow/job/WorkflowRun.java#L1105](https://github.com/jenkinsci//github.com/jenkinsci/workflow-job-plugin/blob/1551f82/src/main/java/org/jenkinsci/plugins/workflow/job/WorkflowRun.java/issues/L1105)
48 | * Now Jenkins is available on port 8081 and boots ([f269c53](https://github.com/jenkinsci/log-parser-plugin/commit/f269c53fa0ddb5ae28ae3719afe6d6ee3ccc9d8e))
49 |
50 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM maven:alpine
2 |
3 | RUN apk update && apk upgrade && \
4 | apk --update add fontconfig ttf-dejavu bash git openssh openjdk8-jre
5 | RUN mkdir -p /data
6 | WORKDIR /data
7 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | // Builds a module using https://github.com/jenkins-infra/pipeline-library
2 | buildPlugin(useContainerAgent: true, configurations: [
3 | [ platform: 'linux', jdk: 21 ],
4 | [ platform: 'windows', jdk: 17 ],
5 | ])
6 |
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .DEFAULT_GOAL := tests
2 |
3 | .PHONY: jenkins
4 | jenkins:
5 | docker-compose up mvn
6 |
7 | .PHONY: tests
8 | tests:
9 | docker-compose run --rm mvn mvn clean test
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | log-parser-plugin
2 | =================
3 |
4 | [](https://ci.jenkins.io/blue/organizations/jenkins/Plugins%2Flog-parser-plugin/branches/)
5 |
6 | Parses the console output and highlights error/warning/info lines. It will make you analyze your build log in a pretty good way in Jenkins
7 | This log parser plugin will parse the console build logs which will be generated by Jenkins build. We can apply the following features in our logs by using this plugin:
8 |
9 | * Categorize the build log into the sections like ERRORS, INFO, DEBUG, WARNING, and HEADER.
10 | * Display these sections in summaries like the total number of errors and info on the build page.
11 | * Highlight the lines of our interest in the build log as per our needs.
12 | * Link the summary of errors and warnings with the full log, which makes us easy to search for a line of interest in the build log.
13 |
14 |
15 | Also see [logparser-rules.txt](src/test/resources/org/jenkinsci/plugins/logparser/maven-project1/logparser-rules.txt) for a sample parsing rule set. See [Wiki](https://wiki.jenkins-ci.org/JENKINS/Log-Parser-Plugin.html) for more details.
16 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | mvn:
5 | build:
6 | context: .
7 | volumes:
8 | - mvn_home:/root/.m2
9 | - ${PWD}:/data
10 | command: "mvn hpi:run"
11 | ports:
12 | - 8081:8080
13 | volumes:
14 | mvn_home:
15 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.3.2
23 | #
24 | # Optional ENV vars
25 | # -----------------
26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source
27 | # MVNW_REPOURL - repo url base for downloading maven distribution
28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
30 | # ----------------------------------------------------------------------------
31 |
32 | set -euf
33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x
34 |
35 | # OS specific support.
36 | native_path() { printf %s\\n "$1"; }
37 | case "$(uname)" in
38 | CYGWIN* | MINGW*)
39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
40 | native_path() { cygpath --path --windows "$1"; }
41 | ;;
42 | esac
43 |
44 | # set JAVACMD and JAVACCMD
45 | set_java_home() {
46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
47 | if [ -n "${JAVA_HOME-}" ]; then
48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then
49 | # IBM's JDK on AIX uses strange locations for the executables
50 | JAVACMD="$JAVA_HOME/jre/sh/java"
51 | JAVACCMD="$JAVA_HOME/jre/sh/javac"
52 | else
53 | JAVACMD="$JAVA_HOME/bin/java"
54 | JAVACCMD="$JAVA_HOME/bin/javac"
55 |
56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
59 | return 1
60 | fi
61 | fi
62 | else
63 | JAVACMD="$(
64 | 'set' +e
65 | 'unset' -f command 2>/dev/null
66 | 'command' -v java
67 | )" || :
68 | JAVACCMD="$(
69 | 'set' +e
70 | 'unset' -f command 2>/dev/null
71 | 'command' -v javac
72 | )" || :
73 |
74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
76 | return 1
77 | fi
78 | fi
79 | }
80 |
81 | # hash string like Java String::hashCode
82 | hash_string() {
83 | str="${1:-}" h=0
84 | while [ -n "$str" ]; do
85 | char="${str%"${str#?}"}"
86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
87 | str="${str#?}"
88 | done
89 | printf %x\\n $h
90 | }
91 |
92 | verbose() { :; }
93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
94 |
95 | die() {
96 | printf %s\\n "$1" >&2
97 | exit 1
98 | }
99 |
100 | trim() {
101 | # MWRAPPER-139:
102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
103 | # Needed for removing poorly interpreted newline sequences when running in more
104 | # exotic environments such as mingw bash on Windows.
105 | printf "%s" "${1}" | tr -d '[:space:]'
106 | }
107 |
108 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
109 | while IFS="=" read -r key value; do
110 | case "${key-}" in
111 | distributionUrl) distributionUrl=$(trim "${value-}") ;;
112 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
113 | esac
114 | done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
115 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
116 |
117 | case "${distributionUrl##*/}" in
118 | maven-mvnd-*bin.*)
119 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
120 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
121 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
122 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
123 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
124 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
125 | *)
126 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
127 | distributionPlatform=linux-amd64
128 | ;;
129 | esac
130 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
131 | ;;
132 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
133 | *) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
134 | esac
135 |
136 | # apply MVNW_REPOURL and calculate MAVEN_HOME
137 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
138 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
139 | distributionUrlName="${distributionUrl##*/}"
140 | distributionUrlNameMain="${distributionUrlName%.*}"
141 | distributionUrlNameMain="${distributionUrlNameMain%-bin}"
142 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
143 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
144 |
145 | exec_maven() {
146 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
147 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
148 | }
149 |
150 | if [ -d "$MAVEN_HOME" ]; then
151 | verbose "found existing MAVEN_HOME at $MAVEN_HOME"
152 | exec_maven "$@"
153 | fi
154 |
155 | case "${distributionUrl-}" in
156 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
157 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
158 | esac
159 |
160 | # prepare tmp dir
161 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
162 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
163 | trap clean HUP INT TERM EXIT
164 | else
165 | die "cannot create temp dir"
166 | fi
167 |
168 | mkdir -p -- "${MAVEN_HOME%/*}"
169 |
170 | # Download and Install Apache Maven
171 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
172 | verbose "Downloading from: $distributionUrl"
173 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
174 |
175 | # select .zip or .tar.gz
176 | if ! command -v unzip >/dev/null; then
177 | distributionUrl="${distributionUrl%.zip}.tar.gz"
178 | distributionUrlName="${distributionUrl##*/}"
179 | fi
180 |
181 | # verbose opt
182 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
183 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
184 |
185 | # normalize http auth
186 | case "${MVNW_PASSWORD:+has-password}" in
187 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
188 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
189 | esac
190 |
191 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
192 | verbose "Found wget ... using wget"
193 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
194 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
195 | verbose "Found curl ... using curl"
196 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
197 | elif set_java_home; then
198 | verbose "Falling back to use Java to download"
199 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
200 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
201 | cat >"$javaSource" <<-END
202 | public class Downloader extends java.net.Authenticator
203 | {
204 | protected java.net.PasswordAuthentication getPasswordAuthentication()
205 | {
206 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
207 | }
208 | public static void main( String[] args ) throws Exception
209 | {
210 | setDefault( new Downloader() );
211 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
212 | }
213 | }
214 | END
215 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java
216 | verbose " - Compiling Downloader.java ..."
217 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
218 | verbose " - Running Downloader.java ..."
219 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
220 | fi
221 |
222 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
223 | if [ -n "${distributionSha256Sum-}" ]; then
224 | distributionSha256Result=false
225 | if [ "$MVN_CMD" = mvnd.sh ]; then
226 | echo "Checksum validation is not supported for maven-mvnd." >&2
227 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
228 | exit 1
229 | elif command -v sha256sum >/dev/null; then
230 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
231 | distributionSha256Result=true
232 | fi
233 | elif command -v shasum >/dev/null; then
234 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
235 | distributionSha256Result=true
236 | fi
237 | else
238 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
239 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
240 | exit 1
241 | fi
242 | if [ $distributionSha256Result = false ]; then
243 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
244 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
245 | exit 1
246 | fi
247 | fi
248 |
249 | # unzip and move
250 | if command -v unzip >/dev/null; then
251 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
252 | else
253 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
254 | fi
255 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
256 | mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
257 |
258 | clean || :
259 | exec_maven "$@"
260 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | <# : batch portion
2 | @REM ----------------------------------------------------------------------------
3 | @REM Licensed to the Apache Software Foundation (ASF) under one
4 | @REM or more contributor license agreements. See the NOTICE file
5 | @REM distributed with this work for additional information
6 | @REM regarding copyright ownership. The ASF licenses this file
7 | @REM to you under the Apache License, Version 2.0 (the
8 | @REM "License"); you may not use this file except in compliance
9 | @REM with the License. You may obtain a copy of the License at
10 | @REM
11 | @REM http://www.apache.org/licenses/LICENSE-2.0
12 | @REM
13 | @REM Unless required by applicable law or agreed to in writing,
14 | @REM software distributed under the License is distributed on an
15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | @REM KIND, either express or implied. See the License for the
17 | @REM specific language governing permissions and limitations
18 | @REM under the License.
19 | @REM ----------------------------------------------------------------------------
20 |
21 | @REM ----------------------------------------------------------------------------
22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2
23 | @REM
24 | @REM Optional ENV vars
25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution
26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
28 | @REM ----------------------------------------------------------------------------
29 |
30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
31 | @SET __MVNW_CMD__=
32 | @SET __MVNW_ERROR__=
33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
34 | @SET PSModulePath=
35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
37 | )
38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
39 | @SET __MVNW_PSMODULEP_SAVE=
40 | @SET __MVNW_ARG0_NAME__=
41 | @SET MVNW_USERNAME=
42 | @SET MVNW_PASSWORD=
43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
44 | @echo Cannot start maven from wrapper >&2 && exit /b 1
45 | @GOTO :EOF
46 | : end batch / begin powershell #>
47 |
48 | $ErrorActionPreference = "Stop"
49 | if ($env:MVNW_VERBOSE -eq "true") {
50 | $VerbosePreference = "Continue"
51 | }
52 |
53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
55 | if (!$distributionUrl) {
56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
57 | }
58 |
59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
60 | "maven-mvnd-*" {
61 | $USE_MVND = $true
62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
63 | $MVN_CMD = "mvnd.cmd"
64 | break
65 | }
66 | default {
67 | $USE_MVND = $false
68 | $MVN_CMD = $script -replace '^mvnw','mvn'
69 | break
70 | }
71 | }
72 |
73 | # apply MVNW_REPOURL and calculate MAVEN_HOME
74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
75 | if ($env:MVNW_REPOURL) {
76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
78 | }
79 | $distributionUrlName = $distributionUrl -replace '^.*/',''
80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
82 | if ($env:MAVEN_USER_HOME) {
83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
84 | }
85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
87 |
88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
91 | exit $?
92 | }
93 |
94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
96 | }
97 |
98 | # prepare tmp dir
99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
102 | trap {
103 | if ($TMP_DOWNLOAD_DIR.Exists) {
104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
106 | }
107 | }
108 |
109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
110 |
111 | # Download and Install Apache Maven
112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
113 | Write-Verbose "Downloading from: $distributionUrl"
114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
115 |
116 | $webclient = New-Object System.Net.WebClient
117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
119 | }
120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
122 |
123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
125 | if ($distributionSha256Sum) {
126 | if ($USE_MVND) {
127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
128 | }
129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
132 | }
133 | }
134 |
135 | # unzip and move
136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
138 | try {
139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
140 | } catch {
141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
142 | Write-Error "fail to move MAVEN_HOME"
143 | }
144 | } finally {
145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
147 | }
148 |
149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
150 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | org.jenkins-ci.plugins
7 | plugin
8 | 5.16
9 |
10 |
11 |
12 | log-parser
13 | hpi
14 | Log Parser Plugin
15 | 2.5.1-SNAPSHOT
16 | https://github.com/jenkinsci/${project.artifactId}-plugin
17 | Parses the console log generated by a build
18 |
19 |
20 | 2.492
21 | ${jenkins.baseline}.3
22 |
23 |
24 | 3.1.1
25 | 3.3
26 |
27 |
28 |
29 |
30 | MIT license
31 | All source code is under the MIT license.
32 |
33 |
34 |
35 |
36 |
37 |
38 | org.apache.maven.plugins
39 | maven-pmd-plugin
40 | 3.26.0
41 |
42 | 17
43 |
44 | /rulesets/basic.xml
45 | /rulesets/braces.xml
46 | /rulesets/clone.xml
47 | /rulesets/codesize.xml
48 | /rulesets/design.xml
49 | /rulesets/finalizers.xml
50 | /rulesets/imports.xml
51 | /rulesets/j2ee.xml
52 | /rulesets/optimizations.xml
53 | /rulesets/strictexception.xml
54 | /rulesets/strings.xml
55 | /rulesets/unusedcode.xml
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | io.jenkins.tools.bom
66 | bom-${jenkins.baseline}.x
67 | 4836.vdf03ded1f27c
68 | import
69 | pom
70 |
71 |
72 |
73 |
74 |
75 |
76 | org.jenkins-ci.plugins
77 | scm-api
78 | test
79 |
80 |
81 | io.jenkins
82 | configuration-as-code
83 | test
84 |
85 |
86 | org.jenkins-ci.plugins.workflow
87 | workflow-cps
88 | test
89 |
90 |
91 | org.jenkins-ci.plugins.workflow
92 | workflow-basic-steps
93 | test
94 |
95 |
96 | org.jenkins-ci.plugins.workflow
97 | workflow-durable-task-step
98 | test
99 |
100 |
101 | org.jenkins-ci.plugins.workflow
102 | workflow-job
103 | test
104 |
105 |
106 | org.mockito
107 | mockito-core
108 | test
109 |
110 |
111 | org.mockito
112 | mockito-junit-jupiter
113 | test
114 |
115 |
116 | org.assertj
117 | assertj-core
118 | 3.27.3
119 | test
120 |
121 |
122 | org.jenkins-ci.main
123 | jenkins-test-harness-tools
124 | 2.2
125 | test
126 |
127 |
128 | org.jenkins-ci.main
129 | maven-plugin
130 | test
131 |
132 |
133 | org.jenkins-ci.plugins
134 | mailer
135 | test
136 |
137 |
138 | org.jenkins-ci.plugins
139 | timestamper
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | org.apache.maven.plugins
148 | maven-javadoc-plugin
149 |
150 | -Xdoclint:none
151 |
152 |
153 |
154 | maven-release-plugin
155 |
156 | v@{project.version}
157 |
158 |
159 |
160 |
161 |
162 |
163 | https://${GITHUB_TOKEN}@github.com/jenkinsci/log-parser-plugin.git
164 | scm:git:${project.scm.url}
165 | scm:git:${project.scm.url}
166 | v2.4.2
167 |
168 |
169 |
170 |
171 | repo.jenkins-ci.org
172 | https://repo.jenkins-ci.org/public/
173 |
174 |
175 |
176 |
177 |
178 | repo.jenkins-ci.org
179 | https://repo.jenkins-ci.org/public/
180 |
181 |
182 |
183 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/ClassicParsingStrategy.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import hudson.FilePath;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.File;
7 | import java.io.IOException;
8 | import java.io.InputStreamReader;
9 | import java.util.ArrayList;
10 | import java.util.HashMap;
11 | import java.util.concurrent.ExecutorService;
12 | import java.util.concurrent.Executors;
13 | import java.util.concurrent.TimeUnit;
14 | import java.util.logging.Level;
15 | import java.util.logging.Logger;
16 | import java.util.regex.Pattern;
17 |
18 | /**
19 | * This was the only available {@link ParsingStrategy} in 2.3.0 and earlier.
20 | *
21 | * For each build, this strategy will:
22 | *
23 | *
Copy the log into {@code java.io.tmpdir}
24 | *
Stream the copied log file to count lines via {@link LogParserUtils#countLines(String)}
25 | *
Create an {@link ExecutorService} via {@link Executors#newCachedThreadPool()}
26 | *
Determine number of {@link LogParserThread} tasks from lines / ({@link LogParserUtils#getLinesPerThread()} + 1)
27 | *
Submit and wait for all tasks to finish
28 | *
Aggregate lines into status from each task
29 | *
30 | *
31 | * @see StreamParsingStrategy
32 | * @since 2.4.0
33 | */
34 | class ClassicParsingStrategy implements ParsingStrategy {
35 | @Override
36 | public HashMap parse(ParsingInput input) {
37 | final Logger logger = Logger.getLogger(this.getClass().getName());
38 |
39 | // Copy remote file to temp local location
40 | String tempDir = System.getProperty("java.io.tmpdir");
41 | if (!tempDir.endsWith(File.separator)) {
42 | tempDir = tempDir + File.separator;
43 | }
44 |
45 | final String tempFileLocation = tempDir + "log-parser_" + input.getSignature();
46 | final File tempFile = new File(tempFileLocation);
47 | final FilePath tempFilePath = new FilePath(tempFile);
48 |
49 | try {
50 | tempFilePath.copyFrom(input.getLog());
51 |
52 | logger.log(Level.INFO, "Local temp file:" + tempFileLocation);
53 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(tempFilePath.read(), input.getCharset()))) {
54 | String[] parsingRulesArray = input.getParsingRulesArray();
55 | Pattern[] compiledPatterns = input.getCompiledPatterns();
56 | int threadCounter = 0;
57 |
58 | final ArrayList runners = new ArrayList<>();
59 | final LogParserReader logParserReader = new LogParserReader(reader);
60 |
61 | final ExecutorService execSvc = Executors.newCachedThreadPool();
62 | int linesInLog = LogParserUtils.countLines(tempFileLocation);
63 | final int threadsNeeded = linesInLog
64 | / LogParserUtils.getLinesPerThread() + 1;
65 |
66 | // Read and parse the log parts. Keep the threads and results in an
67 | // array for future reference when writing
68 | for (int i = 0; i < threadsNeeded; i++) {
69 | final LogParserThread logParserThread = new LogParserThread(
70 | logParserReader, parsingRulesArray, compiledPatterns,
71 | threadCounter);
72 | runners.add(logParserThread);
73 | execSvc.execute(logParserThread);
74 | threadCounter++;
75 | }
76 |
77 | // Wait for all threads to finish before sequentially writing the
78 | // outcome
79 | execSvc.shutdown();
80 | execSvc.awaitTermination(3600, TimeUnit.SECONDS);
81 |
82 | // Sort the threads in the order of the log parts they read
83 | // It could be that thread #1 read log part #2 and thread #2 read log
84 | // part #1
85 |
86 | final int runnersSize = runners.size();
87 | LogParserThread[] sortedRunners = new LogParserThread[runnersSize];
88 | for (LogParserThread logParserThread : runners) {
89 | final LogParserLogPart logPart = logParserThread.getLogPart();
90 | if (logPart != null) {
91 | final int logPartNum = logPart.getLogPartNum();
92 | sortedRunners[logPartNum] = logParserThread;
93 | }
94 | }
95 |
96 | final HashMap result = new HashMap<>();
97 | HashMap moreLineStatusMatches;
98 | for (int i = 0; i < runnersSize; i++) {
99 | final LogParserThread logParserThread = sortedRunners[i];
100 | if (logParserThread != null) {
101 | moreLineStatusMatches = getLineStatusMatches(
102 | logParserThread.getLineStatuses(), i);
103 | result.putAll(moreLineStatusMatches);
104 | }
105 | }
106 | return result;
107 | }
108 | } catch (IOException | InterruptedException e) {
109 | throw new RuntimeException(e);
110 | } finally {
111 | // Delete temp file
112 | try {
113 | tempFilePath.delete();
114 | } catch (IOException | InterruptedException e) {
115 | logger.log(Level.WARNING, "Failed to delete " + tempFilePath, e);
116 | }
117 | }
118 | }
119 |
120 | private HashMap getLineStatusMatches(
121 | final String[] statuses, final int logPart) {
122 | final HashMap result = new HashMap<>();
123 | String status;
124 | int line_num;
125 | final int linesPerThread = LogParserUtils.getLinesPerThread();
126 | if (statuses != null && statuses.length > 0) {
127 | for (int i = 0; i < statuses.length; i++) {
128 | status = statuses[i];
129 | line_num = i + logPart * linesPerThread;
130 | result.put(String.valueOf(line_num), status);
131 | }
132 | }
133 | return result;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/CompiledPatterns.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import edu.umd.cs.findbugs.annotations.CheckForNull;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.regex.Pattern;
8 |
9 | public class CompiledPatterns {
10 |
11 | private String errorMsg;
12 | private Pattern[] compiledPatterns;
13 | private List extraTags;
14 |
15 | public CompiledPatterns() {
16 | this.errorMsg = null;
17 | this.compiledPatterns = null;
18 | this.extraTags = new ArrayList<>();
19 | }
20 |
21 | public String getError() {
22 | return errorMsg;
23 | }
24 |
25 | public void setError(final String errorMsg) {
26 | if (errorMsg == null || errorMsg.trim().length() == 0) {
27 | this.errorMsg = null;
28 | } else {
29 | this.errorMsg = errorMsg.trim();
30 | }
31 | }
32 |
33 | @CheckForNull
34 | public Pattern[] getCompiledPatterns() {
35 | return compiledPatterns;
36 | }
37 |
38 | public void setCompiledPatters(final Pattern[] compiledPatterns) {
39 | this.compiledPatterns = compiledPatterns;
40 | }
41 |
42 | public List getExtraTags() {
43 | return extraTags;
44 | }
45 |
46 | public void setExtraTags(List extraTags) {
47 | this.extraTags = extraTags;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LineToStatus.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import hudson.console.ConsoleNote;
4 |
5 | import java.util.List;
6 | import java.util.function.UnaryOperator;
7 | import java.util.regex.Matcher;
8 | import java.util.regex.Pattern;
9 |
10 | /**
11 | * Extracted from {@link LogParserThread} so the same implementation could be
12 | * used in different {@link ParsingStrategy} implementations.
13 | * @since 2.4.0
14 | */
15 | class LineToStatus implements UnaryOperator {
16 | private final List patterns;
17 |
18 | LineToStatus(List patterns) {
19 | this.patterns = patterns;
20 | }
21 |
22 | @Override
23 | public String apply(String s) {
24 | // For now, strip out ConsoleNote(s) before parsing.
25 | // Notes are injected into log lines, and can break start-of-line
26 | // patterns, and include html. Will likely need alternative way to
27 | // handle in the future.
28 | String line = ConsoleNote.removeNotes(s);
29 | for (ParsingRulePattern parsingRulePattern : patterns) {
30 | String rule = parsingRulePattern.getRule();
31 | if (LogParserUtils.skipParsingRule(rule)) {
32 | continue;
33 | }
34 | Pattern pattern = parsingRulePattern.getPattern();
35 | Matcher matcher = pattern.matcher(line);
36 | if (matcher.find()) {
37 | String status = rule.split("\\s")[0];
38 | return LogParserUtils.standardizeStatus(status);
39 | }
40 | }
41 | return LogParserConsts.NONE;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserAction.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import hudson.Functions;
4 | import hudson.model.Action;
5 | import hudson.model.AbstractBuild;
6 | import hudson.model.Job;
7 | import hudson.model.Run;
8 | import hudson.plugins.logparser.action.LogParserProjectAction;
9 | import hudson.util.Area;
10 | import hudson.util.ChartUtil;
11 | import hudson.util.ColorPalette;
12 | import hudson.util.DataSetBuilder;
13 | import hudson.util.ShiftedCategoryAxis;
14 | import hudson.util.StackedAreaRenderer2;
15 |
16 | import jenkins.tasks.SimpleBuildStep;
17 |
18 | import java.awt.Color;
19 | import java.io.File;
20 | import java.io.IOException;
21 |
22 | import java.util.Collection;
23 | import java.util.Collections;
24 |
25 | import javax.servlet.ServletException;
26 |
27 | import org.jfree.chart.ChartFactory;
28 | import org.jfree.chart.JFreeChart;
29 | import org.jfree.chart.axis.CategoryAxis;
30 | import org.jfree.chart.axis.CategoryLabelPositions;
31 | import org.jfree.chart.axis.NumberAxis;
32 | import org.jfree.chart.plot.CategoryPlot;
33 | import org.jfree.chart.plot.PlotOrientation;
34 | import org.jfree.chart.renderer.category.StackedAreaRenderer;
35 | import org.jfree.data.category.CategoryDataset;
36 | import org.jfree.ui.RectangleInsets;
37 | import org.kohsuke.stapler.StaplerRequest;
38 | import org.kohsuke.stapler.StaplerResponse;
39 |
40 | public class LogParserAction implements Action, SimpleBuildStep.LastBuildAction {
41 |
42 | final private Run, ?> build;
43 | final private LogParserResult result;
44 | final private boolean showGraphs;
45 |
46 | private static String urlName = "parsed_console";
47 |
48 | @Deprecated
49 | public LogParserAction(final AbstractBuild, ?> build, final LogParserResult result, final boolean showGraphs) {
50 | this((Run, ?>) build, result, showGraphs);
51 | }
52 |
53 | public LogParserAction(final Run, ?> build, final LogParserResult result, final boolean showGraphs) {
54 | this.build = build;
55 | this.result = result;
56 | this.showGraphs = showGraphs;
57 | }
58 |
59 | public Collection extends Action> getProjectActions() {
60 | if (showGraphs) {
61 | final Job, ?> job = build.getParent();
62 | return Collections.singleton(new LogParserProjectAction(job));
63 | } else {
64 | return Collections.emptyList();
65 | }
66 | }
67 |
68 | @Override
69 | public String getIconFileName() {
70 | return "clipboard.gif";
71 | }
72 |
73 | @Override
74 | public String getDisplayName() {
75 | return "Console Output (parsed)";
76 | }
77 |
78 | @Override
79 | public String getUrlName() {
80 | return urlName;
81 | }
82 |
83 | public static String getUrlNameStat() {
84 | return urlName;
85 | }
86 |
87 | public Run, ?> getOwner() {
88 | return build;
89 | }
90 |
91 | // Used by the summary.jelly of this class to show some totals from the
92 | // result
93 | public LogParserResult getResult() {
94 | return result;
95 | }
96 |
97 | public LogParserAction getPreviousAction() {
98 | Run, ?> build = this.getOwner();
99 |
100 | while (true) {
101 |
102 | build = build.getPreviousBuild();
103 |
104 | if (build == null)
105 | return null;
106 | LogParserAction action = build.getAction(LogParserAction.class);
107 | if (action != null)
108 | return action;
109 | }
110 | }
111 |
112 | public void doDynamic(final StaplerRequest req, final StaplerResponse rsp)
113 | throws IOException, ServletException, InterruptedException {
114 | final String dir = result.getHtmlLogPath();
115 | final String file = req.getRestOfPath();
116 | final String fileArray[] = file.split("/");
117 | final String lastFileInPath = fileArray[fileArray.length - 1];
118 | final File f = new File(dir + "/" + lastFileInPath);
119 | rsp.serveFile(req, f.toURI().toURL());
120 |
121 | }
122 |
123 | public void doGraph(StaplerRequest req, StaplerResponse rsp)
124 | throws IOException {
125 | if (ChartUtil.awtProblemCause != null) {
126 | // not available. send out error message
127 | rsp.sendRedirect2(req.getContextPath() + "/images/headless.png");
128 | return;
129 | }
130 |
131 | if (req.checkIfModified(getOwner().getTimestamp(), rsp))
132 | return;
133 |
134 | ChartUtil.generateGraph(req, rsp, createChart(req, buildDataSet()),
135 | calcDefaultSize());
136 | }
137 |
138 | public void doGraphMap(StaplerRequest req, StaplerResponse rsp)
139 | throws IOException {
140 | if (req.checkIfModified(this.getOwner().getTimestamp(), rsp))
141 | return;
142 | ChartUtil.generateClickableMap(req, rsp,
143 | createChart(req, buildDataSet()), calcDefaultSize());
144 | }
145 |
146 | private Area calcDefaultSize() {
147 | Area res = Functions.getScreenResolution();
148 | if (res != null && res.width <= 800)
149 | return new Area(250, 100);
150 | else
151 | return new Area(500, 200);
152 | }
153 |
154 | private CategoryDataset buildDataSet() {
155 |
156 | DataSetBuilder dsb = new DataSetBuilder<>();
157 |
158 | for (LogParserAction a = this; a != null; a = a.getPreviousAction()) {
159 | dsb.add(a.result.getTotalErrors(), "0",
160 | new ChartUtil.NumberOnlyBuildLabel(a.getOwner()));
161 | dsb.add(a.result.getTotalWarnings(), "1",
162 | new ChartUtil.NumberOnlyBuildLabel(a.getOwner()));
163 | dsb.add(a.result.getTotalInfos(), "2",
164 | new ChartUtil.NumberOnlyBuildLabel(a.getOwner()));
165 | dsb.add(a.result.getTotalDebugs(), "3",
166 | new ChartUtil.NumberOnlyBuildLabel(a.getOwner()));
167 | for (String extraTag : a.result.getExtraTags()) {
168 | dsb.add(a.result.getTotalCountsByExtraTag(extraTag), extraTag,
169 | new ChartUtil.NumberOnlyBuildLabel(a.getOwner()));
170 | }
171 | }
172 | return dsb.build();
173 | }
174 |
175 | private JFreeChart createChart(StaplerRequest req, CategoryDataset dataset) {
176 |
177 | final String relPath = getRelPath(req);
178 |
179 | final JFreeChart chart = ChartFactory.createStackedAreaChart(
180 | null, // chart title
181 | null, // unused
182 | "count", // range axis label
183 | dataset, // data
184 | PlotOrientation.VERTICAL, // orientation
185 | false, // include legend
186 | true, // tooltips
187 | false // urls
188 | );
189 |
190 | // NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...
191 |
192 | // set the background color for the chart...
193 |
194 | //final StandardLegend legend = (StandardLegend) chart.getLegend();
195 | //legend.setAnchor(StandardLegend.SOUTH);
196 |
197 | chart.setBackgroundPaint(Color.white);
198 |
199 | final CategoryPlot plot = chart.getCategoryPlot();
200 |
201 | //plot.setAxisOffset(new Spacer(Spacer.ABSOLUTE, 5.0, 5.0, 5.0, 5.0));
202 | plot.setBackgroundPaint(Color.WHITE);
203 | plot.setOutlinePaint(null);
204 | plot.setForegroundAlpha(0.8f);
205 | //plot.setDomainGridlinesVisible(true);
206 | //plot.setDomainGridlinePaint(Color.white);
207 | plot.setRangeGridlinesVisible(true);
208 | plot.setRangeGridlinePaint(Color.black);
209 |
210 | CategoryAxis domainAxis = new ShiftedCategoryAxis(null);
211 | plot.setDomainAxis(domainAxis);
212 | domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
213 | domainAxis.setLowerMargin(0.0);
214 | domainAxis.setUpperMargin(0.0);
215 | domainAxis.setCategoryMargin(0.0);
216 |
217 | final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
218 | rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
219 |
220 | StackedAreaRenderer ar = new StackedAreaRenderer2() {
221 |
222 | private static final long serialVersionUID = 1L;
223 |
224 | @Override
225 | public String generateURL(CategoryDataset dataset, int row,
226 | int column) {
227 | ChartUtil.NumberOnlyBuildLabel label = (ChartUtil.NumberOnlyBuildLabel) dataset
228 | .getColumnKey(column);
229 | return relPath + label.build.getNumber() + "/testReport/";
230 | }
231 |
232 | @Override
233 | public String generateToolTip(CategoryDataset dataset, int row,
234 | int column) {
235 | ChartUtil.NumberOnlyBuildLabel label = (ChartUtil.NumberOnlyBuildLabel) dataset
236 | .getColumnKey(column);
237 | LogParserResult result = label.build.getAction(LogParserAction.class).getResult();
238 | switch (row) {
239 | case 0:
240 | return "Errors: " + result.getTotalErrors();
241 | case 1:
242 | return "Warnings: " + result.getTotalWarnings();
243 | case 2:
244 | return "Infos: " + result.getTotalInfos();
245 | default:
246 | return "Debugs: " + result.getTotalDebugs();
247 | }
248 | }
249 | };
250 | plot.setRenderer(ar);
251 | ar.setSeriesPaint(0, ColorPalette.RED); // error
252 | ar.setSeriesPaint(1, ColorPalette.YELLOW); // warning
253 | ar.setSeriesPaint(2, ColorPalette.BLUE); // info
254 | ar.setSeriesPaint(3, ColorPalette.GREY); // debug
255 |
256 | // crop extra space around the graph
257 | plot.setInsets(new RectangleInsets(0, 0, 0, 5.0));
258 |
259 | return chart;
260 | }
261 |
262 | private String getRelPath(StaplerRequest req) {
263 | String relPath = req.getParameter("rel");
264 | if (relPath == null)
265 | return "";
266 | return relPath;
267 | }
268 |
269 | }
270 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserBuildStep.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import hudson.Launcher;
4 | import hudson.model.Action;
5 | import hudson.model.BuildListener;
6 | import hudson.model.AbstractBuild;
7 | import hudson.model.AbstractProject;
8 | import hudson.tasks.BuildStep;
9 | import hudson.tasks.BuildStepMonitor;
10 |
11 | import java.io.IOException;
12 | import java.util.Collection;
13 | import java.util.Collections;
14 |
15 | public class LogParserBuildStep implements BuildStep {
16 |
17 | public boolean prebuild(final AbstractBuild, ?> build, final BuildListener listener) {
18 | return true;
19 | }
20 |
21 | public boolean perform(final AbstractBuild, ?> build, final Launcher launcher, final BuildListener listener)
22 | throws InterruptedException, IOException {
23 | return true;
24 | }
25 |
26 | public BuildStepMonitor getRequiredMonitorService() {
27 | return BuildStepMonitor.NONE;
28 | }
29 |
30 | public Action getProjectAction(final AbstractProject, ?> project) {
31 | return null;
32 | }
33 |
34 | public Collection extends Action> getProjectActions(AbstractProject, ?> project) {
35 | return Collections.emptyList();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserColumn.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import hudson.Extension;
4 | import hudson.model.Job;
5 | import hudson.model.Run;
6 | import hudson.views.ListViewColumn;
7 | import hudson.views.ListViewColumnDescriptor;
8 | import net.sf.json.JSONObject;
9 | import org.kohsuke.stapler.StaplerRequest;
10 |
11 | public class LogParserColumn extends ListViewColumn {
12 | public int[] getResult(Job job) {
13 | if (job == null) {
14 | return null;
15 | }
16 | Run build = job.getLastCompletedBuild();
17 | if (build == null) {
18 | return null;
19 | }
20 | LogParserAction action = build.getAction(LogParserAction.class);
21 | if (action == null) {
22 | return null;
23 | }
24 | LogParserResult result = action.getResult();
25 | if (result == null) {
26 | return null;
27 | }
28 |
29 | return new int[]{result.getTotalErrors(), result.getTotalWarnings(), result.getTotalInfos(), result.getTotalDebugs()};
30 | }
31 |
32 | public String getUrl(Job job) {
33 | if (job == null) {
34 | return null;
35 | }
36 | Run build = job.getLastCompletedBuild();
37 | if (build == null) {
38 | return null;
39 | }
40 | return build.getUrl() + LogParserAction.getUrlNameStat();
41 | }
42 |
43 | @Extension
44 | public static class LogParserColumnDescriptor extends ListViewColumnDescriptor {
45 | @Override
46 | public ListViewColumn newInstance(StaplerRequest req, JSONObject formData) throws FormException {
47 | return new LogParserColumn();
48 | }
49 |
50 | @Override
51 | public String getDisplayName() {
52 | return Messages.LogParserColumn_Header();
53 | }
54 |
55 | @Override
56 | public boolean shownByDefault() {
57 | return false;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserConsts.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import jenkins.model.Jenkins;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | public class LogParserConsts {
9 |
10 | public static final String ERROR = "ERROR";
11 | public static final String WARNING = "WARNING";
12 | public static final String INFO = "INFO";
13 | public static final String DEBUG = "DEBUG";
14 | public static final String NONE = "NONE";
15 | public static final String START = "START"; // marks a beginning of a section
16 | public static final String DEFAULT = NONE;
17 |
18 | // Error messages
19 | public static final String CANNOT_PARSE = "log-parser plugin ERROR: Cannot parse log ";
20 | public static final String NOT_INT = " is not an integer - using default";
21 |
22 | public static final List LEGAL_STATUS = Arrays.asList(ERROR, WARNING, INFO, DEBUG, NONE, START);
23 | public static final List STATUSES_WITH_LINK_FILES = Arrays.asList(ERROR, WARNING, INFO, DEBUG);
24 | public static final List STATUSES_WITH_SECTIONS_IN_LINK_FILES = Arrays.asList(ERROR, WARNING, DEBUG);
25 |
26 | public static String getHtmlOpeningTags() {
27 | final String hudsonRoot = Jenkins.get().getRootUrl();
28 | final String pluginResourceUrl = String.format("%s/plugin/log-parser/", Jenkins.RESOURCE_PATH).substring(1);
29 |
30 | return "\n" + "\n" + "\t\n"
31 | + "\t\tlog-parser plugin page\n"
32 | + "\t\t\n"
33 | +"\t\n"
34 | + "\t\n";
35 | }
36 |
37 | public static final String getHtmlClosingTags() {
38 | return "\t\n" + "\n";
39 | }
40 |
41 | // Parsing in threads for performance
42 | public static final int LINES_PER_THREAD = 10000; // How many lines to parse
43 | // in each thread
44 | public static final int MAX_THREADS = 2; // How many concurrent threads to
45 | // run (unused when implementing
46 | // cached thread pool)
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserDisplayConsts.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import java.util.HashMap;
4 | import org.apache.commons.lang.WordUtils;
5 |
6 | public class LogParserDisplayConsts {
7 |
8 | final private HashMap colorTable = new HashMap<>();
9 | final private HashMap iconTable = new HashMap<>();
10 | final private HashMap linkListDisplay = new HashMap<>();
11 | final private HashMap linkListDisplayPlural = new HashMap<>();
12 |
13 | public static final String DEFAULT_COLOR = "blue";
14 | public static final String DEFAULT_ICON = "blue.gif";
15 |
16 | public static String getDefaultLinkListDisplay(String status) {
17 | return WordUtils.capitalize(status);
18 | }
19 |
20 | public static String getDefaultLinkListDisplayPlural(String status) {
21 | return getDefaultLinkListDisplay(status) + "s";
22 | }
23 |
24 | public LogParserDisplayConsts() {
25 | // Color of each status
26 | colorTable.put(LogParserConsts.ERROR, "red");
27 | colorTable.put(LogParserConsts.WARNING, "orange");
28 | colorTable.put(LogParserConsts.INFO, "blue");
29 | colorTable.put(LogParserConsts.START, "blue");
30 | colorTable.put(LogParserConsts.DEBUG, "grey");
31 |
32 | // Icon for each status in the summary
33 | iconTable.put(LogParserConsts.ERROR, "red.gif");
34 | iconTable.put(LogParserConsts.WARNING, "yellow.gif");
35 | iconTable.put(LogParserConsts.INFO, "blue.gif");
36 | iconTable.put(LogParserConsts.DEBUG, "gray.gif");
37 |
38 | // How to display in link summary html
39 | linkListDisplay.put(LogParserConsts.ERROR, "Error");
40 | linkListDisplay.put(LogParserConsts.WARNING, "Warning");
41 | linkListDisplay.put(LogParserConsts.INFO, "Info");
42 | linkListDisplay.put(LogParserConsts.DEBUG, "Debug");
43 |
44 | linkListDisplayPlural.put(LogParserConsts.ERROR, "Errors");
45 | linkListDisplayPlural.put(LogParserConsts.WARNING, "Warnings");
46 | linkListDisplayPlural.put(LogParserConsts.INFO, "Infos");
47 | linkListDisplayPlural.put(LogParserConsts.DEBUG, "Debugs");
48 | }
49 |
50 | public HashMap getColorTable() {
51 | return colorTable;
52 | }
53 |
54 | public HashMap getIconTable() {
55 | return iconTable;
56 | }
57 |
58 | public HashMap getLinkListDisplay() {
59 | return linkListDisplay;
60 | }
61 |
62 | public HashMap getLinkListDisplayPlural() {
63 | return linkListDisplayPlural;
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserLogPart.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | public class LogParserLogPart {
4 |
5 | private String[] lines;
6 | private int logPartNum;
7 |
8 | // Intentional - first object is created, then fields are set later on.
9 | public LogParserLogPart() {
10 | }
11 |
12 | public String[] getLines() {
13 | return lines;
14 | }
15 |
16 | public void setLines(final String[] lines) {
17 | this.lines = lines;
18 | }
19 |
20 | public int getLogPartNum() {
21 | return logPartNum;
22 | }
23 |
24 | public void setLogPartNum(final int logPartNum) {
25 | this.logPartNum = logPartNum;
26 | }
27 |
28 | public boolean isEmpty() {
29 | return lines[0] == null;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserParser.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import hudson.FilePath;
4 | import hudson.console.ConsoleNote;
5 | import hudson.model.AbstractBuild;
6 | import hudson.model.Run;
7 | import hudson.remoting.VirtualChannel;
8 | import hudson.plugins.timestamper.api.TimestamperAPI;
9 |
10 | import java.io.BufferedReader;
11 | import java.io.BufferedWriter;
12 | import java.io.FileWriter;
13 | import java.io.IOException;
14 | import java.io.InputStream;
15 | import java.nio.charset.Charset;
16 | import java.util.ArrayList;
17 | import java.util.Calendar;
18 | import java.util.HashMap;
19 | import java.util.List;
20 | import java.util.Map;
21 | import java.util.logging.Level;
22 | import java.util.logging.Logger;
23 | import java.util.regex.Pattern;
24 |
25 | public class LogParserParser {
26 |
27 | final private HashMap statusCount = new HashMap<>();
28 | final private HashMap writers = new HashMap<>();
29 | final private HashMap linkFiles = new HashMap<>();
30 |
31 | final private String[] parsingRulesArray;
32 | final private Pattern[] compiledPatterns;
33 | final private CompiledPatterns compiledPatternsPlusError;
34 | final private List extraTags;
35 |
36 | // if key is 3-ERROR it shows how many errors are in section 3
37 | final private HashMap statusCountPerSection = new HashMap<>();
38 | final private ArrayList headerForSection = new ArrayList<>();
39 | private int sectionCounter = 0;
40 |
41 | final private LogParserDisplayConsts displayConstants = new LogParserDisplayConsts();
42 |
43 | final private VirtualChannel channel;
44 | final private boolean preformattedHtml;
45 |
46 | public LogParserParser(final FilePath parsingRulesFile,
47 | final boolean preformattedHtml, final VirtualChannel channel)
48 | throws IOException {
49 |
50 | // init logger
51 | final Logger logger = Logger.getLogger(getClass().getName());
52 |
53 | this.parsingRulesArray = LogParserUtils
54 | .readParsingRules(parsingRulesFile);
55 |
56 | // This causes each regular expression to be compiled once for better
57 | // performance
58 | this.compiledPatternsPlusError = LogParserUtils.compilePatterns(
59 | this.parsingRulesArray, logger);
60 | this.compiledPatterns = this.compiledPatternsPlusError
61 | .getCompiledPatterns();
62 | this.extraTags = this.compiledPatternsPlusError.getExtraTags();
63 |
64 | this.preformattedHtml = preformattedHtml;
65 | this.channel = channel;
66 |
67 | // Count of lines in this status
68 | statusCount.put(LogParserConsts.ERROR, 0);
69 | statusCount.put(LogParserConsts.WARNING, 0);
70 | statusCount.put(LogParserConsts.INFO, 0);
71 | statusCount.put(LogParserConsts.DEBUG, 0);
72 | for (String extraTag : this.extraTags) {
73 | statusCount.put(extraTag, 0);
74 | }
75 | }
76 |
77 | /*
78 | * This method creates the parsed log file : log.html It also creates the
79 | * lists of links to these errors/warnings/info messages respectively :
80 | * errorLinks.html, warningLinks.html, infoLinks.html
81 | */
82 | @Deprecated
83 | public LogParserResult parseLog(final AbstractBuild, ?> build) throws IOException, InterruptedException {
84 | return this.parseLog((Run, ?>) build);
85 | }
86 |
87 | public LogParserResult parseLog(final Run, ?> build) throws IOException, InterruptedException {
88 |
89 | // init logger
90 | final Logger logger = Logger.getLogger(getClass().getName());
91 |
92 | // Get console log file
93 | final InputStream log = build.getLogInputStream();
94 | final String logDirectory = build.getRootDir().getAbsolutePath();
95 |
96 | // Determine parsed log files
97 | final String parsedFilePath = logDirectory + "/log_content.html";
98 | final String errorLinksFilePath = logDirectory + "/logerrorLinks.html";
99 | final String warningLinksFilePath = logDirectory + "/logwarningLinks.html";
100 | final String infoLinksFilePath = logDirectory + "/loginfoLinks.html";
101 | final String debugLinksFilePath = logDirectory + "/logdebugLinks.html";
102 | final Map linksFilePathByExtraTags = new HashMap<>();
103 | for (String extraTag : this.extraTags) {
104 | linksFilePathByExtraTags.put(extraTag, logDirectory + "/log" + extraTag + "Links.html");
105 | }
106 | final String buildRefPath = logDirectory + "/log_ref.html";
107 | final String buildWrapperPath = logDirectory + "/log.html";
108 |
109 | // Record file paths in HashMap
110 | linkFiles.put(LogParserConsts.ERROR, errorLinksFilePath);
111 | linkFiles.put(LogParserConsts.WARNING, warningLinksFilePath);
112 | linkFiles.put(LogParserConsts.INFO, infoLinksFilePath);
113 | linkFiles.put(LogParserConsts.DEBUG, debugLinksFilePath);
114 | for (String extraTag : this.extraTags) {
115 | linkFiles.put(extraTag, linksFilePathByExtraTags.get(extraTag));
116 | }
117 |
118 | // Open console log for reading and all other files for writing
119 | try (BufferedWriter writer = new BufferedWriter(new FileWriter(parsedFilePath))) {
120 |
121 | // Record writers to links files in hash
122 | writers.put(LogParserConsts.ERROR, new BufferedWriter(new FileWriter(
123 | errorLinksFilePath)));
124 | writers.put(LogParserConsts.WARNING, new BufferedWriter(new FileWriter(
125 | warningLinksFilePath)));
126 | writers.put(LogParserConsts.INFO, new BufferedWriter(new FileWriter(
127 | infoLinksFilePath)));
128 | writers.put(LogParserConsts.DEBUG, new BufferedWriter(new FileWriter(
129 | debugLinksFilePath)));
130 | for (String extraTag : this.extraTags) {
131 | writers.put(extraTag, new BufferedWriter(new FileWriter(
132 | linksFilePathByExtraTags.get(extraTag))));
133 | }
134 |
135 | // Loop on the console log as long as there are input lines and parse
136 | // line by line
137 | // At the end of this loop, we will have:
138 | // - a parsed log with colored lines
139 | // - 4 links files which will be consolidated into one referencing html
140 | // file.
141 |
142 | // Create dummy header and section for beginning of log
143 | final String shortLink = " Beginning of log";
144 | LogParserWriter.writeHeaderTemplateToAllLinkFiles(writers, sectionCounter); // This enters a line which will later be
145 | // replaced by the actual header and count for
146 | // this header
147 | headerForSection.add(shortLink);
148 | writer.write(LogParserConsts.getHtmlOpeningTags());
149 |
150 | // write styles for log body
151 | final String styles = "\n";
156 | writer.write(styles);
157 |
158 | if (this.preformattedHtml)
159 | writer.write("
");
160 | // Read bulks of lines, parse
161 | parseLogBody(build, writer, log,
162 | logger);
163 |
164 | // Write parsed output, links, etc.
165 | //writeLogBody();
166 |
167 | // Close html footer
168 | if (this.preformattedHtml)
169 | writer.write("
");
170 | writer.write(LogParserConsts.getHtmlClosingTags());
171 | } finally {
172 | for (BufferedWriter writer : writers.values()) {
173 | writer.close();
174 | }
175 | }
176 |
177 | // Build the reference html from the warnings/errors/info html files
178 | // created in the loop above
179 | LogParserWriter.writeReferenceHtml(buildRefPath, headerForSection,
180 | statusCountPerSection, displayConstants.getIconTable(),
181 | displayConstants.getLinkListDisplay(),
182 | displayConstants.getLinkListDisplayPlural(), statusCount,
183 | linkFiles, extraTags);
184 | // Write the wrapping html for the reference page and the parsed log page
185 | LogParserWriter.writeWrapperHtml(buildWrapperPath);
186 |
187 | final String buildUrlPath = build.getUrl(); // job/cat_log/58
188 | final String buildActionPath = LogParserAction.getUrlNameStat(); // "parsed_console";
189 | final String parsedLogURL = buildUrlPath + buildActionPath + "/log.html";
190 |
191 | // Create result class
192 | final LogParserResult result = new LogParserResult();
193 | result.setHtmlLogFile(parsedFilePath);
194 | result.setTotalErrors(statusCount.get(LogParserConsts.ERROR));
195 | result.setTotalWarnings(statusCount.get(LogParserConsts.WARNING));
196 | result.setTotalInfos(statusCount.get(LogParserConsts.INFO));
197 | result.setTotalDebugs(statusCount.get(LogParserConsts.DEBUG));
198 | for (String extraTag : this.extraTags) {
199 | result.putTotalCountsByExtraTag(extraTag, statusCount.get(extraTag));
200 | }
201 | result.setErrorLinksFile(errorLinksFilePath);
202 | result.setWarningLinksFile(warningLinksFilePath);
203 | result.setInfoLinksFile(infoLinksFilePath);
204 | result.setDebugLinksFile(debugLinksFilePath);
205 | for (String extraTag : this.extraTags) {
206 | result.putLinksFileByExtraTag(extraTag, linksFilePathByExtraTags.get(extraTag));
207 | }
208 | result.setParsedLogURL(parsedLogURL);
209 | result.setHtmlLogPath(logDirectory);
210 | result.setBadParsingRulesError(this.compiledPatternsPlusError.getError());
211 | result.setExtraTags(this.extraTags);
212 |
213 | return result;
214 |
215 | }
216 |
217 | public String parseLine(final String line) throws IOException {
218 | return parseLine(line, null);
219 | }
220 |
221 | public static String convertEscapeSequencesToHtml(String text) {
222 | // Remove xterm color escape sequence with an empty space.
223 | return text.replaceAll("\u001B\\[(\\d{1,2})(;\\d{1,2})?(;\\d{1,2})?m", "");
224 | }
225 |
226 | public String parseLine(final String line, final String status)
227 | throws IOException {
228 | String parsedLine = line;
229 | String effectiveStatus = status;
230 | if (status == null) {
231 | effectiveStatus = LogParserConsts.NONE;
232 | } else if (status.equals(LogParserConsts.START)) {
233 | effectiveStatus = LogParserConsts.INFO;
234 | }
235 |
236 | // need to strip out for display also (in addition to parsing).
237 | parsedLine = ConsoleNote.removeNotes(parsedLine);
238 | // Allows < to be seen in log which is html
239 | parsedLine = parsedLine.replaceAll("<", "<");
240 | // Allows > to be seen in log which is html
241 | parsedLine = parsedLine.replaceAll(">", ">");
242 |
243 | // Remove xterm color escape sequence with an empty space.
244 | parsedLine = convertEscapeSequencesToHtml(parsedLine);
245 |
246 | if (effectiveStatus != null
247 | && !effectiveStatus.equals(LogParserConsts.NONE)) {
248 | // Increment count of the status
249 | incrementCounter(effectiveStatus);
250 | incrementCounterPerSection(effectiveStatus, sectionCounter);
251 | // Color line according to the status
252 | final String parsedLineColored = colorLine(parsedLine,
253 | effectiveStatus);
254 |
255 | // Mark line and add to left side links of highlighted lines
256 | final String parsedLineColoredAndMarked = addMarkerAndLink(
257 | parsedLineColored, effectiveStatus, status);
258 | parsedLine = parsedLineColoredAndMarked;
259 | }
260 | final StringBuffer result = new StringBuffer(parsedLine);
261 | if (!preformattedHtml)
262 | result.append(" \n");
263 | return result.toString();
264 | }
265 |
266 | public void incrementCounter(final String status) {
267 | final int currentVal = statusCount.get(status);
268 | statusCount.put(status, currentVal + 1);
269 | }
270 |
271 | public void incrementCounterPerSection(final String status,
272 | final int sectionNumber) {
273 | final String key = LogParserUtils.getSectionCountKey(status,
274 | sectionNumber);
275 | Integer currentValInteger = statusCountPerSection.get(key);
276 | // No value - entered yet - initialize with 0
277 | if (currentValInteger == null) {
278 | currentValInteger = 0;
279 | }
280 | final int newVal = currentValInteger + 1;
281 | statusCountPerSection.put(key, newVal);
282 | }
283 |
284 | private String colorLine(final String line, final String status) {
285 | String color = displayConstants.getColorTable().get(status);
286 | if (color == null) {
287 | color = LogParserDisplayConsts.DEFAULT_COLOR;
288 | }
289 | final StringBuffer result = new StringBuffer("");
294 | result.append(line);
295 | result.append("");
296 | return result.toString();
297 | }
298 |
299 | private String addMarkerAndLink(final String line,
300 | final String effectiveStatus, final String status)
301 | throws IOException {
302 | // Add marker
303 | final String statusCountStr = statusCount
304 | .get(effectiveStatus).toString();
305 | final String marker = effectiveStatus + statusCountStr;
306 |
307 | // Add link
308 | final StringBuffer shortLink = new StringBuffer(
309 | " ");
312 | shortLink.append(line);
313 | shortLink.append("");
314 |
315 | final StringBuffer link = new StringBuffer("
");
319 |
320 | final BufferedWriter linkWriter = (BufferedWriter) writers
321 | .get(effectiveStatus);
322 | linkWriter.write(link.toString());
323 | linkWriter.newLine(); // Write system dependent end of line.
324 |
325 | // Mark the line
326 | final StringBuffer markedLine = new StringBuffer("
");
329 | markedLine.append(line);
330 |
331 | // Handle case where we are entering a new section
332 | if (status.equals(LogParserConsts.START)) {
333 | sectionCounter++;
334 | // This enters a line which will later be replaced by the actual
335 | // header and count for this header
336 | LogParserWriter.writeHeaderTemplateToAllLinkFiles(writers, sectionCounter);
337 |
338 | final StringBuffer brShortLink = new StringBuffer(" ");
339 | brShortLink.append(shortLink);
340 | headerForSection.add(brShortLink.toString());
341 | }
342 |
343 | return markedLine.toString();
344 | }
345 |
346 | private void parseLogBody(final Run, ?> build, final BufferedWriter writer, final InputStream log,
347 | final Logger logger) throws IOException, InterruptedException {
348 |
349 | // Logging information - start
350 | final String signature = build.getParent().getName() + "_build_"
351 | + build.getNumber();
352 | logger.log(Level.INFO, "LogParserParser: Start parsing : " + signature);
353 | final Calendar calendarStart = Calendar.getInstance();
354 | Charset charset = build.getCharset();
355 |
356 | final HashMap lineStatusMatches = channel.call(
357 | new LogParserStatusComputer(log, parsingRulesArray, compiledPatterns, signature, charset));
358 |
359 | // Read log file from start - line by line and apply the statuses as
360 | // found by the threads.
361 | String query = "time=yyyy-MM-dd HH:MM:ss.SSS&appendLog";
362 | try (BufferedReader reader = TimestamperAPI.get().read(build, query)) {
363 | String line;
364 | String status;
365 | int line_num = 0;
366 | while ((line = reader.readLine()) != null) {
367 | status = lineStatusMatches.get(String.valueOf(line_num));
368 | final String parsedLine = parseLine(line, status);
369 | // This is for displaying sections in the links part
370 | writer.write(parsedLine);
371 | writer.newLine(); // Write system dependent end of line.
372 | line_num++;
373 | }
374 | }
375 |
376 | // Logging information - end
377 | final Calendar calendarEnd = Calendar.getInstance();
378 | final long diffSeconds = (calendarEnd.getTimeInMillis() - calendarStart
379 | .getTimeInMillis()) / 1000;
380 | final long diffMinutes = diffSeconds / 60;
381 | logger.log(Level.INFO, "LogParserParser: Parsing took " + diffMinutes
382 | + " minutes (" + diffSeconds + ") seconds.");
383 |
384 | }
385 |
386 | }
387 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserPublisher.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4 | import hudson.Extension;
5 | import hudson.FilePath;
6 | import hudson.Launcher;
7 | import hudson.Util;
8 | import hudson.model.AbstractProject;
9 | import hudson.model.Result;
10 | import hudson.model.Run;
11 | import hudson.model.TaskListener;
12 | import hudson.tasks.BuildStepDescriptor;
13 | import hudson.tasks.BuildStepMonitor;
14 | import hudson.tasks.Publisher;
15 | import hudson.tasks.Recorder;
16 | import hudson.util.ListBoxModel;
17 | import jenkins.model.Jenkins;
18 | import jenkins.tasks.SimpleBuildStep;
19 | import net.sf.json.JSONObject;
20 | import org.jenkinsci.Symbol;
21 | import org.kohsuke.stapler.DataBoundConstructor;
22 | import org.kohsuke.stapler.DataBoundSetter;
23 | import org.kohsuke.stapler.StaplerRequest;
24 |
25 | import java.io.File;
26 | import java.io.IOException;
27 | import java.io.Serializable;
28 | import java.util.ArrayList;
29 | import java.util.List;
30 | import java.util.logging.Level;
31 | import java.util.logging.Logger;
32 |
33 | public class LogParserPublisher extends Recorder implements SimpleBuildStep, Serializable {
34 | static final String NULL_PARSING_RULES = "Path to global parsing rules is null";
35 | private static final long serialVersionUID = 1L;
36 | public boolean unstableOnWarning;
37 | public boolean failBuildOnError;
38 | public boolean showGraphs;
39 | public String parsingRulesPath = null;
40 | public boolean useProjectRule;
41 | public String projectRulePath = null;
42 |
43 | /**
44 | * Create new LogParserPublisher.
45 | *
46 | * @param unstableOnWarning mark build unstable if warnings found.
47 | * @param failBuildOnError mark build failed if errors found.
48 | * @param showGraphs show graphs on job page.
49 | * @param parsingRulesPath path to the global parsing rules.
50 | * @param useProjectRule true if we use a project specific rule.
51 | * @param projectRulePath path to project specific rules relative to
52 | * workspace root.
53 | */
54 | @Deprecated
55 | @SuppressFBWarnings(value = "PA_PUBLIC_PRIMITIVE_ATTRIBUTE", justification = "Backwards compatibility")
56 | private LogParserPublisher(final boolean unstableOnWarning,
57 | final boolean failBuildOnError, final boolean showGraphs,
58 | final String parsingRulesPath, final boolean useProjectRule,
59 | final String projectRulePath) {
60 |
61 | this.unstableOnWarning = unstableOnWarning;
62 | this.failBuildOnError = failBuildOnError;
63 | this.showGraphs = showGraphs;
64 | this.parsingRulesPath = parsingRulesPath;
65 | this.useProjectRule = useProjectRule;
66 | this.projectRulePath = projectRulePath;
67 | }
68 |
69 | @DataBoundConstructor
70 | public LogParserPublisher(boolean useProjectRule, String projectRulePath, String parsingRulesPath) {
71 | super();
72 | if (useProjectRule) {
73 | this.projectRulePath = Util.fixEmpty(projectRulePath);
74 | this.parsingRulesPath = null;
75 | } else {
76 | this.parsingRulesPath = Util.fixEmpty(parsingRulesPath);
77 | this.projectRulePath = null;
78 | }
79 | this.useProjectRule = useProjectRule;
80 | }
81 |
82 | @DataBoundSetter
83 | public void setUnstableOnWarning(boolean unstableOnWarning) {
84 | this.unstableOnWarning = unstableOnWarning;
85 | }
86 |
87 | @DataBoundSetter
88 | public void setFailBuildOnError(boolean failBuildOnError) {
89 | this.failBuildOnError = failBuildOnError;
90 | }
91 |
92 | @DataBoundSetter
93 | public void setShowGraphs(boolean showGraphs) {
94 | this.showGraphs = showGraphs;
95 | }
96 |
97 | @Override
98 | public void perform(Run, ?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws
99 | InterruptedException, IOException {
100 |
101 | final Logger logger = Logger.getLogger(getClass().getName());
102 | LogParserResult result = new LogParserResult();
103 | try {
104 | // Create a parser with the parsing rules as configured : colors, regular expressions, etc.
105 | boolean preformattedHtml = !((DescriptorImpl) getDescriptor()).getLegacyFormatting();
106 | final FilePath parsingRulesFile;
107 | if (useProjectRule) {
108 | parsingRulesFile = new FilePath(workspace, projectRulePath);
109 | } else if (parsingRulesPath == null) {
110 | logger.log(Level.SEVERE, LogParserConsts.CANNOT_PARSE + build, NULL_PARSING_RULES);
111 | result.setFailedToParseError(NULL_PARSING_RULES);
112 | build.setResult(Result.ABORTED);
113 | build.addAction(new LogParserAction(build, result, showGraphs));
114 | return;
115 | } else {
116 | parsingRulesFile = new FilePath(new File(parsingRulesPath));
117 | }
118 | final LogParserParser parser = new LogParserParser(parsingRulesFile, preformattedHtml, launcher.getChannel());
119 | // Parse the build's log according to these rules and get the result
120 | result = parser.parseLog(build);
121 |
122 | // Mark build as failed/unstable if necessary
123 | if (this.failBuildOnError && result.getTotalErrors() > 0) {
124 | build.setResult(Result.FAILURE);
125 | } else if (this.unstableOnWarning && result.getTotalWarnings() > 0) {
126 | build.setResult(Result.UNSTABLE);
127 | }
128 | } catch (IOException e) {
129 | // Failure to parse should not fail the build - but should be
130 | // handled as a serious error.
131 | // This should catch all process problems during parsing, including
132 | // parser file not found..
133 | logger.log(Level.SEVERE, LogParserConsts.CANNOT_PARSE + build, e);
134 | result.setFailedToParseError(e.toString());
135 | } catch (InterruptedException e) {
136 | logger.log(Level.SEVERE, LogParserConsts.CANNOT_PARSE + build, e);
137 | result.setFailedToParseError(e.toString());
138 | build.setResult(Result.ABORTED);
139 | }
140 |
141 | // Add an action created with the above results
142 | final LogParserAction action = new LogParserAction(build, result, showGraphs);
143 | build.addAction(action);
144 | }
145 |
146 | @Override
147 | public BuildStepDescriptor getDescriptor() {
148 | return Jenkins.get().getDescriptorByType(LogParserPublisher.DescriptorImpl.class);
149 | }
150 |
151 | @Extension @Symbol("logParser")
152 | public static final class DescriptorImpl extends
153 | BuildStepDescriptor {
154 |
155 | private List parsingRulesGlobal = new ArrayList<>();
156 | private boolean useLegacyFormatting = false;
157 |
158 | public DescriptorImpl() {
159 | super(LogParserPublisher.class);
160 | load();
161 | }
162 |
163 | @Override
164 | public String getDisplayName() {
165 | return "Console output (build log) parsing";
166 | }
167 |
168 | @Override
169 | public String getHelpFile() {
170 | return "/plugin/log-parser/help.html";
171 | }
172 |
173 | @Override
174 | public boolean isApplicable(
175 | final Class extends AbstractProject> jobType) {
176 | return true;
177 | }
178 |
179 | public List getParsingRulesGlobal() {
180 | return parsingRulesGlobal;
181 | }
182 |
183 | @DataBoundSetter
184 | public void setParsingRulesGlobal(List parsingRulesChoices) {
185 | this.parsingRulesGlobal = parsingRulesChoices;
186 | }
187 |
188 | public boolean getLegacyFormatting() {
189 | return useLegacyFormatting;
190 | }
191 |
192 | @DataBoundSetter
193 | public void setLegacyFormatting(boolean useLegacyFormatting) {
194 | this.useLegacyFormatting = useLegacyFormatting;
195 | }
196 |
197 | @Override
198 | public boolean configure(final StaplerRequest req, final JSONObject json)
199 | throws FormException {
200 | useLegacyFormatting = false;
201 | parsingRulesGlobal = new ArrayList<>();
202 | req.bindJSON(this, json);
203 | save();
204 | return true;
205 | }
206 |
207 | public ListBoxModel doFillParsingRulesPathItems() {
208 | ListBoxModel items = new ListBoxModel();
209 | for (ParserRuleFile file : parsingRulesGlobal) {
210 | items.add(file.getName(), file.getPath());
211 | }
212 | return items;
213 | }
214 | }
215 |
216 | public BuildStepMonitor getRequiredMonitorService() {
217 | return BuildStepMonitor.NONE;
218 | }
219 |
220 | /*
221 | * This is read by the config.jelly : ${instance.parserRuleChoices} and
222 | * displays the available choices of parsing rules which were configured in
223 | * the global configurations
224 | */
225 | public List getParserRuleChoices() {
226 | // Get the descriptor which holds the global configurations and extract
227 | // the available parsing rules from there
228 | return ((DescriptorImpl) this.getDescriptor()).getParsingRulesGlobal();
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserReader.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.util.logging.Level;
6 | import java.util.logging.Logger;
7 |
8 | public class LogParserReader {
9 |
10 | final private BufferedReader reader;
11 | int logPartNum = 0;
12 | boolean endOfFile = false;
13 |
14 | public LogParserReader(final BufferedReader reader) {
15 | this.reader = reader;
16 | }
17 |
18 | public synchronized LogParserLogPart readLogPart(final int threadNum) throws IOException {
19 | final Logger logger = Logger.getLogger(this.getClass().getName());
20 | logger.log(Level.INFO, "Start reading log part " + logPartNum + " in thread #" + threadNum);
21 | final int numLines = LogParserUtils.getLinesPerThread();
22 | String[] lines = new String[numLines];
23 | final LogParserLogPart result = new LogParserLogPart();
24 |
25 | int counter = 0;
26 | String line;
27 | while (counter < numLines && (line = reader.readLine()) != null) {
28 | lines[counter++] = line;
29 | }
30 | logger.log(Level.INFO, "Done reading log part " + logPartNum);
31 | result.setLines(lines);
32 | result.setLogPartNum(logPartNum);
33 |
34 | if (result.isEmpty()) {
35 | this.endOfFile = true;
36 | }
37 | logPartNum++; // increment counter for next call of method by another thread
38 |
39 | return result;
40 | }
41 |
42 | public boolean isEndOfFile() {
43 | return endOfFile;
44 | }
45 |
46 | public void setEndOfFile(final boolean endOfFile) {
47 | this.endOfFile = endOfFile;
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserResult.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import java.io.File;
4 | import java.io.FileReader;
5 | import java.io.IOException;
6 | import java.io.RandomAccessFile;
7 | import java.io.Reader;
8 | import java.util.Collection;
9 | import java.util.HashMap;
10 | import java.util.HashSet;
11 | import java.util.Map;
12 | import java.util.Set;
13 |
14 | public class LogParserResult {
15 |
16 | private int totalErrors = 0;
17 | private int totalWarnings = 0;
18 | private int totalInfos = 0;
19 | private int totalDebugs = 0;
20 | private final Map totalCountsByExtraTag = new HashMap<>();
21 |
22 | private String htmlLogFile;
23 | private String errorLinksFile;
24 | private String warningLinksFile;
25 | private String infoLinksFile;
26 | private String debugLinksFile;
27 | private final Map linkedFilesByExtraTag = new HashMap<>();
28 | private Set extraTags = new HashSet<>();
29 |
30 | private String parsedLogURL;
31 | private String htmlLogPath;
32 |
33 | private String failedToParseError;
34 | private String badParsingRulesError;
35 |
36 | protected Object readResolve() {
37 | if (extraTags == null) { // avoid NPE when deserializing old results
38 | extraTags = new HashSet<>();
39 | }
40 | return this;
41 | }
42 |
43 | public String getBadParsingRulesError() {
44 | return badParsingRulesError;
45 | }
46 |
47 | public String getBadParsingRulesErrorDisplay() {
48 | return badParsingRulesError.replaceAll("\n", " ");
49 | }
50 |
51 | public void setBadParsingRulesError(final String badParsingRulesError) {
52 | this.badParsingRulesError = badParsingRulesError;
53 | }
54 |
55 | public String getFailedToParseError() {
56 | return failedToParseError;
57 | }
58 |
59 | public void setFailedToParseError(final String failedToParseError) {
60 | this.failedToParseError = failedToParseError;
61 | }
62 |
63 | public int getTotalErrors() {
64 | return totalErrors;
65 | }
66 |
67 | public int getTotalWarnings() {
68 | return totalWarnings;
69 | }
70 |
71 | public int getTotalInfos() {
72 | return totalInfos;
73 | }
74 |
75 | public int getTotalDebugs() {
76 | return totalDebugs;
77 | }
78 |
79 | public int getTotalCountsByExtraTag(String tag) {
80 | return totalCountsByExtraTag.get(tag);
81 | }
82 |
83 | public String getHtmlLogFile() {
84 | return htmlLogFile;
85 | }
86 |
87 | public String getHtmlLogPath() {
88 | return htmlLogPath;
89 | }
90 |
91 | public String getErrorLinksFile() {
92 | return errorLinksFile;
93 | }
94 |
95 | public String getWarningLinksFile() {
96 | return warningLinksFile;
97 | }
98 |
99 | public String getInfoLinksFile() {
100 | return infoLinksFile;
101 | }
102 |
103 | public String getDebugLinksFile() {
104 | return debugLinksFile;
105 | }
106 |
107 | public String getLinksFileByExtraTag(String tag) {
108 | return linkedFilesByExtraTag.get(tag);
109 | }
110 |
111 | public String getParsedLogURL() {
112 | return parsedLogURL;
113 | }
114 |
115 | public Reader getReader(final String filePath) throws IOException {
116 | final File logFile = new File(filePath);
117 | if (logFile.exists()) {
118 | return new FileReader(logFile);
119 | }
120 | return null;
121 | }
122 |
123 | public Reader getLogReader() throws IOException {
124 | return getReader(getHtmlLogFile());
125 | }
126 |
127 | public Reader getErrorLinksReader() throws IOException {
128 | return getReader(getErrorLinksFile());
129 | }
130 |
131 | public Reader getWarningLinksReader() throws IOException {
132 | return getReader(getWarningLinksFile());
133 | }
134 |
135 | public Reader getInfoLinksReader() throws IOException {
136 | return getReader(getInfoLinksFile());
137 | }
138 |
139 | public Reader getDebugLinkedReader() throws IOException {
140 | return getReader(getDebugLinksFile());
141 | }
142 |
143 | public Reader getLinkedReaderByExtraTag(String tag) throws IOException {
144 | return getReader(getLinksFileByExtraTag(tag));
145 | }
146 |
147 | public void setHtmlLogFile(final String file) {
148 | this.htmlLogFile = file;
149 | }
150 |
151 | public void setHtmlLogPath(final String dir) {
152 | this.htmlLogPath = dir;
153 | }
154 |
155 | public void setErrorLinksFile(final String file) {
156 | this.errorLinksFile = file;
157 | }
158 |
159 | public void setWarningLinksFile(final String file) {
160 | this.warningLinksFile = file;
161 | }
162 |
163 | public void setInfoLinksFile(final String file) {
164 | this.infoLinksFile = file;
165 | }
166 |
167 | public void setDebugLinksFile(final String file) {
168 | this.debugLinksFile = file;
169 | }
170 |
171 | public void putLinksFileByExtraTag(final String tag, final String file) {
172 | this.linkedFilesByExtraTag.put(tag, file);
173 | }
174 |
175 | public void setTotalErrors(final int totalErrors) {
176 | this.totalErrors = totalErrors;
177 | }
178 |
179 | public void setTotalWarnings(final int totalWarnings) {
180 | this.totalWarnings = totalWarnings;
181 | }
182 |
183 | public void setTotalInfos(final int totalInfos) {
184 | this.totalInfos = totalInfos;
185 | }
186 |
187 | public void setTotalDebugs(final int totalDebugs) {
188 | this.totalDebugs = totalDebugs;
189 | }
190 |
191 | public void putTotalCountsByExtraTag(final String tag, final int totalCounts) {
192 | this.totalCountsByExtraTag.put(tag, totalCounts);
193 | }
194 |
195 | public void setParsedLogURL(final String parsedLogURL) {
196 | this.parsedLogURL = parsedLogURL;
197 | }
198 |
199 | public File getHtmlLogFileToRead() {
200 | return new File(this.htmlLogFile);
201 | }
202 |
203 | public void setExtraTags(Collection extraTags) {
204 | this.extraTags.addAll(extraTags);
205 | }
206 |
207 | public Set getExtraTags() {
208 | return this.extraTags;
209 | }
210 |
211 | public String getHtmlContent() {
212 | final StringBuffer result = new StringBuffer("");
213 | String line = "";
214 | try {
215 | File file = null;
216 | RandomAccessFile f = null;
217 | try {
218 | file = this.getHtmlLogFileToRead();
219 | f = new RandomAccessFile(file, "r");
220 |
221 | while ((line = f.readLine()) != null) {
222 | result.append(line);
223 | result.append(" ");
224 | }
225 | } finally {
226 | f.close();
227 | }
228 |
229 | } catch (Exception e) {
230 | e.printStackTrace();
231 | }
232 | return result.toString();
233 | }
234 |
235 | }
236 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserStatusComputer.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import hudson.remoting.RemoteInputStream;
4 | import jenkins.security.MasterToSlaveCallable;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.nio.charset.Charset;
9 | import java.util.HashMap;
10 | import java.util.regex.Pattern;
11 |
12 | public class LogParserStatusComputer extends MasterToSlaveCallable, RuntimeException> {
13 |
14 | private static final long serialVersionUID = 646211554890510833L;
15 | final private String[] parsingRulesArray;
16 | final private Pattern[] compiledPatterns;
17 | private final InputStream remoteLog;
18 | private final String signature;
19 | private final String charsetName;
20 |
21 | public LogParserStatusComputer(
22 | final InputStream log,
23 | final String[] parsingRulesArray,
24 | final Pattern[] compiledPatterns,
25 | final String signature,
26 | final Charset charset) {
27 | this.parsingRulesArray = parsingRulesArray;
28 | this.compiledPatterns = compiledPatterns;
29 | this.remoteLog = new RemoteInputStream(log, RemoteInputStream.Flag.GREEDY);
30 | this.signature = signature;
31 | this.charsetName = charset.name();
32 | }
33 |
34 | /**
35 | * Prefer the other constructor that allows for passing in the {@link Charset}.
36 | * This constructor relies on the default charset.
37 | * @param log
38 | * @param parsingRulesArray
39 | * @param compiledPatterns
40 | * @param signature
41 | * @throws IOException
42 | * @throws InterruptedException
43 | */
44 | @Deprecated
45 | public LogParserStatusComputer(
46 | final InputStream log, final String[] parsingRulesArray,
47 | final Pattern[] compiledPatterns,
48 | final String signature) throws IOException, InterruptedException {
49 | this(log, parsingRulesArray, compiledPatterns, signature, Charset.defaultCharset());
50 | }
51 |
52 | public HashMap call() {
53 | try {
54 | return computeStatusMatches(remoteLog, signature, charsetName);
55 | // rethrow any exception here to report why the
56 | // parsing failed
57 | } catch (InterruptedException e) {
58 | throw new RuntimeException(e);
59 | } catch (IOException e) {
60 | throw new RuntimeException(e);
61 | }
62 | }
63 |
64 | private HashMap computeStatusMatches(
65 | final InputStream log,
66 | final String signature,
67 | final String charsetName) throws IOException, InterruptedException {
68 | // SLAVE PART START
69 | ParsingStrategyLocator locator = ParsingStrategyLocator.create();
70 | ParsingStrategy strategy = locator.get();
71 |
72 | ParsingInput input = new ParsingInput(parsingRulesArray, compiledPatterns, log, signature, charsetName);
73 | return strategy.parse(input);
74 | // SLAVE PART END
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserThread.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import java.io.IOException;
4 | import java.util.LinkedList;
5 | import java.util.List;
6 | import java.util.logging.Level;
7 | import java.util.logging.Logger;
8 | import java.util.regex.Pattern;
9 |
10 | class LogParserThread extends Thread {
11 |
12 | private LogParserLogPart logPart;
13 | private final String[] parsingRulesArray;
14 | private final Pattern[] compiledPatterns;
15 | private final int threadNum;
16 | private String[] logPartStatuses;
17 | private int numOfLines;
18 | private final LogParserReader logParserReader;
19 |
20 | public LogParserThread(final LogParserReader logParserReader,
21 | final String[] parsingRulesArray, final Pattern[] compiledPatterns,
22 | final int threadNum) {
23 | this.parsingRulesArray = parsingRulesArray;
24 | this.compiledPatterns = compiledPatterns;
25 | this.threadNum = threadNum;
26 | this.logParserReader = logParserReader;
27 | }
28 |
29 | @Override
30 | public void run() {
31 | try {
32 | // Synchronized method so as not to read from the same file from
33 | // several threads.
34 | logPart = logParserReader.readLogPart(this.threadNum);
35 | } catch (IOException ignored) {
36 | }
37 | logPartStatuses = getLineStatuses(logPart.getLines());
38 |
39 | }
40 |
41 | public String[] getLineStatuses() {
42 | return this.logPartStatuses;
43 | }
44 |
45 | public LogParserLogPart getLogPart() {
46 | return this.logPart;
47 | }
48 |
49 | public int getNumOfLines() {
50 | return this.numOfLines;
51 | }
52 |
53 | private String[] getLineStatuses(final String[] logPart) {
54 |
55 | final Logger logger = Logger.getLogger(this.getClass().getName());
56 | logger.log(Level.INFO, "LogParserThread: Start parsing log part "
57 | + this.logPart.getLogPartNum());
58 |
59 | numOfLines = 0;
60 | String[] result = new String[logPart.length];
61 | for (int i = 0; i < logPart.length; i++) {
62 | final String line = logPart[i];
63 | if (line == null) {
64 | continue;
65 | }
66 | numOfLines++;
67 | final String status = getLineStatus(line);
68 | result[i] = status;
69 | }
70 |
71 | logger.log(Level.INFO, "LogParserThread: Done parsing log part "
72 | + this.logPart.getLogPartNum());
73 |
74 | return result;
75 | }
76 |
77 | private String getLineStatus(String line) {
78 | List patterns = new LinkedList<>();
79 | for (int i = 0; i < parsingRulesArray.length; i++) {
80 | String rule = parsingRulesArray[i];
81 | Pattern pattern = compiledPatterns[i];
82 | patterns.add(new ParsingRulePattern(rule, pattern));
83 | }
84 | LineToStatus toStatus = new LineToStatus(patterns);
85 | return toStatus.apply(line);
86 | }
87 |
88 | public int getThreadNum() {
89 | return threadNum;
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserUtils.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import hudson.FilePath;
4 |
5 | import java.io.FileReader;
6 | import java.io.IOException;
7 | import java.io.LineNumberReader;
8 | import java.util.ArrayList;
9 | import java.util.Arrays;
10 | import java.util.List;
11 | import java.util.Locale;
12 | import java.util.logging.Level;
13 | import java.util.logging.Logger;
14 | import java.util.regex.Pattern;
15 |
16 | public final class LogParserUtils {
17 |
18 | public static String[] readParsingRules(final FilePath parsingRulesFile) throws IOException {
19 | try {
20 | return parsingRulesFile.readToString().split("\n");
21 | } catch (InterruptedException ie) {
22 | throw new IOException(ie);
23 | }
24 | }
25 |
26 | public static boolean skipParsingRule(final String parsingRule) {
27 | return parsingRule == null || parsingRule.equals("")
28 | || parsingRule.charAt(0) == '#'
29 | || parsingRule.startsWith("\\s")
30 | || parsingRule.startsWith("\r") // Carriage return
31 | || parsingRule.contains("$header"); // for now - disregard rules
32 | // with header in them
33 | }
34 |
35 | public static String standardizeStatus(final String status) {
36 | String result = status;
37 | if (result.equalsIgnoreCase("ok")) {
38 | result = LogParserConsts.NONE;
39 | } else if (result.equalsIgnoreCase("end")) {
40 | result = LogParserConsts.INFO;
41 | } else if (result.equalsIgnoreCase("warn")
42 | || result.equalsIgnoreCase("end")) {
43 | result = LogParserConsts.WARNING;
44 | } else if (LogParserConsts.LEGAL_STATUS.contains(result.toUpperCase(Locale.ENGLISH))) {
45 | result = result.toUpperCase(Locale.ENGLISH);
46 | }
47 |
48 | // If some non-existent status is in the configuration - disregard it
49 | // Arbitrary tag: this condition check is commented out so that arbitrary status can come through
50 |
51 | //final List legals = LogParserConsts.LEGAL_STATUS;
52 | //if (!legals.contains(result)) {
53 | // result = LogParserConsts.DEFAULT;
54 | //}
55 |
56 | return result;
57 | }
58 |
59 | public static CompiledPatterns compilePatterns(
60 | final String[] parsingRulesArray, final Logger logger) {
61 |
62 | Pattern[] result = new Pattern[parsingRulesArray.length];
63 | final StringBuffer badParsingRules = new StringBuffer();
64 | List extraTags = new ArrayList<>();
65 |
66 | for (int i = 0; i < parsingRulesArray.length; i++) {
67 | final String parsingRule = parsingRulesArray[i];
68 | result[i] = null;
69 | if (!skipParsingRule(parsingRule)) {
70 | try {
71 | final String ruleParts[] = parsingRule.split("\\s");
72 | String regexp = ruleParts[1];
73 | String tag = ruleParts[0];
74 | if (!LogParserConsts.LEGAL_STATUS.contains(tag.toUpperCase(Locale.ENGLISH))
75 | && !Arrays.asList("OK", "END", "WARN").contains(tag.toUpperCase(Locale.ENGLISH))) {
76 | extraTags.add(tag);
77 | }
78 |
79 | final int firstDash = parsingRule.indexOf('/');
80 | final int lastDash = parsingRule.lastIndexOf('/');
81 | if (firstDash != -1 && lastDash != -1) {
82 | regexp = parsingRule.substring(firstDash + 1, lastDash);
83 | final Pattern p = Pattern.compile(regexp);
84 | result[i] = p;
85 |
86 | }
87 | } catch (Exception e) {
88 | // Could not use rule for some reason - ignore rule and log it
89 | final String errorMsg = "Bad parsing rule:" + parsingRule
90 | + ", Error:" + e.getMessage();
91 | logger.log(Level.SEVERE, errorMsg);
92 | badParsingRules.append('\n');
93 | badParsingRules.append(errorMsg);
94 | }
95 | }
96 | }
97 |
98 | final CompiledPatterns fullResult = new CompiledPatterns();
99 | fullResult.setCompiledPatters(result);
100 | fullResult.setError(badParsingRules.toString());
101 | fullResult.setExtraTags(extraTags);
102 | return fullResult;
103 | }
104 |
105 | public static String getSectionCountKey(final String status,
106 | final int sectionNumber) {
107 | return sectionNumber + "-" + status;
108 | }
109 |
110 | public static int getNumThreads() {
111 | int result = LogParserConsts.MAX_THREADS;
112 | final String maxThreadsByEnvStr = System
113 | .getenv("HUDSON_LOG_PARSER_THREADS");
114 | if (maxThreadsByEnvStr != null) {
115 | try {
116 | result = Integer.parseInt(maxThreadsByEnvStr);
117 | } catch (Exception e) {
118 | // Do nothing - use the default;
119 | Logger.getLogger("getNumThreads").log(Level.FINEST,
120 | "HUDSON_LOG_PARSER_THREADS" + LogParserConsts.NOT_INT);
121 | }
122 | }
123 | return result;
124 | }
125 |
126 | public static int getLinesPerThread() {
127 | int result = LogParserConsts.LINES_PER_THREAD;
128 | final String linesByEnvStr = System
129 | .getenv("HUDSON_LOG_PARSER_LINES_PER_THREAD");
130 | if (linesByEnvStr != null) {
131 | try {
132 | result = Integer.parseInt(linesByEnvStr);
133 | } catch (Exception e) {
134 | // Do nothing - use the default;
135 | Logger.getLogger("getLinesPerThread").log(Level.FINEST,
136 | "HUDSON_LOG_PARSER_LINES_PER_THREAD" + LogParserConsts.NOT_INT);
137 | }
138 | }
139 | return result;
140 | }
141 |
142 | public static int countLines(final String filename) throws IOException {
143 | try (LineNumberReader reader = new LineNumberReader(new FileReader(filename))) {
144 | int count = 0;
145 | while (reader.readLine() != null) {
146 | // Read the whole file to count the lines.
147 | count++;
148 | }
149 | count = reader.getLineNumber();
150 | return count;
151 | }
152 | }
153 |
154 | private LogParserUtils() {
155 | // PMD warning to use singleton or bypass by private empty constructor
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/src/main/java/hudson/plugins/logparser/LogParserWriter.java:
--------------------------------------------------------------------------------
1 | package hudson.plugins.logparser;
2 |
3 | import jenkins.model.Jenkins;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.BufferedWriter;
7 | import java.io.FileReader;
8 | import java.io.FileWriter;
9 | import java.io.IOException;
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 |
14 | public final class LogParserWriter {
15 |
16 | public static void writeHeaderTemplateToAllLinkFiles(
17 | final HashMap writers,
18 | final int sectionCounter) throws IOException {
19 | final List statuses = LogParserConsts.STATUSES_WITH_SECTIONS_IN_LINK_FILES;
20 | for (String status : statuses) {
21 | final BufferedWriter linkWriter = writers.get(status);
22 | String str = "HEADER HERE: #" + sectionCounter;
23 | linkWriter.write(str + "\n");
24 | }
25 |
26 | }
27 |
28 | public static void writeWrapperHtml(final String buildWrapperPath)
29 | throws IOException {
30 | final String wrapperHtml = "\n";
37 |
38 | try (BufferedWriter writer = new BufferedWriter(new FileWriter(buildWrapperPath))) {
39 | writer.write(wrapperHtml);
40 | }
41 | }
42 |
43 | public static void writeReferenceHtml(final String buildRefPath,
44 | final ArrayList headerForSection,
45 | final HashMap statusCountPerSection,
46 | final HashMap iconTable,
47 | final HashMap linkListDisplay,
48 | final HashMap linkListDisplayPlural,
49 | final HashMap statusCount,
50 | final HashMap linkFiles,
51 | final List extraTags) throws IOException {
52 |
53 | try (BufferedWriter writer = new BufferedWriter(new FileWriter(buildRefPath))) {
54 | // Hudson stylesheets
55 | writer.write(LogParserConsts.getHtmlOpeningTags());
56 | // Write Errors
57 | writeLinks(writer, LogParserConsts.ERROR, headerForSection,
58 | statusCountPerSection, iconTable, linkListDisplay,
59 | linkListDisplayPlural, statusCount, linkFiles);
60 | // Write Warnings
61 | writeLinks(writer, LogParserConsts.WARNING, headerForSection,
62 | statusCountPerSection, iconTable, linkListDisplay,
63 | linkListDisplayPlural, statusCount, linkFiles);
64 | // Write Infos
65 | writeLinks(writer, LogParserConsts.INFO, headerForSection,
66 | statusCountPerSection, iconTable, linkListDisplay,
67 | linkListDisplayPlural, statusCount, linkFiles);
68 | // Write Debugs
69 | writeLinks(writer, LogParserConsts.DEBUG, headerForSection,
70 | statusCountPerSection, iconTable, linkListDisplay,
71 | linkListDisplayPlural, statusCount, linkFiles);
72 | // Write extra tags
73 | for (String extraTag : extraTags) {
74 | writeLinks(writer, extraTag, headerForSection,
75 | statusCountPerSection, iconTable, linkListDisplay,
76 | linkListDisplayPlural, statusCount, linkFiles);
77 | }
78 | writer.write(LogParserConsts.getHtmlClosingTags());
79 | }
80 | }
81 |
82 | private static void writeLinks(final BufferedWriter writer,
83 | final String status, final ArrayList headerForSection,
84 | final HashMap statusCountPerSection,
85 | final HashMap iconTable,
86 | final HashMap linkListDisplay,
87 | final HashMap linkListDisplayPlural,
88 | final HashMap statusCount,
89 | final HashMap linkFiles) throws IOException {
90 | String statusIcon = iconTable.get(status);
91 | if (statusIcon == null) {
92 | statusIcon = LogParserDisplayConsts.DEFAULT_ICON;
93 | }
94 | String linkListDisplayStr = linkListDisplay.get(status);
95 | if (linkListDisplayStr == null) {
96 | linkListDisplayStr = LogParserDisplayConsts.getDefaultLinkListDisplay(status);
97 | }
98 | String linkListDisplayStrPlural = linkListDisplayPlural.get(status);
99 | if (linkListDisplayStrPlural == null) {
100 | linkListDisplayStrPlural = LogParserDisplayConsts.getDefaultLinkListDisplayPlural(status);
101 | }
102 | final String linkListCount = statusCount.get(status).toString();
103 |
104 | final String hudsonRoot = Jenkins.get().getRootUrl();
105 | final String iconLocation = String.format("%s/plugin/log-parser/images/", Jenkins.RESOURCE_PATH).substring(1);
106 |
107 | final String styles =
108 | "\n";
115 | writer.write(styles);
116 |
117 | final String linksStart = "\n"
119 | + ""
120 | + linkListDisplayStr + " (" + linkListCount + ") \n"
121 | + "
\n";
123 | writer.write(linksStart);
124 |
125 | // Read the links file and insert here
126 | try (BufferedReader reader = new BufferedReader(new FileReader(linkFiles.get(status)))) {
127 | final String summaryLine = " (SUMMARY_INT_HERE LINK_LIST_DISPLAY_STR in this section) ";
128 |
129 | final String headerTemplateRegexp = "HEADER HERE:";
130 | final String headerTemplateSplitBy = "#";
131 |
132 | // If it's a header line - put the header of the section
133 | String line;
134 | while ((line = reader.readLine()) != null) {
135 | String curSummaryLine = null;
136 | if (line.startsWith(headerTemplateRegexp)) {
137 | final int headerNum = Integer.parseInt(line.split(headerTemplateSplitBy)[1]);
138 | line = headerForSection.get(headerNum);
139 | final String key = LogParserUtils.getSectionCountKey(status, headerNum);
140 | final Integer summaryInt = statusCountPerSection.get(key);
141 | if (summaryInt == null || summaryInt == 0) {
142 | // Don't write the header if there are no relevant lines for
143 | // this section
144 | line = null;
145 | } else {
146 | String linkListDisplayStrWithPlural = linkListDisplayStr;
147 | if (summaryInt > 1) {
148 | linkListDisplayStrWithPlural = linkListDisplayStrPlural;
149 | }
150 | curSummaryLine = summaryLine.replace("SUMMARY_INT_HERE",
151 | summaryInt.toString()).replace(
152 | "LINK_LIST_DISPLAY_STR",
153 | linkListDisplayStrWithPlural);
154 | }
155 |
156 | }
157 |
158 | if (line != null) {
159 | writer.write(line);
160 | writer.newLine(); // Write system dependent end of line.
161 | }
162 | if (curSummaryLine != null) {
163 | writer.write(curSummaryLine);
164 | writer.newLine(); // Write system dependent end of line.
165 | }
166 | }
167 | }
168 |
169 | final String linksEnd = "
11 | * The default strategy is {@link ClassicParsingStrategy}, which was the only available strategy through v2.3.0.
12 | * This class logs the strategy selected at INFO level.
13 | *
14 | * An invalid parameter is logged at WARNING and falls back to {@link ClassicParsingStrategy}.
15 | *
16 | * @since 2.4.0
17 | */
18 | class ParsingStrategyLocator {
19 | private static final String SYSTEM_PROPERTY = ParsingStrategy.class.getName();
20 | private static final String CLASSIC = ClassicParsingStrategy.class.getName();
21 | private static final String STREAM = StreamParsingStrategy.class.getName();
22 | private static final Logger LOGGER = Logger.getLogger(ParsingStrategyLocator.class.getName());
23 | private final Map systemProperties;
24 |
25 | ParsingStrategyLocator(Map systemProperties) {
26 | this.systemProperties = systemProperties;
27 | }
28 |
29 | static ParsingStrategyLocator create() {
30 | Map typedProperties = new HashMap<>();
31 | Properties properties = System.getProperties();
32 | for (Map.Entry