From 820f787c7cd84c1a3119e024d1d24731d762d37a Mon Sep 17 00:00:00 2001 From: Pooja Bhagat Date: Mon, 29 Dec 2025 14:29:37 +0530 Subject: [PATCH 1/2] Working metrics export --- README.md | 25 + WORKSPACE | 1 + maven_install.json | 734 +++++++++++++++++- pom.xml | 12 + src/main/java/com/glean/proxy/BUILD.bazel | 1 + .../com/glean/proxy/FilterConfiguration.java | 10 +- .../java/com/glean/proxy/ProxyNetworking.java | 35 + .../java/com/glean/proxy/filters/BUILD.bazel | 1 + .../glean/proxy/filters/CompositeFilter.java | 12 + .../proxy/filters/ProxyMetricsFilter.java | 48 ++ .../java/com/glean/proxy/metrics/BUILD.bazel | 16 + .../com/glean/proxy/metrics/MetricData.java | 151 ++++ .../com/glean/proxy/metrics/ProxyMetrics.java | 38 + .../proxy/metrics/ProxyMetricsExporter.java | 278 +++++++ 14 files changed, 1349 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/glean/proxy/filters/ProxyMetricsFilter.java create mode 100644 src/main/java/com/glean/proxy/metrics/BUILD.bazel create mode 100644 src/main/java/com/glean/proxy/metrics/MetricData.java create mode 100644 src/main/java/com/glean/proxy/metrics/ProxyMetrics.java create mode 100644 src/main/java/com/glean/proxy/metrics/ProxyMetricsExporter.java diff --git a/README.md b/README.md index 5d80253..923ef4d 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,13 @@ GleanProxy is configured through environment variables. By default, the set of f |---------------------|-------------|---------| | `CLOUD_PLATFORM` | Cloud platform (`AWS` or `GOOGLE`) | None | +### Metrics Configuration + +| Variable | Description | Default | +|---------------------|-------------|---------| +| `GCP_PROJECT_ID` | GCP project ID for Stackdriver metrics export | None | +| `ENABLE_METRICS_EXPORT` | Enable/disable metrics export to Stackdriver (true/false) | Enabled if `GCP_PROJECT_ID` is set | + ### Filter Configuration Variables | Variable | Description | Default | @@ -178,6 +185,24 @@ curl localhost:8080/liveness_check curl --proxy localhost:8080 https://www.glean.com ``` +### Running with Metrics Export + +To export metrics to GCP Cloud Monitoring (Stackdriver): + +```bash +export CLOUD_PLATFORM=GOOGLE +export GCP_PROJECT_ID=your-gcp-project-id +export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json + +bazel run //src/main/java/com/glean/proxy:ProxyMain 8080 +``` + +Metrics are exported every 60 seconds and include: +- `proxy/request_count` - Total number of requests (labeled by status_code, method) +- `proxy/request_latency` - Request latency distribution in milliseconds (labeled by status_code, method) + +To run locally without exporting metrics, omit `GCP_PROJECT_ID` or set `ENABLE_METRICS_EXPORT=false`. + ## Contributing Please see [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines. diff --git a/WORKSPACE b/WORKSPACE index b6dfe0f..4f85147 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -199,6 +199,7 @@ maven_install( # This was downgraded, so we can keep netty at 4.1.* "io.github.littleproxy:littleproxy:2.4.0", "io.netty:netty-codec-http:4.1.127.Final", + "com.google.cloud:google-cloud-monitoring:3.52.0", "org.apache.httpcomponents:httpclient:4.5.14", "com.uber.nullaway:nullaway:0.10.9", "org.mockito:mockito-core:5.12.0", diff --git a/maven_install.json b/maven_install.json index 9a26ba3..db0df54 100755 --- a/maven_install.json +++ b/maven_install.json @@ -1,8 +1,81 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -610273456, - "__RESOLVED_ARTIFACTS_HASH": -747619382, + "__INPUT_ARTIFACTS_HASH": 1879168519, + "__RESOLVED_ARTIFACTS_HASH": 833789247, + "conflict_resolution": { + "com.google.code.gson:gson:2.10.1": "com.google.code.gson:gson:2.11.0" + }, "artifacts": { + "com.google.android:annotations": { + "shasums": { + "jar": "ba734e1e84c09d615af6a09d33034b4f0442f8772dec120efb376d86a565ae15", + "sources": "e9b667aa958df78ea1ad115f7bbac18a5869c3128b1d5043feb360b0cfce9d40" + }, + "version": "4.1.1.4" + }, + "com.google.api.grpc:proto-google-cloud-monitoring-v3": { + "shasums": { + "jar": "8238b3216aa59ffeed9a157833d7fcc844a7dc9f101b23d728f1cdec6c5c3ba2", + "sources": "2d4bf6619df590be4904c3e7dfe763b32f559dd53251211e0a64f708c0ddff6b" + }, + "version": "3.52.0" + }, + "com.google.api.grpc:proto-google-common-protos": { + "shasums": { + "jar": "8b67033e6419b3810f2ee5afbb80ea2b34f1c3baa4e8f9e5b630320fb3607039", + "sources": "a4238ae00a7f482df3f1a54e989bdbddd66507f8ad66c4c1809b6e89983c531b" + }, + "version": "2.45.1" + }, + "com.google.api:api-common": { + "shasums": { + "jar": "429a441ae9b709e3e04e708efe91aaaecc24d1e6252595fbbaa7f9a2f05f992a", + "sources": "1ca3dedc720ac9aa2e4c7fff20bd3fc24c222feccc8fced8cf32fd120a61529d" + }, + "version": "2.37.1" + }, + "com.google.api:gax": { + "shasums": { + "jar": "dd0f83c3a98557f8c88d7803a1365554d28709c0e1623a2f6644f2301ff976c1", + "sources": "b1a1ea40614421caa891ab2824745ee4da96ae1c9e663c35e6e92903998dc71d" + }, + "version": "2.54.1" + }, + "com.google.api:gax-grpc": { + "shasums": { + "jar": "cedb1c2b224560cb005b30a75617dda9832d4efe206d873aadff6e8667dc1f02", + "sources": "afdf9cb9771cc566b5df1ebf95e2a00e25c6ee1ae476009e90293402d798de61" + }, + "version": "2.54.1" + }, + "com.google.auth:google-auth-library-credentials": { + "shasums": { + "jar": "e4717df3f5ea5b9f910ddb173d42d887c26be5ee76e58c5e3951b2f638ce74e2", + "sources": "ee767a413b5c852575075e18f1ca745b15048fb3853e196d3cc545ebb9d06e23" + }, + "version": "1.27.0" + }, + "com.google.auth:google-auth-library-oauth2-http": { + "shasums": { + "jar": "34887235c78c9221e1d95f1b4117b0bb81a0a5ef7b9e67bf005f19d73a8f74b8", + "sources": "5f3bfb2d9a1ed8fb21ed7d59282ba257596fe64188de1a6b28a31795d264614a" + }, + "version": "1.27.0" + }, + "com.google.auto.value:auto-value-annotations": { + "shasums": { + "jar": "5a055ce4255333b3346e1a8703da5bf8ff049532286fdcd31712d624abe111dd", + "sources": "d7941e5f19bb38afcfa85350d57e5245856c23c98c2bbe32f6d31b5577f2bc33" + }, + "version": "1.11.0" + }, + "com.google.cloud:google-cloud-monitoring": { + "shasums": { + "jar": "9727aa7d94d8dd036bffccf9abc5d18970b1e5e9827896f9907f7450ed10f5e6", + "sources": "5671d4d59e5d22f4a4905d1cb1e8425c89860b30bbb49a9d8730bffb3b78a503" + }, + "version": "3.52.0" + }, "com.google.code.findbugs:jsr305": { "shasums": { "jar": "766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7", @@ -12,10 +85,10 @@ }, "com.google.code.gson:gson": { "shasums": { - "jar": "4241c14a7727c34feea6507ec801318a3d4a90f070e4525681079fb94ee4c593", - "sources": "eee1cc5c1f4267ee194cc245777e68084738ef390acd763354ce0ff6bfb7bcc1" + "jar": "57928d6e5a6edeb2abd3770a8f95ba44dce45f3b23b7a9dc2b309c581552a78b", + "sources": "49a853f71bc874ee1898a4ad5009b57d0c536e5a998b3890253ffbf4b7276ad3" }, - "version": "2.10.1" + "version": "2.11.0" }, "com.google.errorprone:error_prone_annotations": { "shasums": { @@ -45,6 +118,20 @@ }, "version": "9999.0-empty-to-avoid-conflict-with-guava" }, + "com.google.http-client:google-http-client": { + "shasums": { + "jar": "8340bbaf4410bd25921842e424cac3db9c916fdf47adfe73b8cba8dba7a06103", + "sources": "638c13eeffbef4bdb551d705f32885cb87247fa41d2e59754d05b131550a7681" + }, + "version": "1.45.0" + }, + "com.google.http-client:google-http-client-gson": { + "shasums": { + "jar": "44dc1da1b7914fe7df306ac79e8c04d879cbfcb2e6afc8eb106ecf0be9dce96e", + "sources": "faebaf26ab46243ad007fa4c9f569bc5f301dca615f3e58ede2d5aebdbd11852" + }, + "version": "1.45.0" + }, "com.google.j2objc:j2objc-annotations": { "shasums": { "jar": "88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64", @@ -52,6 +139,27 @@ }, "version": "3.0.0" }, + "com.google.protobuf:protobuf-java": { + "shasums": { + "jar": "8540247fad9e06baefa8fb45eb313802d019f485f14300e0f9d6b556ed88e753", + "sources": "d686e802329e42954e72e9b3b148b67eeb4f6f3ed327abc4508b79fda4937c3b" + }, + "version": "3.25.5" + }, + "com.google.protobuf:protobuf-java-util": { + "shasums": { + "jar": "dacc58b2c3d2fa8d4bddc1acb881e78d6cf7c137dd78bc1d67f6aca732436a8d", + "sources": "db5bda981edcda1fd4d60bf4cc7dca9ab144f6b880233fd4d1582fd0065f232c" + }, + "version": "3.25.5" + }, + "com.google.re2j:re2j": { + "shasums": { + "jar": "4f657af51ab8bb0909bcc3eb40862d26125af8cbcf92aaaba595fed77f947bc0", + "sources": "ddc3b47bb1e556ac4c0d02c9d8ff18f3260198b76b720567a70eed0a03d3fed6" + }, + "version": "1.7" + }, "com.uber.nullaway:nullaway": { "shasums": { "jar": "38327fd62ceb0683881d68ee7bf97598133f0287f3c670416f50f85cf8ef418b", @@ -61,10 +169,10 @@ }, "commons-codec:commons-codec": { "shasums": { - "jar": "e599d5318e97aa48f42136a2927e6dfa4e8881dff0e6c8e3109ddbbff51d7b7d", - "sources": "901cb5d1f7c2877017c95d3c5efd5a497738d0162ef72cdf58e9cb13f50b2e9c" + "jar": "f9f6cb103f2ddc3c99a9d80ada2ae7bf0685111fd6bffccb72033d1da4e6ff23", + "sources": "2283aff7f425dff23ebdb7a8fc0f03ae21b4ced7a43aacba47cedae126dc5d4a" }, - "version": "1.11" + "version": "1.17.1" }, "commons-logging:commons-logging": { "shasums": { @@ -80,6 +188,111 @@ }, "version": "2.4.0" }, + "io.grpc:grpc-alts": { + "shasums": { + "jar": "91bf6e78af5d0edae98857a798e47aa0789159774009a64125d46def9aabcbf8", + "sources": "b68f9c241d7786c88472a95e31547b8553832ad8e5d54a5a3699e45cdc598bf9" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-api": { + "shasums": { + "jar": "b5120a11da5ce5ddfab019bbb69f5868529c9b5def1ba5b283251cc95fb3ba91", + "sources": "3e4b31496f2c8b7cd51b425af767c72d44b38fdbb56a6e8c247acb8a721c4e8c" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-auth": { + "shasums": { + "jar": "f686aa689d39175e6ef6cf3dc207df43552653fed7912cfb6c2d31866349d28b", + "sources": "472a101cd3c3aeac559d7cf4a43bb5c3cbee02a7531883844c2108d30ef1f366" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-context": { + "shasums": { + "jar": "45f85a394466f963f1f7a5c5555e6dda35efd05ce1c687203a217d7048f6f089", + "sources": "31d4fc1054b5c0bc75924e82ca425dcf624f895e7525da900b94cfa87a2bea53" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-core": { + "shasums": { + "jar": "a8dd684c322ba8a86a95b2653fa4106c918a56edd863e45aaf63839d6a6ea082", + "sources": "38838e62fbc22a78d40f4214da2e638f60d32e3b67ff7daab35d619d7538beb0" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-googleapis": { + "shasums": { + "jar": "20359d482402ea24de696b94ee27293d3af746fec9dbb479b88d750c57c2397b", + "sources": "e2e87539687f2d790e154c6204d7409d6f29a42674e21f4c9b7c4991620305bd" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-grpclb": { + "shasums": { + "jar": "13a2f7d7c50ef4dc5f3744c0f75341b0c64a2a6dcb2449f8a2089c5ed013751d", + "sources": "488540b83dbb7865de7ae778edbf062cbafbb61e6bbf4b13fd86a4890cefb0e2" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-inprocess": { + "shasums": { + "jar": "11d3649382bf7780a868e129965b929bdb6efcc3de2ef6f629141e79a0366b53", + "sources": "327f4e93e1dd92fd4d1920ee26adfb95f8867374b2fa728332a86c8c78f1ae84" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-netty-shaded": { + "shasums": { + "jar": "de6fd10fef856dbb83ecbd0baee5313a274bb8202adca01091161024e25fd1dc", + "sources": "31d4fc1054b5c0bc75924e82ca425dcf624f895e7525da900b94cfa87a2bea53" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-protobuf": { + "shasums": { + "jar": "79704cf169ee27151fce4375a4d91fe828d276d921eef5a7f497d020b0a5d345", + "sources": "dafd411f98051013a6f37b9f60f9f25e4bdb9a69644f7eec64dd0fbfedc8dd0c" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-protobuf-lite": { + "shasums": { + "jar": "60e92e148b4f86c06885afa79a86beb74bffcdcba47f8b65dc7010ba6701fa80", + "sources": "ae4dfe070f6832dfdce73c3ea093b68165c851d879f9185c55e741b98fef4042" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-services": { + "shasums": { + "jar": "2d3e6911bfec8a20af982a4c14d2bca5bdb1c079af250fb778bc71950c611c15", + "sources": "062e07eb635df35fbb84e98e1dc5bec44dbe7c224b73a37a2de483f41fabc254" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-stub": { + "shasums": { + "jar": "7c4090509cd6ea0432305f9397da21127b691905bdcf163a306bedb1c6f4ead7", + "sources": "4d4046adfc05f2d738cf67fd95275e59c3def5aa77d99596393b9d7d34fc29b1" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-util": { + "shasums": { + "jar": "a97484519d75a6f530b95eab7723318752b2c5febca11fc9141dd78bd09e7eda", + "sources": "516a0590c868c8c46175acdd4a01c7f5d42b12aafacb9aefeb195fa935d9442c" + }, + "version": "1.68.0" + }, + "io.grpc:grpc-xds": { + "shasums": { + "jar": "bcfabb73ba1ab3ea44283c88ed0c8fcdcaa9bdbd54b8fb0cd9faa19388a47f9d", + "sources": "5d3c2392eada53d3fd6b7ff167f2e66ed18b8d616477cd84842e501bbb9a338f" + }, + "version": "1.68.0" + }, "io.netty:netty-all": { "shasums": { "jar": "230fdaa7d53002efb7dc08d4666a999b488858671eaa04357326b66e50b5c0e9", @@ -301,6 +514,34 @@ }, "version": "4.1.116.Final" }, + "io.opencensus:opencensus-api": { + "shasums": { + "jar": "f1474d47f4b6b001558ad27b952e35eda5cc7146788877fc52938c6eba24b382", + "sources": "6748d57aaae81995514ad3e2fb11a95aa88e158b3f93450288018eaccf31e86b" + }, + "version": "0.31.1" + }, + "io.opencensus:opencensus-contrib-http-util": { + "shasums": { + "jar": "3ea995b55a4068be22989b70cc29a4d788c2d328d1d50613a7a9afd13fdd2d0a", + "sources": "d55afd5f96dc724bd903a77a38b0a344d0e59f02a64b9ab2f32618bc582ea924" + }, + "version": "0.31.1" + }, + "io.perfmark:perfmark-api": { + "shasums": { + "jar": "c7b478503ec524e55df19b424d46d27c8a68aeb801664fadd4f069b71f52d0f6", + "sources": "311551ab29cf51e5a8abee6a019e88dee47d1ea71deb9fcd3649db9c51b237bc" + }, + "version": "0.27.0" + }, + "javax.annotation:javax.annotation-api": { + "shasums": { + "jar": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", + "sources": "128971e52e0d84a66e3b6e049dab8ad7b2c58b7e1ad37fa2debd3d40c2947b95" + }, + "version": "1.3.2" + }, "net.bytebuddy:byte-buddy": { "shasums": { "jar": "7472e3961992c12a9fd4f6d67c21de4280abe18f292704dd49d7338289f8acf5", @@ -345,10 +586,10 @@ }, "org.checkerframework:checker-qual": { "shasums": { - "jar": "3fbc2e98f05854c3df16df9abaa955b91b15b3ecac33623208ed6424640ef0f6", - "sources": "d6bdee58964cd05aabfca4e44947d3cbdada6bf617ed618b62b3b0d5a21de339" + "jar": "9840a77c175a95a60c66da8f506871a907a6c4fc8a703bef38c715bc6d4de626", + "sources": "3b6277f0186e31f775c205cfe165f0fe98995e83108b15314e7a4cb93e3f9c96" }, - "version": "3.43.0" + "version": "3.47.0" }, "org.checkerframework:dataflow-nullaway": { "shasums": { @@ -357,6 +598,20 @@ }, "version": "3.26.0" }, + "org.codehaus.mojo:animal-sniffer-annotations": { + "shasums": { + "jar": "c720e6e5bcbe6b2f48ded75a47bccdb763eede79d14330102e0d352e3d89ed92", + "sources": "4270ce5531ed0f12e4234e08f240ef3b45ee3ceeb16e28d44abc61c12cf522ca" + }, + "version": "1.24" + }, + "org.conscrypt:conscrypt-openjdk-uber": { + "shasums": { + "jar": "eaf537d98e033d0f0451cd1b8cc74e02d7b55ec882da63c88060d806ba89c348", + "sources": "aa1d02e65351e202e83ece0614bce1022aa1da6e77313ef7c7663ab45fa9e3a5" + }, + "version": "2.5.2" + }, "org.mockito:mockito-core": { "shasums": { "jar": "4a2eb29237050da749e90a46f948bce7e26ec22b671e41f59b1ac6f4b6408229", @@ -384,9 +639,78 @@ "sources": "8bd6a94a7f610ea9f0180f8b6832bd8b4398f123bea8ad8febe00c36c49e1d1e" }, "version": "2.0.13" + }, + "org.threeten:threetenbp": { + "shasums": { + "jar": "857917d2319a4e92dc1c5e3aeb75a0dac84445ed315e7ac3d82bb8d2b298977f", + "sources": "b4d3602a948a10ea275991d4144c3cbbd9b9000bd3b58dfc7b74b240688ca4a9" + }, + "version": "1.7.0" } }, "dependencies": { + "com.google.api.grpc:proto-google-cloud-monitoring-v3": [ + "com.google.auto.value:auto-value-annotations", + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:listenablefuture", + "com.google.j2objc:j2objc-annotations", + "javax.annotation:javax.annotation-api", + "org.checkerframework:checker-qual" + ], + "com.google.cloud:google-cloud-monitoring": [ + "com.google.android:annotations", + "com.google.api.grpc:proto-google-cloud-monitoring-v3", + "com.google.api.grpc:proto-google-common-protos", + "com.google.api:api-common", + "com.google.api:gax", + "com.google.api:gax-grpc", + "com.google.auth:google-auth-library-credentials", + "com.google.auth:google-auth-library-oauth2-http", + "com.google.auto.value:auto-value-annotations", + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:failureaccess", + "com.google.guava:guava", + "com.google.guava:listenablefuture", + "com.google.http-client:google-http-client", + "com.google.http-client:google-http-client-gson", + "com.google.j2objc:j2objc-annotations", + "com.google.protobuf:protobuf-java", + "com.google.protobuf:protobuf-java-util", + "com.google.re2j:re2j", + "commons-codec:commons-codec", + "io.grpc:grpc-alts", + "io.grpc:grpc-api", + "io.grpc:grpc-auth", + "io.grpc:grpc-context", + "io.grpc:grpc-core", + "io.grpc:grpc-googleapis", + "io.grpc:grpc-grpclb", + "io.grpc:grpc-inprocess", + "io.grpc:grpc-netty-shaded", + "io.grpc:grpc-protobuf", + "io.grpc:grpc-protobuf-lite", + "io.grpc:grpc-services", + "io.grpc:grpc-stub", + "io.grpc:grpc-util", + "io.grpc:grpc-xds", + "io.opencensus:opencensus-api", + "io.opencensus:opencensus-contrib-http-util", + "io.perfmark:perfmark-api", + "javax.annotation:javax.annotation-api", + "org.apache.httpcomponents:httpclient", + "org.apache.httpcomponents:httpcore", + "org.checkerframework:checker-qual", + "org.codehaus.mojo:animal-sniffer-annotations", + "org.conscrypt:conscrypt-openjdk-uber", + "org.threeten:threetenbp" + ], + "com.google.code.gson:gson": [ + "com.google.errorprone:error_prone_annotations" + ], "com.google.guava:guava": [ "com.google.errorprone:error_prone_annotations", "com.google.guava:failureaccess", @@ -571,6 +895,67 @@ "io.netty:netty-all:jar:sources" ], "packages": { + "com.google.android:annotations": [ + "android.annotation" + ], + "com.google.api.grpc:proto-google-cloud-monitoring-v3": [ + "com.google.monitoring.v3" + ], + "com.google.api.grpc:proto-google-common-protos": [ + "com.google.api", + "com.google.apps.card.v1", + "com.google.cloud", + "com.google.cloud.audit", + "com.google.cloud.location", + "com.google.geo.type", + "com.google.logging.type", + "com.google.longrunning", + "com.google.rpc", + "com.google.rpc.context", + "com.google.shopping.type", + "com.google.type" + ], + "com.google.api:api-common": [ + "com.google.api.core", + "com.google.api.pathtemplate", + "com.google.api.resourcenames" + ], + "com.google.api:gax": [ + "com.google.api.gax.batching", + "com.google.api.gax.core", + "com.google.api.gax.longrunning", + "com.google.api.gax.nativeimage", + "com.google.api.gax.paging", + "com.google.api.gax.retrying", + "com.google.api.gax.rpc", + "com.google.api.gax.rpc.internal", + "com.google.api.gax.rpc.mtls", + "com.google.api.gax.tracing", + "com.google.api.gax.util" + ], + "com.google.api:gax-grpc": [ + "com.google.api.gax.grpc", + "com.google.api.gax.grpc.nativeimage", + "com.google.longrunning", + "com.google.longrunning.stub" + ], + "com.google.auth:google-auth-library-credentials": [ + "com.google.auth" + ], + "com.google.auth:google-auth-library-oauth2-http": [ + "com.google.auth.http", + "com.google.auth.oauth2" + ], + "com.google.auto.value:auto-value-annotations": [ + "com.google.auto.value", + "com.google.auto.value.extension.memoized", + "com.google.auto.value.extension.serializable", + "com.google.auto.value.extension.toprettystring" + ], + "com.google.cloud:google-cloud-monitoring": [ + "com.google.cloud.monitoring.v3", + "com.google.cloud.monitoring.v3.stub" + ], "com.google.code.findbugs:jsr305": [ "javax.annotation", "javax.annotation.concurrent", @@ -614,9 +999,40 @@ "com.google.common.xml", "com.google.thirdparty.publicsuffix" ], + "com.google.http-client:google-http-client": [ + "com.google.api.client.http", + "com.google.api.client.http.apache", + "com.google.api.client.http.javanet", + "com.google.api.client.http.json", + "com.google.api.client.json", + "com.google.api.client.json.rpc2", + "com.google.api.client.json.webtoken", + "com.google.api.client.testing.http", + "com.google.api.client.testing.http.apache", + "com.google.api.client.testing.http.javanet", + "com.google.api.client.testing.json", + "com.google.api.client.testing.json.webtoken", + "com.google.api.client.testing.util", + "com.google.api.client.util", + "com.google.api.client.util.escape", + "com.google.api.client.util.store" + ], + "com.google.http-client:google-http-client-gson": [ + "com.google.api.client.json.gson" + ], "com.google.j2objc:j2objc-annotations": [ "com.google.j2objc.annotations" ], + "com.google.protobuf:protobuf-java": [ + "com.google.protobuf", + "com.google.protobuf.compiler" + ], + "com.google.protobuf:protobuf-java-util": [ + "com.google.protobuf.util" + ], + "com.google.re2j:re2j": [ + "com.google.re2j" + ], "com.uber.nullaway:nullaway": [ "com.uber.nullaway", "com.uber.nullaway.annotations", @@ -650,6 +1066,178 @@ "org.littleshoot.proxy.extras", "org.littleshoot.proxy.impl" ], + "io.grpc:grpc-alts": [ + "io.grpc.alts", + "io.grpc.alts.internal" + ], + "io.grpc:grpc-api": [ + "io.grpc" + ], + "io.grpc:grpc-auth": [ + "io.grpc.auth" + ], + "io.grpc:grpc-core": [ + "io.grpc.internal" + ], + "io.grpc:grpc-googleapis": [ + "io.grpc.googleapis" + ], + "io.grpc:grpc-grpclb": [ + "io.grpc.grpclb", + "io.grpc.lb.v1" + ], + "io.grpc:grpc-inprocess": [ + "io.grpc.inprocess" + ], + "io.grpc:grpc-netty-shaded": [ + "io.grpc.netty.shaded.io.grpc.netty", + "io.grpc.netty.shaded.io.netty.bootstrap", + "io.grpc.netty.shaded.io.netty.buffer", + "io.grpc.netty.shaded.io.netty.buffer.search", + "io.grpc.netty.shaded.io.netty.channel", + "io.grpc.netty.shaded.io.netty.channel.embedded", + "io.grpc.netty.shaded.io.netty.channel.epoll", + "io.grpc.netty.shaded.io.netty.channel.group", + "io.grpc.netty.shaded.io.netty.channel.internal", + "io.grpc.netty.shaded.io.netty.channel.local", + "io.grpc.netty.shaded.io.netty.channel.nio", + "io.grpc.netty.shaded.io.netty.channel.oio", + "io.grpc.netty.shaded.io.netty.channel.pool", + "io.grpc.netty.shaded.io.netty.channel.socket", + "io.grpc.netty.shaded.io.netty.channel.socket.nio", + "io.grpc.netty.shaded.io.netty.channel.socket.oio", + "io.grpc.netty.shaded.io.netty.channel.unix", + "io.grpc.netty.shaded.io.netty.handler.address", + "io.grpc.netty.shaded.io.netty.handler.codec", + "io.grpc.netty.shaded.io.netty.handler.codec.base64", + "io.grpc.netty.shaded.io.netty.handler.codec.bytes", + "io.grpc.netty.shaded.io.netty.handler.codec.compression", + "io.grpc.netty.shaded.io.netty.handler.codec.http", + "io.grpc.netty.shaded.io.netty.handler.codec.http.cookie", + "io.grpc.netty.shaded.io.netty.handler.codec.http.cors", + "io.grpc.netty.shaded.io.netty.handler.codec.http.multipart", + "io.grpc.netty.shaded.io.netty.handler.codec.http.websocketx", + "io.grpc.netty.shaded.io.netty.handler.codec.http.websocketx.extensions", + "io.grpc.netty.shaded.io.netty.handler.codec.http.websocketx.extensions.compression", + "io.grpc.netty.shaded.io.netty.handler.codec.http2", + "io.grpc.netty.shaded.io.netty.handler.codec.json", + "io.grpc.netty.shaded.io.netty.handler.codec.marshalling", + "io.grpc.netty.shaded.io.netty.handler.codec.protobuf", + "io.grpc.netty.shaded.io.netty.handler.codec.rtsp", + "io.grpc.netty.shaded.io.netty.handler.codec.serialization", + "io.grpc.netty.shaded.io.netty.handler.codec.socks", + "io.grpc.netty.shaded.io.netty.handler.codec.socksx", + "io.grpc.netty.shaded.io.netty.handler.codec.socksx.v4", + "io.grpc.netty.shaded.io.netty.handler.codec.socksx.v5", + "io.grpc.netty.shaded.io.netty.handler.codec.spdy", + "io.grpc.netty.shaded.io.netty.handler.codec.string", + "io.grpc.netty.shaded.io.netty.handler.codec.xml", + "io.grpc.netty.shaded.io.netty.handler.flow", + "io.grpc.netty.shaded.io.netty.handler.flush", + "io.grpc.netty.shaded.io.netty.handler.ipfilter", + "io.grpc.netty.shaded.io.netty.handler.logging", + "io.grpc.netty.shaded.io.netty.handler.pcap", + "io.grpc.netty.shaded.io.netty.handler.proxy", + "io.grpc.netty.shaded.io.netty.handler.ssl", + "io.grpc.netty.shaded.io.netty.handler.ssl.ocsp", + "io.grpc.netty.shaded.io.netty.handler.ssl.util", + "io.grpc.netty.shaded.io.netty.handler.stream", + "io.grpc.netty.shaded.io.netty.handler.timeout", + "io.grpc.netty.shaded.io.netty.handler.traffic", + "io.grpc.netty.shaded.io.netty.internal.tcnative", + "io.grpc.netty.shaded.io.netty.resolver", + "io.grpc.netty.shaded.io.netty.util", + "io.grpc.netty.shaded.io.netty.util.collection", + "io.grpc.netty.shaded.io.netty.util.concurrent", + "io.grpc.netty.shaded.io.netty.util.internal", + "io.grpc.netty.shaded.io.netty.util.internal.logging", + "io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues", + "io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.atomic", + "io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.util", + "io.grpc.netty.shaded.io.netty.util.internal.svm" + ], + "io.grpc:grpc-protobuf": [ + "io.grpc.protobuf" + ], + "io.grpc:grpc-protobuf-lite": [ + "io.grpc.protobuf.lite" + ], + "io.grpc:grpc-services": [ + "io.grpc.binarylog.v1", + "io.grpc.channelz.v1", + "io.grpc.health.v1", + "io.grpc.protobuf.services", + "io.grpc.protobuf.services.internal", + "io.grpc.reflection.v1", + "io.grpc.reflection.v1alpha", + "io.grpc.services" + ], + "io.grpc:grpc-stub": [ + "io.grpc.stub", + "io.grpc.stub.annotations" + ], + "io.grpc:grpc-util": [ + "io.grpc.util" + ], + "io.grpc:grpc-xds": [ + "io.grpc.xds", + "io.grpc.xds.client", + "io.grpc.xds.internal", + "io.grpc.xds.internal.rbac.engine", + "io.grpc.xds.internal.security", + "io.grpc.xds.internal.security.certprovider", + "io.grpc.xds.internal.security.trust", + "io.grpc.xds.orca", + "io.grpc.xds.shaded.com.github.udpa.udpa.type.v1", + "io.grpc.xds.shaded.com.github.xds.core.v3", + "io.grpc.xds.shaded.com.github.xds.data.orca.v3", + "io.grpc.xds.shaded.com.github.xds.service.orca.v3", + "io.grpc.xds.shaded.com.github.xds.type.matcher.v3", + "io.grpc.xds.shaded.com.github.xds.type.v3", + "io.grpc.xds.shaded.com.google.api.expr.v1alpha1", + "io.grpc.xds.shaded.dev.cel.expr", + "io.grpc.xds.shaded.envoy.annotations", + "io.grpc.xds.shaded.io.envoyproxy.envoy.admin.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.accesslog.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.bootstrap.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.cluster.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.metrics.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.overload.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.rbac.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.config.trace.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.data.accesslog.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.clusters.aggregate.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.common.fault.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.fault.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.rate_limit_quota.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.rbac.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.router.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.client_side_weighted_round_robin.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.common.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.least_request.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.pick_first.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.ring_hash.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.round_robin.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.load_balancing_policies.wrr_locality.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.transport_sockets.tls.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.service.discovery.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.service.load_stats.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.service.rate_limit_quota.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.service.status.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.http.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.matcher.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.metadata.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.tracing.v3", + "io.grpc.xds.shaded.io.envoyproxy.envoy.type.v3", + "io.grpc.xds.shaded.io.envoyproxy.pgv.validate", + "io.grpc.xds.shaded.udpa.annotations", + "io.grpc.xds.shaded.xds.annotations.v3" + ], "io.netty:netty-buffer": [ "io.netty.buffer", "io.netty.buffer.search" @@ -791,6 +1379,37 @@ "io.netty.channel.udt", "io.netty.channel.udt.nio" ], + "io.opencensus:opencensus-api": [ + "io.opencensus.common", + "io.opencensus.internal", + "io.opencensus.metrics", + "io.opencensus.metrics.data", + "io.opencensus.metrics.export", + "io.opencensus.resource", + "io.opencensus.stats", + "io.opencensus.tags", + "io.opencensus.tags.propagation", + "io.opencensus.tags.unsafe", + "io.opencensus.trace", + "io.opencensus.trace.config", + "io.opencensus.trace.export", + "io.opencensus.trace.internal", + "io.opencensus.trace.propagation", + "io.opencensus.trace.samplers", + "io.opencensus.trace.unsafe" + ], + "io.opencensus:opencensus-contrib-http-util": [ + "io.opencensus.contrib.http", + "io.opencensus.contrib.http.util" + ], + "io.perfmark:perfmark-api": [ + "io.perfmark" + ], + "javax.annotation:javax.annotation-api": [ + "javax.annotation", + "javax.annotation.security", + "javax.annotation.sql" + ], "net.bytebuddy:byte-buddy": [ "net.bytebuddy", "net.bytebuddy.agent.builder", @@ -946,6 +1565,7 @@ "org.checkerframework.checker.interning.qual", "org.checkerframework.checker.lock.qual", "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nonempty.qual", "org.checkerframework.checker.nullness.qual", "org.checkerframework.checker.optional.qual", "org.checkerframework.checker.propkey.qual", @@ -1012,6 +1632,14 @@ "org.checkerframework.nullaway.org.plumelib.reflection", "org.checkerframework.nullaway.org.plumelib.util" ], + "org.codehaus.mojo:animal-sniffer-annotations": [ + "org.codehaus.mojo.animal_sniffer" + ], + "org.conscrypt:conscrypt-openjdk-uber": [ + "org.conscrypt", + "org.conscrypt.ct", + "org.conscrypt.io" + ], "org.mockito:mockito-core": [ "org.mockito", "org.mockito.codegen", @@ -1098,10 +1726,38 @@ ], "org.slf4j:slf4j-jdk14": [ "org.slf4j.jul" + ], + "org.threeten:threetenbp": [ + "org.threeten.bp", + "org.threeten.bp.chrono", + "org.threeten.bp.format", + "org.threeten.bp.jdk8", + "org.threeten.bp.temporal", + "org.threeten.bp.zone" ] }, "repositories": { "https://repo1.maven.org/maven2/": [ + "com.google.android:annotations", + "com.google.android:annotations:jar:sources", + "com.google.api.grpc:proto-google-cloud-monitoring-v3", + "com.google.api.grpc:proto-google-cloud-monitoring-v3:jar:sources", + "com.google.api.grpc:proto-google-common-protos", + "com.google.api.grpc:proto-google-common-protos:jar:sources", + "com.google.api:api-common", + "com.google.api:api-common:jar:sources", + "com.google.api:gax", + "com.google.api:gax-grpc", + "com.google.api:gax-grpc:jar:sources", + "com.google.api:gax:jar:sources", + "com.google.auth:google-auth-library-credentials", + "com.google.auth:google-auth-library-credentials:jar:sources", + "com.google.auth:google-auth-library-oauth2-http", + "com.google.auth:google-auth-library-oauth2-http:jar:sources", + "com.google.auto.value:auto-value-annotations", + "com.google.auto.value:auto-value-annotations:jar:sources", + "com.google.cloud:google-cloud-monitoring", + "com.google.cloud:google-cloud-monitoring:jar:sources", "com.google.code.findbugs:jsr305", "com.google.code.findbugs:jsr305:jar:sources", "com.google.code.gson:gson", @@ -1113,8 +1769,18 @@ "com.google.guava:guava", "com.google.guava:guava:jar:sources", "com.google.guava:listenablefuture", + "com.google.http-client:google-http-client", + "com.google.http-client:google-http-client-gson", + "com.google.http-client:google-http-client-gson:jar:sources", + "com.google.http-client:google-http-client:jar:sources", "com.google.j2objc:j2objc-annotations", "com.google.j2objc:j2objc-annotations:jar:sources", + "com.google.protobuf:protobuf-java", + "com.google.protobuf:protobuf-java-util", + "com.google.protobuf:protobuf-java-util:jar:sources", + "com.google.protobuf:protobuf-java:jar:sources", + "com.google.re2j:re2j", + "com.google.re2j:re2j:jar:sources", "com.uber.nullaway:nullaway", "com.uber.nullaway:nullaway:jar:sources", "commons-codec:commons-codec", @@ -1123,6 +1789,36 @@ "commons-logging:commons-logging:jar:sources", "io.github.littleproxy:littleproxy", "io.github.littleproxy:littleproxy:jar:sources", + "io.grpc:grpc-alts", + "io.grpc:grpc-alts:jar:sources", + "io.grpc:grpc-api", + "io.grpc:grpc-api:jar:sources", + "io.grpc:grpc-auth", + "io.grpc:grpc-auth:jar:sources", + "io.grpc:grpc-context", + "io.grpc:grpc-context:jar:sources", + "io.grpc:grpc-core", + "io.grpc:grpc-core:jar:sources", + "io.grpc:grpc-googleapis", + "io.grpc:grpc-googleapis:jar:sources", + "io.grpc:grpc-grpclb", + "io.grpc:grpc-grpclb:jar:sources", + "io.grpc:grpc-inprocess", + "io.grpc:grpc-inprocess:jar:sources", + "io.grpc:grpc-netty-shaded", + "io.grpc:grpc-netty-shaded:jar:sources", + "io.grpc:grpc-protobuf", + "io.grpc:grpc-protobuf-lite", + "io.grpc:grpc-protobuf-lite:jar:sources", + "io.grpc:grpc-protobuf:jar:sources", + "io.grpc:grpc-services", + "io.grpc:grpc-services:jar:sources", + "io.grpc:grpc-stub", + "io.grpc:grpc-stub:jar:sources", + "io.grpc:grpc-util", + "io.grpc:grpc-util:jar:sources", + "io.grpc:grpc-xds", + "io.grpc:grpc-xds:jar:sources", "io.netty:netty-all", "io.netty:netty-buffer", "io.netty:netty-buffer:jar:sources", @@ -1188,6 +1884,14 @@ "io.netty:netty-transport-udt", "io.netty:netty-transport-udt:jar:sources", "io.netty:netty-transport:jar:sources", + "io.opencensus:opencensus-api", + "io.opencensus:opencensus-api:jar:sources", + "io.opencensus:opencensus-contrib-http-util", + "io.opencensus:opencensus-contrib-http-util:jar:sources", + "io.perfmark:perfmark-api", + "io.perfmark:perfmark-api:jar:sources", + "javax.annotation:javax.annotation-api", + "javax.annotation:javax.annotation-api:jar:sources", "net.bytebuddy:byte-buddy", "net.bytebuddy:byte-buddy-agent", "net.bytebuddy:byte-buddy-agent:jar:sources", @@ -1204,6 +1908,10 @@ "org.checkerframework:checker-qual:jar:sources", "org.checkerframework:dataflow-nullaway", "org.checkerframework:dataflow-nullaway:jar:sources", + "org.codehaus.mojo:animal-sniffer-annotations", + "org.codehaus.mojo:animal-sniffer-annotations:jar:sources", + "org.conscrypt:conscrypt-openjdk-uber", + "org.conscrypt:conscrypt-openjdk-uber:jar:sources", "org.mockito:mockito-core", "org.mockito:mockito-core:jar:sources", "org.objenesis:objenesis", @@ -1211,7 +1919,9 @@ "org.slf4j:slf4j-api", "org.slf4j:slf4j-api:jar:sources", "org.slf4j:slf4j-jdk14", - "org.slf4j:slf4j-jdk14:jar:sources" + "org.slf4j:slf4j-jdk14:jar:sources", + "org.threeten:threetenbp", + "org.threeten:threetenbp:jar:sources" ] }, "version": "2" diff --git a/pom.xml b/pom.xml index d300e82..351cc06 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,18 @@ 2.0.13 + + + io.opencensus + opencensus-api + 0.31.1 + + + io.opencensus + opencensus-exporter-stats-stackdriver + 0.31.1 + + com.uber.nullaway diff --git a/src/main/java/com/glean/proxy/BUILD.bazel b/src/main/java/com/glean/proxy/BUILD.bazel index 8921ec6..b1b287f 100644 --- a/src/main/java/com/glean/proxy/BUILD.bazel +++ b/src/main/java/com/glean/proxy/BUILD.bazel @@ -19,6 +19,7 @@ java_library( deps = [ "//src/main/java/com/glean/proxy/filters", "//src/main/java/com/glean/proxy/filters/helpers", + "//src/main/java/com/glean/proxy/metrics", "//src/main/java/com/glean/proxy/schemas", "@maven//:io_github_littleproxy_littleproxy", "@maven//:io_netty_netty_codec_http", diff --git a/src/main/java/com/glean/proxy/FilterConfiguration.java b/src/main/java/com/glean/proxy/FilterConfiguration.java index c5af098..1806427 100644 --- a/src/main/java/com/glean/proxy/FilterConfiguration.java +++ b/src/main/java/com/glean/proxy/FilterConfiguration.java @@ -6,8 +6,10 @@ import com.glean.proxy.filters.IpAddressRequestFilter; import com.glean.proxy.filters.IpAddressRequestFilter.ALLOWED_PROXY_ADDRESS_TYPE; import com.glean.proxy.filters.ProxyDebugFilter; +import com.glean.proxy.filters.ProxyMetricsFilter; import com.glean.proxy.filters.UpgradeRequestFilter; import com.glean.proxy.filters.helpers.AllowedEgressDomains; +import com.glean.proxy.metrics.ProxyMetrics; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.HttpRequest; import java.util.ArrayList; @@ -78,6 +80,11 @@ public record FilterConfiguration( "true".equalsIgnoreCase(System.getenv("UPGRADE_HTTP_REQUESTS")); return (request, ctx) -> new UpgradeRequestFilter(request, upgradeHttpRequests); }, + ProxyMetricsFilter.class.getSimpleName(), + () -> { + final ProxyMetrics metrics = ProxyMetrics.getInstance(); + return (request, ctx) -> new ProxyMetricsFilter(request, metrics); + }, // Debug filters ProxyDebugFilter.class.getSimpleName(), @@ -123,7 +130,8 @@ public static FilterConfiguration fromEnvironment() { String.join( ",", IpAddressRequestFilter.class.getSimpleName(), - UpgradeRequestFilter.class.getSimpleName()); + UpgradeRequestFilter.class.getSimpleName(), + ProxyMetricsFilter.class.getSimpleName()); String crossPlatformFiltersString = System.getenv().getOrDefault("CROSS_PLATFORM_FILTERS", defaultCrossPlatformFilters); diff --git a/src/main/java/com/glean/proxy/ProxyNetworking.java b/src/main/java/com/glean/proxy/ProxyNetworking.java index 2caa748..86262d3 100644 --- a/src/main/java/com/glean/proxy/ProxyNetworking.java +++ b/src/main/java/com/glean/proxy/ProxyNetworking.java @@ -1,6 +1,9 @@ package com.glean.proxy; +import com.glean.proxy.metrics.ProxyMetrics; +import com.glean.proxy.metrics.ProxyMetricsExporter; import java.net.InetSocketAddress; +import java.time.Clock; import java.util.logging.Logger; import org.littleshoot.proxy.ChainedProxyManager; import org.littleshoot.proxy.HttpProxyServer; @@ -9,6 +12,7 @@ public class ProxyNetworking { private static final Logger logger = Logger.getLogger(ProxyNetworking.class.getName()); + private static ProxyMetricsExporter metricsExporter; protected final ThreadPoolConfiguration threadPoolConfiguration; protected final FilterConfiguration filterConfiguration; @@ -75,10 +79,41 @@ public ProxyNetworking build() { if (chainedProxyManager == null) { chainedProxyManager = ChainedProxyConfiguration.fromEnvironment(); } + + // Initialize metrics exporter + initializeMetricsExporter(); + return new ProxyNetworking(this); } } + private static void initializeMetricsExporter() { + String gcpProjectId = System.getenv("GCP_PROJECT_ID"); + String enableMetricsExport = System.getenv("ENABLE_METRICS_EXPORT"); + + // Always initialize ProxyMetrics for local tracking + ProxyMetrics metrics = ProxyMetrics.getInstance(); + + // Check if metrics export is explicitly disabled + if (enableMetricsExport != null && + (enableMetricsExport.equalsIgnoreCase("false") || enableMetricsExport.equals("0"))) { + logger.info("Metrics export disabled via ENABLE_METRICS_EXPORT. Metrics will be tracked locally only."); + return; + } + + if (gcpProjectId == null || gcpProjectId.isEmpty()) { + logger.warning("GCP_PROJECT_ID not set. Metrics will be tracked but not exported to Cloud Monitoring."); + return; + } + + try { + logger.info("Initializing ProxyMetricsExporter for project: " + gcpProjectId); + metricsExporter = new ProxyMetricsExporter(gcpProjectId, Clock.systemUTC(), metrics.getMetrics()); + } catch (Exception e) { + logger.severe("Failed to initialize ProxyMetricsExporter: " + e.getMessage()); + } + } + private static ThreadPoolConfiguration createThreadPoolConfigurationFromEnvironment() { int numAcceptorThreads = Integer.parseInt(System.getenv().getOrDefault("NUM_ACCEPTOR_THREADS", "4")); diff --git a/src/main/java/com/glean/proxy/filters/BUILD.bazel b/src/main/java/com/glean/proxy/filters/BUILD.bazel index 39efb31..aaaaf78 100644 --- a/src/main/java/com/glean/proxy/filters/BUILD.bazel +++ b/src/main/java/com/glean/proxy/filters/BUILD.bazel @@ -8,6 +8,7 @@ java_library( visibility = ["//visibility:public"], deps = [ "//src/main/java/com/glean/proxy/filters/helpers", + "//src/main/java/com/glean/proxy/metrics", "//src/main/java/com/glean/proxy/schemas", "@maven//:com_google_code_gson_gson", "@maven//:io_github_littleproxy_littleproxy", diff --git a/src/main/java/com/glean/proxy/filters/CompositeFilter.java b/src/main/java/com/glean/proxy/filters/CompositeFilter.java index f3e1f1e..90af2e2 100644 --- a/src/main/java/com/glean/proxy/filters/CompositeFilter.java +++ b/src/main/java/com/glean/proxy/filters/CompositeFilter.java @@ -50,4 +50,16 @@ public HttpResponse proxyToServerRequest(HttpObject httpObject) { } return null; } + + @Override + public HttpObject proxyToClientResponse(HttpObject httpObject) { + HttpObject currentObject = httpObject; + for (HttpFilters filter : filters) { + HttpObject result = filter.proxyToClientResponse(currentObject); + if (result != null) { + currentObject = result; + } + } + return currentObject; + } } diff --git a/src/main/java/com/glean/proxy/filters/ProxyMetricsFilter.java b/src/main/java/com/glean/proxy/filters/ProxyMetricsFilter.java new file mode 100644 index 0000000..2a1a1ff --- /dev/null +++ b/src/main/java/com/glean/proxy/filters/ProxyMetricsFilter.java @@ -0,0 +1,48 @@ +package com.glean.proxy.filters; + +import com.glean.proxy.metrics.ProxyMetrics; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import java.util.logging.Logger; +import org.littleshoot.proxy.HttpFiltersAdapter; + +/** + * Filter that records metrics for proxy requests using OpenCensus. + * Tracks request counts and latencies, labeled by HTTP method and success/failure status. + */ +public class ProxyMetricsFilter extends HttpFiltersAdapter { + private static final Logger logger = Logger.getLogger(ProxyMetricsFilter.class.getName()); + + private long requestStartTime; + private final ProxyMetrics metrics; + + public ProxyMetricsFilter(HttpRequest originalRequest, ProxyMetrics metrics) { + super(originalRequest); + this.metrics = metrics; + } + + @Override + public HttpResponse clientToProxyRequest(HttpObject httpObject) { + requestStartTime = System.currentTimeMillis(); + return null; + } + + @Override + public HttpObject proxyToClientResponse(HttpObject httpObject) { + if (httpObject instanceof HttpResponse) { + HttpResponse response = (HttpResponse) httpObject; + long latencyMs = System.currentTimeMillis() - requestStartTime; + String method = originalRequest.method().name(); + int statusCode = response.status().code(); + + metrics.recordRequest(statusCode, method, latencyMs); + + logger.finest( + String.format( + "Proxy request: method=%s, status=%d, latency=%dms", + method, statusCode, latencyMs)); + } + return httpObject; + } +} diff --git a/src/main/java/com/glean/proxy/metrics/BUILD.bazel b/src/main/java/com/glean/proxy/metrics/BUILD.bazel new file mode 100644 index 0000000..56061e3 --- /dev/null +++ b/src/main/java/com/glean/proxy/metrics/BUILD.bazel @@ -0,0 +1,16 @@ +load("@rules_java//java:defs.bzl", "java_library") + +package(default_visibility = ["//visibility:public"]) + +java_library( + name = "metrics", + srcs = glob(["*.java"]), + visibility = ["//visibility:public"], + deps = [ + "@maven//:com_google_api_api_common", + "@maven//:com_google_api_grpc_proto_google_cloud_monitoring_v3", + "@maven//:com_google_api_grpc_proto_google_common_protos", + "@maven//:com_google_cloud_google_cloud_monitoring", + "@maven//:com_google_protobuf_protobuf_java", + ], +) diff --git a/src/main/java/com/glean/proxy/metrics/MetricData.java b/src/main/java/com/glean/proxy/metrics/MetricData.java new file mode 100644 index 0000000..abdd27b --- /dev/null +++ b/src/main/java/com/glean/proxy/metrics/MetricData.java @@ -0,0 +1,151 @@ +package com.glean.proxy.metrics; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Stores aggregated metric data for a specific tag combination. + * Thread-safe using atomic operations. + */ +public class MetricData { + // Latency bucket boundaries in milliseconds + // Creates buckets: [0,10), [10,25), [25,50), [50,100), [100,250), [250,500), + // [500,1000), [1000,2500), [2500,5000), [5000,10000), [10000,∞) + public static final double[] BUCKET_BOUNDS = { + 0, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000 + }; + + private final String statusCode; + private final String method; + private final AtomicLong count = new AtomicLong(0); + private final AtomicLong totalLatencyMs = new AtomicLong(0); + private final AtomicLong minLatencyMs = new AtomicLong(Long.MAX_VALUE); + private final AtomicLong maxLatencyMs = new AtomicLong(0); + + // Histogram buckets: one more than bounds (for overflow bucket) + private final AtomicLong[] bucketCounts = new AtomicLong[BUCKET_BOUNDS.length + 1]; + + public MetricData(String statusCode, String method) { + this.statusCode = statusCode; + this.method = method; + + // Initialize bucket counters + for (int i = 0; i < bucketCounts.length; i++) { + bucketCounts[i] = new AtomicLong(0); + } + } + + public void record(long latencyMs) { + count.incrementAndGet(); + totalLatencyMs.addAndGet(latencyMs); + + // Update histogram bucket + int bucketIndex = findBucketIndex(latencyMs); + bucketCounts[bucketIndex].incrementAndGet(); + + // Update min + long currentMin = minLatencyMs.get(); + while (latencyMs < currentMin) { + if (minLatencyMs.compareAndSet(currentMin, latencyMs)) { + break; + } + currentMin = minLatencyMs.get(); + } + + // Update max + long currentMax = maxLatencyMs.get(); + while (latencyMs > currentMax) { + if (maxLatencyMs.compareAndSet(currentMax, latencyMs)) { + break; + } + currentMax = maxLatencyMs.get(); + } + } + + private int findBucketIndex(long latencyMs) { + for (int i = 0; i < BUCKET_BOUNDS.length; i++) { + if (latencyMs < BUCKET_BOUNDS[i]) { + return i; + } + } + // Overflow bucket (values >= last bound) + return BUCKET_BOUNDS.length; + } + + public String getStatusCode() { + return statusCode; + } + + public String getMethod() { + return method; + } + + public long getCount() { + return count.get(); + } + + public long getTotalLatencyMs() { + return totalLatencyMs.get(); + } + + public long getMinLatencyMs() { + long min = minLatencyMs.get(); + return min == Long.MAX_VALUE ? 0 : min; + } + + public long getMaxLatencyMs() { + return maxLatencyMs.get(); + } + + public double getAvgLatencyMs() { + long c = count.get(); + return c == 0 ? 0 : (double) totalLatencyMs.get() / c; + } + + /** + * Get bucket counts for histogram. + * Returns a snapshot of current bucket counts. + */ + public long[] getBucketCounts() { + long[] counts = new long[bucketCounts.length]; + for (int i = 0; i < bucketCounts.length; i++) { + counts[i] = bucketCounts[i].get(); + } + return counts; + } + + /** + * Reset all metrics (called after export). + */ + public void reset() { + count.set(0); + totalLatencyMs.set(0); + minLatencyMs.set(Long.MAX_VALUE); + maxLatencyMs.set(0); + + // Reset histogram buckets + for (AtomicLong bucket : bucketCounts) { + bucket.set(0); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MetricData that = (MetricData) o; + return Objects.equals(statusCode, that.statusCode) && Objects.equals(method, that.method); + } + + @Override + public int hashCode() { + return Objects.hash(statusCode, method); + } + + @Override + public String toString() { + return String.format( + "MetricData{status=%s, method=%s, count=%d, avg=%.2fms, min=%dms, max=%dms}", + statusCode, method, getCount(), getAvgLatencyMs(), getMinLatencyMs(), getMaxLatencyMs()); + } +} diff --git a/src/main/java/com/glean/proxy/metrics/ProxyMetrics.java b/src/main/java/com/glean/proxy/metrics/ProxyMetrics.java new file mode 100644 index 0000000..ad73bc5 --- /dev/null +++ b/src/main/java/com/glean/proxy/metrics/ProxyMetrics.java @@ -0,0 +1,38 @@ +package com.glean.proxy.metrics; + +import com.glean.proxy.metrics.ProxyMetricsExporter.MetricKey; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +/** + * Metrics collector for proxy requests. + * Uses manual in-memory tracking instead of OpenCensus views. + */ +public class ProxyMetrics { + private static final Logger logger = Logger.getLogger(ProxyMetrics.class.getName()); + private static ProxyMetrics instance; + + // Manual metric tracking (thread-safe) + private final ConcurrentHashMap metrics = new ConcurrentHashMap<>(); + + public static synchronized ProxyMetrics getInstance() { + if (instance == null) { + instance = new ProxyMetrics(); + } + return instance; + } + + public ConcurrentHashMap getMetrics() { + return metrics; + } + + public void recordRequest(int statusCode, String method, long latencyMs) { + try { + MetricKey key = new MetricKey(String.valueOf(statusCode), method); + MetricData data = metrics.computeIfAbsent(key, k -> new MetricData(k.getStatusCode(), k.getMethod())); + data.record(latencyMs); + } catch (Exception e) { + logger.severe("Failed to record metrics: " + e.getMessage()); + } + } +} diff --git a/src/main/java/com/glean/proxy/metrics/ProxyMetricsExporter.java b/src/main/java/com/glean/proxy/metrics/ProxyMetricsExporter.java new file mode 100644 index 0000000..da3f0aa --- /dev/null +++ b/src/main/java/com/glean/proxy/metrics/ProxyMetricsExporter.java @@ -0,0 +1,278 @@ +package com.glean.proxy.metrics; + +import com.google.api.Distribution; +import com.google.api.Distribution.BucketOptions; +import com.google.api.Distribution.BucketOptions.Explicit; +import com.google.api.Metric; +import com.google.api.MonitoredResource; +import com.google.cloud.monitoring.v3.MetricServiceClient; +import com.google.monitoring.v3.CreateTimeSeriesRequest; +import com.google.monitoring.v3.Point; +import com.google.monitoring.v3.ProjectName; +import com.google.monitoring.v3.TimeInterval; +import com.google.monitoring.v3.TimeSeries; +import com.google.monitoring.v3.TypedValue; +import com.google.protobuf.Timestamp; +import java.io.Closeable; +import java.time.Clock; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * Periodically exports proxy metrics to GCP Cloud Monitoring. + */ +public class ProxyMetricsExporter implements Closeable { + private static final Logger logger = Logger.getLogger(ProxyMetricsExporter.class.getName()); + private static final long EXPORT_INTERVAL_MS = 60000; // 60 seconds + + private final String projectId; + private final Clock clock; + private final Timer exportTimer; + private final ConcurrentHashMap metrics; + private MetricServiceClient metricServiceClient; + + public ProxyMetricsExporter( + String projectId, Clock clock, ConcurrentHashMap metrics) { + this.projectId = projectId; + this.clock = clock; + this.metrics = metrics; + this.exportTimer = new Timer("ProxyMetricsExporter", true); + + // Initialize GCP Monitoring client + try { + this.metricServiceClient = MetricServiceClient.create(); + logger.info("MetricServiceClient created successfully"); + } catch (Exception e) { + logger.severe("Failed to create MetricServiceClient: " + e.getMessage()); + e.printStackTrace(); + } + + // Schedule periodic exports + exportTimer.scheduleAtFixedRate( + new TimerTask() { + @Override + public void run() { + exportMetrics(); + } + }, + EXPORT_INTERVAL_MS, + EXPORT_INTERVAL_MS); + + logger.info( + String.format( + "ProxyMetricsExporter initialized for project %s, exporting every %d seconds", + projectId, EXPORT_INTERVAL_MS / 1000)); + } + + private void exportMetrics() { + if (metricServiceClient == null) { + logger.warning("MetricServiceClient not initialized, skipping export"); + return; + } + + try { + Instant now = clock.instant(); + List timeSeriesList = new ArrayList<>(); + + // Snapshot and reset metrics + Map snapshot = new ConcurrentHashMap<>(metrics); + int totalMetrics = 0; + + for (Map.Entry entry : snapshot.entrySet()) { + MetricKey key = entry.getKey(); + MetricData data = entry.getValue(); + + long count = data.getCount(); + if (count == 0) { + continue; // Skip empty metrics + } + + totalMetrics++; + + // Create time series for request count + timeSeriesList.add(createCountTimeSeries(key, data, now)); + + // Create time series for request latency (distribution) + timeSeriesList.add(createLatencyTimeSeries(key, data, now)); + + // Reset the metric after export + MetricData originalData = metrics.get(key); + if (originalData != null) { + originalData.reset(); + } + } + + if (timeSeriesList.isEmpty()) { + logger.fine("No metrics to export"); + return; + } + + // Send to GCP Cloud Monitoring + ProjectName projectName = ProjectName.of(projectId); + CreateTimeSeriesRequest request = + CreateTimeSeriesRequest.newBuilder() + .setName(projectName.toString()) + .addAllTimeSeries(timeSeriesList) + .build(); + + metricServiceClient.createTimeSeries(request); + + logger.info( + String.format( + "Successfully exported %d metrics (%d time series) to Cloud Monitoring", + totalMetrics, timeSeriesList.size())); + + } catch (Exception e) { + logger.severe("Failed to export metrics: " + e.getMessage()); + e.printStackTrace(); + } + } + + private TimeSeries createCountTimeSeries(MetricKey key, MetricData data, Instant now) { + Metric metric = + Metric.newBuilder() + .setType("custom.googleapis.com/proxy/request_count") + .putLabels("status_code", key.getStatusCode()) + .putLabels("method", key.getMethod()) + .build(); + + MonitoredResource resource = + MonitoredResource.newBuilder().setType("global").putLabels("project_id", projectId).build(); + + Timestamp timestamp = Timestamp.newBuilder().setSeconds(now.getEpochSecond()).build(); + TimeInterval interval = + TimeInterval.newBuilder().setEndTime(timestamp).setStartTime(timestamp).build(); + + Point point = + Point.newBuilder() + .setInterval(interval) + .setValue(TypedValue.newBuilder().setInt64Value(data.getCount()).build()) + .build(); + + return TimeSeries.newBuilder() + .setMetric(metric) + .setResource(resource) + .setValueType(com.google.api.MetricDescriptor.ValueType.INT64) + .setMetricKind(com.google.api.MetricDescriptor.MetricKind.GAUGE) + .addPoints(point) + .build(); + } + + private TimeSeries createLatencyTimeSeries(MetricKey key, MetricData data, Instant now) { + Metric metric = + Metric.newBuilder() + .setType("custom.googleapis.com/proxy/request_latency_distribution") + .putLabels("status_code", key.getStatusCode()) + .putLabels("method", key.getMethod()) + .build(); + + MonitoredResource resource = + MonitoredResource.newBuilder().setType("global").putLabels("project_id", projectId).build(); + + Timestamp timestamp = Timestamp.newBuilder().setSeconds(now.getEpochSecond()).build(); + TimeInterval interval = + TimeInterval.newBuilder().setEndTime(timestamp).setStartTime(timestamp).build(); + + // Create distribution with explicit bucket boundaries + // Convert double[] to List for BucketOptions + List bounds = Arrays.stream(MetricData.BUCKET_BOUNDS) + .boxed() + .collect(Collectors.toList()); + + BucketOptions bucketOptions = + BucketOptions.newBuilder() + .setExplicitBuckets(Explicit.newBuilder().addAllBounds(bounds).build()) + .build(); + + // Get bucket counts from data + long[] bucketCounts = data.getBucketCounts(); + + Distribution.Builder distributionBuilder = + Distribution.newBuilder() + .setCount(data.getCount()) + .setMean(data.getAvgLatencyMs()) + .setBucketOptions(bucketOptions); + + // Add bucket counts (convert long[] to List) + for (long count : bucketCounts) { + distributionBuilder.addBucketCounts(count); + } + + Distribution distribution = distributionBuilder.build(); + + Point point = + Point.newBuilder() + .setInterval(interval) + .setValue(TypedValue.newBuilder().setDistributionValue(distribution).build()) + .build(); + + return TimeSeries.newBuilder() + .setMetric(metric) + .setResource(resource) + .setValueType(com.google.api.MetricDescriptor.ValueType.DISTRIBUTION) + .setMetricKind(com.google.api.MetricDescriptor.MetricKind.GAUGE) + .addPoints(point) + .build(); + } + + @Override + public void close() { + logger.info("Shutting down ProxyMetricsExporter..."); + exportTimer.cancel(); + + // Do final export + exportMetrics(); + + if (metricServiceClient != null) { + metricServiceClient.close(); + } + logger.info("ProxyMetricsExporter shut down"); + } + + /** + * Key for grouping metrics by status code and method. + */ + public static class MetricKey { + private final String statusCode; + private final String method; + + public MetricKey(String statusCode, String method) { + this.statusCode = statusCode; + this.method = method; + } + + public String getStatusCode() { + return statusCode; + } + + public String getMethod() { + return method; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MetricKey that = (MetricKey) o; + return statusCode.equals(that.statusCode) && method.equals(that.method); + } + + @Override + public int hashCode() { + return 31 * statusCode.hashCode() + method.hashCode(); + } + + @Override + public String toString() { + return String.format("MetricKey{status=%s, method=%s}", statusCode, method); + } + } +} From 795c49c74587c7b015956973b0d8437415ed1635 Mon Sep 17 00:00:00 2001 From: Pooja Bhagat Date: Mon, 29 Dec 2025 16:41:05 +0530 Subject: [PATCH 2/2] Use OpenTelemetry --- WORKSPACE | 77 +-- maven_install.json | 613 +++++++++++++++++- .../java/com/glean/proxy/ProxyNetworking.java | 25 +- .../java/com/glean/proxy/metrics/BUILD.bazel | 6 + .../com/glean/proxy/metrics/MetricData.java | 151 ----- .../proxy/metrics/OpenTelemetrySetup.java | 84 +++ .../com/glean/proxy/metrics/ProxyMetrics.java | 58 +- .../proxy/metrics/ProxyMetricsExporter.java | 278 -------- 8 files changed, 789 insertions(+), 503 deletions(-) delete mode 100644 src/main/java/com/glean/proxy/metrics/MetricData.java create mode 100644 src/main/java/com/glean/proxy/metrics/OpenTelemetrySetup.java delete mode 100644 src/main/java/com/glean/proxy/metrics/ProxyMetricsExporter.java diff --git a/WORKSPACE b/WORKSPACE index 4f85147..39945f6 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,6 +1,32 @@ workspace(name = "com_github_gleanwork_glean-proxy") +load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies", "aspect_bazel_lib_register_toolchains") +load("@aspect_rules_js//js:repositories.bzl", "rules_js_dependencies") +load("@aspect_rules_lint//format:repositories.bzl", "rules_lint_dependencies") +load("@aspect_rules_lint//lint:pmd.bzl", "fetch_pmd") +load("@bazel_features//:deps.bzl", "bazel_features_deps") +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@contrib_rules_jvm//:gazelle_setup.bzl", "contrib_rules_jvm_gazelle_setup") + +# Fetches the contrib_rules_jvm dependencies. +# If you want to have a different version of some dependency, +# you should fetch it *before* calling this. +load("@contrib_rules_jvm//:repositories.bzl", "contrib_rules_jvm_deps", "contrib_rules_jvm_gazelle_deps") + +# Now ensure that the downloaded deps are properly configured +load("@contrib_rules_jvm//:setup.bzl", "contrib_rules_jvm_setup") +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") +load("@maven//:defs.bzl", "pinned_maven_install") +load("@rules_java//java:repositories.bzl", "rules_java_dependencies", "rules_java_toolchains") +load("@rules_jvm_external//:defs.bzl", "maven_install") +load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps") +load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup") +load("@rules_oci//oci:dependencies.bzl", "rules_oci_dependencies") +load("@rules_oci//oci:pull.bzl", "oci_pull") +load("@rules_oci//oci:repositories.bzl", "oci_register_toolchains") +load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies") http_archive( name = "platforms", @@ -18,8 +44,6 @@ http_archive( url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.16.0/bazel_features-v1.16.0.tar.gz", ) -load("@bazel_features//:deps.bzl", "bazel_features_deps") - bazel_features_deps() http_archive( @@ -30,8 +54,6 @@ http_archive( ], ) -load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") - rules_pkg_dependencies() http_archive( @@ -43,12 +65,8 @@ http_archive( url = "https://github.com/aspect-build/rules_lint/releases/download/v1.0.2/rules_lint-v1.0.2.tar.gz", ) -load("@aspect_rules_lint//format:repositories.bzl", "rules_lint_dependencies") - rules_lint_dependencies() -load("@aspect_rules_lint//lint:pmd.bzl", "fetch_pmd") - fetch_pmd() http_archive( @@ -58,8 +76,6 @@ http_archive( url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.8.1/bazel-lib-v2.8.1.tar.gz", ) -load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies", "aspect_bazel_lib_register_toolchains") - aspect_bazel_lib_dependencies() aspect_bazel_lib_register_toolchains() @@ -73,8 +89,6 @@ http_archive( ], ) -load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") - go_rules_dependencies() go_register_toolchains(go_version = "1.23.7") @@ -88,8 +102,6 @@ http_archive( ], ) -load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") - gazelle_dependencies(go_sdk = "go_sdk") http_archive( @@ -99,8 +111,6 @@ http_archive( url = "https://github.com/bazelbuild/rules_proto/releases/download/6.0.0/rules_proto-6.0.0.tar.gz", ) -load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies") - rules_proto_dependencies() http_archive( @@ -111,8 +121,6 @@ http_archive( ], ) -load("@rules_java//java:repositories.bzl", "rules_java_dependencies", "rules_java_toolchains") - rules_java_dependencies() rules_java_toolchains() @@ -140,22 +148,12 @@ http_archive( url = "https://github.com/bazel-contrib/rules_jvm/releases/download/v0.27.0/rules_jvm-v0.27.0.tar.gz", ) -# Fetches the contrib_rules_jvm dependencies. -# If you want to have a different version of some dependency, -# you should fetch it *before* calling this. -load("@contrib_rules_jvm//:repositories.bzl", "contrib_rules_jvm_deps", "contrib_rules_jvm_gazelle_deps") - contrib_rules_jvm_deps() contrib_rules_jvm_gazelle_deps() -# Now ensure that the downloaded deps are properly configured -load("@contrib_rules_jvm//:setup.bzl", "contrib_rules_jvm_setup") - contrib_rules_jvm_setup() -load("@contrib_rules_jvm//:gazelle_setup.bzl", "contrib_rules_jvm_gazelle_setup") - contrib_rules_jvm_gazelle_setup() RULES_JVM_EXTERNAL_TAG = "6.1" @@ -169,12 +167,8 @@ http_archive( url = "https://github.com/bazelbuild/rules_jvm_external/releases/download/%s/rules_jvm_external-%s.tar.gz" % (RULES_JVM_EXTERNAL_TAG, RULES_JVM_EXTERNAL_TAG), ) -load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps") - rules_jvm_external_deps() -load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup") - rules_jvm_external_setup() http_archive( @@ -184,14 +178,10 @@ http_archive( url = "https://github.com/aspect-build/rules_js/releases/download/v2.3.7/rules_js-v2.3.7.tar.gz", ) -load("@aspect_rules_js//js:repositories.bzl", "rules_js_dependencies") - # This pulls in nodejs env and other aspect bazel lib set up for us. # https://github.com/aspect-build/rules_js/blob/066df77bd0dd6247ffd9ec728bbdcf6f6203f2e0/js/repositories.bzl#L21 rules_js_dependencies() -load("@rules_jvm_external//:defs.bzl", "maven_install") - maven_install( artifacts = [ "org.assertj:assertj-core:3.24.2", @@ -204,6 +194,13 @@ maven_install( "com.uber.nullaway:nullaway:0.10.9", "org.mockito:mockito-core:5.12.0", "org.slf4j:slf4j-jdk14:2.0.13", + "io.opentelemetry:opentelemetry-api:1.44.1", + "io.opentelemetry:opentelemetry-sdk:1.44.1", + "io.opentelemetry:opentelemetry-sdk-common:1.44.1", + "io.opentelemetry:opentelemetry-sdk-metrics:1.44.1", + "io.opentelemetry:opentelemetry-exporter-otlp:1.44.1", + "io.opentelemetry.semconv:opentelemetry-semconv:1.28.0-alpha", + "com.google.cloud.opentelemetry:exporter-metrics:0.33.0", ], fetch_sources = True, maven_install_json = "//:maven_install.json", @@ -212,8 +209,6 @@ maven_install( ], ) -load("@maven//:defs.bzl", "pinned_maven_install") - pinned_maven_install() http_archive( @@ -223,16 +218,10 @@ http_archive( url = "https://github.com/bazel-contrib/rules_oci/releases/download/v2.0.1/rules_oci-v2.0.1.tar.gz", ) -load("@rules_oci//oci:dependencies.bzl", "rules_oci_dependencies") - rules_oci_dependencies() -load("@rules_oci//oci:repositories.bzl", "oci_register_toolchains") - oci_register_toolchains(name = "oci") -load("@rules_oci//oci:pull.bzl", "oci_pull") - oci_pull( name = "distroless_java17", digest = "sha256:eba3112cc48f46e4eac153191f229baa7bd1895f9d6219b497699b803fd4b4a2", diff --git a/maven_install.json b/maven_install.json index db0df54..639fcb8 100755 --- a/maven_install.json +++ b/maven_install.json @@ -1,7 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 1879168519, - "__RESOLVED_ARTIFACTS_HASH": 833789247, + "__INPUT_ARTIFACTS_HASH": -126544629, + "__RESOLVED_ARTIFACTS_HASH": -711352574, "conflict_resolution": { "com.google.code.gson:gson:2.10.1": "com.google.code.gson:gson:2.11.0" }, @@ -27,6 +27,13 @@ }, "version": "2.45.1" }, + "com.google.api.grpc:proto-google-iam-v1": { + "shasums": { + "jar": "9e0cc98ed7939c4c915a7f545da2a773440d566f00922dfa00f619fe63f662a1", + "sources": "46463a467f0d1165b77588dac46c8b112491145d8b07fdb99488c9a06c713186" + }, + "version": "1.40.1" + }, "com.google.api:api-common": { "shasums": { "jar": "429a441ae9b709e3e04e708efe91aaaecc24d1e6252595fbbaa7f9a2f05f992a", @@ -69,6 +76,27 @@ }, "version": "1.11.0" }, + "com.google.cloud.opentelemetry:exporter-metrics": { + "shasums": { + "jar": "7a9ab87683a97357a95389ff3cec9684785648076f9b158d44da3becb93bb036", + "sources": "1693896c58280c0583ec7d4e63dee1cc2ba8b4c8bad4b10f701cca326686018f" + }, + "version": "0.33.0" + }, + "com.google.cloud.opentelemetry:shared-resourcemapping": { + "shasums": { + "jar": "eea9a3e11db783626cc15dd1835c1ed09f53b70a1fca8c58bf723ce813e9f843", + "sources": "b772c7f966814ffa2ceb408b9ee155726c700b6f5af6ecd79ebcb21b9bc39506" + }, + "version": "0.33.0" + }, + "com.google.cloud:google-cloud-core": { + "shasums": { + "jar": "baa2f9768f1665b2c18cf730b863b307adeb95cf34ab9a68ad723a6c5e6a3159", + "sources": "1878d4414057b0b40a615f9227da4c6cb4257c3bc3467247178b34440540dee3" + }, + "version": "2.44.1" + }, "com.google.cloud:google-cloud-monitoring": { "shasums": { "jar": "9727aa7d94d8dd036bffccf9abc5d18970b1e5e9827896f9907f7450ed10f5e6", @@ -160,6 +188,27 @@ }, "version": "1.7" }, + "com.squareup.okhttp3:okhttp": { + "shasums": { + "jar": "b1050081b14bb7a3a7e55a4d3ef01b5dcfabc453b4573a4fc019767191d5f4e0", + "sources": "d91a769a4140e542cddbac4e67fcf279299614e8bfd53bd23b85e60c2861341c" + }, + "version": "4.12.0" + }, + "com.squareup.okio:okio": { + "shasums": { + "jar": "8e63292e5c53bb93c4a6b0c213e79f15990fed250c1340f1c343880e1c9c39b5", + "sources": "64d5b6667f064511dd93100173f735b2d5052a1c926858f4b6a05b84e825ef94" + }, + "version": "3.6.0" + }, + "com.squareup.okio:okio-jvm": { + "shasums": { + "jar": "67543f0736fc422ae927ed0e504b98bc5e269fda0d3500579337cb713da28412", + "sources": "870a42a7b468af8fd24d67b7b62e5861aab398c56f15a80b10642e7001564e2f" + }, + "version": "3.6.0" + }, "com.uber.nullaway:nullaway": { "shasums": { "jar": "38327fd62ceb0683881d68ee7bf97598133f0287f3c670416f50f85cf8ef418b", @@ -528,6 +577,104 @@ }, "version": "0.31.1" }, + "io.opentelemetry.semconv:opentelemetry-semconv": { + "shasums": { + "jar": "e8ab86e93cef09e421a6213f4cf18421fcc6e1f9cf0ab94b9a31ed4460ddf553", + "sources": "b0588ae0617071c30451fe0f4916b2cde7aa8d24b542ee696a7bf59f7d7f46a8" + }, + "version": "1.28.0-alpha" + }, + "io.opentelemetry:opentelemetry-api": { + "shasums": { + "jar": "097e2e71c8b8c813f4a13176baafbbbb124b1253f5c9fffd110bc2add74ace93", + "sources": "0d9d8c1391c2a8a79ebcb013f38c172ad4cbef6b52e690501e8cb55effabc9da" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-api-incubator": { + "shasums": { + "jar": "8dbf451ce580fa0252ee9ca77331fd21710710ca9b735a1359241ead3be8ebe5", + "sources": "400ee017921af6a987de5ca9aabd2afbd2989c1f3c9eaedc4f1ff8299eace6c1" + }, + "version": "1.44.1-alpha" + }, + "io.opentelemetry:opentelemetry-context": { + "shasums": { + "jar": "006b3f7c3880356a86f02c40eedeba124f226a2f145fe904cc1b7def0088bab0", + "sources": "50f136587f0d1bf317fbfdd9cf5f589a321d7fd366e3cbea1dc511e458115324" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-exporter-common": { + "shasums": { + "jar": "259b65adc1e8969804b4232529bd52cbaaabf86049c404f25bce35898023696b", + "sources": "160c784b8d35721b7eee04e6e06e7bdccc2cee512e648e0509c99ab0efbbe09d" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-exporter-otlp": { + "shasums": { + "jar": "9384faa831f082dd10ca4b09eb1906fa2cdd8a8a0fdbfca56ebb4fb4d6e205ec", + "sources": "03d8e56a41bf49674ea3679d6360e0f16cfee4e7ea2c035fc9bcc81108482688" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-exporter-otlp-common": { + "shasums": { + "jar": "589385cede8e974a092753d7bafbc5afd26e8eaad970e975fb51106557bdae41", + "sources": "b1b1b8c29eb3e119fc5cd909e1d309b75190f53724b872eaffa7229458309818" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-exporter-sender-okhttp": { + "shasums": { + "jar": "7b4eaef47b9bae37cc2b6ca9975defd1d49a7de8b4a31fc2dffd582f09d8dac8", + "sources": "80908010581be389fb32c6388f7bb7ab3aed6a27b9e910a1805bbd7605495db3" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-sdk": { + "shasums": { + "jar": "7c64f330ec197a1eb88059b97dc9376fdb9836695027c96a0cb4e74ef917cebe", + "sources": "567caff6fe790a8eef0195c712368c13bae2f5d69ebf51bf3f5393166be43e11" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-sdk-common": { + "shasums": { + "jar": "3770fc477ee0fab21e796bb559f0a996d38c744f3a170868d1b3b85b63b827d0", + "sources": "e1801125d48e0ba0cb016db0cea3445aaec57c322e6765ac788e3f9b959a3ec1" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi": { + "shasums": { + "jar": "f3478af96e4f2f5fd60283522553de4ce99c03606a16e460d07bc6eee12f9f95", + "sources": "46b251d7737595cf49cfb2f3bae905951efc4cf78085ba02c625f7fed781b89f" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-sdk-logs": { + "shasums": { + "jar": "ba4a97966ded2927dcaae6e91bf0a1707867e44cc3dd2ae4c7ef8214bdf8daf7", + "sources": "f6c63d81b3189fe1a76a712f4eff11b60300c093ce4d65ad73fee5b811648ca6" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-sdk-metrics": { + "shasums": { + "jar": "3c09668a96f1e9443d2894ae456674c8c38b221c096c391fc81616daa672bf68", + "sources": "afe4b9c4cb2328bb2180cf4bc280293191eb3ab8b4165ab725bab9e73a194a1b" + }, + "version": "1.44.1" + }, + "io.opentelemetry:opentelemetry-sdk-trace": { + "shasums": { + "jar": "ff013ab945567dccb5dde638f7a366519defbd2d138df1370936e847ef608c87", + "sources": "24f7b867a0f2f3203e4eb2069137bbd01936e7df7d08d3ec2dfcad478444ae27" + }, + "version": "1.44.1" + }, "io.perfmark:perfmark-api": { "shasums": { "jar": "c7b478503ec524e55df19b424d46d27c8a68aeb801664fadd4f069b71f52d0f6", @@ -612,6 +759,41 @@ }, "version": "2.5.2" }, + "org.jetbrains.kotlin:kotlin-stdlib": { + "shasums": { + "jar": "55e989c512b80907799f854309f3bc7782c5b3d13932442d0379d5c472711504", + "sources": "4dad0d6fe62c142313e09e3b693c9dccc6bf9dc7a56a37c91be9edb7de0cf45b" + }, + "version": "1.9.10" + }, + "org.jetbrains.kotlin:kotlin-stdlib-common": { + "shasums": { + "jar": "cde3341ba18a2ba262b0b7cf6c55b20c90e8d434e42c9a13e6a3f770db965a88", + "sources": "63f2b6dfd58e3e1c82a4b37a2e1743996ca182bf44dc2aadd264b8fb95fddcd3" + }, + "version": "1.9.10" + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk7": { + "shasums": { + "jar": "ac6361bf9ad1ed382c2103d9712c47cdec166232b4903ed596e8876b0681c9b7", + "sources": "ea10d3e5e6e695d8a5283cbf116321acae6ba42d0bdd3eda50f7c34a26fa25cb" + }, + "version": "1.9.10" + }, + "org.jetbrains.kotlin:kotlin-stdlib-jdk8": { + "shasums": { + "jar": "a4c74d94d64ce1abe53760fe0389dd941f6fc558d0dab35e47c085a11ec80f28", + "sources": "40e9a80f6b953d12389623760d438e69914098d0c4d7053f70f90533ec041259" + }, + "version": "1.9.10" + }, + "org.jetbrains:annotations": { + "shasums": { + "jar": "ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478", + "sources": "42a5e144b8e81d50d6913d1007b695e62e614705268d8cf9f13dbdc478c2c68e" + }, + "version": "13.0" + }, "org.mockito:mockito-core": { "shasums": { "jar": "4a2eb29237050da749e90a46f948bce7e26ec22b671e41f59b1ac6f4b6408229", @@ -659,6 +841,67 @@ "javax.annotation:javax.annotation-api", "org.checkerframework:checker-qual" ], + "com.google.api.grpc:proto-google-common-protos": [ + "com.google.protobuf:protobuf-java" + ], + "com.google.api.grpc:proto-google-iam-v1": [ + "com.google.api.grpc:proto-google-common-protos", + "com.google.protobuf:protobuf-java" + ], + "com.google.api:api-common": [ + "com.google.auto.value:auto-value-annotations", + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:guava", + "com.google.j2objc:j2objc-annotations", + "javax.annotation:javax.annotation-api" + ], + "com.google.api:gax": [ + "com.google.api.grpc:proto-google-common-protos", + "com.google.api:api-common", + "com.google.auth:google-auth-library-credentials", + "com.google.auth:google-auth-library-oauth2-http", + "com.google.guava:guava", + "com.google.protobuf:protobuf-java", + "io.opencensus:opencensus-api", + "org.threeten:threetenbp" + ], + "com.google.auth:google-auth-library-oauth2-http": [ + "com.google.auth:google-auth-library-credentials", + "com.google.auto.value:auto-value-annotations", + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:guava", + "com.google.http-client:google-http-client", + "com.google.http-client:google-http-client-gson" + ], + "com.google.cloud.opentelemetry:exporter-metrics": [ + "com.google.auto.value:auto-value-annotations", + "com.google.cloud.opentelemetry:shared-resourcemapping", + "com.google.cloud:google-cloud-core", + "com.google.cloud:google-cloud-monitoring", + "io.opentelemetry.semconv:opentelemetry-semconv", + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-sdk-metrics", + "org.slf4j:slf4j-api" + ], + "com.google.cloud:google-cloud-core": [ + "com.google.api.grpc:proto-google-common-protos", + "com.google.api.grpc:proto-google-iam-v1", + "com.google.api:api-common", + "com.google.api:gax", + "com.google.auth:google-auth-library-credentials", + "com.google.auth:google-auth-library-oauth2-http", + "com.google.auto.value:auto-value-annotations", + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:guava", + "com.google.http-client:google-http-client", + "com.google.http-client:google-http-client-gson", + "com.google.protobuf:protobuf-java", + "com.google.protobuf:protobuf-java-util", + "org.threeten:threetenbp" + ], "com.google.cloud:google-cloud-monitoring": [ "com.google.android:annotations", "com.google.api.grpc:proto-google-cloud-monitoring-v3", @@ -718,6 +961,40 @@ "com.google.j2objc:j2objc-annotations", "org.checkerframework:checker-qual" ], + "com.google.http-client:google-http-client": [ + "com.google.code.findbugs:jsr305", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:guava", + "com.google.j2objc:j2objc-annotations", + "io.grpc:grpc-context", + "io.opencensus:opencensus-api", + "io.opencensus:opencensus-contrib-http-util", + "org.apache.httpcomponents:httpclient", + "org.apache.httpcomponents:httpcore" + ], + "com.google.http-client:google-http-client-gson": [ + "com.google.code.gson:gson", + "com.google.http-client:google-http-client" + ], + "com.google.protobuf:protobuf-java-util": [ + "com.google.code.findbugs:jsr305", + "com.google.code.gson:gson", + "com.google.errorprone:error_prone_annotations", + "com.google.guava:guava", + "com.google.j2objc:j2objc-annotations", + "com.google.protobuf:protobuf-java" + ], + "com.squareup.okhttp3:okhttp": [ + "com.squareup.okio:okio", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + ], + "com.squareup.okio:okio": [ + "com.squareup.okio:okio-jvm" + ], + "com.squareup.okio:okio-jvm": [ + "org.jetbrains.kotlin:kotlin-stdlib-common", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + ], "com.uber.nullaway:nullaway": [ "com.google.guava:guava", "org.checkerframework:dataflow-nullaway" @@ -728,6 +1005,9 @@ "org.apache.commons:commons-lang3", "org.slf4j:slf4j-api" ], + "io.grpc:grpc-context": [ + "io.grpc:grpc-api" + ], "io.netty:netty-all": [ "io.netty:netty-buffer", "io.netty:netty-codec", @@ -873,6 +1153,68 @@ "io.netty:netty-common", "io.netty:netty-transport" ], + "io.opencensus:opencensus-api": [ + "io.grpc:grpc-context" + ], + "io.opencensus:opencensus-contrib-http-util": [ + "com.google.guava:guava", + "io.opencensus:opencensus-api" + ], + "io.opentelemetry:opentelemetry-api": [ + "io.opentelemetry:opentelemetry-context" + ], + "io.opentelemetry:opentelemetry-api-incubator": [ + "io.opentelemetry:opentelemetry-api" + ], + "io.opentelemetry:opentelemetry-exporter-common": [ + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi" + ], + "io.opentelemetry:opentelemetry-exporter-otlp": [ + "io.opentelemetry:opentelemetry-exporter-otlp-common", + "io.opentelemetry:opentelemetry-exporter-sender-okhttp", + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi", + "io.opentelemetry:opentelemetry-sdk-logs", + "io.opentelemetry:opentelemetry-sdk-metrics", + "io.opentelemetry:opentelemetry-sdk-trace" + ], + "io.opentelemetry:opentelemetry-exporter-otlp-common": [ + "io.opentelemetry:opentelemetry-api-incubator", + "io.opentelemetry:opentelemetry-exporter-common" + ], + "io.opentelemetry:opentelemetry-exporter-sender-okhttp": [ + "com.squareup.okhttp3:okhttp", + "io.opentelemetry:opentelemetry-exporter-common", + "io.opentelemetry:opentelemetry-sdk-common" + ], + "io.opentelemetry:opentelemetry-sdk": [ + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-sdk-common", + "io.opentelemetry:opentelemetry-sdk-logs", + "io.opentelemetry:opentelemetry-sdk-metrics", + "io.opentelemetry:opentelemetry-sdk-trace" + ], + "io.opentelemetry:opentelemetry-sdk-common": [ + "io.opentelemetry:opentelemetry-api" + ], + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi": [ + "io.opentelemetry:opentelemetry-sdk" + ], + "io.opentelemetry:opentelemetry-sdk-logs": [ + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-api-incubator", + "io.opentelemetry:opentelemetry-sdk-common" + ], + "io.opentelemetry:opentelemetry-sdk-metrics": [ + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-api-incubator", + "io.opentelemetry:opentelemetry-sdk-common" + ], + "io.opentelemetry:opentelemetry-sdk-trace": [ + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-api-incubator", + "io.opentelemetry:opentelemetry-sdk-common" + ], "org.apache.httpcomponents:httpclient": [ "commons-codec:commons-codec", "commons-logging:commons-logging", @@ -881,6 +1223,17 @@ "org.assertj:assertj-core": [ "net.bytebuddy:byte-buddy" ], + "org.jetbrains.kotlin:kotlin-stdlib": [ + "org.jetbrains.kotlin:kotlin-stdlib-common", + "org.jetbrains:annotations" + ], + "org.jetbrains.kotlin:kotlin-stdlib-jdk7": [ + "org.jetbrains.kotlin:kotlin-stdlib" + ], + "org.jetbrains.kotlin:kotlin-stdlib-jdk8": [ + "org.jetbrains.kotlin:kotlin-stdlib", + "org.jetbrains.kotlin:kotlin-stdlib-jdk7" + ], "org.mockito:mockito-core": [ "net.bytebuddy:byte-buddy", "net.bytebuddy:byte-buddy-agent", @@ -915,6 +1268,10 @@ "com.google.shopping.type", "com.google.type" ], + "com.google.api.grpc:proto-google-iam-v1": [ + "com.google.iam.v1", + "com.google.iam.v1.logging" + ], "com.google.api:api-common": [ "com.google.api.core", "com.google.api.pathtemplate", @@ -952,6 +1309,18 @@ "com.google.auto.value.extension.serializable", "com.google.auto.value.extension.toprettystring" ], + "com.google.cloud.opentelemetry:exporter-metrics": [ + "com.google.cloud.opentelemetry.metric" + ], + "com.google.cloud.opentelemetry:shared-resourcemapping": [ + "com.google.cloud.opentelemetry.resource", + "com.google.cloud.opentelemetry.shadow.semconv" + ], + "com.google.cloud:google-cloud-core": [ + "com.google.cloud", + "com.google.cloud.spi", + "com.google.cloud.testing" + ], "com.google.cloud:google-cloud-monitoring": [ "com.google.cloud.monitoring.v3", "com.google.cloud.monitoring.v3.stub" @@ -1033,6 +1402,29 @@ "com.google.re2j:re2j": [ "com.google.re2j" ], + "com.squareup.okhttp3:okhttp": [ + "okhttp3", + "okhttp3.internal", + "okhttp3.internal.authenticator", + "okhttp3.internal.cache", + "okhttp3.internal.cache2", + "okhttp3.internal.concurrent", + "okhttp3.internal.connection", + "okhttp3.internal.http", + "okhttp3.internal.http1", + "okhttp3.internal.http2", + "okhttp3.internal.io", + "okhttp3.internal.platform", + "okhttp3.internal.platform.android", + "okhttp3.internal.proxy", + "okhttp3.internal.publicsuffix", + "okhttp3.internal.tls", + "okhttp3.internal.ws" + ], + "com.squareup.okio:okio-jvm": [ + "okio", + "okio.internal" + ], "com.uber.nullaway:nullaway": [ "com.uber.nullaway", "com.uber.nullaway.annotations", @@ -1402,6 +1794,122 @@ "io.opencensus.contrib.http", "io.opencensus.contrib.http.util" ], + "io.opentelemetry.semconv:opentelemetry-semconv": [ + "io.opentelemetry.semconv" + ], + "io.opentelemetry:opentelemetry-api": [ + "io.opentelemetry.api", + "io.opentelemetry.api.baggage", + "io.opentelemetry.api.baggage.propagation", + "io.opentelemetry.api.common", + "io.opentelemetry.api.internal", + "io.opentelemetry.api.logs", + "io.opentelemetry.api.metrics", + "io.opentelemetry.api.trace", + "io.opentelemetry.api.trace.propagation", + "io.opentelemetry.api.trace.propagation.internal" + ], + "io.opentelemetry:opentelemetry-api-incubator": [ + "io.opentelemetry.api.incubator.events", + "io.opentelemetry.api.incubator.logs", + "io.opentelemetry.api.incubator.metrics", + "io.opentelemetry.api.incubator.propagation", + "io.opentelemetry.api.incubator.trace" + ], + "io.opentelemetry:opentelemetry-context": [ + "io.opentelemetry.context", + "io.opentelemetry.context.internal.shaded", + "io.opentelemetry.context.propagation" + ], + "io.opentelemetry:opentelemetry-exporter-common": [ + "io.opentelemetry.exporter.internal", + "io.opentelemetry.exporter.internal.auth", + "io.opentelemetry.exporter.internal.compression", + "io.opentelemetry.exporter.internal.grpc", + "io.opentelemetry.exporter.internal.http", + "io.opentelemetry.exporter.internal.marshal" + ], + "io.opentelemetry:opentelemetry-exporter-otlp": [ + "io.opentelemetry.exporter.otlp.all.internal", + "io.opentelemetry.exporter.otlp.http.logs", + "io.opentelemetry.exporter.otlp.http.metrics", + "io.opentelemetry.exporter.otlp.http.trace", + "io.opentelemetry.exporter.otlp.internal", + "io.opentelemetry.exporter.otlp.logs", + "io.opentelemetry.exporter.otlp.metrics", + "io.opentelemetry.exporter.otlp.trace" + ], + "io.opentelemetry:opentelemetry-exporter-otlp-common": [ + "io.opentelemetry.exporter.internal.otlp", + "io.opentelemetry.exporter.internal.otlp.logs", + "io.opentelemetry.exporter.internal.otlp.metrics", + "io.opentelemetry.exporter.internal.otlp.traces", + "io.opentelemetry.proto.collector.logs.v1.internal", + "io.opentelemetry.proto.collector.metrics.v1.internal", + "io.opentelemetry.proto.collector.profiles.v1experimental.internal", + "io.opentelemetry.proto.collector.trace.v1.internal", + "io.opentelemetry.proto.common.v1.internal", + "io.opentelemetry.proto.logs.v1.internal", + "io.opentelemetry.proto.metrics.v1.internal", + "io.opentelemetry.proto.profiles.v1experimental.internal", + "io.opentelemetry.proto.resource.v1.internal", + "io.opentelemetry.proto.trace.v1.internal" + ], + "io.opentelemetry:opentelemetry-exporter-sender-okhttp": [ + "io.opentelemetry.exporter.sender.okhttp.internal" + ], + "io.opentelemetry:opentelemetry-sdk": [ + "io.opentelemetry.sdk" + ], + "io.opentelemetry:opentelemetry-sdk-common": [ + "io.opentelemetry.sdk.common", + "io.opentelemetry.sdk.common.export", + "io.opentelemetry.sdk.common.internal", + "io.opentelemetry.sdk.internal", + "io.opentelemetry.sdk.resources" + ], + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi": [ + "io.opentelemetry.sdk.autoconfigure.spi", + "io.opentelemetry.sdk.autoconfigure.spi.internal", + "io.opentelemetry.sdk.autoconfigure.spi.logs", + "io.opentelemetry.sdk.autoconfigure.spi.metrics", + "io.opentelemetry.sdk.autoconfigure.spi.traces" + ], + "io.opentelemetry:opentelemetry-sdk-logs": [ + "io.opentelemetry.sdk.logs", + "io.opentelemetry.sdk.logs.data", + "io.opentelemetry.sdk.logs.export", + "io.opentelemetry.sdk.logs.internal" + ], + "io.opentelemetry:opentelemetry-sdk-metrics": [ + "io.opentelemetry.sdk.metrics", + "io.opentelemetry.sdk.metrics.data", + "io.opentelemetry.sdk.metrics.export", + "io.opentelemetry.sdk.metrics.internal", + "io.opentelemetry.sdk.metrics.internal.aggregator", + "io.opentelemetry.sdk.metrics.internal.concurrent", + "io.opentelemetry.sdk.metrics.internal.data", + "io.opentelemetry.sdk.metrics.internal.debug", + "io.opentelemetry.sdk.metrics.internal.descriptor", + "io.opentelemetry.sdk.metrics.internal.exemplar", + "io.opentelemetry.sdk.metrics.internal.export", + "io.opentelemetry.sdk.metrics.internal.state", + "io.opentelemetry.sdk.metrics.internal.view" + ], + "io.opentelemetry:opentelemetry-sdk-trace": [ + "io.opentelemetry.internal.shaded.jctools.counters", + "io.opentelemetry.internal.shaded.jctools.maps", + "io.opentelemetry.internal.shaded.jctools.queues", + "io.opentelemetry.internal.shaded.jctools.queues.atomic", + "io.opentelemetry.internal.shaded.jctools.queues.atomic.unpadded", + "io.opentelemetry.internal.shaded.jctools.queues.unpadded", + "io.opentelemetry.internal.shaded.jctools.util", + "io.opentelemetry.sdk.trace", + "io.opentelemetry.sdk.trace.data", + "io.opentelemetry.sdk.trace.export", + "io.opentelemetry.sdk.trace.internal", + "io.opentelemetry.sdk.trace.samplers" + ], "io.perfmark:perfmark-api": [ "io.perfmark" ], @@ -1640,6 +2148,55 @@ "org.conscrypt.ct", "org.conscrypt.io" ], + "org.jetbrains.kotlin:kotlin-stdlib": [ + "kotlin", + "kotlin.annotation", + "kotlin.collections", + "kotlin.collections.builders", + "kotlin.collections.jdk8", + "kotlin.collections.unsigned", + "kotlin.comparisons", + "kotlin.concurrent", + "kotlin.contracts", + "kotlin.coroutines", + "kotlin.coroutines.cancellation", + "kotlin.coroutines.intrinsics", + "kotlin.coroutines.jvm.internal", + "kotlin.enums", + "kotlin.experimental", + "kotlin.internal", + "kotlin.internal.jdk7", + "kotlin.internal.jdk8", + "kotlin.io", + "kotlin.io.encoding", + "kotlin.io.path", + "kotlin.jdk7", + "kotlin.js", + "kotlin.jvm", + "kotlin.jvm.functions", + "kotlin.jvm.internal", + "kotlin.jvm.internal.markers", + "kotlin.jvm.internal.unsafe", + "kotlin.jvm.jdk8", + "kotlin.jvm.optionals", + "kotlin.math", + "kotlin.properties", + "kotlin.random", + "kotlin.random.jdk8", + "kotlin.ranges", + "kotlin.reflect", + "kotlin.sequences", + "kotlin.streams.jdk8", + "kotlin.system", + "kotlin.text", + "kotlin.text.jdk8", + "kotlin.time", + "kotlin.time.jdk8" + ], + "org.jetbrains:annotations": [ + "org.intellij.lang.annotations", + "org.jetbrains.annotations" + ], "org.mockito:mockito-core": [ "org.mockito", "org.mockito.codegen", @@ -1744,6 +2301,8 @@ "com.google.api.grpc:proto-google-cloud-monitoring-v3:jar:sources", "com.google.api.grpc:proto-google-common-protos", "com.google.api.grpc:proto-google-common-protos:jar:sources", + "com.google.api.grpc:proto-google-iam-v1", + "com.google.api.grpc:proto-google-iam-v1:jar:sources", "com.google.api:api-common", "com.google.api:api-common:jar:sources", "com.google.api:gax", @@ -1756,6 +2315,12 @@ "com.google.auth:google-auth-library-oauth2-http:jar:sources", "com.google.auto.value:auto-value-annotations", "com.google.auto.value:auto-value-annotations:jar:sources", + "com.google.cloud.opentelemetry:exporter-metrics", + "com.google.cloud.opentelemetry:exporter-metrics:jar:sources", + "com.google.cloud.opentelemetry:shared-resourcemapping", + "com.google.cloud.opentelemetry:shared-resourcemapping:jar:sources", + "com.google.cloud:google-cloud-core", + "com.google.cloud:google-cloud-core:jar:sources", "com.google.cloud:google-cloud-monitoring", "com.google.cloud:google-cloud-monitoring:jar:sources", "com.google.code.findbugs:jsr305", @@ -1781,6 +2346,12 @@ "com.google.protobuf:protobuf-java:jar:sources", "com.google.re2j:re2j", "com.google.re2j:re2j:jar:sources", + "com.squareup.okhttp3:okhttp", + "com.squareup.okhttp3:okhttp:jar:sources", + "com.squareup.okio:okio", + "com.squareup.okio:okio-jvm", + "com.squareup.okio:okio-jvm:jar:sources", + "com.squareup.okio:okio:jar:sources", "com.uber.nullaway:nullaway", "com.uber.nullaway:nullaway:jar:sources", "commons-codec:commons-codec", @@ -1888,6 +2459,34 @@ "io.opencensus:opencensus-api:jar:sources", "io.opencensus:opencensus-contrib-http-util", "io.opencensus:opencensus-contrib-http-util:jar:sources", + "io.opentelemetry.semconv:opentelemetry-semconv", + "io.opentelemetry.semconv:opentelemetry-semconv:jar:sources", + "io.opentelemetry:opentelemetry-api", + "io.opentelemetry:opentelemetry-api-incubator", + "io.opentelemetry:opentelemetry-api-incubator:jar:sources", + "io.opentelemetry:opentelemetry-api:jar:sources", + "io.opentelemetry:opentelemetry-context", + "io.opentelemetry:opentelemetry-context:jar:sources", + "io.opentelemetry:opentelemetry-exporter-common", + "io.opentelemetry:opentelemetry-exporter-common:jar:sources", + "io.opentelemetry:opentelemetry-exporter-otlp", + "io.opentelemetry:opentelemetry-exporter-otlp-common", + "io.opentelemetry:opentelemetry-exporter-otlp-common:jar:sources", + "io.opentelemetry:opentelemetry-exporter-otlp:jar:sources", + "io.opentelemetry:opentelemetry-exporter-sender-okhttp", + "io.opentelemetry:opentelemetry-exporter-sender-okhttp:jar:sources", + "io.opentelemetry:opentelemetry-sdk", + "io.opentelemetry:opentelemetry-sdk-common", + "io.opentelemetry:opentelemetry-sdk-common:jar:sources", + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi", + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:jar:sources", + "io.opentelemetry:opentelemetry-sdk-logs", + "io.opentelemetry:opentelemetry-sdk-logs:jar:sources", + "io.opentelemetry:opentelemetry-sdk-metrics", + "io.opentelemetry:opentelemetry-sdk-metrics:jar:sources", + "io.opentelemetry:opentelemetry-sdk-trace", + "io.opentelemetry:opentelemetry-sdk-trace:jar:sources", + "io.opentelemetry:opentelemetry-sdk:jar:sources", "io.perfmark:perfmark-api", "io.perfmark:perfmark-api:jar:sources", "javax.annotation:javax.annotation-api", @@ -1912,6 +2511,16 @@ "org.codehaus.mojo:animal-sniffer-annotations:jar:sources", "org.conscrypt:conscrypt-openjdk-uber", "org.conscrypt:conscrypt-openjdk-uber:jar:sources", + "org.jetbrains.kotlin:kotlin-stdlib", + "org.jetbrains.kotlin:kotlin-stdlib-common", + "org.jetbrains.kotlin:kotlin-stdlib-common:jar:sources", + "org.jetbrains.kotlin:kotlin-stdlib-jdk7", + "org.jetbrains.kotlin:kotlin-stdlib-jdk7:jar:sources", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar:sources", + "org.jetbrains.kotlin:kotlin-stdlib:jar:sources", + "org.jetbrains:annotations", + "org.jetbrains:annotations:jar:sources", "org.mockito:mockito-core", "org.mockito:mockito-core:jar:sources", "org.objenesis:objenesis", diff --git a/src/main/java/com/glean/proxy/ProxyNetworking.java b/src/main/java/com/glean/proxy/ProxyNetworking.java index 86262d3..ec868eb 100644 --- a/src/main/java/com/glean/proxy/ProxyNetworking.java +++ b/src/main/java/com/glean/proxy/ProxyNetworking.java @@ -1,9 +1,8 @@ package com.glean.proxy; +import com.glean.proxy.metrics.OpenTelemetrySetup; import com.glean.proxy.metrics.ProxyMetrics; -import com.glean.proxy.metrics.ProxyMetricsExporter; import java.net.InetSocketAddress; -import java.time.Clock; import java.util.logging.Logger; import org.littleshoot.proxy.ChainedProxyManager; import org.littleshoot.proxy.HttpProxyServer; @@ -12,7 +11,7 @@ public class ProxyNetworking { private static final Logger logger = Logger.getLogger(ProxyNetworking.class.getName()); - private static ProxyMetricsExporter metricsExporter; + private static OpenTelemetrySetup openTelemetrySetup; protected final ThreadPoolConfiguration threadPoolConfiguration; protected final FilterConfiguration filterConfiguration; @@ -73,6 +72,10 @@ public ProxyNetworking build() { if (threadPoolConfiguration == null) { threadPoolConfiguration = createThreadPoolConfigurationFromEnvironment(); } + + // Initialize metrics exporter before filter configuration, since filters use ProxyMetrics + initializeMetricsExporter(); + if (filterConfiguration == null) { filterConfiguration = FilterConfiguration.fromEnvironment(); } @@ -80,9 +83,6 @@ public ProxyNetworking build() { chainedProxyManager = ChainedProxyConfiguration.fromEnvironment(); } - // Initialize metrics exporter - initializeMetricsExporter(); - return new ProxyNetworking(this); } } @@ -91,26 +91,23 @@ private static void initializeMetricsExporter() { String gcpProjectId = System.getenv("GCP_PROJECT_ID"); String enableMetricsExport = System.getenv("ENABLE_METRICS_EXPORT"); - // Always initialize ProxyMetrics for local tracking - ProxyMetrics metrics = ProxyMetrics.getInstance(); - // Check if metrics export is explicitly disabled if (enableMetricsExport != null && (enableMetricsExport.equalsIgnoreCase("false") || enableMetricsExport.equals("0"))) { - logger.info("Metrics export disabled via ENABLE_METRICS_EXPORT. Metrics will be tracked locally only."); + logger.info("Metrics export disabled via ENABLE_METRICS_EXPORT."); return; } if (gcpProjectId == null || gcpProjectId.isEmpty()) { - logger.warning("GCP_PROJECT_ID not set. Metrics will be tracked but not exported to Cloud Monitoring."); + logger.warning("Set GCP_PROJECT_ID environment variable to enable metric export."); return; } try { - logger.info("Initializing ProxyMetricsExporter for project: " + gcpProjectId); - metricsExporter = new ProxyMetricsExporter(gcpProjectId, Clock.systemUTC(), metrics.getMetrics()); + openTelemetrySetup = new OpenTelemetrySetup(gcpProjectId); + ProxyMetrics.initialize(openTelemetrySetup.getMeter()); } catch (Exception e) { - logger.severe("Failed to initialize ProxyMetricsExporter: " + e.getMessage()); + logger.severe("Failed to initialize OpenTelemetry: " + e.getMessage()); } } diff --git a/src/main/java/com/glean/proxy/metrics/BUILD.bazel b/src/main/java/com/glean/proxy/metrics/BUILD.bazel index 56061e3..aba2147 100644 --- a/src/main/java/com/glean/proxy/metrics/BUILD.bazel +++ b/src/main/java/com/glean/proxy/metrics/BUILD.bazel @@ -11,6 +11,12 @@ java_library( "@maven//:com_google_api_grpc_proto_google_cloud_monitoring_v3", "@maven//:com_google_api_grpc_proto_google_common_protos", "@maven//:com_google_cloud_google_cloud_monitoring", + "@maven//:com_google_cloud_opentelemetry_exporter_metrics", "@maven//:com_google_protobuf_protobuf_java", + "@maven//:io_opentelemetry_opentelemetry_api", + "@maven//:io_opentelemetry_opentelemetry_sdk", + "@maven//:io_opentelemetry_opentelemetry_sdk_common", + "@maven//:io_opentelemetry_opentelemetry_sdk_metrics", + "@maven//:io_opentelemetry_semconv_opentelemetry_semconv", ], ) diff --git a/src/main/java/com/glean/proxy/metrics/MetricData.java b/src/main/java/com/glean/proxy/metrics/MetricData.java deleted file mode 100644 index abdd27b..0000000 --- a/src/main/java/com/glean/proxy/metrics/MetricData.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.glean.proxy.metrics; - -import java.util.Objects; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Stores aggregated metric data for a specific tag combination. - * Thread-safe using atomic operations. - */ -public class MetricData { - // Latency bucket boundaries in milliseconds - // Creates buckets: [0,10), [10,25), [25,50), [50,100), [100,250), [250,500), - // [500,1000), [1000,2500), [2500,5000), [5000,10000), [10000,∞) - public static final double[] BUCKET_BOUNDS = { - 0, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000 - }; - - private final String statusCode; - private final String method; - private final AtomicLong count = new AtomicLong(0); - private final AtomicLong totalLatencyMs = new AtomicLong(0); - private final AtomicLong minLatencyMs = new AtomicLong(Long.MAX_VALUE); - private final AtomicLong maxLatencyMs = new AtomicLong(0); - - // Histogram buckets: one more than bounds (for overflow bucket) - private final AtomicLong[] bucketCounts = new AtomicLong[BUCKET_BOUNDS.length + 1]; - - public MetricData(String statusCode, String method) { - this.statusCode = statusCode; - this.method = method; - - // Initialize bucket counters - for (int i = 0; i < bucketCounts.length; i++) { - bucketCounts[i] = new AtomicLong(0); - } - } - - public void record(long latencyMs) { - count.incrementAndGet(); - totalLatencyMs.addAndGet(latencyMs); - - // Update histogram bucket - int bucketIndex = findBucketIndex(latencyMs); - bucketCounts[bucketIndex].incrementAndGet(); - - // Update min - long currentMin = minLatencyMs.get(); - while (latencyMs < currentMin) { - if (minLatencyMs.compareAndSet(currentMin, latencyMs)) { - break; - } - currentMin = minLatencyMs.get(); - } - - // Update max - long currentMax = maxLatencyMs.get(); - while (latencyMs > currentMax) { - if (maxLatencyMs.compareAndSet(currentMax, latencyMs)) { - break; - } - currentMax = maxLatencyMs.get(); - } - } - - private int findBucketIndex(long latencyMs) { - for (int i = 0; i < BUCKET_BOUNDS.length; i++) { - if (latencyMs < BUCKET_BOUNDS[i]) { - return i; - } - } - // Overflow bucket (values >= last bound) - return BUCKET_BOUNDS.length; - } - - public String getStatusCode() { - return statusCode; - } - - public String getMethod() { - return method; - } - - public long getCount() { - return count.get(); - } - - public long getTotalLatencyMs() { - return totalLatencyMs.get(); - } - - public long getMinLatencyMs() { - long min = minLatencyMs.get(); - return min == Long.MAX_VALUE ? 0 : min; - } - - public long getMaxLatencyMs() { - return maxLatencyMs.get(); - } - - public double getAvgLatencyMs() { - long c = count.get(); - return c == 0 ? 0 : (double) totalLatencyMs.get() / c; - } - - /** - * Get bucket counts for histogram. - * Returns a snapshot of current bucket counts. - */ - public long[] getBucketCounts() { - long[] counts = new long[bucketCounts.length]; - for (int i = 0; i < bucketCounts.length; i++) { - counts[i] = bucketCounts[i].get(); - } - return counts; - } - - /** - * Reset all metrics (called after export). - */ - public void reset() { - count.set(0); - totalLatencyMs.set(0); - minLatencyMs.set(Long.MAX_VALUE); - maxLatencyMs.set(0); - - // Reset histogram buckets - for (AtomicLong bucket : bucketCounts) { - bucket.set(0); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - MetricData that = (MetricData) o; - return Objects.equals(statusCode, that.statusCode) && Objects.equals(method, that.method); - } - - @Override - public int hashCode() { - return Objects.hash(statusCode, method); - } - - @Override - public String toString() { - return String.format( - "MetricData{status=%s, method=%s, count=%d, avg=%.2fms, min=%dms, max=%dms}", - statusCode, method, getCount(), getAvgLatencyMs(), getMinLatencyMs(), getMaxLatencyMs()); - } -} diff --git a/src/main/java/com/glean/proxy/metrics/OpenTelemetrySetup.java b/src/main/java/com/glean/proxy/metrics/OpenTelemetrySetup.java new file mode 100644 index 0000000..d9ea80c --- /dev/null +++ b/src/main/java/com/glean/proxy/metrics/OpenTelemetrySetup.java @@ -0,0 +1,84 @@ +package com.glean.proxy.metrics; + +import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; +import com.google.cloud.opentelemetry.metric.MetricConfiguration; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ResourceAttributes; +import java.io.Closeable; +import java.time.Duration; +import java.util.logging.Logger; + +/** + * Initializes and configures OpenTelemetry with GCP Cloud Monitoring exporter. + */ +public class OpenTelemetrySetup implements Closeable { + private static final Logger logger = Logger.getLogger(OpenTelemetrySetup.class.getName()); + private static final String INSTRUMENTATION_SCOPE_NAME = "glean-proxy"; + + private final OpenTelemetrySdk openTelemetry; + private final SdkMeterProvider meterProvider; + + public OpenTelemetrySetup(String projectId) { + try { + logger.info("Initializing OpenTelemetry for project: " + projectId); + + // Create resource with service name + Resource resource = Resource.getDefault() + .merge(Resource.builder() + .put(ResourceAttributes.SERVICE_NAME, "glean-proxy") + .put(ResourceAttributes.SERVICE_NAMESPACE, "glean") + .build()); + + // Create GCP Cloud Monitoring exporter + MetricConfiguration metricConfiguration = MetricConfiguration.builder() + .setProjectId(projectId) + .setPrefix("custom.googleapis.com") // Use custom prefix instead of default workload.googleapis.com + .build(); + MetricExporter metricExporter = + GoogleCloudMetricExporter.createWithConfiguration(metricConfiguration); + + // Create periodic metric reader (exports every 60 seconds) + PeriodicMetricReader metricReader = PeriodicMetricReader.builder(metricExporter) + .setInterval(Duration.ofSeconds(60)) + .build(); + + // Create meter provider + this.meterProvider = SdkMeterProvider.builder() + .setResource(resource) + .registerMetricReader(metricReader) + .build(); + + // Build OpenTelemetry SDK + this.openTelemetry = OpenTelemetrySdk.builder() + .setMeterProvider(meterProvider) + .build(); + + + } catch (Exception e) { + logger.severe("Failed to initialize OpenTelemetry: " + e.getMessage()); + } + } + + public Meter getMeter() { + return openTelemetry.getMeter(INSTRUMENTATION_SCOPE_NAME); + } + + public OpenTelemetry getOpenTelemetry() { + return openTelemetry; + } + + @Override + public void close() { + logger.info("Shutting down OpenTelemetry..."); + if (meterProvider != null) { + meterProvider.close(); + } + logger.info("OpenTelemetry shut down"); + } +} diff --git a/src/main/java/com/glean/proxy/metrics/ProxyMetrics.java b/src/main/java/com/glean/proxy/metrics/ProxyMetrics.java index ad73bc5..37fcf91 100644 --- a/src/main/java/com/glean/proxy/metrics/ProxyMetrics.java +++ b/src/main/java/com/glean/proxy/metrics/ProxyMetrics.java @@ -1,36 +1,66 @@ package com.glean.proxy.metrics; -import com.glean.proxy.metrics.ProxyMetricsExporter.MetricKey; -import java.util.concurrent.ConcurrentHashMap; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; import java.util.logging.Logger; /** - * Metrics collector for proxy requests. - * Uses manual in-memory tracking instead of OpenCensus views. + * Metrics collector for proxy requests using OpenTelemetry. + * Records request counts and latency distributions with labels for status code and method. */ public class ProxyMetrics { private static final Logger logger = Logger.getLogger(ProxyMetrics.class.getName()); private static ProxyMetrics instance; - // Manual metric tracking (thread-safe) - private final ConcurrentHashMap metrics = new ConcurrentHashMap<>(); + // Attribute keys for labels + private static final AttributeKey STATUS_CODE = AttributeKey.stringKey("status_code"); + private static final AttributeKey METHOD = AttributeKey.stringKey("method"); - public static synchronized ProxyMetrics getInstance() { + private final LongCounter requestCounter; + private final DoubleHistogram latencyHistogram; + + private ProxyMetrics(Meter meter) { + this.requestCounter = meter + .counterBuilder("proxy/request_count") + .setDescription("Total number of proxy requests") + .setUnit("1") + .build(); + + this.latencyHistogram = meter + .histogramBuilder("proxy/request_latency") + .setDescription("Latency of proxy requests in milliseconds") + .setUnit("ms") + .build(); + + logger.info("ProxyMetrics initialized with OpenTelemetry"); + } + + public static synchronized void initialize(Meter meter) { if (instance == null) { - instance = new ProxyMetrics(); + instance = new ProxyMetrics(meter); } - return instance; } - public ConcurrentHashMap getMetrics() { - return metrics; + public static synchronized ProxyMetrics getInstance() { + if (instance == null) { + throw new IllegalStateException("ProxyMetrics not initialized. Call initialize(Meter) first."); + } + return instance; } public void recordRequest(int statusCode, String method, long latencyMs) { try { - MetricKey key = new MetricKey(String.valueOf(statusCode), method); - MetricData data = metrics.computeIfAbsent(key, k -> new MetricData(k.getStatusCode(), k.getMethod())); - data.record(latencyMs); + Attributes attributes = Attributes.builder() + .put(STATUS_CODE, String.valueOf(statusCode)) + .put(METHOD, method) + .build(); + + requestCounter.add(1, attributes); + latencyHistogram.record(latencyMs, attributes); + } catch (Exception e) { logger.severe("Failed to record metrics: " + e.getMessage()); } diff --git a/src/main/java/com/glean/proxy/metrics/ProxyMetricsExporter.java b/src/main/java/com/glean/proxy/metrics/ProxyMetricsExporter.java deleted file mode 100644 index da3f0aa..0000000 --- a/src/main/java/com/glean/proxy/metrics/ProxyMetricsExporter.java +++ /dev/null @@ -1,278 +0,0 @@ -package com.glean.proxy.metrics; - -import com.google.api.Distribution; -import com.google.api.Distribution.BucketOptions; -import com.google.api.Distribution.BucketOptions.Explicit; -import com.google.api.Metric; -import com.google.api.MonitoredResource; -import com.google.cloud.monitoring.v3.MetricServiceClient; -import com.google.monitoring.v3.CreateTimeSeriesRequest; -import com.google.monitoring.v3.Point; -import com.google.monitoring.v3.ProjectName; -import com.google.monitoring.v3.TimeInterval; -import com.google.monitoring.v3.TimeSeries; -import com.google.monitoring.v3.TypedValue; -import com.google.protobuf.Timestamp; -import java.io.Closeable; -import java.time.Clock; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -/** - * Periodically exports proxy metrics to GCP Cloud Monitoring. - */ -public class ProxyMetricsExporter implements Closeable { - private static final Logger logger = Logger.getLogger(ProxyMetricsExporter.class.getName()); - private static final long EXPORT_INTERVAL_MS = 60000; // 60 seconds - - private final String projectId; - private final Clock clock; - private final Timer exportTimer; - private final ConcurrentHashMap metrics; - private MetricServiceClient metricServiceClient; - - public ProxyMetricsExporter( - String projectId, Clock clock, ConcurrentHashMap metrics) { - this.projectId = projectId; - this.clock = clock; - this.metrics = metrics; - this.exportTimer = new Timer("ProxyMetricsExporter", true); - - // Initialize GCP Monitoring client - try { - this.metricServiceClient = MetricServiceClient.create(); - logger.info("MetricServiceClient created successfully"); - } catch (Exception e) { - logger.severe("Failed to create MetricServiceClient: " + e.getMessage()); - e.printStackTrace(); - } - - // Schedule periodic exports - exportTimer.scheduleAtFixedRate( - new TimerTask() { - @Override - public void run() { - exportMetrics(); - } - }, - EXPORT_INTERVAL_MS, - EXPORT_INTERVAL_MS); - - logger.info( - String.format( - "ProxyMetricsExporter initialized for project %s, exporting every %d seconds", - projectId, EXPORT_INTERVAL_MS / 1000)); - } - - private void exportMetrics() { - if (metricServiceClient == null) { - logger.warning("MetricServiceClient not initialized, skipping export"); - return; - } - - try { - Instant now = clock.instant(); - List timeSeriesList = new ArrayList<>(); - - // Snapshot and reset metrics - Map snapshot = new ConcurrentHashMap<>(metrics); - int totalMetrics = 0; - - for (Map.Entry entry : snapshot.entrySet()) { - MetricKey key = entry.getKey(); - MetricData data = entry.getValue(); - - long count = data.getCount(); - if (count == 0) { - continue; // Skip empty metrics - } - - totalMetrics++; - - // Create time series for request count - timeSeriesList.add(createCountTimeSeries(key, data, now)); - - // Create time series for request latency (distribution) - timeSeriesList.add(createLatencyTimeSeries(key, data, now)); - - // Reset the metric after export - MetricData originalData = metrics.get(key); - if (originalData != null) { - originalData.reset(); - } - } - - if (timeSeriesList.isEmpty()) { - logger.fine("No metrics to export"); - return; - } - - // Send to GCP Cloud Monitoring - ProjectName projectName = ProjectName.of(projectId); - CreateTimeSeriesRequest request = - CreateTimeSeriesRequest.newBuilder() - .setName(projectName.toString()) - .addAllTimeSeries(timeSeriesList) - .build(); - - metricServiceClient.createTimeSeries(request); - - logger.info( - String.format( - "Successfully exported %d metrics (%d time series) to Cloud Monitoring", - totalMetrics, timeSeriesList.size())); - - } catch (Exception e) { - logger.severe("Failed to export metrics: " + e.getMessage()); - e.printStackTrace(); - } - } - - private TimeSeries createCountTimeSeries(MetricKey key, MetricData data, Instant now) { - Metric metric = - Metric.newBuilder() - .setType("custom.googleapis.com/proxy/request_count") - .putLabels("status_code", key.getStatusCode()) - .putLabels("method", key.getMethod()) - .build(); - - MonitoredResource resource = - MonitoredResource.newBuilder().setType("global").putLabels("project_id", projectId).build(); - - Timestamp timestamp = Timestamp.newBuilder().setSeconds(now.getEpochSecond()).build(); - TimeInterval interval = - TimeInterval.newBuilder().setEndTime(timestamp).setStartTime(timestamp).build(); - - Point point = - Point.newBuilder() - .setInterval(interval) - .setValue(TypedValue.newBuilder().setInt64Value(data.getCount()).build()) - .build(); - - return TimeSeries.newBuilder() - .setMetric(metric) - .setResource(resource) - .setValueType(com.google.api.MetricDescriptor.ValueType.INT64) - .setMetricKind(com.google.api.MetricDescriptor.MetricKind.GAUGE) - .addPoints(point) - .build(); - } - - private TimeSeries createLatencyTimeSeries(MetricKey key, MetricData data, Instant now) { - Metric metric = - Metric.newBuilder() - .setType("custom.googleapis.com/proxy/request_latency_distribution") - .putLabels("status_code", key.getStatusCode()) - .putLabels("method", key.getMethod()) - .build(); - - MonitoredResource resource = - MonitoredResource.newBuilder().setType("global").putLabels("project_id", projectId).build(); - - Timestamp timestamp = Timestamp.newBuilder().setSeconds(now.getEpochSecond()).build(); - TimeInterval interval = - TimeInterval.newBuilder().setEndTime(timestamp).setStartTime(timestamp).build(); - - // Create distribution with explicit bucket boundaries - // Convert double[] to List for BucketOptions - List bounds = Arrays.stream(MetricData.BUCKET_BOUNDS) - .boxed() - .collect(Collectors.toList()); - - BucketOptions bucketOptions = - BucketOptions.newBuilder() - .setExplicitBuckets(Explicit.newBuilder().addAllBounds(bounds).build()) - .build(); - - // Get bucket counts from data - long[] bucketCounts = data.getBucketCounts(); - - Distribution.Builder distributionBuilder = - Distribution.newBuilder() - .setCount(data.getCount()) - .setMean(data.getAvgLatencyMs()) - .setBucketOptions(bucketOptions); - - // Add bucket counts (convert long[] to List) - for (long count : bucketCounts) { - distributionBuilder.addBucketCounts(count); - } - - Distribution distribution = distributionBuilder.build(); - - Point point = - Point.newBuilder() - .setInterval(interval) - .setValue(TypedValue.newBuilder().setDistributionValue(distribution).build()) - .build(); - - return TimeSeries.newBuilder() - .setMetric(metric) - .setResource(resource) - .setValueType(com.google.api.MetricDescriptor.ValueType.DISTRIBUTION) - .setMetricKind(com.google.api.MetricDescriptor.MetricKind.GAUGE) - .addPoints(point) - .build(); - } - - @Override - public void close() { - logger.info("Shutting down ProxyMetricsExporter..."); - exportTimer.cancel(); - - // Do final export - exportMetrics(); - - if (metricServiceClient != null) { - metricServiceClient.close(); - } - logger.info("ProxyMetricsExporter shut down"); - } - - /** - * Key for grouping metrics by status code and method. - */ - public static class MetricKey { - private final String statusCode; - private final String method; - - public MetricKey(String statusCode, String method) { - this.statusCode = statusCode; - this.method = method; - } - - public String getStatusCode() { - return statusCode; - } - - public String getMethod() { - return method; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - MetricKey that = (MetricKey) o; - return statusCode.equals(that.statusCode) && method.equals(that.method); - } - - @Override - public int hashCode() { - return 31 * statusCode.hashCode() + method.hashCode(); - } - - @Override - public String toString() { - return String.format("MetricKey{status=%s, method=%s}", statusCode, method); - } - } -}