Microsoftのドキュメントの誤訳を報告したら炎上した件について(当事者目線での話)
この件について改善がなされるとの発表がMicrosoft DOCS International Teamからありました。
少なくとも、誤訳の報告についてはやりやすくなるそうです。
ご担当者様及びに尽力してくださった方々には感謝いたします。
https://github.com/dotnet/docs.ja-jp/issues/118#issuecomment-408283458
今回の私の記事に憤ってる人たちは改善されたことを確認するためにドキュメントフィードバックを送りまくってそれをブログにでもまとめればいいと思うんですよ。
— 軒先のネコ (@megascus) 2018年7月28日
-----------------------------------追記ここまで----------------------------------------------
ということで、おとといぐらいから炎上している件ですが、レポーター目線で今のところきちんとした解説がないので、何があったのかを説明したいと思っています。
(タイトルが正確でないのは自覚があります)
以下のチケットですね。
https://github.com/dotnet/docs.ja-jp/issues/118
なお、以下の記事がMicrosoftのエヴァンジェリスト様からよくまとまってると評価されて広まっていますが、まったくもって正しくないです。*1
https://ufcpp.net/blog/2018/7/DocsMistranslation/
// 有識者を名乗るテレビのコメンターが物知り顔で適当なことを言っているのをお茶の間で眺めてしまった当事者の気持ちがよくわかった。
20180727追記ここから
記事の中で、「こんなのわかるわけないよっていうチケット」という表現を使っていますが、Microsoftの社員様からこんなチケット判るわけがないという言葉をいただいたためそのように表現しています。実際にわかりにくいかどうかはgithubのチケットを確認のうえ、各自でご判断ください。
20180727追記ここまで
この記事を書いた人の属性
普段はMicrosoft製品はWindowsぐらいしか使っていません。今回の誤訳の報告もtwitter上で、翻訳おかしいよpgrというのが回ってきてみてみたら、ページにフィードバックフォームがあったから報告した程度のライト層です。なので、Microsoft社とのビジネス上のつながりは薄い(ほぼ皆無)ですし、Microsoft製品のコミュニティには数えるほどしか参加したことがありません。
ページに紐づいたフィードバックフォームからgithubに転載されるMicrosoftのドキュメントフィードバックの仕組み
以下のページにもフィードバックフォームがあるので適当に遊んでみるとよいと思います。
https://docs.microsoft.com/ja-jp/azure/architecture/patterns/
それを押すと画面の下に移動し、githubのログインを求められます。
githubにログインするとフォームが現れるので、それに入力してsubmitするとgithubに転載されます。
この記事書くためにキャプチャ撮った時に初めて気が付いたけど、直接githubにフィードバックを残すってオプションもあるのね・・・・・・(このフォームを介するかどうか程度でしかない)
その結果、githubだけ見るとこんなのわかるわけないよっていうチケットが出来上がりました。
投稿者が思いもよらない運用
- 後から見たほかの人がわからない形でgithubに転記される(せめて、ソースへのリンクがgithub上のmdファイルへではなくdocs.microsoftの対象ページへのリンクだったらわかりやすかったかもしれない)
- フィードバックのページ自体はすべて日本語で運用されているので、まさかその先が日本語がわからない人しかいないとは思わない
githubに転記された段階で他の人がわかりやすいように書き直せよって言われればそうかもしれないけど、ぽっと出の人にそこまで求めるのか・・・・・・・・
利用者に深いコントリビュートを求めるMicrosoftの報告の仕組み
これだけは最初のページでの報告の仕方を読んで理解しました。最初に全然正しくないと書きましたが、ここに関しては正しいんだろうなぁと思います。
以下のような感じでスクショを張ればよいとのこと。
https://github.com/dotnet/docs.ja-jp/issues/118#issuecomment-407252836
ここまでの報告は無理だろ
最初のフォームだとそもそもスクショを張ることはできないし、というあたりで、まったく外した記事になっている。そもそも通りすがり程度で報告しただけの人にそこまでの労力を求めないでほしい。
深いコントリビュートが求められるマイクロソフトコミュニティ
なんとなく見ててそうだろうなぁとは思っていたのですが、マイクロソフトコミュニティはビジネス上のつながりが強すぎて、深い形でのコントリビュートがされて当然、そうでなければやらないほうが良いというのがあるんだろうなぁと思いました。
私なんかはコミュニティ関係は完全に無償でやってて趣味程度なので、特にまじめにやるつもりはないのですが、お金もらってるMicrosoftの担当者きちんと仕事しろではなく、報告者ちゃんとしろみたいな記事を、Microsoftのエヴァンジェリスト様が広める程度なのは、マイクロソフトコミュニティのヒエラルキーの底辺としては悲しい。MSMVPとかもあって、Microsoftの機嫌を損ねるわけにもいかないのでしょうがないのかもしれませんが。
個人的な反省
- githubに転記された段階できちんと書き直すべきだったかもしれない。ただし、自称有識者が物知り顔で外した解説記事を書く程度になるとは思わなかったし、Microsoftの中の人から空リプで、こんな報告わかるわけないだろと非難されるとは思っていなかった。
- -1をお願いするようなツイートはよくなかった。ただし、githubのコミュニケーションとして問題がある対応については-1をつけるというのは通常の範囲だと思うし、そのコミュニケーションを設計したのはgithub(現:Microsoft)だし、Microsoftの中の人からDMで代替手段の提示がないまま-1つけるのを煽るのは止めろ的な発言されたのは割と悲しい。だれか解決方法わかる人を探せと言われても、そこまでのコストを払わなければいけないのか・・・・・・・
Microsoftへのお願い
コミュニティである以上、自称有識者が出てきて物知り顔でちんぷんかんぷんな解説が始まり、レポーターが疲弊するってことは今後もあり得るので、コミュニティを介さない形でのフィードバックの仕方を検討してほしい。
以上の通り、上記のブログについては正しくない内容なので、なぜこれを御社がこの記事は正しい記事ですと広めたのかについては、今回の顛末報告のなかできちんと説明してほしい。
(顛末報告があるのか知らんけど)
自分たちで作ったシステムを棚に上げて、SNS上でこんなのわかるわけないだろみたいな発言は慎むべきかと。
という感じで。
お願い
これはあくまでレポーター目線の話なので、間違えていることもたくさんあると思います。これは間違えてるぞということがあればコメント欄に記載ください。(その時に参考文献があるとより望ましい)
空リプでdisられてもわからんよ・・・・・
追記
空リプでこんなのわかるわけないと投げてきたMS社員等はいましたが、チケット内外でサポートしてくださった @yoshioterada さんや @okazuki さんなどには感謝を申し上げます。
ただ、MSMVPの人がMSのドキュメントは昔からこうなんだ!文句があるならもっとコントリビュートするかMSの社員になってから言えや!的なことを呟いていたのはめっちゃ笑ったw
togetterでも悪意のあるまとめられ方してて面白いw
https://togetter.com/li/1250494
上記は消されてしまったので、保管していた人のツイートでも
https://twitter.com/_sfus/status/1022404526811176960
追記の追記
空リプでこの記事の内容間違ってるってツイートしているMS社員がいたので記念に張り付けておきます。(空リプで文句言う人がいて悲しいって話をしたのに・・・・・・)
https://twitter.com/wreulicke/status/1022356995607683072
追記の追記の追記
当初チケットが立つまでの履歴ができました。ありがとうございます!!!
https://togetter.com/li/1250507
追記の追記の追記の追記
変なこと言ってたMSMVPがいたよーとかコメント欄で報告してくださっている方もいますが私はMSMVPにかかわっているわけではないので、必要があればMSに直接お伝えくださるようお願いします。
以下からその人が本当にMSMVPであるかも検索できますので、そちらから特定したうえでMSに伝えれば伝わるでしょう。
https://mvp.microsoft.com/en-us/MvpSearch?lo=Japan&sc=e
Javaのスレッドで発生したキャッチされてない例外をログに出力する
概要
- Thread#setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler) を呼び出すことでアプリケーション全体のログ設定を変更することができる。
- 上の設定をThreadGroupで上書きすることができる。ただし、ThreadGroup自体がUncaughtExceptionHandlerを継承しているため、ThreadGroupのサブクラスを自前で作成する必要がある。
- 上の設定をThread#setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)を呼び出すことで上書きすることができる。
説明
Javaのスレッドの実行では、通常、エラーハンドリングは自前で行うのですが、キャッチされないRuntimeExceptionの処理を忘れてしまったりすることもあるため、共通のログ処理等を入れておくと便利です。
Thread#setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)を呼び出してエラー処理を書き換える
Thread#setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler) を呼び出すことでアプリケーション全体のログ設定を変更することができます。
ただ、何かしらのフレームワークを使用している場合は、すでに処理が含まれている可能性があるため、使用前には必ず何も設定されていないことを確認してください。
Thread.UncaughtExceptionHandler dueh = Thread.getDefaultUncaughtExceptionHandler(); if (dueh == null) { Thread.setDefaultUncaughtExceptionHandler((Thread t, Throwable e) -> { System.out.println("これはデフォルトのUncaughtExceptionHandlerで出力されています。"); System.out.println("ThreadGroup=" + t.getThreadGroup().getName() + "," + "Thread=" + t.getName()); e.printStackTrace();//適切なロガーで出力してください。 }); }
ThreadGroupでエラー処理を書き換える
Javaにはいくつかのスレッドをまとめるスレッドグループというものが存在します。エラーハンドリングを複数のスレッドにまとめて設定することができます。
ThreadGroupクラスになりますが、このクラス自体がUncaughtExceptionHandlerインターフェースを実装しているため、ログ設定をいじるためにはThreadGroupクラスを継承したクラスを作成する必要があります。また、Javaのスレッドがどのスレッドグループに所属するかどうかの設定は通常、スレッドの作成時のみにしかできないため、ExecutorServiceの作成時にThreadFactoryを渡して所属するスレッドグループを指定してあげる必要があります。
import java.util.concurrent.ThreadFactory; class LoggingByThreadGroupThreadFactory implements ThreadFactory { private static final ThreadGroup loggingThreadGroup = new LoggingThreadGroup("loggingThreadGroup"); @Override public Thread newThread(Runnable r) { Thread t = new Thread(loggingThreadGroup, r); //スレッドを作成するときにスレッドグループを渡すことでそのスレッドグループに所属するスレッドになる。 // t.setUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl());ここで設定するとThreadGroupは使用されない return t; } static class LoggingThreadGroup extends ThreadGroup { public LoggingThreadGroup(String name) { super(name); } @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("これはスレッドグループで出力されています。"); System.out.println("ThreadGroup=" + t.getThreadGroup().getName() + "," + "Thread=" + t.getName()); e.printStackTrace();//適切なロガーで出力してください。 } } }
ExecutorService service = Executors.newFixedThreadPool(1, new LoggingByThreadGroupThreadFactory());
Thread毎にエラー処理を書き換える
Thread#setUncaughtExceptionHandler(UncaughtExceptionHandler)を呼び出すことでスレッド毎のエラー処理を書き換えることができます。
ThreadGroupと同様にExecutorServiceの作成時にThreadFactoryを渡すことで、スレッド内で実行されるロジックからログ出力の設定を隠ぺいすることができます。
import java.util.concurrent.ThreadFactory; public class LoggingThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl()); return t; } private class UncaughtExceptionHandlerImpl implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("これはスレッドに設定されたUncaughtExceptionHandlerで出力されています。"); System.out.println("ThreadGroup=" + t.getThreadGroup().getName() + "," + "Thread=" + t.getName()); e.printStackTrace();//適切なロガーで出力してください。 } } }
ExecutorService service = Executors.newFixedThreadPool(1, new LoggingThreadFactory());
もちろん、自分自身のスレッドのエラー処理を書き換えてしまうこともできます。
ただ、わけがわからなくなると思うのでピンポイントでのデバッグ用途以外ではやらないほうが良いでしょう。
Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl());
まとめ
説明したAPIを使用することでスレッド内で発生したキャッチされない例外を捕捉することができるようになります。
通常は標準エラーにスタックトレースが出力されてしまいますが、必要に応じてカスタマイズするようにしてください。
ソースコードの全体は以下に。
https://gist.github.com/megascus/0961698e426bf7d1f23b2f7be9be8210
Oracleにjdbcで接続したときのタイムゾーンの設定
以下のような感じらしい。
デフォルトではクライアントのユーザーが使用しているタイムゾーンが使用される。=システム環境変数(user.timezone)の値
システム環境変数で指定されているため、実行時のオプションで上書きすることが出来る
java -jar XXXX.jar -Duser.timezone=Asia/Tokyo
特定のセッションだけタイムゾーンを変更したい場合は、ALTER SESSIONで変更することが出来る。
※タイムゾーンを変更するためのAPIは調べた限り存在しないっぽい。
ALTER SESSION SET TIME_ZONE='Asia/Tokyo'
現在のセッションのタイムゾーンを確認したい場合は以下のSQLを実行する。
select SESSIONTIMEZONE from dual
なお、コネクションプールを使用している場合はセッションの変更は引き継がれてしまうので注意。
Tomcatのバージョンごとのweb.xmlのヘッダー
それぞれバージョンがあってないと一部使用できない機能があるので注意。
※metadata-completeのデフォルト値はfalseで、trueの場合はウェブアプリケーションに関係するアノテーションが読み込まれない。
Tomcat 10.1
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" version="6.0" metadata-complete="false">
Tomcat 10.0
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0" metadata-complete="false">
Tomcat 9.X
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="false">
Tomcat 8.X
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="false">
Tomcat 7.X
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="false">
JenkinsのGitプラグインで更新チェックに複数のブランチが引っかかった場合
JenkinsのGitプラグインで更新チェックに複数のブランチが引っかかった場合は複数のブランチに対してそれぞれビルドが走る。
ひとつのビルドタスクに以下のような記述が出て、他のはそれに引きずられてビルドされる感じ。
Multiple candidate revisions Scheduling another build to catch up with ${project_name}