Repository: netobserv/flowlogs-pipeline
Pull request #599 (2024-02-19 13:51:47)

Bump github.com/netobserv/netobserv-ebpf-agent from 0.3.3-0.20240117144239-03bb6a38b3f0 to 0.3.3
Bumps [github.com/netobserv/netobserv-ebpf-agent](https://github.com/netobserv/netobserv-ebpf-agent) from 0.3.3-0.20240117144239-03bb6a38b3f0 to 0.3.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/netobserv/netobserv-ebpf-agent/releases">github.com/netobserv/netobserv-ebpf-agent's releases</a>.</em></p>
<blockquote>
<h2>v0.3.3</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>Non-breaking changes</h3>
<ul>
<li>[NETOBSERV-1240](https://issues.redhat.com//browse/NETOBSERV-1240): prefer flows with DNS records when checking for dup by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/174">netobserv/netobserv-ebpf-agent#174</a></li>
<li>[NETOBSERV-1222](https://issues.redhat.com//browse/NETOBSERV-1222): create PR template by <a href="https://github.com/jotak"><code>@​jotak</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/175">netobserv/netobserv-ebpf-agent#175</a></li>
<li>Bump github.com/sirupsen/logrus from 1.8.1 to 1.9.3 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/179">netobserv/netobserv-ebpf-agent#179</a></li>
<li>Bump golang.org/x/sys from 0.6.0 to 0.12.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/177">netobserv/netobserv-ebpf-agent#177</a></li>
<li>Bump k8s.io/client-go from 0.24.0 to 0.28.2 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/180">netobserv/netobserv-ebpf-agent#180</a></li>
<li>Bump github.com/stretchr/testify from 1.8.1 to 1.8.4 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/182">netobserv/netobserv-ebpf-agent#182</a></li>
<li>Bump google.golang.org/protobuf from 1.28.1 to 1.31.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/183">netobserv/netobserv-ebpf-agent#183</a></li>
<li>[NETOBSERV-1317](https://issues.redhat.com//browse/NETOBSERV-1317): change netns errors to warnings to avoid confustion by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/186">netobserv/netobserv-ebpf-agent#186</a></li>
<li>[NETOBSERV-1312](https://issues.redhat.com//browse/NETOBSERV-1312): remove GC from ring buff mapper not needed by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/185">netobserv/netobserv-ebpf-agent#185</a></li>
<li>[NETOBSERV-1255](https://issues.redhat.com//browse/NETOBSERV-1255) Adding Raw Packet export capability as Packet Capture Agent(PCA) by <a href="https://github.com/shach33"><code>@​shach33</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/113">netobserv/netobserv-ebpf-agent#113</a></li>
<li>[NETOBSERV-1151](https://issues.redhat.com//browse/NETOBSERV-1151): move flp decode grpc pipe to netobserv agent to avoid flp code updates by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/168">netobserv/netobserv-ebpf-agent#168</a></li>
<li>Bump github.com/netobserv/flowlogs-pipeline from 0.1.9 to 0.1.10 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/193">netobserv/netobserv-ebpf-agent#193</a></li>
<li>Bump github.com/caarlos0/env/v6 from 6.9.1 to 6.10.1 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/194">netobserv/netobserv-ebpf-agent#194</a></li>
<li>Bump github.com/vishvananda/netns from 0.0.0-20191106174202-0a2b9b5464df to 0.0.4 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/191">netobserv/netobserv-ebpf-agent#191</a></li>
<li>Bump github.com/segmentio/kafka-go from 0.4.38 to 0.4.43 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/190">netobserv/netobserv-ebpf-agent#190</a></li>
<li>Bump github.com/vmware/go-ipfix from 0.5.13 to 0.7.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/192">netobserv/netobserv-ebpf-agent#192</a></li>
<li>close all maps when agent is terminating by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/196">netobserv/netobserv-ebpf-agent#196</a></li>
<li>[NETOBSERV-926](https://issues.redhat.com//browse/NETOBSERV-926): Fix kafka and connection tracking by <a href="https://github.com/OlivierCazade"><code>@​OlivierCazade</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/195">netobserv/netobserv-ebpf-agent#195</a></li>
<li>Bump github.com/fsnotify/fsnotify from 1.5.1 to 1.6.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/200">netobserv/netobserv-ebpf-agent#200</a></li>
<li>Bump google.golang.org/grpc from 1.53.0 to 1.58.2 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/197">netobserv/netobserv-ebpf-agent#197</a></li>
<li>[NETOBSERV-588](https://issues.redhat.com//browse/NETOBSERV-588) Add IPv4/6 DSCP field to the exported flow by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/158">netobserv/netobserv-ebpf-agent#158</a></li>
<li>Filter interfaces by their associated IPs by <a href="https://github.com/masonj5n"><code>@​masonj5n</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/111">netobserv/netobserv-ebpf-agent#111</a></li>
<li>Bump github.com/vmware/go-ipfix from 0.7.0 to 0.8.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/203">netobserv/netobserv-ebpf-agent#203</a></li>
<li>Bump golang.org/x/sys from 0.12.0 to 0.13.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/204">netobserv/netobserv-ebpf-agent#204</a></li>
<li>Bump golang.org/x/net from 0.13.0 to 0.17.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/205">netobserv/netobserv-ebpf-agent#205</a></li>
<li>Workflow: build images from release branches by <a href="https://github.com/jotak"><code>@​jotak</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/209">netobserv/netobserv-ebpf-agent#209</a></li>
<li>Bump github.com/segmentio/kafka-go from 0.4.43 to 0.4.44 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/212">netobserv/netobserv-ebpf-agent#212</a></li>
<li>Bump google.golang.org/grpc from 1.58.2 to 1.58.3 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/210">netobserv/netobserv-ebpf-agent#210</a></li>
<li>update github actions to use go1.21 by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/213">netobserv/netobserv-ebpf-agent#213</a></li>
<li>Bump github.com/cilium/ebpf from 0.11.0 to 0.12.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/211">netobserv/netobserv-ebpf-agent#211</a></li>
<li>Bump k8s.io/client-go from 0.28.2 to 0.28.3 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/214">netobserv/netobserv-ebpf-agent#214</a></li>
<li>Bump github.com/fsnotify/fsnotify from 1.6.0 to 1.7.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/215">netobserv/netobserv-ebpf-agent#215</a></li>
<li>Bump github.com/cilium/ebpf from 0.12.0 to 0.12.2 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/216">netobserv/netobserv-ebpf-agent#216</a></li>
<li>[NETOBSERV-1245](https://issues.redhat.com//browse/NETOBSERV-1245): [NETOBSERV-1304](https://issues.redhat.com//browse/NETOBSERV-1304): fix TCP DNS query by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/206">netobserv/netobserv-ebpf-agent#206</a></li>
<li>Bump google.golang.org/grpc from 1.58.3 to 1.59.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/219">netobserv/netobserv-ebpf-agent#219</a></li>
<li>[NETOBSERV-1379](https://issues.redhat.com//browse/NETOBSERV-1379): enhance DNS debugging to dbg DNS over TCP with NA fields by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/218">netobserv/netobserv-ebpf-agent#218</a></li>
<li>Bump github.com/cilium/ebpf from 0.12.2 to 0.12.3 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/221">netobserv/netobserv-ebpf-agent#221</a></li>
<li>Bump k8s.io/apimachinery from 0.28.3 to 0.28.4 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/227">netobserv/netobserv-ebpf-agent#227</a></li>
<li>Bump k8s.io/client-go from 0.28.3 to 0.28.4 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/226">netobserv/netobserv-ebpf-agent#226</a></li>
<li>Bump sigs.k8s.io/e2e-framework from 0.0.6 to 0.3.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/198">netobserv/netobserv-ebpf-agent#198</a></li>
<li>Add a new exporter type: DirectFLP by <a href="https://github.com/jotak"><code>@​jotak</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/223">netobserv/netobserv-ebpf-agent#223</a></li>
<li>[NETOBSERV-1298](https://issues.redhat.com//browse/NETOBSERV-1298): include duplicate list in the exported record by <a href="https://github.com/msherif1234"><code>@​msherif1234</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/217">netobserv/netobserv-ebpf-agent#217</a></li>
<li>Bump golang.org/x/sys from 0.14.1-0.20231108175955-e4099bfacb8c to 0.15.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/231">netobserv/netobserv-ebpf-agent#231</a></li>
<li>Bump github.com/segmentio/kafka-go from 0.4.44 to 0.4.45 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/232">netobserv/netobserv-ebpf-agent#232</a></li>
<li>Bump github.com/segmentio/kafka-go from 0.4.45 to 0.4.46 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/netobserv/netobserv-ebpf-agent/pull/234">netobserv/netobserv-ebpf-agent#234</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a href="https://github.com/netobserv/netobserv-ebpf-agent/commits/v0.3.3">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/netobserv/netobserv-ebpf-agent&package-manager=go_modules&previous-version=0.3.3-0.20240117144239-03bb6a38b3f0&new-version=0.3.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please assign [eranra](https://github.com/eranra) for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=netobserv%2Fflowlogs-pipeline).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/netobserv/flowlogs-pipeline/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["eranra"]} -->
openshift-ci[bot]: Hi @dependabot[bot]. Thanks for your PR.

I'm waiting for a [netobserv](https://github.com/orgs/netobserv/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://github.com/orgs/netobserv/people) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=netobserv%2Fflowlogs-pipeline).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/599?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`831e2ad`)](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/commit/831e2ad12d6d49e0e81886a4dbb8e55094725aa7?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv) 66.06% compared to head [(`45300cf`)](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/599?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv) 66.06%.


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #599   +/-   ##
=======================================
  Coverage   66.06%   66.06%           
=======================================
  Files         103      103           
  Lines        7509     7509           
=======================================
  Hits         4961     4961           
  Misses       2255     2255           
  Partials      293      293           
```

| [Flag](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/599/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/599/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv) | `66.06% <ø> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/599?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv).


Patch:
diff --git a/Makefile b/Makefile
index f1121391..5bc157f4 100644
--- a/Makefile
+++ b/Makefile
@@ -132,8 +132,10 @@ clean: ## Clean
 TEST_OPTS := -race -coverpkg=./... -covermode=atomic -coverprofile=/tmp/coverage.out
 .PHONY: tests-unit
 tests-unit: validate_go ## Unit tests
+	# tests may rely on non-thread safe libs such as go-ipfix => no -race flag
+	go test $$(go list ./... | grep /testnorace)
 	# enabling CGO is required for -race flag
-	CGO_ENABLED=1 go test -p 1 $(TEST_OPTS) $$(go list ./... | grep -v /e2e)
+	CGO_ENABLED=1 go test -p 1 $(TEST_OPTS) $$(go list ./... | grep -v /e2e | grep -v /testnorace)
 
 .PHONY: tests-fast
 tests-fast: TEST_OPTS=
diff --git a/docs/api.md b/docs/api.md
index d45a2f5e..26ae6e93 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -17,22 +17,16 @@ Following is the supported API format for prometheus encode:
                      counter: monotonically increasing counter whose value can only increase
                      histogram: counts samples in configurable buckets
                      agg_histogram: counts samples in configurable buckets, pre-aggregated via an Aggregate stage
-                 filter: an optional criterion to filter entries by. Deprecated: use filters instead.
-                     key: the key to match and filter by
-                     value: the value to match and filter by
-                     type: (enum) the type of filter match: exact (default), presence, absence or regex
-                         exact: match exactly the provided fitler value
-                         presence: filter key must be present (filter value is ignored)
-                         absence: filter key must be absent (filter value is ignored)
-                         regex: match filter value as a regular expression
                  filters: a list of criteria to filter entries by
                          key: the key to match and filter by
                          value: the value to match and filter by
-                         type: (enum) the type of filter match: exact (default), presence, absence or regex
-                             exact: match exactly the provided fitler value
+                         type: (enum) the type of filter match: equal (default), not_equal, presence, absence, match_regex or not_match_regex
+                             equal: match exactly the provided filter value
+                             not_equal: the value must be different from the provided filter
                              presence: filter key must be present (filter value is ignored)
                              absence: filter key must be absent (filter value is ignored)
-                             regex: match filter value as a regular expression
+                             match_regex: match filter value as a regular expression
+                             not_match_regex: the filter value must not match the provided regular expression
                  valueKey: entry key from which to resolve metric value
                  labels: labels to be associated with the metric
                  buckets: histogram buckets
@@ -350,22 +344,16 @@ Following is the supported API format for writing metrics to an OpenTelemetry co
                      counter: monotonically increasing counter whose value can only increase
                      histogram: counts samples in configurable buckets
                      agg_histogram: counts samples in configurable buckets, pre-aggregated via an Aggregate stage
-                 filter: an optional criterion to filter entries by. Deprecated: use filters instead.
-                     key: the key to match and filter by
-                     value: the value to match and filter by
-                     type: (enum) the type of filter match: exact (default), presence, absence or regex
-                         exact: match exactly the provided fitler value
-                         presence: filter key must be present (filter value is ignored)
-                         absence: filter key must be absent (filter value is ignored)
-                         regex: match filter value as a regular expression
                  filters: a list of criteria to filter entries by
                          key: the key to match and filter by
                          value: the value to match and filter by
-                         type: (enum) the type of filter match: exact (default), presence, absence or regex
-                             exact: match exactly the provided fitler value
+                         type: (enum) the type of filter match: equal (default), not_equal, presence, absence, match_regex or not_match_regex
+                             equal: match exactly the provided filter value
+                             not_equal: the value must be different from the provided filter
                              presence: filter key must be present (filter value is ignored)
                              absence: filter key must be absent (filter value is ignored)
-                             regex: match filter value as a regular expression
+                             match_regex: match filter value as a regular expression
+                             not_match_regex: the filter value must not match the provided regular expression
                  valueKey: entry key from which to resolve metric value
                  labels: labels to be associated with the metric
                  buckets: histogram buckets
diff --git a/go.mod b/go.mod
index e6cc120a..91dcdfab 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,7 @@ require (
 	github.com/mitchellh/mapstructure v1.5.0
 	github.com/netobserv/gopipes v0.3.0
 	github.com/netobserv/loki-client-go v0.0.0-20220927092034-f37122a54500
-	github.com/netobserv/netobserv-ebpf-agent v0.3.3-0.20240117144239-03bb6a38b3f0
+	github.com/netobserv/netobserv-ebpf-agent v0.3.3
 	github.com/netsampler/goflow2 v1.3.7
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.18.0
@@ -28,23 +28,23 @@ require (
 	github.com/spf13/viper v1.18.2
 	github.com/stretchr/testify v1.8.4
 	github.com/vladimirvivien/gexe v0.2.0
-	github.com/vmware/go-ipfix v0.8.2
-	go.opentelemetry.io/otel v1.23.1
+	github.com/vmware/go-ipfix v0.9.0
+	go.opentelemetry.io/otel v1.24.0
 	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.23.1
-	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1
+	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.23.1
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1
-	go.opentelemetry.io/otel/metric v1.23.1
-	go.opentelemetry.io/otel/sdk v1.23.1
-	go.opentelemetry.io/otel/sdk/metric v1.23.1
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0
+	go.opentelemetry.io/otel/metric v1.24.0
+	go.opentelemetry.io/otel/sdk v1.24.0
+	go.opentelemetry.io/otel/sdk/metric v1.24.0
 	golang.org/x/net v0.21.0
-	google.golang.org/grpc v1.61.0
+	google.golang.org/grpc v1.62.0
 	google.golang.org/protobuf v1.32.0
 	gopkg.in/yaml.v2 v2.4.0
-	k8s.io/api v0.29.1
-	k8s.io/apimachinery v0.29.1
-	k8s.io/client-go v0.29.1
+	k8s.io/api v0.29.2
+	k8s.io/apimachinery v0.29.2
+	k8s.io/client-go v0.29.2
 	sigs.k8s.io/e2e-framework v0.3.0
 )
 
@@ -68,7 +68,7 @@ require (
 	github.com/google/gnostic-models v0.6.8 // indirect
 	github.com/google/go-cmp v0.6.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
-	github.com/google/uuid v1.5.0 // indirect
+	github.com/google/uuid v1.6.0 // indirect
 	github.com/gorilla/websocket v1.5.0 // indirect
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
@@ -111,7 +111,7 @@ require (
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 	github.com/xdg-go/scram v1.1.2 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
-	go.opentelemetry.io/otel/trace v1.23.1 // indirect
+	go.opentelemetry.io/otel/trace v1.24.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.1.0 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/multierr v1.9.0 // indirect
@@ -123,8 +123,8 @@ require (
 	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index f6bda568..b04f31a4 100644
--- a/go.sum
+++ b/go.sum
@@ -401,8 +401,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
-github.com/google/uuid v1.5.0/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/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
@@ -631,8 +631,8 @@ github.com/netobserv/gopipes v0.3.0 h1:IYmPnnAVCdSK7VmHmpFhrVBOEm45qpgbZmJz1sSW+
 github.com/netobserv/gopipes v0.3.0/go.mod h1:N7/Gz05EOF0CQQSKWsv3eof22Cj2PB08Pbttw98YFYU=
 github.com/netobserv/loki-client-go v0.0.0-20220927092034-f37122a54500 h1:RmnoJe/ci5q+QdM7upFdxiU+D8F3L3qTd5wXCwwHefw=
 github.com/netobserv/loki-client-go v0.0.0-20220927092034-f37122a54500/go.mod h1:LHXpc5tjKvsfZn0pwLKrvlgEhZcCaw3Di9mUEZGAI4E=
-github.com/netobserv/netobserv-ebpf-agent v0.3.3-0.20240117144239-03bb6a38b3f0 h1:dnJe5o1gTV5MQXTdKvOCPuPxUiHVpw0y74Z4hkq1NUQ=
-github.com/netobserv/netobserv-ebpf-agent v0.3.3-0.20240117144239-03bb6a38b3f0/go.mod h1:Q2lVXLtF079yacn9yNyduqn/GUEI/d+2ySvycwcnSVw=
+github.com/netobserv/netobserv-ebpf-agent v0.3.3 h1:4e/mDVhSTd8JzaqKapaQ78BFLJP/GYpYWQ/LHPArJeI=
+github.com/netobserv/netobserv-ebpf-agent v0.3.3/go.mod h1:R3zRIhu5faj91s9pCU735oULLC+oBDPzA/xVwiUJ9PY=
 github.com/netobserv/prometheus-common v0.46.0-netobserv h1:gpnbXZtSWBauwlSm0aC//mAwAUIvblW6xqNV4mnY2mE=
 github.com/netobserv/prometheus-common v0.46.0-netobserv/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
 github.com/netsampler/goflow2 v1.3.7 h1:XZaTy8kkMnGXpJ9hS3KbO1McyrFTpVNhVFEx9rNhMmc=
@@ -840,8 +840,8 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
 github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
 github.com/vladimirvivien/gexe v0.2.0 h1:nbdAQ6vbZ+ZNsolCgSVb9Fno60kzSuvtzVh6Ytqi/xY=
 github.com/vladimirvivien/gexe v0.2.0/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6eBht9vGVst8w=
-github.com/vmware/go-ipfix v0.8.2 h1:7pnmXZpI0995psJgno4Bur5fr9PCxGQuKjCI/RYurzA=
-github.com/vmware/go-ipfix v0.8.2/go.mod h1:NvEehcpptPOTBaLSkMA+88l2Oe8YNelVBdvj8PA/1d0=
+github.com/vmware/go-ipfix v0.9.0 h1:4/N5eFliqULEaCUQV0lafOpN/1bItPE9OTAPGhrIXus=
+github.com/vmware/go-ipfix v0.9.0/go.mod h1:MYEdL6Uel2ufOZyVCKvIAaw9hwnewK8aPr7rnwRbxMY=
 github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
 github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
 github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
@@ -873,26 +873,26 @@ 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.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY=
-go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA=
+go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
+go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.23.1 h1:ZqRWZJGHXV/1yCcEEVJ6/Uz2JtM79DNS8OZYa3vVY/A=
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.23.1/go.mod h1:D7ynngPWlGJrqyGSDOdscuv7uqttfCE3jcBvffDv9y4=
-go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps=
-go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1 h1:o8iWeVFa1BcLtVEV0LzrCxV2/55tB3xLxADr6Kyoey4=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1/go.mod h1:SEVfdK4IoBnbT2FXNM/k8yC08MrfbhWk3U4ljM8B3HE=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.23.1 h1:q/Nj5/2TZRIt6PderQ9oU0M00fzoe8UZuINGw6ETGTw=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.23.1/go.mod h1:DTE9yAu6r08jU3xa68GiSeI7oRcSEQ2RpKbbQGO+dWM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1 h1:p3A5+f5l9e/kuEBwLOrnpkIDHQFlHmbiVxMURWRK6gQ=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1/go.mod h1:OClrnXUjBqQbInvjJFjYSnMxBSCXBF8r3b34WqjiIrQ=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1 h1:cfuy3bXmLJS7M1RZmAL6SuhGtKUp2KEsrm00OlAXkq4=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1/go.mod h1:22jr92C6KwlwItJmQzfixzQM3oyyuYLCfHiMY+rpsPU=
-go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo=
-go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI=
-go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E=
-go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk=
-go.opentelemetry.io/otel/sdk/metric v1.23.1 h1:T9/8WsYg+ZqIpMWwdISVVrlGb/N0Jr1OHjR/alpKwzg=
-go.opentelemetry.io/otel/sdk/metric v1.23.1/go.mod h1:8WX6WnNtHCgUruJ4TJ+UssQjMtpxkpX0zveQC8JG/E0=
-go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8=
-go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
+go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
+go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
+go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
+go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
+go.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9osbiBrJrz/w8=
+go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0=
+go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
+go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
 go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
 go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -1046,7 +1046,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1295,11 +1295,11 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/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-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=
-google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
-google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
+google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
+google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@@ -1319,8 +1319,8 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
 google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
-google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
+google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
+google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
 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=
@@ -1379,15 +1379,15 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
 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=
 k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
-k8s.io/api v0.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw=
-k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ=
+k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
+k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
 k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo=
 k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
-k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc=
-k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
+k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
+k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
 k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
-k8s.io/client-go v0.29.1 h1:19B/+2NGEwnFLzt0uB5kNJnfTsbV8w6TgQRz9l7ti7A=
-k8s.io/client-go v0.29.1/go.mod h1:TDG/psL9hdet0TI9mGyHJSgRkW3H9JZk2dNEUS7bRks=
+k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
+k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
 k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
diff --git a/pkg/api/api.go b/pkg/api/api.go
index 0bbc0d72..83f921e8 100644
--- a/pkg/api/api.go
+++ b/pkg/api/api.go
@@ -50,10 +50,12 @@ const (
 	AddKubernetesRuleType        = "add_kubernetes"
 	AddKubernetesInfraRuleType   = "add_kubernetes_infra"
 	ReinterpretDirectionRuleType = "reinterpret_direction"
-	PromFilterExact              = "exact"
+	PromFilterEqual              = "equal"
+	PromFilterNotEqual           = "not_equal"
 	PromFilterPresence           = "presence"
 	PromFilterAbsence            = "absence"
-	PromFilterRegex              = "regex"
+	PromFilterRegex              = "match_regex"
+	PromFilterNotRegex           = "not_match_regex"
 
 	TagYaml = "yaml"
 	TagDoc  = "doc"
diff --git a/pkg/api/encode_prom.go b/pkg/api/encode_prom.go
index 07213d61..001dab3f 100644
--- a/pkg/api/encode_prom.go
+++ b/pkg/api/encode_prom.go
@@ -50,7 +50,6 @@ type PromConnectionInfo struct {
 type MetricsItem struct {
 	Name       string          `yaml:"name" json:"name" doc:"the metric name"`
 	Type       string          `yaml:"type" json:"type" enum:"MetricEncodeOperationEnum" doc:"one of the following:"`
-	Filter     MetricsFilter   `yaml:"filter,omitempty" json:"filter,omitempty" doc:"an optional criterion to filter entries by. Deprecated: use filters instead."`
 	Filters    []MetricsFilter `yaml:"filters" json:"filters" doc:"a list of criteria to filter entries by"`
 	ValueKey   string          `yaml:"valueKey" json:"valueKey" doc:"entry key from which to resolve metric value"`
 	Labels     []string        `yaml:"labels" json:"labels" doc:"labels to be associated with the metric"`
@@ -58,26 +57,21 @@ type MetricsItem struct {
 	ValueScale float64         `yaml:"valueScale" json:"valueScale" doc:"scale factor of the value (MetricVal := FlowVal / Scale)"`
 }
 
-func (i *MetricsItem) GetFilters() []MetricsFilter {
-	if len(i.Filters) == 0 && i.Filter.Key != "" {
-		return []MetricsFilter{i.Filter}
-	}
-	return i.Filters
-}
-
 type MetricsItems []MetricsItem
 
 type MetricsFilter struct {
 	Key   string `yaml:"key" json:"key" doc:"the key to match and filter by"`
 	Value string `yaml:"value" json:"value" doc:"the value to match and filter by"`
-	Type  string `yaml:"type" json:"type" enum:"MetricEncodeFilterTypeEnum" doc:"the type of filter match: exact (default), presence, absence or regex"`
+	Type  string `yaml:"type" json:"type" enum:"MetricEncodeFilterTypeEnum" doc:"the type of filter match: equal (default), not_equal, presence, absence, match_regex or not_match_regex"`
 }
 
 type MetricEncodeFilterTypeEnum struct {
-	Exact    string `yaml:"exact" json:"exact" doc:"match exactly the provided fitler value"`
-	Presence string `yaml:"presence" json:"presence" doc:"filter key must be present (filter value is ignored)"`
-	Absence  string `yaml:"absence" json:"absence" doc:"filter key must be absent (filter value is ignored)"`
-	Regex    string `yaml:"regex" json:"regex" doc:"match filter value as a regular expression"`
+	Equal         string `yaml:"equal" json:"equal" doc:"match exactly the provided filter value"`
+	NotEqual      string `yaml:"not_equal" json:"not_equal" doc:"the value must be different from the provided filter"`
+	Presence      string `yaml:"presence" json:"presence" doc:"filter key must be present (filter value is ignored)"`
+	Absence       string `yaml:"absence" json:"absence" doc:"filter key must be absent (filter value is ignored)"`
+	MatchRegex    string `yaml:"match_regex" json:"match_regex" doc:"match filter value as a regular expression"`
+	NotMatchRegex string `yaml:"not_match_regex" json:"not_match_regex" doc:"the filter value must not match the provided regular expression"`
 }
 
 func MetricEncodeFilterTypeName(t string) string {
diff --git a/pkg/pipeline/encode/encode_prom.go b/pkg/pipeline/encode/encode_prom.go
index 9780e1be..9cdef8e1 100644
--- a/pkg/pipeline/encode/encode_prom.go
+++ b/pkg/pipeline/encode/encode_prom.go
@@ -18,229 +18,82 @@
 package encode
 
 import (
-	"fmt"
-	"strings"
 	"time"
 
 	"github.com/netobserv/flowlogs-pipeline/pkg/api"
 	"github.com/netobserv/flowlogs-pipeline/pkg/config"
 	"github.com/netobserv/flowlogs-pipeline/pkg/operational"
-	putils "github.com/netobserv/flowlogs-pipeline/pkg/pipeline/utils"
 	promserver "github.com/netobserv/flowlogs-pipeline/pkg/prometheus"
-	"github.com/netobserv/flowlogs-pipeline/pkg/utils"
-
 	"github.com/prometheus/client_golang/prometheus"
 	log "github.com/sirupsen/logrus"
 )
 
 const defaultExpiryTime = time.Duration(2 * time.Minute)
 
-type gaugeInfo struct {
-	gauge *prometheus.GaugeVec
-	info  *MetricInfo
-}
-
-type counterInfo struct {
-	counter *prometheus.CounterVec
-	info    *MetricInfo
-}
-
-type histoInfo struct {
-	histo *prometheus.HistogramVec
-	info  *MetricInfo
-}
-
 type EncodeProm struct {
-	cfg              *api.PromEncode
-	registerer       prometheus.Registerer
-	gauges           []gaugeInfo
-	counters         []counterInfo
-	histos           []histoInfo
-	aggHistos        []histoInfo
-	expiryTime       time.Duration
-	mCache           *putils.TimedCache
-	mChacheLenMetric prometheus.Gauge
-	exitChan         <-chan struct{}
-	metricsProcessed prometheus.Counter
-	metricsDropped   prometheus.Counter
-	errorsCounter    *prometheus.CounterVec
+	cfg          *api.PromEncode
+	registerer   prometheus.Registerer
+	metricCommon *MetricsCommonStruct
 }
 
-var (
-	MetricsProcessed = operational.DefineMetric(
-		"metrics_processed",
-		"Number of metrics processed",
-		operational.TypeCounter,
-		"stage",
-	)
-	MetricsDropped = operational.DefineMetric(
-		"metrics_dropped",
-		"Number of metrics dropped",
-		operational.TypeCounter,
-		"stage",
-	)
-	EncodePromErrors = operational.DefineMetric(
-		"encode_prom_errors",
-		"Total errors during metrics generation",
-		operational.TypeCounter,
-		"error", "metric", "key",
-	)
-	mChacheLen = operational.DefineMetric(
-		"encode_prom_metrics_reported",
-		"Total number of prometheus metrics reported by this stage",
-		operational.TypeGauge,
-		"stage",
-	)
-)
-
-// Encode encodes a metric before being stored
+// Encode encodes a metric before being stored; the heavy work is done by the MetricCommonEncode
 func (e *EncodeProm) Encode(metricRecord config.GenericMap) {
 	log.Tracef("entering EncodeMetric. metricRecord = %v", metricRecord)
-
-	// Process counters
-	for _, mInfo := range e.counters {
-		labels, value := e.prepareMetric(metricRecord, mInfo.info, mInfo.counter.MetricVec)
-		if labels == nil {
-			continue
-		}
-		m, err := mInfo.counter.GetMetricWith(labels)
-		if err != nil {
-			log.Errorf("labels registering error on %s: %v", mInfo.info.Name, err)
-			e.errorsCounter.WithLabelValues("LabelsRegisteringError", mInfo.info.Name, "").Inc()
-			continue
-		}
-		m.Add(value)
-		e.metricsProcessed.Inc()
-	}
-
-	// Process gauges
-	for _, mInfo := range e.gauges {
-		labels, value := e.prepareMetric(metricRecord, mInfo.info, mInfo.gauge.MetricVec)
-		if labels == nil {
-			continue
-		}
-		m, err := mInfo.gauge.GetMetricWith(labels)
-		if err != nil {
-			log.Errorf("labels registering error on %s: %v", mInfo.info.Name, err)
-			e.errorsCounter.WithLabelValues("LabelsRegisteringError", mInfo.info.Name, "").Inc()
-			continue
-		}
-		m.Set(value)
-		e.metricsProcessed.Inc()
-	}
-
-	// Process histograms
-	for _, mInfo := range e.histos {
-		labels, value := e.prepareMetric(metricRecord, mInfo.info, mInfo.histo.MetricVec)
-		if labels == nil {
-			continue
-		}
-		m, err := mInfo.histo.GetMetricWith(labels)
-		if err != nil {
-			log.Errorf("labels registering error on %s: %v", mInfo.info.Name, err)
-			e.errorsCounter.WithLabelValues("LabelsRegisteringError", mInfo.info.Name, "").Inc()
-			continue
-		}
-		m.Observe(value)
-		e.metricsProcessed.Inc()
-	}
-
-	// Process pre-aggregated histograms
-	for _, mInfo := range e.aggHistos {
-		labels, values := e.prepareAggHisto(metricRecord, mInfo.info, mInfo.histo.MetricVec)
-		if labels == nil {
-			continue
-		}
-		m, err := mInfo.histo.GetMetricWith(labels)
-		if err != nil {
-			log.Errorf("labels registering error on %s: %v", mInfo.info.Name, err)
-			e.errorsCounter.WithLabelValues("LabelsRegisteringError", mInfo.info.Name, "").Inc()
-			continue
-		}
-		for _, v := range values {
-			m.Observe(v)
-		}
-		e.metricsProcessed.Inc()
-	}
+	e.metricCommon.MetricCommonEncode(e, metricRecord)
 }
 
-func (e *EncodeProm) prepareMetric(flow config.GenericMap, info *MetricInfo, m *prometheus.MetricVec) (map[string]string, float64) {
-	val := e.extractGenericValue(flow, info)
-	if val == nil {
-		return nil, 0
-	}
-	floatVal, err := utils.ConvertToFloat64(val)
+func (e *EncodeProm) ProcessCounter(m interface{}, labels map[string]string, value float64) error {
+	counter := m.(*prometheus.CounterVec)
+	mm, err := counter.GetMetricWith(labels)
 	if err != nil {
-		e.errorsCounter.WithLabelValues("ValueConversionError", info.Name, info.ValueKey).Inc()
-		return nil, 0
-	}
-	if info.ValueScale != 0 {
-		floatVal = floatVal / info.ValueScale
+		return err
 	}
-
-	entryLabels, key := ExtractLabelsAndKey(flow, &info.MetricsItem)
-	// Update entry for expiry mechanism (the entry itself is its own cleanup function)
-	_, ok := e.mCache.UpdateCacheEntry(key, func() { m.Delete(entryLabels) })
-	if !ok {
-		e.metricsDropped.Inc()
-		return nil, 0
-	}
-	return entryLabels, floatVal
+	mm.Add(value)
+	return nil
 }
 
-func (e *EncodeProm) prepareAggHisto(flow config.GenericMap, info *MetricInfo, m *prometheus.MetricVec) (map[string]string, []float64) {
-	val := e.extractGenericValue(flow, info)
-	if val == nil {
-		return nil, nil
-	}
-	values, ok := val.([]float64)
-	if !ok {
-		e.errorsCounter.WithLabelValues("HistoValueConversionError", info.Name, info.ValueKey).Inc()
-		return nil, nil
+func (e *EncodeProm) ProcessGauge(m interface{}, labels map[string]string, value float64, key string) error {
+	gauge := m.(*prometheus.GaugeVec)
+	mm, err := gauge.GetMetricWith(labels)
+	if err != nil {
+		return err
 	}
+	mm.Set(value)
+	return nil
+}
 
-	entryLabels, key := ExtractLabelsAndKey(flow, &info.MetricsItem)
-	// Update entry for expiry mechanism (the entry itself is its own cleanup function)
-	_, ok = e.mCache.UpdateCacheEntry(key, func() { m.Delete(entryLabels) })
-	if !ok {
-		e.metricsDropped.Inc()
-		return nil, nil
+func (e *EncodeProm) ProcessHist(m interface{}, labels map[string]string, value float64) error {
+	hist := m.(*prometheus.HistogramVec)
+	mm, err := hist.GetMetricWith(labels)
+	if err != nil {
+		return err
 	}
-	return entryLabels, values
+	mm.Observe(value)
+	return nil
 }
 
-func (e *EncodeProm) extractGenericValue(flow config.GenericMap, info *MetricInfo) interface{} {
-	for _, pred := range info.FilterPredicates {
-		if !pred(flow) {
-			return nil
-		}
-	}
-	if info.ValueKey == "" {
-		// No value key means it's a records / flows counter (1 flow = 1 increment), so just return 1
-		return 1
+func (e *EncodeProm) ProcessAggHist(m interface{}, labels map[string]string, values []float64) error {
+	hist := m.(*prometheus.HistogramVec)
+	mm, err := hist.GetMetricWith(labels)
+	if err != nil {
+		return err
 	}
-	val, found := flow[info.ValueKey]
-	if !found {
-		e.errorsCounter.WithLabelValues("RecordKeyMissing", info.Name, info.ValueKey).Inc()
-		return nil
+	for _, v := range values {
+		mm.Observe(v)
 	}
-	return val
+	return nil
 }
 
-func ExtractLabelsAndKey(flow config.GenericMap, info *api.MetricsItem) (map[string]string, string) {
-	entryLabels := make(map[string]string, len(info.Labels))
-	key := strings.Builder{}
-	key.WriteString(info.Name)
-	key.WriteRune('|')
-	for _, t := range info.Labels {
-		entryLabels[t] = ""
-		if v, ok := flow[t]; ok {
-			entryLabels[t] = fmt.Sprintf("%v", v)
-		}
-		key.WriteString(entryLabels[t])
-		key.WriteRune('|')
+func (e *EncodeProm) GetChacheEntry(entryLabels map[string]string, m interface{}) interface{} {
+	switch mv := m.(type) {
+	case *prometheus.CounterVec:
+		return func() { mv.Delete(entryLabels) }
+	case *prometheus.GaugeVec:
+		return func() { mv.Delete(entryLabels) }
+	case *prometheus.HistogramVec:
+		return func() { mv.Delete(entryLabels) }
 	}
-	return entryLabels, key.String()
+	return nil
 }
 
 // callback function from lru cleanup
@@ -248,19 +101,6 @@ func (e *EncodeProm) Cleanup(cleanupFunc interface{}) {
 	cleanupFunc.(func())()
 }
 
-func (e *EncodeProm) cleanupExpiredEntriesLoop() {
-	ticker := time.NewTicker(e.expiryTime)
-	for {
-		select {
-		case <-e.exitChan:
-			log.Debugf("exiting cleanupExpiredEntriesLoop because of signal")
-			return
-		case <-ticker.C:
-			e.mCache.CleanupExpiredEntries(e.expiryTime, e.Cleanup)
-		}
-	}
-}
-
 func NewEncodeProm(opMetrics *operational.Metrics, params config.StageParam) (Encoder, error) {
 	cfg := api.PromEncode{}
 	if params.Encode != nil && params.Encode.Prom != nil {
@@ -282,11 +122,13 @@ func NewEncodeProm(opMetrics *operational.Metrics, params config.StageParam) (En
 	} else {
 		registerer = prometheus.DefaultRegisterer
 	}
+	w := &EncodeProm{
+		cfg:        params.Encode.Prom,
+		registerer: registerer,
+	}
 
-	counters := []counterInfo{}
-	gauges := []gaugeInfo{}
-	histos := []histoInfo{}
-	aggHistos := []histoInfo{}
+	metricCommon := NewMetricsCommonStruct(opMetrics, cfg.MaxMetrics, params.Name, expiryTime, w.Cleanup)
+	w.metricCommon = metricCommon
 
 	for _, mCfg := range cfg.Metrics {
 		fullMetricName := cfg.Prefix + mCfg.Name
@@ -302,10 +144,7 @@ func NewEncodeProm(opMetrics *operational.Metrics, params config.StageParam) (En
 				log.Errorf("error during prometheus.Register: %v", err)
 				return nil, err
 			}
-			counters = append(counters, counterInfo{
-				counter: counter,
-				info:    mInfo,
-			})
+			metricCommon.AddCounter(counter, mInfo)
 		case api.MetricEncodeOperationName("Gauge"):
 			gauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: fullMetricName, Help: ""}, labels)
 			err := registerer.Register(gauge)
@@ -313,10 +152,7 @@ func NewEncodeProm(opMetrics *operational.Metrics, params config.StageParam) (En
 				log.Errorf("error during prometheus.Register: %v", err)
 				return nil, err
 			}
-			gauges = append(gauges, gaugeInfo{
-				gauge: gauge,
-				info:  mInfo,
-			})
+			metricCommon.AddGauge(gauge, mInfo)
 		case api.MetricEncodeOperationName("Histogram"):
 			log.Debugf("buckets = %v", mCfg.Buckets)
 			hist := prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: fullMetricName, Help: "", Buckets: mCfg.Buckets}, labels)
@@ -325,10 +161,7 @@ func NewEncodeProm(opMetrics *operational.Metrics, params config.StageParam) (En
 				log.Errorf("error during prometheus.Register: %v", err)
 				return nil, err
 			}
-			histos = append(histos, histoInfo{
-				histo: hist,
-				info:  mInfo,
-			})
+			metricCommon.AddHist(hist, mInfo)
 		case api.MetricEncodeOperationName("AggHistogram"):
 			log.Debugf("buckets = %v", mCfg.Buckets)
 			hist := prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: fullMetricName, Help: "", Buckets: mCfg.Buckets}, labels)
@@ -337,38 +170,11 @@ func NewEncodeProm(opMetrics *operational.Metrics, params config.StageParam) (En
 				log.Errorf("error during prometheus.Register: %v", err)
 				return nil, err
 			}
-			aggHistos = append(aggHistos, histoInfo{
-				histo: hist,
-				info:  mInfo,
-			})
+			metricCommon.AddAggHist(hist, mInfo)
 		case "default":
 			log.Errorf("invalid metric type = %v, skipping", mCfg.Type)
 			continue
 		}
 	}
-
-	log.Debugf("counters = %v", counters)
-	log.Debugf("gauges = %v", gauges)
-	log.Debugf("histos = %v", histos)
-	log.Debugf("aggHistos = %v", aggHistos)
-
-	mChacheLenMetric := opMetrics.NewGauge(&mChacheLen, params.Name)
-
-	w := &EncodeProm{
-		cfg:              params.Encode.Prom,
-		registerer:       registerer,
-		counters:         counters,
-		gauges:           gauges,
-		histos:           histos,
-		aggHistos:        aggHistos,
-		expiryTime:       expiryTime.Duration,
-		mCache:           putils.NewTimedCache(cfg.MaxMetrics, mChacheLenMetric),
-		mChacheLenMetric: mChacheLenMetric,
-		exitChan:         putils.ExitChannel(),
-		metricsProcessed: opMetrics.NewCounter(&MetricsProcessed, params.Name),
-		metricsDropped:   opMetrics.NewCounter(&MetricsDropped, params.Name),
-		errorsCounter:    opMetrics.NewCounterVec(&EncodePromErrors),
-	}
-	go w.cleanupExpiredEntriesLoop()
 	return w, nil
 }
diff --git a/pkg/pipeline/encode/encode_prom_metric.go b/pkg/pipeline/encode/encode_prom_metric.go
index 15fe7854..618ccd2a 100644
--- a/pkg/pipeline/encode/encode_prom_metric.go
+++ b/pkg/pipeline/encode/encode_prom_metric.go
@@ -3,6 +3,7 @@ package encode
 import (
 	"fmt"
 	"regexp"
+	"strings"
 
 	"github.com/netobserv/flowlogs-pipeline/pkg/api"
 	"github.com/netobserv/flowlogs-pipeline/pkg/config"
@@ -10,6 +11,8 @@ import (
 
 type Predicate func(flow config.GenericMap) bool
 
+var variableExtractor, _ = regexp.Compile(`\$\(([^\)]+)\)`)
+
 type MetricInfo struct {
 	api.MetricsItem
 	FilterPredicates []Predicate
@@ -29,20 +32,30 @@ func Absence(filter api.MetricsFilter) Predicate {
 	}
 }
 
-func Exact(filter api.MetricsFilter) Predicate {
+func Equal(filter api.MetricsFilter) Predicate {
+	varLookups := extractVarLookups(filter.Value)
 	return func(flow config.GenericMap) bool {
 		if val, found := flow[filter.Key]; found {
 			sVal, ok := val.(string)
 			if !ok {
 				sVal = fmt.Sprint(val)
 			}
-			return sVal == filter.Value
+			value := filter.Value
+			if len(varLookups) > 0 {
+				value = injectVars(flow, value, varLookups)
+			}
+			return sVal == value
 		}
 		return false
 	}
 }
 
-func regex(filter api.MetricsFilter) Predicate {
+func NotEqual(filter api.MetricsFilter) Predicate {
+	pred := Equal(filter)
+	return func(flow config.GenericMap) bool { return !pred(flow) }
+}
+
+func Regex(filter api.MetricsFilter) Predicate {
 	r, _ := regexp.Compile(filter.Value)
 	return func(flow config.GenericMap) bool {
 		if val, found := flow[filter.Key]; found {
@@ -56,26 +69,60 @@ func regex(filter api.MetricsFilter) Predicate {
 	}
 }
 
+func NotRegex(filter api.MetricsFilter) Predicate {
+	pred := Regex(filter)
+	return func(flow config.GenericMap) bool { return !pred(flow) }
+}
+
 func filterToPredicate(filter api.MetricsFilter) Predicate {
 	switch filter.Type {
-	case api.PromFilterExact:
-		return Exact(filter)
+	case api.PromFilterEqual:
+		return Equal(filter)
+	case api.PromFilterNotEqual:
+		return NotEqual(filter)
 	case api.PromFilterPresence:
 		return Presence(filter)
 	case api.PromFilterAbsence:
 		return Absence(filter)
 	case api.PromFilterRegex:
-		return regex(filter)
+		return Regex(filter)
+	case api.PromFilterNotRegex:
+		return NotRegex(filter)
 	}
 	// Default = Exact
-	return Exact(filter)
+	return Equal(filter)
+}
+
+func extractVarLookups(value string) [][]string {
+	// Extract list of variables to lookup
+	// E.g: filter "$(SrcAddr):$(SrcPort)" would return [SrcAddr,SrcPort]
+	if len(value) > 0 {
+		return variableExtractor.FindAllStringSubmatch(value, -1)
+	}
+	return nil
+}
+
+func injectVars(flow config.GenericMap, filterValue string, varLookups [][]string) string {
+	injected := filterValue
+	for _, matchGroup := range varLookups {
+		var value string
+		if rawVal, found := flow[matchGroup[1]]; found {
+			if sVal, ok := rawVal.(string); ok {
+				value = sVal
+			} else {
+				value = fmt.Sprint(rawVal)
+			}
+		}
+		injected = strings.ReplaceAll(injected, matchGroup[0], value)
+	}
+	return injected
 }
 
 func CreateMetricInfo(def api.MetricsItem) *MetricInfo {
 	mi := MetricInfo{
 		MetricsItem: def,
 	}
-	for _, f := range def.GetFilters() {
+	for _, f := range def.Filters {
 		mi.FilterPredicates = append(mi.FilterPredicates, filterToPredicate(f))
 	}
 	return &mi
diff --git a/pkg/pipeline/encode/metrics_common.go b/pkg/pipeline/encode/metrics_common.go
new file mode 100644
index 00000000..c90a5a2e
--- /dev/null
+++ b/pkg/pipeline/encode/metrics_common.go
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2024 IBM, Inc.
+ *
+ * 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.
+ *
+ */
+
+package encode
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/netobserv/flowlogs-pipeline/pkg/api"
+	"github.com/netobserv/flowlogs-pipeline/pkg/config"
+	"github.com/netobserv/flowlogs-pipeline/pkg/operational"
+	putils "github.com/netobserv/flowlogs-pipeline/pkg/pipeline/utils"
+	"github.com/netobserv/flowlogs-pipeline/pkg/utils"
+	"github.com/prometheus/client_golang/prometheus"
+	log "github.com/sirupsen/logrus"
+)
+
+type mInfoStruct struct {
+	genericMetric interface{} // can be a counter, gauge, or histogram pointer
+	info          *MetricInfo
+}
+
+type MetricsCommonStruct struct {
+	gauges           []mInfoStruct
+	counters         []mInfoStruct
+	histos           []mInfoStruct
+	aggHistos        []mInfoStruct
+	mCache           *putils.TimedCache
+	mChacheLenMetric prometheus.Gauge
+	metricsProcessed prometheus.Counter
+	metricsDropped   prometheus.Counter
+	errorsCounter    *prometheus.CounterVec
+	expiryTime       time.Duration
+	exitChan         <-chan struct{}
+}
+
+type MetricsCommonInterface interface {
+	GetChacheEntry(entryLabels map[string]string, m interface{}) interface{}
+	ProcessCounter(m interface{}, labels map[string]string, value float64) error
+	ProcessGauge(m interface{}, labels map[string]string, value float64, key string) error
+	ProcessHist(m interface{}, labels map[string]string, value float64) error
+	ProcessAggHist(m interface{}, labels map[string]string, value []float64) error
+}
+
+var (
+	metricsProcessed = operational.DefineMetric(
+		"metrics_processed",
+		"Number of metrics processed",
+		operational.TypeCounter,
+		"stage",
+	)
+	metricsDropped = operational.DefineMetric(
+		"metrics_dropped",
+		"Number of metrics dropped",
+		operational.TypeCounter,
+		"stage",
+	)
+	encodePromErrors = operational.DefineMetric(
+		"encode_prom_errors",
+		"Total errors during metrics generation",
+		operational.TypeCounter,
+		"error", "metric", "key",
+	)
+	mChacheLen = operational.DefineMetric(
+		"encode_prom_metrics_reported",
+		"Total number of prometheus metrics reported by this stage",
+		operational.TypeGauge,
+		"stage",
+	)
+)
+
+func (m *MetricsCommonStruct) AddCounter(g interface{}, info *MetricInfo) {
+	mStruct := mInfoStruct{genericMetric: g, info: info}
+	m.counters = append(m.counters, mStruct)
+}
+
+func (m *MetricsCommonStruct) AddGauge(g interface{}, info *MetricInfo) {
+	mStruct := mInfoStruct{genericMetric: g, info: info}
+	m.gauges = append(m.gauges, mStruct)
+}
+
+func (m *MetricsCommonStruct) AddHist(g interface{}, info *MetricInfo) {
+	mStruct := mInfoStruct{genericMetric: g, info: info}
+	m.histos = append(m.histos, mStruct)
+}
+
+func (m *MetricsCommonStruct) AddAggHist(g interface{}, info *MetricInfo) {
+	mStruct := mInfoStruct{genericMetric: g, info: info}
+	m.aggHistos = append(m.aggHistos, mStruct)
+}
+
+func (e *MetricsCommonStruct) MetricCommonEncode(mci MetricsCommonInterface, metricRecord config.GenericMap) {
+	log.Tracef("entering MetricCommonEncode. metricRecord = %v", metricRecord)
+
+	// Process counters
+	for _, mInfo := range e.counters {
+		labels, value, _ := e.prepareMetric(mci, metricRecord, mInfo.info, mInfo.genericMetric)
+		if labels == nil {
+			continue
+		}
+		err := mci.ProcessCounter(mInfo.genericMetric, labels, value)
+		if err != nil {
+			log.Errorf("labels registering error on %s: %v", mInfo.info.Name, err)
+			e.errorsCounter.WithLabelValues("LabelsRegisteringError", mInfo.info.Name, "").Inc()
+			continue
+		}
+		e.metricsProcessed.Inc()
+	}
+
+	// Process gauges
+	for _, mInfo := range e.gauges {
+		labels, value, key := e.prepareMetric(mci, metricRecord, mInfo.info, mInfo.genericMetric)
+		if labels == nil {
+			continue
+		}
+		err := mci.ProcessGauge(mInfo.genericMetric, labels, value, key)
+		if err != nil {
+			log.Errorf("labels registering error on %s: %v", mInfo.info.Name, err)
+			e.errorsCounter.WithLabelValues("LabelsRegisteringError", mInfo.info.Name, "").Inc()
+			continue
+		}
+		e.metricsProcessed.Inc()
+	}
+
+	// Process histograms
+	for _, mInfo := range e.histos {
+		labels, value, _ := e.prepareMetric(mci, metricRecord, mInfo.info, mInfo.genericMetric)
+		if labels == nil {
+			continue
+		}
+		err := mci.ProcessHist(mInfo.genericMetric, labels, value)
+		if err != nil {
+			log.Errorf("labels registering error on %s: %v", mInfo.info.Name, err)
+			e.errorsCounter.WithLabelValues("LabelsRegisteringError", mInfo.info.Name, "").Inc()
+			continue
+		}
+		e.metricsProcessed.Inc()
+	}
+
+	// Process pre-aggregated histograms
+	for _, mInfo := range e.aggHistos {
+		labels, values := e.prepareAggHisto(mci, metricRecord, mInfo.info, mInfo.genericMetric)
+		if labels == nil {
+			continue
+		}
+		err := mci.ProcessAggHist(mInfo.genericMetric, labels, values)
+		if err != nil {
+			log.Errorf("labels registering error on %s: %v", mInfo.info.Name, err)
+			e.errorsCounter.WithLabelValues("LabelsRegisteringError", mInfo.info.Name, "").Inc()
+			continue
+		}
+		e.metricsProcessed.Inc()
+	}
+}
+
+func (m *MetricsCommonStruct) prepareMetric(mci MetricsCommonInterface, flow config.GenericMap, info *MetricInfo, mv interface{}) (map[string]string, float64, string) {
+	val := m.extractGenericValue(flow, info)
+	if val == nil {
+		return nil, 0, ""
+	}
+	floatVal, err := utils.ConvertToFloat64(val)
+	if err != nil {
+		m.errorsCounter.WithLabelValues("ValueConversionError", info.Name, info.ValueKey).Inc()
+		return nil, 0, ""
+	}
+	if info.ValueScale != 0 {
+		floatVal = floatVal / info.ValueScale
+	}
+
+	entryLabels, key := extractLabelsAndKey(flow, &info.MetricsItem)
+	// Update entry for expiry mechanism (the entry itself is its own cleanup function)
+	cacheEntry := mci.GetChacheEntry(entryLabels, mv)
+	_, ok := m.mCache.UpdateCacheEntry(key, cacheEntry)
+	if !ok {
+		m.metricsDropped.Inc()
+		return nil, 0, ""
+	}
+	return entryLabels, floatVal, key
+}
+
+func (m *MetricsCommonStruct) prepareAggHisto(mci MetricsCommonInterface, flow config.GenericMap, info *MetricInfo, mc interface{}) (map[string]string, []float64) {
+	val := m.extractGenericValue(flow, info)
+	if val == nil {
+		return nil, nil
+	}
+	values, ok := val.([]float64)
+	if !ok {
+		m.errorsCounter.WithLabelValues("HistoValueConversionError", info.Name, info.ValueKey).Inc()
+		return nil, nil
+	}
+
+	entryLabels, key := extractLabelsAndKey(flow, &info.MetricsItem)
+	// Update entry for expiry mechanism (the entry itself is its own cleanup function)
+	cacheEntry := mci.GetChacheEntry(entryLabels, mc)
+	_, ok = m.mCache.UpdateCacheEntry(key, cacheEntry)
+	if !ok {
+		m.metricsDropped.Inc()
+		return nil, nil
+	}
+	return entryLabels, values
+}
+
+func (m *MetricsCommonStruct) extractGenericValue(flow config.GenericMap, info *MetricInfo) interface{} {
+	for _, pred := range info.FilterPredicates {
+		if !pred(flow) {
+			return nil
+		}
+	}
+	if info.ValueKey == "" {
+		// No value key means it's a records / flows counter (1 flow = 1 increment), so just return 1
+		return 1
+	}
+	val, found := flow[info.ValueKey]
+	if !found {
+		m.errorsCounter.WithLabelValues("RecordKeyMissing", info.Name, info.ValueKey).Inc()
+		return nil
+	}
+	return val
+}
+
+func extractLabelsAndKey(flow config.GenericMap, info *api.MetricsItem) (map[string]string, string) {
+	entryLabels := make(map[string]string, len(info.Labels))
+	key := strings.Builder{}
+	key.WriteString(info.Name)
+	key.WriteRune('|')
+	for _, t := range info.Labels {
+		entryLabels[t] = ""
+		if v, ok := flow[t]; ok {
+			entryLabels[t] = fmt.Sprintf("%v", v)
+		}
+		key.WriteString(entryLabels[t])
+		key.WriteRune('|')
+	}
+	return entryLabels, key.String()
+}
+
+func (m *MetricsCommonStruct) cleanupExpiredEntriesLoop(callback putils.CacheCallback) {
+	ticker := time.NewTicker(m.expiryTime)
+	for {
+		select {
+		case <-m.exitChan:
+			log.Debugf("exiting cleanupExpiredEntriesLoop because of signal")
+			return
+		case <-ticker.C:
+			m.mCache.CleanupExpiredEntries(m.expiryTime, callback)
+		}
+	}
+}
+
+func NewMetricsCommonStruct(opMetrics *operational.Metrics, maxCacheEntries int, name string, expiryTime api.Duration, callback putils.CacheCallback) *MetricsCommonStruct {
+	mChacheLenMetric := opMetrics.NewGauge(&mChacheLen, name)
+	m := &MetricsCommonStruct{
+		mCache:           putils.NewTimedCache(maxCacheEntries, mChacheLenMetric),
+		mChacheLenMetric: mChacheLenMetric,
+		metricsProcessed: opMetrics.NewCounter(&metricsProcessed, name),
+		metricsDropped:   opMetrics.NewCounter(&metricsDropped, name),
+		errorsCounter:    opMetrics.NewCounterVec(&encodePromErrors),
+		expiryTime:       expiryTime.Duration,
+		exitChan:         putils.ExitChannel(),
+	}
+	go m.cleanupExpiredEntriesLoop(callback)
+	return m
+}
diff --git a/pkg/pipeline/encode/opentelemetry/encode_otlpmetrics.go b/pkg/pipeline/encode/opentelemetry/encode_otlpmetrics.go
index 739a7d4e..c645bd53 100644
--- a/pkg/pipeline/encode/opentelemetry/encode_otlpmetrics.go
+++ b/pkg/pipeline/encode/opentelemetry/encode_otlpmetrics.go
@@ -25,9 +25,6 @@ import (
 	"github.com/netobserv/flowlogs-pipeline/pkg/config"
 	"github.com/netobserv/flowlogs-pipeline/pkg/operational"
 	"github.com/netobserv/flowlogs-pipeline/pkg/pipeline/encode"
-	putils "github.com/netobserv/flowlogs-pipeline/pkg/pipeline/utils"
-	"github.com/netobserv/flowlogs-pipeline/pkg/utils"
-	"github.com/prometheus/client_golang/prometheus"
 	log "github.com/sirupsen/logrus"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
@@ -36,116 +33,60 @@ import (
 	"go.opentelemetry.io/otel/sdk/resource"
 )
 
-// TODO: Refactor the code that is common with encode_prom
-const (
-	defaultExpiryTime = time.Duration(2 * time.Minute)
-	flpMeterName      = "flp_meter"
-)
-
-type counterInfo struct {
-	counter *metric.Float64Counter
-	info    *encode.MetricInfo
-}
-
-type gaugeInfo struct {
-	gauge *metric.Float64ObservableGauge
-	info  *encode.MetricInfo
-	obs   Float64Gauge
-}
-
-// TBD: Handle histograms
-/*
-type histoInfo struct {
-	histo *metric.Float64Histogram
-	info  *encode.MetricInfo
-}
-*/
+const defaultExpiryTime = time.Duration(2 * time.Minute)
+const flpMeterName = "flp_meter"
 
 type EncodeOtlpMetrics struct {
-	cfg      api.EncodeOtlpMetrics
-	ctx      context.Context
-	res      *resource.Resource
-	mp       *sdkmetric.MeterProvider
-	counters []counterInfo
-	gauges   []gaugeInfo
-	//histos           []histoInfo
-	//aggHistos        []histoInfo
-	expiryTime       time.Duration
-	mCache           *putils.TimedCache
-	exitChan         <-chan struct{}
-	meter            metric.Meter
-	metricsProcessed prometheus.Counter
-	metricsDropped   prometheus.Counter
-	errorsCounter    *prometheus.CounterVec
+	cfg          api.EncodeOtlpMetrics
+	ctx          context.Context
+	res          *resource.Resource
+	mp           *sdkmetric.MeterProvider
+	meter        metric.Meter
+	metricCommon *encode.MetricsCommonStruct
 }
 
 // Encode encodes a metric to be exported
 func (e *EncodeOtlpMetrics) Encode(metricRecord config.GenericMap) {
 	log.Tracef("entering EncodeOtlpMetrics. entry = %v", metricRecord)
+	e.metricCommon.MetricCommonEncode(e, metricRecord)
+}
 
-	// Process counters
-	for _, mInfo := range e.counters {
-		labels, value, _ := e.prepareMetric(metricRecord, mInfo.info)
-		if labels == nil {
-			continue
-		}
-		// set attributes using the labels
-		attributes := obtainAttributesFromLabels(labels)
-		(*mInfo.counter).Add(e.ctx, value, metric.WithAttributes(attributes...))
-		e.metricsProcessed.Inc()
-	}
+func (e *EncodeOtlpMetrics) ProcessCounter(m interface{}, labels map[string]string, value float64) error {
+	counter := m.(metric.Float64Counter)
+	// set attributes using the labels
+	attributes := obtainAttributesFromLabels(labels)
+	counter.Add(e.ctx, value, metric.WithAttributes(attributes...))
+	return nil
+}
 
-	// Process gauges
-	for _, mInfo := range e.gauges {
-		labels, value, key := e.prepareMetric(metricRecord, mInfo.info)
-		if labels == nil {
-			continue
-		}
-		// set attributes using the labels
-		attributes := obtainAttributesFromLabels(labels)
-		mInfo.obs.Set(key, value, attributes)
-		e.metricsProcessed.Inc()
-	}
-	// TBD: Process histograms
+func (e *EncodeOtlpMetrics) ProcessGauge(m interface{}, labels map[string]string, value float64, key string) error {
+	obs := m.(Float64Gauge)
+	// set attributes using the labels
+	attributes := obtainAttributesFromLabels(labels)
+	obs.Set(key, value, attributes)
+	return nil
 }
 
-func (e *EncodeOtlpMetrics) prepareMetric(flow config.GenericMap, info *encode.MetricInfo) (map[string]string, float64, string) {
-	val := e.extractGenericValue(flow, info)
-	if val == nil {
-		return nil, 0, ""
-	}
-	floatVal, err := utils.ConvertToFloat64(val)
-	if err != nil {
-		e.errorsCounter.WithLabelValues("ValueConversionError", info.Name, info.ValueKey).Inc()
-		return nil, 0, ""
-	}
+func (e *EncodeOtlpMetrics) ProcessHist(m interface{}, labels map[string]string, value float64) error {
+	histo := m.(metric.Float64Histogram)
+	// set attributes using the labels
+	attributes := obtainAttributesFromLabels(labels)
+	histo.Record(e.ctx, value, metric.WithAttributes(attributes...))
+	return nil
+}
 
-	entryLabels, key := encode.ExtractLabelsAndKey(flow, &info.MetricsItem)
-	// Update entry for expiry mechanism (the entry itself is its own cleanup function)
-	_, ok := e.mCache.UpdateCacheEntry(key, entryLabels)
-	if !ok {
-		e.metricsDropped.Inc()
-		return nil, 0, ""
+func (e *EncodeOtlpMetrics) ProcessAggHist(m interface{}, labels map[string]string, values []float64) error {
+	histo := m.(metric.Float64Histogram)
+	// set attributes using the labels
+	attributes := obtainAttributesFromLabels(labels)
+	for _, v := range values {
+		histo.Record(e.ctx, v, metric.WithAttributes(attributes...))
 	}
-	return entryLabels, floatVal, key
+	return nil
 }
 
-func (e *EncodeOtlpMetrics) extractGenericValue(flow config.GenericMap, info *encode.MetricInfo) interface{} {
-	for _, pred := range info.FilterPredicates {
-		if !pred(flow) {
-			return nil
-		}
-	}
-	if info.ValueKey == "" {
-		// No value key means it's a records / flows counter (1 flow = 1 increment), so just return 1
-		return 1
-	}
-	val, found := flow[info.ValueKey]
-	if !found {
-		e.errorsCounter.WithLabelValues("RecordKeyMissing", info.Name, info.ValueKey).Inc()
-		return nil
-	}
-	return val
+func (e *EncodeOtlpMetrics) GetChacheEntry(entryLabels map[string]string, m interface{}) interface{} {
+	return entryLabels
 }
 
 func NewEncodeOtlpMetrics(opMetrics *operational.Metrics, params config.StageParam) (encode.Encoder, error) {
@@ -173,8 +114,17 @@ func NewEncodeOtlpMetrics(opMetrics *operational.Metrics, params config.StagePar
 	}
 
 	meterFactory := otel.Meter(flpMeterName)
-	counters := []counterInfo{}
-	gauges := []gaugeInfo{}
+
+	w := &EncodeOtlpMetrics{
+		cfg:   cfg,
+		ctx:   ctx,
+		res:   res,
+		mp:    mp,
+		meter: meterFactory,
+	}
+
+	metricCommon := encode.NewMetricsCommonStruct(opMetrics, 0, params.Name, expiryTime, nil)
+	w.metricCommon = metricCommon
 
 	for _, mCfg := range cfg.Metrics {
 		fullMetricName := cfg.Prefix + mCfg.Name
@@ -189,10 +139,7 @@ func NewEncodeOtlpMetrics(opMetrics *operational.Metrics, params config.StagePar
 				log.Errorf("error during counter creation: %v", err)
 				return nil, err
 			}
-			counters = append(counters, counterInfo{
-				counter: &counter,
-				info:    mInfo,
-			})
+			metricCommon.AddCounter(counter, mInfo)
 		case api.MetricEncodeOperationName("Gauge"):
 			// at implementation time, only asynchronous gauges are supported by otel in golang
 			obs := Float64Gauge{observations: make(map[string]Float64GaugeEntry)}
@@ -204,56 +151,31 @@ func NewEncodeOtlpMetrics(opMetrics *operational.Metrics, params config.StagePar
 				log.Errorf("error during gauge creation: %v", err)
 				return nil, err
 			}
-			gInfo := gaugeInfo{
-				info:  mInfo,
-				obs:   obs,
-				gauge: &gauge,
+			metricCommon.AddGauge(gauge, mInfo)
+		case api.MetricEncodeOperationName("Histogram"):
+			var histo metric.Float64Histogram
+			if len(mCfg.Buckets) == 0 {
+				histo, err = meter.Float64Histogram(fullMetricName)
+			} else {
+				histo, err = meter.Float64Histogram(fullMetricName,
+					metric.WithExplicitBucketBoundaries(mCfg.Buckets...),
+				)
+
+			}
+			if err != nil {
+				log.Errorf("error during histogram creation: %v", err)
+				return nil, err
 			}
-			gauges = append(gauges, gInfo)
-		// TBD: handle histograms
+			metricCommon.AddHist(histo, mInfo)
 		case "default":
 			log.Errorf("invalid metric type = %v, skipping", mCfg.Type)
 			continue
 		}
 	}
 
-	w := &EncodeOtlpMetrics{
-		cfg:              cfg,
-		ctx:              ctx,
-		res:              res,
-		mp:               mp,
-		meter:            meterFactory,
-		counters:         counters,
-		gauges:           gauges,
-		expiryTime:       expiryTime.Duration,
-		mCache:           putils.NewTimedCache(0, nil),
-		exitChan:         putils.ExitChannel(),
-		metricsProcessed: opMetrics.NewCounter(&encode.MetricsProcessed, params.Name),
-		metricsDropped:   opMetrics.NewCounter(&encode.MetricsDropped, params.Name),
-		errorsCounter:    opMetrics.NewCounterVec(&encode.EncodePromErrors),
-	}
-	go w.cleanupExpiredEntriesLoop()
 	return w, nil
 }
 
-// Cleanup - callback function from lru cleanup
-func (e *EncodeOtlpMetrics) Cleanup(cleanupFunc interface{}) {
-	// nothing more to do
-}
-
-func (e *EncodeOtlpMetrics) cleanupExpiredEntriesLoop() {
-	ticker := time.NewTicker(e.expiryTime)
-	for {
-		select {
-		case <-e.exitChan:
-			log.Debugf("exiting cleanupExpiredEntriesLoop because of signal")
-			return
-		case <-ticker.C:
-			e.mCache.CleanupExpiredEntries(e.expiryTime, e.Cleanup)
-		}
-	}
-}
-
 // At present, golang only supports asynchronous gauge, so we have some function here to support this
 
 type Float64GaugeEntry struct {
diff --git a/pkg/pipeline/write/write_ipfix.go b/pkg/pipeline/write/write_ipfix.go
index 724eda37..e76bb9e1 100644
--- a/pkg/pipeline/write/write_ipfix.go
+++ b/pkg/pipeline/write/write_ipfix.go
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 IBM, Inc.
+ * Copyright (C) 2024 IBM, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -43,7 +43,43 @@ type writeIpfix struct {
 // IPv6Type value as defined in IEEE 802: https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
 const IPv6Type = 0x86DD
 
-var ilog = logrus.WithField("component", "write.Ipfix")
+var (
+	ilog       = logrus.WithField("component", "write.Ipfix")
+	IANAFields = []string{
+		"ethernetType",
+		"flowDirection",
+		"sourceMacAddress",
+		"destinationMacAddress",
+		"protocolIdentifier",
+		"sourceTransportPort",
+		"destinationTransportPort",
+		"octetDeltaCount",
+		"flowStartMilliseconds",
+		"flowEndMilliseconds",
+		"packetDeltaCount",
+		"interfaceName",
+	}
+	IPv4IANAFields = append([]string{
+		"sourceIPv4Address",
+		"destinationIPv4Address",
+	}, IANAFields...)
+	IPv6IANAFields = append([]string{
+		"sourceIPv6Address",
+		"destinationIPv6Address",
+		"nextHeaderIPv6",
+	}, IANAFields...)
+	KubeFields = []string{
+		"sourcePodNamespace",
+		"sourcePodName",
+		"destinationPodNamespace",
+		"destinationPodName",
+		"sourceNodeName",
+		"destinationNodeName",
+	}
+	CustomNetworkFields = []string{
+		"timeFlowRttNs",
+	}
+)
 
 func addElementToTemplate(elementName string, value []byte, elements *[]entities.InfoElementWithValue, registryID uint32) error {
 	element, err := registry.GetInfoElement(elementName, registryID)
@@ -60,30 +96,20 @@ func addElementToTemplate(elementName string, value []byte, elements *[]entities
 	return nil
 }
 
-func addKubeContextToTemplate(elements *[]entities.InfoElementWithValue, registryID uint32) error {
-	err := addElementToTemplate("sourcePodNamespace", nil, elements, registryID)
-	if err != nil {
-		return err
-	}
-	err = addElementToTemplate("sourcePodName", nil, elements, registryID)
-	if err != nil {
-		return err
-	}
-	err = addElementToTemplate("destinationPodNamespace", nil, elements, registryID)
-	if err != nil {
-		return err
-	}
-	err = addElementToTemplate("destinationPodName", nil, elements, registryID)
-	if err != nil {
-		return err
-	}
-	err = addElementToTemplate("sourceNodeName", nil, elements, registryID)
-	if err != nil {
-		return err
+func addNetworkEnrichmentToTemplate(elements *[]entities.InfoElementWithValue, registryID uint32) error {
+	for _, field := range CustomNetworkFields {
+		if err := addElementToTemplate(field, nil, elements, registryID); err != nil {
+			return err
+		}
 	}
-	err = addElementToTemplate("destinationNodeName", nil, elements, registryID)
-	if err != nil {
-		return err
+	return nil
+}
+
+func addKubeContextToTemplate(elements *[]entities.InfoElementWithValue, registryID uint32) error {
+	for _, field := range KubeFields {
+		if err := addElementToTemplate(field, nil, elements, registryID); err != nil {
+			return err
+		}
 	}
 	return nil
 }
@@ -94,32 +120,37 @@ func loadCustomRegistry(EnterpriseID uint32) error {
 		ilog.WithError(err).Errorf("Failed to initialize registry")
 		return err
 	}
-	err = registry.PutInfoElement((*entities.NewInfoElement("sourcePodNamespace", 7733, 13, EnterpriseID, 65535)), EnterpriseID)
+	err = registry.PutInfoElement((*entities.NewInfoElement("sourcePodNamespace", 7733, entities.String, EnterpriseID, 65535)), EnterpriseID)
+	if err != nil {
+		ilog.WithError(err).Errorf("Failed to register element")
+		return err
+	}
+	err = registry.PutInfoElement((*entities.NewInfoElement("sourcePodName", 7734, entities.String, EnterpriseID, 65535)), EnterpriseID)
 	if err != nil {
 		ilog.WithError(err).Errorf("Failed to register element")
 		return err
 	}
-	err = registry.PutInfoElement((*entities.NewInfoElement("sourcePodName", 7734, 13, EnterpriseID, 65535)), EnterpriseID)
+	err = registry.PutInfoElement((*entities.NewInfoElement("destinationPodNamespace", 7735, entities.String, EnterpriseID, 65535)), EnterpriseID)
 	if err != nil {
 		ilog.WithError(err).Errorf("Failed to register element")
 		return err
 	}
-	err = registry.PutInfoElement((*entities.NewInfoElement("destinationPodNamespace", 7735, 13, EnterpriseID, 65535)), EnterpriseID)
+	err = registry.PutInfoElement((*entities.NewInfoElement("destinationPodName", 7736, entities.String, EnterpriseID, 65535)), EnterpriseID)
 	if err != nil {
 		ilog.WithError(err).Errorf("Failed to register element")
 		return err
 	}
-	err = registry.PutInfoElement((*entities.NewInfoElement("destinationPodName", 7736, 13, EnterpriseID, 65535)), EnterpriseID)
+	err = registry.PutInfoElement((*entities.NewInfoElement("sourceNodeName", 7737, entities.String, EnterpriseID, 65535)), EnterpriseID)
 	if err != nil {
 		ilog.WithError(err).Errorf("Failed to register element")
 		return err
 	}
-	err = registry.PutInfoElement((*entities.NewInfoElement("sourceNodeName", 7737, 13, EnterpriseID, 65535)), EnterpriseID)
+	err = registry.PutInfoElement((*entities.NewInfoElement("destinationNodeName", 7738, entities.String, EnterpriseID, 65535)), EnterpriseID)
 	if err != nil {
 		ilog.WithError(err).Errorf("Failed to register element")
 		return err
 	}
-	err = registry.PutInfoElement((*entities.NewInfoElement("destinationNodeName", 7738, 13, EnterpriseID, 65535)), EnterpriseID)
+	err = registry.PutInfoElement((*entities.NewInfoElement("timeFlowRttNs", 7740, entities.Unsigned64, EnterpriseID, 8)), EnterpriseID)
 	if err != nil {
 		ilog.WithError(err).Errorf("Failed to register element")
 		return err
@@ -137,67 +168,21 @@ func SendTemplateRecordv4(exporter *ipfixExporter.ExportingProcess, enrichEnterp
 	}
 	elements := make([]entities.InfoElementWithValue, 0)
 
-	err = addElementToTemplate("ethernetType", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("flowDirection", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("sourceMacAddress", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("destinationMacAddress", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("sourceIPv4Address", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("destinationIPv4Address", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("protocolIdentifier", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("sourceTransportPort", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("destinationTransportPort", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("octetDeltaCount", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("flowStartMilliseconds", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("flowEndMilliseconds", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("packetDeltaCount", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("interfaceName", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
+	for _, field := range IPv4IANAFields {
+		err = addElementToTemplate(field, nil, &elements, registry.IANAEnterpriseID)
+		if err != nil {
+			return 0, nil, err
+		}
 	}
 	if enrichEnterpriseID != 0 {
 		err = addKubeContextToTemplate(&elements, enrichEnterpriseID)
 		if err != nil {
 			return 0, nil, err
 		}
+		err = addNetworkEnrichmentToTemplate(&elements, enrichEnterpriseID)
+		if err != nil {
+			return 0, nil, err
+		}
 	}
 	err = templateSet.AddRecord(elements, templateID)
 	if err != nil {
@@ -222,67 +207,21 @@ func SendTemplateRecordv6(exporter *ipfixExporter.ExportingProcess, enrichEnterp
 	}
 	elements := make([]entities.InfoElementWithValue, 0)
 
-	err = addElementToTemplate("ethernetType", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("flowDirection", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("sourceMacAddress", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("destinationMacAddress", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("sourceIPv6Address", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("destinationIPv6Address", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("nextHeaderIPv6", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("sourceTransportPort", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("destinationTransportPort", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("octetDeltaCount", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("flowStartMilliseconds", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("flowEndMilliseconds", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("packetDeltaCount", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
-	}
-	err = addElementToTemplate("interfaceName", nil, &elements, registry.IANAEnterpriseID)
-	if err != nil {
-		return 0, nil, err
+	for _, field := range IPv6IANAFields {
+		err = addElementToTemplate(field, nil, &elements, registry.IANAEnterpriseID)
+		if err != nil {
+			return 0, nil, err
+		}
 	}
 	if enrichEnterpriseID != 0 {
 		err = addKubeContextToTemplate(&elements, enrichEnterpriseID)
 		if err != nil {
 			return 0, nil, err
 		}
+		err = addNetworkEnrichmentToTemplate(&elements, enrichEnterpriseID)
+		if err != nil {
+			return 0, nil, err
+		}
 	}
 
 	err = templateSet.AddRecord(elements, templateID)
@@ -402,6 +341,12 @@ func setStandardIEValue(record config.GenericMap, ieValPtr *entities.InfoElement
 		} else {
 			return fmt.Errorf("unable to find interface in record")
 		}
+	case "timeFlowRttNs":
+		if record["TimeFlowRttNs"] != nil {
+			ieVal.SetUnsigned64Value(uint64(record["TimeFlowRttNs"].(int64)))
+		} else {
+			return fmt.Errorf("unable to find timeflowrtt in record")
+		}
 	}
 	return nil
 }
diff --git a/vendor/github.com/golang/protobuf/jsonpb/decode.go b/vendor/github.com/golang/protobuf/jsonpb/decode.go
deleted file mode 100644
index 6c16c255..00000000
--- a/vendor/github.com/golang/protobuf/jsonpb/decode.go
+++ /dev/null
@@ -1,530 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package jsonpb
-
-import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io"
-	"math"
-	"reflect"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/golang/protobuf/proto"
-	"google.golang.org/protobuf/encoding/protojson"
-	protoV2 "google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/reflect/protoreflect"
-	"google.golang.org/protobuf/reflect/protoregistry"
-)
-
-const wrapJSONUnmarshalV2 = false
-
-// UnmarshalNext unmarshals the next JSON object from d into m.
-func UnmarshalNext(d *json.Decoder, m proto.Message) error {
-	return new(Unmarshaler).UnmarshalNext(d, m)
-}
-
-// Unmarshal unmarshals a JSON object from r into m.
-func Unmarshal(r io.Reader, m proto.Message) error {
-	return new(Unmarshaler).Unmarshal(r, m)
-}
-
-// UnmarshalString unmarshals a JSON object from s into m.
-func UnmarshalString(s string, m proto.Message) error {
-	return new(Unmarshaler).Unmarshal(strings.NewReader(s), m)
-}
-
-// Unmarshaler is a configurable object for converting from a JSON
-// representation to a protocol buffer object.
-type Unmarshaler struct {
-	// AllowUnknownFields specifies whether to allow messages to contain
-	// unknown JSON fields, as opposed to failing to unmarshal.
-	AllowUnknownFields bool
-
-	// AnyResolver is used to resolve the google.protobuf.Any well-known type.
-	// If unset, the global registry is used by default.
-	AnyResolver AnyResolver
-}
-
-// JSONPBUnmarshaler is implemented by protobuf messages that customize the way
-// they are unmarshaled from JSON. Messages that implement this should also
-// implement JSONPBMarshaler so that the custom format can be produced.
-//
-// The JSON unmarshaling must follow the JSON to proto specification:
-//	https://developers.google.com/protocol-buffers/docs/proto3#json
-//
-// Deprecated: Custom types should implement protobuf reflection instead.
-type JSONPBUnmarshaler interface {
-	UnmarshalJSONPB(*Unmarshaler, []byte) error
-}
-
-// Unmarshal unmarshals a JSON object from r into m.
-func (u *Unmarshaler) Unmarshal(r io.Reader, m proto.Message) error {
-	return u.UnmarshalNext(json.NewDecoder(r), m)
-}
-
-// UnmarshalNext unmarshals the next JSON object from d into m.
-func (u *Unmarshaler) UnmarshalNext(d *json.Decoder, m proto.Message) error {
-	if m == nil {
-		return errors.New("invalid nil message")
-	}
-
-	// Parse the next JSON object from the stream.
-	raw := json.RawMessage{}
-	if err := d.Decode(&raw); err != nil {
-		return err
-	}
-
-	// Check for custom unmarshalers first since they may not properly
-	// implement protobuf reflection that the logic below relies on.
-	if jsu, ok := m.(JSONPBUnmarshaler); ok {
-		return jsu.UnmarshalJSONPB(u, raw)
-	}
-
-	mr := proto.MessageReflect(m)
-
-	// NOTE: For historical reasons, a top-level null is treated as a noop.
-	// This is incorrect, but kept for compatibility.
-	if string(raw) == "null" && mr.Descriptor().FullName() != "google.protobuf.Value" {
-		return nil
-	}
-
-	if wrapJSONUnmarshalV2 {
-		// NOTE: If input message is non-empty, we need to preserve merge semantics
-		// of the old jsonpb implementation. These semantics are not supported by
-		// the protobuf JSON specification.
-		isEmpty := true
-		mr.Range(func(protoreflect.FieldDescriptor, protoreflect.Value) bool {
-			isEmpty = false // at least one iteration implies non-empty
-			return false
-		})
-		if !isEmpty {
-			// Perform unmarshaling into a newly allocated, empty message.
-			mr = mr.New()
-
-			// Use a defer to copy all unmarshaled fields into the original message.
-			dst := proto.MessageReflect(m)
-			defer mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
-				dst.Set(fd, v)
-				return true
-			})
-		}
-
-		// Unmarshal using the v2 JSON unmarshaler.
-		opts := protojson.UnmarshalOptions{
-			DiscardUnknown: u.AllowUnknownFields,
-		}
-		if u.AnyResolver != nil {
-			opts.Resolver = anyResolver{u.AnyResolver}
-		}
-		return opts.Unmarshal(raw, mr.Interface())
-	} else {
-		if err := u.unmarshalMessage(mr, raw); err != nil {
-			return err
-		}
-		return protoV2.CheckInitialized(mr.Interface())
-	}
-}
-
-func (u *Unmarshaler) unmarshalMessage(m protoreflect.Message, in []byte) error {
-	md := m.Descriptor()
-	fds := md.Fields()
-
-	if jsu, ok := proto.MessageV1(m.Interface()).(JSONPBUnmarshaler); ok {
-		return jsu.UnmarshalJSONPB(u, in)
-	}
-
-	if string(in) == "null" && md.FullName() != "google.protobuf.Value" {
-		return nil
-	}
-
-	switch wellKnownType(md.FullName()) {
-	case "Any":
-		var jsonObject map[string]json.RawMessage
-		if err := json.Unmarshal(in, &jsonObject); err != nil {
-			return err
-		}
-
-		rawTypeURL, ok := jsonObject["@type"]
-		if !ok {
-			return errors.New("Any JSON doesn't have '@type'")
-		}
-		typeURL, err := unquoteString(string(rawTypeURL))
-		if err != nil {
-			return fmt.Errorf("can't unmarshal Any's '@type': %q", rawTypeURL)
-		}
-		m.Set(fds.ByNumber(1), protoreflect.ValueOfString(typeURL))
-
-		var m2 protoreflect.Message
-		if u.AnyResolver != nil {
-			mi, err := u.AnyResolver.Resolve(typeURL)
-			if err != nil {
-				return err
-			}
-			m2 = proto.MessageReflect(mi)
-		} else {
-			mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL)
-			if err != nil {
-				if err == protoregistry.NotFound {
-					return fmt.Errorf("could not resolve Any message type: %v", typeURL)
-				}
-				return err
-			}
-			m2 = mt.New()
-		}
-
-		if wellKnownType(m2.Descriptor().FullName()) != "" {
-			rawValue, ok := jsonObject["value"]
-			if !ok {
-				return errors.New("Any JSON doesn't have 'value'")
-			}
-			if err := u.unmarshalMessage(m2, rawValue); err != nil {
-				return fmt.Errorf("can't unmarshal Any nested proto %v: %v", typeURL, err)
-			}
-		} else {
-			delete(jsonObject, "@type")
-			rawJSON, err := json.Marshal(jsonObject)
-			if err != nil {
-				return fmt.Errorf("can't generate JSON for Any's nested proto to be unmarshaled: %v", err)
-			}
-			if err = u.unmarshalMessage(m2, rawJSON); err != nil {
-				return fmt.Errorf("can't unmarshal Any nested proto %v: %v", typeURL, err)
-			}
-		}
-
-		rawWire, err := protoV2.Marshal(m2.Interface())
-		if err != nil {
-			return fmt.Errorf("can't marshal proto %v into Any.Value: %v", typeURL, err)
-		}
-		m.Set(fds.ByNumber(2), protoreflect.ValueOfBytes(rawWire))
-		return nil
-	case "BoolValue", "BytesValue", "StringValue",
-		"Int32Value", "UInt32Value", "FloatValue",
-		"Int64Value", "UInt64Value", "DoubleValue":
-		fd := fds.ByNumber(1)
-		v, err := u.unmarshalValue(m.NewField(fd), in, fd)
-		if err != nil {
-			return err
-		}
-		m.Set(fd, v)
-		return nil
-	case "Duration":
-		v, err := unquoteString(string(in))
-		if err != nil {
-			return err
-		}
-		d, err := time.ParseDuration(v)
-		if err != nil {
-			return fmt.Errorf("bad Duration: %v", err)
-		}
-
-		sec := d.Nanoseconds() / 1e9
-		nsec := d.Nanoseconds() % 1e9
-		m.Set(fds.ByNumber(1), protoreflect.ValueOfInt64(int64(sec)))
-		m.Set(fds.ByNumber(2), protoreflect.ValueOfInt32(int32(nsec)))
-		return nil
-	case "Timestamp":
-		v, err := unquoteString(string(in))
-		if err != nil {
-			return err
-		}
-		t, err := time.Parse(time.RFC3339Nano, v)
-		if err != nil {
-			return fmt.Errorf("bad Timestamp: %v", err)
-		}
-
-		sec := t.Unix()
-		nsec := t.Nanosecond()
-		m.Set(fds.ByNumber(1), protoreflect.ValueOfInt64(int64(sec)))
-		m.Set(fds.ByNumber(2), protoreflect.ValueOfInt32(int32(nsec)))
-		return nil
-	case "Value":
-		switch {
-		case string(in) == "null":
-			m.Set(fds.ByNumber(1), protoreflect.ValueOfEnum(0))
-		case string(in) == "true":
-			m.Set(fds.ByNumber(4), protoreflect.ValueOfBool(true))
-		case string(in) == "false":
-			m.Set(fds.ByNumber(4), protoreflect.ValueOfBool(false))
-		case hasPrefixAndSuffix('"', in, '"'):
-			s, err := unquoteString(string(in))
-			if err != nil {
-				return fmt.Errorf("unrecognized type for Value %q", in)
-			}
-			m.Set(fds.ByNumber(3), protoreflect.ValueOfString(s))
-		case hasPrefixAndSuffix('[', in, ']'):
-			v := m.Mutable(fds.ByNumber(6))
-			return u.unmarshalMessage(v.Message(), in)
-		case hasPrefixAndSuffix('{', in, '}'):
-			v := m.Mutable(fds.ByNumber(5))
-			return u.unmarshalMessage(v.Message(), in)
-		default:
-			f, err := strconv.ParseFloat(string(in), 0)
-			if err != nil {
-				return fmt.Errorf("unrecognized type for Value %q", in)
-			}
-			m.Set(fds.ByNumber(2), protoreflect.ValueOfFloat64(f))
-		}
-		return nil
-	case "ListValue":
-		var jsonArray []json.RawMessage
-		if err := json.Unmarshal(in, &jsonArray); err != nil {
-			return fmt.Errorf("bad ListValue: %v", err)
-		}
-
-		lv := m.Mutable(fds.ByNumber(1)).List()
-		for _, raw := range jsonArray {
-			ve := lv.NewElement()
-			if err := u.unmarshalMessage(ve.Message(), raw); err != nil {
-				return err
-			}
-			lv.Append(ve)
-		}
-		return nil
-	case "Struct":
-		var jsonObject map[string]json.RawMessage
-		if err := json.Unmarshal(in, &jsonObject); err != nil {
-			return fmt.Errorf("bad StructValue: %v", err)
-		}
-
-		mv := m.Mutable(fds.ByNumber(1)).Map()
-		for key, raw := range jsonObject {
-			kv := protoreflect.ValueOf(key).MapKey()
-			vv := mv.NewValue()
-			if err := u.unmarshalMessage(vv.Message(), raw); err != nil {
-				return fmt.Errorf("bad value in StructValue for key %q: %v", key, err)
-			}
-			mv.Set(kv, vv)
-		}
-		return nil
-	}
-
-	var jsonObject map[string]json.RawMessage
-	if err := json.Unmarshal(in, &jsonObject); err != nil {
-		return err
-	}
-
-	// Handle known fields.
-	for i := 0; i < fds.Len(); i++ {
-		fd := fds.Get(i)
-		if fd.IsWeak() && fd.Message().IsPlaceholder() {
-			continue //  weak reference is not linked in
-		}
-
-		// Search for any raw JSON value associated with this field.
-		var raw json.RawMessage
-		name := string(fd.Name())
-		if fd.Kind() == protoreflect.GroupKind {
-			name = string(fd.Message().Name())
-		}
-		if v, ok := jsonObject[name]; ok {
-			delete(jsonObject, name)
-			raw = v
-		}
-		name = string(fd.JSONName())
-		if v, ok := jsonObject[name]; ok {
-			delete(jsonObject, name)
-			raw = v
-		}
-
-		field := m.NewField(fd)
-		// Unmarshal the field value.
-		if raw == nil || (string(raw) == "null" && !isSingularWellKnownValue(fd) && !isSingularJSONPBUnmarshaler(field, fd)) {
-			continue
-		}
-		v, err := u.unmarshalValue(field, raw, fd)
-		if err != nil {
-			return err
-		}
-		m.Set(fd, v)
-	}
-
-	// Handle extension fields.
-	for name, raw := range jsonObject {
-		if !strings.HasPrefix(name, "[") || !strings.HasSuffix(name, "]") {
-			continue
-		}
-
-		// Resolve the extension field by name.
-		xname := protoreflect.FullName(name[len("[") : len(name)-len("]")])
-		xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname)
-		if xt == nil && isMessageSet(md) {
-			xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension"))
-		}
-		if xt == nil {
-			continue
-		}
-		delete(jsonObject, name)
-		fd := xt.TypeDescriptor()
-		if fd.ContainingMessage().FullName() != m.Descriptor().FullName() {
-			return fmt.Errorf("extension field %q does not extend message %q", xname, m.Descriptor().FullName())
-		}
-
-		field := m.NewField(fd)
-		// Unmarshal the field value.
-		if raw == nil || (string(raw) == "null" && !isSingularWellKnownValue(fd) && !isSingularJSONPBUnmarshaler(field, fd)) {
-			continue
-		}
-		v, err := u.unmarshalValue(field, raw, fd)
-		if err != nil {
-			return err
-		}
-		m.Set(fd, v)
-	}
-
-	if !u.AllowUnknownFields && len(jsonObject) > 0 {
-		for name := range jsonObject {
-			return fmt.Errorf("unknown field %q in %v", name, md.FullName())
-		}
-	}
-	return nil
-}
-
-func isSingularWellKnownValue(fd protoreflect.FieldDescriptor) bool {
-	if fd.Cardinality() == protoreflect.Repeated {
-		return false
-	}
-	if md := fd.Message(); md != nil {
-		return md.FullName() == "google.protobuf.Value"
-	}
-	if ed := fd.Enum(); ed != nil {
-		return ed.FullName() == "google.protobuf.NullValue"
-	}
-	return false
-}
-
-func isSingularJSONPBUnmarshaler(v protoreflect.Value, fd protoreflect.FieldDescriptor) bool {
-	if fd.Message() != nil && fd.Cardinality() != protoreflect.Repeated {
-		_, ok := proto.MessageV1(v.Interface()).(JSONPBUnmarshaler)
-		return ok
-	}
-	return false
-}
-
-func (u *Unmarshaler) unmarshalValue(v protoreflect.Value, in []byte, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
-	switch {
-	case fd.IsList():
-		var jsonArray []json.RawMessage
-		if err := json.Unmarshal(in, &jsonArray); err != nil {
-			return v, err
-		}
-		lv := v.List()
-		for _, raw := range jsonArray {
-			ve, err := u.unmarshalSingularValue(lv.NewElement(), raw, fd)
-			if err != nil {
-				return v, err
-			}
-			lv.Append(ve)
-		}
-		return v, nil
-	case fd.IsMap():
-		var jsonObject map[string]json.RawMessage
-		if err := json.Unmarshal(in, &jsonObject); err != nil {
-			return v, err
-		}
-		kfd := fd.MapKey()
-		vfd := fd.MapValue()
-		mv := v.Map()
-		for key, raw := range jsonObject {
-			var kv protoreflect.MapKey
-			if kfd.Kind() == protoreflect.StringKind {
-				kv = protoreflect.ValueOf(key).MapKey()
-			} else {
-				v, err := u.unmarshalSingularValue(kfd.Default(), []byte(key), kfd)
-				if err != nil {
-					return v, err
-				}
-				kv = v.MapKey()
-			}
-
-			vv, err := u.unmarshalSingularValue(mv.NewValue(), raw, vfd)
-			if err != nil {
-				return v, err
-			}
-			mv.Set(kv, vv)
-		}
-		return v, nil
-	default:
-		return u.unmarshalSingularValue(v, in, fd)
-	}
-}
-
-var nonFinite = map[string]float64{
-	`"NaN"`:       math.NaN(),
-	`"Infinity"`:  math.Inf(+1),
-	`"-Infinity"`: math.Inf(-1),
-}
-
-func (u *Unmarshaler) unmarshalSingularValue(v protoreflect.Value, in []byte, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
-	switch fd.Kind() {
-	case protoreflect.BoolKind:
-		return unmarshalValue(in, new(bool))
-	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
-		return unmarshalValue(trimQuote(in), new(int32))
-	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
-		return unmarshalValue(trimQuote(in), new(int64))
-	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
-		return unmarshalValue(trimQuote(in), new(uint32))
-	case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
-		return unmarshalValue(trimQuote(in), new(uint64))
-	case protoreflect.FloatKind:
-		if f, ok := nonFinite[string(in)]; ok {
-			return protoreflect.ValueOfFloat32(float32(f)), nil
-		}
-		return unmarshalValue(trimQuote(in), new(float32))
-	case protoreflect.DoubleKind:
-		if f, ok := nonFinite[string(in)]; ok {
-			return protoreflect.ValueOfFloat64(float64(f)), nil
-		}
-		return unmarshalValue(trimQuote(in), new(float64))
-	case protoreflect.StringKind:
-		return unmarshalValue(in, new(string))
-	case protoreflect.BytesKind:
-		return unmarshalValue(in, new([]byte))
-	case protoreflect.EnumKind:
-		if hasPrefixAndSuffix('"', in, '"') {
-			vd := fd.Enum().Values().ByName(protoreflect.Name(trimQuote(in)))
-			if vd == nil {
-				return v, fmt.Errorf("unknown value %q for enum %s", in, fd.Enum().FullName())
-			}
-			return protoreflect.ValueOfEnum(vd.Number()), nil
-		}
-		return unmarshalValue(in, new(protoreflect.EnumNumber))
-	case protoreflect.MessageKind, protoreflect.GroupKind:
-		err := u.unmarshalMessage(v.Message(), in)
-		return v, err
-	default:
-		panic(fmt.Sprintf("invalid kind %v", fd.Kind()))
-	}
-}
-
-func unmarshalValue(in []byte, v interface{}) (protoreflect.Value, error) {
-	err := json.Unmarshal(in, v)
-	return protoreflect.ValueOf(reflect.ValueOf(v).Elem().Interface()), err
-}
-
-func unquoteString(in string) (out string, err error) {
-	err = json.Unmarshal([]byte(in), &out)
-	return out, err
-}
-
-func hasPrefixAndSuffix(prefix byte, in []byte, suffix byte) bool {
-	if len(in) >= 2 && in[0] == prefix && in[len(in)-1] == suffix {
-		return true
-	}
-	return false
-}
-
-// trimQuote is like unquoteString but simply strips surrounding quotes.
-// This is incorrect, but is behavior done by the legacy implementation.
-func trimQuote(in []byte) []byte {
-	if len(in) >= 2 && in[0] == '"' && in[len(in)-1] == '"' {
-		in = in[1 : len(in)-1]
-	}
-	return in
-}
diff --git a/vendor/github.com/golang/protobuf/jsonpb/encode.go b/vendor/github.com/golang/protobuf/jsonpb/encode.go
deleted file mode 100644
index 685c80a6..00000000
--- a/vendor/github.com/golang/protobuf/jsonpb/encode.go
+++ /dev/null
@@ -1,559 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package jsonpb
-
-import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io"
-	"math"
-	"reflect"
-	"sort"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/golang/protobuf/proto"
-	"google.golang.org/protobuf/encoding/protojson"
-	protoV2 "google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/reflect/protoreflect"
-	"google.golang.org/protobuf/reflect/protoregistry"
-)
-
-const wrapJSONMarshalV2 = false
-
-// Marshaler is a configurable object for marshaling protocol buffer messages
-// to the specified JSON representation.
-type Marshaler struct {
-	// OrigName specifies whether to use the original protobuf name for fields.
-	OrigName bool
-
-	// EnumsAsInts specifies whether to render enum values as integers,
-	// as opposed to string values.
-	EnumsAsInts bool
-
-	// EmitDefaults specifies whether to render fields with zero values.
-	EmitDefaults bool
-
-	// Indent controls whether the output is compact or not.
-	// If empty, the output is compact JSON. Otherwise, every JSON object
-	// entry and JSON array value will be on its own line.
-	// Each line will be preceded by repeated copies of Indent, where the
-	// number of copies is the current indentation depth.
-	Indent string
-
-	// AnyResolver is used to resolve the google.protobuf.Any well-known type.
-	// If unset, the global registry is used by default.
-	AnyResolver AnyResolver
-}
-
-// JSONPBMarshaler is implemented by protobuf messages that customize the
-// way they are marshaled to JSON. Messages that implement this should also
-// implement JSONPBUnmarshaler so that the custom format can be parsed.
-//
-// The JSON marshaling must follow the proto to JSON specification:
-//	https://developers.google.com/protocol-buffers/docs/proto3#json
-//
-// Deprecated: Custom types should implement protobuf reflection instead.
-type JSONPBMarshaler interface {
-	MarshalJSONPB(*Marshaler) ([]byte, error)
-}
-
-// Marshal serializes a protobuf message as JSON into w.
-func (jm *Marshaler) Marshal(w io.Writer, m proto.Message) error {
-	b, err := jm.marshal(m)
-	if len(b) > 0 {
-		if _, err := w.Write(b); err != nil {
-			return err
-		}
-	}
-	return err
-}
-
-// MarshalToString serializes a protobuf message as JSON in string form.
-func (jm *Marshaler) MarshalToString(m proto.Message) (string, error) {
-	b, err := jm.marshal(m)
-	if err != nil {
-		return "", err
-	}
-	return string(b), nil
-}
-
-func (jm *Marshaler) marshal(m proto.Message) ([]byte, error) {
-	v := reflect.ValueOf(m)
-	if m == nil || (v.Kind() == reflect.Ptr && v.IsNil()) {
-		return nil, errors.New("Marshal called with nil")
-	}
-
-	// Check for custom marshalers first since they may not properly
-	// implement protobuf reflection that the logic below relies on.
-	if jsm, ok := m.(JSONPBMarshaler); ok {
-		return jsm.MarshalJSONPB(jm)
-	}
-
-	if wrapJSONMarshalV2 {
-		opts := protojson.MarshalOptions{
-			UseProtoNames:   jm.OrigName,
-			UseEnumNumbers:  jm.EnumsAsInts,
-			EmitUnpopulated: jm.EmitDefaults,
-			Indent:          jm.Indent,
-		}
-		if jm.AnyResolver != nil {
-			opts.Resolver = anyResolver{jm.AnyResolver}
-		}
-		return opts.Marshal(proto.MessageReflect(m).Interface())
-	} else {
-		// Check for unpopulated required fields first.
-		m2 := proto.MessageReflect(m)
-		if err := protoV2.CheckInitialized(m2.Interface()); err != nil {
-			return nil, err
-		}
-
-		w := jsonWriter{Marshaler: jm}
-		err := w.marshalMessage(m2, "", "")
-		return w.buf, err
-	}
-}
-
-type jsonWriter struct {
-	*Marshaler
-	buf []byte
-}
-
-func (w *jsonWriter) write(s string) {
-	w.buf = append(w.buf, s...)
-}
-
-func (w *jsonWriter) marshalMessage(m protoreflect.Message, indent, typeURL string) error {
-	if jsm, ok := proto.MessageV1(m.Interface()).(JSONPBMarshaler); ok {
-		b, err := jsm.MarshalJSONPB(w.Marshaler)
-		if err != nil {
-			return err
-		}
-		if typeURL != "" {
-			// we are marshaling this object to an Any type
-			var js map[string]*json.RawMessage
-			if err = json.Unmarshal(b, &js); err != nil {
-				return fmt.Errorf("type %T produced invalid JSON: %v", m.Interface(), err)
-			}
-			turl, err := json.Marshal(typeURL)
-			if err != nil {
-				return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err)
-			}
-			js["@type"] = (*json.RawMessage)(&turl)
-			if b, err = json.Marshal(js); err != nil {
-				return err
-			}
-		}
-		w.write(string(b))
-		return nil
-	}
-
-	md := m.Descriptor()
-	fds := md.Fields()
-
-	// Handle well-known types.
-	const secondInNanos = int64(time.Second / time.Nanosecond)
-	switch wellKnownType(md.FullName()) {
-	case "Any":
-		return w.marshalAny(m, indent)
-	case "BoolValue", "BytesValue", "StringValue",
-		"Int32Value", "UInt32Value", "FloatValue",
-		"Int64Value", "UInt64Value", "DoubleValue":
-		fd := fds.ByNumber(1)
-		return w.marshalValue(fd, m.Get(fd), indent)
-	case "Duration":
-		const maxSecondsInDuration = 315576000000
-		// "Generated output always contains 0, 3, 6, or 9 fractional digits,
-		//  depending on required precision."
-		s := m.Get(fds.ByNumber(1)).Int()
-		ns := m.Get(fds.ByNumber(2)).Int()
-		if s < -maxSecondsInDuration || s > maxSecondsInDuration {
-			return fmt.Errorf("seconds out of range %v", s)
-		}
-		if ns <= -secondInNanos || ns >= secondInNanos {
-			return fmt.Errorf("ns out of range (%v, %v)", -secondInNanos, secondInNanos)
-		}
-		if (s > 0 && ns < 0) || (s < 0 && ns > 0) {
-			return errors.New("signs of seconds and nanos do not match")
-		}
-		var sign string
-		if s < 0 || ns < 0 {
-			sign, s, ns = "-", -1*s, -1*ns
-		}
-		x := fmt.Sprintf("%s%d.%09d", sign, s, ns)
-		x = strings.TrimSuffix(x, "000")
-		x = strings.TrimSuffix(x, "000")
-		x = strings.TrimSuffix(x, ".000")
-		w.write(fmt.Sprintf(`"%vs"`, x))
-		return nil
-	case "Timestamp":
-		// "RFC 3339, where generated output will always be Z-normalized
-		//  and uses 0, 3, 6 or 9 fractional digits."
-		s := m.Get(fds.ByNumber(1)).Int()
-		ns := m.Get(fds.ByNumber(2)).Int()
-		if ns < 0 || ns >= secondInNanos {
-			return fmt.Errorf("ns out of range [0, %v)", secondInNanos)
-		}
-		t := time.Unix(s, ns).UTC()
-		// time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits).
-		x := t.Format("2006-01-02T15:04:05.000000000")
-		x = strings.TrimSuffix(x, "000")
-		x = strings.TrimSuffix(x, "000")
-		x = strings.TrimSuffix(x, ".000")
-		w.write(fmt.Sprintf(`"%vZ"`, x))
-		return nil
-	case "Value":
-		// JSON value; which is a null, number, string, bool, object, or array.
-		od := md.Oneofs().Get(0)
-		fd := m.WhichOneof(od)
-		if fd == nil {
-			return errors.New("nil Value")
-		}
-		return w.marshalValue(fd, m.Get(fd), indent)
-	case "Struct", "ListValue":
-		// JSON object or array.
-		fd := fds.ByNumber(1)
-		return w.marshalValue(fd, m.Get(fd), indent)
-	}
-
-	w.write("{")
-	if w.Indent != "" {
-		w.write("\n")
-	}
-
-	firstField := true
-	if typeURL != "" {
-		if err := w.marshalTypeURL(indent, typeURL); err != nil {
-			return err
-		}
-		firstField = false
-	}
-
-	for i := 0; i < fds.Len(); {
-		fd := fds.Get(i)
-		if od := fd.ContainingOneof(); od != nil {
-			fd = m.WhichOneof(od)
-			i += od.Fields().Len()
-			if fd == nil {
-				continue
-			}
-		} else {
-			i++
-		}
-
-		v := m.Get(fd)
-
-		if !m.Has(fd) {
-			if !w.EmitDefaults || fd.ContainingOneof() != nil {
-				continue
-			}
-			if fd.Cardinality() != protoreflect.Repeated && (fd.Message() != nil || fd.Syntax() == protoreflect.Proto2) {
-				v = protoreflect.Value{} // use "null" for singular messages or proto2 scalars
-			}
-		}
-
-		if !firstField {
-			w.writeComma()
-		}
-		if err := w.marshalField(fd, v, indent); err != nil {
-			return err
-		}
-		firstField = false
-	}
-
-	// Handle proto2 extensions.
-	if md.ExtensionRanges().Len() > 0 {
-		// Collect a sorted list of all extension descriptor and values.
-		type ext struct {
-			desc protoreflect.FieldDescriptor
-			val  protoreflect.Value
-		}
-		var exts []ext
-		m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
-			if fd.IsExtension() {
-				exts = append(exts, ext{fd, v})
-			}
-			return true
-		})
-		sort.Slice(exts, func(i, j int) bool {
-			return exts[i].desc.Number() < exts[j].desc.Number()
-		})
-
-		for _, ext := range exts {
-			if !firstField {
-				w.writeComma()
-			}
-			if err := w.marshalField(ext.desc, ext.val, indent); err != nil {
-				return err
-			}
-			firstField = false
-		}
-	}
-
-	if w.Indent != "" {
-		w.write("\n")
-		w.write(indent)
-	}
-	w.write("}")
-	return nil
-}
-
-func (w *jsonWriter) writeComma() {
-	if w.Indent != "" {
-		w.write(",\n")
-	} else {
-		w.write(",")
-	}
-}
-
-func (w *jsonWriter) marshalAny(m protoreflect.Message, indent string) error {
-	// "If the Any contains a value that has a special JSON mapping,
-	//  it will be converted as follows: {"@type": xxx, "value": yyy}.
-	//  Otherwise, the value will be converted into a JSON object,
-	//  and the "@type" field will be inserted to indicate the actual data type."
-	md := m.Descriptor()
-	typeURL := m.Get(md.Fields().ByNumber(1)).String()
-	rawVal := m.Get(md.Fields().ByNumber(2)).Bytes()
-
-	var m2 protoreflect.Message
-	if w.AnyResolver != nil {
-		mi, err := w.AnyResolver.Resolve(typeURL)
-		if err != nil {
-			return err
-		}
-		m2 = proto.MessageReflect(mi)
-	} else {
-		mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL)
-		if err != nil {
-			return err
-		}
-		m2 = mt.New()
-	}
-
-	if err := protoV2.Unmarshal(rawVal, m2.Interface()); err != nil {
-		return err
-	}
-
-	if wellKnownType(m2.Descriptor().FullName()) == "" {
-		return w.marshalMessage(m2, indent, typeURL)
-	}
-
-	w.write("{")
-	if w.Indent != "" {
-		w.write("\n")
-	}
-	if err := w.marshalTypeURL(indent, typeURL); err != nil {
-		return err
-	}
-	w.writeComma()
-	if w.Indent != "" {
-		w.write(indent)
-		w.write(w.Indent)
-		w.write(`"value": `)
-	} else {
-		w.write(`"value":`)
-	}
-	if err := w.marshalMessage(m2, indent+w.Indent, ""); err != nil {
-		return err
-	}
-	if w.Indent != "" {
-		w.write("\n")
-		w.write(indent)
-	}
-	w.write("}")
-	return nil
-}
-
-func (w *jsonWriter) marshalTypeURL(indent, typeURL string) error {
-	if w.Indent != "" {
-		w.write(indent)
-		w.write(w.Indent)
-	}
-	w.write(`"@type":`)
-	if w.Indent != "" {
-		w.write(" ")
-	}
-	b, err := json.Marshal(typeURL)
-	if err != nil {
-		return err
-	}
-	w.write(string(b))
-	return nil
-}
-
-// marshalField writes field description and value to the Writer.
-func (w *jsonWriter) marshalField(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
-	if w.Indent != "" {
-		w.write(indent)
-		w.write(w.Indent)
-	}
-	w.write(`"`)
-	switch {
-	case fd.IsExtension():
-		// For message set, use the fname of the message as the extension name.
-		name := string(fd.FullName())
-		if isMessageSet(fd.ContainingMessage()) {
-			name = strings.TrimSuffix(name, ".message_set_extension")
-		}
-
-		w.write("[" + name + "]")
-	case w.OrigName:
-		name := string(fd.Name())
-		if fd.Kind() == protoreflect.GroupKind {
-			name = string(fd.Message().Name())
-		}
-		w.write(name)
-	default:
-		w.write(string(fd.JSONName()))
-	}
-	w.write(`":`)
-	if w.Indent != "" {
-		w.write(" ")
-	}
-	return w.marshalValue(fd, v, indent)
-}
-
-func (w *jsonWriter) marshalValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
-	switch {
-	case fd.IsList():
-		w.write("[")
-		comma := ""
-		lv := v.List()
-		for i := 0; i < lv.Len(); i++ {
-			w.write(comma)
-			if w.Indent != "" {
-				w.write("\n")
-				w.write(indent)
-				w.write(w.Indent)
-				w.write(w.Indent)
-			}
-			if err := w.marshalSingularValue(fd, lv.Get(i), indent+w.Indent); err != nil {
-				return err
-			}
-			comma = ","
-		}
-		if w.Indent != "" {
-			w.write("\n")
-			w.write(indent)
-			w.write(w.Indent)
-		}
-		w.write("]")
-		return nil
-	case fd.IsMap():
-		kfd := fd.MapKey()
-		vfd := fd.MapValue()
-		mv := v.Map()
-
-		// Collect a sorted list of all map keys and values.
-		type entry struct{ key, val protoreflect.Value }
-		var entries []entry
-		mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
-			entries = append(entries, entry{k.Value(), v})
-			return true
-		})
-		sort.Slice(entries, func(i, j int) bool {
-			switch kfd.Kind() {
-			case protoreflect.BoolKind:
-				return !entries[i].key.Bool() && entries[j].key.Bool()
-			case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
-				return entries[i].key.Int() < entries[j].key.Int()
-			case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
-				return entries[i].key.Uint() < entries[j].key.Uint()
-			case protoreflect.StringKind:
-				return entries[i].key.String() < entries[j].key.String()
-			default:
-				panic("invalid kind")
-			}
-		})
-
-		w.write(`{`)
-		comma := ""
-		for _, entry := range entries {
-			w.write(comma)
-			if w.Indent != "" {
-				w.write("\n")
-				w.write(indent)
-				w.write(w.Indent)
-				w.write(w.Indent)
-			}
-
-			s := fmt.Sprint(entry.key.Interface())
-			b, err := json.Marshal(s)
-			if err != nil {
-				return err
-			}
-			w.write(string(b))
-
-			w.write(`:`)
-			if w.Indent != "" {
-				w.write(` `)
-			}
-
-			if err := w.marshalSingularValue(vfd, entry.val, indent+w.Indent); err != nil {
-				return err
-			}
-			comma = ","
-		}
-		if w.Indent != "" {
-			w.write("\n")
-			w.write(indent)
-			w.write(w.Indent)
-		}
-		w.write(`}`)
-		return nil
-	default:
-		return w.marshalSingularValue(fd, v, indent)
-	}
-}
-
-func (w *jsonWriter) marshalSingularValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
-	switch {
-	case !v.IsValid():
-		w.write("null")
-		return nil
-	case fd.Message() != nil:
-		return w.marshalMessage(v.Message(), indent+w.Indent, "")
-	case fd.Enum() != nil:
-		if fd.Enum().FullName() == "google.protobuf.NullValue" {
-			w.write("null")
-			return nil
-		}
-
-		vd := fd.Enum().Values().ByNumber(v.Enum())
-		if vd == nil || w.EnumsAsInts {
-			w.write(strconv.Itoa(int(v.Enum())))
-		} else {
-			w.write(`"` + string(vd.Name()) + `"`)
-		}
-		return nil
-	default:
-		switch v.Interface().(type) {
-		case float32, float64:
-			switch {
-			case math.IsInf(v.Float(), +1):
-				w.write(`"Infinity"`)
-				return nil
-			case math.IsInf(v.Float(), -1):
-				w.write(`"-Infinity"`)
-				return nil
-			case math.IsNaN(v.Float()):
-				w.write(`"NaN"`)
-				return nil
-			}
-		case int64, uint64:
-			w.write(fmt.Sprintf(`"%d"`, v.Interface()))
-			return nil
-		}
-
-		b, err := json.Marshal(v.Interface())
-		if err != nil {
-			return err
-		}
-		w.write(string(b))
-		return nil
-	}
-}
diff --git a/vendor/github.com/golang/protobuf/jsonpb/json.go b/vendor/github.com/golang/protobuf/jsonpb/json.go
deleted file mode 100644
index 480e2448..00000000
--- a/vendor/github.com/golang/protobuf/jsonpb/json.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package jsonpb provides functionality to marshal and unmarshal between a
-// protocol buffer message and JSON. It follows the specification at
-// https://developers.google.com/protocol-buffers/docs/proto3#json.
-//
-// Do not rely on the default behavior of the standard encoding/json package
-// when called on generated message types as it does not operate correctly.
-//
-// Deprecated: Use the "google.golang.org/protobuf/encoding/protojson"
-// package instead.
-package jsonpb
-
-import (
-	"github.com/golang/protobuf/proto"
-	"google.golang.org/protobuf/reflect/protoreflect"
-	"google.golang.org/protobuf/reflect/protoregistry"
-	"google.golang.org/protobuf/runtime/protoimpl"
-)
-
-// AnyResolver takes a type URL, present in an Any message,
-// and resolves it into an instance of the associated message.
-type AnyResolver interface {
-	Resolve(typeURL string) (proto.Message, error)
-}
-
-type anyResolver struct{ AnyResolver }
-
-func (r anyResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) {
-	return r.FindMessageByURL(string(message))
-}
-
-func (r anyResolver) FindMessageByURL(url string) (protoreflect.MessageType, error) {
-	m, err := r.Resolve(url)
-	if err != nil {
-		return nil, err
-	}
-	return protoimpl.X.MessageTypeOf(m), nil
-}
-
-func (r anyResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
-	return protoregistry.GlobalTypes.FindExtensionByName(field)
-}
-
-func (r anyResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
-	return protoregistry.GlobalTypes.FindExtensionByNumber(message, field)
-}
-
-func wellKnownType(s protoreflect.FullName) string {
-	if s.Parent() == "google.protobuf" {
-		switch s.Name() {
-		case "Empty", "Any",
-			"BoolValue", "BytesValue", "StringValue",
-			"Int32Value", "UInt32Value", "FloatValue",
-			"Int64Value", "UInt64Value", "DoubleValue",
-			"Duration", "Timestamp",
-			"NullValue", "Struct", "Value", "ListValue":
-			return string(s.Name())
-		}
-	}
-	return ""
-}
-
-func isMessageSet(md protoreflect.MessageDescriptor) bool {
-	ms, ok := md.(interface{ IsMessageSet() bool })
-	return ok && ms.IsMessageSet()
-}
diff --git a/vendor/github.com/google/uuid/CHANGELOG.md b/vendor/github.com/google/uuid/CHANGELOG.md
index c9fb829d..7ec5ac7e 100644
--- a/vendor/github.com/google/uuid/CHANGELOG.md
+++ b/vendor/github.com/google/uuid/CHANGELOG.md
@@ -1,5 +1,18 @@
 # Changelog
 
+## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16)
+
+
+### Features
+
+* add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3))
+
+
+### Bug Fixes
+
+* fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06))
+* Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6))
+
 ## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12)
 
 
diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go
index b404f4be..dc60082d 100644
--- a/vendor/github.com/google/uuid/hash.go
+++ b/vendor/github.com/google/uuid/hash.go
@@ -17,6 +17,12 @@ var (
 	NameSpaceOID  = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
 	NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
 	Nil           UUID // empty UUID, all zeros
+
+	// The Max UUID is special form of UUID that is specified to have all 128 bits set to 1.
+	Max = UUID{
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	}
 )
 
 // NewHash returns a new UUID derived from the hash of space concatenated with
diff --git a/vendor/github.com/google/uuid/version7.go b/vendor/github.com/google/uuid/version7.go
index ba9dd5eb..3167b643 100644
--- a/vendor/github.com/google/uuid/version7.go
+++ b/vendor/github.com/google/uuid/version7.go
@@ -44,7 +44,7 @@ func NewV7FromReader(r io.Reader) (UUID, error) {
 
 // makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6])
 // uuid[8] already has the right version number (Variant is 10)
-// see function  NewV7 and NewV7FromReader
+// see function NewV7 and NewV7FromReader
 func makeV7(uuid []byte) {
 	/*
 		 0                   1                   2                   3
@@ -52,7 +52,7 @@ func makeV7(uuid []byte) {
 		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 		|                           unix_ts_ms                          |
 		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-		|          unix_ts_ms           |  ver  |       rand_a          |
+		|          unix_ts_ms           |  ver  |  rand_a (12 bit seq)  |
 		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 		|var|                        rand_b                             |
 		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -61,7 +61,7 @@ func makeV7(uuid []byte) {
 	*/
 	_ = uuid[15] // bounds check
 
-	t := timeNow().UnixMilli()
+	t, s := getV7Time()
 
 	uuid[0] = byte(t >> 40)
 	uuid[1] = byte(t >> 32)
@@ -70,6 +70,35 @@ func makeV7(uuid []byte) {
 	uuid[4] = byte(t >> 8)
 	uuid[5] = byte(t)
 
-	uuid[6] = 0x70 | (uuid[6] & 0x0F)
-	// uuid[8] has already has right version
+	uuid[6] = 0x70 | (0x0F & byte(s>>8))
+	uuid[7] = byte(s)
+}
+
+// lastV7time is the last time we returned stored as:
+//
+//	52 bits of time in milliseconds since epoch
+//	12 bits of (fractional nanoseconds) >> 8
+var lastV7time int64
+
+const nanoPerMilli = 1000000
+
+// getV7Time returns the time in milliseconds and nanoseconds / 256.
+// The returned (milli << 12 + seq) is guarenteed to be greater than
+// (milli << 12 + seq) returned by any previous call to getV7Time.
+func getV7Time() (milli, seq int64) {
+	timeMu.Lock()
+	defer timeMu.Unlock()
+
+	nano := timeNow().UnixNano()
+	milli = nano / nanoPerMilli
+	// Sequence number is between 0 and 3906 (nanoPerMilli>>8)
+	seq = (nano - milli*nanoPerMilli) >> 8
+	now := milli<<12 + seq
+	if now <= lastV7time {
+		now = lastV7time + 1
+		milli = now >> 12
+		seq = now & 0xfff
+	}
+	lastV7time = now
+	return milli, seq
 }
diff --git a/vendor/github.com/netobserv/netobserv-ebpf-agent/pkg/utils/utils.go b/vendor/github.com/netobserv/netobserv-ebpf-agent/pkg/utils/utils.go
index c2a4c80e..f813fd9c 100644
--- a/vendor/github.com/netobserv/netobserv-ebpf-agent/pkg/utils/utils.go
+++ b/vendor/github.com/netobserv/netobserv-ebpf-agent/pkg/utils/utils.go
@@ -91,11 +91,3 @@ func utsnameStr[T int8 | uint8](in []T) string {
 	}
 	return string(out)
 }
-
-func GetInterfaceName(ifIndex uint32) string {
-	iface, err := net.InterfaceByIndex(int(ifIndex))
-	if err != nil {
-		return ""
-	}
-	return iface.Name
-}
diff --git a/vendor/github.com/vmware/go-ipfix/pkg/registry/registry_antrea.csv b/vendor/github.com/vmware/go-ipfix/pkg/registry/registry_antrea.csv
index 68084cb7..725c436f 100644
--- a/vendor/github.com/vmware/go-ipfix/pkg/registry/registry_antrea.csv
+++ b/vendor/github.com/vmware/go-ipfix/pkg/registry/registry_antrea.csv
@@ -56,3 +56,4 @@ ElementID,Name,Abstract Data Type,Data Type Semantics,Status,Description,Units,R
 154,egressIP,string,,current,,,,,,,,56506,
 155,appProtocolName,string,,current,,,,,,,,56506,
 156,httpVals,string,,current,,,,,,,,56506,
+157,egressNodeName,string,,current,,,,,,,,56506,
diff --git a/vendor/github.com/vmware/go-ipfix/pkg/registry/registry_antrea.go b/vendor/github.com/vmware/go-ipfix/pkg/registry/registry_antrea.go
index 5aaf7d59..b180427d 100644
--- a/vendor/github.com/vmware/go-ipfix/pkg/registry/registry_antrea.go
+++ b/vendor/github.com/vmware/go-ipfix/pkg/registry/registry_antrea.go
@@ -78,4 +78,5 @@ func loadAntreaRegistry() {
 	registerInfoElement(*entities.NewInfoElement("egressIP", 154, 13, 56506, 65535), 56506)
 	registerInfoElement(*entities.NewInfoElement("appProtocolName", 155, 13, 56506, 65535), 56506)
 	registerInfoElement(*entities.NewInfoElement("httpVals", 156, 13, 56506, 65535), 56506)
+	registerInfoElement(*entities.NewInfoElement("egressNodeName", 157, 13, 56506, 65535), 56506)
 }
diff --git a/vendor/go.opentelemetry.io/otel/.codespellignore b/vendor/go.opentelemetry.io/otel/.codespellignore
index ae6a3bcf..120b63a9 100644
--- a/vendor/go.opentelemetry.io/otel/.codespellignore
+++ b/vendor/go.opentelemetry.io/otel/.codespellignore
@@ -3,3 +3,5 @@ fo
 te
 collison
 consequentially
+ans
+nam
diff --git a/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/vendor/go.opentelemetry.io/otel/CHANGELOG.md
index 5422ca7d..98f2d204 100644
--- a/vendor/go.opentelemetry.io/otel/CHANGELOG.md
+++ b/vendor/go.opentelemetry.io/otel/CHANGELOG.md
@@ -8,6 +8,26 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ## [Unreleased]
 
+## [1.24.0/0.46.0/0.0.1-alpha] 2024-02-23
+
+This release is the last to support [Go 1.20].
+The next release will require at least [Go 1.21].
+
+### Added
+
+- Support [Go 1.22]. (#4890)
+- Add exemplar support to `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#4900)
+- Add exemplar support to `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#4900)
+- The `go.opentelemetry.io/otel/log` module is added.
+  This module includes OpenTelemetry Go's implementation of the Logs Bridge API.
+  This module is in an alpha state, it is subject to breaking changes.
+  See our [versioning policy](./VERSIONING.md) for more info. (#4961)
+
+### Fixed
+
+- Fix registration of multiple callbacks when using the global meter provider from `go.opentelemetry.io/otel`. (#4945)
+- Fix negative buckets in output of exponential histograms. (#4956)
+
 ## [1.23.1] 2024-02-07
 
 ### Fixed
@@ -31,7 +51,7 @@ See our [versioning policy](VERSIONING.md) for more information about these stab
 
 - Add `WithEndpointURL` option to the `exporters/otlp/otlpmetric/otlpmetricgrpc`, `exporters/otlp/otlpmetric/otlpmetrichttp`, `exporters/otlp/otlptrace/otlptracegrpc` and `exporters/otlp/otlptrace/otlptracehttp` packages. (#4808)
 - Experimental exemplar exporting is added to the metric SDK.
-  See [metric documentation](./sdk/metric/EXPERIMENTAL.md#exemplars) for more information about this feature and how to enable it. (#4871)
+  See [metric documentation](./sdk/metric/internal/x/README.md#exemplars) for more information about this feature and how to enable it. (#4871)
 - `ErrSchemaURLConflict` is added to `go.opentelemetry.io/otel/sdk/resource`.
   This error is returned when a merge of two `Resource`s with different (non-empty) schema URL is attempted. (#4876)
 
@@ -76,7 +96,7 @@ See our [versioning policy](VERSIONING.md) for more information about these stab
   The package contains semantic conventions from the `v1.24.0` version of the OpenTelemetry Semantic Conventions. (#4770)
 - Add `WithResourceAsConstantLabels` option to apply resource attributes for every metric emitted by the Prometheus exporter. (#4733)
 - Experimental cardinality limiting is added to the metric SDK.
-  See [metric documentation](./sdk/metric/EXPERIMENTAL.md#cardinality-limit) for more information about this feature and how to enable it. (#4457)
+  See [metric documentation](./sdk/metric/internal/x/README.md#cardinality-limit) for more information about this feature and how to enable it. (#4457)
 - Add `NewMemberRaw` and `NewKeyValuePropertyRaw` in `go.opentelemetry.io/otel/baggage`. (#4804)
 
 ### Changed
@@ -2829,7 +2849,8 @@ It contains api and sdk for trace and meter.
 - CircleCI build CI manifest files.
 - CODEOWNERS file to track owners of this project.
 
-[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.23.1...HEAD
+[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.24.0...HEAD
+[1.24.0/0.46.0/0.0.1-alpha]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.24.0
 [1.23.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.23.1
 [1.23.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.23.0
 [1.23.0-rc.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.23.0-rc.1
@@ -2907,6 +2928,8 @@ It contains api and sdk for trace and meter.
 [0.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.1
 [0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.0
 
+[Go 1.22]: https://go.dev/doc/go1.22
+[Go 1.21]: https://go.dev/doc/go1.21
 [Go 1.20]: https://go.dev/doc/go1.20
 [Go 1.19]: https://go.dev/doc/go1.19
 [Go 1.18]: https://go.dev/doc/go1.18
diff --git a/vendor/go.opentelemetry.io/otel/Makefile b/vendor/go.opentelemetry.io/otel/Makefile
index 35fc1899..6de95219 100644
--- a/vendor/go.opentelemetry.io/otel/Makefile
+++ b/vendor/go.opentelemetry.io/otel/Makefile
@@ -192,7 +192,7 @@ test-coverage: | $(GOCOVMERGE)
 	done; \
 	$(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt
 
-# Adding a directory will include all benchmarks in that direcotry if a filter is not specified.
+# Adding a directory will include all benchmarks in that directory if a filter is not specified.
 BENCHMARK_TARGETS := sdk/trace
 .PHONY: benchmark
 benchmark: $(BENCHMARK_TARGETS:%=benchmark/%)
@@ -315,4 +315,4 @@ add-tags: | $(MULTIMOD)
 
 .PHONY: lint-markdown
 lint-markdown: 
-	docker run -v "$(CURDIR):$(WORKDIR)" docker://avtodev/markdown-lint:v1 -c $(WORKDIR)/.markdownlint.yaml $(WORKDIR)/**/*.md
+	docker run -v "$(CURDIR):$(WORKDIR)" avtodev/markdown-lint:v1 -c $(WORKDIR)/.markdownlint.yaml $(WORKDIR)/**/*.md
diff --git a/vendor/go.opentelemetry.io/otel/README.md b/vendor/go.opentelemetry.io/otel/README.md
index 44e1bfc9..7766259a 100644
--- a/vendor/go.opentelemetry.io/otel/README.md
+++ b/vendor/go.opentelemetry.io/otel/README.md
@@ -11,14 +11,11 @@ It provides a set of APIs to directly measure performance and behavior of your s
 
 ## Project Status
 
-| Signal  | Status     |
-|---------|------------|
-| Traces  | Stable     |
-| Metrics | Stable     |
-| Logs    | Design [1] |
-
-- [1]: Currently the logs signal development is in a design phase ([#4696](https://github.com/open-telemetry/opentelemetry-go/issues/4696)).
-   No Logs Pull Requests are currently being accepted.
+| Signal  | Status             |
+|---------|--------------------|
+| Traces  | Stable             |
+| Metrics | Stable             |
+| Logs    | In development[^1] |
 
 Progress and status specific to this repository is tracked in our
 [project boards](https://github.com/open-telemetry/opentelemetry-go/projects)
@@ -28,6 +25,8 @@ and
 Project versioning information and stability guarantees can be found in the
 [versioning documentation](VERSIONING.md).
 
+[^1]: https://github.com/orgs/open-telemetry/projects/43
+
 ### Compatibility
 
 OpenTelemetry-Go ensures compatibility with the current supported versions of
@@ -50,14 +49,19 @@ Currently, this project supports the following environments.
 
 | OS      | Go Version | Architecture |
 |---------|------------|--------------|
+| Ubuntu  | 1.22       | amd64        |
 | Ubuntu  | 1.21       | amd64        |
 | Ubuntu  | 1.20       | amd64        |
+| Ubuntu  | 1.22       | 386          |
 | Ubuntu  | 1.21       | 386          |
 | Ubuntu  | 1.20       | 386          |
+| MacOS   | 1.22       | amd64        |
 | MacOS   | 1.21       | amd64        |
 | MacOS   | 1.20       | amd64        |
+| Windows | 1.22       | amd64        |
 | Windows | 1.21       | amd64        |
 | Windows | 1.20       | amd64        |
+| Windows | 1.22       | 386          |
 | Windows | 1.21       | 386          |
 | Windows | 1.20       | 386          |
 
diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/config.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/config.go
index 6eae3e1b..5948e2d9 100644
--- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/config.go
+++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/config.go
@@ -76,6 +76,26 @@ func WithEndpoint(endpoint string) Option {
 	return wrappedOption{oconf.WithEndpoint(endpoint)}
 }
 
+// WithEndpointURL sets the target endpoint URL the Exporter will connect to.
+//
+// If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
+// environment variable is set, and this option is not passed, that variable
+// value will be used. If both are set, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
+// will take precedence.
+//
+// If both this option and WithEndpoint are used, the last used option will
+// take precedence.
+//
+// If an invalid URL is provided, the default value will be kept.
+//
+// By default, if an environment variable is not set, and this option is not
+// passed, "localhost:4317" will be used.
+//
+// This option has no effect if WithGRPCConn is used.
+func WithEndpointURL(u string) Option {
+	return wrappedOption{oconf.WithEndpointURL(u)}
+}
+
 // WithCompression sets the compression strategy the Exporter will use to
 // compress the HTTP body.
 //
diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/doc.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/doc.go
index 94f8b250..a707b5eb 100644
--- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/doc.go
+++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/doc.go
@@ -26,14 +26,14 @@ The value must contain a scheme ("http" or "https") and host.
 The value may additionally contain a port and a path.
 The value should not contain a query string or fragment.
 The configuration can be overridden by OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
-environment variable and by [WithEndpoint], [WithInsecure] options.
+environment variable and by [WithEndpoint], [WithEndpointURL], and [WithInsecure] options.
 
 OTEL_EXPORTER_OTLP_METRICS_ENDPOINT (default: "https://localhost:4318/v1/metrics") -
 target URL to which the exporter sends telemetry.
 The value must contain a scheme ("http" or "https") and host.
 The value may additionally contain a port and a path.
 The value should not contain a query string or fragment.
-The configuration can be overridden by [WithEndpoint], [WitnInsecure], [WithURLPath] options.
+The configuration can be overridden by [WithEndpoint], [WithEndpointURL], [WitnInsecure], and [WithURLPath] options.
 
 OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS (default: none) -
 key-value pairs used as headers associated with HTTP requests.
diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/options.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/options.go
index 59468b9a..253539d7 100644
--- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/options.go
+++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/options.go
@@ -20,6 +20,7 @@ package oconf // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlp
 import (
 	"crypto/tls"
 	"fmt"
+	"net/url"
 	"path"
 	"strings"
 	"time"
@@ -31,6 +32,7 @@ import (
 	"google.golang.org/grpc/encoding/gzip"
 
 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry"
+	"go.opentelemetry.io/otel/internal/global"
 	"go.opentelemetry.io/otel/sdk/metric"
 )
 
@@ -279,6 +281,24 @@ func WithEndpoint(endpoint string) GenericOption {
 	})
 }
 
+func WithEndpointURL(v string) GenericOption {
+	return newGenericOption(func(cfg Config) Config {
+		u, err := url.Parse(v)
+		if err != nil {
+			global.Error(err, "otlpmetric: parse endpoint url", "url", v)
+			return cfg
+		}
+
+		cfg.Metrics.Endpoint = u.Host
+		cfg.Metrics.URLPath = u.Path
+		if u.Scheme != "https" {
+			cfg.Metrics.Insecure = true
+		}
+
+		return cfg
+	})
+}
+
 func WithCompression(compression Compression) GenericOption {
 	return newGenericOption(func(cfg Config) Config {
 		cfg.Metrics.Compression = compression
diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/version.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/version.go
index 59d7c1c2..776c6cdc 100644
--- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/version.go
+++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/version.go
@@ -16,5 +16,5 @@ package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpme
 
 // Version is the current release version of the OpenTelemetry OTLP over HTTP/protobuf metrics exporter in use.
 func Version() string {
-	return "0.44.0"
+	return "1.23.1"
 }
diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go
index ea6b767a..7b4465c4 100644
--- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go
+++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go
@@ -60,7 +60,7 @@ func (w wrappedOption) applyHTTPOption(cfg otlpconfig.Config) otlpconfig.Config
 	return w.ApplyHTTPOption(cfg)
 }
 
-// WithEndpointURL sets the target endpoint URL the Exporter will connect to.
+// WithEndpoint sets the target endpoint the Exporter will connect to.
 //
 // If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
 // environment variable is set, and this option is not passed, that variable
@@ -78,7 +78,7 @@ func WithEndpoint(endpoint string) Option {
 	return wrappedOption{otlpconfig.WithEndpoint(endpoint)}
 }
 
-// WithEndpoint sets the target endpoint URL the Exporter will connect to.
+// WithEndpointURL sets the target endpoint URL the Exporter will connect to.
 //
 // If the OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
 // environment variable is set, and this option is not passed, that variable
diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go
index 54cda3a1..afc89644 100644
--- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go
+++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go
@@ -16,5 +16,5 @@ package otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
 
 // Version is the current release version of the OpenTelemetry OTLP trace exporter in use.
 func Version() string {
-	return "1.23.1"
+	return "1.24.0"
 }
diff --git a/vendor/go.opentelemetry.io/otel/internal/global/meter.go b/vendor/go.opentelemetry.io/otel/internal/global/meter.go
index 0097db47..7ed61c0e 100644
--- a/vendor/go.opentelemetry.io/otel/internal/global/meter.go
+++ b/vendor/go.opentelemetry.io/otel/internal/global/meter.go
@@ -130,9 +130,11 @@ func (m *meter) setDelegate(provider metric.MeterProvider) {
 		inst.setDelegate(meter)
 	}
 
-	for e := m.registry.Front(); e != nil; e = e.Next() {
+	var n *list.Element
+	for e := m.registry.Front(); e != nil; e = n {
 		r := e.Value.(*registration)
 		r.setDelegate(meter)
+		n = e.Next()
 		m.registry.Remove(e)
 	}
 
diff --git a/vendor/go.opentelemetry.io/otel/internal/global/state.go b/vendor/go.opentelemetry.io/otel/internal/global/state.go
index 7985005b..386c8bfd 100644
--- a/vendor/go.opentelemetry.io/otel/internal/global/state.go
+++ b/vendor/go.opentelemetry.io/otel/internal/global/state.go
@@ -63,7 +63,7 @@ func SetTracerProvider(tp trace.TracerProvider) {
 			// to itself.
 			Error(
 				errors.New("no delegate configured in tracer provider"),
-				"Setting tracer provider to it's current value. No delegate will be configured",
+				"Setting tracer provider to its current value. No delegate will be configured",
 			)
 			return
 		}
@@ -92,7 +92,7 @@ func SetTextMapPropagator(p propagation.TextMapPropagator) {
 			// delegate to itself.
 			Error(
 				errors.New("no delegate configured in text map propagator"),
-				"Setting text map propagator to it's current value. No delegate will be configured",
+				"Setting text map propagator to its current value. No delegate will be configured",
 			)
 			return
 		}
@@ -123,7 +123,7 @@ func SetMeterProvider(mp metric.MeterProvider) {
 			// to itself.
 			Error(
 				errors.New("no delegate configured in meter provider"),
-				"Setting meter provider to it's current value. No delegate will be configured",
+				"Setting meter provider to its current value. No delegate will be configured",
 			)
 			return
 		}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/metric/doc.go b/vendor/go.opentelemetry.io/otel/sdk/metric/doc.go
index 53f80c42..475d3e39 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/metric/doc.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/metric/doc.go
@@ -44,4 +44,7 @@
 //
 // See [go.opentelemetry.io/otel/metric] for more information about
 // the metric API.
+//
+// See [go.opentelemetry.io/otel/sdk/metric/internal/x] for information about
+// the experimental features.
 package metric // import "go.opentelemetry.io/otel/sdk/metric"
diff --git a/vendor/go.opentelemetry.io/otel/sdk/metric/exemplar.go b/vendor/go.opentelemetry.io/otel/sdk/metric/exemplar.go
index 77579acd..3f1ce9f1 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/metric/exemplar.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/metric/exemplar.go
@@ -63,7 +63,7 @@ func reservoirFunc[N int64 | float64](agg Aggregation) func() exemplar.Reservoir
 			//   This Exemplar reservoir MAY take a configuration parameter for
 			//   the size of the reservoir. If no size configuration is
 			//   provided, the default size MAY be the number of possible
-			//   concurrent threads (e.g. numer of CPUs) to help reduce
+			//   concurrent threads (e.g. number of CPUs) to help reduce
 			//   contention. Otherwise, a default size of 1 SHOULD be used.
 			n = runtime.NumCPU()
 			if n < 1 {
diff --git a/vendor/go.opentelemetry.io/otel/sdk/metric/internal/aggregate/exponential_histogram.go b/vendor/go.opentelemetry.io/otel/sdk/metric/internal/aggregate/exponential_histogram.go
index 660b8607..4139a6d1 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/metric/internal/aggregate/exponential_histogram.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/metric/internal/aggregate/exponential_histogram.go
@@ -375,6 +375,7 @@ func (e *expoHistogram[N]) delta(dest *metricdata.Aggregation) int {
 
 		hDPts[i].NegativeBucket.Offset = int32(b.negBuckets.startBin)
 		hDPts[i].NegativeBucket.Counts = reset(hDPts[i].NegativeBucket.Counts, len(b.negBuckets.counts), len(b.negBuckets.counts))
+		copy(hDPts[i].NegativeBucket.Counts, b.negBuckets.counts)
 
 		if !e.noSum {
 			hDPts[i].Sum = b.sum
@@ -425,6 +426,7 @@ func (e *expoHistogram[N]) cumulative(dest *metricdata.Aggregation) int {
 
 		hDPts[i].NegativeBucket.Offset = int32(b.negBuckets.startBin)
 		hDPts[i].NegativeBucket.Counts = reset(hDPts[i].NegativeBucket.Counts, len(b.negBuckets.counts), len(b.negBuckets.counts))
+		copy(hDPts[i].NegativeBucket.Counts, b.negBuckets.counts)
 
 		if !e.noSum {
 			hDPts[i].Sum = b.sum
diff --git a/vendor/go.opentelemetry.io/otel/sdk/metric/EXPERIMENTAL.md b/vendor/go.opentelemetry.io/otel/sdk/metric/internal/x/README.md
similarity index 97%
rename from vendor/go.opentelemetry.io/otel/sdk/metric/EXPERIMENTAL.md
rename to vendor/go.opentelemetry.io/otel/sdk/metric/internal/x/README.md
index 658d2027..aba69d65 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/metric/EXPERIMENTAL.md
+++ b/vendor/go.opentelemetry.io/otel/sdk/metric/internal/x/README.md
@@ -9,6 +9,7 @@ See the [Compatibility and Stability](#compatibility-and-stability) section for
 ## Features
 
 - [Cardinality Limit](#cardinality-limit)
+- [Exemplars](#exemplars)
 
 ### Cardinality Limit
 
@@ -103,7 +104,7 @@ unset OTEL_METRICS_EXEMPLAR_FILTER
 
 ## Compatibility and Stability
 
-Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../VERSIONING.md).
+Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../VERSIONING.md).
 These features may be removed or modified in successive version releases, including patch versions.
 
 When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release.
diff --git a/vendor/go.opentelemetry.io/otel/sdk/metric/version.go b/vendor/go.opentelemetry.io/otel/sdk/metric/version.go
index 7433bf7d..310fa5a5 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/metric/version.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/metric/version.go
@@ -16,5 +16,5 @@ package metric // import "go.opentelemetry.io/otel/sdk/metric"
 
 // version is the current release version of the metric SDK in use.
 func version() string {
-	return "1.23.1"
+	return "1.24.0"
 }
diff --git a/vendor/go.opentelemetry.io/otel/sdk/version.go b/vendor/go.opentelemetry.io/otel/sdk/version.go
index b6910a01..42de0b9a 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/version.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/version.go
@@ -16,5 +16,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk"
 
 // Version is the current release version of the OpenTelemetry SDK in use.
 func Version() string {
-	return "1.23.1"
+	return "1.24.0"
 }
diff --git a/vendor/go.opentelemetry.io/otel/version.go b/vendor/go.opentelemetry.io/otel/version.go
index 0d998bca..7b2993a1 100644
--- a/vendor/go.opentelemetry.io/otel/version.go
+++ b/vendor/go.opentelemetry.io/otel/version.go
@@ -16,5 +16,5 @@ package otel // import "go.opentelemetry.io/otel"
 
 // Version is the current release version of OpenTelemetry in use.
 func Version() string {
-	return "1.23.1"
+	return "1.24.0"
 }
diff --git a/vendor/go.opentelemetry.io/otel/versions.yaml b/vendor/go.opentelemetry.io/otel/versions.yaml
index 19a73c62..1b556e67 100644
--- a/vendor/go.opentelemetry.io/otel/versions.yaml
+++ b/vendor/go.opentelemetry.io/otel/versions.yaml
@@ -14,7 +14,7 @@
 
 module-sets:
   stable-v1:
-    version: v1.23.1
+    version: v1.24.0
     modules:
       - go.opentelemetry.io/otel
       - go.opentelemetry.io/otel/bridge/opencensus
@@ -40,10 +40,14 @@ module-sets:
       - go.opentelemetry.io/otel/sdk/metric
       - go.opentelemetry.io/otel/trace
   experimental-metrics:
-    version: v0.45.2
+    version: v0.46.0
     modules:
       - go.opentelemetry.io/otel/example/prometheus
       - go.opentelemetry.io/otel/exporters/prometheus
+  experimental-logs:
+    version: v0.0.1-alpha
+    modules:
+      - go.opentelemetry.io/otel/log
   experimental-schema:
     version: v0.0.7
     modules:
diff --git a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go
index e9e97d45..856c75dd 100644
--- a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go
+++ b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go
@@ -18,8 +18,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.22.0
+// 	protoc-gen-go v1.32.0
+// 	protoc        v4.25.2
 // source: grpc/binlog/v1/binarylog.proto
 
 package grpc_binarylog_v1
diff --git a/vendor/google.golang.org/grpc/encoding/proto/proto.go b/vendor/google.golang.org/grpc/encoding/proto/proto.go
index 0ee3d3ba..66d5cdf0 100644
--- a/vendor/google.golang.org/grpc/encoding/proto/proto.go
+++ b/vendor/google.golang.org/grpc/encoding/proto/proto.go
@@ -23,8 +23,9 @@ package proto
 import (
 	"fmt"
 
-	"github.com/golang/protobuf/proto"
 	"google.golang.org/grpc/encoding"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/protoadapt"
 )
 
 // Name is the name registered for the proto compressor.
@@ -38,21 +39,34 @@ func init() {
 type codec struct{}
 
 func (codec) Marshal(v any) ([]byte, error) {
-	vv, ok := v.(proto.Message)
-	if !ok {
+	vv := messageV2Of(v)
+	if vv == nil {
 		return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v)
 	}
+
 	return proto.Marshal(vv)
 }
 
 func (codec) Unmarshal(data []byte, v any) error {
-	vv, ok := v.(proto.Message)
-	if !ok {
+	vv := messageV2Of(v)
+	if vv == nil {
 		return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v)
 	}
+
 	return proto.Unmarshal(data, vv)
 }
 
+func messageV2Of(v any) proto.Message {
+	switch v := v.(type) {
+	case protoadapt.MessageV1:
+		return protoadapt.MessageV2Of(v)
+	case protoadapt.MessageV2:
+		return v
+	}
+
+	return nil
+}
+
 func (codec) Name() string {
 	return Name
 }
diff --git a/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go b/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
index 24299efd..5bf880d4 100644
--- a/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
+++ b/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
@@ -17,8 +17,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.22.0
+// 	protoc-gen-go v1.32.0
+// 	protoc        v4.25.2
 // source: grpc/health/v1/health.proto
 
 package grpc_health_v1
diff --git a/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go b/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
index 4439cda0..4c46c098 100644
--- a/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
+++ b/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
@@ -18,7 +18,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.22.0
+// - protoc             v4.25.2
 // source: grpc/health/v1/health.proto
 
 package grpc_health_v1
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
index 0f31274a..e8456a77 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
@@ -25,11 +25,12 @@ import (
 	"sync/atomic"
 	"time"
 
-	"github.com/golang/protobuf/proto"
-	"github.com/golang/protobuf/ptypes"
 	binlogpb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
 	"google.golang.org/grpc/metadata"
 	"google.golang.org/grpc/status"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/types/known/durationpb"
+	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
 type callIDGenerator struct {
@@ -88,7 +89,7 @@ func NewTruncatingMethodLogger(h, m uint64) *TruncatingMethodLogger {
 // in TruncatingMethodLogger as possible.
 func (ml *TruncatingMethodLogger) Build(c LogEntryConfig) *binlogpb.GrpcLogEntry {
 	m := c.toProto()
-	timestamp, _ := ptypes.TimestampProto(time.Now())
+	timestamp := timestamppb.Now()
 	m.Timestamp = timestamp
 	m.CallId = ml.callID
 	m.SequenceIdWithinCall = ml.idWithinCallGen.next()
@@ -178,7 +179,7 @@ func (c *ClientHeader) toProto() *binlogpb.GrpcLogEntry {
 		Authority:  c.Authority,
 	}
 	if c.Timeout > 0 {
-		clientHeader.Timeout = ptypes.DurationProto(c.Timeout)
+		clientHeader.Timeout = durationpb.New(c.Timeout)
 	}
 	ret := &binlogpb.GrpcLogEntry{
 		Type: binlogpb.GrpcLogEntry_EVENT_TYPE_CLIENT_HEADER,
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/sink.go b/vendor/google.golang.org/grpc/internal/binarylog/sink.go
index 264de387..9ea598b1 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/sink.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/sink.go
@@ -25,8 +25,8 @@ import (
 	"sync"
 	"time"
 
-	"github.com/golang/protobuf/proto"
 	binlogpb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
+	"google.golang.org/protobuf/proto"
 )
 
 var (
diff --git a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go
index aa97273e..0126d6b5 100644
--- a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go
+++ b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go
@@ -1,3 +1,8 @@
+//go:build !go1.21
+
+// TODO: when this file is deleted (after Go 1.20 support is dropped), delete
+// all of grpcrand and call the rand package directly.
+
 /*
  *
  * Copyright 2018 gRPC authors.
diff --git a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go
new file mode 100644
index 00000000..c37299af
--- /dev/null
+++ b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go
@@ -0,0 +1,73 @@
+//go:build go1.21
+
+/*
+ *
+ * Copyright 2024 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+// Package grpcrand implements math/rand functions in a concurrent-safe way
+// with a global random source, independent of math/rand's global source.
+package grpcrand
+
+import "math/rand"
+
+// This implementation will be used for Go version 1.21 or newer.
+// For older versions, the original implementation with mutex will be used.
+
+// Int implements rand.Int on the grpcrand global source.
+func Int() int {
+	return rand.Int()
+}
+
+// Int63n implements rand.Int63n on the grpcrand global source.
+func Int63n(n int64) int64 {
+	return rand.Int63n(n)
+}
+
+// Intn implements rand.Intn on the grpcrand global source.
+func Intn(n int) int {
+	return rand.Intn(n)
+}
+
+// Int31n implements rand.Int31n on the grpcrand global source.
+func Int31n(n int32) int32 {
+	return rand.Int31n(n)
+}
+
+// Float64 implements rand.Float64 on the grpcrand global source.
+func Float64() float64 {
+	return rand.Float64()
+}
+
+// Uint64 implements rand.Uint64 on the grpcrand global source.
+func Uint64() uint64 {
+	return rand.Uint64()
+}
+
+// Uint32 implements rand.Uint32 on the grpcrand global source.
+func Uint32() uint32 {
+	return rand.Uint32()
+}
+
+// ExpFloat64 implements rand.ExpFloat64 on the grpcrand global source.
+func ExpFloat64() float64 {
+	return rand.ExpFloat64()
+}
+
+// Shuffle implements rand.Shuffle on the grpcrand global source.
+var Shuffle = func(n int, f func(int, int)) {
+	rand.Shuffle(n, f)
+}
diff --git a/vendor/google.golang.org/grpc/internal/pretty/pretty.go b/vendor/google.golang.org/grpc/internal/pretty/pretty.go
index 70331913..52cfab1b 100644
--- a/vendor/google.golang.org/grpc/internal/pretty/pretty.go
+++ b/vendor/google.golang.org/grpc/internal/pretty/pretty.go
@@ -24,7 +24,6 @@ import (
 	"encoding/json"
 	"fmt"
 
-	"github.com/golang/protobuf/jsonpb"
 	protov1 "github.com/golang/protobuf/proto"
 	"google.golang.org/protobuf/encoding/protojson"
 	protov2 "google.golang.org/protobuf/proto"
@@ -38,15 +37,15 @@ const jsonIndent = "  "
 func ToJSON(e any) string {
 	switch ee := e.(type) {
 	case protov1.Message:
-		mm := jsonpb.Marshaler{Indent: jsonIndent}
-		ret, err := mm.MarshalToString(ee)
+		mm := protojson.MarshalOptions{Indent: jsonIndent}
+		ret, err := mm.Marshal(protov1.MessageV2(ee))
 		if err != nil {
 			// This may fail for proto.Anys, e.g. for xDS v2, LDS, the v2
 			// messages are not imported, and this will fail because the message
 			// is not found.
 			return fmt.Sprintf("%+v", ee)
 		}
-		return ret
+		return string(ret)
 	case protov2.Message:
 		mm := protojson.MarshalOptions{
 			Multiline: true,
diff --git a/vendor/google.golang.org/grpc/internal/status/status.go b/vendor/google.golang.org/grpc/internal/status/status.go
index 03ef2fed..c7dbc820 100644
--- a/vendor/google.golang.org/grpc/internal/status/status.go
+++ b/vendor/google.golang.org/grpc/internal/status/status.go
@@ -31,10 +31,11 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/golang/protobuf/proto"
-	"github.com/golang/protobuf/ptypes"
 	spb "google.golang.org/genproto/googleapis/rpc/status"
 	"google.golang.org/grpc/codes"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/protoadapt"
+	"google.golang.org/protobuf/types/known/anypb"
 )
 
 // Status represents an RPC status code, message, and details.  It is immutable
@@ -130,14 +131,14 @@ func (s *Status) Err() error {
 
 // WithDetails returns a new status with the provided details messages appended to the status.
 // If any errors are encountered, it returns nil and the first error encountered.
-func (s *Status) WithDetails(details ...proto.Message) (*Status, error) {
+func (s *Status) WithDetails(details ...protoadapt.MessageV1) (*Status, error) {
 	if s.Code() == codes.OK {
 		return nil, errors.New("no error details for status with code OK")
 	}
 	// s.Code() != OK implies that s.Proto() != nil.
 	p := s.Proto()
 	for _, detail := range details {
-		any, err := ptypes.MarshalAny(detail)
+		any, err := anypb.New(protoadapt.MessageV2Of(detail))
 		if err != nil {
 			return nil, err
 		}
@@ -154,12 +155,12 @@ func (s *Status) Details() []any {
 	}
 	details := make([]any, 0, len(s.s.Details))
 	for _, any := range s.s.Details {
-		detail := &ptypes.DynamicAny{}
-		if err := ptypes.UnmarshalAny(any, detail); err != nil {
+		detail, err := any.UnmarshalNew()
+		if err != nil {
 			details = append(details, err)
 			continue
 		}
-		details = append(details, detail.Message)
+		details = append(details, detail)
 	}
 	return details
 }
diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
index b330cced..83c38298 100644
--- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
+++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
@@ -535,8 +535,8 @@ const minBatchSize = 1000
 // size is too low to give stream goroutines a chance to fill it up.
 //
 // Upon exiting, if the error causing the exit is not an I/O error, run()
-// flushes and closes the underlying connection.  Otherwise, the connection is
-// left open to allow the I/O error to be encountered by the reader instead.
+// flushes the underlying connection.  The connection is always left open to
+// allow different closing behavior on the client and server.
 func (l *loopyWriter) run() (err error) {
 	defer func() {
 		if l.logger.V(logLevel) {
@@ -544,7 +544,6 @@ func (l *loopyWriter) run() (err error) {
 		}
 		if !isIOError(err) {
 			l.framer.writer.Flush()
-			l.conn.Close()
 		}
 		l.cbuf.finish()
 	}()
diff --git a/vendor/google.golang.org/grpc/internal/transport/handler_server.go b/vendor/google.golang.org/grpc/internal/transport/handler_server.go
index a9d70e2a..bd39ff9a 100644
--- a/vendor/google.golang.org/grpc/internal/transport/handler_server.go
+++ b/vendor/google.golang.org/grpc/internal/transport/handler_server.go
@@ -35,7 +35,6 @@ import (
 	"sync"
 	"time"
 
-	"github.com/golang/protobuf/proto"
 	"golang.org/x/net/http2"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials"
@@ -45,6 +44,7 @@ import (
 	"google.golang.org/grpc/peer"
 	"google.golang.org/grpc/stats"
 	"google.golang.org/grpc/status"
+	"google.golang.org/protobuf/proto"
 )
 
 // NewServerHandlerTransport returns a ServerTransport handling gRPC from
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
index c33ac596..eff87996 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
@@ -451,7 +451,13 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
 	}
 	go func() {
 		t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger)
-		t.loopy.run()
+		if err := t.loopy.run(); !isIOError(err) {
+			// Immediately close the connection, as the loopy writer returns
+			// when there are no more active streams and we were draining (the
+			// server sent a GOAWAY).  For I/O errors, the reader will hit it
+			// after draining any remaining incoming data.
+			t.conn.Close()
+		}
 		close(t.writerDone)
 	}()
 	return t, nil
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
index f6bac0e8..3839c1ad 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
@@ -32,13 +32,13 @@ import (
 	"sync/atomic"
 	"time"
 
-	"github.com/golang/protobuf/proto"
 	"golang.org/x/net/http2"
 	"golang.org/x/net/http2/hpack"
 	"google.golang.org/grpc/internal/grpclog"
 	"google.golang.org/grpc/internal/grpcutil"
 	"google.golang.org/grpc/internal/pretty"
 	"google.golang.org/grpc/internal/syscall"
+	"google.golang.org/protobuf/proto"
 
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials"
@@ -322,8 +322,24 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
 	go func() {
 		t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger)
 		t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler
-		t.loopy.run()
+		err := t.loopy.run()
 		close(t.loopyWriterDone)
+		if !isIOError(err) {
+			// Close the connection if a non-I/O error occurs (for I/O errors
+			// the reader will also encounter the error and close).  Wait 1
+			// second before closing the connection, or when the reader is done
+			// (i.e. the client already closed the connection or a connection
+			// error occurred).  This avoids the potential problem where there
+			// is unread data on the receive side of the connection, which, if
+			// closed, would lead to a TCP RST instead of FIN, and the client
+			// encountering errors.  For more info:
+			// https://github.com/grpc/grpc-go/issues/5358
+			select {
+			case <-t.readerDone:
+			case <-time.After(time.Second):
+			}
+			t.conn.Close()
+		}
 	}()
 	go t.keepalive()
 	return t, nil
@@ -609,8 +625,8 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade
 // traceCtx attaches trace to ctx and returns the new context.
 func (t *http2Server) HandleStreams(ctx context.Context, handle func(*Stream)) {
 	defer func() {
-		<-t.loopyWriterDone
 		close(t.readerDone)
+		<-t.loopyWriterDone
 	}()
 	for {
 		t.controlBuf.throttle()
@@ -636,10 +652,6 @@ func (t *http2Server) HandleStreams(ctx context.Context, handle func(*Stream)) {
 				}
 				continue
 			}
-			if err == io.EOF || err == io.ErrUnexpectedEOF {
-				t.Close(err)
-				return
-			}
 			t.Close(err)
 			return
 		}
@@ -1329,6 +1341,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) {
 		if err := t.framer.fr.WriteGoAway(sid, g.code, g.debugData); err != nil {
 			return false, err
 		}
+		t.framer.writer.Flush()
 		if retErr != nil {
 			return false, retErr
 		}
@@ -1349,7 +1362,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) {
 		return false, err
 	}
 	go func() {
-		timer := time.NewTimer(time.Minute)
+		timer := time.NewTimer(5 * time.Second)
 		defer timer.Stop()
 		select {
 		case <-t.drainEvent.Done():
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection.pb.go
index 6f5c786b..8953c9d8 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection.pb.go
@@ -21,8 +21,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.22.0
+// 	protoc-gen-go v1.32.0
+// 	protoc        v4.25.2
 // source: grpc/reflection/v1/reflection.proto
 
 package grpc_reflection_v1
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go
index 62b56a8b..d6cdd5b5 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go
@@ -22,7 +22,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.22.0
+// - protoc             v4.25.2
 // source: grpc/reflection/v1/reflection.proto
 
 package grpc_reflection_v1
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go
index 69fbfb62..929733e7 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go
@@ -18,8 +18,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.22.0
+// 	protoc-gen-go v1.32.0
+// 	protoc        v4.25.2
 // grpc/reflection/v1alpha/reflection.proto is a deprecated file.
 
 package grpc_reflection_v1alpha
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
index 367a029b..ef691406 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
@@ -19,7 +19,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.22.0
+// - protoc             v4.25.2
 // grpc/reflection/v1alpha/reflection.proto is a deprecated file.
 
 package grpc_reflection_v1alpha
diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go
index a4b6bc68..d17ede0f 100644
--- a/vendor/google.golang.org/grpc/rpc_util.go
+++ b/vendor/google.golang.org/grpc/rpc_util.go
@@ -189,6 +189,20 @@ type EmptyCallOption struct{}
 func (EmptyCallOption) before(*callInfo) error      { return nil }
 func (EmptyCallOption) after(*callInfo, *csAttempt) {}
 
+// StaticMethod returns a CallOption which specifies that a call is being made
+// to a method that is static, which means the method is known at compile time
+// and doesn't change at runtime. This can be used as a signal to stats plugins
+// that this method is safe to include as a key to a measurement.
+func StaticMethod() CallOption {
+	return StaticMethodCallOption{}
+}
+
+// StaticMethodCallOption is a CallOption that specifies that a call comes
+// from a static method.
+type StaticMethodCallOption struct {
+	EmptyCallOption
+}
+
 // Header returns a CallOptions that retrieves the header metadata
 // for a unary RPC.
 func Header(md *metadata.MD) CallOption {
@@ -958,6 +972,7 @@ const (
 	SupportPackageIsVersion5 = true
 	SupportPackageIsVersion6 = true
 	SupportPackageIsVersion7 = true
+	SupportPackageIsVersion8 = true
 )
 
 const grpcUA = "grpc-go/" + Version
diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go
index e89c5ac6..0bf5c78b 100644
--- a/vendor/google.golang.org/grpc/server.go
+++ b/vendor/google.golang.org/grpc/server.go
@@ -33,8 +33,6 @@ import (
 	"sync/atomic"
 	"time"
 
-	"golang.org/x/net/trace"
-
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials"
 	"google.golang.org/grpc/encoding"
@@ -131,7 +129,7 @@ type Server struct {
 	drain    bool
 	cv       *sync.Cond              // signaled when connections close for GracefulStop
 	services map[string]*serviceInfo // service name -> service info
-	events   trace.EventLog
+	events   traceEventLog
 
 	quit               *grpcsync.Event
 	done               *grpcsync.Event
@@ -670,7 +668,7 @@ func NewServer(opt ...ServerOption) *Server {
 	s.cv = sync.NewCond(&s.mu)
 	if EnableTracing {
 		_, file, line, _ := runtime.Caller(1)
-		s.events = trace.NewEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line))
+		s.events = newTraceEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line))
 	}
 
 	if s.opts.numServerWorkers > 0 {
@@ -1734,8 +1732,8 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
 	ctx = contextWithServer(ctx, s)
 	var ti *traceInfo
 	if EnableTracing {
-		tr := trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method())
-		ctx = trace.NewContext(ctx, tr)
+		tr := newTrace("grpc.Recv."+methodFamily(stream.Method()), stream.Method())
+		ctx = newTraceContext(ctx, tr)
 		ti = &traceInfo{
 			tr: tr,
 			firstLine: firstLine{
diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go
index d621f52b..814e9983 100644
--- a/vendor/google.golang.org/grpc/stream.go
+++ b/vendor/google.golang.org/grpc/stream.go
@@ -27,7 +27,6 @@ import (
 	"sync"
 	"time"
 
-	"golang.org/x/net/trace"
 	"google.golang.org/grpc/balancer"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/encoding"
@@ -431,7 +430,7 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (*csAttempt, error)
 	var trInfo *traceInfo
 	if EnableTracing {
 		trInfo = &traceInfo{
-			tr: trace.New("grpc.Sent."+methodFamily(method), method),
+			tr: newTrace("grpc.Sent."+methodFamily(method), method),
 			firstLine: firstLine{
 				client: true,
 			},
@@ -440,7 +439,7 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (*csAttempt, error)
 			trInfo.firstLine.deadline = time.Until(deadline)
 		}
 		trInfo.tr.LazyLog(&trInfo.firstLine, false)
-		ctx = trace.NewContext(ctx, trInfo.tr)
+		ctx = newTraceContext(ctx, trInfo.tr)
 	}
 
 	if cs.cc.parsedTarget.URL.Scheme == internal.GRPCResolverSchemeExtraMetadata {
diff --git a/vendor/google.golang.org/grpc/trace.go b/vendor/google.golang.org/grpc/trace.go
index 9ded7932..10f4f798 100644
--- a/vendor/google.golang.org/grpc/trace.go
+++ b/vendor/google.golang.org/grpc/trace.go
@@ -26,8 +26,6 @@ import (
 	"strings"
 	"sync"
 	"time"
-
-	"golang.org/x/net/trace"
 )
 
 // EnableTracing controls whether to trace RPCs using the golang.org/x/net/trace package.
@@ -44,9 +42,31 @@ func methodFamily(m string) string {
 	return m
 }
 
+// traceEventLog mirrors golang.org/x/net/trace.EventLog.
+//
+// It exists in order to avoid importing x/net/trace on grpcnotrace builds.
+type traceEventLog interface {
+	Printf(format string, a ...any)
+	Errorf(format string, a ...any)
+	Finish()
+}
+
+// traceLog mirrors golang.org/x/net/trace.Trace.
+//
+// It exists in order to avoid importing x/net/trace on grpcnotrace builds.
+type traceLog interface {
+	LazyLog(x fmt.Stringer, sensitive bool)
+	LazyPrintf(format string, a ...any)
+	SetError()
+	SetRecycler(f func(any))
+	SetTraceInfo(traceID, spanID uint64)
+	SetMaxEvents(m int)
+	Finish()
+}
+
 // traceInfo contains tracing information for an RPC.
 type traceInfo struct {
-	tr        trace.Trace
+	tr        traceLog
 	firstLine firstLine
 }
 
diff --git a/vendor/google.golang.org/grpc/trace_notrace.go b/vendor/google.golang.org/grpc/trace_notrace.go
new file mode 100644
index 00000000..1da3a230
--- /dev/null
+++ b/vendor/google.golang.org/grpc/trace_notrace.go
@@ -0,0 +1,52 @@
+//go:build grpcnotrace
+
+/*
+ *
+ * Copyright 2024 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+package grpc
+
+// grpcnotrace can be used to avoid importing golang.org/x/net/trace, which in
+// turn enables binaries using gRPC-Go for dead code elimination, which can
+// yield 10-15% improvements in binary size when tracing is not needed.
+
+import (
+	"context"
+	"fmt"
+)
+
+type notrace struct{}
+
+func (notrace) LazyLog(x fmt.Stringer, sensitive bool) {}
+func (notrace) LazyPrintf(format string, a ...any)     {}
+func (notrace) SetError()                              {}
+func (notrace) SetRecycler(f func(any))                {}
+func (notrace) SetTraceInfo(traceID, spanID uint64)    {}
+func (notrace) SetMaxEvents(m int)                     {}
+func (notrace) Finish()                                {}
+
+func newTrace(family, title string) traceLog {
+	return notrace{}
+}
+
+func newTraceContext(ctx context.Context, tr traceLog) context.Context {
+	return ctx
+}
+
+func newTraceEventLog(family, title string) traceEventLog {
+	return nil
+}
diff --git a/vendor/google.golang.org/grpc/trace_withtrace.go b/vendor/google.golang.org/grpc/trace_withtrace.go
new file mode 100644
index 00000000..88d6e857
--- /dev/null
+++ b/vendor/google.golang.org/grpc/trace_withtrace.go
@@ -0,0 +1,39 @@
+//go:build !grpcnotrace
+
+/*
+ *
+ * Copyright 2024 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+package grpc
+
+import (
+	"context"
+
+	t "golang.org/x/net/trace"
+)
+
+func newTrace(family, title string) traceLog {
+	return t.New(family, title)
+}
+
+func newTraceContext(ctx context.Context, tr traceLog) context.Context {
+	return t.NewContext(ctx, tr)
+}
+
+func newTraceEventLog(family, title string) traceEventLog {
+	return t.NewEventLog(family, title)
+}
diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go
index 1ad1ba2a..df85a021 100644
--- a/vendor/google.golang.org/grpc/version.go
+++ b/vendor/google.golang.org/grpc/version.go
@@ -19,4 +19,4 @@
 package grpc
 
 // Version is the current grpc version.
-const Version = "1.61.0"
+const Version = "1.62.0"
diff --git a/vendor/google.golang.org/grpc/vet.sh b/vendor/google.golang.org/grpc/vet.sh
index 5da38a40..7a33c215 100644
--- a/vendor/google.golang.org/grpc/vet.sh
+++ b/vendor/google.golang.org/grpc/vet.sh
@@ -41,7 +41,7 @@ if [[ "$1" = "-install" ]]; then
   popd
   if [[ -z "${VET_SKIP_PROTO}" ]]; then
     if [[ "${GITHUB_ACTIONS}" = "true" ]]; then
-      PROTOBUF_VERSION=22.0 # a.k.a v4.22.0 in pb.go files.
+      PROTOBUF_VERSION=25.2 # a.k.a. v4.22.0 in pb.go files.
       PROTOC_FILENAME=protoc-${PROTOBUF_VERSION}-linux-x86_64.zip
       pushd /home/runner/go
       wget https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/${PROTOC_FILENAME}
diff --git a/vendor/google.golang.org/protobuf/protoadapt/convert.go b/vendor/google.golang.org/protobuf/protoadapt/convert.go
new file mode 100644
index 00000000..ea276d15
--- /dev/null
+++ b/vendor/google.golang.org/protobuf/protoadapt/convert.go
@@ -0,0 +1,31 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package protoadapt bridges the original and new proto APIs.
+package protoadapt
+
+import (
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/runtime/protoiface"
+	"google.golang.org/protobuf/runtime/protoimpl"
+)
+
+// MessageV1 is the original [github.com/golang/protobuf/proto.Message] type.
+type MessageV1 = protoiface.MessageV1
+
+// MessageV2 is the [google.golang.org/protobuf/proto.Message] type used by the
+// current [google.golang.org/protobuf] module, adding support for reflection.
+type MessageV2 = proto.Message
+
+// MessageV1Of converts a v2 message to a v1 message.
+// It returns nil if m is nil.
+func MessageV1Of(m MessageV2) MessageV1 {
+	return protoimpl.X.ProtoMessageV1Of(m)
+}
+
+// MessageV2Of converts a v1 message to a v2 message.
+// It returns nil if m is nil.
+func MessageV2Of(m MessageV1) MessageV2 {
+	return protoimpl.X.ProtoMessageV2Of(m)
+}
diff --git a/vendor/k8s.io/api/core/v1/generated.proto b/vendor/k8s.io/api/core/v1/generated.proto
index cf9b6e6e..d099238c 100644
--- a/vendor/k8s.io/api/core/v1/generated.proto
+++ b/vendor/k8s.io/api/core/v1/generated.proto
@@ -3286,7 +3286,7 @@ message PersistentVolumeStatus {
 
   // lastPhaseTransitionTime is the time the phase transitioned from one to another
   // and automatically resets to current time everytime a volume phase transitions.
-  // This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature.
+  // This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).
   // +featureGate=PersistentVolumeLastPhaseTransitionTime
   // +optional
   optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastPhaseTransitionTime = 4;
diff --git a/vendor/k8s.io/api/core/v1/types.go b/vendor/k8s.io/api/core/v1/types.go
index 1aade380..61ba21bc 100644
--- a/vendor/k8s.io/api/core/v1/types.go
+++ b/vendor/k8s.io/api/core/v1/types.go
@@ -423,7 +423,7 @@ type PersistentVolumeStatus struct {
 	Reason string `json:"reason,omitempty" protobuf:"bytes,3,opt,name=reason"`
 	// lastPhaseTransitionTime is the time the phase transitioned from one to another
 	// and automatically resets to current time everytime a volume phase transitions.
-	// This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature.
+	// This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).
 	// +featureGate=PersistentVolumeLastPhaseTransitionTime
 	// +optional
 	LastPhaseTransitionTime *metav1.Time `json:"lastPhaseTransitionTime,omitempty" protobuf:"bytes,4,opt,name=lastPhaseTransitionTime"`
diff --git a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
index 01152a09..fd6f7dc6 100644
--- a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
+++ b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
@@ -1478,7 +1478,7 @@ var map_PersistentVolumeStatus = map[string]string{
 	"phase":                   "phase indicates if a volume is available, bound to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase",
 	"message":                 "message is a human-readable message indicating details about why the volume is in this state.",
 	"reason":                  "reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.",
-	"lastPhaseTransitionTime": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature.",
+	"lastPhaseTransitionTime": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).",
 }
 
 func (PersistentVolumeStatus) SwaggerDoc() map[string]string {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 239bff73..64131806 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -84,7 +84,6 @@ github.com/gogo/protobuf/sortkeys
 github.com/gogo/protobuf/types
 # github.com/golang/protobuf v1.5.3
 ## explicit; go 1.9
-github.com/golang/protobuf/jsonpb
 github.com/golang/protobuf/proto
 github.com/golang/protobuf/ptypes
 github.com/golang/protobuf/ptypes/any
@@ -119,7 +118,7 @@ github.com/google/go-jsonnet/internal/program
 ## explicit; go 1.12
 github.com/google/gofuzz
 github.com/google/gofuzz/bytesource
-# github.com/google/uuid v1.5.0
+# github.com/google/uuid v1.6.0
 ## explicit
 github.com/google/uuid
 # github.com/gorilla/websocket v1.5.0
@@ -250,7 +249,7 @@ github.com/netobserv/loki-client-go/pkg/labelutil
 github.com/netobserv/loki-client-go/pkg/logproto
 github.com/netobserv/loki-client-go/pkg/metric
 github.com/netobserv/loki-client-go/pkg/urlutil
-# github.com/netobserv/netobserv-ebpf-agent v0.3.3-0.20240117144239-03bb6a38b3f0
+# github.com/netobserv/netobserv-ebpf-agent v0.3.3
 ## explicit; go 1.20
 github.com/netobserv/netobserv-ebpf-agent/pkg/decode
 github.com/netobserv/netobserv-ebpf-agent/pkg/grpc
@@ -473,7 +472,7 @@ github.com/vladimirvivien/gexe/fs
 github.com/vladimirvivien/gexe/http
 github.com/vladimirvivien/gexe/prog
 github.com/vladimirvivien/gexe/vars
-# github.com/vmware/go-ipfix v0.8.2
+# github.com/vmware/go-ipfix v0.9.0
 ## explicit; go 1.21
 github.com/vmware/go-ipfix/pkg/collector
 github.com/vmware/go-ipfix/pkg/entities
@@ -489,7 +488,7 @@ github.com/xdg-go/scram
 # github.com/xdg-go/stringprep v1.0.4
 ## explicit; go 1.11
 github.com/xdg-go/stringprep
-# go.opentelemetry.io/otel v1.23.1
+# go.opentelemetry.io/otel v1.24.0
 ## explicit; go 1.20
 go.opentelemetry.io/otel
 go.opentelemetry.io/otel/attribute
@@ -510,7 +509,7 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envco
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform
-# go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0
+# go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.23.1
 ## explicit; go 1.20
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal
@@ -518,7 +517,7 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/envco
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry
 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform
-# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.23.1
+# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0
 ## explicit; go 1.20
 go.opentelemetry.io/otel/exporters/otlp/otlptrace
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform
@@ -529,19 +528,19 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/envconfig
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry
-# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1
+# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0
 ## explicit; go 1.20
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/envconfig
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/retry
-# go.opentelemetry.io/otel/metric v1.23.1
+# go.opentelemetry.io/otel/metric v1.24.0
 ## explicit; go 1.20
 go.opentelemetry.io/otel/metric
 go.opentelemetry.io/otel/metric/embedded
 go.opentelemetry.io/otel/metric/noop
-# go.opentelemetry.io/otel/sdk v1.23.1
+# go.opentelemetry.io/otel/sdk v1.24.0
 ## explicit; go 1.20
 go.opentelemetry.io/otel/sdk
 go.opentelemetry.io/otel/sdk/instrumentation
@@ -549,7 +548,7 @@ go.opentelemetry.io/otel/sdk/internal
 go.opentelemetry.io/otel/sdk/internal/env
 go.opentelemetry.io/otel/sdk/resource
 go.opentelemetry.io/otel/sdk/trace
-# go.opentelemetry.io/otel/sdk/metric v1.23.1
+# go.opentelemetry.io/otel/sdk/metric v1.24.0
 ## explicit; go 1.20
 go.opentelemetry.io/otel/sdk/metric
 go.opentelemetry.io/otel/sdk/metric/internal
@@ -557,7 +556,7 @@ go.opentelemetry.io/otel/sdk/metric/internal/aggregate
 go.opentelemetry.io/otel/sdk/metric/internal/exemplar
 go.opentelemetry.io/otel/sdk/metric/internal/x
 go.opentelemetry.io/otel/sdk/metric/metricdata
-# go.opentelemetry.io/otel/trace v1.23.1
+# go.opentelemetry.io/otel/trace v1.24.0
 ## explicit; go 1.20
 go.opentelemetry.io/otel/trace
 go.opentelemetry.io/otel/trace/embedded
@@ -647,14 +646,14 @@ google.golang.org/appengine/internal/log
 google.golang.org/appengine/internal/remote_api
 google.golang.org/appengine/internal/urlfetch
 google.golang.org/appengine/urlfetch
-# google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917
+# google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80
 ## explicit; go 1.19
 google.golang.org/genproto/googleapis/api/httpbody
-# google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917
+# google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80
 ## explicit; go 1.19
 google.golang.org/genproto/googleapis/rpc/errdetails
 google.golang.org/genproto/googleapis/rpc/status
-# google.golang.org/grpc v1.61.0
+# google.golang.org/grpc v1.62.0
 ## explicit; go 1.19
 google.golang.org/grpc
 google.golang.org/grpc/attributes
@@ -738,6 +737,7 @@ google.golang.org/protobuf/internal/set
 google.golang.org/protobuf/internal/strs
 google.golang.org/protobuf/internal/version
 google.golang.org/protobuf/proto
+google.golang.org/protobuf/protoadapt
 google.golang.org/protobuf/reflect/protodesc
 google.golang.org/protobuf/reflect/protoreflect
 google.golang.org/protobuf/reflect/protoregistry
@@ -764,7 +764,7 @@ gopkg.in/yaml.v2
 # gopkg.in/yaml.v3 v3.0.1
 ## explicit
 gopkg.in/yaml.v3
-# k8s.io/api v0.29.1
+# k8s.io/api v0.29.2
 ## explicit; go 1.21
 k8s.io/api/admissionregistration/v1
 k8s.io/api/admissionregistration/v1alpha1
@@ -818,7 +818,7 @@ k8s.io/api/scheduling/v1beta1
 k8s.io/api/storage/v1
 k8s.io/api/storage/v1alpha1
 k8s.io/api/storage/v1beta1
-# k8s.io/apimachinery v0.29.1
+# k8s.io/apimachinery v0.29.2
 ## explicit; go 1.21
 k8s.io/apimachinery/pkg/api/equality
 k8s.io/apimachinery/pkg/api/errors
@@ -873,7 +873,7 @@ k8s.io/apimachinery/pkg/watch
 k8s.io/apimachinery/third_party/forked/golang/json
 k8s.io/apimachinery/third_party/forked/golang/netutil
 k8s.io/apimachinery/third_party/forked/golang/reflect
-# k8s.io/client-go v0.29.1
+# k8s.io/client-go v0.29.2
 ## explicit; go 1.21
 k8s.io/client-go/applyconfigurations/admissionregistration/v1
 k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1


Test patch:
diff --git a/cmd/flowlogs-pipeline/main_test.go b/cmd/flowlogs-pipeline/main_test.go
index fca40f8e..b9f45082 100644
--- a/cmd/flowlogs-pipeline/main_test.go
+++ b/cmd/flowlogs-pipeline/main_test.go
@@ -50,7 +50,7 @@ func TestPipelineConfigSetup(t *testing.T) {
 
 	js := `{
     "PipeLine": "[{\"name\":\"grpc\"},{\"follows\":\"grpc\",\"name\":\"enrich\"},{\"follows\":\"enrich\",\"name\":\"loki\"},{\"follows\":\"enrich\",\"name\":\"prometheus\"}]",
-    "Parameters": "[{\"ingest\":{\"grpc\":{\"port\":2055},\"type\":\"grpc\"},\"name\":\"grpc\"},{\"name\":\"enrich\",\"transform\":{\"network\":{\"rules\":[{\"input\":\"SrcAddr\",\"output\":\"SrcK8S\",\"type\":\"add_kubernetes\"},{\"input\":\"DstAddr\",\"output\":\"DstK8S\",\"type\":\"add_kubernetes\"},{\"input\":\"DstPort\",\"output\":\"Service\",\"parameters\":\"Proto\",\"type\":\"add_service\"},{\"input\":\"SrcAddr\",\"output\":\"SrcSubnet\",\"parameters\":\"/16\",\"type\":\"add_subnet\"}]},\"type\":\"network\"}},{\"name\":\"loki\",\"write\":{\"loki\":{\"batchSize\":102400,\"batchWait\":\"1s\",\"clientConfig\":{\"follow_redirects\":false,\"proxy_url\":null,\"tls_config\":{\"insecure_skip_verify\":false}},\"labels\":[\"SrcK8S_Namespace\",\"SrcK8S_OwnerName\",\"DstK8S_Namespace\",\"DstK8S_OwnerName\",\"FlowDirection\"],\"maxBackoff\":\"5m0s\",\"maxRetries\":10,\"minBackoff\":\"1s\",\"staticLabels\":{\"app\":\"netobserv-flowcollector\"},\"tenantID\":\"netobserv\",\"timeout\":\"10s\",\"timestampLabel\":\"TimeFlowEndMs\",\"timestampScale\":\"1ms\",\"url\":\"http://loki.netobserv.svc:3100/\"},\"type\":\"loki\"}},{\"encode\":{\"prom\":{\"metrics\":[{\"buckets\":null,\"filter\":{\"key\":\"\",\"value\":\"\"},\"labels\":[\"Service\",\"SrcK8S_Namespace\"],\"name\":\"bandwidth_per_network_service_per_namespace\",\"type\":\"counter\",\"valueKey\":\"Bytes\"},{\"buckets\":null,\"filter\":{\"key\":\"\",\"value\":\"\"},\"labels\":[\"SrcSubnet\"],\"name\":\"bandwidth_per_source_subnet\",\"type\":\"counter\",\"valueKey\":\"Bytes\"},{\"buckets\":null,\"filter\":{\"key\":\"\",\"value\":\"\"},\"labels\":[\"Service\"],\"name\":\"network_service_total\",\"type\":\"counter\",\"valueKey\":\"\"}],\"prefix\":\"netobserv_\"},\"type\":\"prom\"},\"name\":\"prometheus\"}]",
+    "Parameters": "[{\"ingest\":{\"grpc\":{\"port\":2055},\"type\":\"grpc\"},\"name\":\"grpc\"},{\"name\":\"enrich\",\"transform\":{\"network\":{\"rules\":[{\"input\":\"SrcAddr\",\"output\":\"SrcK8S\",\"type\":\"add_kubernetes\"},{\"input\":\"DstAddr\",\"output\":\"DstK8S\",\"type\":\"add_kubernetes\"},{\"input\":\"DstPort\",\"output\":\"Service\",\"parameters\":\"Proto\",\"type\":\"add_service\"},{\"input\":\"SrcAddr\",\"output\":\"SrcSubnet\",\"parameters\":\"/16\",\"type\":\"add_subnet\"}]},\"type\":\"network\"}},{\"name\":\"loki\",\"write\":{\"loki\":{\"batchSize\":102400,\"batchWait\":\"1s\",\"clientConfig\":{\"follow_redirects\":false,\"proxy_url\":null,\"tls_config\":{\"insecure_skip_verify\":false}},\"labels\":[\"SrcK8S_Namespace\",\"SrcK8S_OwnerName\",\"DstK8S_Namespace\",\"DstK8S_OwnerName\",\"FlowDirection\"],\"maxBackoff\":\"5m0s\",\"maxRetries\":10,\"minBackoff\":\"1s\",\"staticLabels\":{\"app\":\"netobserv-flowcollector\"},\"tenantID\":\"netobserv\",\"timeout\":\"10s\",\"timestampLabel\":\"TimeFlowEndMs\",\"timestampScale\":\"1ms\",\"url\":\"http://loki.netobserv.svc:3100/\"},\"type\":\"loki\"}},{\"encode\":{\"prom\":{\"metrics\":[{\"buckets\":null,\"labels\":[\"Service\",\"SrcK8S_Namespace\"],\"name\":\"bandwidth_per_network_service_per_namespace\",\"type\":\"counter\",\"valueKey\":\"Bytes\"},{\"buckets\":null,\"labels\":[\"SrcSubnet\"],\"name\":\"bandwidth_per_source_subnet\",\"type\":\"counter\",\"valueKey\":\"Bytes\"},{\"buckets\":null,\"labels\":[\"Service\"],\"name\":\"network_service_total\",\"type\":\"counter\",\"valueKey\":\"\"}],\"prefix\":\"netobserv_\"},\"type\":\"prom\"},\"name\":\"prometheus\"}]",
     "Health": {
         "Port": "8080"
     },
diff --git a/pkg/config/pipeline_builder_test.go b/pkg/config/pipeline_builder_test.go
index d2ef9ebe..d5670de4 100644
--- a/pkg/config/pipeline_builder_test.go
+++ b/pkg/config/pipeline_builder_test.go
@@ -166,7 +166,7 @@ func TestKafkaPromPipeline(t *testing.T) {
 
 	b, err = json.Marshal(params[4])
 	require.NoError(t, err)
-	require.JSONEq(t, `{"name":"prom","encode":{"type":"prom","prom":{"expiryTime":"50s", "metrics":[{"name":"connections_per_source_as","type":"counter","filter":{"key":"","type":"","value":""},"filters":[{"key":"name","type":"","value":"src_as_connection_count"}],"valueKey":"recent_count","valueScale":0,"labels":["by","aggregate"],"buckets":[]}],"prefix":"flp_"}}}`, string(b))
+	require.JSONEq(t, `{"name":"prom","encode":{"type":"prom","prom":{"expiryTime":"50s", "metrics":[{"name":"connections_per_source_as","type":"counter","filters":[{"key":"name","type":"","value":"src_as_connection_count"}],"valueKey":"recent_count","valueScale":0,"labels":["by","aggregate"],"buckets":[]}],"prefix":"flp_"}}}`, string(b))
 }
 
 func TestForkPipeline(t *testing.T) {
diff --git a/pkg/pipeline/aggregate_prom_test.go b/pkg/pipeline/aggregate_prom_test.go
index 6c820682..776fc3c8 100644
--- a/pkg/pipeline/aggregate_prom_test.go
+++ b/pkg/pipeline/aggregate_prom_test.go
@@ -68,19 +68,19 @@ parameters:
        metrics:
          - name: flow_count
            type: counter
-           filter: {key: name, value: bandwidth_count}
+           filters: [{key: name, value: bandwidth_count}]
            valueKey: recent_count
            labels:
              - service
          - name: bytes_sum
            type: counter
-           filter: {key: name, value: bandwidth_sum}
+           filters: [{key: name, value: bandwidth_sum}]
            valueKey: recent_op_value
            labels:
              - service
          - name: bytes_histogram
            type: agg_histogram
-           filter: {key: name, value: bandwidth_raw_values}
+           filters: [{key: name, value: bandwidth_raw_values}]
            valueKey: recent_raw_values
            labels:
              - service
diff --git a/pkg/pipeline/encode/encode_prom_test.go b/pkg/pipeline/encode/encode_prom_test.go
index 6a2f81a8..0820a81b 100644
--- a/pkg/pipeline/encode/encode_prom_test.go
+++ b/pkg/pipeline/encode/encode_prom_test.go
@@ -46,7 +46,7 @@ parameters:
         metrics:
           - name: Bytes
             type: gauge
-            filter: {key: dstAddr, value: 10.1.2.4}
+            filters: [{key: dstAddr, value: 10.1.2.4}]
             valueKey: bytes
             labels:
               - srcAddr
@@ -54,7 +54,7 @@ parameters:
               - srcPort
           - name: Packets
             type: counter
-            filter: {key: dstAddr, value: 10.1.2.4}
+            filters: [{key: dstAddr, value: 10.1.2.4}]
             valueKey: packets
             labels:
               - srcAddr
@@ -93,30 +93,30 @@ func Test_NewEncodeProm(t *testing.T) {
 	encodeProm, err := initProm(cfg.Parameters[0].Encode.Prom)
 	require.NoError(t, err)
 
-	require.Equal(t, 1, len(encodeProm.counters))
-	require.Equal(t, 1, len(encodeProm.gauges))
-	require.Equal(t, 1, len(encodeProm.histos))
-	require.Equal(t, 1, len(encodeProm.aggHistos))
-	require.Equal(t, time.Second, encodeProm.expiryTime)
+	require.Equal(t, 1, len(encodeProm.metricCommon.counters))
+	require.Equal(t, 1, len(encodeProm.metricCommon.gauges))
+	require.Equal(t, 1, len(encodeProm.metricCommon.histos))
+	require.Equal(t, 1, len(encodeProm.metricCommon.aggHistos))
+	require.Equal(t, time.Second, encodeProm.metricCommon.expiryTime)
 
-	require.Equal(t, encodeProm.gauges[0].info.Name, "Bytes")
+	require.Equal(t, encodeProm.metricCommon.gauges[0].info.Name, "Bytes")
 	expectedList := []string{"srcAddr", "dstAddr", "srcPort"}
-	require.Equal(t, encodeProm.gauges[0].info.Labels, expectedList)
+	require.Equal(t, encodeProm.metricCommon.gauges[0].info.Labels, expectedList)
 
-	require.Equal(t, encodeProm.counters[0].info.Name, "Packets")
+	require.Equal(t, encodeProm.metricCommon.counters[0].info.Name, "Packets")
 	expectedList = []string{"srcAddr", "dstAddr", "dstPort"}
-	require.Equal(t, encodeProm.counters[0].info.Labels, expectedList)
+	require.Equal(t, encodeProm.metricCommon.counters[0].info.Labels, expectedList)
 	entry := test.GetExtractMockEntry()
 	encodeProm.Encode(entry)
 
 	// verify entries are in cache; one for the gauge and one for the counter
-	entriesMapLen := encodeProm.mCache.GetCacheLen()
+	entriesMapLen := encodeProm.metricCommon.mCache.GetCacheLen()
 	require.Equal(t, 2, entriesMapLen)
 
 	// wait a couple seconds so that the entry will expire
 	time.Sleep(2 * time.Second)
-	encodeProm.mCache.CleanupExpiredEntries(encodeProm.expiryTime, encodeProm.Cleanup)
-	entriesMapLen = encodeProm.mCache.GetCacheLen()
+	encodeProm.metricCommon.mCache.CleanupExpiredEntries(encodeProm.metricCommon.expiryTime, encodeProm.Cleanup)
+	entriesMapLen = encodeProm.metricCommon.mCache.GetCacheLen()
 	require.Equal(t, 0, entriesMapLen)
 }
 
@@ -361,7 +361,7 @@ func Test_FilterDirection(t *testing.T) {
 				Name:     "ingress_or_inner_packets_total",
 				Type:     "counter",
 				ValueKey: "packets",
-				Filters:  []api.MetricsFilter{{Key: "dir", Value: "0|2", Type: "regex"}},
+				Filters:  []api.MetricsFilter{{Key: "dir", Value: "0|2", Type: "match_regex"}},
 			},
 		},
 	}
@@ -380,6 +380,63 @@ func Test_FilterDirection(t *testing.T) {
 	require.Contains(t, exposed, `test_ingress_or_inner_packets_total 1010`)
 }
 
+func Test_FilterSameOrDifferentNamespace(t *testing.T) {
+	metrics := []config.GenericMap{
+		{
+			"src-ns":  "a",
+			"dst-ns":  "b",
+			"packets": 10,
+		},
+		{
+			"src-ns":  "b",
+			"dst-ns":  "a",
+			"packets": 200,
+		},
+		{
+			"src-ns":  "a",
+			"dst-ns":  "a",
+			"packets": 3000,
+		},
+		{
+			"src-ns":  "b",
+			"dst-ns":  "b",
+			"packets": 40000,
+		},
+	}
+	params := api.PromEncode{
+		Prefix: "test_",
+		ExpiryTime: api.Duration{
+			Duration: time.Duration(60 * time.Second),
+		},
+		Metrics: []api.MetricsItem{
+			{
+				Name:     "packets_same_namespace_total",
+				Type:     "counter",
+				ValueKey: "packets",
+				Filters:  []api.MetricsFilter{{Key: "src-ns", Value: "$(dst-ns)"}},
+			},
+			{
+				Name:     "packets_different_namespace_total",
+				Type:     "counter",
+				ValueKey: "packets",
+				Filters:  []api.MetricsFilter{{Key: "src-ns", Value: "$(dst-ns)", Type: "not_equal"}},
+			},
+		},
+	}
+
+	encodeProm, err := initProm(&params)
+	require.NoError(t, err)
+	for _, metric := range metrics {
+		encodeProm.Encode(metric)
+	}
+	time.Sleep(100 * time.Millisecond)
+
+	exposed := test.ReadExposedMetrics(t)
+
+	require.Contains(t, exposed, `test_packets_same_namespace_total 43000`)
+	require.Contains(t, exposed, `test_packets_different_namespace_total 210`)
+}
+
 func Test_ValueScale(t *testing.T) {
 	metrics := []config.GenericMap{{"rtt": 15_000_000} /*15ms*/, {"rtt": 110_000_000} /*110ms*/}
 	params := api.PromEncode{
@@ -631,18 +688,18 @@ func Test_MultipleProm(t *testing.T) {
 	encodeProm3, err := initProm(cfg.Parameters[2].Encode.Prom)
 	require.NoError(t, err)
 
-	require.Equal(t, 0, len(encodeProm1.counters))
-	require.Equal(t, 1, len(encodeProm1.gauges))
-	require.Equal(t, 0, len(encodeProm1.histos))
-	require.Equal(t, 0, len(encodeProm1.aggHistos))
-	require.Equal(t, 1, len(encodeProm2.counters))
-	require.Equal(t, 0, len(encodeProm2.gauges))
-	require.Equal(t, 0, len(encodeProm2.histos))
-	require.Equal(t, 0, len(encodeProm2.aggHistos))
-	require.Equal(t, 0, len(encodeProm3.counters))
-	require.Equal(t, 0, len(encodeProm3.gauges))
-	require.Equal(t, 1, len(encodeProm3.histos))
-	require.Equal(t, 0, len(encodeProm3.aggHistos))
+	require.Equal(t, 0, len(encodeProm1.metricCommon.counters))
+	require.Equal(t, 1, len(encodeProm1.metricCommon.gauges))
+	require.Equal(t, 0, len(encodeProm1.metricCommon.histos))
+	require.Equal(t, 0, len(encodeProm1.metricCommon.aggHistos))
+	require.Equal(t, 1, len(encodeProm2.metricCommon.counters))
+	require.Equal(t, 0, len(encodeProm2.metricCommon.gauges))
+	require.Equal(t, 0, len(encodeProm2.metricCommon.histos))
+	require.Equal(t, 0, len(encodeProm2.metricCommon.aggHistos))
+	require.Equal(t, 0, len(encodeProm3.metricCommon.counters))
+	require.Equal(t, 0, len(encodeProm3.metricCommon.gauges))
+	require.Equal(t, 1, len(encodeProm3.metricCommon.histos))
+	require.Equal(t, 0, len(encodeProm3.metricCommon.aggHistos))
 
 	require.Equal(t, "test1_", encodeProm1.cfg.Prefix)
 	require.Equal(t, "test2_", encodeProm2.cfg.Prefix)
@@ -650,3 +707,12 @@ func Test_MultipleProm(t *testing.T) {
 
 	// TODO: Add test for different addresses, but need to deal with StartPromServer (ListenAndServe)
 }
+
+func Test_Filters_extractVarLookups(t *testing.T) {
+	variables := extractVarLookups("$(abc)--$(def)")
+
+	require.Equal(t, [][]string{{"$(abc)", "abc"}, {"$(def)", "def"}}, variables)
+
+	variables = extractVarLookups("")
+	require.Empty(t, variables)
+}
diff --git a/pkg/pipeline/encode/opentelemetry/encode_otlp_test.go b/pkg/pipeline/encode/opentelemetry/encode_otlp_test.go
index 5d882ebe..1864f328 100644
--- a/pkg/pipeline/encode/opentelemetry/encode_otlp_test.go
+++ b/pkg/pipeline/encode/opentelemetry/encode_otlp_test.go
@@ -50,10 +50,10 @@ type fakeOltpLoggerProvider struct {
 type fakeOltpLogger struct {
 }
 
-var receivedData []logs.LogRecord
+var otlpReceivedData []logs.LogRecord
 
 func (f *fakeOltpLogger) Emit(msg logs.LogRecord) {
-	receivedData = append(receivedData, msg)
+	otlpReceivedData = append(otlpReceivedData, msg)
 }
 
 func (f *fakeOltpLoggerProvider) Logger(name string, options ...logs.LoggerOption) logs.Logger {
@@ -61,7 +61,7 @@ func (f *fakeOltpLoggerProvider) Logger(name string, options ...logs.LoggerOptio
 }
 
 func initNewEncodeOtlpLogs(t *testing.T) encode.Encoder {
-	receivedData = []logs.LogRecord{}
+	otlpReceivedData = []logs.LogRecord{}
 	v, cfg := test.InitConfig(t, testOtlpConfig)
 	require.NotNil(t, v)
 
@@ -88,14 +88,14 @@ func Test_EncodeOtlpLogs(t *testing.T) {
 	newEncode.Encode(entry1)
 	newEncode.Encode(entry2)
 	// verify contents of the output
-	require.Len(t, receivedData, 2)
+	require.Len(t, otlpReceivedData, 2)
 	expected1, _ := json.Marshal(entry1)
 	expected2, _ := json.Marshal(entry2)
-	require.Equal(t, string(expected1), *receivedData[0].Body())
-	require.Equal(t, string(expected2), *receivedData[1].Body())
+	require.Equal(t, string(expected1), *otlpReceivedData[0].Body())
+	require.Equal(t, string(expected2), *otlpReceivedData[1].Body())
 }
 
-func Test_TLSConfigEmpty(t *testing.T) {
+func Test_EncodeOtlpTLSConfigEmpty(t *testing.T) {
 	cfg := config.StageParam{
 		Encode: &config.Encode{
 			OtlpLogs: &api.EncodeOtlpLogs{OtlpConnectionInfo: &api.OtlpConnectionInfo{
@@ -111,7 +111,7 @@ func Test_TLSConfigEmpty(t *testing.T) {
 	require.NotNil(t, newEncode)
 }
 
-func Test_TLSConfigNotEmpty(t *testing.T) {
+func Test_EncodeOtlpTLSConfigNotEmpty(t *testing.T) {
 	cfg := config.StageParam{
 		Encode: &config.Encode{
 			OtlpLogs: &api.EncodeOtlpLogs{OtlpConnectionInfo: &api.OtlpConnectionInfo{
@@ -193,9 +193,5 @@ func Test_EncodeOtlpMetrics(t *testing.T) {
 	newEncode, err := NewEncodeOtlpMetrics(operational.NewMetrics(&config.MetricsSettings{}), cfg)
 	require.NoError(t, err)
 	require.NotNil(t, newEncode)
-	em := newEncode.(*EncodeOtlpMetrics)
-	require.Equal(t, 2, len(em.counters))
-	require.Equal(t, 1, len(em.gauges))
-	require.Equal(t, "metric3", em.counters[1].info.Name)
-	require.Equal(t, []string{"label21", "label22"}, em.gauges[0].info.Labels)
+	// TODO: add more tests
 }
diff --git a/pkg/pipeline/encode/prom_cache_test.go b/pkg/pipeline/encode/prom_cache_test.go
index bab6d71e..649b310e 100644
--- a/pkg/pipeline/encode/prom_cache_test.go
+++ b/pkg/pipeline/encode/prom_cache_test.go
@@ -113,12 +113,12 @@ func Test_Prom_Cache1(t *testing.T) {
 	entries = utils.GenerateConnectionFlowEntries(10)
 	require.Equal(t, 10, len(entries))
 	encodeEntries(promEncode, entries)
-	require.Equal(t, 10, promEncode.mCache.GetCacheLen())
+	require.Equal(t, 10, promEncode.metricCommon.mCache.GetCacheLen())
 
 	entries = utils.GenerateConnectionFlowEntries(40)
 	require.Equal(t, 40, len(entries))
 	encodeEntries(promEncode, entries)
-	require.Equal(t, 30, promEncode.mCache.GetCacheLen())
+	require.Equal(t, 30, promEncode.metricCommon.mCache.GetCacheLen())
 }
 
 func Test_Prom_Cache2(t *testing.T) {
@@ -133,12 +133,12 @@ func Test_Prom_Cache2(t *testing.T) {
 	entries = utils.GenerateConnectionFlowEntries(10)
 	require.Equal(t, 10, len(entries))
 	encodeEntries(promEncode, entries)
-	require.Equal(t, 20, promEncode.mCache.GetCacheLen())
+	require.Equal(t, 20, promEncode.metricCommon.mCache.GetCacheLen())
 
 	entries = utils.GenerateConnectionFlowEntries(40)
 	require.Equal(t, 40, len(entries))
 	encodeEntries(promEncode, entries)
-	require.Equal(t, 30, promEncode.mCache.GetCacheLen())
+	require.Equal(t, 30, promEncode.metricCommon.mCache.GetCacheLen())
 }
 
 func Test_Prom_Cache3(t *testing.T) {
@@ -153,10 +153,10 @@ func Test_Prom_Cache3(t *testing.T) {
 	entries = utils.GenerateConnectionFlowEntries(10)
 	require.Equal(t, 10, len(entries))
 	encodeEntries(promEncode, entries)
-	require.Equal(t, 20, promEncode.mCache.GetCacheLen())
+	require.Equal(t, 20, promEncode.metricCommon.mCache.GetCacheLen())
 
 	entries = utils.GenerateConnectionFlowEntries(40)
 	require.Equal(t, 40, len(entries))
 	encodeEntries(promEncode, entries)
-	require.Equal(t, 80, promEncode.mCache.GetCacheLen())
+	require.Equal(t, 80, promEncode.metricCommon.mCache.GetCacheLen())
 }
diff --git a/pkg/pipeline/write/testnorace/write_ipfix_test.go b/pkg/pipeline/write/testnorace/write_ipfix_test.go
new file mode 100644
index 00000000..075764d4
--- /dev/null
+++ b/pkg/pipeline/write/testnorace/write_ipfix_test.go
@@ -0,0 +1,222 @@
+package testnorace
+
+import (
+	"context"
+	"fmt"
+	"net"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/netobserv/flowlogs-pipeline/pkg/api"
+	"github.com/netobserv/flowlogs-pipeline/pkg/config"
+	"github.com/netobserv/flowlogs-pipeline/pkg/pipeline/write"
+	"github.com/netobserv/netobserv-ebpf-agent/pkg/decode"
+	"github.com/netobserv/netobserv-ebpf-agent/pkg/pbflow"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"github.com/vmware/go-ipfix/pkg/collector"
+	"github.com/vmware/go-ipfix/pkg/entities"
+	"google.golang.org/protobuf/types/known/durationpb"
+	"google.golang.org/protobuf/types/known/timestamppb"
+	"k8s.io/apimachinery/pkg/util/wait"
+)
+
+var (
+	startTime  = time.Now()
+	endTime    = startTime.Add(7 * time.Second)
+	FullPBFlow = pbflow.Record{
+		Direction: pbflow.Direction_EGRESS,
+		Bytes:     1024,
+		DataLink: &pbflow.DataLink{
+			DstMac: 0x112233445566,
+			SrcMac: 0x010203040506,
+		},
+		Network: &pbflow.Network{
+			SrcAddr: &pbflow.IP{
+				IpFamily: &pbflow.IP_Ipv4{Ipv4: 0x01020304},
+			},
+			DstAddr: &pbflow.IP{
+				IpFamily: &pbflow.IP_Ipv4{Ipv4: 0x05060708},
+			},
+			Dscp: 1,
+		},
+		Duplicate:   false,
+		EthProtocol: 2048,
+		Packets:     3,
+		Transport: &pbflow.Transport{
+			Protocol: 17,
+			SrcPort:  23000,
+			DstPort:  443,
+		},
+		TimeFlowStart: timestamppb.New(startTime),
+		TimeFlowEnd:   timestamppb.New(endTime),
+		Interface:     "eth0",
+		AgentIp: &pbflow.IP{
+			IpFamily: &pbflow.IP_Ipv4{Ipv4: 0x0a090807},
+		},
+		PktDropBytes:           15,
+		PktDropPackets:         1,
+		PktDropLatestFlags:     1,
+		PktDropLatestState:     1,
+		PktDropLatestDropCause: 5,
+		Flags:                  0x110,
+		DnsId:                  123,
+		DnsFlags:               0x80,
+		DnsErrno:               0,
+		DnsLatency:             durationpb.New(150 * time.Millisecond),
+		TimeFlowRtt:            durationpb.New(20 * time.Millisecond),
+		IcmpCode:               0,
+		IcmpType:               0,
+		DupList: []*pbflow.DupMapEntry{
+			{
+				Interface: "eth0",
+				Direction: pbflow.Direction_EGRESS,
+			},
+		},
+	}
+)
+
+func TestEnrichedIPFIXFlow(t *testing.T) {
+	cp := startCollector(t)
+	addr := cp.GetAddress().(*net.UDPAddr)
+
+	flow := decode.PBFlowToMap(&FullPBFlow)
+
+	// Add enrichment
+	flow["SrcK8S_Name"] = "pod A"
+	flow["SrcK8S_Namespace"] = "ns1"
+	flow["SrcK8S_HostName"] = "node1"
+	flow["DstK8S_Name"] = "pod B"
+	flow["DstK8S_Namespace"] = "ns2"
+	flow["DstK8S_HostName"] = "node2"
+
+	writer, err := write.NewWriteIpfix(config.StageParam{
+		Write: &config.Write{
+			Ipfix: &api.WriteIpfix{
+				TargetHost:   addr.IP.String(),
+				TargetPort:   addr.Port,
+				Transport:    addr.Network(),
+				EnterpriseID: 9999,
+			},
+		},
+	})
+	require.NoError(t, err)
+
+	writer.Write(flow)
+
+	// Read collector
+	// 1st = IPv4 template
+	tplv4Msg := <-cp.GetMsgChan()
+	// 2nd = IPv6 template (ignore)
+	<-cp.GetMsgChan()
+	// 3rd = data record
+	dataMsg := <-cp.GetMsgChan()
+	cp.CloseMsgChan()
+	cp.Stop()
+
+	// Check template
+	assert.Equal(t, uint16(10), tplv4Msg.GetVersion())
+	templateSet := tplv4Msg.GetSet()
+	templateElements := templateSet.GetRecords()[0].GetOrderedElementList()
+	assert.Len(t, templateElements, 21)
+	assert.Equal(t, uint32(0), templateElements[0].GetInfoElement().EnterpriseId)
+
+	// Check data
+	assert.Equal(t, uint16(10), dataMsg.GetVersion())
+	dataSet := dataMsg.GetSet()
+	record := dataSet.GetRecords()[0]
+
+	expectedFields := append(write.IPv4IANAFields, write.KubeFields...)
+	expectedFields = append(expectedFields, write.CustomNetworkFields...)
+
+	for _, name := range expectedFields {
+		element, _, exist := record.GetInfoElementWithValue(name)
+		assert.Truef(t, exist, "element with name %s should exist in the record", name)
+		assert.NotNil(t, element)
+		matchElement(t, element, flow)
+	}
+}
+
+func matchElement(t *testing.T, element entities.InfoElementWithValue, flow config.GenericMap) {
+	switch element.GetName() {
+	case "sourceIPv4Address":
+		assert.Equal(t, flow["SrcAddr"], element.GetIPAddressValue().String())
+	case "destinationIPv4Address":
+		assert.Equal(t, flow["DstAddr"], element.GetIPAddressValue().String())
+	case "ethernetType":
+		assert.Equal(t, flow["Etype"], uint32(element.GetUnsigned16Value()))
+	case "flowDirection":
+		assert.Equal(t, flow["FlowDirection"], int(element.GetUnsigned8Value()))
+	case "protocolIdentifier":
+		assert.Equal(t, flow["Proto"], uint32(element.GetUnsigned8Value()))
+	case "sourceTransportPort":
+		assert.Equal(t, flow["SrcPort"], uint32(element.GetUnsigned16Value()))
+	case "destinationTransportPort":
+		assert.Equal(t, flow["DstPort"], uint32(element.GetUnsigned16Value()))
+	case "octetDeltaCount":
+		assert.Equal(t, flow["Bytes"], element.GetUnsigned64Value())
+	case "flowStartMilliseconds":
+		assert.Equal(t, flow["TimeFlowStartMs"], int64(element.GetUnsigned64Value()))
+	case "flowEndMilliseconds":
+		assert.Equal(t, flow["TimeFlowEndMs"], int64(element.GetUnsigned64Value()))
+	case "packetDeltaCount":
+		assert.Equal(t, flow["Packets"], element.GetUnsigned64Value())
+	case "interfaceName":
+		assert.Equal(t, flow["Interface"], element.GetStringValue())
+	case "sourcePodNamespace":
+		assert.Equal(t, flow["SrcK8S_Namespace"], element.GetStringValue())
+	case "sourcePodName":
+		assert.Equal(t, flow["SrcK8S_Name"], element.GetStringValue())
+	case "destinationPodNamespace":
+		assert.Equal(t, flow["DstK8S_Namespace"], element.GetStringValue())
+	case "destinationPodName":
+		assert.Equal(t, flow["DstK8S_Name"], element.GetStringValue())
+	case "sourceNodeName":
+		assert.Equal(t, flow["SrcK8S_HostName"], element.GetStringValue())
+	case "destinationNodeName":
+		assert.Equal(t, flow["DstK8S_HostName"], element.GetStringValue())
+	case "timeFlowRttNs":
+		assert.Equal(t, uint64(flow["TimeFlowRttNs"].(int64)), element.GetUnsigned64Value())
+	case "sourceMacAddress":
+	case "destinationMacAddress":
+		// Getting some discrepancies here, need to figure out why
+	default:
+		assert.Fail(t, "missing check on element", element.GetName())
+	}
+}
+
+func startCollector(t *testing.T) *collector.CollectingProcess {
+	address, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
+	require.NoError(t, err)
+
+	cp, err := collector.InitCollectingProcess(collector.CollectorInput{
+		Address:       address.String(),
+		Protocol:      address.Network(),
+		MaxBufferSize: 2048,
+		TemplateTTL:   0,
+		ServerCert:    nil,
+		ServerKey:     nil,
+	})
+	require.NoError(t, err)
+
+	go cp.Start()
+
+	// Wait for collector to be ready
+	checkConn := func(ctx context.Context) (bool, error) {
+		addr := cp.GetAddress()
+		if addr == nil || strings.HasSuffix(addr.String(), ":0") {
+			return false, fmt.Errorf("random port is not resolved")
+		}
+		conn, err := net.Dial(cp.GetAddress().Network(), cp.GetAddress().String())
+		if err != nil {
+			return false, err
+		}
+		conn.Close()
+		return true, nil
+	}
+	err = wait.PollUntilContextTimeout(context.TODO(), 100*time.Millisecond, 2*time.Second, false, checkConn)
+	require.NoError(t, err, "Connection timeout in collector setup")
+
+	return cp
+}