【初心者向け】エラーログの読み方解説

Agaroot IT Partners(AITP)のtomoです。

プログラマ、エンジニアとして働く中で、避けては通れないのがエラーログの解読。自分たちのコードのどこが間違っているのか、どうすれば直るのか理解するには、大抵の場合ログを読むのが一番手っ取り早いでしょう。

しかし初心者はもちろん、数年以上のご経験のある方でも意外とこのログの解読に苦労されている方が多いのではないでしょうか?

そこで今回は僭越ながらエラーログ解読のコツをプログラミング初心者向けにお伝えできればと思います。

この記事はこのような方におすすめ

  • プログラミング初心者の方(言語不問、あくまでもプログラミング全般に通底するところのみ触れます)
  • 長文の英語エラーログを前に絶望している方

要約

  • 読むべき箇所は自分(達)が書いたコードに関する記述のみ
  • エラーログの英文は怖くない
  • エラーログは確実なファクトを端的に伝えているだけ、解決策は自分達で探るもの

初心者にありがちなエラーログ解読

プログラマ、エンジニアとして曲がりながら何年か勤めてきた中で、たくさんの初心者の方にも出会ってきました。初心者の方がエラーログに出くわしてしまった時、このような反応を示されることが多いかと思います。

全部読もうとする

プログラミングを始めてみようと一念発起された初心者の方には熱心な方、真面目な方が多くいらっしゃいます。そのため初めて出くわしたエラーログを全て読もうとされる方も少なくありません。

しかし残念ながら、大抵全部読もうとされても何もわからず、長時間かけた労力が無駄に終わってしまうかと思います(もっとも、経験値にはなったかと思うので本当の意味においては無駄でないはずです)。

英語に拒否反応

プログラミングにおけるエラーログとはつまり、長い英文です。英語学習に苦手意識があると、どうしてもエラーログにも拒否反応を示される方が多いでしょう。

どうすればいいか全くわからない

そして何より、エラーログを前にどうすればいいのかわからない。長文のどこから手をつけて、どのように原因を探ればいいのかわからない。

私がかつて客席常駐のエンジニアだった頃、新人の方がPCを前に完全に固まっていたのでどうしたのか聞いたところ、「エラーログの長文を見て何をしたらわからず、頭が真っ白になりました」とおっしゃっていました。

分かります、その気持ち。最初はよくわからなすぎて頭が真っ白になったり、イライラしてしまったり反応は人それぞれですが、私含めて最初はエラーログに困ったものです。
ぜひこの記事を通して、その拒否反応を克服してください💪

エラーログはこうして読み解く

では、どのようにエラーログを読み解いていけばいいのでしょうか?私は大まかにこのような流れでエラーログを読むようにしています。

  1. 自分(達)が書いたコードに関する記述を探す
  2. 1. に記述されたエラーの原因を考える
  3. 2. の原因を踏まえて解決策を考える

「え? たった3ステップ? しかも当たり前のことを並べただけなのでは…?」と思われた方も多いかと思います。はい、このたった3つのシンプルなステップを踏まえれば大抵のエラーは解決できるかと思います。

解説用のソースコードおよびエラーログ

今回解説していくにあたり、以下のコードとエラーログを用いていこうかと思います。

よければ読者の皆様も、下記サイトにアクセスし、左側にソースコードを記述、Runボタンを押してエラーログが出力されるのを確認してみてください。
https://lwebapp.com/en/python-playground

実際のプロダクト開発などで見かけるソースコードに比べると遥かに単純なソースコードで、当然エラーログも簡単なものになっていますが、ノウハウを理解するにはまずはこのくらいが適切かと思います。

ソースコード

def calc(numerator, denominator):
    return numerator / denominator


print(calc(1, int(False)))

エラーログ

