2024年9月8日日曜日

lacinia pedestalのschemaのビルドはtry catchで異常時の情報をprintすると便利


背景

clojure(言語)のlacinia-pedestal(ライブラリ)でgraphql実装時にschemaなどに不備があってビルドに失敗するとエラーが表示されて処理が止まります。
標準で表示されるエラーの内容の情報が少なくて不具合発生時の原因特定に苦労していたのですが、try catch後にprintしたら原因が分かりやすくなりました。
便利だったので対応内容を紹介します。

対象

lacinia pedestalを利用しているサーバー

作成中のログ取りサーバーに適用しました。
https://github.com/walmartlabs/lacinia-pedestal

対応内容

schemaのビルド処理にtry catch printを適用します。

対応前
(defn build-schema []
(-> (io/resource "schema.edn")
slurp
edn/read-string
(util/inject-resolvers resolver-map)
schema/compile))

対応後
(defn build-schema []
(try
(let [schema (-> (io/resource "schema.edn")
slurp
edn/read-string
(util/inject-resolvers resolver-map)
schema/compile)]
schema)
(catch Exception e
(println "failed build-schema")
(println e)
(throw e))))

リポジトリのコードで示すと、下記の箇所に変更を施しました。
https://github.com/asukiaaa/clj-server-practice/blob/c8f72a095db2d41b3a7e4f943c04049fef39f01c/back/src/asuki/back/handlers/graphql.clj#L14-L25

表示内容の変化

対応前はビルドを記述している箇所で問題が発生したことだけが通知されていたので、不具合箇所の特定が手間でした。

対応前
Syntax error (ExceptionInfo) compiling at (asuki/back/handlers/graphql.clj:32:1).
inject error: not found

Full report at:
/tmp/clojure-38089154129225883.edn

対応後はschemaのビルドに対して何が悪いのか情報が表示されます。
下記のエラーはQuery/devie_groupの挿入先が無いのが問題と分かります。

