Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Use OTel to export Prometheus Metrics #419

Merged
merged 8 commits into from
Feb 27, 2023

Conversation

thisthat
Copy link
Member

@thisthat thisthat commented Feb 19, 2023

Related Issues

In #186, there was a discussion about going with OpenTelemetry or a direct Prometheus implementation.
The agreement was to have a scraping endpoint, i.e., pull mode, and not support the push mode.
This PR wants to replace the existing direct Prometheus implementation with the vendor-agnostic OpenTelemetry one, maintaining the same feature set.

Example of exposed metrics:
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 5.1e-05
go_gc_duration_seconds{quantile="0.25"} 0.000174874
go_gc_duration_seconds{quantile="0.5"} 0.000559251
go_gc_duration_seconds{quantile="0.75"} 0.000875708
go_gc_duration_seconds{quantile="1"} 0.001527791
go_gc_duration_seconds_sum 0.010100666
go_gc_duration_seconds_count 18
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 21
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.19.3"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
go_memstats_alloc_bytes 2.6836392e+07
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
# TYPE go_memstats_alloc_bytes_total counter
go_memstats_alloc_bytes_total 2.76925672e+08
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
# TYPE go_memstats_buck_hash_sys_bytes gauge
go_memstats_buck_hash_sys_bytes 6351
# HELP go_memstats_frees_total Total number of frees.
# TYPE go_memstats_frees_total counter
go_memstats_frees_total 2.563256e+06
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
# TYPE go_memstats_gc_sys_bytes gauge
go_memstats_gc_sys_bytes 7.975824e+06
# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use.
# TYPE go_memstats_heap_alloc_bytes gauge
go_memstats_heap_alloc_bytes 2.6836392e+07
# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used.
# TYPE go_memstats_heap_idle_bytes gauge
go_memstats_heap_idle_bytes 4.0656896e+07
# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use.
# TYPE go_memstats_heap_inuse_bytes gauge
go_memstats_heap_inuse_bytes 3.2415744e+07
# HELP go_memstats_heap_objects Number of allocated objects.
# TYPE go_memstats_heap_objects gauge
go_memstats_heap_objects 77579
# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS.
# TYPE go_memstats_heap_released_bytes gauge
go_memstats_heap_released_bytes 3.0916608e+07
# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system.
# TYPE go_memstats_heap_sys_bytes gauge
go_memstats_heap_sys_bytes 7.307264e+07
# HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection.
# TYPE go_memstats_last_gc_time_seconds gauge
go_memstats_last_gc_time_seconds 1.676843405619668e+09
# HELP go_memstats_lookups_total Total number of pointer lookups.
# TYPE go_memstats_lookups_total counter
go_memstats_lookups_total 0
# HELP go_memstats_mallocs_total Total number of mallocs.
# TYPE go_memstats_mallocs_total counter
go_memstats_mallocs_total 2.640835e+06
# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures.
# TYPE go_memstats_mcache_inuse_bytes gauge
go_memstats_mcache_inuse_bytes 12000
# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system.
# TYPE go_memstats_mcache_sys_bytes gauge
go_memstats_mcache_sys_bytes 15600
# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures.
# TYPE go_memstats_mspan_inuse_bytes gauge
go_memstats_mspan_inuse_bytes 300832
# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system.
# TYPE go_memstats_mspan_sys_bytes gauge
go_memstats_mspan_sys_bytes 701760
# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place.
# TYPE go_memstats_next_gc_bytes gauge
go_memstats_next_gc_bytes 3.620952e+07
# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations.
# TYPE go_memstats_other_sys_bytes gauge
go_memstats_other_sys_bytes 1.972625e+06
# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator.
# TYPE go_memstats_stack_inuse_bytes gauge
go_memstats_stack_inuse_bytes 2.424832e+06
# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator.
# TYPE go_memstats_stack_sys_bytes gauge
go_memstats_stack_sys_bytes 2.424832e+06
# HELP go_memstats_sys_bytes Number of bytes obtained from system.
# TYPE go_memstats_sys_bytes gauge
go_memstats_sys_bytes 8.6169632e+07
# HELP go_threads Number of OS threads created.
# TYPE go_threads gauge
go_threads 18
# HELP otel_scope_info Instrumentation Scope metadata
# TYPE otel_scope_info gauge
otel_scope_info{otel_scope_name="openfeature/flagd",otel_scope_version=""} 1
# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.
# TYPE promhttp_metric_handler_requests_in_flight gauge
promhttp_metric_handler_requests_in_flight 1
# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.
# TYPE promhttp_metric_handler_requests_total counter
promhttp_metric_handler_requests_total{code="200"} 78
promhttp_metric_handler_requests_total{code="500"} 0
promhttp_metric_handler_requests_total{code="503"} 0
# HELP request_duration_seconds The latency of the HTTP requests
# TYPE request_duration_seconds histogram
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="0"} 0
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="5"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="10"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="25"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="50"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="75"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="100"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="250"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="500"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="750"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="1000"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="2500"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="5000"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="7500"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="10000"} 22661
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="+Inf"} 22661
request_duration_seconds_sum{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name=""} 33.89927686600012
request_duration_seconds_count{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name=""} 22661
# HELP requests_inflight The number of inflight requests being handled at the same time
# TYPE requests_inflight gauge
requests_inflight{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name=""} 3
# HELP response_size_bytes_bytes The size of the HTTP responses
# TYPE response_size_bytes_bytes histogram
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="0"} 0
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="5"} 0
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="10"} 0
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="25"} 0
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="50"} 1
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="75"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="100"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="250"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="500"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="750"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="1000"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="2500"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="5000"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="7500"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="10000"} 22661
response_size_bytes_bytes_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="",le="+Inf"} 22661
response_size_bytes_bytes_sum{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name=""} 1.654229e+06
response_size_bytes_bytes_count{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name=""} 22661
# HELP target_info Target metadata
# TYPE target_info gauge
target_info{service_name="unknown_service:___go_build_github_com_open_feature_flagd",telemetry_sdk_language="go",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="1.13.0"} 1

