pax_global_header00006660000000000000000000000064151652522320014515gustar00rootroot0000000000000052 comment=759f04b4f566e182568512f5b9a1153fd389a1cd anchore-go-logger-1fa893f/000077500000000000000000000000001516525223200154375ustar00rootroot00000000000000anchore-go-logger-1fa893f/.binny.yaml000066400000000000000000000027141516525223200175240ustar00rootroot00000000000000# only pull in version updates that were released more than a week ago (low-pass filter for quickly-retracted releases) cooldown: 7d tools: # we want to use a pinned version of binny to manage the toolchain (so binny manages itself!) - name: binny version: want: v0.13.0 method: github-release with: repo: anchore/binny # used at release to generate the changelog - name: chronicle version: want: v0.8.3 method: github-release with: repo: anchore/chronicle # used for linting - name: golangci-lint version: want: v2.11.4 method: github-release with: repo: golangci/golangci-lint # used for organizing imports during static analysis - name: gosimports version: want: v0.3.8 method: github-release with: repo: rinchsan/gosimports # used during static analysis for license compliance - name: bouncer version: want: v0.4.0 method: github-release with: repo: wagoodman/go-bouncer # used for showing the changelog at release - name: glow version: want: v2.1.1 method: github-release with: repo: charmbracelet/glow # used for running all local and CI tasks - name: task version: want: v3.49.1 method: github-release with: repo: go-task/task # used for creating GitHub releases in CI - name: gh version: want: v2.89.0 method: github-release with: repo: cli/cli anchore-go-logger-1fa893f/.bouncer.yaml000066400000000000000000000035741516525223200200470ustar00rootroot00000000000000permit: - BSD.* - CC0.* - MIT.* - Apache.* - MPL.* - ISC - WTFPL ignore-packages: # packageurl-go is released under the MIT license located in the root of the repo at /mit.LICENSE - github.com/anchore/packageurl-go # both of these dependencies are specified as Apache-2.0 in their respective GitHub READMEs - github.com/alibabacloud-go/cr-20160607/client - github.com/alibabacloud-go/tea-xml/service # crypto/internal/boring is released under the openSSL license as a part of the Golang Standard Libary - crypto/internal/boring # from: https://github.com/spdx/tools-golang/blob/main/LICENSE.code # The tools-golang source code is provided and may be used, at your option, # under either: # * Apache License, version 2.0 (Apache-2.0), OR # * GNU General Public License, version 2.0 or later (GPL-2.0-or-later). # (we choose Apache-2.0) - github.com/spdx/tools-golang # from: https://github.com/xi2/xz/blob/master/LICENSE # All these files have been put into the public domain. # You can do whatever you want with these files. - github.com/xi2/xz # from: https://gitlab.com/cznic/sqlite/-/blob/v1.15.4/LICENSE # This is a BSD-3-Clause license - modernc.org/libc - modernc.org/libc/errno - modernc.org/libc/fcntl - modernc.org/libc/fts - modernc.org/libc/grp - modernc.org/libc/langinfo - modernc.org/libc/limits - modernc.org/libc/netdb - modernc.org/libc/netinet/in - modernc.org/libc/poll - modernc.org/libc/pthread - modernc.org/libc/pwd - modernc.org/libc/signal - modernc.org/libc/stdio - modernc.org/libc/stdlib - modernc.org/libc/sys/socket - modernc.org/libc/sys/stat - modernc.org/libc/sys/types - modernc.org/libc/termios - modernc.org/libc/time - modernc.org/libc/unistd - modernc.org/libc/utime - modernc.org/libc/uuid/uuid - modernc.org/libc/wctype - modernc.org/mathutil - modernc.org/memory anchore-go-logger-1fa893f/.github/000077500000000000000000000000001516525223200167775ustar00rootroot00000000000000anchore-go-logger-1fa893f/.github/dependabot.yml000066400000000000000000000015001516525223200216230ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: gomod directory: "/" cooldown: default-days: 7 schedule: interval: "weekly" day: "friday" open-pull-requests-limit: 10 labels: - "dependencies" groups: go-minor-patch: applies-to: version-updates patterns: - "*" update-types: - "minor" - "patch" - package-ecosystem: "github-actions" directories: - "/" - "/.github/actions/*" cooldown: default-days: 7 schedule: interval: "weekly" day: "friday" open-pull-requests-limit: 10 labels: - "dependencies" groups: actions-minor-patch: applies-to: version-updates patterns: - "*" update-types: - "minor" - "patch" anchore-go-logger-1fa893f/.github/workflows/000077500000000000000000000000001516525223200210345ustar00rootroot00000000000000anchore-go-logger-1fa893f/.github/workflows/codeql.yaml000066400000000000000000000026211516525223200231700ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ "main" ] pull_request: branches: [ "main" ] schedule: - cron: '38 11 * * 3' jobs: analyze: name: Analyze (${{ matrix.language }}) runs-on: ubuntu-latest permissions: security-events: write packages: read actions: read contents: read strategy: fail-fast: false matrix: include: - language: actions build-mode: none - language: go build-mode: manual steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Go if: matrix.language == 'go' uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod - name: Initialize CodeQL uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Build (Go) if: matrix.build-mode == 'manual' shell: bash run: go build ./... - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: category: "/language:${{matrix.language}}" anchore-go-logger-1fa893f/.github/workflows/oss-project-board-add.yaml000066400000000000000000000004661516525223200260110ustar00rootroot00000000000000name: Add to OSS board on: issues: types: - opened - reopened - transferred - labeled jobs: run: permissions: issues: read uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main" secrets: token: ${{ secrets.OSS_PROJECT_GH_TOKEN }} anchore-go-logger-1fa893f/.github/workflows/release.yaml000066400000000000000000000026701516525223200233450ustar00rootroot00000000000000name: "Release" permissions: contents: read checks: read concurrency: group: release cancel-in-progress: false on: workflow_dispatch: inputs: version: description: tag the latest commit on main with the given version (prefixed with v) required: true jobs: version-available: uses: anchore/workflows/.github/workflows/check-version-available.yaml@4f25313f96311410cad4173f74617654a3e46d48 # v0.3.0 with: version: ${{ github.event.inputs.version }} check-gate: uses: anchore/workflows/.github/workflows/check-gate.yaml@4f25313f96311410cad4173f74617654a3e46d48 # v0.3.0 with: checks: '["Static analysis", "Unit tests"]' release: needs: [check-gate, version-available] environment: release runs-on: ubuntu-24.04 permissions: contents: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 with: fetch-depth: 0 # we need the full history to reason about changelogs and tags persist-credentials: true # setup checkout, go, go-make, binny, and cache go modules - uses: anchore/go-make/.github/actions/setup@1747ccaf5ab9a24fc6beaff2c4665007fe656462 # dev branch! - name: Create release env: GITHUB_TOKEN: ${{ github.token }} DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} RELEASE_VERSION: ${{ github.event.inputs.version }} run: make ci-release anchore-go-logger-1fa893f/.github/workflows/remove-awaiting-response-label.yaml000066400000000000000000000004671516525223200277360ustar00rootroot00000000000000name: "Manage Awaiting Response Label" on: issue_comment: types: [created] jobs: run: permissions: issues: write pull-requests: write uses: "anchore/workflows/.github/workflows/remove-awaiting-response-label.yaml@main" secrets: token: ${{ secrets.OSS_PROJECT_GH_TOKEN }} anchore-go-logger-1fa893f/.github/workflows/validate-github-actions.yaml000066400000000000000000000013001516525223200264210ustar00rootroot00000000000000name: "Validate GitHub Actions" on: workflow_dispatch: pull_request: push: branches: - main paths: - '.github/workflows/**' - '.github/actions/**' permissions: contents: read jobs: zizmor: name: "Lint" runs-on: ubuntu-latest permissions: contents: read security-events: write # for uploading SARIF results steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Run zizmor" uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 with: advanced-security: true inputs: .github anchore-go-logger-1fa893f/.github/workflows/validations.yaml000066400000000000000000000017331516525223200242410ustar00rootroot00000000000000name: "Validations" on: workflow_dispatch: push: branches: - main pull_request: permissions: contents: read jobs: Static-Analysis: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Static analysis" runs-on: ubuntu-24.04 steps: # setup checkout, go, go-make, binny, and cache go modules - uses: anchore/go-make/.github/actions/setup@1747ccaf5ab9a24fc6beaff2c4665007fe656462 # dev branch! - name: Run static analysis run: make static-analysis Unit-Test: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Unit tests" runs-on: ubuntu-24.04 steps: # setup checkout, go, go-make, binny, and cache go modules - uses: anchore/go-make/.github/actions/setup@1747ccaf5ab9a24fc6beaff2c4665007fe656462 # dev branch! - name: Run unit tests run: make unit anchore-go-logger-1fa893f/.github/zizmor.yml000066400000000000000000000002751516525223200210600ustar00rootroot00000000000000rules: unpinned-uses: ignore: # Allow unpinned uses of trusted internal anchore/workflows actions - oss-project-board-add.yaml - remove-awaiting-response-label.yaml anchore-go-logger-1fa893f/.gitignore000066400000000000000000000010011516525223200174170ustar00rootroot00000000000000# IDE .server/ .vscode/ .history/ .idea/ # local development go.work go.work.sum /specs mise.toml # releases CHANGELOG.md VERSION /test/results /dist /snapshot # assets *.fingerprint *.tar *.jar *.war *.ear *.jpi *.hpi *.zip *.log .images .tmp/ .tool/ coverage.txt bin/ # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # macOS Finder metadata .DS_STORE *.profile anchore-go-logger-1fa893f/.golangci.yaml000066400000000000000000000055701516525223200201730ustar00rootroot00000000000000version: "2" run: tests: false linters: default: none enable: - asciicheck - bodyclose - copyloopvar - dogsled - dupl - errcheck - funlen - gocognit - goconst - gocritic - gocyclo - goprintffuncname - gosec - govet - ineffassign - misspell - nakedret - nolintlint - revive - staticcheck - unconvert - unparam - unused - whitespace settings: funlen: lines: 100 statements: 60 gocognit: min-complexity: 40 gocritic: enabled-checks: - deferInLoop gosec: excludes: - G115 exclusions: generated: lax presets: - comments - common-false-positives - legacy - std-error-handling paths: - third_party$ - builtin$ - examples$ # do not enable... # - deadcode # The owner seems to have abandoned the linter. Replaced by "unused". # - depguard # We don't have a configuration for this yet # - goprintffuncname # does not catch all cases and there are exceptions # - nakedret # does not catch all cases and should not fail a build # - gochecknoglobals # - gochecknoinits # this is too aggressive # - rowserrcheck disabled per generics https://github.com/golangci/golangci-lint/issues/2649 # - godot # - godox # - goerr113 # - goimports # we're using gosimports now instead to account for extra whitespaces (see https://github.com/golang/go/issues/20818) # - golint # deprecated # - gomnd # this is too aggressive # - interfacer # this is a good idea, but is no longer supported and is prone to false positives # - lll # without a way to specify per-line exception cases, this is not usable # - maligned # this is an excellent linter, but tricky to optimize and we are not sensitive to memory layout optimizations # - nestif # - prealloc # following this rule isn't consistently a good idea, as it sometimes forces unnecessary allocations that result in less idiomatic code # - rowserrcheck # not in a repo with sql, so this is not useful # - scopelint # deprecated # - structcheck # The owner seems to have abandoned the linter. Replaced by "unused". # - testpackage # - varcheck # The owner seems to have abandoned the linter. Replaced by "unused". # - wsl # this doens't have an auto-fixer yet and is pretty noisy (https://github.com/bombsimon/wsl/issues/90) issues: max-same-issues: 25 uniq-by-line: false # TODO: enable this when we have coverage on docstring comments # # The list of ids of default excludes to include or disable. # include: # - EXC0002 # disable excluding of issues about comments from golint formatters: enable: - gofmt - goimports exclusions: generated: lax paths: - third_party$ - builtin$ - examples$anchore-go-logger-1fa893f/.make/000077500000000000000000000000001516525223200164325ustar00rootroot00000000000000anchore-go-logger-1fa893f/.make/go.mod000066400000000000000000000004361516525223200175430ustar00rootroot00000000000000module builder go 1.25.0 require github.com/anchore/go-make v0.0.0-20260407164102-1747ccaf5ab9 require ( github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/goccy/go-yaml v1.19.2 // indirect golang.org/x/mod v0.34.0 // indirect golang.org/x/sys v0.42.0 // indirect ) anchore-go-logger-1fa893f/.make/go.sum000066400000000000000000000015671516525223200175760ustar00rootroot00000000000000github.com/anchore/go-make v0.0.0-20260407164102-1747ccaf5ab9 h1:Ox6/R5xMkekXWNR50oapRh6BnDIUrQFA0QsDicxn8tA= github.com/anchore/go-make v0.0.0-20260407164102-1747ccaf5ab9/go.mod h1:JafD2Md95wih7aGmA12yVoReZW3mOgIuwHbw5aOcIOQ= github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= anchore-go-logger-1fa893f/.make/main.go000066400000000000000000000004741516525223200177120ustar00rootroot00000000000000package main import ( . "github.com/anchore/go-make" "github.com/anchore/go-make/tasks/golint" "github.com/anchore/go-make/tasks/gotest" "github.com/anchore/go-make/tasks/release" ) func main() { Makefile( gotest.Tasks(), golint.Tasks(), release.ChangelogTask(), release.TagAndCreateGHRelease(), ) } anchore-go-logger-1fa893f/DEVELOPING.md000066400000000000000000000006371516525223200174230ustar00rootroot00000000000000# Developing ## Getting started In order to test and develop in this repo you will need the following dependencies installed: - make After cloning the repo run `make bootstrap` to download go mod dependencies, create the `/.tmp` dir, and download helper utilities. The main `make` tasks for common static analysis and testing are `lint`, `lint-fix`, and `unit`. See `make help` for all the current make tasks. anchore-go-logger-1fa893f/LICENSE000066400000000000000000000261351516525223200164530ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. anchore-go-logger-1fa893f/Makefile000066400000000000000000000000561516525223200171000ustar00rootroot00000000000000.PHONY: * .DEFAULT: %: @go run -C .make . $@ anchore-go-logger-1fa893f/adapter/000077500000000000000000000000001516525223200170575ustar00rootroot00000000000000anchore-go-logger-1fa893f/adapter/discard/000077500000000000000000000000001516525223200204705ustar00rootroot00000000000000anchore-go-logger-1fa893f/adapter/discard/logger.go000066400000000000000000000017141516525223200223010ustar00rootroot00000000000000package discard import ( "io" iface "github.com/anchore/go-logger" ) var _ iface.Logger = (*logger)(nil) var _ iface.Controller = (*logger)(nil) type logger struct { } func New() iface.Logger { return &logger{} } func (l *logger) Tracef(_ string, _ ...interface{}) { } func (l *logger) Debugf(_ string, _ ...interface{}) {} func (l *logger) Infof(_ string, _ ...interface{}) {} func (l *logger) Warnf(_ string, _ ...interface{}) {} func (l *logger) Errorf(_ string, _ ...interface{}) {} func (l *logger) Trace(_ ...interface{}) {} func (l *logger) Debug(_ ...interface{}) {} func (l *logger) Info(_ ...interface{}) {} func (l *logger) Warn(_ ...interface{}) {} func (l *logger) Error(_ ...interface{}) {} func (l *logger) WithFields(_ ...interface{}) iface.MessageLogger { return l } func (l *logger) Nested(_ ...interface{}) iface.Logger { return l } func (l *logger) SetOutput(_ io.Writer) {} func (l *logger) GetOutput() io.Writer { return nil } anchore-go-logger-1fa893f/adapter/logrus/000077500000000000000000000000001516525223200203725ustar00rootroot00000000000000anchore-go-logger-1fa893f/adapter/logrus/formatter.go000066400000000000000000000236701516525223200227340ustar00rootroot00000000000000package logrus import ( "bytes" "fmt" "io" "os" "regexp" "sort" "strings" "sync" "time" "github.com/mgutz/ansi" "github.com/sirupsen/logrus" "golang.org/x/term" ) /* Note: this code was copied from https://github.com/x-cray/logrus-prefixed-formatter (which wasn't updated in 5 years and needed adjustments) */ const defaultTimestampFormat = time.RFC3339 var ( baseTimestamp = time.Now() defaultColorScheme = &ColorScheme{ InfoLevelStyle: "green", WarnLevelStyle: "yellow", ErrorLevelStyle: "red", FatalLevelStyle: "red", PanicLevelStyle: "red", DebugLevelStyle: "blue", TraceLevelStyle: "magenta+h", PrefixStyle: "cyan", TimestampStyle: "black+h", } noColorsColorScheme = &compiledColorScheme{ InfoLevelColor: ansi.ColorFunc(""), WarnLevelColor: ansi.ColorFunc(""), ErrorLevelColor: ansi.ColorFunc(""), FatalLevelColor: ansi.ColorFunc(""), PanicLevelColor: ansi.ColorFunc(""), DebugLevelColor: ansi.ColorFunc(""), TraceLevelColor: ansi.ColorFunc(""), PrefixColor: ansi.ColorFunc(""), TimestampColor: ansi.ColorFunc(""), } defaultCompiledColorScheme = compileColorScheme(defaultColorScheme) ) func miniTS() int { return int(time.Since(baseTimestamp) / time.Second) } type ColorScheme struct { InfoLevelStyle string WarnLevelStyle string ErrorLevelStyle string FatalLevelStyle string PanicLevelStyle string DebugLevelStyle string TraceLevelStyle string PrefixStyle string TimestampStyle string } type compiledColorScheme struct { InfoLevelColor func(string) string WarnLevelColor func(string) string ErrorLevelColor func(string) string FatalLevelColor func(string) string PanicLevelColor func(string) string DebugLevelColor func(string) string TraceLevelColor func(string) string PrefixColor func(string) string TimestampColor func(string) string } type TextFormatter struct { // Set to true to bypass checking for a TTY before outputting colors. ForceColors bool // Force disabling colors. For a TTY colors are enabled by default. DisableColors bool // Force formatted layout, even for non-TTY output. ForceFormatting bool // Disable timestamp logging. useful when output is redirected to logging // system that already adds timestamps. DisableTimestamp bool // Disable the conversion of the log levels to uppercase DisableUppercase bool // Enable logging the full timestamp when a TTY is attached instead of just // the time passed since beginning of execution. FullTimestamp bool // Timestamp format to use for display when a full timestamp is printed. TimestampFormat string // The fields are sorted by default for a consistent output. For applications // that log extremely frequently and don't use the JSON formatter this may not // be desired. DisableSorting bool // Wrap empty fields in quotes if true. QuoteEmptyFields bool // Can be set to the override the default quoting character " // with something else. For example: ', or `. QuoteCharacter string // Pad msg field with spaces on the right for display. // The value for this parameter will be the size of padding. // Its default value is zero, which means no padding will be applied for msg. SpacePadding int // Color scheme to use. colorScheme *compiledColorScheme // Whether the logger's out is to a terminal. isTerminal bool sync.Once } func getCompiledColor(main string, fallback string) func(string) string { var style string if main != "" { style = main } else { style = fallback } return ansi.ColorFunc(style) } func compileColorScheme(s *ColorScheme) *compiledColorScheme { return &compiledColorScheme{ InfoLevelColor: getCompiledColor(s.InfoLevelStyle, defaultColorScheme.InfoLevelStyle), WarnLevelColor: getCompiledColor(s.WarnLevelStyle, defaultColorScheme.WarnLevelStyle), ErrorLevelColor: getCompiledColor(s.ErrorLevelStyle, defaultColorScheme.ErrorLevelStyle), FatalLevelColor: getCompiledColor(s.FatalLevelStyle, defaultColorScheme.FatalLevelStyle), PanicLevelColor: getCompiledColor(s.PanicLevelStyle, defaultColorScheme.PanicLevelStyle), DebugLevelColor: getCompiledColor(s.DebugLevelStyle, defaultColorScheme.DebugLevelStyle), TraceLevelColor: getCompiledColor(s.TraceLevelStyle, defaultColorScheme.TraceLevelStyle), PrefixColor: getCompiledColor(s.PrefixStyle, defaultColorScheme.PrefixStyle), TimestampColor: getCompiledColor(s.TimestampStyle, defaultColorScheme.TimestampStyle), } } func (f *TextFormatter) init(entry *logrus.Entry) { if len(f.QuoteCharacter) == 0 { f.QuoteCharacter = "\"" } if entry.Logger != nil { f.isTerminal = f.checkIfTerminal(entry.Logger.Out) } } func (f *TextFormatter) checkIfTerminal(w io.Writer) bool { switch v := w.(type) { case *os.File: return term.IsTerminal(int(v.Fd())) default: return false } } func (f *TextFormatter) SetColorScheme(colorScheme *ColorScheme) { f.colorScheme = compileColorScheme(colorScheme) } func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) { var b *bytes.Buffer var keys = make([]string, 0, len(entry.Data)) for k := range entry.Data { keys = append(keys, k) } lastKeyIdx := len(keys) - 1 if !f.DisableSorting { sort.Strings(keys) } if entry.Buffer != nil { b = entry.Buffer } else { b = &bytes.Buffer{} } prefixFieldClashes(entry.Data) f.Do(func() { f.init(entry) }) isFormatted := f.ForceFormatting || f.isTerminal timestampFormat := f.TimestampFormat if timestampFormat == "" { timestampFormat = defaultTimestampFormat } if isFormatted { isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors var colorScheme *compiledColorScheme if isColored { if f.colorScheme == nil { colorScheme = defaultCompiledColorScheme } else { colorScheme = f.colorScheme } } else { colorScheme = noColorsColorScheme } f.printColored(b, entry, keys, timestampFormat, colorScheme) } else { if !f.DisableTimestamp { f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat), true) } f.appendKeyValue(b, "level", entry.Level.String(), true) if entry.Message != "" { f.appendKeyValue(b, "msg", entry.Message, lastKeyIdx >= 0) } for i, key := range keys { f.appendKeyValue(b, key, entry.Data[key], lastKeyIdx != i) } } b.WriteByte('\n') return b.Bytes(), nil } func (f *TextFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry, keys []string, timestampFormat string, colorScheme *compiledColorScheme) { var levelColor func(string) string var levelText string switch entry.Level { case logrus.InfoLevel: levelColor = colorScheme.InfoLevelColor case logrus.WarnLevel: levelColor = colorScheme.WarnLevelColor case logrus.ErrorLevel: levelColor = colorScheme.ErrorLevelColor case logrus.FatalLevel: levelColor = colorScheme.FatalLevelColor case logrus.PanicLevel: levelColor = colorScheme.PanicLevelColor case logrus.DebugLevel: levelColor = colorScheme.DebugLevelColor case logrus.TraceLevel: levelColor = colorScheme.TraceLevelColor default: levelColor = colorScheme.DebugLevelColor } if entry.Level != logrus.WarnLevel { levelText = entry.Level.String() } else { levelText = "warn" } if !f.DisableUppercase { levelText = strings.ToUpper(levelText) } level := levelColor(fmt.Sprintf("%5s", levelText)) prefix := "" message := entry.Message if prefixValue, ok := entry.Data["prefix"]; ok { val, ok := prefixValue.(string) if ok { prefix = colorScheme.PrefixColor(" " + val + ":") } } else { prefixValue, trimmedMsg := extractPrefix(entry.Message) if len(prefixValue) > 0 { prefix = colorScheme.PrefixColor(" " + prefixValue + ":") message = trimmedMsg } } messageFormat := "%s" if f.SpacePadding != 0 { messageFormat = fmt.Sprintf("%%-%ds", f.SpacePadding) } if f.DisableTimestamp { fmt.Fprintf(b, "%s%s "+messageFormat, level, prefix, message) } else { var timestamp string if !f.FullTimestamp { timestamp = fmt.Sprintf("[%04d]", miniTS()) } else { timestamp = fmt.Sprintf("[%s]", entry.Time.Format(timestampFormat)) } fmt.Fprintf(b, "%s %s%s "+messageFormat, colorScheme.TimestampColor(timestamp), level, prefix, message) } for _, k := range keys { if k != "prefix" { v := entry.Data[k] fmt.Fprintf(b, " %s=%+v", levelColor(k), v) } } } func (f *TextFormatter) needsQuoting(text string) bool { if f.QuoteEmptyFields && len(text) == 0 { return true } for _, ch := range text { if (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9') && ch != '-' && ch != '.' { return true } } return false } func extractPrefix(msg string) (string, string) { prefix := "" regex := regexp.MustCompile(`^\[(.*?)]`) if regex.MatchString(msg) { match := regex.FindString(msg) prefix, msg = match[1:len(match)-1], strings.TrimSpace(msg[len(match):]) } return prefix, msg } func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}, appendSpace bool) { b.WriteString(key) b.WriteByte('=') f.appendValue(b, value) if appendSpace { b.WriteByte(' ') } } func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { switch value := value.(type) { case string: if !f.needsQuoting(value) { b.WriteString(value) } else { fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter) } case error: errmsg := value.Error() if !f.needsQuoting(errmsg) { b.WriteString(errmsg) } else { fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter) } default: fmt.Fprint(b, value) } } // This is to not silently overwrite `time`, `msg` and `level` fields when // dumping it. If this code wasn't there doing: // // logrus.WithField("level", 1).Info("hello") // // would just silently drop the user provided level. Instead with this code // it'll be logged as: // // {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} func prefixFieldClashes(data logrus.Fields) { if t, ok := data["time"]; ok { data["fields.time"] = t } if m, ok := data["msg"]; ok { data["fields.msg"] = m } if l, ok := data["level"]; ok { data["fields.level"] = l } } anchore-go-logger-1fa893f/adapter/logrus/formatter_test.go000066400000000000000000000011401516525223200237570ustar00rootroot00000000000000package logrus import ( "testing" "github.com/stretchr/testify/assert" ) func Test_extractPrefix(t *testing.T) { tests := []struct { name string msg string prefix string rest string }{ { name: "no prefix", msg: "hello world", prefix: "", rest: "hello world", }, { name: "prefix", msg: "[0000] hello world", prefix: "0000", rest: "hello world", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { prefix, rest := extractPrefix(tt.msg) assert.Equal(t, tt.prefix, prefix) assert.Equal(t, tt.rest, rest) }) } } anchore-go-logger-1fa893f/adapter/logrus/logger.go000066400000000000000000000131001516525223200221730ustar00rootroot00000000000000package logrus import ( "fmt" "io" "io/fs" "os" "github.com/sirupsen/logrus" iface "github.com/anchore/go-logger" ) var _ iface.Logger = (*logger)(nil) var _ iface.Controller = (*logger)(nil) const ( defaultLogFilePermissions fs.FileMode = 0644 timestampFormat = "2006-01-02 15:04:05" ) // Config contains all configurable values for the Logrus entry type Config struct { EnableConsole bool FileLocation string Level iface.Level Formatter logrus.Formatter CaptureCallerInfo bool NoLock bool } func DefaultConfig() Config { return Config{ EnableConsole: true, FileLocation: "", Level: iface.InfoLevel, CaptureCallerInfo: false, NoLock: false, Formatter: DefaultTextFormatter(), } } func DefaultTextFormatter() logrus.Formatter { return &TextFormatter{ TimestampFormat: timestampFormat, ForceFormatting: true, } } func DefaultJSONFormatter() logrus.Formatter { return &logrus.JSONFormatter{ TimestampFormat: timestampFormat, DisableTimestamp: false, DisableHTMLEscape: false, PrettyPrint: false, } } // logger contains all runtime values for using Logrus with the configured output target and input configuration values. type logger struct { config Config logger *logrus.Logger output io.Writer } // Use adapts the given logger based on the provided configuration func Use(l *logrus.Logger, cfg Config) (iface.Logger, error) { var output io.Writer switch { case cfg.EnableConsole && cfg.FileLocation != "": logFile, err := os.OpenFile(cfg.FileLocation, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, defaultLogFilePermissions) if err != nil { return nil, fmt.Errorf("unable to setup log file: %w", err) } output = io.MultiWriter(os.Stderr, logFile) case cfg.EnableConsole: output = os.Stderr case cfg.FileLocation != "": logFile, err := os.OpenFile(cfg.FileLocation, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, defaultLogFilePermissions) if err != nil { return nil, fmt.Errorf("unable to setup log file: %w", err) } output = logFile default: output = io.Discard } var level logrus.Level if cfg.Level == iface.DisabledLevel { level = logrus.PanicLevel } else { level = getLogLevel(cfg.Level) } l.SetOutput(output) l.SetLevel(level) l.SetReportCaller(cfg.CaptureCallerInfo) if cfg.NoLock { l.SetNoLock() } if cfg.Formatter != nil { l.SetFormatter(cfg.Formatter) } else { l.SetFormatter(DefaultTextFormatter()) } return &logger{ config: cfg, logger: l, output: output, }, nil } // New creates a new logger with the given configuration func New(cfg Config) (iface.Logger, error) { return Use(logrus.New(), cfg) } // Tracef takes a formatted template string and template arguments for the trace logging level. func (l *logger) Tracef(format string, args ...interface{}) { l.logger.Tracef(format, args...) } // Debugf takes a formatted template string and template arguments for the debug logging level. func (l *logger) Debugf(format string, args ...interface{}) { l.logger.Debugf(format, args...) } // Infof takes a formatted template string and template arguments for the info logging level. func (l *logger) Infof(format string, args ...interface{}) { l.logger.Infof(format, args...) } // Warnf takes a formatted template string and template arguments for the warning logging level. func (l *logger) Warnf(format string, args ...interface{}) { l.logger.Warnf(format, args...) } // Errorf takes a formatted template string and template arguments for the error logging level. func (l *logger) Errorf(format string, args ...interface{}) { l.logger.Errorf(format, args...) } // Trace logs the given arguments at the trace logging level. func (l *logger) Trace(args ...interface{}) { l.logger.Trace(args...) } // Debug logs the given arguments at the debug logging level. func (l *logger) Debug(args ...interface{}) { l.logger.Debug(args...) } // Info logs the given arguments at the info logging level. func (l *logger) Info(args ...interface{}) { l.logger.Info(args...) } // Warn logs the given arguments at the warning logging level. func (l *logger) Warn(args ...interface{}) { l.logger.Warn(args...) } // Error logs the given arguments at the error logging level. func (l *logger) Error(args ...interface{}) { l.logger.Error(args...) } // WithFields returns a message entry with multiple key-value fields. func (l *logger) WithFields(fields ...interface{}) iface.MessageLogger { return l.logger.WithFields(getFields(fields...)) } func (l *logger) Nested(fields ...interface{}) iface.Logger { return &nestedLogger{entry: l.logger.WithFields(getFields(fields...))} } func (l *logger) SetOutput(writer io.Writer) { l.output = writer l.logger.SetOutput(writer) } func (l *logger) GetOutput() io.Writer { return l.output } func getFields(fields ...interface{}) logrus.Fields { f := make(logrus.Fields) offset := 0 for i, val := range fields { // there can be a fields map anywhere within the parameters if fieldsMap, ok := val.(iface.Fields); ok { for k, v := range fieldsMap { f[k] = v } offset++ continue } // virtually skip any field maps found when figuring if this is a key or a value if (i-offset)%2 != 0 { f[fmt.Sprintf("%s", fields[i-1])] = val } } return f } func getLogLevel(level iface.Level) logrus.Level { switch level { case iface.ErrorLevel: return logrus.ErrorLevel case iface.WarnLevel: return logrus.WarnLevel case iface.InfoLevel: return logrus.InfoLevel case iface.DebugLevel: return logrus.DebugLevel case iface.TraceLevel: return logrus.TraceLevel } return logrus.PanicLevel } anchore-go-logger-1fa893f/adapter/logrus/nested_logger.go000066400000000000000000000045031516525223200235440ustar00rootroot00000000000000package logrus import ( "github.com/sirupsen/logrus" iface "github.com/anchore/go-logger" ) var _ iface.Logger = (*nestedLogger)(nil) // nestedLogger is a wrapper for Logrus to enable nested logging configuration (loggers that always attach key-value pairs to all log entries) type nestedLogger struct { entry *logrus.Entry } // Tracef takes a formatted template string and template arguments for the trace logging level. func (l *nestedLogger) Tracef(format string, args ...interface{}) { l.entry.Tracef(format, args...) } // Debugf takes a formatted template string and template arguments for the debug logging level. func (l *nestedLogger) Debugf(format string, args ...interface{}) { l.entry.Debugf(format, args...) } // Infof takes a formatted template string and template arguments for the info logging level. func (l *nestedLogger) Infof(format string, args ...interface{}) { l.entry.Infof(format, args...) } // Warnf takes a formatted template string and template arguments for the warning logging level. func (l *nestedLogger) Warnf(format string, args ...interface{}) { l.entry.Warnf(format, args...) } // Errorf takes a formatted template string and template arguments for the error logging level. func (l *nestedLogger) Errorf(format string, args ...interface{}) { l.entry.Errorf(format, args...) } // Trace logs the given arguments at the trace logging level. func (l *nestedLogger) Trace(args ...interface{}) { l.entry.Trace(args...) } // Debug logs the given arguments at the debug logging level. func (l *nestedLogger) Debug(args ...interface{}) { l.entry.Debug(args...) } // Info logs the given arguments at the info logging level. func (l *nestedLogger) Info(args ...interface{}) { l.entry.Info(args...) } // Warn logs the given arguments at the warning logging level. func (l *nestedLogger) Warn(args ...interface{}) { l.entry.Warn(args...) } // Error logs the given arguments at the error logging level. func (l *nestedLogger) Error(args ...interface{}) { l.entry.Error(args...) } // WithFields returns a message entry with multiple key-value fields. func (l *nestedLogger) WithFields(fields ...interface{}) iface.MessageLogger { return l.entry.WithFields(getFields(fields...)) } func (l *nestedLogger) Nested(fields ...interface{}) iface.Logger { return &nestedLogger{entry: l.entry.WithFields(getFields(fields...))} } anchore-go-logger-1fa893f/adapter/redact/000077500000000000000000000000001516525223200203215ustar00rootroot00000000000000anchore-go-logger-1fa893f/adapter/redact/logger.go000066400000000000000000000066731516525223200221430ustar00rootroot00000000000000package redact import ( "fmt" "io" iface "github.com/anchore/go-logger" ) var _ iface.Logger = (*redactingLogger)(nil) var _ iface.Controller = (*redactingLogger)(nil) type redactingLogger struct { log iface.MessageLogger redactor Redactor } func New(log iface.MessageLogger, redactor Redactor) iface.Logger { if r, ok := log.(*redactingLogger); ok { // this is already a redacting logger, so just return it, but attach it to all discovered existing stores r.redactor = newRedactorCollection(r.redactor, redactor) return r } return &redactingLogger{ log: log, redactor: redactor, } } func (r *redactingLogger) SetOutput(writer io.Writer) { if c, ok := r.log.(iface.Controller); ok { c.SetOutput(writer) } } func (r *redactingLogger) GetOutput() io.Writer { if c, ok := r.log.(iface.Controller); ok { return c.GetOutput() } return nil } func (r *redactingLogger) Errorf(format string, args ...interface{}) { r.log.Errorf(r.redactString(format), r.redactFields(args)...) } func (r *redactingLogger) Error(args ...interface{}) { r.log.Error(r.redactFields(args)...) } func (r *redactingLogger) Warnf(format string, args ...interface{}) { r.log.Warnf(r.redactString(format), r.redactFields(args)...) } func (r *redactingLogger) Warn(args ...interface{}) { r.log.Warn(r.redactFields(args)...) } func (r *redactingLogger) Infof(format string, args ...interface{}) { r.log.Infof(r.redactString(format), r.redactFields(args)...) } func (r *redactingLogger) Info(args ...interface{}) { r.log.Info(r.redactFields(args)...) } func (r *redactingLogger) Debugf(format string, args ...interface{}) { r.log.Debugf(r.redactString(format), r.redactFields(args)...) } func (r *redactingLogger) Debug(args ...interface{}) { r.log.Debug(r.redactFields(args)...) } func (r *redactingLogger) Tracef(format string, args ...interface{}) { r.log.Tracef(r.redactString(format), r.redactFields(args)...) } func (r *redactingLogger) Trace(args ...interface{}) { r.log.Trace(r.redactFields(args)...) } func (r *redactingLogger) WithFields(fields ...interface{}) iface.MessageLogger { if l, ok := r.log.(iface.FieldLogger); ok { return New(l.WithFields(r.redactFields(fields)...), r.redactor) } return r } func (r *redactingLogger) Nested(fields ...interface{}) iface.Logger { if l, ok := r.log.(iface.NestedLogger); ok { return New(l.Nested(r.redactFields(fields)...), r.redactor) } return r } func (r *redactingLogger) redactFields(fields []interface{}) []interface{} { for i, v := range fields { switch vv := v.(type) { case string: fields[i] = r.redactString(vv) case int, int32, int64, int16, int8, float32, float64: // don't coerce non-string primitives to different types fields[i] = vv case iface.Fields: for kkk, vvv := range vv { delete(vv, kkk) // this key may have data that should be redacted redactedKey := r.redactString(kkk) switch vvvv := vvv.(type) { case string: vv[redactedKey] = r.redactString(vvvv) case int, int32, int64, int16, int8, float32, float64: // don't coerce non-string primitives to different types (but still redact the key) vv[redactedKey] = vvvv default: vv[redactedKey] = r.redactString(fmt.Sprintf("%+v", vvvv)) } } fields[i] = vv default: // coerce to a string and redact fields[i] = r.redactString(fmt.Sprintf("%+v", vv)) } } return fields } func (r *redactingLogger) redactString(s string) string { return r.redactor.RedactString(s) } anchore-go-logger-1fa893f/adapter/redact/logger_test.go000066400000000000000000000042711516525223200231720ustar00rootroot00000000000000package redact import ( "bytes" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/anchore/go-logger" "github.com/anchore/go-logger/adapter/logrus" ) func Test_RedactingLogger(t *testing.T) { tests := []struct { name string redact []string }{ { name: "single value", redact: []string{"joe"}, }, { name: "multi value", redact: []string{"bob", "alice"}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { out, err := logrus.New(logrus.Config{ Level: logger.TraceLevel, }) require.NoError(t, err) buff := bytes.Buffer{} out.(logger.Controller).SetOutput(&buff) redactor := New(out, NewStore(test.redact...)) var fieldObj = make(logger.Fields) for _, v := range test.redact { fieldObj[v] = v } format := "" var fields []interface{} for _, v := range test.redact { fields = append(fields, v) format += "%s" } fields = append(fields, 3) format += "%d" fields = append(fields, int32(3)) format += "%d" fields = append(fields, 3.2) format += "%f" fields = append(fields, float32(4.3)) format += "%f" fields = append(fields, fieldObj) format += "%+v" var interlacedFields []interface{} for i, f := range fields { interlacedFields = append(interlacedFields, fmt.Sprintf("%d", i), f) } nestedFieldLogger := redactor.Nested(interlacedFields...).WithFields(interlacedFields...) nestedFieldLogger.Tracef(format, fields...) nestedFieldLogger.Trace(fields...) nestedFieldLogger.Debugf(format, fields...) nestedFieldLogger.Debug(fields...) nestedFieldLogger.Infof(format, fields...) nestedFieldLogger.Info(fields...) nestedFieldLogger.Warnf(format, fields...) nestedFieldLogger.Warn(fields...) nestedFieldLogger.Errorf(format, fields...) nestedFieldLogger.Error(fields...) result := buff.String() // this is a string indicator that we've coerced an instance to a new type that does not match the format type (e.g. %d) assert.NotContains(t, result, "%") assert.NotEmpty(t, result) for _, v := range test.redact { assert.NotContains(t, result, v) } }) } } anchore-go-logger-1fa893f/adapter/redact/store.go000066400000000000000000000043421516525223200220070ustar00rootroot00000000000000package redact import ( "strings" "sync" "github.com/google/uuid" "github.com/scylladb/go-set/strset" ) type Store interface { Redactor StoreWriter } type Redactor interface { RedactString(string) string identifiable } type StoreWriter interface { Add(value ...string) identifiable } type identifiable interface { id() string } // redactorCollection holds a list of redactors, applying all of them to Redact* calls type redactorCollection []Redactor var _ Redactor = (*redactorCollection)(nil) func newRedactorCollection(readers ...Redactor) Redactor { collection := make(redactorCollection, 0, len(readers)) ids := strset.New() addReader := func(rs ...Redactor) { for _, r := range rs { if ids.Has(r.id()) { continue } collection = append(collection, r) ids.Add(r.id()) } } for _, r := range readers { if rs, ok := r.(redactorCollection); ok { addReader(rs...) } else { addReader(r) } } return collection } func (c redactorCollection) RedactString(s string) string { for _, r := range c { s = r.RedactString(s) } return s } func (c redactorCollection) id() (val string) { for _, r := range c { val += r.id() } return val } // store maintains a list of redactions, and implements Redactor Redact* methods type store struct { redactions *strset.Set lock *sync.RWMutex _id string } var _ Store = (*store)(nil) func NewStore(values ...string) Store { return &store{ redactions: strset.New(values...), lock: &sync.RWMutex{}, _id: uuid.New().String(), } } func (w *store) id() string { return w._id } func (w *store) Add(values ...string) { w.lock.Lock() defer w.lock.Unlock() for _, value := range values { if len(value) <= 1 { // smallest possible redaction string must be larger than 1 character continue } w.redactions.Add(value) } } func (w *store) values() []string { w.lock.RLock() defer w.lock.RUnlock() return w.redactions.List() } func (w *store) RedactString(str string) string { for _, s := range w.values() { // note: we don't use the length of the redaction string to determine the replacement string, as even the length could be considered sensitive str = strings.ReplaceAll(str, s, strings.Repeat("*", 7)) } return str } anchore-go-logger-1fa893f/go.mod000066400000000000000000000010671516525223200165510ustar00rootroot00000000000000module github.com/anchore/go-logger go 1.24.0 require ( github.com/google/uuid v1.6.0 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/scylladb/go-set v1.0.2 github.com/sirupsen/logrus v1.9.4 github.com/stretchr/testify v1.11.1 golang.org/x/term v0.40.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.41.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) anchore-go-logger-1fa893f/go.sum000066400000000000000000000051161516525223200165750ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/scylladb/go-set v1.0.2 h1:SkvlMCKhP0wyyct6j+0IHJkBkSZL+TDzZ4E7f7BCcRE= github.com/scylladb/go-set v1.0.2/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= anchore-go-logger-1fa893f/logger.go000066400000000000000000000045761516525223200172610ustar00rootroot00000000000000package logger import ( "fmt" "io" "strings" ) type Level string const ( DisabledLevel Level = "" ErrorLevel Level = "error" WarnLevel Level = "warn" InfoLevel Level = "info" DebugLevel Level = "debug" TraceLevel Level = "trace" ) func Levels() []Level { return []Level{ ErrorLevel, WarnLevel, InfoLevel, DebugLevel, TraceLevel, } } type Logger interface { MessageLogger FieldLogger NestedLogger } type Controller interface { SetOutput(io.Writer) GetOutput() io.Writer } type NestedLogger interface { Nested(fields ...interface{}) Logger } type FieldLogger interface { WithFields(fields ...interface{}) MessageLogger } type Fields map[string]interface{} type MessageLogger interface { ErrorMessageLogger WarnMessageLogger InfoMessageLogger DebugMessageLogger TraceMessageLogger } // type MessageLogger interface { // Logf(level Level, format string, args ...interface{}) // Log(level Level, args ...interface{}) //} type ErrorMessageLogger interface { Errorf(format string, args ...interface{}) Error(args ...interface{}) } type WarnMessageLogger interface { Warnf(format string, args ...interface{}) Warn(args ...interface{}) } type InfoMessageLogger interface { Infof(format string, args ...interface{}) Info(args ...interface{}) } type DebugMessageLogger interface { Debugf(format string, args ...interface{}) Debug(args ...interface{}) } type TraceMessageLogger interface { Tracef(format string, args ...interface{}) Trace(args ...interface{}) } func LevelFromString(l string) (Level, error) { switch strings.ToLower(l) { case "": return DisabledLevel, nil case "error", "err", "e": return ErrorLevel, nil case "warn", "warning", "w": return WarnLevel, nil case "info", "information", "informational", "i": return InfoLevel, nil case "debug", "debugging", "d": return DebugLevel, nil case "trace", "t": return TraceLevel, nil } return Level(l), fmt.Errorf("not a valid log level: %q", l) } func LevelFromVerbosity(v int, levels ...Level) Level { if len(levels) == 0 { return DisabledLevel } if v >= len(levels) { return levels[len(levels)-1] } if v <= 0 { return levels[0] } return levels[v] } func IsLevel(l Level, levels ...Level) bool { for _, level := range levels { if l == level { return true } } return false } func IsVerbose(level Level) bool { return IsLevel(level, InfoLevel, DebugLevel) } anchore-go-logger-1fa893f/logger_test.go000066400000000000000000000031111516525223200203000ustar00rootroot00000000000000package logger import ( "testing" "github.com/stretchr/testify/assert" ) func TestLevelFromVerbosity(t *testing.T) { tests := []struct { name string v int levels []Level want Level }{ { name: "no configured levels disables logging", v: 0, levels: []Level{}, want: DisabledLevel, }, { name: "no configured levels disables logging (with negative verbosity)", v: -1, levels: []Level{}, want: DisabledLevel, }, { name: "negative verbosity selects the lowest level", v: -10, levels: []Level{ WarnLevel, InfoLevel, DebugLevel, TraceLevel, }, want: WarnLevel, }, { name: "select lowest level", v: 0, levels: []Level{ WarnLevel, InfoLevel, DebugLevel, TraceLevel, }, want: WarnLevel, }, { name: "positive valid verbosity selects correct level index", v: 1, levels: []Level{ WarnLevel, InfoLevel, DebugLevel, TraceLevel, }, want: InfoLevel, }, { name: "select highest level index", v: 3, levels: []Level{ WarnLevel, InfoLevel, DebugLevel, TraceLevel, }, want: TraceLevel, }, { name: "select edge of bounds", v: 4, levels: []Level{ WarnLevel, InfoLevel, DebugLevel, TraceLevel, }, want: TraceLevel, }, { name: "select out of bounds", v: 5, levels: []Level{ WarnLevel, InfoLevel, DebugLevel, TraceLevel, }, want: TraceLevel, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := LevelFromVerbosity(tt.v, tt.levels...) assert.Equal(t, tt.want, got) }) } }