対応後
failed build-schema
#error {
:cause inject error: not found
:data {:key :Query/device_group}
:via
[{:type clojure.lang.ExceptionInfo
:message inject error: not found
:data {:key :Query/device_group}
:at [com.walmartlabs.lacinia.internal_utils$name__GT_path invokeStatic internal_utils.clj 227]}]
:trace
[[com.walmartlabs.lacinia.internal_utils$name__GT_path invokeStatic internal_utils.clj 227]
[com.walmartlabs.lacinia.internal_utils$name__GT_path invoke internal_utils.clj 217]
[com.walmartlabs.lacinia.util$inject_resolvers$fn__27335 invoke util.clj 209]
[clojure.lang.PersistentHashMap$NodeSeq kvreduce PersistentHashMap.java 1307]
[clojure.lang.PersistentHashMap$BitmapIndexedNode kvreduce PersistentHashMap.java 802]
[clojure.lang.PersistentHashMap$NodeSeq kvreduce PersistentHashMap.java 1312]
[clojure.lang.PersistentHashMap$BitmapIndexedNode kvreduce PersistentHashMap.java 802]
[clojure.lang.PersistentHashMap kvreduce PersistentHashMap.java 236]
[clojure.core$fn__8460 invokeStatic core.clj 6847]
[clojure.core$fn__8460 invoke core.clj 6832]
[clojure.core.protocols$fn__8189$G__8184__8198 invoke protocols.clj 175]
[clojure.core$reduce_kv invokeStatic core.clj 6858]
[clojure.core$reduce_kv invoke core.clj 6849]
[com.walmartlabs.lacinia.util$inject_resolvers invokeStatic util.clj 208]
[com.walmartlabs.lacinia.util$inject_resolvers invoke util.clj 193]
[asuki.back.handlers.graphql$build_schema invokeStatic graphql.clj 19]
[asuki.back.handlers.graphql$build_schema invoke graphql.clj 14]
[asuki.back.handlers.graphql$eval36218 invokeStatic graphql.clj 27]
[asuki.back.handlers.graphql$eval36218 invoke graphql.clj 27]
[clojure.lang.Compiler eval Compiler.java 7181]
[clojure.lang.Compiler load Compiler.java 7640]
[clojure.lang.RT loadResourceScript RT.java 381]
[clojure.lang.RT loadResourceScript RT.java 372]
[clojure.lang.RT load RT.java 459]
[clojure.lang.RT load RT.java 424]
[clojure.core$load$fn__6856 invoke core.clj 6115]
[clojure.core$load invokeStatic core.clj 6114]
[clojure.core$load doInvoke core.clj 6098]
[clojure.lang.RestFn invoke RestFn.java 408]
[clojure.core$load_one invokeStatic core.clj 5897]
[clojure.core$load_one invoke core.clj 5892]
[clojure.core$load_lib$fn__6796 invoke core.clj 5937]
[clojure.core$load_lib invokeStatic core.clj 5936]
[clojure.core$load_lib doInvoke core.clj 5917]
[clojure.lang.RestFn applyTo RestFn.java 142]
[clojure.core$apply invokeStatic core.clj 669]
[clojure.core$load_libs invokeStatic core.clj 5974]
[clojure.core$load_libs doInvoke core.clj 5958]
[clojure.lang.RestFn applyTo RestFn.java 137]
[clojure.core$apply invokeStatic core.clj 669]
[clojure.core$require invokeStatic core.clj 5996]
[clojure.core$require doInvoke core.clj 5996]
[clojure.lang.RestFn invoke RestFn.java 930]
[asuki.back.route$eval17323$loading__6737__auto____17324 invoke route.clj 1]
[asuki.back.route$eval17323 invokeStatic route.clj 1]
[asuki.back.route$eval17323 invoke route.clj 1]
[clojure.lang.Compiler eval Compiler.java 7181]
[clojure.lang.Compiler eval Compiler.java 7170]
[clojure.lang.Compiler load Compiler.java 7640]
[clojure.lang.RT loadResourceScript RT.java 381]
[clojure.lang.RT loadResourceScript RT.java 372]
[clojure.lang.RT load RT.java 459]
[clojure.lang.RT load RT.java 424]
[clojure.core$load$fn__6856 invoke core.clj 6115]
[clojure.core$load invokeStatic core.clj 6114]
[clojure.core$load doInvoke core.clj 6098]
[clojure.lang.RestFn invoke RestFn.java 408]
[clojure.core$load_one invokeStatic core.clj 5897]
[clojure.core$load_one invoke core.clj 5892]
[clojure.core$load_lib$fn__6796 invoke core.clj 5937]
[clojure.core$load_lib invokeStatic core.clj 5936]
[clojure.core$load_lib doInvoke core.clj 5917]
[clojure.lang.RestFn applyTo RestFn.java 142]
[clojure.core$apply invokeStatic core.clj 669]
[clojure.core$load_libs invokeStatic core.clj 5974]
[clojure.core$load_libs doInvoke core.clj 5958]
[clojure.lang.RestFn applyTo RestFn.java 137]
[clojure.core$apply invokeStatic core.clj 669]
[clojure.core$require invokeStatic core.clj 5996]
[clojure.core$require doInvoke core.clj 5996]
[clojure.lang.RestFn invoke RestFn.java 551]
[asuki.back.core$eval214$loading__6737__auto____215 invoke core.clj 1]
[asuki.back.core$eval214 invokeStatic core.clj 1]
[asuki.back.core$eval214 invoke core.clj 1]
[clojure.lang.Compiler eval Compiler.java 7181]
[clojure.lang.Compiler eval Compiler.java 7170]
[clojure.lang.Compiler load Compiler.java 7640]
[clojure.lang.RT loadResourceScript RT.java 381]
[clojure.lang.RT loadResourceScript RT.java 372]
[clojure.lang.RT load RT.java 459]
[clojure.lang.RT load RT.java 424]
[clojure.core$load$fn__6856 invoke core.clj 6115]
[clojure.core$load invokeStatic core.clj 6114]
[clojure.core$load doInvoke core.clj 6098]
[clojure.lang.RestFn invoke RestFn.java 408]
[clojure.core$load_one invokeStatic core.clj 5897]
[clojure.core$load_one invoke core.clj 5892]
[clojure.core$load_lib$fn__6796 invoke core.clj 5937]
[clojure.core$load_lib invokeStatic core.clj 5936]
[clojure.core$load_lib doInvoke core.clj 5917]
[clojure.lang.RestFn applyTo RestFn.java 142]
[clojure.core$apply invokeStatic core.clj 669]
[clojure.core$load_libs invokeStatic core.clj 5974]
[clojure.core$load_libs doInvoke core.clj 5958]
[clojure.lang.RestFn applyTo RestFn.java 137]
[clojure.core$apply invokeStatic core.clj 669]
[clojure.core$require invokeStatic core.clj 5996]
[clojure.core$require doInvoke core.clj 5996]
[clojure.lang.RestFn invoke RestFn.java 408]
[clojure.run.exec$requiring_resolve_SINGLEQUOTE_ invokeStatic exec.clj 36]
[clojure.run.exec$requiring_resolve_SINGLEQUOTE_ invoke exec.clj 29]
[clojure.run.exec$exec$fn__151 invoke exec.clj 44]
[clojure.run.exec$exec invokeStatic exec.clj 43]
[clojure.run.exec$exec doInvoke exec.clj 39]
[clojure.lang.RestFn invoke RestFn.java 423]
[clojure.run.exec$_main$fn__205 invoke exec.clj 180]
[clojure.run.exec$_main invokeStatic exec.clj 176]
[clojure.run.exec$_main doInvoke exec.clj 139]
[clojure.lang.RestFn invoke RestFn.java 397]
[clojure.lang.AFn applyToHelper AFn.java 152]
[clojure.lang.RestFn applyTo RestFn.java 132]
[clojure.lang.Var applyTo Var.java 705]
[clojure.core$apply invokeStatic core.clj 667]
[clojure.main$main_opt invokeStatic main.clj 514]
[clojure.main$main_opt invoke main.clj 510]
[clojure.main$main invokeStatic main.clj 664]
[clojure.main$main doInvoke main.clj 616]
[clojure.lang.RestFn applyTo RestFn.java 137]
[clojure.lang.Var applyTo Var.java 705]
[clojure.main main main.java 40]]}
Syntax error (ExceptionInfo) compiling at (asuki/back/handlers/graphql.clj:27:1).
inject error: not found

Full report at:
/tmp/clojure-11531825119366624527.edn

try catch printはns-trackerとも相性が良い

コードの自動更新ライブラリであるns-trackerを使うと開発が捗るのですが、解釈時のエラーが無視されるのでschemaのビルド時のエラーも無視されます。
しかしながら、ns-trackerを使ってコードを読み直すときもprintは実施されるので、今回紹介したtry catch printを使うとエラーが黙認されずに表示されるので相性が良いです。

おわり

try catch printを使うことでlacinia pedestalのschemaのビルド時の不具合情報を表示できました。
また、ns-tracker利用時にも不具合情報が見れるため、開発しやすくなりました。

今回try catch printで表示した情報は標準で出て欲しいエラー情報だと思うので、こういうことをしなくても表示する方法ご存知でしたら、コメントなどで教えてもらえると嬉しいです。

参考

lacinia-pedestal
ns-tracker
try
catch
print

0 件のコメント :