Notes

There are several benefits of using OpenTelemetry. The most prominent one is if we introduce Span supports in flagd, we could get out-of-the-box support for exemplars to pin-point slow requests.

Follow-up Tasks

  • Introduce a flag to specify the OpenTelemetry collector URL ❓

@thisthat thisthat changed the title feat: Use OTel to export Prom Metrics feat: Use OTel to export Prometheus Metrics Feb 19, 2023
@codecov
Copy link

codecov bot commented Feb 19, 2023

Codecov Report

Merging #419 (3a1f43d) into main (8daf613) will increase coverage by 0.05%.
The diff coverage is 76.78%.

@@            Coverage Diff             @@
##             main     #419      +/-   ##
==========================================
+ Coverage   64.93%   64.99%   +0.05%     
==========================================
  Files          11       11              
  Lines        1369     1374       +5     
==========================================
+ Hits          889      893       +4     
+ Misses        427      426       -1     
- Partials       53       55       +2     
Impacted Files Coverage Δ
pkg/service/connect_service.go 59.85% <40.62%> (+0.22%) ⬆️
pkg/service/connect_metrics.go 85.10% <91.25%> (+0.96%) ⬆️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@thisthat thisthat marked this pull request as ready for review February 20, 2023 09:12
@beeme1mr
Copy link
Member

Hey @thisthat, thanks for the PR. This looks like a great improvement that will set us up to add trace support in the future.

@thisthat
Copy link
Member Author

Hey @beeme1mr, I am happy to provide a follow up PR with tracing support 😉

Copy link
Member

@toddbaert toddbaert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not an OTel metrics expert, so I will approve but please consider my approval "low value" 😅 . It all looks right to me though.

@beeme1mr
Copy link
Member

I noticed that we haven't documented how to use Prometheus with Flagd. I created a follow up task to address that.
#422

@beeme1mr
Copy link
Member

Hey @thisthat, I spent some time today digging into your PR and had some feedback. It looks like the request/response metric names and properties have changed.

Before:
http_request_duration_seconds_bucket{code="200",handler="/schema.v1.Service/ResolveBoolean",method="POST",service="",le="0.005"} 6

After:
request_duration_seconds_bucket{http_method="POST",http_status_code="200",http_url="/schema.v1.Service/ResolveBoolean",otel_scope_name="openfeature/flagd",otel_scope_version="",service_name="openfeature/flagd",le="0"} 0

If possible, it would be nice to keep the original names and values so that we maintain backwards compatibility.

Next, it looks like the hitogram buckets have changed in the following ways:

Before:

  • le="0.005"
  • le="0.01"
  • le="0.025"
  • le="0.05"
  • le="0.1"
  • le="0.25"
  • le="0.5"
  • le="1"
  • le="2.5"
  • le="5"
  • le="10"
  • le="+Inf"

After:

  • le="0"
  • le="5"
  • le="10"
  • le="25"
  • le="50"
  • le="75"
  • le="100"
  • le="250"
  • le="500"
  • le="750"
  • le="1000"
  • le="2500"
  • le="5000"
  • le="7500"
  • le="10000"
  • le="+Inf"

I also noticed that the otel_scope_version is empty the service_name under the target_info gauge is "unknown_service".

@beeme1mr beeme1mr self-requested a review February 22, 2023 21:16
Copy link
Member

@beeme1mr beeme1mr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please maintain backwards compatibility.

@thisthat
Copy link
Member Author

Hey @beeme1mr thanks for looking into that! You are right, I have overlooked the bucket size and the subsystem declaration! I'll fix this!

I also noticed that the otel_scope_version is empty the service_name under the target_info gauge is "unknown_service".

This is because we don't configure any OTel Resource and the default values of the SDK are used. Here's the link to the SemConv: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md#service

if you don't mind, I will do that in a follow-up together with the tracing part since they go hand-in-hand

Copy link
Member

@beeme1mr beeme1mr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @thisthat, looks good and works well.

