Initial QSfera import
This commit is contained in:
+290
@@ -0,0 +1,290 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.12.2] - 2025-09-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Use TracerProvider from request context when available ([#92])
|
||||
- Fix high-cardinality metrics and allows user control attributes each metric record ([#95])
|
||||
|
||||
## [0.12.1] - 2025-02-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix superfluous header writer ([#89])
|
||||
|
||||
## [0.12.0] - 2025-01-18
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade `go.opentelemetry.io/otel`, `go.opentelemetry.io/otel/sdk`, & `go.opentelemetry.io/otel/trace` to `v1.34.0`. ([#87])
|
||||
- Upgrade `go.opentelemetry.io/otel/metric` to `v1.34.0`. ([#87])
|
||||
- Set the metric version to be the same as middleware version which is `v0.12.0` to make it easy for maintainer of this repo to maintain the versioning. ([#88])
|
||||
|
||||
## [0.11.0] - 2024-11-27
|
||||
|
||||
### Added
|
||||
|
||||
- Add metric package as middleware `go-chi/chi`, support `request_duration_millis`, `requests_inflight`, and `response_size_bytes` metric. Using `go.opentelemetry.io/otel/metric` which the version is `v1.32.0`. ([#69])
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade `go.opentelemetry.io/otel`, `go.opentelemetry.io/otel/sdk`, & `go.opentelemetry.io/otel/trace` to `v1.32.0`. ([#74])
|
||||
|
||||
## [0.10.1] - 2024-10-27
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade `go.opentelemetry.io/otel`, `go.opentelemetry.io/otel/sdk`, & `go.opentelemetry.io/otel/trace` to `v1.31.0`. ([#70])
|
||||
|
||||
### Fixed
|
||||
|
||||
- Span for websocket connection now won't be marked as error span. ([#67])
|
||||
|
||||
## [0.10.0] - 2024-09-17
|
||||
|
||||
### Added
|
||||
|
||||
- Add `WithTraceResponseHeaders` option to include trace information in response headers, this option replaces the deprecated `WithTraceIDResponseHeader` option. ([#62])
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade `go.opentelemetry.io/otel`, `go.opentelemetry.io/otel/sdk`, & `go.opentelemetry.io/otel/trace` to `v1.30.0`. ([#64])
|
||||
- Set the go versions for testing in both `Makefile` & `compatibility-test.yml` to `1.22` & `1.23`. ([#64])
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix Gobrew installation in CI pipeline. ([#63])
|
||||
|
||||
### Removed
|
||||
|
||||
- Drop support for Go 1.21. ([#64])
|
||||
- Deprecated `WithTraceIDResponseHeader` option, use `WithTraceResponseHeaders` instead. ([#62])
|
||||
|
||||
## [0.9.0] - 2024-07-06
|
||||
|
||||
### Changed
|
||||
|
||||
- `WithFilter` option now support multiple filter functions, just like in [otelmux](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/v1.24.0/instrumentation/github.com/gorilla/mux/otelmux/config.go#L106-L110). ([#47])
|
||||
- Upgrade `go.opentelemetry.io/otel`, `go.opentelemetry.io/otel/sdk`, & `go.opentelemetry.io/otel/trace` to `v1.28.0`. ([#49])
|
||||
- Upgrade `github.com/go-chi/chi/v5` to `v5.1.0`. ([#49])
|
||||
- Set the go versions for testing in both `Makefile` & `compatibility-test.yml` to `1.21` & `1.22`. ([#49])
|
||||
|
||||
### Removed
|
||||
|
||||
- Drop support for Go `<1.21`. ([#49])
|
||||
|
||||
## [0.8.0] - 2024-04-29
|
||||
|
||||
### ⚠️ Notice ⚠️
|
||||
|
||||
This release is the last to support Go `1.19`. The next release will require at least Go `1.21`.
|
||||
|
||||
### Added
|
||||
|
||||
- Add `WithPublicEndpoint` & `WithPublicEndpointFn` options. ([#43])
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to `v1.24.0` of `go.opentelemetry.io/otel`. ([#41])
|
||||
- Upgrade to `v1.20.0` of `go.opentelemetry.io/otel/semconv`. ([#41])
|
||||
- Adjust Go version for both `examples/basic` & `examples/multi-services` to `1.19` & `go.opentelemetry.io/otel` to `v1.24.0`. ([#41])
|
||||
- Update otelhttp version to `0.49.0` since it is the version that uses otel `1.24.0` internally, check [here](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/v1.24.0/instrumentation/net/http/otelhttp/go.mod#L8) for details. ([#42])
|
||||
- Set the go versions in compatibility-test.yml to 1.19, 1.20, & 1.21. ([#42])
|
||||
- Set the sampling strategy to always sample in test cases to avoid random error. ([#42])
|
||||
- Use `otlptrace` exporter instead of `jaeger` exporter in `examples/multi-services`. ([#42])
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove the deprecated `jaeger` exporter from `examples/multi-services` & use `otlptrace` exporter instead. ([#42])
|
||||
- Drop support for Go `<1.19`. ([#41])
|
||||
|
||||
## [0.7.0] - 2024-04-22
|
||||
|
||||
### ⚠️ Notice ⚠️
|
||||
|
||||
This release is the last to support Go `1.18`. The next release will require at least Go `1.19`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to `v1.14.0` of `go.opentelemetry.io/otel`. ([#38])
|
||||
- Upgrade to `v1.17.0` of `go.opentelemetry.io/otel/semconv`. ([#38])
|
||||
- Adjust Go version for both `examples/basic` & `examples/multi-services` to `1.18` & `go.opentelemetry.io/otel` to `v1.14.0`. ([#38])
|
||||
- Change `http.server_name` attributes to `net.host.name`, this is because semconv is removing this attribute for http. ([#38])
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove `http.target` attribute on implementation & tests based on [this comment](https://github.com/open-telemetry/opentelemetry-go/blob/v1.17.0/semconv/internal/v2/http.go#L160-L165). ([#39])
|
||||
- Drop support for Go `<1.18`. ([#38])
|
||||
|
||||
## [0.6.0] - 2024-04-02
|
||||
|
||||
### ⚠️ Notice ⚠️
|
||||
|
||||
This release is the last to support Go `1.15`. The next release will require at least Go `1.18`.
|
||||
|
||||
### Added
|
||||
|
||||
- Add `WithTraceIDResponseHeader` option to enable adding trace id into response header. ([#36])
|
||||
- Add multiple go versions test scripts for local and CI pipeline. ([#29])
|
||||
- Add compatibility testing for `ubuntu`, `macos` and `windows`. ([#32])
|
||||
- Add repo essentials docs. ([#33])
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to `v5.0.12` of `go-chi/chi`. ([#29])
|
||||
- Upgrade to `v1.10.0` of `go.opentelemetry.io/otel`. ([#29])
|
||||
- Upgrade to `v1.12.0` of `go.opentelemetry.io/otel/semconv`. ([#29])
|
||||
- Set the required go version for both `examples/basic` & `examples/multi-services` to `1.15`, `go-chi/chi` to `v5.0.12`, & `go.opentelemetry.io/otel` to `v1.10.0` ([#35])
|
||||
|
||||
## [0.5.2] - 2024-03-25
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix empty status code. ([#30])
|
||||
|
||||
### Changed
|
||||
|
||||
- Return `http.StatusOK` (200) as a default `http.status_code` span attribute. ([#30])
|
||||
|
||||
## [0.5.1] - 2023-02-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix broken empty routes. ([#18])
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to `v5.0.8` of `go-chi/chi`.
|
||||
|
||||
## [0.5.0] - 2022-10-02
|
||||
|
||||
### Added
|
||||
|
||||
- Add multi services example. ([#9])
|
||||
- Add `WithFilter()` option to ignore tracing in certain endpoints. ([#11])
|
||||
|
||||
## [0.4.0] - 2022-02-22
|
||||
|
||||
### Added
|
||||
|
||||
- Add Option `WithRequestMethodInSpanName()` to handle vendor that do not include HTTP request method as mentioned in [#6]. ([#7])
|
||||
- Refine description for `WithChiRoutes()` option to announce it is possible to override the span name in underlying handler with this option.
|
||||
|
||||
### Changed
|
||||
|
||||
## [0.3.0] - 2022-01-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix both `docker-compose.yml` & `Dockerfile` in the example. ([#5])
|
||||
|
||||
### Added
|
||||
|
||||
- Add `WithChiRoutes()` option to make the middleware able to determine full route pattern on span creation. ([#5])
|
||||
- Set all known span attributes on span creation rather than set them after request is being executed. ([#5])
|
||||
|
||||
## [0.2.1] - 2022-01-08
|
||||
|
||||
### Added
|
||||
|
||||
- Add build example to CI pipeline. ([#2])
|
||||
|
||||
### Changed
|
||||
|
||||
- Use `ctx.RoutePattern()` to get span name, this is to strip out noisy wildcard pattern. ([#1])
|
||||
|
||||
## [0.2.0] - 2021-10-18
|
||||
|
||||
### Added
|
||||
|
||||
- Set service name on tracer provider from code example.
|
||||
|
||||
### Changed
|
||||
|
||||
- Update dependencies in go.mod
|
||||
- Upgrade to `v1.0.1` of `go.opentelemetry.io/otel`.
|
||||
- Upgrade to `v5.0.4` of `go-chi/chi`.
|
||||
- Update latest test to use `otelmux` format.
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove `HTTPResponseContentLengthKey`
|
||||
- Remove `HTTPTargetKey`, since automatically set in `HTTPServerAttributesFromHTTPRequest`
|
||||
|
||||
## [0.1.0] - 2021-08-11
|
||||
|
||||
This is the first release of otelchi.
|
||||
It contains instrumentation for trace and depends on:
|
||||
|
||||
- otel => `v1.0.0-RC2`
|
||||
- go-chi/chi => `v5.0.3`
|
||||
|
||||
### Added
|
||||
|
||||
- Instrumentation for trace.
|
||||
- CI files.
|
||||
- Example code for a basic usage.
|
||||
- Apache-2.0 license.
|
||||
|
||||
[#95]: https://github.com/riandyrn/otelchi/pull/95
|
||||
[#92]: https://github.com/riandyrn/otelchi/pull/92
|
||||
[#89]: https://github.com/riandyrn/otelchi/pull/89
|
||||
[#88]: https://github.com/riandyrn/otelchi/pull/88
|
||||
[#87]: https://github.com/riandyrn/otelchi/pull/87
|
||||
[#74]: https://github.com/riandyrn/otelchi/pull/74
|
||||
[#70]: https://github.com/riandyrn/otelchi/pull/70
|
||||
[#69]: https://github.com/riandyrn/otelchi/pull/69
|
||||
[#67]: https://github.com/riandyrn/otelchi/pull/67
|
||||
[#64]: https://github.com/riandyrn/otelchi/pull/64
|
||||
[#63]: https://github.com/riandyrn/otelchi/pull/63
|
||||
[#62]: https://github.com/riandyrn/otelchi/pull/62
|
||||
[#49]: https://github.com/riandyrn/otelchi/pull/49
|
||||
[#47]: https://github.com/riandyrn/otelchi/pull/47
|
||||
[#43]: https://github.com/riandyrn/otelchi/pull/43
|
||||
[#42]: https://github.com/riandyrn/otelchi/pull/42
|
||||
[#41]: https://github.com/riandyrn/otelchi/pull/41
|
||||
[#39]: https://github.com/riandyrn/otelchi/pull/39
|
||||
[#38]: https://github.com/riandyrn/otelchi/pull/38
|
||||
[#36]: https://github.com/riandyrn/otelchi/pull/36
|
||||
[#35]: https://github.com/riandyrn/otelchi/pull/35
|
||||
[#33]: https://github.com/riandyrn/otelchi/pull/33
|
||||
[#32]: https://github.com/riandyrn/otelchi/pull/32
|
||||
[#30]: https://github.com/riandyrn/otelchi/pull/30
|
||||
[#29]: https://github.com/riandyrn/otelchi/pull/29
|
||||
[#18]: https://github.com/riandyrn/otelchi/pull/18
|
||||
[#11]: https://github.com/riandyrn/otelchi/pull/11
|
||||
[#9]: https://github.com/riandyrn/otelchi/pull/9
|
||||
[#7]: https://github.com/riandyrn/otelchi/pull/7
|
||||
[#6]: https://github.com/riandyrn/otelchi/pull/6
|
||||
[#5]: https://github.com/riandyrn/otelchi/pull/5
|
||||
[#2]: https://github.com/riandyrn/otelchi/pull/2
|
||||
[#1]: https://github.com/riandyrn/otelchi/pull/1
|
||||
|
||||
[Unreleased]: https://github.com/riandyrn/otelchi/compare/v0.12.2...HEAD
|
||||
[0.12.2]: https://github.com/riandyrn/otelchi/releases/tag/v0.12.2
|
||||
[0.12.1]: https://github.com/riandyrn/otelchi/releases/tag/v0.12.1
|
||||
[0.12.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.12.0
|
||||
[0.11.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.11.0
|
||||
[0.10.1]: https://github.com/riandyrn/otelchi/releases/tag/v0.10.1
|
||||
[0.10.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.10.0
|
||||
[0.9.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.9.0
|
||||
[0.8.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.8.0
|
||||
[0.7.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.7.0
|
||||
[0.6.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.6.0
|
||||
[0.5.2]: https://github.com/riandyrn/otelchi/releases/tag/v0.5.2
|
||||
[0.5.1]: https://github.com/riandyrn/otelchi/releases/tag/v0.5.1
|
||||
[0.5.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.5.0
|
||||
[0.4.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.4.0
|
||||
[0.3.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.3.0
|
||||
[0.2.1]: https://github.com/riandyrn/otelchi/releases/tag/v0.2.1
|
||||
[0.2.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.2.0
|
||||
[0.1.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.1.0
|
||||
+1
@@ -0,0 +1 @@
|
||||
* @riandyrn @ilhamsyahids
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
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 [2021] [Riandy Rahman Nugraha]
|
||||
|
||||
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.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
.PHONY: *
|
||||
|
||||
GO_VERSIONS="1.22 1.23"
|
||||
|
||||
# This is the command that will be used to run the tests
|
||||
go-test:
|
||||
go build .
|
||||
go test ./...
|
||||
|
||||
# This is the command that will be used to run the tests in a Docker container, useful when executing the test locally
|
||||
test:
|
||||
docker build \
|
||||
-t go-test \
|
||||
--build-arg GO_VERSIONS=${GO_VERSIONS} \
|
||||
-f ./test/infras/Dockerfile . && \
|
||||
docker run --rm go-test
|
||||
|
||||
make test-build-examples
|
||||
|
||||
test-build-examples:
|
||||
make test-build-basic-example
|
||||
make test-build-multi-services-example
|
||||
|
||||
test-build-basic-example:
|
||||
docker build -f ./examples/basic/Dockerfile .
|
||||
|
||||
test-build-multi-services-example:
|
||||
docker build -f ./examples/multi-services/back-svc/Dockerfile .
|
||||
docker build -f ./examples/multi-services/front-svc/Dockerfile .
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# otelchi
|
||||
|
||||
[](https://github.com/riandyrn/otelchi/actions/workflows/compatibility-test.yaml)
|
||||
[](https://goreportcard.com/report/github.com/riandyrn/otelchi)
|
||||
[](https://pkg.go.dev/mod/github.com/riandyrn/otelchi)
|
||||
|
||||
OpenTelemetry instrumentation for [go-chi/chi](https://github.com/go-chi/chi).
|
||||
|
||||
Essentially this is an adaptation from [otelmux](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation/github.com/gorilla/mux/otelmux) but instead of using `gorilla/mux`, we use `go-chi/chi`.
|
||||
|
||||
Currently, this library can only instrument traces and metrics.
|
||||
|
||||
Contributions are welcomed!
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
$ go get github.com/riandyrn/otelchi
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
See [examples](./examples) for details.
|
||||
|
||||
## Why Port This?
|
||||
|
||||
I was planning to make this project as part of the Open Telemetry Go instrumentation project. However, based on [this comment](https://github.com/open-telemetry/opentelemetry-go-contrib/pull/986#issuecomment-941280855) they no longer accept new instrumentation. This is why I maintain this project here.
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# Release Process
|
||||
|
||||
This document explains how to create releases in this project in each release scenario.
|
||||
|
||||
Currently there are 2 release procedures for this project:
|
||||
|
||||
- [Latest Release](#latest-release)
|
||||
- [Non-Latest Release](#non-latest-release)
|
||||
|
||||
## Latest Release
|
||||
|
||||
This procedure is used when we want to create new release from the latest development edge (latest commit in the `master` branch).
|
||||
|
||||
The steps for this procedure are the following:
|
||||
|
||||
1. Create a new branch from the `master` branch with the following name format: `pre_release/v{MAJOR}.{MINOR}.{BUILD}`. For example, if we want to release for version `0.6.0`, we will first create a new branch from the `master` called `pre_release/v0.6.0`.
|
||||
2. Update method `version.Version()` to return the target version.
|
||||
3. Update the `CHANGELOG.md` to include all the notable changes.
|
||||
4. Create a new PR from this branch to `master` with the title: `Release v{MAJOR}.{MINOR}.{BUILD}` (e.g `Release v0.6.0`).
|
||||
5. At least one maintainer should approve the PR. However if the PR is created by the repo owner, it doesn't need to get approval from other maintainers.
|
||||
6. Upon approval, the PR will be merged to `master` and the branch will be deleted.
|
||||
7. Create new release from the `master` branch.
|
||||
8. Set the title to `Release v{MAJOR}.{MINOR}.{BUILD}` (e.g `Release v0.6.0`).
|
||||
9. Set the newly release tag using this format: `v{MAJOR}.{MINOR}.{BUILD}` (e.g `v0.6.0`).
|
||||
10. Set the description of the release to match with the content inside `CHANGELOG.md`.
|
||||
11. Set the release as the latest release.
|
||||
12. Publish the release.
|
||||
13. Done.
|
||||
|
||||
## Non-Latest Release
|
||||
|
||||
This procedure is used when we need to create fix or patch for the older releases. Consider the following scenario:
|
||||
|
||||
1. For example our latest release is version `0.7.1` which has the minimum go version `1.18`.
|
||||
2. Let say our user got a critical bug in version `0.6.0` which has the minimum go version `1.15`.
|
||||
3. Due to some constraints, this user cannot upgrade his/her minimum go version.
|
||||
4. We decided to create fix for this version by releasing `0.6.1`.
|
||||
|
||||
In this scenario, the procedure is the following:
|
||||
|
||||
1. Create a new branch from the version that we want to patch.
|
||||
2. We name the new branch with the increment in the build value. So for example if we want to create patch for `0.6.0`, then we should create new branch with name: `patch_release/v0.6.1`.
|
||||
3. We create again new branch that will use `patch_release/v0.6.1` as base. Let say `fix/handle-cve-233`.
|
||||
4. We will push any necessary changes to `fix/handle-cve-233`.
|
||||
5. Create a new PR that target `patch_release/v0.6.1`.
|
||||
6. Follow step `2-10` as described in the [Latest Release](#latest-release).
|
||||
7. Publish the release without setting the release as the latest release.
|
||||
8. Done.
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
package otelchi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// These defaults are used in `TraceHeaderConfig`.
|
||||
const (
|
||||
DefaultTraceIDResponseHeaderKey = "X-Trace-Id"
|
||||
DefaultTraceSampledResponseHeaderKey = "X-Trace-Sampled"
|
||||
)
|
||||
|
||||
// config is used to configure the mux middleware.
|
||||
type config struct {
|
||||
tracerProvider oteltrace.TracerProvider
|
||||
propagators propagation.TextMapPropagator
|
||||
chiRoutes chi.Routes
|
||||
requestMethodInSpanName bool
|
||||
filters []Filter
|
||||
traceIDResponseHeaderKey string
|
||||
traceSampledResponseHeaderKey string
|
||||
publicEndpointFn func(r *http.Request) bool
|
||||
}
|
||||
|
||||
// Option specifies instrumentation configuration options.
|
||||
type Option interface {
|
||||
apply(*config)
|
||||
}
|
||||
|
||||
type optionFunc func(*config)
|
||||
|
||||
func (o optionFunc) apply(c *config) {
|
||||
o(c)
|
||||
}
|
||||
|
||||
// Filter is a predicate used to determine whether a given http.Request should
|
||||
// be traced. A Filter must return true if the request should be traced.
|
||||
type Filter func(*http.Request) bool
|
||||
|
||||
// WithPropagators specifies propagators to use for extracting
|
||||
// information from the HTTP requests. If none are specified, global
|
||||
// ones will be used.
|
||||
func WithPropagators(propagators propagation.TextMapPropagator) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.propagators = propagators
|
||||
})
|
||||
}
|
||||
|
||||
// WithTracerProvider specifies a tracer provider to use for creating a tracer.
|
||||
// If none is specified, the global provider is used.
|
||||
func WithTracerProvider(provider oteltrace.TracerProvider) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.tracerProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithChiRoutes specified the routes that being used by application. Its main
|
||||
// purpose is to provide route pattern as span name during span creation. If this
|
||||
// option is not set, by default the span will be given name at the end of span
|
||||
// execution. For some people, this behavior is not desirable since they want
|
||||
// to override the span name on underlying handler. By setting this option, it
|
||||
// is possible for them to override the span name.
|
||||
func WithChiRoutes(routes chi.Routes) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.chiRoutes = routes
|
||||
})
|
||||
}
|
||||
|
||||
// WithRequestMethodInSpanName is used for adding http request method to span name.
|
||||
// While this is not necessary for vendors that properly implemented the tracing
|
||||
// specs (e.g Jaeger, AWS X-Ray, etc...), but for other vendors such as Elastic
|
||||
// and New Relic this might be helpful.
|
||||
//
|
||||
// See following threads for details:
|
||||
//
|
||||
// - https://github.com/riandyrn/otelchi/pull/3#issuecomment-1005883910
|
||||
// - https://github.com/riandyrn/otelchi/issues/6#issuecomment-1034461912
|
||||
func WithRequestMethodInSpanName(isActive bool) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.requestMethodInSpanName = isActive
|
||||
})
|
||||
}
|
||||
|
||||
// WithFilter adds a filter to the list of filters used by the handler.
|
||||
// If any filter indicates to exclude a request then the request will not be
|
||||
// traced. All filters must allow a request to be traced for a Span to be created.
|
||||
// If no filters are provided then all requests are traced.
|
||||
// Filters will be invoked for each processed request, it is advised to make them
|
||||
// simple and fast.
|
||||
func WithFilter(filter Filter) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.filters = append(cfg.filters, filter)
|
||||
})
|
||||
}
|
||||
|
||||
// WithTraceIDResponseHeader enables adding trace id into response header.
|
||||
// It accepts a function that generates the header key name. If this parameter
|
||||
// function set to `nil` the default header key which is `X-Trace-Id` will be used.
|
||||
//
|
||||
// Deprecated: use `WithTraceResponseHeaders` instead.
|
||||
func WithTraceIDResponseHeader(headerKeyFunc func() string) Option {
|
||||
cfg := TraceHeaderConfig{
|
||||
TraceIDHeader: "",
|
||||
TraceSampledHeader: "",
|
||||
}
|
||||
if headerKeyFunc != nil {
|
||||
cfg.TraceIDHeader = headerKeyFunc()
|
||||
}
|
||||
return WithTraceResponseHeaders(cfg)
|
||||
}
|
||||
|
||||
// TraceHeaderConfig is configuration for trace headers in the response.
|
||||
type TraceHeaderConfig struct {
|
||||
TraceIDHeader string // if non-empty overrides the default of X-Trace-ID
|
||||
TraceSampledHeader string // if non-empty overrides the default of X-Trace-Sampled
|
||||
}
|
||||
|
||||
// WithTraceResponseHeaders configures the response headers for trace information.
|
||||
// It accepts a TraceHeaderConfig struct that contains the keys for the Trace ID
|
||||
// and Trace Sampled headers. If the provided keys are empty, default values will
|
||||
// be used for the respective headers.
|
||||
func WithTraceResponseHeaders(cfg TraceHeaderConfig) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
c.traceIDResponseHeaderKey = cfg.TraceIDHeader
|
||||
if c.traceIDResponseHeaderKey == "" {
|
||||
c.traceIDResponseHeaderKey = DefaultTraceIDResponseHeaderKey
|
||||
}
|
||||
|
||||
c.traceSampledResponseHeaderKey = cfg.TraceSampledHeader
|
||||
if c.traceSampledResponseHeaderKey == "" {
|
||||
c.traceSampledResponseHeaderKey = DefaultTraceSampledResponseHeaderKey
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// WithPublicEndpoint is used for marking every endpoint as public endpoint.
|
||||
// This means if the incoming request has span context, it won't be used as
|
||||
// parent span by the span generated by this middleware, instead the generated
|
||||
// span will be the root span (new trace) and then linked to the span from the
|
||||
// incoming request.
|
||||
//
|
||||
// Let say we have the following scenario:
|
||||
//
|
||||
// 1. We have 2 systems: `SysA` & `SysB`.
|
||||
// 2. `SysA` has the following services: `SvcA.1` & `SvcA.2`.
|
||||
// 3. `SysB` has the following services: `SvcB.1` & `SvcB.2`.
|
||||
// 4. `SvcA.2` is used internally only by `SvcA.1`.
|
||||
// 5. `SvcB.2` is used internally only by `SvcB.1`.
|
||||
// 6. All of these services already instrumented otelchi & using the same collector (e.g Jaeger).
|
||||
// 7. In `SvcA.1` we should set `WithPublicEndpoint()` since it is the entry point (a.k.a "public endpoint") for entering `SysA`.
|
||||
// 8. In `SvcA.2` we should not set `WithPublicEndpoint()` since it is only used internally by `SvcA.1` inside `SysA`.
|
||||
// 9. Point 7 & 8 also applies to both services in `SysB`.
|
||||
//
|
||||
// Now, whenever `SvcA.1` calls `SvcA.2` there will be only a single trace generated. This trace will contain 2 spans: root span from `SvcA.1` & child span from `SvcA.2`.
|
||||
//
|
||||
// But if let say `SvcA.2` calls `SvcB.1`, then there will be 2 traces generated: trace from `SysA` & trace from `SysB`. But in trace generated in `SysB` there will be like a marking that this trace is actually related to trace in `SysA` (a.k.a linked with the trace from `SysA`).
|
||||
func WithPublicEndpoint() Option {
|
||||
return WithPublicEndpointFn(func(r *http.Request) bool { return true })
|
||||
}
|
||||
|
||||
// WithPublicEndpointFn runs with every request, and allows conditionally
|
||||
// configuring the Handler to link the generated span with an incoming span
|
||||
// context.
|
||||
//
|
||||
// If the function return `true` the generated span will be linked with the
|
||||
// incoming span context. Otherwise, the generated span will be set as the
|
||||
// child span of the incoming span context.
|
||||
//
|
||||
// Essentially it has the same functionality as `WithPublicEndpoint` but with
|
||||
// more flexibility.
|
||||
func WithPublicEndpointFn(fn func(r *http.Request) bool) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
cfg.publicEndpointFn = fn
|
||||
})
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
package otelchi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/felixge/httpsnoop"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/riandyrn/otelchi/version"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.20.0/httpconv"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
tracerName = "github.com/riandyrn/otelchi"
|
||||
)
|
||||
|
||||
func newTracer(tp trace.TracerProvider) trace.Tracer {
|
||||
return tp.Tracer(tracerName, trace.WithInstrumentationVersion(version.Version()))
|
||||
}
|
||||
|
||||
// Middleware sets up a handler to start tracing the incoming
|
||||
// requests. The serverName parameter should describe the name of the
|
||||
// (virtual) server handling the request.
|
||||
func Middleware(serverName string, opts ...Option) func(next http.Handler) http.Handler {
|
||||
cfg := config{}
|
||||
for _, opt := range opts {
|
||||
opt.apply(&cfg)
|
||||
}
|
||||
|
||||
var tracer oteltrace.Tracer
|
||||
if cfg.tracerProvider != nil {
|
||||
tracer = newTracer(cfg.tracerProvider)
|
||||
}
|
||||
|
||||
if cfg.propagators == nil {
|
||||
cfg.propagators = otel.GetTextMapPropagator()
|
||||
}
|
||||
|
||||
return func(handler http.Handler) http.Handler {
|
||||
return traceware{
|
||||
config: cfg,
|
||||
serverName: serverName,
|
||||
tracer: tracer,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type traceware struct {
|
||||
config
|
||||
serverName string
|
||||
tracer oteltrace.Tracer
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
type recordingResponseWriter struct {
|
||||
writer http.ResponseWriter
|
||||
written bool
|
||||
status int
|
||||
}
|
||||
|
||||
var rrwPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &recordingResponseWriter{}
|
||||
},
|
||||
}
|
||||
|
||||
func getRRW(writer http.ResponseWriter) *recordingResponseWriter {
|
||||
rrw := rrwPool.Get().(*recordingResponseWriter)
|
||||
rrw.written = false
|
||||
rrw.status = http.StatusOK
|
||||
rrw.writer = httpsnoop.Wrap(writer, httpsnoop.Hooks{
|
||||
Write: func(next httpsnoop.WriteFunc) httpsnoop.WriteFunc {
|
||||
return func(b []byte) (int, error) {
|
||||
if !rrw.written {
|
||||
rrw.written = true
|
||||
}
|
||||
return next(b)
|
||||
}
|
||||
},
|
||||
WriteHeader: func(next httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc {
|
||||
return func(statusCode int) {
|
||||
if !rrw.written {
|
||||
rrw.written = true
|
||||
rrw.status = statusCode
|
||||
// only call next WriteHeader when header is not written yet
|
||||
// this is to prevent superfluous WriteHeader call
|
||||
next(statusCode)
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
return rrw
|
||||
}
|
||||
|
||||
func putRRW(rrw *recordingResponseWriter) {
|
||||
rrw.writer = nil
|
||||
rrwPool.Put(rrw)
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler interface. It does the actual
|
||||
// tracing of the request.
|
||||
func (tw traceware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// go through all filters if any
|
||||
for _, filter := range tw.filters {
|
||||
// if there is a filter that returns false, we skip tracing
|
||||
// and execute next handler
|
||||
if !filter(r) {
|
||||
tw.handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// extract tracing header using propagator
|
||||
ctx := tw.propagators.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
||||
// create span, based on specification, we need to set already known attributes
|
||||
// when creating the span, the only thing missing here is HTTP route pattern since
|
||||
// in go-chi/chi route pattern could only be extracted once the request is executed
|
||||
// check here for details:
|
||||
//
|
||||
// https://github.com/go-chi/chi/issues/150#issuecomment-278850733
|
||||
//
|
||||
// if we have access to chi routes, we could extract the route pattern beforehand.
|
||||
spanName := ""
|
||||
routePattern := ""
|
||||
spanAttributes := httpconv.ServerRequest(tw.serverName, r)
|
||||
|
||||
if tw.chiRoutes != nil {
|
||||
rctx := chi.NewRouteContext()
|
||||
if tw.chiRoutes.Match(rctx, r.Method, r.URL.Path) {
|
||||
routePattern = rctx.RoutePattern()
|
||||
spanName = addPrefixToSpanName(tw.requestMethodInSpanName, r.Method, routePattern)
|
||||
spanAttributes = append(spanAttributes, semconv.HTTPRoute(routePattern))
|
||||
}
|
||||
}
|
||||
|
||||
// define span start options
|
||||
spanOpts := []oteltrace.SpanStartOption{
|
||||
oteltrace.WithAttributes(spanAttributes...),
|
||||
oteltrace.WithSpanKind(oteltrace.SpanKindServer),
|
||||
}
|
||||
|
||||
if tw.publicEndpointFn != nil && tw.publicEndpointFn(r) {
|
||||
// mark span as the root span
|
||||
spanOpts = append(spanOpts, oteltrace.WithNewRoot())
|
||||
|
||||
// linking incoming span context to the root span, we need to
|
||||
// ensure if the incoming span context is valid (because it is
|
||||
// possible for us to receive invalid span context due to various
|
||||
// reason such as bug or context propagation error) and it is
|
||||
// coming from another service (remote) before linking it to the
|
||||
// root span
|
||||
spanCtx := oteltrace.SpanContextFromContext(ctx)
|
||||
if spanCtx.IsValid() && spanCtx.IsRemote() {
|
||||
spanOpts = append(
|
||||
spanOpts,
|
||||
oteltrace.WithLinks(oteltrace.Link{
|
||||
SpanContext: spanCtx,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tracer := tw.tracer
|
||||
if tracer == nil {
|
||||
if span := trace.SpanFromContext(r.Context()); span.SpanContext().IsValid() {
|
||||
tracer = newTracer(span.TracerProvider())
|
||||
} else {
|
||||
tracer = newTracer(otel.GetTracerProvider())
|
||||
}
|
||||
}
|
||||
|
||||
// start span
|
||||
ctx, span := tracer.Start(ctx, spanName, spanOpts...)
|
||||
defer span.End()
|
||||
|
||||
// put trace_id to response header only when `WithTraceIDResponseHeader` is used
|
||||
if len(tw.traceIDResponseHeaderKey) > 0 && span.SpanContext().HasTraceID() {
|
||||
w.Header().Add(tw.traceIDResponseHeaderKey, span.SpanContext().TraceID().String())
|
||||
w.Header().Add(tw.traceSampledResponseHeaderKey, strconv.FormatBool(span.SpanContext().IsSampled()))
|
||||
}
|
||||
|
||||
// get recording response writer
|
||||
rrw := getRRW(w)
|
||||
defer putRRW(rrw)
|
||||
|
||||
// execute next http handler
|
||||
r = r.WithContext(ctx)
|
||||
tw.handler.ServeHTTP(rrw.writer, r)
|
||||
|
||||
// set span name & http route attribute if route pattern cannot be determined
|
||||
// during span creation
|
||||
if len(routePattern) == 0 {
|
||||
routePattern = chi.RouteContext(r.Context()).RoutePattern()
|
||||
span.SetAttributes(semconv.HTTPRoute(routePattern))
|
||||
|
||||
spanName = addPrefixToSpanName(tw.requestMethodInSpanName, r.Method, routePattern)
|
||||
span.SetName(spanName)
|
||||
}
|
||||
|
||||
// check if the request is a WebSocket upgrade request
|
||||
if isWebSocketRequest(r) {
|
||||
span.SetStatus(codes.Unset, "WebSocket upgrade request")
|
||||
return
|
||||
}
|
||||
|
||||
// set status code attribute
|
||||
span.SetAttributes(semconv.HTTPStatusCode(rrw.status))
|
||||
|
||||
// set span status
|
||||
span.SetStatus(httpconv.ServerStatus(rrw.status))
|
||||
}
|
||||
|
||||
func addPrefixToSpanName(shouldAdd bool, prefix, spanName string) string {
|
||||
// in chi v5.0.8, the root route will be returned has an empty string
|
||||
// (see https://github.com/go-chi/chi/blob/v5.0.8/context.go#L126)
|
||||
if spanName == "" {
|
||||
spanName = "/"
|
||||
}
|
||||
|
||||
if shouldAdd && len(spanName) > 0 {
|
||||
spanName = prefix + " " + spanName
|
||||
}
|
||||
return spanName
|
||||
}
|
||||
|
||||
// isWebSocketRequest checks if an HTTP request is a WebSocket upgrade request
|
||||
// Fix: https://github.com/riandyrn/otelchi/issues/66
|
||||
func isWebSocketRequest(r *http.Request) bool {
|
||||
// Check if the Connection header contains "Upgrade"
|
||||
connectionHeader := r.Header.Get("Connection")
|
||||
if !strings.Contains(strings.ToLower(connectionHeader), "upgrade") {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the Upgrade header is "websocket"
|
||||
upgradeHeader := r.Header.Get("Upgrade")
|
||||
return strings.ToLower(upgradeHeader) == "websocket"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package version
|
||||
|
||||
// Version is the current release version of otelchi in use.
|
||||
func Version() string {
|
||||
return "0.12.2"
|
||||
}
|
||||
Reference in New Issue
Block a user