Traceback (most recent call last):
  File "/lib/python3.10/asyncio/futures.py", line 201, in result
    raise self._exception
  File "/lib/python3.10/asyncio/tasks.py", line 232, in __step
    result = coro.send(None)
  File "/lib/python3.10/site-packages/_pyodide/_base.py", line 500, in eval_code_async
    await CodeRunner(
  File "/lib/python3.10/site-packages/_pyodide/_base.py", line 351, in run_async
    coroutine = eval(self.code, globals, locals)
  File "<exec>", line 5, in <module>
  File "<exec>", line 2, in calc
ZeroDivisionError: division by zero

自分(達)が書いたコードに関する記述を探す

ではここから各ステップの説明に入ります。

「初心者にありがちなエラーログ解読」でも触れましたが、初心者の方はエラーログを全て読もうとしがちです。

しかし、本当は全て読む必要はありません。では、どこを読むべきか?
答えは簡単で、自分(チームでの開発であれば自分達)が書いたコードに関する記述です。

もう一度エラーログを見てください。エラーログのうち前半部分にある/lib/python3.10/site-packages/_pyodide/_base.py/lib/python3.10/asyncio/futures.pyを、私は一切開発していません。ましてやそれらのどこをどう変えれば今回のエラーが解消するのか知る由もありません(ついでに言えば、これらのコードに自ら手を加えるべきではないでしょう)。
こういった自分(達)が書いていないコード、つまり利用している言語やライブラリなどのコードに関する記述を読んでも、大抵の場合何も解決策は掴めないことが多いです。

私が知っているコードは後半部分に書かれたline 5line 2の記述です。これは私が書いたコードです。ここならば、どこをどう変えると何が起こるのか多少は想像つくでしょう。
そしてそれの結果出力されているZeroDivisionError: division by zeroというのが、どうやら今回解消すべきエラーそのもののようだと分かります。

このように、まずは自分達が書いたコードに関する記述のみを探ると、読むべき箇所が絞られ、事象がはっきりします。
チームで開発している場合、自分自身は知らないコードなどがエラーログに出力されていることもあるので、その際には上長や他メンバーに協力を仰ぎながら読み取っていきましょう。

エラーログ前半部分

Traceback (most recent call last):
  File "/lib/python3.10/asyncio/futures.py", line 201, in result
    raise self._exception
  File "/lib/python3.10/asyncio/tasks.py", line 232, in __step
    result = coro.send(None)
  File "/lib/python3.10/site-packages/_pyodide/_base.py", line 500, in eval_code_async
    await CodeRunner(
  File "/lib/python3.10/site-packages/_pyodide/_base.py", line 351, in run_async
    coroutine = eval(self.code, globals, locals)

エラーログ後半部分

  File "<exec>", line 5, in <module>
  File "<exec>", line 2, in calc
ZeroDivisionError: division by zero

エラーの原因を考える

では次に、ログに書かれたエラーが起きた原因を考えてみましょう。

今回出力されたエラーとはZeroDivisionError: division by zeroです。これはcalc関数内の2行目で起こっており、そしてこの関数は5行目で呼び出されています。

そもそも、ZeroDivisionError: division by zeroとはどういった意味かと言えば、0で割ること、例えば100 ÷ 0 = ?といった計算を行っているということです。

このような計算は大抵のプログラミング言語で禁止されエラーとなりますし、小学校での割り算の勉強を思い出していただければ、0で割り算を行うと答えが出せないことはご存知かと思います。

では、何故0で割ってしまっているのか? これを自分が書いたコードから探っていくと、どうやらcalc関数の2つ目の引数denominatorに0が代入されていることに気づくかと思います。
となると怪しいのが5行目の呼び出しで2つ目の引数をint(False)と指定していること。Python入門書などが手元にあれば、bool型の値をint型に変換したらどうなるのか調べてみてください。Falseの場合は0になると書かれているかと思います(おそらく他言語でもそうなることが多いでしょう)。
https://water2litter.net/rum/post/python_bool_to_int/

今回は自己解決も不可能ではないレベルだったかと思いますが、エラーメッセージなどをGoogleなどで検索したり、Stack Overflowなどで質問してみると効果的なことも多いかと思います。

少し話はそれますが、上記のZeroDivisionError: division by zeroという文言、これだけ見れば大変単純な英語だと思いませんか? 単語などが分からずとも、翻訳サイトなどで検索すれば「ゼロ除算」などと日本語訳が出るかと思います。
エラーログに限らず、プログラミングや広くIT業界で見聞きする英語はそれほど難しいものではないように感じます。
語彙に関して言えば、”server”や”function”など、英語の英単語、イディオムがそのまま日本語でも使われていたりプログラミング言語で予約語になっていることが多いです。入門書などで知った用語がそのまま英文で登場することが多く、理解もスムーズにできるでしょう。
また文法、語法もそこまで難しくなく、例えば仮定法が使われたり一文に複数の節が含まれたりすることはまずありません。
機械学習やブロックチェーンに関する英語論文などは別ですが、レファレンスガイドやGitHub、Stack Overflowなどで見かける英語は、機械翻訳を適宜利用すれば理解できるかと思います。
初心者にありがちなエラーログ解読の特徴として、英語に拒否反応を示すというのを挙げましたが、臆することなく英文に挑戦してみてください🔤

解決策を考える

ここまで分かったことを整理すると

  • calc関数の2つ目の引数に0を指定するとZeroDivisionError: division by zeroというエラーが起こる
  • 5行目で実際に、2つ目の引数に0を指定してcalc関数を呼び出しているのでエラーが起きている

となるかと思います。

逆に言えば、どこをどのように変更すべきなのか、エラーログのメッセージも直接的には指示してくれません。エラーをどう解消するのか考えるのはあくまでも自分(達)なのです。ということで、解決策を考えてみましょう。

単純に「エラーが起きないようにする」だけならば、例えば以下のように様々な方法が考えられます(もちろん他の選択肢も考えられるでしょう)。

  • 引数の値が0でないかチェックする
    • 0であれば独自例外を投げるようにする
    • 0であれば計算処理を行わないようにする(関数の呼び出し元であれば、そもそも関数を呼ばないようにする)
  • 引数の値が0であれば固定値などを返却する
    • 数値
      • 1つ目の引数(numerator)をそのまま返却する
      • 1や0などを返却する
    • None(Python以外の言語でいうnullに相当)

上記選択肢はそれぞれ、calc関数内で実施することも呼び出し元で実施することもできるでしょう。
このように様々な選択肢が考えられるわけですが、この中から自分(達)にとってベストな選択肢を探っていくことになります。

その際に一番重要なのが、そもそも修正しようとしている処理は何を行うためのものなのか意識することです。
例えばcalc関数が割合や確率などを計算するためのものであれば、上記で挙げた「1や0などを返却する」という選択肢には注意しなければなりません。計算結果を100%や0%と同一とみなすことを意味するためです。
処理の目的そのものに背くような選択肢を選ぶべきではありません。

より可読性や保守性が高いコードを目指すための参考として、プログラミング設計に関するノウハウをまとめたデザインパターンも参考になるかと思います。例えば下記書籍などがビジネスで用いるデザインパターンの参考になるかと思います(ただ、少し難しいです💦)。
https://www.oreilly.co.jp/books/9784814400331/

チーム開発の場合には上長や他メンバーにもご相談ください。チームで採用しているデザインパターンがあったり、他のソースコードで既に類似の問題に対応していて、それを参照するように助言してくれるかもしれません。また、他メンバーが書いたソースコードのバグ発見につながる可能性もあります。

なお、修正の影響範囲があまりにも大きすぎたり、期限が迫っていたりすれば理想の策を断念し、次善の策を採用することも多いです。とある関数を修正するのが理想的だとしても、その関数が100箇所以上で呼び出されているとしたらどうでしょうか? 呼び出し元を含めた再実装やテストを行う労力を避けるため、関数そのものでなく一部の呼び出し元に手を加えるといった判断が行われることは少なくありません。

このようにエラーへの対応の仕方は様々に考えられますが、様々な開発経験を通して次第に慣れてくるかと思います。

まとめ

いかがだったでしょうか。最初は突然画面に表示されたエラーログを前に呆然としてしまいがちですが、順を追ってエラー解読していく作業をこなしていくと、だんだんとエラー解決に慣れていくはずです。

手順は至って単純。自分(達)のコードに関する記述のみを抜き出してエラー内容を理解し、その原因を特定してから解決策を考える、それだけです。
エラーログ解読を繰り返す中で、どこを読めばいいのか、原因は何かが即座にわかるようになり、そしてより良い解決策も思いつくようになるかと思います。

本記事が参考になれば幸いです👋

関連するタグ