JupyterNotebook + almondで Scala 環境を構築する
仕事でたまたま、整形の難しいアプリケーションログを追うことになった。
テキストをいじくるとき、自分はAmmoniteをよく利用するのだが、
(これってもしかして、JupyterNotebookが使えるのでは…?)
とふと思い立った。
色々調べたところ、almondというものを使うと良さそうだった。
インストール
(ちょっと調べた感じだとDockerImageが見当たらなかった…)
Macです。
Jupyterのインストール
$ brew install jupyter
coursierのインストール
almondのインストールに使用します
$ brew install --HEAD coursier/formulas/coursier
almondのインストール
$ SCALA_VERSION=2.12.7 ALMOND_VERSION=0.1.11 $ coursier bootstrap \ -i user -I user:sh.almond:scala-kernel-api_$SCALA_VERSION:$ALMOND_VERSION \ sh.almond:scala-kernel_$SCALA_VERSION:$ALMOND_VERSION \ -o almond $ ./almond --install
使ってみる
JupyterNotebookを立ち上げる
$ jupyter notebook
ブラウザが自動的に立ち上がる
Notebookを作成
立ち上がった画面から、
New → Notebook: Scala
を選択
Scalaコードを書く
すげー!
補完もちゃんと効きます!!
AmmoniteのMagicImportもちゃんと使えるし、
plotly-scalaでグラフも書ける!!!
これめっちゃ楽しいですね。
Jupyterは色々なエクスポートの方法があるので、調査レポートにはもってこいですし(エンジニアも読みやすい)、普通にREPLとして使うのも全然いけますね!
DockerImageください!!
追記
趣味で作っているもので良ければhttps://t.co/fqy4LdC9vY
— 齊藤健司(ゆめかけ) (@yume_piece1010) 2018年11月15日
元々、jupyter-scala だったものが、almod と名前変わったんです
@yume_piece1010 さんがDockerImageを作成されてたそうです!やったぜ。
jupyter-scala
から almond
に名前が変わったんですねー。
さらに追記
上記から更に削った軽量版も作っていただきました!
https://t.co/ciM6lrOWC9
— 齊藤健司(ゆめかけ) (@yume_piece1010) 2018年11月15日
作りました〜
docker run -it -p 8888:8888 -v $(pwd):/root/notebooks poad/docker-jupyter-scala
GradleでJavaのコードフォーマット環境を整える
最近GradleでJavaのコードフォーマットの環境を整えたので、その備忘録。
google-java-format-gradle-plugin
これが一番楽そうだった
build.gradleに依存を追加する
plugins { id 'com.github.sherter.google-java-format' version '0.7.1' }
フォーマットを実行する
$ ./gradlew googleJavaFormat /home/blacky/projects/java/sentry-config/src/main/java/com/github/nomadblacky/sentry/config/DefaultTypesafeConfigSentryClientFactory.java: formatted successfully BUILD SUCCESSFUL in 1s 1 actionable task: 1 executed
フォーマットをテストする
$ ./gradlew verifyGoogleJavaFormat > Task :verifyGoogleJavaFormat FAILED The following files are not formatted properly: /home/blacky/projects/java/sentry-config/src/main/java/com/github/nomadblacky/sentry/config/DefaultTypesafeConfigSentryClientFactory.java FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':verifyGoogleJavaFormat'. > Problems: formatting style violations * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 1s 1 actionable task: 1 executed
デフォルトで check
のタスクにも verifyGoogleJavaFormat
が含まれる
$ ./gradlew check --dry-run :compileJava SKIPPED :processResources SKIPPED :classes SKIPPED :compileTestJava SKIPPED :processTestResources SKIPPED :testClasses SKIPPED :test SKIPPED :verifyGoogleJavaFormat SKIPPED :check SKIPPED BUILD SUCCESSFUL in 0s
Gitのpre-commitフックを仕込む
pre-commit を設定しておくとフォーマット忘れを防げる。
.git/hooks/pre-commit
#!/bin/bash set -o pipefail ./gradlew googleJavaFormat | grep "formatted successfully" > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "\nJava source code(s) are formatted. Please add files agein." exit 1 fi exit 0
set -o pipefail
しておかないとgrepにかからなかったときに終了コード1を返してくれない。`
$ git commit --allow-empty <-------------> 0% INITIALIZING [0s] <-------------> 0% EXECUTING [0s]> :googleJavaFormat<-------------> 0% EXECUTING [0s]> :googleJavaFormat<-------------> 0% EXECUTING [0s]> :googleJavaFormat<-------------> 0% EXECUTING [0s]> :googleJavaFormat<-------------> 0% WAITING Java source code(s) are formatted. Please add files agein.
(ちょっとGradleの出力が変だけど、フォーマットがかかった場合にコミットが中断されるのが分かる)
おまけ: [IntelliJ IDEA] google-java-format プラグイン
Reformat Code
(Ctrl+Alt+L) でフォーマットがかかるようになる。
ファイルの保存時にフォーマットする設定は無いみたい…
また、上記のGradleプラグインとかなりフォーマットの挙動が異なるので、結局はGradleのタスク叩くことになる…
環境毎にpre-commitフックを仕込む必要がある点がちょっと面倒かも。
ほかにいい方法あれば教えてください🙇
「ロギングにおける十戒」を読んで
たまたま検索に引っかかった記事が面白そうだったので読んでみた感想文。
1. 自分でログを書くべからず
printfを使ったり、自分でファイルにログエントリを書き込んだり、あるいはログローテートを手動でやってはいけない。オペレータにお願いして、一般的なライブラリを使うか、システムAPIコールを使ってやろう。
自分でログ機構を実装しないでエコシステムに乗りましょうという話。 最近はJVMアプリのロギング周りを追っているけど、確かにslf4jのインターフェイスと実装を分ける設計は合理的に感じる。
いま関わっているプロジェクトでも自作ロガーが使われており、エコシステムに乗れなくて辛い思いをしている…
2. 適切なレベルでログを出力すべし
当たり前のようで意外と守れていないことが多い気がしている。
最近自分が見たのは、バッチ処理の開始・終了をWARNレベルで出力しているところ。
これはログレベルが間違っているのは明白であるが…
NOTICE レベル : 本番環境でプログラムを動かす時のレベルだ。エラーには分類されないが記録すべき全てをログに残す。
ここでの説明に従うなら、NOTICEレベルを使うのだろうけど、slf4jにはNOTICEレベルはないので、どう表現するのが正しいのだろうか?
Markerをうまく使って表現するのがいい?
3. 汝のログカテゴリを尊重せよ
多くの場合、Java開発者はログの文がカテゴリ分けされたように見えるようFQDNを使う。これは、プログラムがシンプルな責任原則を大切にしている場合に特に有効なやり方だ。
なんとなくクラスのFQDNを渡していたけど、あれはカテゴリ分けの意図があるのかと気づきを得られた。
そういえばLogbackもパッケージ階層ごとに振る舞いを変えられるような設計になっていたなと。
(前述の自作ロガー、ロガー名をひとつに潰すような実装になってたので、そこは直さなきゃいけないなと感じた…)
4. 意味のあるログを書くべし
これは本当に大事だと自分も思う。
このログを読んだ人はどう思うのか…という気遣いが大切なのかも。
また、前のメッセージの内容に依存したメッセージを表示してもいけない。前のメッセージが、違うカテゴリやレベル、あるいはマルチスレッドのためや非同期処理のために最悪違う場所(前)に記録されることもありうるからだ。
このあたりはMDCあたりが助けてくれそうな気がする。
5. ログは英語で書くべし
「読み手は日本人だし、ログは日本語でいいでしょう」と思っていた自分からすると、これには少し驚きがあった。
英語は、メッセージをASCII文字でロギングすできる。ログメッセージがどう扱われるか本当の所は分からないし、どこかにメッセージがアーカイブされる前にどんなソフトウェアレイヤやメディアを経由するかも分からないので、これは特に重要だ。
言いたいことはわかるものの、emojiの流行でマルチバイト文字に強くなってきた(?)昨今ではあまり気にならない気がしているけどどうなんだろう…。
ログの文(例えばWARNとERRORレベル全部など)をローカライズするなら、以下のような特定の意味のあるエラーコードを接頭辞としてつけることを忘れないようにしよう。これにより、ユーザは言語に関係なくインターネットで検索して、情報を見つけることができる。
何かしらのサービスでREST APIを使う時によく見る方法だけど、確かにこれは良さそうだ。社内アプリケーションなどでも役立ちそう。
6. ログは背景を持つべし
正しい背景なしには、これらのメッセージは雑音でしかない。これらは、何の価値も持たず、トラブルシューティングに有用なスペースを消費してしまう。
4と似通った話? ごもっともである。
背景を保持する簡単な方法は、Javaのロギングライブラリ実装であるMDCを使うことだ。
最近、Sentryを利用するところでMDCを知ったけど、引用通りに便利だなと感じている。
その一方で非同期処理が多くなるScalaだと取り回しが難しい…
kamon-logbackがそのひとつの解決策だと思っているけど、Kamon自体ちゃんと触れてないので追々やっていきたい…
7. ログはコンピュータがパースできるフォーマットで出力すべし
ログエントリは人間が読むには非常によいが、機械に処理するのはとても不得意なものだ。手動でログファイルを読むには不十分な時もあるだろうし、自動処理(たとえばアラート通知や監査など)が必要なこともあるだろう。
ログにJSONを組み込むあたりはログ解析をちゃんとやろうとしたときに必須になりそうな気がする。
ちゃんとしたログ解析をやったことないので何とも言えないけど…
8. ログは長すぎず短すぎないものにすべし
多すぎるログは、そこから十分な価値を得るのが本当に難しくなってしまう。そういったログを手動で追う時、午前3時に本番環境での出来事をトラブルシュートするような場合に無駄な情報が多すぎるのはよいことではない。
このあたり、最近は自分もよく悩んでいる気がする…。
ついつい色んなデータを取りにいきたいと思ってしまう。
また、ログ出力したいデータを取得するのにビジネスロジックが圧迫されてしまうのもいま個人的に感じている課題だ。
これはたぶん、Sentryにログを出力するときに一度にまとめて情報を送りつけようとしているせいかもしれない。
Sentryだけで原因がわかるのが一番ではあるがそれも難しい。
ログの持つデータは適切な場所で吐いてやるのが正しいのかなと感じた。
9. ログの読み手のことを考えるべし
・自分自身のためにトラブルシュートするエンドユーザ(クライアントあるいはデスクトップ用プログラムを想像しよう) ・本番環境での問題をトラブルシュートする、システム管理者あるいはオペレーションエンジニア ・開発中のデバッグ、あるいは本番環境での問題解決をする開発者
「誰が」ログを読むのか、という点はつい忘れがちなので気をつけたい…
10. トラブルシューティングのためだけのログにするべからず
監査 : ビジネス上の要求から必要なことがある。マネジメント上、あるいは法律家が扱うような重要なイベントを補足しておく意味がある。そのシステムのユーザが何をしているかを記述する文が必要だ(例えば、誰がログインしたか、誰が編集したか、等々)。
プロファイリング : ログにはタイムスタンプ(時にそれはミリ秒単位のレベルで)が付けられており、プログラムの位置をプロファイリングする便利なツールになり得る。例えば、オペレーションの始まりと終わりにログを仕掛ければ、プログラム自身に仕組みを備えなくても、(ログをパースすることで)パフォーマンスを推測することもできる。
統計 : 特定のイベント(ある種のエラーなど)が発生するたびにログを記録するようにしておけば、実行中のプログラム(あるいはユーザの行動)の興味深い統計情報が取れるだろう。これは、連続した大量のエラーを検知してアラートを発報するような使い方も可能だ。
ここまで辿り着くには上記の戒めを守るのが必須であり、長い道のりになるんだろうなと感じる…。
(今関わっているプロジェクトではトラブルシューティングに使えるログを吐けてるかも怪しい…)
ロギングの世界はとても奥が深いなぁと思わせてくれる読み応えのある記事だった。
つい影に隠れがちな技術だけど、プロファイリングやAPMなどより、一番手軽な「可視化」の手段なんだなと思い直した。
自分達の行いを省みさせてくれるツールとして、もっともっとロギングのいろはを学んでいきたい。
typesafe-configでSentryクライアントの設定をできる sentry-config をリリースしました
そもそもSentryってなに
アプリケーションの例外などのログを溜め込み、集計・可視化を行えるSaaSです。
詳細は割愛。
モチベーション
Sentryのクライアント実装こと、 sentry-java は環境変数とシステムプロパティでの設定方法を提供しています。
しかしながら、Scalaエンジニアを生業としている自分からすると、アプリケーションの設定は application.conf こと、typesafe-config が使われていることがほとんどです。
環境変数・システムプロパティ・typesafe-configと、設定が分散してしまい管理が大変だなと感じていました。
そこで、type-safeconfigからSentryの設定を読み込めればいいじゃん!と思い、ライブラリの作成に手を付けました。
使い方
ライブラリの依存を追加
執筆時点では 0.3.0
が最新になります
compile 'com.github.nomadblacky:sentry-config:0.3.0'
Sentryの設定を追加
sentry.properties
ファイルに以下を追加して、
factory=com.github.nomadblacky.sentry.config.SentryClientFactory
application.conf
に設定を書きます。
sentry { dsn = "DSN" environment = debug }
利用する
あとは普通にクライアントを利用するだけです。
import io.sentry.Sentry; import io.sentry.event.Event; import io.sentry.event.EventBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; public class App { private static void manualUsage() { EventBuilder eventBuilder = new EventBuilder() .withMessage("This is a test!") .withLevel(Event.Level.INFO); Sentry.capture(eventBuilder); } private static void withLogback() { Logger logger = LoggerFactory.getLogger(App.class); try (MDC.MDCCloseable closeable = MDC.putCloseable("sampleMdcTag", "Hey!")) { logger.warn("WARN from Logback!"); } } private static void uncaughtException() { throw new RuntimeException("This is an uncaught exception!"); } public static void main(String[] args) { manualUsage(); withLogback(); uncaughtException(); } }
課題
- 設定項目が網羅されてない・中途半端に穴抜けしている
- もくもく開発するしかない
- 一部の設定値の適用にシステムプロパティの上書きを行っている
- これはコードをちゃんと読んでなかった。直せそう。
- そもそもapplication.confに設定を逃しているのに、sentry.propertyなどでファクトリの設定が必要なのが微妙
- どうしよう
- コードがきたない・びみょう
- 久しぶりにJava書いたのでゆるして…
初めてMaven Centralにライブラリを公開したのですが、自分の子供のようでちょっと感慨深いものがあります。
ライブラリそのものはまだまだ未熟ですが、少しずつ着実に育てていければと思っています。
Sentryめも
最近Sentryを使い始めたのでメモ。雑。
DSN(Data Source Name) の設定 (JVM)
ログの吐き出し先。
JVMプロパティなどで設定する
java -Dsentry.dsn=https://public:private@host:port/1 -jar app.jar
Logbackの設定
build.sbt
libraryDependencies += "io.sentry" % "sentry-logback" % "1.7.5"
<appender name="Sentry" class="io.sentry.logback.SentryAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>WARN</level> </filter> </appender> <root> <appender-ref ref="Sentry" /> </root>
Auto Resolve
イベントが発行されてから設定時間ぶん経つと自動的にResolveされる機能らしい
Deploy Tracking
デプロイを記録することで、そのリリースでどんなIssueが上がったのかをサマったりしてくれるっぽい。
社で使っているGitLabは未対応だった…
雑記
- 社で外付けディスプレイを一枚増やした。
- 常にDatadogの画面を開いているのだけどとてもいい感じ。ワクワクしてくる。
- 最近はいわゆるSRE業のモチベーションが高い。
- Datadogやっていくぞ
【転職しました】転職を検討しております
(無事、転職いたしました)
Table of Contents
転職を検討しています。
— 黒鍵 (@blac_k_ey) 2017年11月14日
Scala、もしくはデータ分析系に関われるお仕事がありましたら、DM頂けると嬉しいです。
この度は転職を検討しております。
Scala、もしくはデータ分析に関われる仕事を希望しております。
略歴
- 千葉県在住
- 1993年6月生まれ(24歳)
- 商業高校 情報系学科卒業
- 専門学校(3年制)中退
- 1社目
- およそ2年半勤務
- ソーシャルゲームの開発・運用を経験
- Javaを用いたWebアプリケーションで、いわゆる内製フレームワークでした。
- テストを書いていない、運用はほとんど手作業という環境から、様々な自動化を行いました。
- Gradleを導入し、下記の自動化の足がかりとしました。
- Jenkinsでテスト・静的解析・デプロイを自動化
- 毎月行っているイベントの設定ファイルを自動生成し、手作業の時間を削減
- 手作業での設定が必要になる部分はなるべくテストし、不備があった場合はSlackで通知
- …など、不具合は早期に発見できるよう努めました。
- レガシーコード改善ガイド に書いてあることが目の前にあって、この本には非常にお世話になりました。
- この本に出会わなければテストを書かない人間になっていたかもしれないし、技術的負債に立ち向かう力を得られなかったかもしれません…
- KPI測定のためにBIツール(Qlik Sense)を導入しました。
- この頃から、データを収集し、理解できる形にまとめるというプロセスに魅力を感じ始めたと思います。
- 転職理由
- 開発はほとんどひとりで行っていた。
- 自分のやっていることが正しいのか誰も証明できない。
- 同じフィールドに立って開発する仲間が欲しかった。
- レガシーな環境をある程度改善してきたものの、ひとりで行うには限界があった。
- (そのために人を当ててくれそうになかった)
- 技術に対して敏感なエンジニアが居なかった。
- 勉強会の開催もほとんどない、技術に対する会話もほとんどない。
- 開発はほとんどひとりで行っていた。
- 2社目
- 現在3ヶ月目
- 転職(を検討している)理由
- WIP
主なスキル
- Java
- 学生時代を含め、およそ6年ほど経験しています。
- 主に業務ではWebアプリケーションの構築を経験しました。
- Scala
- 1年前から触り始め、その表現力の高さに魅力を感じています。
- Scalaの基礎となる部分はおおよそ理解しているつもりです。
- Ruby
- 学生時代に触り始め、今ではテキストを整形するといった、ちょっとリッチなシェルスクリプトという位置づけで使っています。
- Linux
- 学生時代に触り始め、環境構築の容易さから、趣味の端末ではUbuntuを利用しています。
これから学びたい
- Scala
- ライブラリ、フレームワークを駆使して、より発展的なアプリケーション開発ができることを目指します。
- データ分析基盤
- データ収集、可視化などに必要なインフラを設計・構築できるようになることを目指します。
- R(および統計学)
- R言語を学ぶとともに、統計学に関する知識を身につけ、効果的なデータ分析ができるようになることを目指します。
- 現在、Rによるやさしい統計学 を読み進めております。
- 将来、機械学習及びディープラーニングの学習につながればと思っております。
- R言語を学ぶとともに、統計学に関する知識を身につけ、効果的なデータ分析ができるようになることを目指します。
最近作ったものなど
基本的に GitHub でソースコードを公開しておりますのでご覧頂ければと思います。
- scalasamples
- Scalaのサンプルコード集。
- 新しい言語を学ぶ上で、テストコードにサンプルコードを書くということをよくやっています。
- テスト数だけ自分が学んできたという指標になるので、モチベーションの維持がしやすいです。
- 現在、250以上のサンプルコードがあります。
- otogame-updater
- ansible-playbooks
以上を踏まえて希望するお仕事
- 未経験ではありますが、データサイエンスおよび、その周辺技術に興味があり、業務を通して力をつけていきたいと考えております。
- Scalaを採用していることが望ましいです。
- 近い年代のエンジニア(±2歳ほど)と一緒に仕事ができることが望ましいです。
- 技術的負債を良くないと感じ、立ち向かっていける環境が望ましいです。
以上、こんな未熟者ではございますが、お仕事あるよという企業様がございましたら、Twitter にてDM頂けますと幸いです。
【ぽえむ】今週やったこと(10/30~11/5)
Table of Contents
ScalaとRのアドベントカレンダーに応募してしまった。
文章を書くのが苦手なのはこのブログでもTwitterでも見て取れると思う。 (大学を出ていないので、こういうのに不慣れなのは尚更である) とはいえ、いつまでも苦手なままではいかないなーと…。
何かこう、書きやすいお題がないかなーと考えていたところ、 「この一週間で(プログラムを)書いたこと、やったこと」を書くのがいいのではないか と、思い立ったところで筆を執った次第である。
というわけで、以下ポエム
読書
データサイエンティスト要請読本 登竜門編
データサイエンティスト養成読本 登竜門編 (Software Design plus)
- 作者: 高橋淳一,野村嗣,西村隆宏,水上ひろき,林田賢二,森清貴,越水直人,露崎博之,早川敦士,牧允皓,黒柳敬一
- 出版社/メーカー: 技術評論社
- 発売日: 2017/03/25
- メディア: 大型本
- この商品を含むブログを見る
読もう読もうと積んでいたので、一気に4章まで読んだ。
とはいえ、コマンドライン操作や基本的なデータベース知識など、
ある程度知見がある内容が多かったので時間はかからなかった。
5章からはRStudioやJupiterが絡んでくるので読むのが楽しみ。
Trailhead
お仕事柄Salesforceの勉強をしている。
今は1日にChallenge(問題)を3つ解くというペースでやっている。
基本的に通勤電車の時間で手を付けているので、ハンズオン系のモジュールはあまり手付かず…
とりあえず年内にレンジャー(バッジ100個)は行けるのではないかなぁという見込み…
fish-shell
`hub` コマンドの保管を入れた
https://github.com/NomadBlacky/fish-config/commit/7acd597f2289bf999d6e93fa274baf3247896876
fishはこういった保管を手軽に定義できるのがいい…
(自分で書いたことあるとは言ってない)
SalesforceDXの保管とか多分ないよね…?
`sfdx force:doc:commands:list` からコマンドの一覧は取ってこれるので、ちょっと頑張ればできるんじゃないかなぁ…
Emacs
faceの設定をいじるなど
https://github.com/NomadBlacky/.emacs.d/commit/d7936f708d9ebda6423f2fb5b319e15b99310f4f
見た目の設定とかいちいち設定ファイル書くのめんどくさいなーとか思ってたのだけど、
`customize-face` でインタラクティブに設定できることを最近知ってツエーってなった。
org-modeを使い始めた
先日、 Emacs実践入門 出版記念イベント に参加して、org-modeの最強たる故を肌で感じた…
というわけで、この文章もorg-modeで書いている。
markdownへの変換も `org-md-export-to-markdown` で一発。
ついでに目次もつけてくれちゃう…
org-mode道の第一歩を踏み出した。
Scala
enum的なものをScalaで使う方法を学んだ
現状、Scalaはenumをサポートしていないので、自分で実装する必要がある。
Enumerationを使う方法は簡潔だけど、Javaのenumのような値や振る舞いを持たせるのに不便…
パターンマッチでも網羅性を担保できない。
基本的には sealed abstract class を使う方法が良さそう。
冗長になるけど仕方ないか…
otogame-updater
某音楽ゲームのレーティングやスコアの更新をチェックしてSNSに投げるーってやつ。
ServerlessFrameworkにScalaサポートがあったので、サーバレスやってみたーという感じ。
scala-scraperでスクレイピングしてDynamoDBに溜め込んで、更新があればTwitterに投げる。
今週は上記のEnumeration周りでハマったり、
関数を切る単位を間違えたせいで、えらくデカくなってしまった関数をリファクタリングしたりなどした。
やはり関数をより小さく保つのは改めて大事だなーと。
しばらく悩んで放置気味だったのですっきり。
とりあえず、ドメインとデータ取得のロジックはひととおり出来た感じ。
あとは、Lambdaのハンドラを実装すれば最低限求めてたものは完成…とおもったが、
楽曲の情報をユーザーIDと楽曲IDをキーにしたいと思ったら、DynamoDBじゃ微妙なんじゃないかなーと…さあどうしよう。
DynamoDBのStreamで更新の通知楽勝じゃーんとか思ってたけど甘かった。
そもそもNoSQLの使い方をまともに知らずに使った結果でした。
設計正しい知識を持ってちゃんとやろうね。趣味だから許される。