Experimental browser for the Atmosphere
{ "uri": "at://did:plc:hwevmowznbiukdf6uk5dwrrq/sh.tangled.repo.pull/3lojbxfx3gh22", "cid": "bafyreidsuszlcxpi6qir7nlnvagb6gnhigg6nntqzq2ygtysmi34orq4uy", "value": { "$type": "sh.tangled.repo.pull", "patch": "From df0e8a216871ed29e17db195d90b592a2de41209 Mon Sep 17 00:00:00 2001\nFrom: Anirudh Oppiliappan <x@icyphox.sh>\nDate: Tue, 6 May 2025 19:13:38 +0300\nSubject: [PATCH 1/2] telemetry: init telemetry package\n\nChill, it's not what you think. Helpers and middleware for tracing and\nmetrics provided by OpenTelemetry.\n---\n go.mod | 27 ++++++++++---\n go.sum | 66 ++++++++++++++++++++++---------\n telemetry/middleware.go | 88 +++++++++++++++++++++++++++++++++++++++++\n telemetry/provider.go | 65 ++++++++++++++++++++++++++++++\n telemetry/telemetry.go | 64 ++++++++++++++++++++++++++++++\n 5 files changed, 286 insertions(+), 24 deletions(-)\n create mode 100644 telemetry/middleware.go\n create mode 100644 telemetry/provider.go\n create mode 100644 telemetry/telemetry.go\n\ndiff --git a/go.mod b/go.mod\nindex 8b7dc42..d68dc64 100644\n--- a/go.mod\n+++ b/go.mod\n@@ -26,6 +26,15 @@ require (\n \tgithub.com/sethvargo/go-envconfig v1.1.0\n \tgithub.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e\n \tgithub.com/yuin/goldmark v1.4.13\n+\tgo.opentelemetry.io/otel v1.35.0\n+\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0\n+\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0\n+\tgo.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0\n+\tgo.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0\n+\tgo.opentelemetry.io/otel/metric v1.35.0\n+\tgo.opentelemetry.io/otel/sdk v1.35.0\n+\tgo.opentelemetry.io/otel/sdk/metric v1.35.0\n+\tgo.opentelemetry.io/otel/trace v1.35.0\n \tgolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028\n )\n \n@@ -39,6 +48,7 @@ require (\n \tgithub.com/bmatcuk/doublestar/v4 v4.7.1 // indirect\n \tgithub.com/carlmjohnson/versioninfo v0.22.5 // indirect\n \tgithub.com/casbin/govaluate v1.3.0 // indirect\n+\tgithub.com/cenkalti/backoff/v4 v4.3.0 // indirect\n \tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n \tgithub.com/cloudflare/circl v1.6.0 // indirect\n \tgithub.com/davecgh/go-spew v1.1.1 // indirect\n@@ -47,13 +57,14 @@ require (\n \tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n \tgithub.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect\n \tgithub.com/go-git/go-billy/v5 v5.6.2 // indirect\n-\tgithub.com/go-logr/logr v1.4.1 // indirect\n+\tgithub.com/go-logr/logr v1.4.2 // indirect\n \tgithub.com/go-logr/stdr v1.2.2 // indirect\n \tgithub.com/goccy/go-json v0.10.2 // indirect\n \tgithub.com/gogo/protobuf v1.3.2 // indirect\n \tgithub.com/gorilla/css v1.0.1 // indirect\n \tgithub.com/gorilla/securecookie v1.1.2 // indirect\n \tgithub.com/gorilla/websocket v1.5.1 // indirect\n+\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect\n \tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n \tgithub.com/hashicorp/go-retryablehttp v0.7.5 // indirect\n \tgithub.com/hashicorp/golang-lru v1.0.2 // indirect\n@@ -99,18 +110,22 @@ require (\n \tgithub.com/xanzy/ssh-agent v0.3.3 // indirect\n \tgitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect\n \tgitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect\n+\tgo.opentelemetry.io/auto/sdk v1.1.0 // indirect\n \tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect\n-\tgo.opentelemetry.io/otel v1.21.0 // indirect\n-\tgo.opentelemetry.io/otel/metric v1.21.0 // indirect\n-\tgo.opentelemetry.io/otel/trace v1.21.0 // indirect\n+\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect\n+\tgo.opentelemetry.io/proto/otlp v1.5.0 // indirect\n \tgo.uber.org/atomic v1.11.0 // indirect\n \tgo.uber.org/multierr v1.11.0 // indirect\n \tgo.uber.org/zap v1.26.0 // indirect\n \tgolang.org/x/crypto v0.37.0 // indirect\n \tgolang.org/x/net v0.39.0 // indirect\n-\tgolang.org/x/sys v0.32.0 // indirect\n+\tgolang.org/x/sys v0.33.0 // indirect\n+\tgolang.org/x/text v0.24.0 // indirect\n \tgolang.org/x/time v0.5.0 // indirect\n-\tgoogle.golang.org/protobuf v1.34.2 // indirect\n+\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect\n+\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect\n+\tgoogle.golang.org/grpc v1.71.0 // indirect\n+\tgoogle.golang.org/protobuf v1.36.5 // indirect\n \tgopkg.in/warnings.v0 v0.1.2 // indirect\n \tgopkg.in/yaml.v3 v3.0.1 // indirect\n \tlukechampine.com/blake3 v1.2.1 // indirect\ndiff --git a/go.sum b/go.sum\nindex 9e5b733..f96a434 100644\n--- a/go.sum\n+++ b/go.sum\n@@ -42,6 +42,8 @@ github.com/casbin/casbin/v2 v2.103.0/go.mod h1:Ee33aqGrmES+GNL17L0h9X28wXuo829wn\n github.com/casbin/govaluate v1.2.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=\n github.com/casbin/govaluate v1.3.0 h1:VA0eSY0M2lA86dYd5kPPuNZMUD9QkWnOCnavGrw9myc=\n github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=\n+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=\n+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=\n github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\n github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\n github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=\n@@ -82,8 +84,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6\n github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk=\n github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8=\n github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\n-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=\n-github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\n+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=\n+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\n github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\n github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\n github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=\n@@ -93,9 +95,11 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\n github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\n github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=\n github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\n+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\n+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\n github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\n-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\n-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\n+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\n+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\n github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\n github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\n github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\n@@ -111,6 +115,8 @@ github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzq\n github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=\n github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=\n github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=\n+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=\n+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=\n github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\n github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=\n@@ -227,8 +233,8 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG\n github.com/resend/resend-go/v2 v2.15.0 h1:B6oMEPf8IEQwn2Ovx/9yymkESLDSeNfLFaNMw+mzHhE=\n github.com/resend/resend-go/v2 v2.15.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ=\n github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\n-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=\n-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=\n+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=\n+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=\n github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\n github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=\n github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\n@@ -268,21 +274,39 @@ gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRyS\n gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8=\n gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q=\n gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I=\n+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=\n+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=\n go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=\n go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=\n-go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=\n-go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=\n-go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=\n-go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=\n-go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=\n-go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=\n+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=\n+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=\n+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=\n+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=\n+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=\n+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=\n+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=\n+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=\n+go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 h1:PB3Zrjs1sG1GBX51SXyTSoOTqcDglmsk7nT6tkKPb/k=\n+go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0/go.mod h1:U2R3XyVPzn0WX7wOIypPuptulsMcPDPs/oiSVOMVnHY=\n+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 h1:T0Ec2E+3YZf5bgTNQVet8iTDW7oIk03tXHq+wkwIDnE=\n+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0/go.mod h1:30v2gqH+vYGJsesLWFov8u47EpYTcIQcBjKpI6pJThg=\n+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=\n+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=\n+go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=\n+go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=\n+go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=\n+go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=\n+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=\n+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=\n+go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=\n+go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=\n go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\n go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\n go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=\n go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=\n go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\n-go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=\n-go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=\n+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\n+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\n go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\n go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\n go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\n@@ -357,8 +381,8 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\n golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\n golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\n golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\n-golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=\n-golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=\n+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=\n+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=\n golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\n golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\n golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\n@@ -395,8 +419,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T\n golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\n golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=\n golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\n-google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=\n-google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=\n+google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=\n+google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=\n+google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=\n+google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=\n+google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=\n+google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=\n+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=\n+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=\n gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\n gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\n gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ndiff --git a/telemetry/middleware.go b/telemetry/middleware.go\nnew file mode 100644\nindex 0000000..29c3d36\n--- /dev/null\n+++ b/telemetry/middleware.go\n@@ -0,0 +1,88 @@\n+package telemetry\n+\n+import (\n+\t\"fmt\"\n+\t\"net/http\"\n+\t\"time\"\n+\n+\t\"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\"\n+\totelmetric \"go.opentelemetry.io/otel/metric\"\n+\t\"go.opentelemetry.io/otel/semconv/v1.13.0/httpconv\"\n+)\n+\n+func (t *Telemetry) RequestDuration() func(next http.Handler) http.Handler {\n+\tconst (\n+\t\tmetricNameRequestDurationMs = \"request_duration_millis\"\n+\t\tmetricUnitRequestDurationMs = \"ms\"\n+\t\tmetricDescRequestDurationMs = \"Measures the latency of HTTP requests processed by the server, in milliseconds.\"\n+\t)\n+\thistogram, err := t.meter.Int64Histogram(\n+\t\tmetricNameRequestDurationMs,\n+\t\totelmetric.WithDescription(metricDescRequestDurationMs),\n+\t\totelmetric.WithUnit(metricUnitRequestDurationMs),\n+\t)\n+\tif err != nil {\n+\t\tpanic(fmt.Sprintf(\"unable to create %s histogram: %v\", metricNameRequestDurationMs, err))\n+\t}\n+\n+\treturn func(next http.Handler) http.Handler {\n+\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n+\t\t\t// capture the start time of the request\n+\t\t\tstartTime := time.Now()\n+\n+\t\t\t// execute next http handler\n+\t\t\tnext.ServeHTTP(w, r)\n+\n+\t\t\t// record the request duration\n+\t\t\tduration := time.Since(startTime)\n+\t\t\thistogram.Record(\n+\t\t\t\tr.Context(),\n+\t\t\t\tint64(duration.Milliseconds()),\n+\t\t\t\totelmetric.WithAttributes(\n+\t\t\t\t\thttpconv.ServerRequest(t.serviceName, r)...,\n+\t\t\t\t),\n+\t\t\t)\n+\t\t})\n+\t}\n+}\n+\n+func (t *Telemetry) RequestInFlight() func(next http.Handler) http.Handler {\n+\tconst (\n+\t\tmetricNameRequestInFlight = \"request_in_flight\"\n+\t\tmetricDescRequestInFlight = \"Measures the number of concurrent HTTP requests being processed by the server.\"\n+\t\tmetricUnitRequestInFlight = \"1\"\n+\t)\n+\n+\t// counter to capture requests in flight\n+\tcounter, err := t.meter.Int64UpDownCounter(\n+\t\tmetricNameRequestInFlight,\n+\t\totelmetric.WithDescription(metricDescRequestInFlight),\n+\t\totelmetric.WithUnit(metricUnitRequestInFlight),\n+\t)\n+\tif err != nil {\n+\t\tpanic(fmt.Sprintf(\"unable to create %s counter: %v\", metricNameRequestInFlight, err))\n+\t}\n+\n+\treturn func(next http.Handler) http.Handler {\n+\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n+\t\t\tattrs := otelmetric.WithAttributes(httpconv.ServerRequest(t.serviceName, r)...)\n+\n+\t\t\t// increase the number of requests in flight\n+\t\t\tcounter.Add(r.Context(), 1, attrs)\n+\n+\t\t\t// execute next http handler\n+\t\t\tnext.ServeHTTP(w, r)\n+\n+\t\t\t// decrease the number of requests in flight\n+\t\t\tcounter.Add(r.Context(), -1, attrs)\n+\t\t})\n+\t}\n+}\n+\n+func (t *Telemetry) WithRouteTag() func(next http.Handler) http.Handler {\n+\treturn func(next http.Handler) http.Handler {\n+\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n+\t\t\totelhttp.WithRouteTag(r.URL.Path, next)\n+\t\t})\n+\t}\n+}\ndiff --git a/telemetry/provider.go b/telemetry/provider.go\nnew file mode 100644\nindex 0000000..b641eaf\n--- /dev/null\n+++ b/telemetry/provider.go\n@@ -0,0 +1,65 @@\n+package telemetry\n+\n+import (\n+\t\"context\"\n+\t\"fmt\"\n+\t\"time\"\n+\n+\t\"go.opentelemetry.io/otel\"\n+\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc\"\n+\t\"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n+\t\"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric\"\n+\t\"go.opentelemetry.io/otel/exporters/stdout/stdouttrace\"\n+\t\"go.opentelemetry.io/otel/sdk/metric\"\n+\t\"go.opentelemetry.io/otel/sdk/resource\"\n+\t\"go.opentelemetry.io/otel/sdk/trace\"\n+)\n+\n+func NewTracerProvider(ctx context.Context, res *resource.Resource, isDev bool) (*trace.TracerProvider, error) {\n+\tvar exporter trace.SpanExporter\n+\tvar err error\n+\n+\tif isDev {\n+\t\texporter, err = stdouttrace.New()\n+\t\tif err != nil {\n+\t\t\treturn nil, fmt.Errorf(\"failed to create stdout trace exporter: %w\", err)\n+\t\t}\n+\t} else {\n+\t\texporter, err = otlptracegrpc.New(ctx)\n+\t\tif err != nil {\n+\t\t\treturn nil, fmt.Errorf(\"failed to create OTLP trace exporter: %w\", err)\n+\t\t}\n+\t}\n+\n+\ttp := trace.NewTracerProvider(\n+\t\ttrace.WithBatcher(exporter),\n+\t\ttrace.WithResource(res),\n+\t)\n+\totel.SetTracerProvider(tp)\n+\n+\treturn tp, nil\n+}\n+\n+func NewMeterProvider(ctx context.Context, res *resource.Resource, isDev bool) (*metric.MeterProvider, error) {\n+\tvar exporter metric.Exporter\n+\tvar err error\n+\n+\tif isDev {\n+\t\texporter, err = stdoutmetric.New()\n+\t\tif err != nil {\n+\t\t\treturn nil, fmt.Errorf(\"failed to create stdout metric exporter: %w\", err)\n+\t\t}\n+\t} else {\n+\t\texporter, err = otlpmetricgrpc.New(ctx)\n+\t\tif err != nil {\n+\t\t\treturn nil, fmt.Errorf(\"failed to create OTLP metric exporter: %w\", err)\n+\t\t}\n+\t}\n+\n+\tmp := metric.NewMeterProvider(\n+\t\tmetric.WithReader(metric.NewPeriodicReader(exporter, metric.WithInterval(10*time.Second))),\n+\t\tmetric.WithResource(res),\n+\t)\n+\totel.SetMeterProvider(mp)\n+\treturn mp, nil\n+}\ndiff --git a/telemetry/telemetry.go b/telemetry/telemetry.go\nnew file mode 100644\nindex 0000000..b9b8594\n--- /dev/null\n+++ b/telemetry/telemetry.go\n@@ -0,0 +1,64 @@\n+package telemetry\n+\n+import (\n+\t\"context\"\n+\n+\totelmetric \"go.opentelemetry.io/otel/metric\"\n+\t\"go.opentelemetry.io/otel/sdk/metric\"\n+\t\"go.opentelemetry.io/otel/sdk/resource\"\n+\t\"go.opentelemetry.io/otel/sdk/trace\"\n+\tsemconv \"go.opentelemetry.io/otel/semconv/v1.17.0\"\n+\toteltrace \"go.opentelemetry.io/otel/trace\"\n+)\n+\n+type Telemetry struct {\n+\ttp *trace.TracerProvider\n+\tmp *metric.MeterProvider\n+\n+\tmeter otelmetric.Meter\n+\ttracer oteltrace.Tracer\n+\n+\tserviceName string\n+\tserviceVersion string\n+}\n+\n+func NewTelemetry(ctx context.Context, serviceName, serviceVersion string, isDev bool) (*Telemetry, error) {\n+\tres := resource.NewWithAttributes(\n+\t\tsemconv.SchemaURL,\n+\t\tsemconv.ServiceName(serviceName),\n+\t\tsemconv.ServiceVersion(serviceVersion),\n+\t)\n+\n+\ttp, err := NewTracerProvider(ctx, res, isDev)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\tmp, err := NewMeterProvider(ctx, res, isDev)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\treturn &Telemetry{\n+\t\ttp: tp,\n+\t\tmp: mp,\n+\n+\t\tmeter: mp.Meter(serviceName),\n+\t\ttracer: tp.Tracer(serviceVersion),\n+\n+\t\tserviceName: serviceName,\n+\t\tserviceVersion: serviceVersion,\n+\t}, nil\n+}\n+\n+func (t *Telemetry) Meter() otelmetric.Meter {\n+\treturn t.meter\n+}\n+\n+func (t *Telemetry) Tracer() oteltrace.Tracer {\n+\treturn t.tracer\n+}\n+\n+func (t *Telemetry) TraceStart(ctx context.Context, name string) (context.Context, oteltrace.Span) {\n+\treturn ctx, oteltrace.SpanFromContext(ctx)\n+}\n-- \n2.43.0\n\n\nFrom 8257ff64f40a32c5f1fa629501b9c489b87ddc42 Mon Sep 17 00:00:00 2001\nFrom: Anirudh Oppiliappan <x@icyphox.sh>\nDate: Tue, 6 May 2025 19:13:38 +0300\nSubject: [PATCH 2/2] appview: state: setup metrics middleware\n\n---\n appview/config.go | 1 +\n appview/state/router.go | 8 +++++++-\n appview/state/state.go | 38 +++++++++++++++++++++++++++++---------\n cmd/appview/main.go | 6 ++++--\n 4 files changed, 41 insertions(+), 12 deletions(-)\n\ndiff --git a/appview/config.go b/appview/config.go\nindex c7d321c..d57decb 100644\n--- a/appview/config.go\n+++ b/appview/config.go\n@@ -17,6 +17,7 @@ type Config struct {\n \tCamoSharedSecret string `env:\"TANGLED_CAMO_SHARED_SECRET\"`\n \tAvatarSharedSecret string `env:\"TANGLED_AVATAR_SHARED_SECRET\"`\n \tAvatarHost string `env:\"TANGLED_AVATAR_HOST, default=https://avatar.tangled.sh\"`\n+\tEnableTelemetry bool `env:\"TANGLED_TELEMETRY_ENABLED, default=false\"`\n }\n \n func LoadConfig(ctx context.Context) (*Config, error) {\ndiff --git a/appview/state/router.go b/appview/state/router.go\nindex dcd5fce..ce55c1e 100644\n--- a/appview/state/router.go\n+++ b/appview/state/router.go\n@@ -13,6 +13,13 @@ import (\n func (s *State) Router() http.Handler {\n \trouter := chi.NewRouter()\n \n+\tif s.telemetry != nil {\n+\t\t// top-level telemetry middleware\n+\t\trouter.Use(s.telemetry.RequestDuration())\n+\t\trouter.Use(s.telemetry.RequestInFlight())\n+\t\trouter.Use(s.telemetry.WithRouteTag())\n+\t}\n+\n \trouter.HandleFunc(\"/*\", func(w http.ResponseWriter, r *http.Request) {\n \t\tpat := chi.URLParam(r, \"*\")\n \t\tif strings.HasPrefix(pat, \"did:\") || strings.HasPrefix(pat, \"@\") {\n@@ -48,7 +55,6 @@ func (s *State) Router() http.Handler {\n \n func (s *State) UserRouter() http.Handler {\n \tr := chi.NewRouter()\n-\n \t// strip @ from user\n \tr.Use(StripLeadingAt)\n \ndiff --git a/appview/state/state.go b/appview/state/state.go\nindex 460a2bb..f2383ea 100644\n--- a/appview/state/state.go\n+++ b/appview/state/state.go\n@@ -9,6 +9,7 @@ import (\n \t\"log\"\n \t\"log/slog\"\n \t\"net/http\"\n+\t\"runtime/debug\"\n \t\"strings\"\n \t\"time\"\n \n@@ -24,20 +25,22 @@ import (\n \t\"tangled.sh/tangled.sh/core/appview/pages\"\n \t\"tangled.sh/tangled.sh/core/jetstream\"\n \t\"tangled.sh/tangled.sh/core/rbac\"\n+\t\"tangled.sh/tangled.sh/core/telemetry\"\n )\n \n type State struct {\n-\tdb *db.DB\n-\tauth *auth.Auth\n-\tenforcer *rbac.Enforcer\n-\ttidClock *syntax.TIDClock\n-\tpages *pages.Pages\n-\tresolver *appview.Resolver\n-\tjc *jetstream.JetstreamClient\n-\tconfig *appview.Config\n+\tdb *db.DB\n+\tauth *auth.Auth\n+\tenforcer *rbac.Enforcer\n+\ttidClock *syntax.TIDClock\n+\tpages *pages.Pages\n+\tresolver *appview.Resolver\n+\tjc *jetstream.JetstreamClient\n+\ttelemetry *telemetry.Telemetry\n+\tconfig *appview.Config\n }\n \n-func Make(config *appview.Config) (*State, error) {\n+func Make(ctx context.Context, config *appview.Config) (*State, error) {\n \td, err := db.Make(config.DbPath)\n \tif err != nil {\n \t\treturn nil, err\n@@ -59,6 +62,14 @@ func Make(config *appview.Config) (*State, error) {\n \n \tresolver := appview.NewResolver()\n \n+\tbi, ok := debug.ReadBuildInfo()\n+\tvar version string\n+\tif ok {\n+\t\tversion = bi.Main.Version\n+\t} else {\n+\t\tversion = \"v0.0.0-unknown\"\n+\t}\n+\n \twrapper := db.DbWrapper{d}\n \tjc, err := jetstream.NewJetstreamClient(\n \t\tconfig.JetstreamEndpoint,\n@@ -77,6 +88,14 @@ func Make(config *appview.Config) (*State, error) {\n \t\treturn nil, fmt.Errorf(\"failed to start jetstream watcher: %w\", err)\n \t}\n \n+\tvar tele *telemetry.Telemetry\n+\tif config.EnableTelemetry {\n+\t\ttele, err = telemetry.NewTelemetry(ctx, \"appview\", version, config.Dev)\n+\t\tif err != nil {\n+\t\t\treturn nil, fmt.Errorf(\"failed to setup telemetry: %w\", err)\n+\t\t}\n+\t}\n+\n \tstate := &State{\n \t\td,\n \t\tauth,\n@@ -85,6 +104,7 @@ func Make(config *appview.Config) (*State, error) {\n \t\tpgs,\n \t\tresolver,\n \t\tjc,\n+\t\ttele,\n \t\tconfig,\n \t}\n \ndiff --git a/cmd/appview/main.go b/cmd/appview/main.go\nindex 2cc33c2..ebe2aae 100644\n--- a/cmd/appview/main.go\n+++ b/cmd/appview/main.go\n@@ -14,13 +14,15 @@ import (\n func main() {\n \tslog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil)))\n \n-\tc, err := appview.LoadConfig(context.Background())\n+\tctx := context.Background()\n+\n+\tc, err := appview.LoadConfig(ctx)\n \tif err != nil {\n \t\tlog.Println(\"failed to load config\", \"error\", err)\n \t\treturn\n \t}\n \n-\tstate, err := state.Make(c)\n+\tstate, err := state.Make(ctx, c)\n \n \tif err != nil {\n \t\tlog.Fatal(err)\n-- \n2.43.0\n\n", "title": "telemetry: init telemetry package", "pullId": 80, "source": { "branch": "tracing" }, "createdAt": "", "targetRepo": "at://did:plc:wshs7t2adsemcrrd4snkeqli/sh.tangled.repo/3liuighjy2h22", "targetBranch": "master" } }