generated from TrueCloudLab/basic
Compare commits
No commits in common. "master" and "empty" have entirely different histories.
40 changed files with 2 additions and 3627 deletions
|
@ -1,21 +0,0 @@
|
||||||
name: DCO action
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
dco:
|
|
||||||
name: DCO
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.22'
|
|
||||||
|
|
||||||
- name: Run commit format checker
|
|
||||||
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
|
|
||||||
with:
|
|
||||||
from: 'origin/${{ github.event.pull_request.base.ref }}'
|
|
|
@ -1,73 +0,0 @@
|
||||||
name: Tests and linters
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
name: Lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.22'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Install linters
|
|
||||||
run: make lint-install
|
|
||||||
|
|
||||||
- name: Run linters
|
|
||||||
run: make lint
|
|
||||||
|
|
||||||
tests:
|
|
||||||
name: Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
go_versions: [ '1.21', '1.22' ]
|
|
||||||
fail-fast: false
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '${{ matrix.go_versions }}'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: make test
|
|
||||||
|
|
||||||
tests-race:
|
|
||||||
name: Tests with -race
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.22'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: go test ./... -count=1 -race
|
|
||||||
|
|
||||||
staticcheck:
|
|
||||||
name: Staticcheck
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.22'
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Install staticcheck
|
|
||||||
run: make staticcheck-install
|
|
||||||
|
|
||||||
- name: Run staticcheck
|
|
||||||
run: make staticcheck-run
|
|
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -1,2 +0,0 @@
|
||||||
/**/*.pb.go -diff -merge
|
|
||||||
/**/*.pb.go linguist-generated=true
|
|
51
.gitignore
vendored
51
.gitignore
vendored
|
@ -1,51 +0,0 @@
|
||||||
# IDE
|
|
||||||
.idea
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# Vendoring
|
|
||||||
vendor
|
|
||||||
|
|
||||||
# tempfiles
|
|
||||||
.DS_Store
|
|
||||||
*~
|
|
||||||
.cache
|
|
||||||
|
|
||||||
temp
|
|
||||||
tmp
|
|
||||||
|
|
||||||
# binary
|
|
||||||
bin/
|
|
||||||
release/
|
|
||||||
|
|
||||||
# coverage
|
|
||||||
coverage.txt
|
|
||||||
coverage.html
|
|
||||||
|
|
||||||
# testing
|
|
||||||
cmd/test
|
|
||||||
/plugins/
|
|
||||||
testfile
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.neofs-cli.yml
|
|
||||||
|
|
||||||
# debhelpers
|
|
||||||
debian/*debhelper*
|
|
||||||
|
|
||||||
# logfiles
|
|
||||||
debian/*.log
|
|
||||||
|
|
||||||
# .substvars
|
|
||||||
debian/*.substvars
|
|
||||||
|
|
||||||
# .bash-completion
|
|
||||||
debian/*.bash-completion
|
|
||||||
|
|
||||||
# Install folders and files
|
|
||||||
debian/frostfs-cli/
|
|
||||||
debian/frostfs-ir/
|
|
||||||
debian/files
|
|
||||||
debian/frostfs-storage/
|
|
||||||
debian/changelog
|
|
||||||
man/
|
|
||||||
debs/
|
|
|
@ -1,66 +0,0 @@
|
||||||
# This file contains all available configuration options
|
|
||||||
# with their default values.
|
|
||||||
|
|
||||||
# options for analysis running
|
|
||||||
run:
|
|
||||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
|
||||||
timeout: 10m
|
|
||||||
|
|
||||||
# include test files or not, default is true
|
|
||||||
tests: false
|
|
||||||
|
|
||||||
# output configuration options
|
|
||||||
output:
|
|
||||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
|
||||||
format: tab
|
|
||||||
|
|
||||||
# all available settings of specific linters
|
|
||||||
linters-settings:
|
|
||||||
exhaustive:
|
|
||||||
# indicates that switch statements are to be considered exhaustive if a
|
|
||||||
# 'default' case is present, even if all enum members aren't listed in the
|
|
||||||
# switch
|
|
||||||
default-signifies-exhaustive: true
|
|
||||||
govet:
|
|
||||||
# report about shadowed variables
|
|
||||||
check-shadowing: false
|
|
||||||
staticcheck:
|
|
||||||
checks: ["all"]
|
|
||||||
funlen:
|
|
||||||
lines: 80 # default 60
|
|
||||||
statements: 60 # default 40
|
|
||||||
gocognit:
|
|
||||||
min-complexity: 40 # default 30
|
|
||||||
|
|
||||||
linters:
|
|
||||||
enable:
|
|
||||||
# mandatory linters
|
|
||||||
- govet
|
|
||||||
- revive
|
|
||||||
|
|
||||||
# some default golangci-lint linters
|
|
||||||
- errcheck
|
|
||||||
- gosimple
|
|
||||||
- godot
|
|
||||||
- ineffassign
|
|
||||||
- staticcheck
|
|
||||||
- typecheck
|
|
||||||
- unused
|
|
||||||
|
|
||||||
# extra linters
|
|
||||||
- bidichk
|
|
||||||
- durationcheck
|
|
||||||
- exhaustive
|
|
||||||
- exportloopref
|
|
||||||
- gofumpt
|
|
||||||
- goimports
|
|
||||||
- misspell
|
|
||||||
- predeclared
|
|
||||||
- reassign
|
|
||||||
- whitespace
|
|
||||||
- containedctx
|
|
||||||
- funlen
|
|
||||||
- gocognit
|
|
||||||
- contextcheck
|
|
||||||
disable-all: true
|
|
||||||
fast: false
|
|
|
@ -1,38 +0,0 @@
|
||||||
ci:
|
|
||||||
autofix_prs: false
|
|
||||||
|
|
||||||
repos:
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
||||||
rev: v4.4.0
|
|
||||||
hooks:
|
|
||||||
- id: check-added-large-files
|
|
||||||
- id: check-case-conflict
|
|
||||||
- id: check-executables-have-shebangs
|
|
||||||
- id: check-shebang-scripts-are-executable
|
|
||||||
- id: check-merge-conflict
|
|
||||||
- id: check-json
|
|
||||||
- id: check-xml
|
|
||||||
- id: check-yaml
|
|
||||||
- id: trailing-whitespace
|
|
||||||
args: [--markdown-linebreak-ext=md]
|
|
||||||
- id: end-of-file-fixer
|
|
||||||
exclude: ".key$"
|
|
||||||
|
|
||||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
|
||||||
rev: v0.9.0.2
|
|
||||||
hooks:
|
|
||||||
- id: shellcheck
|
|
||||||
|
|
||||||
- repo: https://github.com/golangci/golangci-lint
|
|
||||||
rev: v1.59.1
|
|
||||||
hooks:
|
|
||||||
- id: golangci-lint
|
|
||||||
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: go-unit-tests
|
|
||||||
name: go unit tests
|
|
||||||
entry: make test GOFLAGS=''
|
|
||||||
pass_filenames: false
|
|
||||||
types: [go]
|
|
||||||
language: system
|
|
201
LICENSE
201
LICENSE
|
@ -1,201 +0,0 @@
|
||||||
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.
|
|
64
Makefile
64
Makefile
|
@ -1,64 +0,0 @@
|
||||||
#!/usr/bin/make -f
|
|
||||||
SHELL = bash
|
|
||||||
|
|
||||||
TRUECLOUDLAB_LINT_VERSION ?= 0.0.3
|
|
||||||
TMP_DIR := .cache
|
|
||||||
OUTPUT_LINT_DIR ?= $(shell pwd)/bin
|
|
||||||
LINT_VERSION ?= 1.59.1
|
|
||||||
LINT_DIR = $(OUTPUT_LINT_DIR)/golangci-lint-$(LINT_VERSION)-v$(TRUECLOUDLAB_LINT_VERSION)
|
|
||||||
|
|
||||||
# Run all code formatters
|
|
||||||
fmts: fmt imports
|
|
||||||
|
|
||||||
# Reformat code
|
|
||||||
fmt:
|
|
||||||
@echo "⇒ Processing gofmt check"
|
|
||||||
@gofumpt -s -w .
|
|
||||||
|
|
||||||
# Reformat imports
|
|
||||||
imports:
|
|
||||||
@echo "⇒ Processing goimports check"
|
|
||||||
@goimports -w .
|
|
||||||
|
|
||||||
# Run Unit Test with go test
|
|
||||||
test: GOFLAGS ?= "-count=1"
|
|
||||||
test:
|
|
||||||
@echo "⇒ Running go test"
|
|
||||||
@GOFLAGS="$(GOFLAGS)" go test ./...
|
|
||||||
|
|
||||||
# Activate pre-commit hooks
|
|
||||||
pre-commit:
|
|
||||||
pre-commit install -t pre-commit -t commit-msg
|
|
||||||
|
|
||||||
# Deactivate pre-commit hooks
|
|
||||||
unpre-commit:
|
|
||||||
pre-commit uninstall -t pre-commit -t commit-msg
|
|
||||||
|
|
||||||
pre-commit-run:
|
|
||||||
@pre-commit run -a --hook-stage manual
|
|
||||||
|
|
||||||
# Install linters
|
|
||||||
lint-install:
|
|
||||||
@mkdir -p $(TMP_DIR)
|
|
||||||
@rm -rf $(TMP_DIR)/linters
|
|
||||||
@git -c advice.detachedHead=false clone --branch v$(TRUECLOUDLAB_LINT_VERSION) https://git.frostfs.info/TrueCloudLab/linters.git $(TMP_DIR)/linters
|
|
||||||
@make -C $(TMP_DIR)/linters lib CGO_ENABLED=1 OUT_DIR=$(OUTPUT_LINT_DIR)
|
|
||||||
@rm -rf $(TMP_DIR)/linters
|
|
||||||
@rmdir $(TMP_DIR) 2>/dev/null || true
|
|
||||||
@CGO_ENABLED=1 GOBIN=$(LINT_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$(LINT_VERSION)
|
|
||||||
|
|
||||||
# Run linters
|
|
||||||
lint:
|
|
||||||
@if [ ! -d "$(LINT_DIR)" ]; then \
|
|
||||||
echo "Run make lint-install"; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
@$(LINT_DIR)/golangci-lint run
|
|
||||||
|
|
||||||
# Install staticcheck
|
|
||||||
staticcheck-install:
|
|
||||||
@go install honnef.co/go/tools/cmd/staticcheck@latest
|
|
||||||
|
|
||||||
# Run staticcheck
|
|
||||||
staticcheck-run:
|
|
||||||
@staticcheck ./...
|
|
23
README.md
23
README.md
|
@ -1,22 +1,3 @@
|
||||||
# Helper Go libraries for working with metrics, traces and logging
|
# WIP area: this repo is just a fork!
|
||||||
|
|
||||||
See package documentation
|
Useful things may be published only in [other branches](../../../branches)
|
||||||
at [pkg.go.dev](https://pkg.go.dev/git.frostfs.info/TrueCloudLab/frostfs-observability)
|
|
||||||
|
|
||||||
## License and copyright
|
|
||||||
|
|
||||||
Copyright 2023-2024 FrostFS contributors
|
|
||||||
|
|
||||||
```
|
|
||||||
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.
|
|
||||||
```
|
|
||||||
|
|
47
go.mod
47
go.mod
|
@ -1,47 +0,0 @@
|
||||||
module git.frostfs.info/TrueCloudLab/frostfs-observability
|
|
||||||
|
|
||||||
go 1.21
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0-rc.0
|
|
||||||
github.com/prometheus/client_golang v1.15.1
|
|
||||||
github.com/prometheus/client_model v0.3.0
|
|
||||||
github.com/stretchr/testify v1.9.0
|
|
||||||
go.opentelemetry.io/otel v1.28.0
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0
|
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0
|
|
||||||
go.opentelemetry.io/otel/sdk v1.28.0
|
|
||||||
go.opentelemetry.io/otel/trace v1.28.0
|
|
||||||
google.golang.org/grpc v1.64.0
|
|
||||||
google.golang.org/protobuf v1.34.2
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3 // indirect
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
github.com/prometheus/common v0.42.0 // indirect
|
|
||||||
github.com/prometheus/procfs v0.9.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
|
||||||
go.uber.org/zap v1.26.0
|
|
||||||
golang.org/x/net v0.26.0 // indirect
|
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
|
||||||
golang.org/x/text v0.16.0 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
|
458
go.sum
458
go.sum
|
@ -1,458 +0,0 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
|
||||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
|
||||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
|
||||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
|
||||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
|
||||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
|
||||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
|
||||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
|
||||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
|
||||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
|
||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
|
||||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
|
||||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
|
||||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
|
||||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
|
||||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
|
||||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
|
||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
|
||||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
|
||||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
|
||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
|
||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
|
||||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
|
||||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
|
||||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0-rc.0 h1:mdLirNAJBxnGgyB6pjZLcs6ue/6eZGBui6gXspfq4ks=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0-rc.0/go.mod h1:kdXbOySqcQeTxiqglW7aahTmWZy3Pgi6SYL36yvKeyA=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3 h1:o95KDiV/b1xdkumY5YbLR0/n2+wBxUpgf3HgfKgTyLI=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3/go.mod h1:hTxjzRcX49ogbTGVJ1sM5mz5s+SSgiGIyL3jjPxl32E=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
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/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
|
|
||||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
|
||||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
|
||||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
|
||||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
|
||||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
|
||||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|
||||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
|
||||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw=
|
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ=
|
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y=
|
|
||||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
|
||||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
|
||||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
|
||||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
|
||||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
|
||||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
|
||||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
|
||||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
|
||||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
|
||||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
|
||||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
|
||||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
|
||||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
|
||||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
|
||||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
|
||||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
|
||||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
|
||||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
|
||||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
|
||||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
|
||||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
|
||||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
|
||||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
|
||||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
|
||||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
|
||||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
|
||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
|
|
||||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
|
||||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
|
||||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
|
||||||
google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|
|
@ -1,67 +0,0 @@
|
||||||
package lokicore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/logging/lokicore/loki"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"go.uber.org/zap/zapcore"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Zap Core for loki.
|
|
||||||
// Expands the zapcore.Core interface with calls to export logs to Loki.
|
|
||||||
type LokiCore struct {
|
|
||||||
original zapcore.Core
|
|
||||||
encoder zapcore.Encoder
|
|
||||||
loki *loki.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(original zapcore.Core, lokiCfg loki.Config) *LokiCore {
|
|
||||||
encoderConfig := zap.NewProductionEncoderConfig()
|
|
||||||
encoder := zapcore.NewJSONEncoder(encoderConfig)
|
|
||||||
encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.RFC3339Nano)
|
|
||||||
|
|
||||||
return &LokiCore{
|
|
||||||
original: original,
|
|
||||||
encoder: encoder,
|
|
||||||
loki: loki.Setup(lokiCfg),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *LokiCore) With(fields []zapcore.Field) zapcore.Core {
|
|
||||||
return &LokiCore{
|
|
||||||
original: c.original.With(fields),
|
|
||||||
encoder: c.encoder,
|
|
||||||
loki: c.loki,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *LokiCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
|
||||||
if err := c.original.Write(entry, fields); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer, err := c.encoder.EncodeEntry(entry, fields)
|
|
||||||
defer buffer.Free()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.loki.Send(buffer.String(), entry.Time)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *LokiCore) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
|
||||||
if c.Enabled(entry.Level) {
|
|
||||||
return checked.AddCore(entry, c)
|
|
||||||
}
|
|
||||||
return checked
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *LokiCore) Sync() error {
|
|
||||||
return c.original.Sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *LokiCore) Enabled(level zapcore.Level) bool {
|
|
||||||
return c.original.Enabled(level)
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
# git.frostfs.info/TrueCloudLab/frostfs-observability/loki"
|
|
||||||
|
|
||||||
A simple asynchronous client in Go for sending logs to Loki.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/logging/lokicore/loki"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
loki := loki.Setup(loki.Config{
|
|
||||||
Address: "localhost:3100/api/prom/push",
|
|
||||||
Labels: map[string]string{
|
|
||||||
"label": "test",
|
|
||||||
},
|
|
||||||
BatchWait: 1000,
|
|
||||||
BatchEntriesNumber: 200,
|
|
||||||
Enabled: true,
|
|
||||||
})
|
|
||||||
defer loki.Shutdown()
|
|
||||||
|
|
||||||
loki.Send("log message", time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
|
@ -1,50 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/logging/lokicore/loki"
|
|
||||||
)
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
const (
|
|
||||||
countMsgGroup = 100
|
|
||||||
countMsg = 500000
|
|
||||||
)
|
|
||||||
|
|
||||||
func send(loki *loki.Client) {
|
|
||||||
wg.Add(1)
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
for j := 0; j < countMsg/countMsgGroup; j++ {
|
|
||||||
for i := 0; i < countMsgGroup; i++ {
|
|
||||||
err := loki.Send(strconv.Itoa(j)+" "+strconv.Itoa(i)+" test log message", time.Now())
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "send: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time.Sleep(20 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
loki := loki.Setup(loki.Config{
|
|
||||||
Endpoint: "localhost:3100/api/prom/push",
|
|
||||||
Labels: map[string]string{
|
|
||||||
"label": "test",
|
|
||||||
},
|
|
||||||
BatchWait: 1000,
|
|
||||||
BatchEntriesNumber: 200,
|
|
||||||
Enabled: true,
|
|
||||||
})
|
|
||||||
go send(loki)
|
|
||||||
send(loki)
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
loki.Shutdown()
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
package loki
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Send sends the message to the loki server.
|
|
||||||
// If the client is disabled, it returns error.
|
|
||||||
// If the entries channel is full, the message is discarded and returns error.
|
|
||||||
func (client *Client) Send(msg string, timestamp time.Time) error {
|
|
||||||
if !client.IsEnabled() {
|
|
||||||
client.missedMessages++
|
|
||||||
return errors.New("client disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
if client.addToEntries(timestamp, msg) {
|
|
||||||
if client.missedMessages > 0 {
|
|
||||||
client.addToEntries(time.Now(), strconv.FormatInt(client.missedMessages, 10)+" messages missed")
|
|
||||||
client.missedMessages = 0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
client.missedMessages++
|
|
||||||
return errors.New("channel is full")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// addToEntries attempts to add a log entry to the entries channel.
|
|
||||||
// It returns true if the entry was added successfully, and false otherwise.
|
|
||||||
func (client *Client) addToEntries(timestamp time.Time, msg string) bool {
|
|
||||||
select {
|
|
||||||
case client.entries <- logEntry{
|
|
||||||
Ts: timestamp,
|
|
||||||
Line: msg,
|
|
||||||
}:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run manages the sending of log batches to Loki.
|
|
||||||
// It collects log entries into batches and sends them either when a batch is full,
|
|
||||||
// or when the maximum wait time has elapsed.
|
|
||||||
func (client *Client) run() {
|
|
||||||
if !client.IsEnabled() {
|
|
||||||
client.drainEntries()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client.waitGroup.Add(1)
|
|
||||||
defer client.waitGroup.Done()
|
|
||||||
|
|
||||||
var batch []logEntry
|
|
||||||
maxWait := time.NewTimer(client.config.BatchWait)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-client.quit:
|
|
||||||
for len(client.entries) > 0 {
|
|
||||||
entry := <-client.entries
|
|
||||||
batch = append(batch, entry)
|
|
||||||
}
|
|
||||||
batch = client.processMissedMessages(batch)
|
|
||||||
client.processBatch(batch)
|
|
||||||
return
|
|
||||||
|
|
||||||
case entry := <-client.entries:
|
|
||||||
batch = append(batch, entry)
|
|
||||||
if len(batch) >= client.config.BatchEntriesNumber {
|
|
||||||
batch = client.processBatch(batch)
|
|
||||||
maxWait.Reset(client.config.BatchWait)
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-maxWait.C:
|
|
||||||
batch = client.processBatch(batch)
|
|
||||||
maxWait.Reset(client.config.BatchWait)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) drainEntries() {
|
|
||||||
for len(client.entries) > 0 {
|
|
||||||
<-client.entries
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) processBatch(batch []logEntry) []logEntry {
|
|
||||||
if len(batch) > 0 {
|
|
||||||
client.sendLogs(batch)
|
|
||||||
batch = batch[:0]
|
|
||||||
}
|
|
||||||
return batch
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) processMissedMessages(batch []logEntry) []logEntry {
|
|
||||||
if client.missedMessages > 0 {
|
|
||||||
batch = append(batch, logEntry{
|
|
||||||
Ts: time.Now(),
|
|
||||||
Line: strconv.FormatInt(client.missedMessages, 10) + " messages missed",
|
|
||||||
})
|
|
||||||
client.missedMessages = 0
|
|
||||||
}
|
|
||||||
return batch
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package loki
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (client *Client) sendLogs(entries []logEntry) {
|
|
||||||
if len(entries) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var streams []stream
|
|
||||||
stream := stream{
|
|
||||||
Labels: client.config.Labels,
|
|
||||||
Entries: entries,
|
|
||||||
}
|
|
||||||
streams = append(streams, stream)
|
|
||||||
|
|
||||||
msg := promtailMsg{Streams: streams}
|
|
||||||
jsonMsg, err := json.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client.mutex.RLock()
|
|
||||||
endpoint := client.config.Endpoint
|
|
||||||
client.mutex.RUnlock()
|
|
||||||
|
|
||||||
client.sendRequest("POST", endpoint, "application/json", jsonMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) sendRequest(method, url string, ctype string, reqBody []byte) {
|
|
||||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(reqBody))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", ctype)
|
|
||||||
|
|
||||||
resp, _ := client.client.Do(req)
|
|
||||||
if resp != nil {
|
|
||||||
defer resp.Body.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *stream) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(&struct {
|
|
||||||
Labels string `json:"labels"`
|
|
||||||
Entries []logEntry `json:"entries"`
|
|
||||||
}{
|
|
||||||
Labels: p.Labels.String(),
|
|
||||||
Entries: p.Entries,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l labels) String() string {
|
|
||||||
var labelPairs []string
|
|
||||||
for key, value := range l {
|
|
||||||
labelPairs = append(labelPairs, fmt.Sprintf(`%s="%s"`, key, value))
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("{%s}", strings.Join(labelPairs, ","))
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
package loki
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const logEntriesChanSize = 5000
|
|
||||||
|
|
||||||
// Represents a single log entry.
|
|
||||||
type logEntry struct {
|
|
||||||
Ts time.Time `json:"ts"`
|
|
||||||
Line string `json:"line"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type labels map[string]string
|
|
||||||
|
|
||||||
// Stream represents a stream of log entries with associated labels.
|
|
||||||
type stream struct {
|
|
||||||
Labels labels `json:"-"`
|
|
||||||
Entries []logEntry `json:"entries"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type promtailMsg struct {
|
|
||||||
Streams []stream `json:"streams"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client is a client for sending log entries.
|
|
||||||
type Client struct {
|
|
||||||
config Config
|
|
||||||
quit chan struct{}
|
|
||||||
entries chan logEntry
|
|
||||||
waitGroup sync.WaitGroup
|
|
||||||
client http.Client
|
|
||||||
missedMessages int64
|
|
||||||
mutex sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Enabled bool
|
|
||||||
// E.g. localhost:3100/api/prom/push.
|
|
||||||
Endpoint string
|
|
||||||
Labels map[string]string
|
|
||||||
// Maximum message buffering time.
|
|
||||||
BatchWait time.Duration
|
|
||||||
// Maximum number of messages in the queue.
|
|
||||||
BatchEntriesNumber int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup initializes the client with the given configuration and starts the processing goroutine.
|
|
||||||
// It is the caller's responsibility to call Shutdown() to free resources.
|
|
||||||
func Setup(conf Config) *Client {
|
|
||||||
client := newClient()
|
|
||||||
client.config = conf
|
|
||||||
client.config.Endpoint = normalizeURL(client.config.Endpoint)
|
|
||||||
go client.run()
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown stops the client and waits for all logs to be sent.
|
|
||||||
func (client *Client) Shutdown() {
|
|
||||||
client.mutex.Lock()
|
|
||||||
client.config.Enabled = false
|
|
||||||
client.mutex.Unlock()
|
|
||||||
|
|
||||||
close(client.quit)
|
|
||||||
client.waitGroup.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEnabled checks whether the client is enabled.
|
|
||||||
func (client *Client) IsEnabled() bool {
|
|
||||||
client.mutex.RLock()
|
|
||||||
defer client.mutex.RUnlock()
|
|
||||||
return client.config.Enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
func newClient() *Client {
|
|
||||||
return &Client{
|
|
||||||
quit: make(chan struct{}),
|
|
||||||
entries: make(chan logEntry, logEntriesChanSize),
|
|
||||||
client: http.Client{},
|
|
||||||
config: Config{Enabled: false},
|
|
||||||
mutex: sync.RWMutex{},
|
|
||||||
waitGroup: sync.WaitGroup{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeURL(host string) string {
|
|
||||||
if !regexp.MustCompile(`^https?:\/\/`).MatchString(host) {
|
|
||||||
host = "http://" + host
|
|
||||||
}
|
|
||||||
|
|
||||||
return host
|
|
||||||
}
|
|
105
metrics/desc.go
105
metrics/desc.go
|
@ -1,105 +0,0 @@
|
||||||
package metrics
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Descriptions contains metric Description suitable for further processing.
|
|
||||||
// The only reason for it to exist is `prometheus.Desc` disallowing field access directly.
|
|
||||||
// https://github.com/prometheus/client_golang/pull/326
|
|
||||||
// https://github.com/prometheus/client_golang/issues/516
|
|
||||||
// https://github.com/prometheus/client_golang/issues/222
|
|
||||||
type Description struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Help string `json:"help"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
ConstantLabels prometheus.Labels `json:"constant_labels,omitempty"`
|
|
||||||
VariableLabels []string `json:"variable_labels,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGauge returns new registered prometheus.Gauge.
|
|
||||||
func NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge {
|
|
||||||
value := prometheus.NewGauge(opts)
|
|
||||||
MustRegister(value, Description{
|
|
||||||
Name: prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
|
||||||
Type: dto.MetricType_GAUGE.String(),
|
|
||||||
Help: opts.Help,
|
|
||||||
ConstantLabels: opts.ConstLabels,
|
|
||||||
})
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGaugeVec returns new registered *prometheus.GaugeVec.
|
|
||||||
func NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec {
|
|
||||||
value := prometheus.NewGaugeVec(opts, labelNames)
|
|
||||||
MustRegister(value, Description{
|
|
||||||
Name: prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
|
||||||
Type: dto.MetricType_GAUGE.String(),
|
|
||||||
Help: opts.Help,
|
|
||||||
ConstantLabels: opts.ConstLabels,
|
|
||||||
VariableLabels: labelNames,
|
|
||||||
})
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGaugeFunc returns new registered prometheus.GaugeFunc.
|
|
||||||
func NewGaugeFunc(opts prometheus.GaugeOpts, f func() float64) prometheus.GaugeFunc {
|
|
||||||
value := prometheus.NewGaugeFunc(opts, f)
|
|
||||||
MustRegister(value, Description{
|
|
||||||
Name: prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
|
||||||
Type: dto.MetricType_GAUGE.String(),
|
|
||||||
Help: opts.Help,
|
|
||||||
ConstantLabels: opts.ConstLabels,
|
|
||||||
})
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCounter returns new registered prometheus.Counter.
|
|
||||||
func NewCounter(opts prometheus.CounterOpts) prometheus.Counter {
|
|
||||||
value := prometheus.NewCounter(opts)
|
|
||||||
MustRegister(value, Description{
|
|
||||||
Name: prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: opts.Help,
|
|
||||||
ConstantLabels: opts.ConstLabels,
|
|
||||||
})
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCounterVec returns new registered *prometheus.CounterVec.
|
|
||||||
func NewCounterVec(opts prometheus.CounterOpts, labels []string) *prometheus.CounterVec {
|
|
||||||
value := prometheus.NewCounterVec(opts, labels)
|
|
||||||
MustRegister(value, Description{
|
|
||||||
Name: prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: opts.Help,
|
|
||||||
ConstantLabels: opts.ConstLabels,
|
|
||||||
VariableLabels: labels,
|
|
||||||
})
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHistogramVec returns new registered *prometheus.HistogramVec.
|
|
||||||
func NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec {
|
|
||||||
value := prometheus.NewHistogramVec(opts, labelNames)
|
|
||||||
MustRegister(value, Description{
|
|
||||||
Name: prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
|
||||||
Type: dto.MetricType_HISTOGRAM.String(),
|
|
||||||
Help: opts.Help,
|
|
||||||
ConstantLabels: opts.ConstLabels,
|
|
||||||
VariableLabels: labelNames,
|
|
||||||
})
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// DescribeAll returns descriptions for all registered metrics.
|
|
||||||
func DescribeAll() []Description {
|
|
||||||
registeredDescriptionsMtx.Lock()
|
|
||||||
defer registeredDescriptionsMtx.Unlock()
|
|
||||||
|
|
||||||
ds := make([]Description, len(registeredDescriptions))
|
|
||||||
copy(ds, registeredDescriptions)
|
|
||||||
return ds
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
package metrics
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDescribeAll(t *testing.T) {
|
|
||||||
const (
|
|
||||||
namespace = "my_ns"
|
|
||||||
subsystem = "mysub"
|
|
||||||
)
|
|
||||||
NewCounter(prometheus.CounterOpts{
|
|
||||||
Namespace: namespace,
|
|
||||||
Subsystem: subsystem,
|
|
||||||
Name: "my_counter",
|
|
||||||
})
|
|
||||||
|
|
||||||
labels := []string{"label1", "label2"}
|
|
||||||
NewGaugeVec(prometheus.GaugeOpts{
|
|
||||||
Namespace: namespace,
|
|
||||||
Subsystem: subsystem,
|
|
||||||
Name: "my_gauge",
|
|
||||||
}, labels)
|
|
||||||
|
|
||||||
constLabels := prometheus.Labels{
|
|
||||||
"const1": "abc",
|
|
||||||
"const2": "xyz",
|
|
||||||
}
|
|
||||||
NewCounter(prometheus.CounterOpts{
|
|
||||||
Namespace: namespace,
|
|
||||||
Subsystem: subsystem,
|
|
||||||
Name: "with_const_labels",
|
|
||||||
ConstLabels: constLabels,
|
|
||||||
})
|
|
||||||
|
|
||||||
descriptions := DescribeAll()
|
|
||||||
|
|
||||||
seen := make(map[string]bool)
|
|
||||||
for i := range descriptions {
|
|
||||||
if !strings.HasPrefix(descriptions[i].Name, namespace) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
require.False(t, seen[descriptions[i].Name], "metric %s was seen twice", descriptions[i].Name)
|
|
||||||
seen[descriptions[i].Name] = true
|
|
||||||
|
|
||||||
switch descriptions[i].Name {
|
|
||||||
case prometheus.BuildFQName(namespace, subsystem, "my_counter"):
|
|
||||||
require.True(t, len(descriptions[i].VariableLabels) == 0)
|
|
||||||
case prometheus.BuildFQName(namespace, subsystem, "my_gauge"):
|
|
||||||
require.Equal(t, labels, descriptions[i].VariableLabels)
|
|
||||||
case prometheus.BuildFQName(namespace, subsystem, "with_const_labels"):
|
|
||||||
require.Equal(t, len(constLabels), len(descriptions[i].ConstantLabels))
|
|
||||||
require.Equal(t, constLabels, descriptions[i].ConstantLabels)
|
|
||||||
default:
|
|
||||||
require.FailNow(t, "unexpected metric name: %s", descriptions[i].Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
require.Equal(t, 3, len(seen), "not all registered metrics were iterated over")
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/metrics"
|
|
||||||
grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var clientMetrics = grpcprom.NewClientMetrics(
|
|
||||||
grpcprom.WithClientHandlingTimeHistogram(
|
|
||||||
grpcprom.WithHistogramBuckets(prometheus.DefBuckets),
|
|
||||||
),
|
|
||||||
grpcprom.WithClientStreamRecvHistogram(
|
|
||||||
grpcprom.WithHistogramBuckets(prometheus.DefBuckets),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Description copied from repository of grpc-ecosystem
|
|
||||||
// https://github.com/grpc-ecosystem/go-grpc-middleware/blob/71d7422112b1d7fadd4b8bf12a6f33ba6d22e98e/providers/prometheus/client_metrics.go#L31
|
|
||||||
descs := []metrics.Description{
|
|
||||||
{
|
|
||||||
Name: "grpc_client_started_total",
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: "Total number of RPCs started on the client.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "grpc_client_handled_total",
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: "Total number of RPCs completed by the client, regardless of success or failure.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "grpc_client_msg_received_total",
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: "Total number of RPC stream messages received by the client.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "grpc_client_msg_sent_total",
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: "Total number of gRPC stream messages sent by the client.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "grpc_client_handling_seconds",
|
|
||||||
Type: dto.MetricType_HISTOGRAM.String(),
|
|
||||||
Help: "Histogram of response latency (seconds) of the gRPC until it is finished by the application.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "grpc_client_msg_recv_handling_seconds",
|
|
||||||
Type: dto.MetricType_HISTOGRAM.String(),
|
|
||||||
Help: "Histogram of response latency (seconds) of the gRPC single message receive.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
metrics.MustRegister(clientMetrics, descs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUnaryClientInterceptor returns client interceptor to collect metrics from unary RPCs.
|
|
||||||
func NewUnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
|
||||||
return clientMetrics.UnaryClientInterceptor()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStreamClientInterceptor returns client interceptor to collect metrics from stream RPCs.
|
|
||||||
func NewStreamClientInterceptor() grpc.StreamClientInterceptor {
|
|
||||||
return clientMetrics.StreamClientInterceptor()
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/metrics"
|
|
||||||
grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var serverMetrics = grpcprom.NewServerMetrics(
|
|
||||||
grpcprom.WithServerHandlingTimeHistogram(
|
|
||||||
grpcprom.WithHistogramBuckets(prometheus.DefBuckets),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Description copied from grpc-ecosystem:
|
|
||||||
// https://github.com/grpc-ecosystem/go-grpc-middleware/blob/71d7422112b1d7fadd4b8bf12a6f33ba6d22e98e/providers/prometheus/server_metrics.go#L26
|
|
||||||
descs := []metrics.Description{
|
|
||||||
{
|
|
||||||
Name: "grpc_server_started_total",
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: "Total number of RPCs started on the server.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "grpc_server_handled_total",
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: "Total number of RPCs completed on the server, regardless of success or failure.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "grpc_server_msg_received_total",
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: "Total number of RPC stream messages received on the server.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "grpc_server_msg_sent_total",
|
|
||||||
Type: dto.MetricType_COUNTER.String(),
|
|
||||||
Help: "Total number of gRPC stream messages sent by the server.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "grpc_server_handling_seconds",
|
|
||||||
Type: dto.MetricType_HISTOGRAM.String(),
|
|
||||||
Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.",
|
|
||||||
VariableLabels: []string{"grpc_type", "grpc_service", "grpc_method"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
metrics.MustRegister(serverMetrics, descs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUnaryServerInterceptor returns server interceptor to collect metrics from unary RPCs.
|
|
||||||
func NewUnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
|
||||||
return serverMetrics.UnaryServerInterceptor()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStreamServerInterceptor returns server interceptor to collect metrics from stream RPCs.
|
|
||||||
func NewStreamServerInterceptor() grpc.StreamServerInterceptor {
|
|
||||||
return serverMetrics.StreamServerInterceptor()
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package metrics
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
registry = prometheus.NewRegistry()
|
|
||||||
// registeredDescriptionsMtx protects collectors slice.
|
|
||||||
// It should not be acessed concurrently, but we can easily forget this in future, thus this mutex.
|
|
||||||
registeredDescriptionsMtx sync.Mutex
|
|
||||||
registeredDescriptions []Description
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
registry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
|
|
||||||
registry.MustRegister(collectors.NewGoCollector())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register registers custom collectors to registry.
|
|
||||||
// Should be used with metrics from other packages.
|
|
||||||
func Register(customCollectors ...prometheus.Collector) {
|
|
||||||
registry.MustRegister(customCollectors...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MustRegister(c prometheus.Collector, descs ...Description) {
|
|
||||||
registry.MustRegister(c)
|
|
||||||
registeredDescriptionsMtx.Lock()
|
|
||||||
defer registeredDescriptionsMtx.Unlock()
|
|
||||||
registeredDescriptions = append(registeredDescriptions, descs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handler returns an http.Handler for the local registry.
|
|
||||||
func Handler() http.Handler {
|
|
||||||
promhttp.Handler()
|
|
||||||
return promhttp.InstrumentMetricHandler(
|
|
||||||
registry,
|
|
||||||
promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
|
|
||||||
}
|
|
0
testdata/tracing/invalid_empty_root_ca.pem
vendored
0
testdata/tracing/invalid_empty_root_ca.pem
vendored
1
testdata/tracing/invalid_root_ca.pem
vendored
1
testdata/tracing/invalid_root_ca.pem
vendored
|
@ -1 +0,0 @@
|
||||||
invalid content
|
|
|
@ -1,12 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD
|
|
||||||
VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh
|
|
||||||
bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw
|
|
||||||
MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g
|
|
||||||
UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT
|
|
||||||
BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx
|
|
||||||
uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV
|
|
||||||
HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/
|
|
||||||
+wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147
|
|
||||||
bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm
|
|
||||||
-----END CERTIFICATE-----
|
|
|
@ -1,13 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD
|
|
||||||
VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
|
|
||||||
A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
|
|
||||||
WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz
|
|
||||||
IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
|
|
||||||
AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi
|
|
||||||
QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR
|
|
||||||
HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
|
|
||||||
BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D
|
|
||||||
9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8
|
|
||||||
p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD
|
|
||||||
-----END CERTIFICATE-----
|
|
|
@ -1,90 +0,0 @@
|
||||||
package tracing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/x509"
|
|
||||||
"fmt"
|
|
||||||
"maps"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Exporter is type of tracing target.
|
|
||||||
type Exporter string
|
|
||||||
|
|
||||||
const (
|
|
||||||
StdoutExporter Exporter = "stdout"
|
|
||||||
OTLPgRPCExporter Exporter = "otlp_grpc"
|
|
||||||
NoOpExporter Exporter = "noop"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
// Enabled is true, if tracing enabled.
|
|
||||||
Enabled bool
|
|
||||||
// Exporter is collector type.
|
|
||||||
Exporter Exporter
|
|
||||||
// Endpoint is collector endpoint for OTLP exporters.
|
|
||||||
Endpoint string
|
|
||||||
// ServerCaCertPool is cert pool of the remote server CA certificate. Use for TLS setup.
|
|
||||||
ServerCaCertPool *x509.CertPool
|
|
||||||
|
|
||||||
// Service is service name that will be used in tracing.
|
|
||||||
// Mandatory.
|
|
||||||
Service string
|
|
||||||
// InstanceID is identity of service instance.
|
|
||||||
// Optional.
|
|
||||||
InstanceID string
|
|
||||||
// Version is version of service instance.
|
|
||||||
// Optional.
|
|
||||||
Version string
|
|
||||||
// Attributes is KV list of attributes.
|
|
||||||
// Optional.
|
|
||||||
Attributes map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) validate() error {
|
|
||||||
if !c.Enabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Exporter != StdoutExporter && c.Exporter != OTLPgRPCExporter && c.Exporter != NoOpExporter {
|
|
||||||
return fmt.Errorf("tracing config error: unknown exporter '%s', valid values are %v",
|
|
||||||
c.Exporter, []string{string(StdoutExporter), string(OTLPgRPCExporter), string(NoOpExporter)})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.Service) == 0 {
|
|
||||||
return fmt.Errorf("tracing config error: service name must be specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Exporter == OTLPgRPCExporter && len(c.Endpoint) == 0 {
|
|
||||||
return fmt.Errorf("tracing config error: exporter '%s' requires endpoint", c.Exporter)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) hasChange(other *Config) bool {
|
|
||||||
if !c.Enabled && !other.Enabled {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if c.Enabled != other.Enabled {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c.Exporter == StdoutExporter && other.Exporter == StdoutExporter) ||
|
|
||||||
(c.Exporter == NoOpExporter && other.Exporter == NoOpExporter) {
|
|
||||||
return !c.serviceInfoEqual(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
if other.Exporter == OTLPgRPCExporter && !c.ServerCaCertPool.Equal(other.ServerCaCertPool) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Exporter != other.Exporter ||
|
|
||||||
c.Endpoint != other.Endpoint ||
|
|
||||||
!c.serviceInfoEqual(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) serviceInfoEqual(other *Config) bool {
|
|
||||||
return c.Service == other.Service &&
|
|
||||||
c.InstanceID == other.InstanceID &&
|
|
||||||
c.Version == other.Version &&
|
|
||||||
maps.Equal(c.Attributes, other.Attributes)
|
|
||||||
}
|
|
|
@ -1,278 +0,0 @@
|
||||||
package tracing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/x509"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestConfig_validate(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
config Config
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "disabled",
|
|
||||||
wantErr: false,
|
|
||||||
config: Config{
|
|
||||||
Enabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "stdout",
|
|
||||||
wantErr: false,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: StdoutExporter,
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "noop",
|
|
||||||
wantErr: false,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: StdoutExporter,
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "OTLP gRPC",
|
|
||||||
wantErr: false,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Service: "test",
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown exporter",
|
|
||||||
wantErr: true,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: "unknown",
|
|
||||||
Service: "test",
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no exporter",
|
|
||||||
wantErr: true,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Service: "test",
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no service",
|
|
||||||
wantErr: true,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no endpoint for grpc",
|
|
||||||
wantErr: true,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v0.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if err := tt.config.validate(); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Config.validate() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_hasChange(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
config Config
|
|
||||||
other Config
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "disabled configs always equal",
|
|
||||||
want: false,
|
|
||||||
config: Config{
|
|
||||||
Enabled: false,
|
|
||||||
Exporter: StdoutExporter,
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
other: Config{
|
|
||||||
Enabled: false,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "enabled",
|
|
||||||
want: true,
|
|
||||||
config: Config{
|
|
||||||
Enabled: false,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
other: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "disabled",
|
|
||||||
want: true,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
other: Config{
|
|
||||||
Enabled: false,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "do not use endpoint for stdout",
|
|
||||||
want: false,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: StdoutExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
other: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: StdoutExporter,
|
|
||||||
Endpoint: "otherhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "do not use endpoint for noop",
|
|
||||||
want: false,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: NoOpExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
other: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: NoOpExporter,
|
|
||||||
Endpoint: "otherhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "use endpoint for grpc",
|
|
||||||
want: true,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
other: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "otherhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "use tls root ca certificate for grpc",
|
|
||||||
want: true,
|
|
||||||
config: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
ServerCaCertPool: readCertPoolByPath(t, "../testdata/tracing/valid_google_globalsign_r4_rsa_root_ca.pem"),
|
|
||||||
},
|
|
||||||
other: Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: OTLPgRPCExporter,
|
|
||||||
Endpoint: "localhost:4717",
|
|
||||||
Service: "test",
|
|
||||||
InstanceID: "s01",
|
|
||||||
Version: "v1.0.0",
|
|
||||||
ServerCaCertPool: readCertPoolByPath(t, "../testdata/tracing/valid_google_gts_r4_ecdsa_root_ca.pem"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := tt.config.hasChange(&tt.other); got != tt.want {
|
|
||||||
t.Errorf("Config.equal() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readCertPoolByPath(t *testing.T, path string) *x509.CertPool {
|
|
||||||
ca, err := os.ReadFile(path)
|
|
||||||
require.NoError(t, err)
|
|
||||||
roots := x509.NewCertPool()
|
|
||||||
ok := roots.AppendCertsFromPEM(ca)
|
|
||||||
require.True(t, ok)
|
|
||||||
return roots
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
|
||||||
srv "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/examples/grpc/server"
|
|
||||||
tracing_grpc "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
|
||||||
)
|
|
||||||
|
|
||||||
type server struct {
|
|
||||||
srv.UnimplementedServerServer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *server) Echo(ctx context.Context, req *srv.Request) (*srv.Response, error) {
|
|
||||||
sc := trace.SpanFromContext(ctx).SpanContext()
|
|
||||||
if !sc.TraceID().IsValid() || !sc.SpanID().IsValid() {
|
|
||||||
return nil, fmt.Errorf("no trace id or span id on server side")
|
|
||||||
}
|
|
||||||
log.Printf("server trace id: %v", sc.TraceID())
|
|
||||||
log.Printf("server span id: %v", sc.SpanID())
|
|
||||||
return &srv.Response{
|
|
||||||
Value: req.GetValue(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyClientTraceID(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
|
||||||
sc := trace.SpanFromContext(ctx).SpanContext()
|
|
||||||
if !sc.TraceID().IsValid() || !sc.SpanID().IsValid() {
|
|
||||||
return fmt.Errorf("no trace id or span id on client side")
|
|
||||||
}
|
|
||||||
log.Printf("client trace id: %v", sc.TraceID())
|
|
||||||
log.Printf("client span id: %v", sc.SpanID())
|
|
||||||
return invoker(ctx, method, req, reply, cc, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
tracingCfg := tracing.Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: tracing.NoOpExporter,
|
|
||||||
Service: "example-grpc",
|
|
||||||
}
|
|
||||||
enabled, err := tracing.Setup(ctx, tracingCfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to setup tracing: %v", err)
|
|
||||||
}
|
|
||||||
if !enabled {
|
|
||||||
log.Fatalf("failed to enable tracing")
|
|
||||||
}
|
|
||||||
|
|
||||||
lis, err := net.Listen("tcp", ":7000")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to listen: %v", err)
|
|
||||||
}
|
|
||||||
s := grpc.NewServer(
|
|
||||||
grpc.ChainStreamInterceptor(tracing_grpc.NewStreamServerInterceptor()),
|
|
||||||
grpc.ChainUnaryInterceptor(tracing_grpc.NewUnaryServerInterceptor()),
|
|
||||||
)
|
|
||||||
srv.RegisterServerServer(s, &server{})
|
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
if err := s.Serve(lis); err != nil {
|
|
||||||
log.Fatalf("failed to serve: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
|
|
||||||
cc, err := grpc.NewClient(":7000",
|
|
||||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
|
||||||
grpc.WithChainUnaryInterceptor(
|
|
||||||
tracing_grpc.NewUnaryClientInteceptor(),
|
|
||||||
verifyClientTraceID,
|
|
||||||
),
|
|
||||||
grpc.WithChainStreamInterceptor(
|
|
||||||
tracing_grpc.NewStreamClientInterceptor(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to dial: %v", err)
|
|
||||||
}
|
|
||||||
client := srv.NewServerClient(cc)
|
|
||||||
resp, err := client.Echo(ctx, &srv.Request{
|
|
||||||
Value: "Hello!",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to get response: %v", err)
|
|
||||||
}
|
|
||||||
log.Printf("response received: %s", resp.GetValue())
|
|
||||||
|
|
||||||
s.GracefulStop()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
|
@ -1,297 +0,0 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.26.0
|
|
||||||
// protoc v3.21.9
|
|
||||||
// source: server/server.proto
|
|
||||||
|
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
context "context"
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
codes "google.golang.org/grpc/codes"
|
|
||||||
status "google.golang.org/grpc/status"
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Request struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) Reset() {
|
|
||||||
*x = Request{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_server_server_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Request) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Request) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_server_server_proto_msgTypes[0]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Request.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Request) Descriptor() ([]byte, []int) {
|
|
||||||
return file_server_server_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Request) GetValue() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Value
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type Response struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Response) Reset() {
|
|
||||||
*x = Response{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_server_server_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Response) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Response) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Response) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_server_server_proto_msgTypes[1]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Response.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Response) Descriptor() ([]byte, []int) {
|
|
||||||
return file_server_server_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Response) GetValue() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Value
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_server_server_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_server_server_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
|
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x1f, 0x0a,
|
|
||||||
0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
|
||||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x20,
|
|
||||||
0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
|
|
||||||
0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
|
||||||
0x32, 0x33, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x45, 0x63,
|
|
||||||
0x68, 0x6f, 0x12, 0x0f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75,
|
|
||||||
0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73,
|
|
||||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x2e, 0x66, 0x72, 0x6f,
|
|
||||||
0x73, 0x74, 0x66, 0x73, 0x2e, 0x69, 0x6e, 0x66, 0x6f, 0x2f, 0x54, 0x72, 0x75, 0x65, 0x43, 0x6c,
|
|
||||||
0x6f, 0x75, 0x64, 0x4c, 0x61, 0x62, 0x2f, 0x66, 0x72, 0x6f, 0x73, 0x74, 0x66, 0x73, 0x2d, 0x70,
|
|
||||||
0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x2f,
|
|
||||||
0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x65, 0x72,
|
|
||||||
0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_server_server_proto_rawDescOnce sync.Once
|
|
||||||
file_server_server_proto_rawDescData = file_server_server_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_server_server_proto_rawDescGZIP() []byte {
|
|
||||||
file_server_server_proto_rawDescOnce.Do(func() {
|
|
||||||
file_server_server_proto_rawDescData = protoimpl.X.CompressGZIP(file_server_server_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_server_server_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_server_server_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
|
||||||
var file_server_server_proto_goTypes = []interface{}{
|
|
||||||
(*Request)(nil), // 0: server.Request
|
|
||||||
(*Response)(nil), // 1: server.Response
|
|
||||||
}
|
|
||||||
var file_server_server_proto_depIdxs = []int32{
|
|
||||||
0, // 0: server.Server.Echo:input_type -> server.Request
|
|
||||||
1, // 1: server.Server.Echo:output_type -> server.Response
|
|
||||||
1, // [1:2] is the sub-list for method output_type
|
|
||||||
0, // [0:1] is the sub-list for method input_type
|
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
|
||||||
0, // [0:0] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_server_server_proto_init() }
|
|
||||||
func file_server_server_proto_init() {
|
|
||||||
if File_server_server_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !protoimpl.UnsafeEnabled {
|
|
||||||
file_server_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Request); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_server_server_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Response); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_server_server_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 2,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 1,
|
|
||||||
},
|
|
||||||
GoTypes: file_server_server_proto_goTypes,
|
|
||||||
DependencyIndexes: file_server_server_proto_depIdxs,
|
|
||||||
MessageInfos: file_server_server_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_server_server_proto = out.File
|
|
||||||
file_server_server_proto_rawDesc = nil
|
|
||||||
file_server_server_proto_goTypes = nil
|
|
||||||
file_server_server_proto_depIdxs = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ context.Context
|
|
||||||
var _ grpc.ClientConnInterface
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the grpc package it is being compiled against.
|
|
||||||
const _ = grpc.SupportPackageIsVersion6
|
|
||||||
|
|
||||||
// ServerClient is the client API for Server service.
|
|
||||||
//
|
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
|
||||||
type ServerClient interface {
|
|
||||||
Echo(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type serverClient struct {
|
|
||||||
cc grpc.ClientConnInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServerClient(cc grpc.ClientConnInterface) ServerClient {
|
|
||||||
return &serverClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverClient) Echo(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
|
|
||||||
out := new(Response)
|
|
||||||
err := c.cc.Invoke(ctx, "/server.Server/Echo", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerServer is the server API for Server service.
|
|
||||||
type ServerServer interface {
|
|
||||||
Echo(context.Context, *Request) (*Response, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnimplementedServerServer can be embedded to have forward compatible implementations.
|
|
||||||
type UnimplementedServerServer struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*UnimplementedServerServer) Echo(context.Context, *Request) (*Response, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterServerServer(s *grpc.Server, srv ServerServer) {
|
|
||||||
s.RegisterService(&_Server_serviceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _Server_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(Request)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(ServerServer).Echo(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/server.Server/Echo",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(ServerServer).Echo(ctx, req.(*Request))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _Server_serviceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "server.Server",
|
|
||||||
HandlerType: (*ServerServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "Echo",
|
|
||||||
Handler: _Server_Echo_Handler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Streams: []grpc.StreamDesc{},
|
|
||||||
Metadata: "server/server.proto",
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package server;
|
|
||||||
|
|
||||||
option go_package = "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/example_grpc/server";
|
|
||||||
|
|
||||||
service Server {
|
|
||||||
rpc Echo(Request) returns (Response);
|
|
||||||
}
|
|
||||||
|
|
||||||
message Request {
|
|
||||||
string value = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Response {
|
|
||||||
string value = 1;
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
type grpcMetadataCarrier struct {
|
|
||||||
md *metadata.MD
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *grpcMetadataCarrier) Get(key string) string {
|
|
||||||
values := c.md.Get(key)
|
|
||||||
if len(values) > 0 {
|
|
||||||
return values[0]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *grpcMetadataCarrier) Set(key string, value string) {
|
|
||||||
c.md.Set(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *grpcMetadataCarrier) Keys() []string {
|
|
||||||
result := make([]string, 0, c.md.Len())
|
|
||||||
for key := range *c.md {
|
|
||||||
result = append(result, key)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientStream struct {
|
|
||||||
originalStream grpc.ClientStream
|
|
||||||
desc *grpc.StreamDesc
|
|
||||||
span trace.Span
|
|
||||||
finished chan<- error
|
|
||||||
done <-chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newgRPCClientStream(originalStream grpc.ClientStream, desc *grpc.StreamDesc, span trace.Span, finished chan<- error, done <-chan struct{}) grpc.ClientStream {
|
|
||||||
return &clientStream{
|
|
||||||
originalStream: originalStream,
|
|
||||||
desc: desc,
|
|
||||||
span: span,
|
|
||||||
finished: finished,
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *clientStream) Header() (metadata.MD, error) {
|
|
||||||
md, err := cs.originalStream.Header()
|
|
||||||
if err != nil {
|
|
||||||
select {
|
|
||||||
case <-cs.done:
|
|
||||||
case cs.finished <- err:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return md, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *clientStream) Trailer() metadata.MD {
|
|
||||||
return cs.originalStream.Trailer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *clientStream) CloseSend() error {
|
|
||||||
cs.span.AddEvent("client.stream.close.send.start")
|
|
||||||
err := cs.originalStream.CloseSend()
|
|
||||||
if err != nil {
|
|
||||||
select {
|
|
||||||
case <-cs.done:
|
|
||||||
case cs.finished <- err:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cs.span.AddEvent("client.stream.close.send.finish")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *clientStream) Context() context.Context {
|
|
||||||
return cs.originalStream.Context()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *clientStream) SendMsg(m any) error {
|
|
||||||
cs.span.AddEvent("client.stream.send.msg.start", trace.WithAttributes(
|
|
||||||
attribute.String("message.type", fmt.Sprintf("%T", m))),
|
|
||||||
)
|
|
||||||
err := cs.originalStream.SendMsg(m)
|
|
||||||
if err != nil {
|
|
||||||
select {
|
|
||||||
case <-cs.done:
|
|
||||||
case cs.finished <- err:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cs.span.AddEvent("client.stream.send.msg.finish", trace.WithAttributes(
|
|
||||||
attribute.String("message.type", fmt.Sprintf("%T", m))),
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *clientStream) RecvMsg(m any) error {
|
|
||||||
cs.span.AddEvent("client.stream.receive.msg.start", trace.WithAttributes(
|
|
||||||
attribute.String("message.type", fmt.Sprintf("%T", m))),
|
|
||||||
)
|
|
||||||
err := cs.originalStream.RecvMsg(m)
|
|
||||||
if err != nil || !cs.desc.ServerStreams {
|
|
||||||
select {
|
|
||||||
case <-cs.done:
|
|
||||||
case cs.finished <- err:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cs.span.AddEvent("client.stream.receive.msg.finish", trace.WithAttributes(
|
|
||||||
attribute.String("message.type", fmt.Sprintf("%T", m))),
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,160 +0,0 @@
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
"go.opentelemetry.io/otel/codes"
|
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
grpc_codes "google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewUnaryClientInteceptor creates new gRPC unary interceptor to save gRPC client traces.
|
|
||||||
func NewUnaryClientInteceptor() grpc.UnaryClientInterceptor {
|
|
||||||
return func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
|
||||||
ctx, span := startClientSpan(ctx, cc, method)
|
|
||||||
defer span.End()
|
|
||||||
|
|
||||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
|
||||||
if err != nil {
|
|
||||||
grpcStatus, _ := status.FromError(err)
|
|
||||||
span.SetStatus(codes.Error, grpcStatus.Message())
|
|
||||||
span.SetAttributes(semconv.RPCGRPCStatusCodeKey.Int64(int64(grpcStatus.Code())))
|
|
||||||
} else {
|
|
||||||
span.SetStatus(codes.Ok, "")
|
|
||||||
span.SetAttributes(semconv.RPCGRPCStatusCodeKey.Int64(int64(grpc_codes.OK)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStreamClientInterceptor creates new gRPC stream interceptor to save gRPC client traces.
|
|
||||||
func NewStreamClientInterceptor() grpc.StreamClientInterceptor {
|
|
||||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
||||||
ctx, span := startClientSpan(ctx, cc, method)
|
|
||||||
str, err := streamer(ctx, desc, cc, method, opts...)
|
|
||||||
if err != nil {
|
|
||||||
grpcStatus, _ := status.FromError(err)
|
|
||||||
span.SetStatus(codes.Error, grpcStatus.Message())
|
|
||||||
span.SetAttributes(semconv.RPCGRPCStatusCodeKey.Int64(int64(grpcStatus.Code())))
|
|
||||||
span.End()
|
|
||||||
return str, err
|
|
||||||
}
|
|
||||||
|
|
||||||
finished := make(chan error)
|
|
||||||
done := make(chan struct{})
|
|
||||||
strWrp := newgRPCClientStream(str, desc, span, finished, done)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer close(done)
|
|
||||||
defer span.End()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err := <-finished:
|
|
||||||
if err == nil || err == io.EOF {
|
|
||||||
setGRPCSpanStatus(span, nil)
|
|
||||||
} else {
|
|
||||||
setGRPCSpanStatus(span, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case <-ctx.Done():
|
|
||||||
setGRPCSpanStatus(span, ctx.Err())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return strWrp, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUnaryServerInterceptor creates new gRPC unary interceptor to save gRPC server traces.
|
|
||||||
func NewUnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
|
||||||
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
|
||||||
ctx = extractGRPCTraceInfo(ctx)
|
|
||||||
var span trace.Span
|
|
||||||
ctx, span = tracing.StartSpanFromContext(ctx, info.FullMethod,
|
|
||||||
trace.WithAttributes(
|
|
||||||
semconv.RPCSystemGRPC,
|
|
||||||
semconv.RPCMethod(info.FullMethod),
|
|
||||||
),
|
|
||||||
trace.WithSpanKind(trace.SpanKindServer))
|
|
||||||
defer span.End()
|
|
||||||
|
|
||||||
resp, err = handler(ctx, req)
|
|
||||||
|
|
||||||
setGRPCSpanStatus(span, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStreamServerInterceptor creates new gRPC stream interceptor to save gRPC server traces.
|
|
||||||
func NewStreamServerInterceptor() grpc.StreamServerInterceptor {
|
|
||||||
return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
|
||||||
ctx := extractGRPCTraceInfo(ss.Context())
|
|
||||||
var span trace.Span
|
|
||||||
ctx, span = tracing.StartSpanFromContext(ctx, info.FullMethod,
|
|
||||||
trace.WithAttributes(
|
|
||||||
semconv.RPCSystemGRPC,
|
|
||||||
semconv.RPCMethod(info.FullMethod),
|
|
||||||
),
|
|
||||||
trace.WithSpanKind(trace.SpanKindServer))
|
|
||||||
defer span.End()
|
|
||||||
|
|
||||||
err := handler(srv, newgRPCServerStream(ctx, ss, span))
|
|
||||||
|
|
||||||
setGRPCSpanStatus(span, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func startClientSpan(ctx context.Context, cc *grpc.ClientConn, method string) (context.Context, trace.Span) {
|
|
||||||
ctx, span := tracing.StartSpanFromContext(ctx, method, trace.WithAttributes(
|
|
||||||
semconv.RPCSystemGRPC,
|
|
||||||
semconv.RPCMethod(method),
|
|
||||||
attribute.String("rpc.grpc.target", cc.Target())),
|
|
||||||
trace.WithSpanKind(trace.SpanKindClient),
|
|
||||||
)
|
|
||||||
ctx = setGRPCTraceInfo(ctx)
|
|
||||||
return ctx, span
|
|
||||||
}
|
|
||||||
|
|
||||||
func setGRPCSpanStatus(span trace.Span, err error) {
|
|
||||||
if err != nil {
|
|
||||||
grpcStatus, _ := status.FromError(err)
|
|
||||||
span.SetStatus(codes.Error, grpcStatus.Message())
|
|
||||||
span.SetAttributes(semconv.RPCGRPCStatusCodeKey.Int64(int64(grpcStatus.Code())))
|
|
||||||
} else {
|
|
||||||
span.SetStatus(codes.Ok, "")
|
|
||||||
span.SetAttributes(semconv.RPCGRPCStatusCodeKey.Int64(int64(grpc_codes.OK)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractGRPCTraceInfo(ctx context.Context) context.Context {
|
|
||||||
md, ok := metadata.FromIncomingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
carrier := &grpcMetadataCarrier{
|
|
||||||
md: &md,
|
|
||||||
}
|
|
||||||
return tracing.Propagator.Extract(ctx, carrier)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setGRPCTraceInfo(ctx context.Context) context.Context {
|
|
||||||
md, ok := metadata.FromOutgoingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
md = metadata.MD{}
|
|
||||||
}
|
|
||||||
carrier := &grpcMetadataCarrier{
|
|
||||||
md: &md,
|
|
||||||
}
|
|
||||||
tracing.Propagator.Inject(ctx, carrier)
|
|
||||||
return metadata.NewOutgoingContext(ctx, md)
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
type serverStream struct {
|
|
||||||
originalStream grpc.ServerStream
|
|
||||||
ctx context.Context // nolint:containedctx
|
|
||||||
span trace.Span
|
|
||||||
}
|
|
||||||
|
|
||||||
func newgRPCServerStream(ctx context.Context, originalStream grpc.ServerStream, span trace.Span) grpc.ServerStream {
|
|
||||||
return &serverStream{
|
|
||||||
originalStream: originalStream,
|
|
||||||
ctx: ctx,
|
|
||||||
span: span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *serverStream) SetHeader(md metadata.MD) error {
|
|
||||||
return ss.originalStream.SendHeader(md)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *serverStream) SendHeader(md metadata.MD) error {
|
|
||||||
return ss.originalStream.SendHeader(md)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *serverStream) SetTrailer(md metadata.MD) {
|
|
||||||
ss.originalStream.SetTrailer(md)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *serverStream) Context() context.Context {
|
|
||||||
return ss.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *serverStream) SendMsg(m any) error {
|
|
||||||
ss.span.AddEvent("server.stream.send.msg.start", trace.WithAttributes(
|
|
||||||
attribute.String("message.type", fmt.Sprintf("%T", m))),
|
|
||||||
)
|
|
||||||
err := ss.originalStream.SendMsg(m)
|
|
||||||
ss.span.AddEvent("server.stream.send.msg.finish", trace.WithAttributes(
|
|
||||||
attribute.String("message.type", fmt.Sprintf("%T", m))),
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *serverStream) RecvMsg(m any) error {
|
|
||||||
ss.span.AddEvent("server.stream.receive.msg.start", trace.WithAttributes(
|
|
||||||
attribute.String("message.type", fmt.Sprintf("%T", m))),
|
|
||||||
)
|
|
||||||
err := ss.originalStream.RecvMsg(m)
|
|
||||||
ss.span.AddEvent("server.stream.receive.msg.finish", trace.WithAttributes(
|
|
||||||
attribute.String("message.type", fmt.Sprintf("%T", m))),
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
package tracing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/propagation"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
traceIDHeader = "x-frostfs-trace-id"
|
|
||||||
spanIDHeader = "x-frostfs-span-id"
|
|
||||||
flagsHeader = "x-frostfs-trace-flags"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
flagsSampled = 1 << iota
|
|
||||||
)
|
|
||||||
|
|
||||||
// propagator serializes SpanContext to/from headers.
|
|
||||||
// x-frostfs-trace-id - TraceID, 16 bytes, hex-string (32 bytes).
|
|
||||||
// x-frostfs-span-id - SpanID, 8 bytes, hexstring (16 bytes).
|
|
||||||
// x-frostfs-trace-flags - trace flags (now sampled only).
|
|
||||||
type propagator struct{}
|
|
||||||
|
|
||||||
// Propagator is propagation.TextMapPropagator instance, used to extract/inject trace info from/to remote context.
|
|
||||||
var Propagator propagation.TextMapPropagator = &propagator{}
|
|
||||||
|
|
||||||
// Inject injects tracing info to carrier.
|
|
||||||
func (p *propagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
|
|
||||||
sc := trace.SpanFromContext(ctx).SpanContext()
|
|
||||||
if !sc.TraceID().IsValid() || !sc.SpanID().IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var flags int
|
|
||||||
if sc.IsSampled() {
|
|
||||||
flags |= flagsSampled
|
|
||||||
}
|
|
||||||
|
|
||||||
carrier.Set(traceIDHeader, sc.TraceID().String())
|
|
||||||
carrier.Set(spanIDHeader, sc.SpanID().String())
|
|
||||||
carrier.Set(flagsHeader, fmt.Sprintf("%x", flags))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract extracts tracing info from carrier and returns context with tracing info.
|
|
||||||
// In case of error returns ctx.
|
|
||||||
func (p *propagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
|
|
||||||
spanConfig := trace.SpanContextConfig{}
|
|
||||||
var err error
|
|
||||||
|
|
||||||
traceIDStr := carrier.Get(traceIDHeader)
|
|
||||||
traceIDDefined := traceIDStr != ""
|
|
||||||
if traceIDDefined {
|
|
||||||
spanConfig.TraceID, err = trace.TraceIDFromHex(traceIDStr)
|
|
||||||
if err != nil {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spanIDstr := carrier.Get(spanIDHeader)
|
|
||||||
spanIDDefined := spanIDstr != ""
|
|
||||||
if spanIDDefined {
|
|
||||||
spanConfig.SpanID, err = trace.SpanIDFromHex(spanIDstr)
|
|
||||||
if err != nil {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if traceIDDefined != spanIDDefined {
|
|
||||||
return ctx // traceID + spanID must be defined OR no traceID and no spanID
|
|
||||||
}
|
|
||||||
|
|
||||||
flagsStr := carrier.Get(flagsHeader)
|
|
||||||
if flagsStr != "" {
|
|
||||||
var v int64
|
|
||||||
v, err = strconv.ParseInt(flagsStr, 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
if v&flagsSampled == flagsSampled {
|
|
||||||
spanConfig.TraceFlags = trace.FlagsSampled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return trace.ContextWithRemoteSpanContext(ctx, trace.NewSpanContext(spanConfig))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fields returns the keys whose values are set with Inject.
|
|
||||||
func (p *propagator) Fields() []string {
|
|
||||||
return []string{traceIDHeader, spanIDHeader, flagsHeader}
|
|
||||||
}
|
|
|
@ -1,256 +0,0 @@
|
||||||
package tracing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testCarrier struct {
|
|
||||||
Values map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testCarrier) Get(key string) string {
|
|
||||||
return c.Values[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testCarrier) Set(key string, value string) {
|
|
||||||
c.Values[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *testCarrier) Keys() []string {
|
|
||||||
res := make([]string, 0, len(c.Values))
|
|
||||||
for k := range c.Values {
|
|
||||||
res = append(res, k)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
var p = &propagator{}
|
|
||||||
|
|
||||||
func TestPropagator_Inject(t *testing.T) {
|
|
||||||
t.Run("injects trace_id and span_id if valid", func(t *testing.T) {
|
|
||||||
traceIDBytes := make([]byte, 16)
|
|
||||||
rand.Read(traceIDBytes)
|
|
||||||
traceIDHex := hex.EncodeToString(traceIDBytes)
|
|
||||||
|
|
||||||
spanIDBytes := make([]byte, 8)
|
|
||||||
rand.Read(spanIDBytes)
|
|
||||||
spanIDHex := hex.EncodeToString(spanIDBytes)
|
|
||||||
|
|
||||||
spanConfig := trace.SpanContextConfig{}
|
|
||||||
spanConfig.TraceID, _ = trace.TraceIDFromHex(traceIDHex)
|
|
||||||
spanConfig.SpanID, _ = trace.SpanIDFromHex(spanIDHex)
|
|
||||||
spanConfig.TraceFlags = trace.FlagsSampled
|
|
||||||
|
|
||||||
ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.NewSpanContext(spanConfig))
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
p.Inject(ctx, c)
|
|
||||||
|
|
||||||
require.Equal(t, 3, len(c.Values), "not all headers were saved")
|
|
||||||
require.Equal(t, traceIDHex, c.Values[traceIDHeader], "unexpected trace id")
|
|
||||||
require.Equal(t, spanIDHex, c.Values[spanIDHeader], "unexpected span id")
|
|
||||||
require.Equal(t, "1", c.Values[flagsHeader], "unexpected flags")
|
|
||||||
})
|
|
||||||
t.Run("doesn't injects if trace_id is invalid", func(t *testing.T) {
|
|
||||||
traceIDBytes := make([]byte, 16)
|
|
||||||
traceIDHex := hex.EncodeToString(traceIDBytes)
|
|
||||||
|
|
||||||
spanIDBytes := make([]byte, 8)
|
|
||||||
rand.Read(spanIDBytes)
|
|
||||||
spanIDHex := hex.EncodeToString(spanIDBytes)
|
|
||||||
|
|
||||||
spanConfig := trace.SpanContextConfig{}
|
|
||||||
spanConfig.TraceID, _ = trace.TraceIDFromHex(traceIDHex)
|
|
||||||
spanConfig.SpanID, _ = trace.SpanIDFromHex(spanIDHex)
|
|
||||||
spanConfig.TraceFlags = trace.FlagsSampled
|
|
||||||
|
|
||||||
ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.NewSpanContext(spanConfig))
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
p.Inject(ctx, c)
|
|
||||||
|
|
||||||
require.Equal(t, 0, len(c.Values), "some headers were saved")
|
|
||||||
})
|
|
||||||
t.Run("doesn't injects if span_id is invalid", func(t *testing.T) {
|
|
||||||
traceIDBytes := make([]byte, 16)
|
|
||||||
rand.Read(traceIDBytes)
|
|
||||||
traceIDHex := hex.EncodeToString(traceIDBytes)
|
|
||||||
|
|
||||||
spanIDBytes := make([]byte, 8)
|
|
||||||
spanIDHex := hex.EncodeToString(spanIDBytes)
|
|
||||||
|
|
||||||
spanConfig := trace.SpanContextConfig{}
|
|
||||||
spanConfig.TraceID, _ = trace.TraceIDFromHex(traceIDHex)
|
|
||||||
spanConfig.SpanID, _ = trace.SpanIDFromHex(spanIDHex)
|
|
||||||
spanConfig.TraceFlags = trace.FlagsSampled
|
|
||||||
|
|
||||||
ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.NewSpanContext(spanConfig))
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
p.Inject(ctx, c)
|
|
||||||
|
|
||||||
require.Equal(t, 0, len(c.Values), "some headers were saved")
|
|
||||||
})
|
|
||||||
t.Run("injects flags if no flags specified", func(t *testing.T) {
|
|
||||||
traceIDBytes := make([]byte, 16)
|
|
||||||
rand.Read(traceIDBytes)
|
|
||||||
traceIDHex := hex.EncodeToString(traceIDBytes)
|
|
||||||
|
|
||||||
spanIDBytes := make([]byte, 8)
|
|
||||||
rand.Read(spanIDBytes)
|
|
||||||
spanIDHex := hex.EncodeToString(spanIDBytes)
|
|
||||||
|
|
||||||
spanConfig := trace.SpanContextConfig{}
|
|
||||||
spanConfig.TraceID, _ = trace.TraceIDFromHex(traceIDHex)
|
|
||||||
spanConfig.SpanID, _ = trace.SpanIDFromHex(spanIDHex)
|
|
||||||
|
|
||||||
ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.NewSpanContext(spanConfig))
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
p.Inject(ctx, c)
|
|
||||||
|
|
||||||
require.Equal(t, 3, len(c.Values), "not all headers were saved")
|
|
||||||
require.Equal(t, traceIDHex, c.Values[traceIDHeader], "unexpected trace id")
|
|
||||||
require.Equal(t, spanIDHex, c.Values[spanIDHeader], "unexpected span id")
|
|
||||||
require.Equal(t, "0", c.Values[flagsHeader], "unexpected flags")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPropagator_Extract(t *testing.T) {
|
|
||||||
t.Run("extracts if set", func(t *testing.T) {
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
traceIDBytes := make([]byte, 16)
|
|
||||||
rand.Read(traceIDBytes)
|
|
||||||
traceIDHex := hex.EncodeToString(traceIDBytes)
|
|
||||||
c.Values[traceIDHeader] = traceIDHex
|
|
||||||
|
|
||||||
spanIDBytes := make([]byte, 8)
|
|
||||||
rand.Read(spanIDBytes)
|
|
||||||
spanIDHex := hex.EncodeToString(spanIDBytes)
|
|
||||||
c.Values[spanIDHeader] = spanIDHex
|
|
||||||
|
|
||||||
c.Values[flagsHeader] = "1"
|
|
||||||
|
|
||||||
ctx := p.Extract(context.Background(), c)
|
|
||||||
|
|
||||||
sc := trace.SpanFromContext(ctx).SpanContext()
|
|
||||||
require.True(t, sc.HasTraceID(), "trace_id was not set")
|
|
||||||
require.Equal(t, traceIDHex, sc.TraceID().String(), "trace_id doesn't match")
|
|
||||||
require.True(t, sc.HasSpanID(), "span_id was not set")
|
|
||||||
require.Equal(t, spanIDHex, sc.SpanID().String(), "span_id doesn't match")
|
|
||||||
require.True(t, sc.IsSampled(), "sampled was not set")
|
|
||||||
})
|
|
||||||
t.Run("not extracts if only trace_id defined", func(t *testing.T) {
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
traceIDBytes := make([]byte, 16)
|
|
||||||
rand.Read(traceIDBytes)
|
|
||||||
traceIDHex := hex.EncodeToString(traceIDBytes)
|
|
||||||
c.Values[traceIDHeader] = traceIDHex
|
|
||||||
c.Values[flagsHeader] = "1"
|
|
||||||
|
|
||||||
ctx := p.Extract(context.Background(), c)
|
|
||||||
|
|
||||||
sc := trace.SpanFromContext(ctx).SpanContext()
|
|
||||||
require.False(t, sc.HasTraceID(), "trace_id was set")
|
|
||||||
require.False(t, sc.HasSpanID(), "span_id was set")
|
|
||||||
require.False(t, sc.IsSampled(), "sampled was set")
|
|
||||||
})
|
|
||||||
t.Run("not extracts if only span_id defined", func(t *testing.T) {
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
spanIDBytes := make([]byte, 8)
|
|
||||||
rand.Read(spanIDBytes)
|
|
||||||
spanIDHex := hex.EncodeToString(spanIDBytes)
|
|
||||||
c.Values[spanIDHeader] = spanIDHex
|
|
||||||
c.Values[flagsHeader] = "1"
|
|
||||||
|
|
||||||
ctx := p.Extract(context.Background(), c)
|
|
||||||
|
|
||||||
sc := trace.SpanFromContext(ctx).SpanContext()
|
|
||||||
require.False(t, sc.HasTraceID(), "trace_id was set")
|
|
||||||
require.False(t, sc.HasSpanID(), "span_id was set")
|
|
||||||
require.False(t, sc.IsSampled(), "sampled was set")
|
|
||||||
})
|
|
||||||
t.Run("not extracts if trace_id is in invalid", func(t *testing.T) {
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Values[traceIDHeader] = "loren ipsum"
|
|
||||||
|
|
||||||
spanIDBytes := make([]byte, 8)
|
|
||||||
rand.Read(spanIDBytes)
|
|
||||||
spanIDHex := hex.EncodeToString(spanIDBytes)
|
|
||||||
c.Values[spanIDHeader] = spanIDHex
|
|
||||||
c.Values[flagsHeader] = "1"
|
|
||||||
|
|
||||||
ctx := p.Extract(context.Background(), c)
|
|
||||||
|
|
||||||
sc := trace.SpanFromContext(ctx).SpanContext()
|
|
||||||
require.False(t, sc.HasTraceID(), "trace_id was set")
|
|
||||||
require.False(t, sc.HasSpanID(), "span_id was set")
|
|
||||||
require.False(t, sc.IsSampled(), "sampled was set")
|
|
||||||
})
|
|
||||||
t.Run("not extracts if span_id is invalid", func(t *testing.T) {
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Values[spanIDHeader] = "loren ipsum"
|
|
||||||
|
|
||||||
traceIDBytes := make([]byte, 16)
|
|
||||||
rand.Read(traceIDBytes)
|
|
||||||
traceIDHex := hex.EncodeToString(traceIDBytes)
|
|
||||||
c.Values[traceIDHeader] = traceIDHex
|
|
||||||
c.Values[flagsHeader] = "1"
|
|
||||||
|
|
||||||
ctx := p.Extract(context.Background(), c)
|
|
||||||
|
|
||||||
sc := trace.SpanFromContext(ctx).SpanContext()
|
|
||||||
require.False(t, sc.HasTraceID(), "trace_id was set")
|
|
||||||
require.False(t, sc.HasSpanID(), "span_id was set")
|
|
||||||
require.False(t, sc.IsSampled(), "sampled was set")
|
|
||||||
})
|
|
||||||
t.Run("not extracts if flags is invalid", func(t *testing.T) {
|
|
||||||
c := &testCarrier{
|
|
||||||
Values: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
traceIDBytes := make([]byte, 16)
|
|
||||||
rand.Read(traceIDBytes)
|
|
||||||
traceIDHex := hex.EncodeToString(traceIDBytes)
|
|
||||||
c.Values[traceIDHeader] = traceIDHex
|
|
||||||
|
|
||||||
spanIDBytes := make([]byte, 8)
|
|
||||||
rand.Read(spanIDBytes)
|
|
||||||
spanIDHex := hex.EncodeToString(spanIDBytes)
|
|
||||||
c.Values[spanIDHeader] = spanIDHex
|
|
||||||
|
|
||||||
c.Values[flagsHeader] = "loren ipsum"
|
|
||||||
|
|
||||||
ctx := p.Extract(context.Background(), c)
|
|
||||||
|
|
||||||
sc := trace.SpanFromContext(ctx).SpanContext()
|
|
||||||
require.False(t, sc.HasTraceID(), "trace_id was set")
|
|
||||||
require.False(t, sc.HasSpanID(), "span_id was set")
|
|
||||||
require.False(t, sc.IsSampled(), "sampled was set")
|
|
||||||
})
|
|
||||||
}
|
|
176
tracing/setup.go
176
tracing/setup.go
|
@ -1,176 +0,0 @@
|
||||||
package tracing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
|
||||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
|
||||||
"go.opentelemetry.io/otel/sdk/resource"
|
|
||||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
|
||||||
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
"go.opentelemetry.io/otel/trace/noop"
|
|
||||||
"google.golang.org/grpc/credentials"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrEmptyServerRootCaPool indicates that cert pool is empty and does not have any certificates inside.
|
|
||||||
var ErrEmptyServerRootCaPool = errors.New("empty server root ca cert pool")
|
|
||||||
|
|
||||||
var (
|
|
||||||
// tracingLock protects provider, done, config and tracer from concurrent update.
|
|
||||||
// These fields change when the config is updated or the application is shutdown.
|
|
||||||
tracingLock = &sync.Mutex{}
|
|
||||||
|
|
||||||
provider *sdktrace.TracerProvider
|
|
||||||
done bool
|
|
||||||
|
|
||||||
config = Config{}
|
|
||||||
tracer = getDefaultTracer()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Setup initializes global tracer.
|
|
||||||
// Returns true if global tracer was updated.
|
|
||||||
// Shutdown method must be called for graceful shutdown.
|
|
||||||
func Setup(ctx context.Context, cfg Config) (bool, error) {
|
|
||||||
if err := cfg.validate(); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tracingLock.Lock()
|
|
||||||
defer tracingLock.Unlock()
|
|
||||||
|
|
||||||
if done {
|
|
||||||
return false, fmt.Errorf("failed to setup tracing: already shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !config.hasChange(&cfg) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cfg.Enabled {
|
|
||||||
config = cfg
|
|
||||||
tracer.Store(&tracerHolder{Tracer: noop.NewTracerProvider().Tracer("")})
|
|
||||||
return true, flushAndShutdown(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
exp, err := getExporter(ctx, &cfg)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
prevProvider := provider
|
|
||||||
|
|
||||||
provider = sdktrace.NewTracerProvider(
|
|
||||||
sdktrace.WithBatcher(exp),
|
|
||||||
sdktrace.WithResource(newResource(&cfg)),
|
|
||||||
)
|
|
||||||
|
|
||||||
config = cfg
|
|
||||||
tracer.Store(&tracerHolder{Tracer: provider.Tracer(cfg.Service)})
|
|
||||||
|
|
||||||
var retErr error
|
|
||||||
if prevProvider != nil {
|
|
||||||
retErr = prevProvider.ForceFlush(ctx)
|
|
||||||
if err := prevProvider.Shutdown(ctx); err != nil {
|
|
||||||
if retErr == nil {
|
|
||||||
retErr = err
|
|
||||||
} else {
|
|
||||||
retErr = fmt.Errorf("%v ; %v", retErr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, retErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown shutdowns tracing.
|
|
||||||
func Shutdown(ctx context.Context) error {
|
|
||||||
tracingLock.Lock()
|
|
||||||
defer tracingLock.Unlock()
|
|
||||||
|
|
||||||
if done {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
done = true
|
|
||||||
|
|
||||||
config = Config{}
|
|
||||||
tracer.Store(&tracerHolder{Tracer: noop.NewTracerProvider().Tracer("")})
|
|
||||||
|
|
||||||
return flushAndShutdown(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultTracer() *atomic.Pointer[tracerHolder] {
|
|
||||||
v := new(atomic.Pointer[tracerHolder])
|
|
||||||
v.Store(&tracerHolder{Tracer: noop.NewTracerProvider().Tracer("")})
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func flushAndShutdown(ctx context.Context) error {
|
|
||||||
if provider == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp := provider
|
|
||||||
provider = nil
|
|
||||||
var retErr error
|
|
||||||
retErr = tmp.ForceFlush(ctx)
|
|
||||||
if err := tmp.Shutdown(ctx); err != nil {
|
|
||||||
if retErr == nil {
|
|
||||||
retErr = err
|
|
||||||
} else {
|
|
||||||
retErr = fmt.Errorf("%v ; %v", retErr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func getExporter(ctx context.Context, cfg *Config) (sdktrace.SpanExporter, error) {
|
|
||||||
switch cfg.Exporter {
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("failed to setup tracing: unknown tracing exporter (%s)", cfg.Exporter)
|
|
||||||
case StdoutExporter:
|
|
||||||
return stdouttrace.New()
|
|
||||||
case NoOpExporter:
|
|
||||||
return tracetest.NewNoopExporter(), nil
|
|
||||||
case OTLPgRPCExporter:
|
|
||||||
securityOption := otlptracegrpc.WithInsecure()
|
|
||||||
if cfg.ServerCaCertPool != nil {
|
|
||||||
if cfg.ServerCaCertPool.Equal(x509.NewCertPool()) {
|
|
||||||
return nil, fmt.Errorf("failed to setup tracing: %w", ErrEmptyServerRootCaPool)
|
|
||||||
}
|
|
||||||
securityOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(cfg.ServerCaCertPool, ""))
|
|
||||||
}
|
|
||||||
return otlptracegrpc.New(ctx, otlptracegrpc.WithEndpoint(cfg.Endpoint), securityOption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newResource(cfg *Config) *resource.Resource {
|
|
||||||
attrs := []attribute.KeyValue{
|
|
||||||
semconv.ServiceName(cfg.Service),
|
|
||||||
}
|
|
||||||
if len(cfg.Version) > 0 {
|
|
||||||
attrs = append(attrs, semconv.ServiceVersion(cfg.Version))
|
|
||||||
}
|
|
||||||
if len(cfg.InstanceID) > 0 {
|
|
||||||
attrs = append(attrs, semconv.ServiceInstanceID(cfg.InstanceID))
|
|
||||||
}
|
|
||||||
for k, v := range cfg.Attributes {
|
|
||||||
attrs = append(attrs, attribute.String(k, v))
|
|
||||||
}
|
|
||||||
return resource.NewWithAttributes(
|
|
||||||
semconv.SchemaURL,
|
|
||||||
attrs...,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tracerHolder struct {
|
|
||||||
Tracer trace.Tracer
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
package tracing_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/x509"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSetup(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
config tracing.Config
|
|
||||||
want bool
|
|
||||||
expErr error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "setup stdout exporter",
|
|
||||||
config: tracing.Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: tracing.StdoutExporter,
|
|
||||||
Service: "service-name",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
expErr: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "setup noop exporter",
|
|
||||||
config: tracing.Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: tracing.NoOpExporter,
|
|
||||||
Service: "service-name",
|
|
||||||
},
|
|
||||||
|
|
||||||
want: true,
|
|
||||||
expErr: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "setup otlp_grpc insecure exporter",
|
|
||||||
config: tracing.Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: tracing.OTLPgRPCExporter,
|
|
||||||
Service: "service-name",
|
|
||||||
Endpoint: "test-endpoint.com:4317",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
expErr: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "setup otlp_grpc secure exporter with valid rsa root ca certificate",
|
|
||||||
config: tracing.Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: tracing.OTLPgRPCExporter,
|
|
||||||
Service: "service-name",
|
|
||||||
Endpoint: "test-endpoint.com:4317",
|
|
||||||
ServerCaCertPool: readCertPoolByPath(t, "../testdata/tracing/valid_google_globalsign_r4_rsa_root_ca.pem"),
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
expErr: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "setup otlp_grpc secure exporter with valid ecdsa root ca certificate",
|
|
||||||
config: tracing.Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: tracing.OTLPgRPCExporter,
|
|
||||||
Service: "service-name",
|
|
||||||
Endpoint: "test-endpoint.com:4317",
|
|
||||||
ServerCaCertPool: readCertPoolByPath(t, "../testdata/tracing/valid_google_gts_r4_ecdsa_root_ca.pem"),
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
expErr: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "setup otlp_grpc secure exporter with invalid root ca certificate",
|
|
||||||
config: tracing.Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: tracing.OTLPgRPCExporter,
|
|
||||||
Service: "service-name",
|
|
||||||
Endpoint: "test-endpoint.com:4317",
|
|
||||||
ServerCaCertPool: readCertPoolByPath(t, "../testdata/tracing/invalid_root_ca.pem"),
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
expErr: tracing.ErrEmptyServerRootCaPool,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "setup otlp_grpc secure exporter with empty root ca certificate",
|
|
||||||
config: tracing.Config{
|
|
||||||
Enabled: true,
|
|
||||||
Exporter: tracing.OTLPgRPCExporter,
|
|
||||||
Service: "service-name",
|
|
||||||
Endpoint: "test-endpoint.com:4317",
|
|
||||||
ServerCaCertPool: readCertPoolByPath(t, "../testdata/tracing/invalid_empty_root_ca.pem"),
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
expErr: tracing.ErrEmptyServerRootCaPool,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := tracing.Setup(context.Background(), tt.config)
|
|
||||||
require.ErrorIs(t, err, tt.expErr)
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("Setup config = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readCertPoolByPath(t *testing.T, path string) *x509.CertPool {
|
|
||||||
ca, err := os.ReadFile(path)
|
|
||||||
require.NoError(t, err)
|
|
||||||
roots := x509.NewCertPool()
|
|
||||||
_ = roots.AppendCertsFromPEM(ca)
|
|
||||||
return roots
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package tracing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StartSpanFromContext creates a span and a context.Context containing the newly-created span.
|
|
||||||
func StartSpanFromContext(ctx context.Context, operationName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
|
|
||||||
return tracer.Load().Tracer.Start(ctx, operationName, opts...)
|
|
||||||
}
|
|
Loading…
Reference in a new issue