From e784184b1c67e0256f551fbdf501976081df4dc9 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Fri, 6 Oct 2023 02:05:19 +0000 Subject: [PATCH 01/11] chore: add prometheus middleware --- package.json | 2 + yarn.lock | 368 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 358 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index ba6e474..14817f3 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@commitlint/config-conventional": "^17.6.7", "@typechain/ethers-v5": "^11.1.1", "@types/express": "^4.17.18", + "@types/express-prometheus-middleware": "^1.2.1", "@types/jest": "^29.5.3", "@types/node": "^18.16.3", "@types/node-slack": "^0.0.31", @@ -45,6 +46,7 @@ "chalk": "^4.1.2", "ethers": "^5.7.2", "express": "^4.18.2", + "express-prometheus-middleware": "^1.2.0", "graphql-request": "^6.1.0", "level": "^8.0.0", "level-ts": "^2.1.0", diff --git a/yarn.lock b/yarn.lock index ba2c4c1..5fe7a92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1326,6 +1326,13 @@ dependencies: "@types/node" "*" +"@types/express-prometheus-middleware@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/express-prometheus-middleware/-/express-prometheus-middleware-1.2.1.tgz#efcf161ba660145de1d7d10c7ff827687b729797" + integrity sha512-5bAqiUHo+UlGsWmVr9oV9eiFd7SnkIgU53RSp/txd8KMDU73Yrdva7WezIJazbW+CldGJu0zF3DPqk8KSJVYDQ== + dependencies: + "@types/express" "*" + "@types/express-serve-static-core@^4.17.33": version "4.17.37" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz#7e4b7b59da9142138a2aaa7621f5abedce8c7320" @@ -1336,7 +1343,7 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@^4.17.18": +"@types/express@*", "@types/express@^4.17.18": version "4.17.18" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.18.tgz#efabf5c4495c1880df1bdffee604b143b29c4a95" integrity sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ== @@ -1593,6 +1600,11 @@ JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + abstract-level@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" @@ -1698,6 +1710,11 @@ ansi-escapes@^5.0.0: dependencies: type-fest "^1.0.2" +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -1740,6 +1757,19 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.7" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" + integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -2084,6 +2114,11 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chownr@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + ci-info@^3.2.0: version "3.8.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" @@ -2134,6 +2169,11 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== + collect-v8-coverage@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" @@ -2213,6 +2253,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -2351,6 +2396,13 @@ debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3. dependencies: ms "2.1.2" +debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -2369,7 +2421,7 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-extend@~0.6.0: +deep-extend@^0.6.0, deep-extend@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== @@ -2407,16 +2459,31 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== +depd@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -2858,6 +2925,16 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== +express-prometheus-middleware@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/express-prometheus-middleware/-/express-prometheus-middleware-1.2.0.tgz#ce913ffe4282411ab0d3d2db095771292801cb18" + integrity sha512-efSwft67rdtiW40D0im1f7Rz1TCGHGzPj6lfK0MxZDcPj6z4f/Ab5VNkWPYZEjvLqZiZ7fbS00CYzpigO8tS+g== + dependencies: + response-time "^2.3.2" + url-value-parser "^2.0.0" + optionalDependencies: + prometheus-gc-stats "^0.6.2" + express@^4.18.2: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" @@ -3114,6 +3191,13 @@ fs-extra@^7.0.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-minipass@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3129,6 +3213,28 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg== + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gc-stats@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/gc-stats/-/gc-stats-1.4.0.tgz#66cd194c5a8eae1138407300bc6cb42c2f6f3cd6" + integrity sha512-4FcCj9e8j8rCjvLkqRpGZBLgTC/xr9XEf5By3x77cDucWWB3pJK6FEwXZCTCbb4z8xdaOoi4owBNrvn3ciDdxA== + dependencies: + nan "^2.13.2" + node-pre-gyp "^0.13.0" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3316,6 +3422,11 @@ has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -3392,7 +3503,7 @@ husky@^8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -3404,6 +3515,13 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore-walk@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== + dependencies: + minimatch "^3.0.4" + ignore@^5.2.0: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" @@ -3458,7 +3576,7 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.4: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -3490,6 +3608,13 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== + dependencies: + number-is-nan "^1.0.0" + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -4593,11 +4718,33 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +minipass@^2.6.0, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mkdirp@^0.5.1, mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -4618,11 +4765,16 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3: +ms@2.1.3, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nan@^2.13.2: + version "2.18.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" + integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== + napi-macros@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.2.2.tgz#817fef20c3e0e40a963fbf7b37d1600bd0201044" @@ -4643,6 +4795,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +needle@^2.2.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" + integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -4680,6 +4841,22 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== +node-pre-gyp@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz#df9ab7b68dd6498137717838e4f92a33fc9daa42" + integrity sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + node-releases@^2.0.13: version "2.0.13" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" @@ -4693,6 +4870,14 @@ node-slack@^0.0.7: deferred "0.7.1" request "~2.x" +nopt@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + dependencies: + abbrev "1" + osenv "^0.1.4" + normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -4718,6 +4903,27 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-bundled@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" + integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -4732,11 +4938,31 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" @@ -4749,6 +4975,11 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" +on-headers@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -4775,6 +5006,11 @@ opencollective-postinstall@^2.0.0: resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== +optional@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/optional/-/optional-0.1.4.tgz#cdb1a9bedc737d2025f690ceeb50e049444fd5b3" + integrity sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw== + optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -4787,6 +5023,24 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -4957,6 +5211,15 @@ prom-client@^14.2.0: dependencies: tdigest "^0.1.1" +prometheus-gc-stats@^0.6.2: + version "0.6.5" + resolved "https://registry.yarnpkg.com/prometheus-gc-stats/-/prometheus-gc-stats-0.6.5.tgz#c24309f586f4483b67438f0464cd7e3074e023a0" + integrity sha512-VsmpEGHt9mMZqlhL+96gz2LsaXEgu2SXQ/tiEqIBLPoUTyPORDNsEiH9DPPZHChdkTTBw3GRV1wGvqdIg4EktQ== + dependencies: + optional "^0.1.3" + optionalDependencies: + gc-stats "^1.4.0" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -5033,6 +5296,16 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -5066,7 +5339,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -"readable-stream@> 1.0.0 < 3.0.0", readable-stream@^2.2.10, readable-stream@^2.2.8: +"readable-stream@> 1.0.0 < 3.0.0", readable-stream@^2.0.6, readable-stream@^2.2.10, readable-stream@^2.2.8: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -5166,6 +5439,14 @@ resolve@^1.10.0, resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +response-time@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/response-time/-/response-time-2.3.2.tgz#ffa71bab952d62f7c1d49b7434355fbc68dffc5a" + integrity sha512-MUIDaDQf+CVqflfTdQ5yam+aYCkXj1PY8fjlPDQ6ppxJlmgZb864pHtA750mayywNg8tx4rS7qH9JXd/OF+3gw== + dependencies: + depd "~1.1.0" + on-headers "~1.0.1" + restore-cursor@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" @@ -5184,6 +5465,13 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -5205,7 +5493,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -5220,12 +5508,17 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@^1.2.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + scrypt-js@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== -"semver@2 || 3 || 4 || 5": +"semver@2 || 3 || 4 || 5", semver@^5.3.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -5271,6 +5564,11 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -5297,7 +5595,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -5427,7 +5725,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw== + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -5459,6 +5766,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== + dependencies: + ansi-regex "^2.0.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -5500,6 +5814,11 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -5544,6 +5863,19 @@ table-layout@^1.0.2: typical "^5.2.0" wordwrapjs "^4.0.0" +tar@^4: + version "4.4.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== + dependencies: + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" + tdigest@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced" @@ -5816,6 +6148,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-value-parser@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/url-value-parser/-/url-value-parser-2.2.0.tgz#f38ae8cd24604ec69bc219d66929ddbbd93a2b32" + integrity sha512-yIQdxJpgkPamPPAPuGdS7Q548rLhny42tg8d4vyTNzFqvOnwqrgHXvgehT09U7fwrzxi3RxCiXjoNUNnNOlQ8A== + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5894,6 +6231,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + wordwrapjs@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" @@ -5948,7 +6292,7 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^3.0.2: +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== From 2db2803d13e467e17e23ac454047fce53fb93493 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Fri, 6 Oct 2023 02:14:17 +0000 Subject: [PATCH 02/11] chore: configure prometheus middleware --- src/utils/api.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/utils/api.ts b/src/utils/api.ts index 13e2534..02e2f4e 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,11 +1,10 @@ import { Server } from "http"; import express, { Request, Response, Router } from "express"; -import client from "prom-client"; import { Express } from "express-serve-static-core"; +import promMiddleware from "express-prometheus-middleware"; import { getLogger } from "./logging"; import { DBService } from "./db"; import { Registry } from "../types"; -import { MetricsService } from "./metrics"; import { version, name, description } from "../../package.json"; export class ApiService { @@ -22,21 +21,19 @@ export class ApiService { } private bootstrap() { - const collectDefaultMetrics = client.collectDefaultMetrics; - collectDefaultMetrics(); - this.app.use(express.json()); + this.app.use( + promMiddleware({ + metricsPath: "/metrics", + metricsApp: this.app, + collectDefaultMetrics: true, + }) + ); this.app.use(express.urlencoded({ extended: true })); this.app.get("/", (_req: Request, res: Response) => { res.send("🐮 Moooo!"); - MetricsService.apiRequestCounter.labels("GET", "/", "200").inc(); }); this.app.use("/api", router); - this.app.get("/metrics", async (_req, res) => { - res.set("Content-Type", client.register.contentType); - const response = await client.register.metrics(); - res.send(response); - }); } public static getInstance(port?: number): ApiService { From 416df8de1f6f508e94510c81e33a90a77e27739f Mon Sep 17 00:00:00 2001 From: mfw78 Date: Fri, 6 Oct 2023 04:52:45 +0000 Subject: [PATCH 03/11] chore: metrics conventions --- src/domain/addContract.ts | 54 ++++--- src/domain/chainContext.ts | 39 ++--- src/domain/checkForAndPlaceOrder.ts | 63 +++++--- src/types/model.ts | 12 +- src/utils/metrics.ts | 227 +++++++++++++++++----------- 5 files changed, 233 insertions(+), 162 deletions(-) diff --git a/src/domain/addContract.ts b/src/domain/addContract.ts index 04c86e0..257cdc4 100644 --- a/src/domain/addContract.ts +++ b/src/domain/addContract.ts @@ -1,5 +1,9 @@ -import { toConditionalOrderParams, getLogger } from "../utils"; -import { MetricsService } from "../utils/metrics"; +import { + toConditionalOrderParams, + getLogger, + handleExecutionError, + isComposableCowCompatible, +} from "../utils"; import { BytesLike, ethers } from "ethers"; import { @@ -14,17 +18,17 @@ import { Registry, } from "../types"; -import { isComposableCowCompatible, handleExecutionError } from "../utils"; import { ChainContext } from "./chainContext"; import { ConditionalOrderParams } from "@cowprotocol/cow-sdk"; - -const { - newOwnerCount, - addNewOwnerErrorCount, - addContractSingleOrderCount, - addContractMerkleRootSetCount, - ownersPopulation, -} = MetricsService; +import { + addContractsErrorsTotal, + addContractsRunDurationSeconds, + merkleRootTotal, + newContractsTotal, + singleOrdersTotal, + totalActiveOrders, + totalActiveOwners, +} from "../utils/metrics"; /** * Listens to these events on the `ComposableCoW` contract: @@ -37,7 +41,20 @@ export async function addContract( context: ChainContext, event: ConditionalOrderCreatedEvent ) { - return _addContract(context, event).catch(handleExecutionError); + const { chainId } = context; + const timer = addContractsRunDurationSeconds + .labels(chainId.toString()) + .startTimer(); + try { + await _addContract(context, event); + } catch (err) { + addContractsErrorsTotal + .labels(context.chainId.toString(), "addContract") + .inc(); + handleExecutionError(err); + } finally { + timer(); + } } async function _addContract( @@ -65,15 +82,12 @@ async function _addContract( registry ); if (added) { - newOwnerCount.labels(context.chainId.toString()).inc(); + newContractsTotal.labels(context.chainId.toString()).inc(); numContractsAdded++; } else { log.error( `Failed to register Smart Order from tx ${tx} on block ${blockNumber}. Error: ${error}` ); - addNewOwnerErrorCount - .labels(context.chainId.toString(), "_addContract") - .inc(); } hasErrors ||= error; @@ -123,7 +137,7 @@ export async function _registerNewOrder( registry ); added = true; - addContractSingleOrderCount.labels(registry.network).inc(); + singleOrdersTotal.labels(registry.network).inc(); } else if ( event.topics[0] == composableCow.getEventTopic("MerkleRootSet") ) { @@ -164,7 +178,7 @@ export async function _registerNewOrder( registry ); added = true; - addContractMerkleRootSetCount.labels(registry.network).inc(); + merkleRootTotal.labels(registry.network).inc(); } } } @@ -223,6 +237,7 @@ export function add( orders: new Map(), composableCow, }); + totalActiveOrders.labels(registry.network).inc(); } } else { log.info(`Adding conditional order to new owner contract ${owner}:`, { @@ -235,7 +250,8 @@ export function add( owner, new Set([{ tx, params, proof, orders: new Map(), composableCow }]) ); - ownersPopulation.labels(registry.network).inc(); + totalActiveOwners.labels(registry.network).inc(); + totalActiveOrders.labels(registry.network).inc(); } } diff --git a/src/domain/chainContext.ts b/src/domain/chainContext.ts index de084dc..e82227a 100644 --- a/src/domain/chainContext.ts +++ b/src/domain/chainContext.ts @@ -12,21 +12,18 @@ import { addContract } from "./addContract"; import { checkForAndPlaceOrder } from "./checkForAndPlaceOrder"; import { ethers } from "ethers"; import { composableCowContract, DBService, getLogger } from "../utils"; -import { MetricsService } from "../utils/metrics"; +import { + blockHeight, + blockTime, + eventsProcessedTotal, + processBlockDurationSeconds, + reorgsTotal, +} from "../utils/metrics"; const WATCHDOG_FREQUENCY = 5 * 1000; // 5 seconds const MULTICALL3 = "0xcA11bde05977b3631167028862bE2a173976CA11"; -// Metrics -const { - blockWatcherBlockHeight, - blockWatcherReorgCount, - blockWatcherBlockTime, - blockWatcherNumEventsProcessed, - processBlockDuration, -} = MetricsService; - /** * The chain context handles watching a single chain for new conditional orders * and executing them. @@ -102,9 +99,7 @@ export class ChainContext { const { pageSize } = this; // Set the block height metric - blockWatcherBlockHeight - .labels(chainId.toString()) - .set(lastProcessedBlock ?? 0); + blockHeight.labels(chainId.toString()).set(lastProcessedBlock ?? 0); // Start watching from (not including) the last processed block (if any) let fromBlock = lastProcessedBlock @@ -181,9 +176,7 @@ export class ChainContext { await this.registry.write(); // Set the block height metric - blockWatcherBlockHeight - .labels(chainId.toString()) - .set(Number(blockNumber)); + blockHeight.labels(chainId.toString()).set(Number(blockNumber)); } catch (err) { log.error(`Error processing block ${blockNumber}`, err); } @@ -246,15 +239,15 @@ export class ChainContext { log.debug(`New block ${blockNumber}`); // Set the block time metric const now = new Date().getTime(); - const blockTime = now - timeLastBlockProcessed; + const _blockTime = (now - timeLastBlockProcessed) / 1000; timeLastBlockProcessed = now; // Set the block time metric - blockWatcherBlockTime.labels(chainId.toString()).set(blockTime); + blockTime.labels(chainId.toString()).set(_blockTime); if (blockNumber <= lastBlockReceived) { // This may be a re-org, so process the block again - blockWatcherReorgCount.labels(chainId.toString()).inc(); + reorgsTotal.labels(chainId.toString()).inc(); log.info(`Re-org detected, re-processing block ${blockNumber}`); } lastBlockReceived = blockNumber; @@ -269,9 +262,7 @@ export class ChainContext { await processBlock(this, Number(blockNumber), events); // Block height metric - blockWatcherBlockHeight - .labels(chainId.toString()) - .set(Number(blockNumber)); + blockHeight.labels(chainId.toString()).set(Number(blockNumber)); } catch { log.error(`Error processing block ${blockNumber}`); } @@ -322,7 +313,7 @@ async function processBlock( blockTimestampOverride?: number ) { const { provider, chainId } = context; - const timer = processBlockDuration + const timer = processBlockDurationSeconds .labels(context.chainId.toString()) .startTimer(); const log = getLogger(`chainContext:processBlock:${chainId}:${blockNumber}`); @@ -343,7 +334,7 @@ async function processBlock( return false; }); log.info(`Result of "addContract": ${_formatResult(result)}`); - blockWatcherNumEventsProcessed.labels(chainId.toString()).inc(); + eventsProcessedTotal.labels(chainId.toString()).inc(); } } diff --git a/src/domain/checkForAndPlaceOrder.ts b/src/domain/checkForAndPlaceOrder.ts index 7c6a936..471da63 100644 --- a/src/domain/checkForAndPlaceOrder.ts +++ b/src/domain/checkForAndPlaceOrder.ts @@ -16,6 +16,7 @@ import { handleOnChainCustomError, } from "../utils"; import { + ConditionalOrder as ConditionalOrderSDK, OrderBookApi, OrderCreation, OrderPostError, @@ -28,16 +29,16 @@ import { formatEpoch, } from "@cowprotocol/cow-sdk"; import { ChainContext } from "./chainContext"; -import { MetricsService } from "../utils/metrics"; - -const { - orderBookOrdersPlaced, - orderBookApiErrors, - pollingOnChainChecks, - pollingOnChainTimer, - pollingUnexpectedErrors, - pollingChecks, -} = MetricsService; +import { + pollingOnChainDurationSeconds, + totalActiveOrders, + totalActiveOwners, + totalOrderBookDiscreteOrders, + totalOrderBookErrors, + totalPollingOnChainChecks, + totalPollingRuns, + totalPollingUnexpectedErrors, +} from "../utils/metrics"; const GPV2SETTLEMENT = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"; @@ -186,6 +187,7 @@ export async function checkForAndPlaceOrder( const action = deleted ? "Stop Watching" : "Failed to stop watching"; log.debug(`${action} conditional order from TX ${conditionalOrder.tx}`); + totalActiveOrders.labels(chainId.toString()).dec(); } } @@ -194,6 +196,7 @@ export async function checkForAndPlaceOrder( for (const [owner, conditionalOrders] of Array.from(ownerOrders.entries())) { if (conditionalOrders.size === 0) { ownerOrders.delete(owner); + totalActiveOwners.labels(chainId.toString()).dec(); } } @@ -219,11 +222,14 @@ async function _processConditionalOrder( orderRef: string ): Promise { const { provider, orderBook, dryRun, chainId } = context; + const { handler } = conditionalOrder.params; const log = getLogger( `checkForAndPlaceOrder:_processConditionalOrder:${orderRef}` ); + const id = ConditionalOrderSDK.leafToId(conditionalOrder.params); + const metricLabels = [chainId.toString(), handler, owner, id]; try { - pollingChecks.labels(chainId.toString()).inc(); + totalPollingRuns.labels(...metricLabels).inc(); const proof = conditionalOrder.proof ? conditionalOrder.proof.path.map((c) => c.toString()) @@ -251,7 +257,9 @@ async function _processConditionalOrder( // Unsupported Order Type (unknown handler) // For now, fallback to legacy behavior // TODO: Decide in the future what to do. Probably, move the error handling to the SDK and kill the poll Legacy - const timer = pollingOnChainTimer.labels(chainId.toString()).startTimer(); + const timer = pollingOnChainDurationSeconds + .labels(...metricLabels) + .startTimer(); pollResult = await _pollLegacy( context, owner, @@ -261,7 +269,7 @@ async function _processConditionalOrder( orderRef ); timer(); - pollingOnChainChecks.labels(chainId.toString()).inc(); + totalPollingOnChainChecks.labels(...metricLabels).inc(); } // Error polling @@ -292,6 +300,7 @@ async function _processConditionalOrder( blockTimestamp, orderRef, dryRun, + metricLabels, }); // In case of error, return early @@ -317,7 +326,7 @@ async function _processConditionalOrder( signature, }; } catch (e: any) { - pollingUnexpectedErrors.labels(chainId.toString()).inc(); + totalPollingUnexpectedErrors.labels(...metricLabels).inc(); return { result: PollResultCode.UNEXPECTED_ERROR, error: e, @@ -379,9 +388,17 @@ async function _placeOrder(params: { orderRef: string; blockTimestamp: number; dryRun: boolean; + metricLabels: string[]; }): Promise | PollResultErrors> { - const { orderUid, order, orderBook, orderRef, blockTimestamp, dryRun } = - params; + const { + orderUid, + order, + orderBook, + orderRef, + blockTimestamp, + dryRun, + metricLabels, + } = params; const log = getLogger(`checkForAndPlaceOrder:_placeOrder:${orderRef}`); const { chainId } = orderBook.context; try { @@ -398,7 +415,7 @@ async function _placeOrder(params: { log.debug(`Order`, postOrder); if (!dryRun) { const orderUid = await orderBook.sendOrder(postOrder); - orderBookOrdersPlaced.labels(chainId.toString()).inc(); + totalOrderBookDiscreteOrders.labels(...metricLabels).inc(); log.info(`API response`, { orderUid }); } } catch (error: any) { @@ -411,8 +428,8 @@ async function _placeOrder(params: { status, body, error, - chainId, - blockTimestamp + blockTimestamp, + metricLabels ); const isSuccess = handleErrorResult.result === PollResultCode.SUCCESS; @@ -455,12 +472,12 @@ function _handleOrderBookError( status: any, body: any, error: any, - chainId: SupportedChainId, - blockTimestamp: number + blockTimestamp: number, + metricLabels: string[] ): Omit | PollResultErrors { const apiError = body?.errorType as OrderPostError.errorType; - orderBookApiErrors - .labels(chainId.toString(), status.toString(), apiError) + totalOrderBookErrors + .labels(...metricLabels, status.toString(), apiError) .inc(); if (status === 400) { // The order is in the OrderBook, all good :) diff --git a/src/types/model.ts b/src/types/model.ts index eab237e..01faff1 100644 --- a/src/types/model.ts +++ b/src/types/model.ts @@ -5,7 +5,7 @@ import { BytesLike } from "ethers"; import type { ConditionalOrderCreatedEvent } from "./generated/ComposableCoW"; import { ConditionalOrderParams, PollResult } from "@cowprotocol/cow-sdk"; import { DBService } from "../utils"; -import { MetricsService } from "../utils/metrics"; +import { totalActiveOrders, totalActiveOwners } from "../utils/metrics"; // Standardise the storage key const LAST_NOTIFIED_ERROR_STORAGE_KEY = "LAST_NOTIFIED_ERROR"; @@ -15,8 +15,6 @@ const CONDITIONAL_ORDER_REGISTRY_VERSION_KEY = "CONDITIONAL_ORDER_REGISTRY_VERSION"; const CONDITIONAL_ORDER_REGISTRY_VERSION = 1; -const { ownersPopulation } = MetricsService; - export const getNetworkStorageKey = (key: string, network: string): string => { return `${key}_${network}`; }; @@ -162,7 +160,13 @@ export class Registry { .catch(() => null); // Return registry (on its latest version) - ownersPopulation.labels(network).set(ownerOrders.size); + totalActiveOwners.labels(network).set(ownerOrders.size); + const numOrders = Array.from(ownerOrders.values()).reduce( + (acc, o) => acc + o.size, + 0 + ); + totalActiveOrders.labels(network).set(numOrders); + return new Registry( ownerOrders, storage, diff --git a/src/utils/metrics.ts b/src/utils/metrics.ts index 273fc50..c753b6e 100644 --- a/src/utils/metrics.ts +++ b/src/utils/metrics.ts @@ -1,94 +1,137 @@ import client from "prom-client"; -export class MetricsService { - static apiRequestCounter = new client.Counter({ - name: "api_request_count", - help: "Total number of API requests", - labelNames: ["method", "path", "status"], - }); - static blockWatcherBlockHeight = new client.Gauge({ - name: "block_watcher_block_height", - help: "Block height of the block watcher", - labelNames: ["chain_id"], - }); - static blockWatcherReorgCount = new client.Counter({ - name: "block_watcher_reorg_count", - help: "Total number of reorgs", - labelNames: ["chain_id"], - }); - static blockWatcherBlockTime = new client.Gauge({ - name: "block_watcher_block_time", - help: "Time since last block", - labelNames: ["chain_id"], - }); - static blockWatcherNumEventsProcessed = new client.Counter({ - name: "block_watcher_num_events_processed", - help: "Total number of events processed", - labelNames: ["chain_id"], - }); - static newOwnerCount = new client.Counter({ - name: "add_contract_new_contract_count", - help: "Total number of new contracts added", - labelNames: ["chain_id"], - }); - static addContractSingleOrderCount = new client.Counter({ - name: "add_contract_single_order_count", - help: "Total number of single orders added", - labelNames: ["chain_id"], - }); - static addContractMerkleRootSetCount = new client.Counter({ - name: "add_contract_merkle_root_set_count", - help: "Total number of merkle root sets added", - labelNames: ["chain_id"], - }); - static addNewOwnerErrorCount = new client.Counter({ - name: "add_contract_error_count", - help: "Total number of errors adding contracts", - labelNames: ["chain_id", "function"], - }); - static addContractRunDuration = new client.Histogram({ - name: "add_contract_run_duration", - help: "Duration of add contract run", - labelNames: ["chain_id"], - }); - static processBlockDuration = new client.Histogram({ - name: "chain_context_process_block_duration", - help: "Duration of chain context process block", - labelNames: ["chain_id"], - }); - static ownersPopulation = new client.Gauge({ - name: "watch_tower_num_owners", - help: "Number of owners", - labelNames: ["chain_id"], - }); - static orderBookOrdersPlaced = new client.Counter({ - name: "orderbook_orders_placed", - help: "Total number of discrete orders placed into the orderbook", - labelNames: ["chain_id"], - }); - static orderBookApiErrors = new client.Counter({ - name: "orderbook_api_errors", - help: "Total number of errors from the orderbook API", - labelNames: ["chain_id", "status", "error"], - }); - static pollingOnChainChecks = new client.Counter({ - name: "polling_onchain_checks", - help: "Total number of on-chain hint checks", - labelNames: ["chain_id"], - }); - static pollingOnChainTimer = new client.Histogram({ - name: "polling_onchain_timer", - help: "Duration of on-chain hint checks", - labelNames: ["chain_id"], - }); - static pollingChecks = new client.Counter({ - name: "polling_checks", - help: "Total number of polling checks", - labelNames: ["chain_id"], - }); - static pollingUnexpectedErrors = new client.Counter({ - name: "polling_unexpected_errors", - help: "Total number of unexpected polling errors", - labelNames: ["chain_id"], - }); -} +const blockHeight = new client.Gauge({ + name: "watch_tower_block_height", + help: "Block height of the block watcher", + labelNames: ["chain_id"], +}); + +const reorgDepth = new client.Gauge({ + name: "watch_tower_reorg_depth", + help: "Depth of the current reorg", + labelNames: ["chain_id"], +}); + +const blockTime = new client.Gauge({ + name: "watch_tower_block_time_seconds", + help: "Time since last block", + labelNames: ["chain_id"], +}); + +const eventsProcessedTotal = new client.Counter({ + name: "watch_tower_events_processed_total", + help: "Total number of events processed", + labelNames: ["chain_id"], +}); + +const reorgsTotal = new client.Counter({ + name: "watch_tower_reorg_total", + help: "Total number of reorgs", + labelNames: ["chain_id"], +}); + +const newContractsTotal = new client.Counter({ + name: "watch_tower_new_contracts_total", + help: "Total number of new contracts", + labelNames: ["chain_id"], +}); + +const singleOrdersTotal = new client.Counter({ + name: "watch_tower_single_orders_total", + help: "Total number of single orders processed", + labelNames: ["chain_id"], +}); + +const merkleRootTotal = new client.Counter({ + name: "watch_tower_merkle_roots_total", + help: "Total number of merkle roots processed", + labelNames: ["chain_id"], +}); + +const addContractsErrorsTotal = new client.Counter({ + name: "watch_tower_add_contracts_errors_total", + help: "Total number of add contracts errors", + labelNames: ["chain_id", "function"], +}); + +const addContractsRunDurationSeconds = new client.Histogram({ + name: "watch_tower_add_contracts_run_duration_seconds", + help: "Duration of add contracts run", + labelNames: ["chain_id"], +}); + +const processBlockDurationSeconds = new client.Histogram({ + name: "watch_tower_process_block_duration_seconds", + help: "Duration of process block", + labelNames: ["chain_id"], +}); + +const totalActiveOwners = new client.Gauge({ + name: "watch_tower_active_owners_total", + help: "Total number of owners with active orders", + labelNames: ["chain_id"], +}); + +const totalActiveOrders = new client.Gauge({ + name: "watch_tower_active_orders_total", + help: "Total number of active orders", + labelNames: ["chain_id"], +}); + +const totalOrderBookDiscreteOrders = new client.Counter({ + name: "watch_tower_orderbook_discrete_orders_total", + help: "Total number of discrete orders posted to the orderbook", + labelNames: ["chain_id", "handler", "owner", "id"], +}); + +const totalOrderBookErrors = new client.Counter({ + name: "watch_tower_orderbook_errors_total", + help: "Total number of errors when interacting with the orderbook", + labelNames: ["chain_id", "handler", "owner", "id", "status", "error"], +}); + +const totalPollingRuns = new client.Counter({ + name: "watch_tower_polling_runs_total", + help: "Total number of polling runs", + labelNames: ["chain_id", "handler", "owner", "id"], +}); + +const totalPollingOnChainChecks = new client.Counter({ + name: "watch_tower_polling_onchain_checks_total", + help: "Total number of on-chain hint checks", + labelNames: ["chain_id", "handler", "owner", "id"], +}); + +const pollingOnChainDurationSeconds = new client.Histogram({ + name: "watch_tower_polling_onchain_duration_seconds", + help: "Duration of on-chain hint checks", + labelNames: ["chain_id", "handler", "owner", "id"], +}); + +const totalPollingUnexpectedErrors = new client.Counter({ + name: "watch_tower_polling_unexpected_errors_total", + help: "Total number of unexpected polling errors", + labelNames: ["chain_id", "handler", "owner", "id"], +}); + +export { + blockHeight, + reorgDepth, + blockTime, + eventsProcessedTotal, + reorgsTotal, + newContractsTotal, + singleOrdersTotal, + merkleRootTotal, + addContractsErrorsTotal, + addContractsRunDurationSeconds, + processBlockDurationSeconds, + totalActiveOwners, + totalActiveOrders, + totalOrderBookDiscreteOrders, + totalOrderBookErrors, + totalPollingRuns, + totalPollingOnChainChecks, + pollingOnChainDurationSeconds, + totalPollingUnexpectedErrors, +}; From 0ba9a672cc8f034bfea0ef6413f5432f56fb4ea7 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Sat, 7 Oct 2023 03:41:43 +0000 Subject: [PATCH 04/11] chore: metrics todo --- src/domain/checkForAndPlaceOrder.ts | 7 ++++++- src/utils/contracts.ts | 13 ++++++++++++- src/utils/metrics.ts | 14 ++++++++++++++ src/utils/utils.spec.ts | 1 + 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/domain/checkForAndPlaceOrder.ts b/src/domain/checkForAndPlaceOrder.ts index 471da63..2cb2556 100644 --- a/src/domain/checkForAndPlaceOrder.ts +++ b/src/domain/checkForAndPlaceOrder.ts @@ -38,6 +38,7 @@ import { totalPollingOnChainChecks, totalPollingRuns, totalPollingUnexpectedErrors, + totalPollingOnChainEthersErrors, } from "../utils/metrics"; const GPV2SETTLEMENT = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"; @@ -529,6 +530,7 @@ async function _pollLegacy( const { contract, multicall, chainId } = context; const logPrefix = `checkForAndPlaceOrder:_pollLegacy:${orderRef}`; const log = getLogger(logPrefix); + const { handler } = conditionalOrder.params; // as we going to use multicall, with `aggregate3Value`, there is no need to do any simulation as the // calls are guaranteed to pass, and will return the results, or the reversion within the ABI-encoded data. // By not using `populateTransaction`, we avoid an `eth_estimateGas` RPC call. @@ -537,6 +539,8 @@ async function _pollLegacy( "getTradeableOrderWithSignature", [owner, conditionalOrder.params, offchainInput, proof] ); + const id = ConditionalOrderSDK.leafToId(conditionalOrder.params); + const metricLabels = [chainId.toString(), owner, handler, id]; try { const lowLevelCall = await multicall.callStatic.aggregate3Value([ @@ -573,12 +577,13 @@ async function _pollLegacy( target, callData, revertData: returnData, + metricLabels, }); } catch (error: any) { // We can only get here from some provider / ethers failure. As the contract hasn't had it's say // we will defer to try again. - // TODO: Add metrics to track this log.error(`${logPrefix} ethers/call Unexpected error`, error); + totalPollingOnChainEthersErrors.labels(...metricLabels).inc(); return { result: PollResultCode.TRY_NEXT_BLOCK, reason: diff --git a/src/utils/contracts.ts b/src/utils/contracts.ts index d63420f..c069ab4 100644 --- a/src/utils/contracts.ts +++ b/src/utils/contracts.ts @@ -8,6 +8,7 @@ import { SupportedChainId, } from "@cowprotocol/cow-sdk"; import { getLogger } from "./logging"; +import { totalPollingOnChainInvalidInterfaces } from "./metrics"; // Selectors that are required to be part of the contract's bytecode in order to be considered compatible const REQUIRED_SELECTORS = [ @@ -188,8 +189,17 @@ export function handleOnChainCustomError(params: { target: string; callData: string; revertData: string; + metricLabels: string[]; }): PollResultErrors { - const { owner, orderRef, chainId, target, callData, revertData } = params; + const { + owner, + orderRef, + chainId, + target, + callData, + revertData, + metricLabels, + } = params; const logPrefix = `contracts:handleOnChainCustomError:${orderRef}`; try { @@ -279,6 +289,7 @@ export function handleOnChainCustomError(params: { log.debug( `Contract returned a non-compliant interface revert via getTradeableOrderWithSignature. Simulate: https://dashboard.tenderly.co/gp-v2/watch-tower-prod/simulator/new?network=${chainId}&contractAddress=${target}&rawFunctionInput=${callData}` ); + totalPollingOnChainInvalidInterfaces.labels(...metricLabels).inc(); return { result: PollResultCode.DONT_TRY_AGAIN, reason: "Order returned a non-compliant (invalid/erroneous) revert hint", diff --git a/src/utils/metrics.ts b/src/utils/metrics.ts index c753b6e..c3b7ded 100644 --- a/src/utils/metrics.ts +++ b/src/utils/metrics.ts @@ -108,6 +108,18 @@ const pollingOnChainDurationSeconds = new client.Histogram({ labelNames: ["chain_id", "handler", "owner", "id"], }); +const totalPollingOnChainInvalidInterfaces = new client.Counter({ + name: "watch_tower_polling_onchain_invalid_interface_total", + help: "Total number of invalid on-chain hint interface", + labelNames: ["chain_id", "handler", "owner", "id"], +}); + +const totalPollingOnChainEthersErrors = new client.Counter({ + name: "watch_tower_polling_onchain_ethers_errors_total", + help: "Total number of ethers on-chain hint errors", + labelNames: ["chain_id", "handler", "owner", "id"], +}); + const totalPollingUnexpectedErrors = new client.Counter({ name: "watch_tower_polling_unexpected_errors_total", help: "Total number of unexpected polling errors", @@ -132,6 +144,8 @@ export { totalOrderBookErrors, totalPollingRuns, totalPollingOnChainChecks, + totalPollingOnChainInvalidInterfaces, + totalPollingOnChainEthersErrors, pollingOnChainDurationSeconds, totalPollingUnexpectedErrors, }; diff --git a/src/utils/utils.spec.ts b/src/utils/utils.spec.ts index 2e90c03..a0048c6 100644 --- a/src/utils/utils.spec.ts +++ b/src/utils/utils.spec.ts @@ -100,6 +100,7 @@ describe("handle on-chain custom errors", () => { revertData: abiToSelector( CUSTOM_ERROR_ABI_MAP[CustomErrorSelectors.SINGLE_ORDER_NOT_AUTHED] ), + metricLabels: [], }; it("should pass a known selector correctly", () => { From 7117753f2f4d25eb03f690c34431da9b2685b11a Mon Sep 17 00:00:00 2001 From: mfw78 Date: Sat, 7 Oct 2023 03:42:15 +0000 Subject: [PATCH 05/11] fix: metric exports --- src/utils/metrics.ts | 66 ++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 45 deletions(-) diff --git a/src/utils/metrics.ts b/src/utils/metrics.ts index c3b7ded..d34c2df 100644 --- a/src/utils/metrics.ts +++ b/src/utils/metrics.ts @@ -1,151 +1,127 @@ import client from "prom-client"; -const blockHeight = new client.Gauge({ +export const blockHeight = new client.Gauge({ name: "watch_tower_block_height", help: "Block height of the block watcher", labelNames: ["chain_id"], }); -const reorgDepth = new client.Gauge({ +export const reorgDepth = new client.Gauge({ name: "watch_tower_reorg_depth", help: "Depth of the current reorg", labelNames: ["chain_id"], }); -const blockTime = new client.Gauge({ +export const blockTime = new client.Gauge({ name: "watch_tower_block_time_seconds", help: "Time since last block", labelNames: ["chain_id"], }); -const eventsProcessedTotal = new client.Counter({ +export const eventsProcessedTotal = new client.Counter({ name: "watch_tower_events_processed_total", help: "Total number of events processed", labelNames: ["chain_id"], }); -const reorgsTotal = new client.Counter({ +export const reorgsTotal = new client.Counter({ name: "watch_tower_reorg_total", help: "Total number of reorgs", labelNames: ["chain_id"], }); -const newContractsTotal = new client.Counter({ +export const newContractsTotal = new client.Counter({ name: "watch_tower_new_contracts_total", help: "Total number of new contracts", labelNames: ["chain_id"], }); -const singleOrdersTotal = new client.Counter({ +export const singleOrdersTotal = new client.Counter({ name: "watch_tower_single_orders_total", help: "Total number of single orders processed", labelNames: ["chain_id"], }); -const merkleRootTotal = new client.Counter({ +export const merkleRootTotal = new client.Counter({ name: "watch_tower_merkle_roots_total", help: "Total number of merkle roots processed", labelNames: ["chain_id"], }); -const addContractsErrorsTotal = new client.Counter({ +export const addContractsErrorsTotal = new client.Counter({ name: "watch_tower_add_contracts_errors_total", help: "Total number of add contracts errors", labelNames: ["chain_id", "function"], }); -const addContractsRunDurationSeconds = new client.Histogram({ +export const addContractsRunDurationSeconds = new client.Histogram({ name: "watch_tower_add_contracts_run_duration_seconds", help: "Duration of add contracts run", labelNames: ["chain_id"], }); -const processBlockDurationSeconds = new client.Histogram({ +export const processBlockDurationSeconds = new client.Histogram({ name: "watch_tower_process_block_duration_seconds", help: "Duration of process block", labelNames: ["chain_id"], }); -const totalActiveOwners = new client.Gauge({ +export const totalActiveOwners = new client.Gauge({ name: "watch_tower_active_owners_total", help: "Total number of owners with active orders", labelNames: ["chain_id"], }); -const totalActiveOrders = new client.Gauge({ +export const totalActiveOrders = new client.Gauge({ name: "watch_tower_active_orders_total", help: "Total number of active orders", labelNames: ["chain_id"], }); -const totalOrderBookDiscreteOrders = new client.Counter({ +export const totalOrderBookDiscreteOrders = new client.Counter({ name: "watch_tower_orderbook_discrete_orders_total", help: "Total number of discrete orders posted to the orderbook", labelNames: ["chain_id", "handler", "owner", "id"], }); -const totalOrderBookErrors = new client.Counter({ +export const totalOrderBookErrors = new client.Counter({ name: "watch_tower_orderbook_errors_total", help: "Total number of errors when interacting with the orderbook", labelNames: ["chain_id", "handler", "owner", "id", "status", "error"], }); -const totalPollingRuns = new client.Counter({ +export const totalPollingRuns = new client.Counter({ name: "watch_tower_polling_runs_total", help: "Total number of polling runs", labelNames: ["chain_id", "handler", "owner", "id"], }); -const totalPollingOnChainChecks = new client.Counter({ +export const totalPollingOnChainChecks = new client.Counter({ name: "watch_tower_polling_onchain_checks_total", help: "Total number of on-chain hint checks", labelNames: ["chain_id", "handler", "owner", "id"], }); -const pollingOnChainDurationSeconds = new client.Histogram({ +export const pollingOnChainDurationSeconds = new client.Histogram({ name: "watch_tower_polling_onchain_duration_seconds", help: "Duration of on-chain hint checks", labelNames: ["chain_id", "handler", "owner", "id"], }); -const totalPollingOnChainInvalidInterfaces = new client.Counter({ +export const totalPollingOnChainInvalidInterfaces = new client.Counter({ name: "watch_tower_polling_onchain_invalid_interface_total", help: "Total number of invalid on-chain hint interface", labelNames: ["chain_id", "handler", "owner", "id"], }); -const totalPollingOnChainEthersErrors = new client.Counter({ +export const totalPollingOnChainEthersErrors = new client.Counter({ name: "watch_tower_polling_onchain_ethers_errors_total", help: "Total number of ethers on-chain hint errors", labelNames: ["chain_id", "handler", "owner", "id"], }); -const totalPollingUnexpectedErrors = new client.Counter({ +export const totalPollingUnexpectedErrors = new client.Counter({ name: "watch_tower_polling_unexpected_errors_total", help: "Total number of unexpected polling errors", labelNames: ["chain_id", "handler", "owner", "id"], }); - -export { - blockHeight, - reorgDepth, - blockTime, - eventsProcessedTotal, - reorgsTotal, - newContractsTotal, - singleOrdersTotal, - merkleRootTotal, - addContractsErrorsTotal, - addContractsRunDurationSeconds, - processBlockDurationSeconds, - totalActiveOwners, - totalActiveOrders, - totalOrderBookDiscreteOrders, - totalOrderBookErrors, - totalPollingRuns, - totalPollingOnChainChecks, - totalPollingOnChainInvalidInterfaces, - totalPollingOnChainEthersErrors, - pollingOnChainDurationSeconds, - totalPollingUnexpectedErrors, -}; From 19f1c0d888d59b0ed80a5e62f6b470f57d82d969 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Sat, 7 Oct 2023 04:27:38 +0000 Subject: [PATCH 06/11] feat: abstract measure time --- src/domain/addContract.ts | 21 ++++++++--------- src/domain/checkForAndPlaceOrder.ts | 15 ++++++++++++ src/utils/metrics.ts | 36 +++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/domain/addContract.ts b/src/domain/addContract.ts index 257cdc4..3b9b19d 100644 --- a/src/domain/addContract.ts +++ b/src/domain/addContract.ts @@ -21,8 +21,10 @@ import { import { ChainContext } from "./chainContext"; import { ConditionalOrderParams } from "@cowprotocol/cow-sdk"; import { + addContractRunsTotal, addContractsErrorsTotal, addContractsRunDurationSeconds, + measureTime, merkleRootTotal, newContractsTotal, singleOrdersTotal, @@ -42,19 +44,14 @@ export async function addContract( event: ConditionalOrderCreatedEvent ) { const { chainId } = context; - const timer = addContractsRunDurationSeconds - .labels(chainId.toString()) - .startTimer(); - try { - await _addContract(context, event); - } catch (err) { + await measureTime( + () => _addContract(context, event), + [chainId.toString()], + addContractsRunDurationSeconds, + addContractRunsTotal, + handleExecutionError, addContractsErrorsTotal - .labels(context.chainId.toString(), "addContract") - .inc(); - handleExecutionError(err); - } finally { - timer(); - } + ); } async function _addContract( diff --git a/src/domain/checkForAndPlaceOrder.ts b/src/domain/checkForAndPlaceOrder.ts index 2cb2556..17b94e0 100644 --- a/src/domain/checkForAndPlaceOrder.ts +++ b/src/domain/checkForAndPlaceOrder.ts @@ -39,6 +39,7 @@ import { totalPollingRuns, totalPollingUnexpectedErrors, totalPollingOnChainEthersErrors, + measureTime, } from "../utils/metrics"; const GPV2SETTLEMENT = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"; @@ -258,6 +259,20 @@ async function _processConditionalOrder( // Unsupported Order Type (unknown handler) // For now, fallback to legacy behavior // TODO: Decide in the future what to do. Probably, move the error handling to the SDK and kill the poll Legacy + pollResult = await measureTime( + () => + _pollLegacy( + context, + owner, + conditionalOrder, + proof, + offchainInput, + orderRef + ), + metricLabels, + pollingOnChainDurationSeconds, + totalPollingOnChainChecks + ); const timer = pollingOnChainDurationSeconds .labels(...metricLabels) .startTimer(); diff --git a/src/utils/metrics.ts b/src/utils/metrics.ts index d34c2df..8b70062 100644 --- a/src/utils/metrics.ts +++ b/src/utils/metrics.ts @@ -1,5 +1,35 @@ import client from "prom-client"; +export function measureTime( + action: () => T, + labels: string[], + durationMetric: client.Histogram, + totalRunsMetric: client.Counter, + errorHandler?: (err: any) => T, + errorMetric?: client.Counter +): T { + const timer = durationMetric.labels(...labels).startTimer(); + let result: T; + try { + result = action(); + } catch (err) { + if (errorHandler === undefined) { + throw err; + } + + if (errorMetric === undefined) { + throw new Error("errorMetric must be defined if errorHandler is defined"); + } + + errorMetric.labels(...labels).inc(); + result = errorHandler(err); + } finally { + timer(); + } + totalRunsMetric.labels(...labels).inc(); + return result; +} + export const blockHeight = new client.Gauge({ name: "watch_tower_block_height", help: "Block height of the block watcher", @@ -48,6 +78,12 @@ export const merkleRootTotal = new client.Counter({ labelNames: ["chain_id"], }); +export const addContractRunsTotal = new client.Counter({ + name: "watch_tower_add_contract_runs_total", + help: "Total number of add contract runs", + labelNames: ["chain_id"], +}); + export const addContractsErrorsTotal = new client.Counter({ name: "watch_tower_add_contracts_errors_total", help: "Total number of add contracts errors", From 60250e4f503d654060c57b0a154243e1025dedf1 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Sun, 8 Oct 2023 07:56:15 +0000 Subject: [PATCH 07/11] fix: standardise metric variable naming --- src/domain/addContract.ts | 10 ++++----- src/domain/checkForAndPlaceOrder.ts | 32 ++++++++++++++--------------- src/types/model.ts | 6 +++--- src/utils/contracts.ts | 4 ++-- src/utils/metrics.ts | 18 ++++++++-------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/domain/addContract.ts b/src/domain/addContract.ts index 3b9b19d..d4ce36d 100644 --- a/src/domain/addContract.ts +++ b/src/domain/addContract.ts @@ -28,8 +28,8 @@ import { merkleRootTotal, newContractsTotal, singleOrdersTotal, - totalActiveOrders, - totalActiveOwners, + activeOrdersTotal, + activeOwnersTotal, } from "../utils/metrics"; /** @@ -234,7 +234,7 @@ export function add( orders: new Map(), composableCow, }); - totalActiveOrders.labels(registry.network).inc(); + activeOrdersTotal.labels(registry.network).inc(); } } else { log.info(`Adding conditional order to new owner contract ${owner}:`, { @@ -247,8 +247,8 @@ export function add( owner, new Set([{ tx, params, proof, orders: new Map(), composableCow }]) ); - totalActiveOwners.labels(registry.network).inc(); - totalActiveOrders.labels(registry.network).inc(); + activeOwnersTotal.labels(registry.network).inc(); + activeOrdersTotal.labels(registry.network).inc(); } } diff --git a/src/domain/checkForAndPlaceOrder.ts b/src/domain/checkForAndPlaceOrder.ts index 17b94e0..88032ef 100644 --- a/src/domain/checkForAndPlaceOrder.ts +++ b/src/domain/checkForAndPlaceOrder.ts @@ -31,14 +31,14 @@ import { import { ChainContext } from "./chainContext"; import { pollingOnChainDurationSeconds, - totalActiveOrders, - totalActiveOwners, - totalOrderBookDiscreteOrders, - totalOrderBookErrors, - totalPollingOnChainChecks, - totalPollingRuns, - totalPollingUnexpectedErrors, - totalPollingOnChainEthersErrors, + activeOrdersTotal, + activeOwnersTotal, + orderBookDiscreteOrdersTotal, + orderBookErrorsTotal, + pollingOnChainChecksTotal, + pollingRunsTotal, + pollingUnexpectedErrorsTotal, + pollingOnChainEthersErrorsTotal, measureTime, } from "../utils/metrics"; @@ -189,7 +189,7 @@ export async function checkForAndPlaceOrder( const action = deleted ? "Stop Watching" : "Failed to stop watching"; log.debug(`${action} conditional order from TX ${conditionalOrder.tx}`); - totalActiveOrders.labels(chainId.toString()).dec(); + activeOrdersTotal.labels(chainId.toString()).dec(); } } @@ -198,7 +198,7 @@ export async function checkForAndPlaceOrder( for (const [owner, conditionalOrders] of Array.from(ownerOrders.entries())) { if (conditionalOrders.size === 0) { ownerOrders.delete(owner); - totalActiveOwners.labels(chainId.toString()).dec(); + activeOwnersTotal.labels(chainId.toString()).dec(); } } @@ -231,7 +231,7 @@ async function _processConditionalOrder( const id = ConditionalOrderSDK.leafToId(conditionalOrder.params); const metricLabels = [chainId.toString(), handler, owner, id]; try { - totalPollingRuns.labels(...metricLabels).inc(); + pollingRunsTotal.labels(...metricLabels).inc(); const proof = conditionalOrder.proof ? conditionalOrder.proof.path.map((c) => c.toString()) @@ -271,7 +271,7 @@ async function _processConditionalOrder( ), metricLabels, pollingOnChainDurationSeconds, - totalPollingOnChainChecks + pollingOnChainChecksTotal ); const timer = pollingOnChainDurationSeconds .labels(...metricLabels) @@ -342,7 +342,7 @@ async function _processConditionalOrder( signature, }; } catch (e: any) { - totalPollingUnexpectedErrors.labels(...metricLabels).inc(); + pollingUnexpectedErrorsTotal.labels(...metricLabels).inc(); return { result: PollResultCode.UNEXPECTED_ERROR, error: e, @@ -431,7 +431,7 @@ async function _placeOrder(params: { log.debug(`Order`, postOrder); if (!dryRun) { const orderUid = await orderBook.sendOrder(postOrder); - totalOrderBookDiscreteOrders.labels(...metricLabels).inc(); + orderBookDiscreteOrdersTotal.labels(...metricLabels).inc(); log.info(`API response`, { orderUid }); } } catch (error: any) { @@ -492,7 +492,7 @@ function _handleOrderBookError( metricLabels: string[] ): Omit | PollResultErrors { const apiError = body?.errorType as OrderPostError.errorType; - totalOrderBookErrors + orderBookErrorsTotal .labels(...metricLabels, status.toString(), apiError) .inc(); if (status === 400) { @@ -598,7 +598,7 @@ async function _pollLegacy( // We can only get here from some provider / ethers failure. As the contract hasn't had it's say // we will defer to try again. log.error(`${logPrefix} ethers/call Unexpected error`, error); - totalPollingOnChainEthersErrors.labels(...metricLabels).inc(); + pollingOnChainEthersErrorsTotal.labels(...metricLabels).inc(); return { result: PollResultCode.TRY_NEXT_BLOCK, reason: diff --git a/src/types/model.ts b/src/types/model.ts index 01faff1..115c693 100644 --- a/src/types/model.ts +++ b/src/types/model.ts @@ -5,7 +5,7 @@ import { BytesLike } from "ethers"; import type { ConditionalOrderCreatedEvent } from "./generated/ComposableCoW"; import { ConditionalOrderParams, PollResult } from "@cowprotocol/cow-sdk"; import { DBService } from "../utils"; -import { totalActiveOrders, totalActiveOwners } from "../utils/metrics"; +import { activeOrdersTotal, activeOwnersTotal } from "../utils/metrics"; // Standardise the storage key const LAST_NOTIFIED_ERROR_STORAGE_KEY = "LAST_NOTIFIED_ERROR"; @@ -160,12 +160,12 @@ export class Registry { .catch(() => null); // Return registry (on its latest version) - totalActiveOwners.labels(network).set(ownerOrders.size); + activeOwnersTotal.labels(network).set(ownerOrders.size); const numOrders = Array.from(ownerOrders.values()).reduce( (acc, o) => acc + o.size, 0 ); - totalActiveOrders.labels(network).set(numOrders); + activeOrdersTotal.labels(network).set(numOrders); return new Registry( ownerOrders, diff --git a/src/utils/contracts.ts b/src/utils/contracts.ts index c069ab4..197a568 100644 --- a/src/utils/contracts.ts +++ b/src/utils/contracts.ts @@ -8,7 +8,7 @@ import { SupportedChainId, } from "@cowprotocol/cow-sdk"; import { getLogger } from "./logging"; -import { totalPollingOnChainInvalidInterfaces } from "./metrics"; +import { pollingOnChainInvalidInterfacesTotal } from "./metrics"; // Selectors that are required to be part of the contract's bytecode in order to be considered compatible const REQUIRED_SELECTORS = [ @@ -289,7 +289,7 @@ export function handleOnChainCustomError(params: { log.debug( `Contract returned a non-compliant interface revert via getTradeableOrderWithSignature. Simulate: https://dashboard.tenderly.co/gp-v2/watch-tower-prod/simulator/new?network=${chainId}&contractAddress=${target}&rawFunctionInput=${callData}` ); - totalPollingOnChainInvalidInterfaces.labels(...metricLabels).inc(); + pollingOnChainInvalidInterfacesTotal.labels(...metricLabels).inc(); return { result: PollResultCode.DONT_TRY_AGAIN, reason: "Order returned a non-compliant (invalid/erroneous) revert hint", diff --git a/src/utils/metrics.ts b/src/utils/metrics.ts index 8b70062..9d7a746 100644 --- a/src/utils/metrics.ts +++ b/src/utils/metrics.ts @@ -102,37 +102,37 @@ export const processBlockDurationSeconds = new client.Histogram({ labelNames: ["chain_id"], }); -export const totalActiveOwners = new client.Gauge({ +export const activeOwnersTotal = new client.Gauge({ name: "watch_tower_active_owners_total", help: "Total number of owners with active orders", labelNames: ["chain_id"], }); -export const totalActiveOrders = new client.Gauge({ +export const activeOrdersTotal = new client.Gauge({ name: "watch_tower_active_orders_total", help: "Total number of active orders", labelNames: ["chain_id"], }); -export const totalOrderBookDiscreteOrders = new client.Counter({ +export const orderBookDiscreteOrdersTotal = new client.Counter({ name: "watch_tower_orderbook_discrete_orders_total", help: "Total number of discrete orders posted to the orderbook", labelNames: ["chain_id", "handler", "owner", "id"], }); -export const totalOrderBookErrors = new client.Counter({ +export const orderBookErrorsTotal = new client.Counter({ name: "watch_tower_orderbook_errors_total", help: "Total number of errors when interacting with the orderbook", labelNames: ["chain_id", "handler", "owner", "id", "status", "error"], }); -export const totalPollingRuns = new client.Counter({ +export const pollingRunsTotal = new client.Counter({ name: "watch_tower_polling_runs_total", help: "Total number of polling runs", labelNames: ["chain_id", "handler", "owner", "id"], }); -export const totalPollingOnChainChecks = new client.Counter({ +export const pollingOnChainChecksTotal = new client.Counter({ name: "watch_tower_polling_onchain_checks_total", help: "Total number of on-chain hint checks", labelNames: ["chain_id", "handler", "owner", "id"], @@ -144,19 +144,19 @@ export const pollingOnChainDurationSeconds = new client.Histogram({ labelNames: ["chain_id", "handler", "owner", "id"], }); -export const totalPollingOnChainInvalidInterfaces = new client.Counter({ +export const pollingOnChainInvalidInterfacesTotal = new client.Counter({ name: "watch_tower_polling_onchain_invalid_interface_total", help: "Total number of invalid on-chain hint interface", labelNames: ["chain_id", "handler", "owner", "id"], }); -export const totalPollingOnChainEthersErrors = new client.Counter({ +export const pollingOnChainEthersErrorsTotal = new client.Counter({ name: "watch_tower_polling_onchain_ethers_errors_total", help: "Total number of ethers on-chain hint errors", labelNames: ["chain_id", "handler", "owner", "id"], }); -export const totalPollingUnexpectedErrors = new client.Counter({ +export const pollingUnexpectedErrorsTotal = new client.Counter({ name: "watch_tower_polling_unexpected_errors_total", help: "Total number of unexpected polling errors", labelNames: ["chain_id", "handler", "owner", "id"], From 17331339569504f2690266b2a57da06a209f0159 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Sun, 8 Oct 2023 07:56:41 +0000 Subject: [PATCH 08/11] fix: remove duplicate polling --- src/domain/checkForAndPlaceOrder.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/domain/checkForAndPlaceOrder.ts b/src/domain/checkForAndPlaceOrder.ts index 88032ef..0961500 100644 --- a/src/domain/checkForAndPlaceOrder.ts +++ b/src/domain/checkForAndPlaceOrder.ts @@ -273,19 +273,6 @@ async function _processConditionalOrder( pollingOnChainDurationSeconds, pollingOnChainChecksTotal ); - const timer = pollingOnChainDurationSeconds - .labels(...metricLabels) - .startTimer(); - pollResult = await _pollLegacy( - context, - owner, - conditionalOrder, - proof, - offchainInput, - orderRef - ); - timer(); - totalPollingOnChainChecks.labels(...metricLabels).inc(); } // Error polling From ab73682b7cbaee9d12e5e2ad9a6028aca66093f9 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Tue, 10 Oct 2023 00:26:33 +0000 Subject: [PATCH 09/11] fix: add re-org depth metric --- src/domain/chainContext.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/domain/chainContext.ts b/src/domain/chainContext.ts index e82227a..0d7f1e2 100644 --- a/src/domain/chainContext.ts +++ b/src/domain/chainContext.ts @@ -17,6 +17,7 @@ import { blockTime, eventsProcessedTotal, processBlockDurationSeconds, + reorgDepth, reorgsTotal, } from "../utils/metrics"; @@ -249,6 +250,9 @@ export class ChainContext { // This may be a re-org, so process the block again reorgsTotal.labels(chainId.toString()).inc(); log.info(`Re-org detected, re-processing block ${blockNumber}`); + reorgDepth + .labels(chainId.toString()) + .set(lastBlockReceived - blockNumber + 1); } lastBlockReceived = blockNumber; From caf88129496deb8c87f1bf7a3ecbb6872386335b Mon Sep 17 00:00:00 2001 From: mfw78 Date: Tue, 10 Oct 2023 00:27:11 +0000 Subject: [PATCH 10/11] fix: measure time named params --- src/utils/metrics.ts | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/utils/metrics.ts b/src/utils/metrics.ts index 9d7a746..15b3d5a 100644 --- a/src/utils/metrics.ts +++ b/src/utils/metrics.ts @@ -1,15 +1,24 @@ import client from "prom-client"; -export function measureTime( - action: () => T, - labels: string[], - durationMetric: client.Histogram, - totalRunsMetric: client.Counter, - errorHandler?: (err: any) => T, - errorMetric?: client.Counter -): T { - const timer = durationMetric.labels(...labels).startTimer(); - let result: T; +export interface MeasureTimeParams { + action: () => T; + labelValues: string[]; + durationMetric: client.Histogram; + totalRunsMetric: client.Counter; + errorHandler?: (err: any) => U; + errorMetric?: client.Counter; +} + +export function measureTime({ + action, + labelValues, + durationMetric, + totalRunsMetric, + errorHandler, + errorMetric, +}: MeasureTimeParams): T | U { + const timer = durationMetric.labels(...labelValues).startTimer(); + let result: T | U; try { result = action(); } catch (err) { @@ -21,12 +30,12 @@ export function measureTime( throw new Error("errorMetric must be defined if errorHandler is defined"); } - errorMetric.labels(...labels).inc(); + errorMetric.labels(...labelValues).inc(); result = errorHandler(err); } finally { timer(); } - totalRunsMetric.labels(...labels).inc(); + totalRunsMetric.labels(...labelValues).inc(); return result; } From 4f7a0368d2f331509b363fa18b56498781f583a3 Mon Sep 17 00:00:00 2001 From: mfw78 Date: Tue, 10 Oct 2023 01:14:45 +0000 Subject: [PATCH 11/11] fix: measure time usage --- src/domain/addContract.ts | 16 ++++++++-------- src/domain/checkForAndPlaceOrder.ts | 17 +++++++++++------ src/utils/utils.spec.ts | 2 +- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/domain/addContract.ts b/src/domain/addContract.ts index d4ce36d..919142e 100644 --- a/src/domain/addContract.ts +++ b/src/domain/addContract.ts @@ -44,14 +44,14 @@ export async function addContract( event: ConditionalOrderCreatedEvent ) { const { chainId } = context; - await measureTime( - () => _addContract(context, event), - [chainId.toString()], - addContractsRunDurationSeconds, - addContractRunsTotal, - handleExecutionError, - addContractsErrorsTotal - ); + await measureTime({ + action: () => _addContract(context, event), + labelValues: [chainId.toString()], + durationMetric: addContractsRunDurationSeconds, + totalRunsMetric: addContractRunsTotal, + errorHandler: handleExecutionError, + errorMetric: addContractsErrorsTotal, + }); } async function _addContract( diff --git a/src/domain/checkForAndPlaceOrder.ts b/src/domain/checkForAndPlaceOrder.ts index 0961500..c0c5b7f 100644 --- a/src/domain/checkForAndPlaceOrder.ts +++ b/src/domain/checkForAndPlaceOrder.ts @@ -259,8 +259,8 @@ async function _processConditionalOrder( // Unsupported Order Type (unknown handler) // For now, fallback to legacy behavior // TODO: Decide in the future what to do. Probably, move the error handling to the SDK and kill the poll Legacy - pollResult = await measureTime( - () => + pollResult = await measureTime({ + action: () => _pollLegacy( context, owner, @@ -269,10 +269,15 @@ async function _processConditionalOrder( offchainInput, orderRef ), - metricLabels, - pollingOnChainDurationSeconds, - pollingOnChainChecksTotal - ); + labelValues: metricLabels, + durationMetric: pollingOnChainDurationSeconds, + totalRunsMetric: pollingOnChainChecksTotal, + }); + } + + // This should be impossible to reach, but satisfies the compiler + if (pollResult === undefined) { + throw new Error("Unexpected error: pollResult is undefined"); } // Error polling diff --git a/src/utils/utils.spec.ts b/src/utils/utils.spec.ts index a0048c6..ae666a4 100644 --- a/src/utils/utils.spec.ts +++ b/src/utils/utils.spec.ts @@ -100,7 +100,7 @@ describe("handle on-chain custom errors", () => { revertData: abiToSelector( CUSTOM_ERROR_ABI_MAP[CustomErrorSelectors.SINGLE_ORDER_NOT_AUTHED] ), - metricLabels: [], + metricLabels: ["chain_id", "handler", "owner", "id"], }; it("should pass a known selector correctly", () => {