FYI, the bucket property names have changed slightly but they seem to be more semantically correct based on the OTel spec.

@toddbaert
Copy link
Member

toddbaert commented Feb 23, 2023

Thanks @thisthat, looks good and works well.

FYI, the bucket property names have changed slightly but they seem to be more semantically correct based on the OTel spec.

I think we should throw a ! in the title and mention this, just so it stands out in the release notes.

I think we can ignore the codecov on this one.

@beeme1mr beeme1mr changed the title feat: Use OTel to export Prometheus Metrics feat!: Use OTel to export Prometheus Metrics Feb 27, 2023
@toddbaert toddbaert force-pushed the noissue/use-otel branch 2 times, most recently from 3eca105 to 6e9c8b8 Compare February 27, 2023 19:18
Signed-off-by: Giovanni Liva <[email protected]>
Signed-off-by: Giovanni Liva <[email protected]>
Signed-off-by: Giovanni Liva <[email protected]>
Signed-off-by: Giovanni Liva <[email protected]>
Signed-off-by: Giovanni Liva <[email protected]>
Signed-off-by: Giovanni Liva <[email protected]>
@toddbaert toddbaert merged commit eb3982a into open-feature:main Feb 27, 2023
beeme1mr pushed a commit that referenced this pull request Mar 2, 2023
🤖 I have created a release *beep* *boop*
---


##
[0.4.0](v0.3.7...v0.4.0)
(2023-03-02)


### ⚠ BREAKING CHANGES

* Use OTel to export metrics (metric name changes)
([#419](#419))

### 🧹 Chore

* add additional sections to the release notes
([#449](#449))
([798f71a](798f71a))
* attach image sbom to release artefacts
([#407](#407))
([fb4ee50](fb4ee50))
* **deps:** update actions/configure-pages digest to fc89b04
([#417](#417))
([04014e7](04014e7))
* **deps:** update amannn/action-semantic-pull-request digest to b6bca70
([#441](#441))
([ce0ebe1](ce0ebe1))
* **deps:** update docker/login-action digest to ec9cdf0
([#437](#437))
([2650670](2650670))
* **deps:** update docker/metadata-action digest to 3343011
([#438](#438))
([e7ebf32](e7ebf32))
* **deps:** update github/codeql-action digest to 32dc499
([#439](#439))
([f91d91b](f91d91b))
* **deps:** update google-github-actions/release-please-action digest to
d3c71f9 ([#406](#406))
([6e1ffb2](6e1ffb2))
* disable caching tests in CI
([#442](#442))
([28a35f6](28a35f6))
* fix race condition on init read
([#409](#409))
([0c9eb23](0c9eb23))
* integration test stability
([#432](#432))
([5a6a5d5](5a6a5d5))
* integration tests
([#312](#312))
([6192ac8](6192ac8))
* reorder release note sections
([df7bfce](df7bfce))
* use -short flag in benchmark tests
([#431](#431))
([e68a6aa](e68a6aa))


### 🐛 Bug Fixes

* **deps:** update kubernetes packages to v0.26.2
([#450](#450))
([2885227](2885227))
* **deps:** update module github.com/bufbuild/connect-go to v1.5.2
([#416](#416))
([feb7f04](feb7f04))
* **deps:** update module
github.com/open-feature/go-sdk-contrib/providers/flagd to v0.1.9
([#427](#427))
([42d2705](42d2705))
* **deps:** update module github.com/open-feature/open-feature-operator
to v0.2.29 ([#429](#429))
([b7fae81](b7fae81))
* **deps:** update module github.com/stretchr/testify to v1.8.2
([#440](#440))
([ab3e674](ab3e674))
* **deps:** update module golang.org/x/net to v0.7.0
([#410](#410))
([c6133b6](c6133b6))
* **deps:** update module sigs.k8s.io/controller-runtime to v0.14.5
([#454](#454))
([f907f11](f907f11))
* remove non-error error log from parseFractionalEvaluationData
([#446](#446))
([34aca79](34aca79))


### ✨ New Features

* add debug logging for merge behaviour
([#456](#456))
([dc71e84](dc71e84))
* add Health and Readiness probes
([#418](#418))
([7f2358c](7f2358c))
* Add version to startup message
([#430](#430))
([8daf613](8daf613))
* introduce flag merge behaviour
([#414](#414))
([524f65e](524f65e))
* introduce grpc sync for flagd
([#297](#297))
([33413f2](33413f2))
* refactor and improve K8s sync provider
([#443](#443))
([4c03bfc](4c03bfc))
* Use OTel to export metrics (metric name changes)
([#419](#419))
([eb3982a](eb3982a))


### 📚 Documentation

* add .net flagd provider
([73d7840](73d7840))
* configuration merge docs
([#455](#455))
([6cb66b1](6cb66b1))
* documentation for creating a provider
([#413](#413))
([d0c099d](d0c099d))
* updated filepaths for schema store regex
([#344](#344))
([2d0e9d9](2d0e9d9))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@github-actions github-actions bot mentioned this pull request Dec 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants