多くの例外メッセージに有用な詳細が含まれていないのはなぜですか?


220

例外メッセージが有用な詳細を含むべきであるという合意のある量があるようです

システムコンポーネントからの多くの一般的な例外に有用な詳細が含まれていないのはなぜですか?

いくつかの例:

  • .NETのListインデックスアクセスがArgumentOutOfRangeExceptionないではない私にしようと、無効でした指標値を教え、またそれは私の許容範囲を教えてくれありません。
  • 基本的に、MSVC C ++標準ライブラリからのすべての例外メッセージはまったく役に立たない(上記と同じように)。
  • .NETのOracleの例外。「言い換えると」「テーブルまたはビューが見つかりません」と表示されますが、どちらも表示されません。

したがって、私には、ほとんどの場合、例外メッセージに有用な詳細が含まれていないようです。私の期待はずれていますか?私はこれに気付くような間違った例外を使用していますか?または、私の印象が間違っているかもしれません:例外の大部分実際に有用な詳細を提供しますか?


59
セキュリティ専門家の側からは、「エラーメッセージにはシステムの内部に関する詳細を含めてはならない」というのが経験則であることに注意してください。
テラスティン

118
@Telastyn:システムが攻撃者に開かれている場合のみ。たとえば、Webサーバーを実行している場合は、エンドユーザーに当たり障りのないエラーメッセージを提供しますが、非常に詳細なエラーメッセージを最後に記録する必要があります。また、ユーザーが攻撃者ではないクライアント側のソフトウェアでは、エラーメッセージをできる限り詳細に表示する必要があります。これにより、何か問題が発生してバグレポートが送信された場合に、対処する情報が多くなります。できるだけ多くの時間を得ることができるからです。
メイソンウィーラー

9
@Snowman:クライアント側のソフトウェアの場合、ユーザーがアクセスできないものは何ですか?マシンの所有者はマシンを所有し、何でも取得できます。
メイソンウィーラー


6
常に追加情報が必要です。あなたが例として挙げたメッセージは非常に良いと思います。それらを使用して問題をデバッグできます。「エラー0x80001234」(Windows Updateに触発された例)よりもはるかに優れています。
usr

回答:


204

例外の概念はソフトウェアエンジニアリングの分野ではまだ十分に成熟していないため、例外には有用な詳細が含まれていません。

はい、IndexOutOfRangeException範囲外の正確なインデックスと、スローされた時点で有効だった範囲を含める必要があり、.NETランタイムの作成者に代わって軽視することはできません。はい、Oracleのtable or view not found例外には、検出されなかったテーブルまたはビューの名前を含める必要があります。また、このことを担当するすべてのユーザーに代わって、軽視することはできません。

大部分は、混乱は、例外には人間が読み取れるメッセージを含めるべきであるという誤った当初の考えに由来し、その例外はすべての例外についての理解の欠如に起因するため、悪循環です。

人は例外に人間が読めるメッセージを含めるべきだと考えているため、例外によって運ばれる情報も人間が読めるメッセージにフォーマットする必要があると考えており、人間が読めるすべてのメッセージを書くことに退屈しているコードを作成するか、そうすることで、pr索好きな目がメッセージを見るかもしれないものに、不適切な量の情報を漏らしてしまうのではないかと恐れています。(他の回答で言及されているセキュリティ問題。)

しかし、問題の真実は、例外が人間が読めるメッセージを含むべきではないため、彼らはそれを心配するべきではないということです。例外とは、プログラマーだけが見たり対処したりするべきものです。障害情報をユーザーに提示する必要がある場合は、非常に高いレベルで、洗練された方法で、統計的には英語ではないユーザーの言語で行う必要があります。

したがって、プログラマにとって、例外の「メッセージ」は例外のクラス名であり、例外に関連する他の情報はすべて、例外オブジェクトの(最終/読み取り専用)メンバー変数にコピーする必要があります。可能であれば、考えられるすべての要素を少しずつ。この方法では、メッセージを生成する必要がない(または生成する必要がない)ため、pr索好きな目には見えません。

以下のコメントでトーマス・オーエンズが表明した懸念に対処するには:

はい、もちろん、あるレベルでは、例外に関するログメッセージ作成します。しかし、あなたはあなたが言っていることに問題を既に見ています:一方では、スタックトレースのない例外ログメッセージは役に立たないが、他方では、ユーザーに例外スタックトレース全体を見せさせたくない。繰り返しますが、ここでの問題は、私たちの視点が従来の慣行によって歪められていることです。ログファイルは従来、プレーンテキストでしたが、これは私たちの学問がまだ始まったばかりの頃は問題なかったかもしれませんが、おそらくもうそうではありません。セキュリティ上の懸念がある場合、ログファイルはバイナリまたは暗号化する必要があります。

バイナリテキストでもプレーンテキストでも、ログファイルは、アプリケーションがデバッグ情報をシリアル化するストリームと考える必要があります。このようなストリームはプログラマーの目だけのものであり、例外のデバッグ情報を生成するタスクは、例外をデバッグログストリームにシリアル化するのと同じくらい簡単にする必要があります。このように、ログを見ると、例外クラス名(既に述べたように、すべての実用的な目的のための「メッセージ」)が表示されます。そして、ログに含めるのが実用的で、スタックトレース全体です。このプロセスでは、人間が読み取れる例外メッセージのフォーマットが顕著に欠落していることに注意してください。

PS

この主題に関する私の考えのもう少しはこの答えで見つけることができます:よい例外メッセージを書く方法

PPS

バイナリログファイルについての私の提案によって多くの人がチェックされているように見えるので、ここで提案しているのはログファイルがバイナリであるべきではないことをさらに明確にするためにもう一度答えを修正しましたが、必要に応じて、ログファイルバイナリにすることができます。


4
.NET Frameworkのいくつかの例外クラスを調べたところ、この種の情報をプログラムで追加する機会がたくさんあることがわかりました。ですから、質問は「なぜそうしないのか」に解決すると思います。しかし、「人間が読める」もの全体については+1です。
ロバートハーヴェイ

7
例外に人間が読めるコンポーネントを含めるべきではないことに同意しません。あるレベルでは、例外に関するログメッセージを作成できます。スタックトレースをユーザーが読み取り可能なログファイルに記録すると、公開したくない実装の詳細が公開されるため、人間が読み取れるメッセージをログに記録する必要があると思います。エラーを含むログファイルが提示された場合、開発者はデバッグを開始し、例外を強制的に発生させるための開始点を持っている必要があります。人間が読めるコンポーネントは、実装を提供せずに適切に詳細化する必要があります。
トーマスオーエンズ

44
プログラマーは人間じゃないの?私の同僚を見ると、これは私がしばらくの間持っていたいくつかの疑念を確認します
...-gbjbaanb

51
繰り返しますが、ソフトウェアがクライアント側である限り、ユーザーにスタックトレース全体を表示させることに何の問題もありません。私がこれまでに取り組んできたすべてのプロフェッショナルソフトウェアプロジェクト、およびアマチュアプロジェクトのほとんどには、未処理の例外が発生したときに完全なエラーダンプを生成するロギングシステムが含まれていました。処理する。また、エラーメッセージを返信するために必要であるため(単に有用ではありませんが、必要です)、ユーザーはいつでも好きなときに見ることができます!何が悪いの?
メイソンウィーラー

13
また、バイナリのみのログファイルについても確信が持てません。主にsystemdの経験があります。これらのログを表示するための特別なツールは非常に紛らわしく、シェークスピアのサルの委員会によって設計されたようです。Webアプリケーションの場合、例外を最初に目にする人は多くの場合sysadminであり、彼がそれを修正する必要があるかどうか(たとえば、ディスクが容量不足になった)か、またはパスバックするかを判断します。開発者に。
マイケルハンプトン

46

システムコンポーネントからの多くの一般的な例外に有用な詳細が含まれていないのはなぜですか?

私の経験では、例外には有用な情報が含まれていない理由がいくつかあります。このような理由はシステムコンポーネントにも当てはまると思いますが、確かなことはわかりません。

  • セキュリティを重視する人々は、例外を情報漏えいの原因と考えています(:)。例外のデフォルトの動作はその情報をユーザーに表示することであるため、プログラマーは注意を怠ってしまうことがあります。
  • C ++では、catchブロックでメモリを割り当てることに反対する意見を耳にしました(良いメッセージを作成するためのコンテキストの少なくとも一部があります)。この割り当ては管理が難しく、さらに悪いことに、メモリ不足の例外が発生する可能性があります。多くの場合、アプリがクラッシュしたり、メモリがリークしたりします。メモリを割り当てずに例外をうまくフォーマットすることは難しく、プログラマーがそうするように、その慣習は言語間で移行した可能性があります。
  • 彼らは知りません。私が意味する、そこにあるコードは持っていないシナリオ全く何が悪かったのかアイデアが。コードがわからない場合-それはあなたに伝えることができません。
  • 私は、ローカライズの懸念により、英語のみの文字列をシステムに入れることができない場所で働いてきました-例外は、英語を話すサポートスタッフのみが読むことができます。
  • いくつかの場所では、例外がアサートのように使用されるのを見てきました。開発中に、何かが行われていない、または変更が1つの場所で行われたが別の場所では行われていないという明確で大きなメッセージを提供するためにあります。多くの場合、これらは十分にユニークであるため、優れたメッセージが重複した努力や単純な混乱を招きます。
  • 人々は怠け者で、ほとんどのプログラマよりもプログラマーです。幸福な道よりも例外的な道に費やす時間ははるかに少なく、これは副作用です。

私の期待はずれですか?私はこれに気付くような間違った例外を使用していますか?

ちょっと?つまり、例外にはいいメッセージがあるはずですが、例外でもあります。例外条件を回避するためのコードの設計に時間を費やすか、例外条件を処理するためのコードを記述して(メッセージを無視して)、コーディング時の一種の対話型フィードバックメカニズムとして使用しないでください。デバッグ目的で使用することは避けられませんが、ほとんどの状況では最小限に抑える必要があります。この問題に気づいたことは、あなたがそれらを防ぐのに十分な仕事をしていないことを心配させます。


私はこれがCとしてタグ付けされていることを知っていますが、例外ベースのエラー処理レポートに大きく依存する可能性があるため(あなたの最後の段落はすべての言語に適用されるわけではありません)
リリエンタール

1
@リリエンタール-など?私がよく知っている言語は、そのようなことを定期的にしていません。
テラスティン

1
この答えには多くの優れた内容があると思いますが、最終的な意見を言うことを避けています。
-djechlin

3
ありがとう。あなたの懸念は根拠がありません(私は願っています:-)。しかし、そのユニットテストで何がうまくいかなかったかを追跡するために余分な時間を費やすたびに、またはログファイルに情報がないためにコードを分析するために余分な時間を費やすたびに、いくつかのメッセージの回避可能なクラッピーにイライラしています:
Martin Ba

@Telastyn SAP独自のABAPには、基本的に動的(多言語)メッセージとともにプログラムの状態(成功、失敗、警告)をユーザーに報告するためのオブジェクトであるメッセージを含むことができる例外クラス構成があります。私はこの種のものがどれほど広まっているのかわからない、またはそれが言語で奨励されていたとしてもそれが可能であったとしても、それが(残念ながら)一般的な慣行である場所が少なくとも1つあることを認めます。
リリエンタール

12

私は過剰なC#の経験、具体的にはC ++を持っているわけではありませんが、これを伝えることができます-開発者が作成した例外は10回のうち9回は、これまでに見られる一般的な例外よりも有用です。

理想的には、一般的な例外がエラーの正確な原因を示し、簡単に修正できますが、現実的には、さまざまな種類の例外をスローできる複数のクラスを持つ大規模なアプリケーションでは、同じ種類の例外は、デフォルトのメッセージに依存するよりも、エラーを返すための独自の出力を記述する方が常に価値があります。

多くの人が指摘しているように、一部のアプリケーションは、セキュリティ上の理由から、または混乱を避けるために、ユーザーに見せたくないエラーメッセージをスローしたくないため、これはどうあるべきかです。

代わりに、設計でアプリケーションにスローされる可能性のあるエラーの種類を予測し(常にエラーが発生する)、問題を特定するのに役立つエラーキャッチメッセージを作成する必要があります。

これは常に役立つとは限りません-どのエラーメッセージが役立つかを常に予測できるとは限らないためです-しかし、それは長期的に自分のアプリケーションをよりよく理解するための最初のステップです。


7

問題は、「システムコンポーネント」(別名標準ライブラリクラス)によってスローされる多くの例外に、有用な詳細が含まれていない理由を具体的に尋ねていることです。

残念ながら、ほとんどの開発者はコアコンポーネントを標準ライブラリに記述せず、詳細な設計ドキュメントやその他の設計根拠を公開する必要もありません。言い換えれば、私たちは確実に知らないかもしれません。

ただし、詳細な例外情報が望ましくない、または重要ではない理由について留意すべき2つの重要なポイントがあります。

  1. 例外は、任意の方法でコードを呼び出すことで使用できます。標準ライブラリは、例外の使用方法に制約を設定できません。具体的には、ユーザーに表示される場合があります。範囲外の配列インデックスを検討してください。これは、攻撃者に役立つ情報を提供する可能性があります。言語設計者は、アプリケーションがスローされた例外をどのように使用するか、またはアプリケーションの種類(Webアプリやデスクトップなど)を使用する方法を知らないため、情報を省くことはセキュリティの観点から安全です。

  2. 例外ユーザーに表示しないください。代わりに、わかりやすいエラーメッセージを表示し、攻撃者がアクセスできない場所に例外を記録します(該当する場合)。エラーが特定されたら、開発者はコードをデバッグし、スタックフレームとロジックパスを検査する必要があります。この時点で、開発者には、例外が期待する以上の情報があります。


3
1.この基準に基づいて、どの情報(「範囲外のインデックス」、スタックトレース)が何であるかは比較的arbitrary意的であり、(インデックスの値)は表示されません。2.関連する動的な値がわかっている場合、デバッグが大幅に高速かつ容易になる可能性があります。例えば、それは多くの場合、すぐに問題が失敗したコードの一部、または正しく良いの入力に対処するために失敗しているコードにガベージ入力されているかどうかを言うだろう
ベン・アーロンソン

@BenAaronson例外のID /クラスは、エラーのタイプを示します。私のポイントは、セキュリティの詳細が省略される可能性がある(つまり、どの特定の値がエラーを引き起こしたか)ことです。その値は、ユーザー入力にまでさかのぼることができ、情報を攻撃者に明らかにする可能性があります。

@Snowman完全なスタックトレースが利用可能で、インデックス番号が利用可能でない場合、セキュリティは考慮事項ではないと思います。確かに、攻撃者がバッファオーバーフローを調査していることは理解していますが、多くの例外は非常に安全なデータも除外しています(たとえば、Oracleテーブルが見つかりませんでした)
gbjbaanb

@gbjbaanbは、ユーザーに完全なスタックトレースを表示する必要があると言っていますか?

1
その洞察を共有していただきありがとうございます。個人的に、私は同意しませんし、セキュリティの議論が完全な誤acyだと思いますが、それは理論的根拠かもしれません。
マーティンBa

4

最初に、diagメッセージに4秒以内に正確なコード行とサブコマンドを表示する情報が読み込まれている場合でも、ユーザーがそれを書き留めたり、サポートスタッフに伝えたりしない可能性があると言って、バブルを爆発させますそして「あなたは違反について何か言っていた...それが複雑に見えたのか分からない!」と言われます。

私は30年以上ソフトウェアを書いて他のソフトウェアの結果をサポートしてきましたが、個人的には、最終結果がどのようにモデルに適合するかに関係なく、例外メッセージの現在の品質は実質的にセキュリティとは何の関係もありません私たちの業界の多くの人が元々独学であり、彼らがコミュニケーションに教訓を含めなかったという事実と関係があるのです。おそらく、すべての新しいコーダーを何年かの間メンテナンスの立場に追い込み、何が間違っているのかを理解しなければならなかったとしたら、彼らは少なくとも何らかの形の精度の重要性を理解するでしょう。

最近、アプリケーションを再構築する際に、戻りコードが3つのグループのいずれかに分類されることが決定されました。

  • 0から9は、追加情報を使用した成功から成功までです。
  • 10〜99は致命的ではない(回復可能な)エラーであり、
  • 101〜255は致命的なエラーです。

(100は何らかの理由で除外されました)

特定のワークフローでは、考えが再利用されたか、ジェネリックコードは致命的なエラーにジェネリックリターン(> 199)を使用し、ワークフローで100の致命的なエラーが発生する可能性があります。メッセージ内のデータをわずかに区別すると、ファイルが見つからないなどのエラーはすべて同じコードを使用し、メッセージで区別できます。

コードが請負業者から戻ってきたとき、事実上すべての単一致命的エラーが戻りコード101だったとき、私たちの驚きを信じないでしょう。

みなさんの質問に対する答えは、最初に作成されたとき、誰にも戻れないプレースホルダーになるはずだったので、メッセージはとても無意味だと思います。最終的に人々は、メッセージではなく問題を解決する方法を見つけました。

その時以来、自己教えた人々は、例外が何を含むべきかという良い例を持っていませんでした。それに加えて、メッセージを読まないユーザーを増やして、サポートに引き渡そうとしません(使用者によってカットアンドペーストされたエラーメッセージを見たことがあります。たくさんの情報のように思えたので、私はすべてを望むことはできなかったので、彼らはそれをランダムに削除しました。

そして、それがより多くの作業であり、フラッシュを追加しない場合、価値がありません...

最後の注意:エラーメッセージにエラー/リターンコードが含まれている場合、実行されたモジュールのどこかに「if condition return code-value」のような行があるはずで、return codeの理由が条件でわかるはずです発生した。これは単純な論理的アプローチのように思えますが、私の人生では、コード80241013または他の非常にユニークな識別子でWindowsのアップグレードが失敗したときに何が起こったのかをMicrosoftに教えてください。ちょっと悲しいですね。


8
「...事実上、すべての単一致命的エラーが戻りコード101であった場合、私たちの驚きを信じられないでしょう。」あなたが正しい。請負業者があなたの指示に従ったときにあなたが驚いたとは信じません。
オダリック

3
chances are the users will never write it down... and you will be told "Well it said something about a violation..." このため、例外ログツールを使用して、スタックトレースを含むエラーレポートを自動的に生成し、場合によってはサーバーに送信することもあります。あまり技術的ではないユーザーが1人いました。彼女がエラーロガーからメッセージを送信するたびに、「何が間違っていたのかわからないが...」などのようになります。 。しかし、私はいつも彼女からエラー報告を受け取りました!
メイソンウィーラー

3

例外には可能な限り多くの情報を含めるか、少なくとも一般的ではないことに同意します。テーブルが見つからない場合、テーブル名がいいでしょう。

しかし、例外を受け取ったコード内の場所で何をしようとしていたかについて、あなたはもっと知っています。制御できないライブラリで問題が発生した場合、状況を修正するために実際に多くのことを行うことはできませんが、どの状況で問題が発生したかについて、より有用な情報を追加できます。

テーブルが見つからない場合、見つからないテーブルはSTUDENTSと呼ばれ、その文字列はコードのどこにもないため、そのテーブルはSTUDENTSと呼ばれると言われてもあまり役に立ちません。

ただし、例外をキャッチして、実行しようとしていたSQLステートメントで例外を再スローすると、名前フィールドがRobertのレコードを挿入しようとしたことがわかります。ドロップテーブルの生徒; (常にxkcdがあります!)

情報に満たない例外と闘うために、やってみようとしていることに特化したより多くの情報でtry-catch-rethrowを行います。

私はおそらく、これが質問の理由に関する答えになるために、ライブラリのメーカーからの焦点が例外メッセージをより良くすることに焦点を当てていない可能性が高い理由を追加する必要があります失敗したものが試行された理由、そのロジックは呼び出しコードにあります。


たとえば、C ++ではthrow_with_nested多く使用する必要があります。
マイルルーティング

3

少し異なる答えをするために:問題のコードはおそらく仕様に合わせて行われています:

  • 関数はXを受け取り、Yを返します
  • Xが無効な場合、例外Zをスローします

最小限の手間で最小限の時間で(レビュー/テストで拒否されることを恐れて)仕様に正確に対応するプレッシャーを加えると、完全に準拠した役に立たないライブラリ例外のレシピが手に入ります。


3

例外には、言語および実装固有のコストがかかります。

たとえば、呼び出しフレームをスローしてから呼び出しフレームをキャッチするまでの間に存在するすべてのデータを破棄するには、C ++例外が必要です。したがって、プログラマーは例外をあまり使用したくない。

Ocamlでは、例外のスローはCとほぼ同じ速度で行われるためsetjmp(コストは通過する呼び出しフレームの数に依存しません)、開発者はそれを大量に使用できます(例外的ではない、非常に一般的な場合でも)。対照的に、C ++例外は十分に重いため、Ocamlの場合のようにあまり使用しないでしょう。

典型的な例は、非常に深い再帰の内部で「停止」できる再帰的な検索または探索です(たとえば、木の葉や統合関数を見つける)。一部の言語では、その条件をすべての呼び出し元に伝播する方が高速です(非常に慣用的です)。他の言語では、例外をスローする方が高速です。

そのため、言語(およびそれを使用する開発者の習慣)に応じて、例外には多くの有用な詳細が含まれる場合があります。


6
「たとえば、C ++例外は、呼び出しフレームをスローしてから呼び出しフレームをキャッチするまでの間に生きているデータをすべて破壊するために必要です。それは高価です。したがって、プログラマは例外をあまり使いたくありません。」合計がらくた。C ++例外の主な強みは、デストラクタが決定論的に呼び出されることです。彼らがただジャンプした場合、誰もそれらを使用しません。
マイルルーティング

私はそれを知っていますが、C ++の例外はOcamlのようではなく、Ocamlのように使用しないのは事実です。
バジルStarynkevitch

5
主にC ++の制御フローにC ++例外を使用しないのは、そうするとコードが読みにくくなり、理解しにくくなるためです。
マイルルーティング

-2

何があなたは、インデックス値または必要な範囲またはテーブルの名前を考えさせる例外のために、便利な詳細?

例外はエラー処理メカニズムではありません。それらは回復メカニズムです。

例外のポイントは、例外を処理できるコードのレベルまでバブルアップすることです。そのレベルがどこであって、必要な情報を持っているか、関係ありません。必要な情報はあるが、すぐにアクセスできない場合は、適切なレベルで例外を処理していません。

追加情報役立つ可能性があると思うのは、クラッシュしてエラーダンプを出力するアプリケーションの絶対的な最上位レベルです。しかし、この情報にアクセスし、コンパイルし、保存するのは例外の仕事ではありません。

どこにでも「新しい例外を投げる」ことができると言っているのではなく、それを良いと呼んでいます。悪い例外を書くことは可能です。しかし、関係のない情報を含めることは、それらを適切に使用するために必要なことではありません。


私はあなたが作っているポイントを理解し、同情しますが、欠落しているデータベーステーブルの名前を無関係とは言いません。賛成票を投じました。
ダニエルホリンレイク

1
@DanielHollinrakeはい、テーブル名は例の中で最も重要ではありません。私が使用しているコードでは、この種の問題は憂鬱なほど一般的であり、テーブル名がどのようにマングルされているを見ると、何が間違っているのかがわかります。私は、これらの事柄が例外とは無関係である理由の例を考えようとしました。多分私はこれを使用することができます...
Odalrick

同意しません。例外を処理するために、アプリケーションをクラッシュから保護したいだけなので、詳細を追加する必要はないかもしれませんが、最後に、それが機能しなかった理由を伝えて修正できるようにする必要があります。例外タイプ以外の手がかりがない場合は、見栄えの良いデバッグを行います。
t3chb0t

@ t3chb0tそれが、スタックトレースと例外のクラス、メモリダンプ、その他すべての目的です。それはどのように役立つだろう、あなたの顧客の通話であれば、「コンピュータがあることは、それがインデックス5にアクセスしようとしましたが、それがなかったことを私に伝えます。」と言います。それは可能性がありますが、リスト、および役に立つかもしれないリストや何かのためのコンテキストをシリアル化する場合にのみ役立ちます。例外をスローするたびにアプリケーションの状態全体をシリアル化しないでください。
オダリック

する方がはるかに簡単です...のはSqlExeptionが発生したとしましょう、それはあなたが知っているすべてです:それはどんな意味を持っていない可能性があり、顧客のために私は、私は多分、その後;-)別の例を得ることができるすべての情報を重視開発者として@Odalrick 修理、それをどの接続文字列、データベース、またはテーブルなどが機能しなかったかを知っている場合....これは、アプリケーションが複数のデータベースを使用している場合はさらに重要です。詳細なスタックトレースを行うには、アプリケーションにpdbファイルを同梱する必要があります...常に可能であるとは限りません。また、メモリダンプは非常に大きくなる可能性があり、常に転送できるとは限りません。
t3chb0t
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.