APIのE2Eは `カラテ` を使うべし。古事記にもそう書いてある。
<これまでのあらすじ>
API開発を終えたサラリマンエンジニアであるタロウは、しめやかにテストを開始した。しかし、そこに待ち受けたのはジェーソン(訳注: JSONのことだと思われる)の複雑怪奇極まりない組み合わせだった。納期を守らないエンジニアは実際シツレイにあたりセプクものだ。このままではジリー・プアー(訳注:徐々に不利)であるタロウの前に恩師であるカラテ・マスターが現れる。
TL;DR
- APIのユニットテスト、E2Eテストには Karateが便利。
- 使い方はクセがあるけど、使えば使うほどよく馴染んでくる。
- <>で囲まれたタイトル内は茶番なので無視していいよ。
- この記事は ただの集団 Advent Calendar 2020 - Adventarの18日目の記事です。
プログラミングする上で避けるべき命名パターン
この記事は ただの集団 AdventCalendar PtW.2019 (https://gw-advent.9wick.com/calendars/21) の4日目の記事です。
前日は erika 様の AWS GlueでETL - Qiita でした。
はじめに
きれいな変数名、クラス名、メソッド名つけれていますか?
出来ていない理由の大半に、
- すでにある名前に合わせている
- 複雑なことをしていて、適切に名前を考えられない
などがあると思いますが、ここではそういった構造や歴史的経緯などは取り扱いません。
単純に見かけた瞬間、「やばい?」という感覚を持ってもらいたいだけです。
ここで言いたいことは、 以下のようなパターンの名前が出てきたら大抵の場合、何かがおかしい可能性があるのでコードを見直しませんか? という問題提起です。
ヒヤリハットだったり、気付きの一助になれば幸いです。
プログラミング言語として、Webアプリケーションでよく使われるような言語を対象にしています。
あと、適当にパターンとかつけていますが特に分類したわけではないし、個人的な考えにすぎないのであまりツッコミしないでください。
略語パターン
標準(だと書いた人が思っている)略語パターン
例
- tmp
- ret
- fmt
- frm
- cd
- cvt
- cmn
やたらと3文字に納めようとしているパターンです。
おそらく書いた人は標準で、よく使われていると思っています。
しかし、残念ながら通じていないことのほうが多いので気をつけたほうが良いです。
こういった単語が出てきた場合、 x, y, z や m, n などに置き換えてもコードが読めるかどうか考えてみましょう。
通じない場合、この命名自体が間違っています。
通じる場合、そもそも意味を持っていないので、 a, b, c などで置き換えてもとの単語を考えさせないようにしましょう。
母音省略パターン
例
- kbn
- kss
- bbr
- nyknb
- 例はいくらでもあるので省略
もはや、なんのことか一見しただけでは意味不明なパターンです。
DBのカラム名などでよく発見されます。
8文字以内に収めようとして母音の一部だけ省略している亜種がある場合もあります。
いかなる場合においても許されない(と自分は考えています。異論は認めます)
たとえファイルサイズやメモリサイズを削りたい場合においても、意味をなしていないので省略したいなあらいっその事全く意味をなさない文字の方がまだマシです。
何らかの意味を持っているのではないか?業界特有の略語があるのではないか?など余計な考えを発生させてしまうことが良くないと思います。
数字Suffixパターン
- xxxx1
- xxxx2
など後ろに数字がついており、かつ数字以外の名前が一致しているパターンです。
メソッド内の変数名の場合、多分順次処理を一時変数に置き換えているのでしょうが、時々使う順番を変えていたりするので読む側に混乱を招きます。
メソッド名そのものの場合、もはや使い分け方法は他人には理解不能になります。
オーバーロードのように、同名のメソッドを型で区別する方法があるのでそういった手法を取る、位なことをしましょう。
そもそも処理ごと違っており同名をつけるべきでない場合もあるので、 気をつけたいパターンです。
文章パターン
- fooAndBarAndBazAnd...
- doHogeThenDoFugaAnd...
やたらと長い変数名やメソッド名がある場合、よく読むと文章や複数の小節から構成されているときがあります(例外としてテスココード中の変数名・メソッド名は除きます)
責務を適切に分離できていない場合、こういったパターンが見受けられます。
このパターンは上記までのパターンに比べ、リファクタリングのチャンスだ、というポジティブに捉えることができます。
And, Or, Then, などが変数名・メソッド名に含まれているのがよくある特徴です。
おそらくメソッドが長い、特定処理に対して適切なドメインを導き出せていない、など改善できそうな事が多いので、積極的に見つけた箇所のリファクタリングを推進すると良いと考えます。
その際に、 コードの臭い - Wikipedia を探すのが良い対処方法になるかもしれません。
英語がわかんないよパターン
動詞と名詞がおかしいパターン
- クラス名に
CreateUser
- メソッド名に
UserCreate
日本人だからね・・・仕方ないね・・・
で、終わればよいのですがおそらく周りのコードに流されて惰性で何も考えずにつけられている事が多いです。
この場合、適切にクラスやメソッドが作られているとは言えないことが多いです。
名前そのものがヤバイ、というよりこの名前が残ってしまっているとおそらくコードにも何らかの問題があると推測される方がヤバイです。
ハイブリッドパターン
- createRiyousha
- sakujoUser
みたいなパターンです。
日本人だからね・・・(ry
では済まないことが多いです。
こちらの場合、動詞や名詞がおかしいパターンよりも深刻な場合があります。
おそらく、他のクラスや他のパッケージに同じ意味を示すような変数名が複数存在している可能性が高いです。
ユビキタス言語がなく、開発者が好き勝手に名前を考えてつけたけど、命名方法は個人の裁量に任された結果発生している可能性が高いです。
今すぐにでも開発者全員集めてドメインモデルをみんなで考えて見ることをおすすめします。
英語で統一されていてもユビキタス言語がない場合もありますが、これは別の問題なので今回取り扱いません。
コメントで言い訳しているパターン
// 本当は○○だが、その後△△△する必要があるため、一旦□□する val savedFileTemp1 = ...
はい。
時間がなく、ひどいコードやひどい名前であることを実装者は知っています。
これを見た人がマネージャーなりリーダーなら、今すぐリファクタリングのタスクを作り本人に是正することをお願いしましょう。同時に「無理させてゴメンね」と優しく声をかけましょう。
マネージャーなのか、リーダーなのか、はたまたプロダクトマネージャーなのかわかりませんが、無理のあるスケジュールや他人が決めた納期という謎の概念によって書いた人は苦渋の選択でこういったコメントを残しています。
変数名とは離れましたが、ひどい変数名になっていたとしてもこういったコードになった原因は違うところにある場合が多いです。
避け方としては、 常にリファクタリングしたりコードを見直す時間も考えていくしかありません。
まとめ
つらつらと書きましたが、こういったパターンに陥らないためには
- プログラム上で使う名前を考える時間は、コードを書く時間の7割を注ぎ込んでも良い
と思うくらいには大事です。
一番時間をかけても良いと思います。
うまい変数名、メソッド名が考えられない、ということは責務の分離が必要だったり、コードそのものや設計そのものを見直すきっかけになります。
納期が・・・という言葉に惑わされない強い心と、ときには組織の仕組みの改善が必要になったりしますが、それはまたの機会に・・・
プログラム上で時間を扱う際に気をつけること
この記事はただの集団 Advent Calendar 2018の22日目の記事です。昨日は wararaki (Kyohei Watarai) · GitHub さんの 玉子焼きとだし巻き玉子を分類してみた - Qiita でした。
結論
結論から先に示しておきます。
- 日付・時刻を扱う際は、目的に合わせて使いたいクラスを適切に選択する必要がある。
- 日付・時刻に関わらず、クラスの選択や設計・実装は目的に沿って行う必要がある。
背景 - 時間を扱う際に発生する問題
あと3日でクリスマスですね。(記事の投稿日は2018年12月22日09:00)
こういった会話は日常的によく行われていると思います。では、これをプログラム上のコードで表現してみましょう。
(※サンプルコードは全てScalaのREPLで記述します。)
scala> import java.time.{Duration, LocalDateTime} import java.time.{Duration, LocalDateTime} scala> val today = LocalDateTime.of(2018, 12, 22, 9, 0, 0) today: java.time.LocalDateTime = 2018-12-22T09:00 scala> val christmasDay = today.plusDays(3) christmasDay: java.time.LocalDateTime = 2018-12-25T09:00
一見良さそうに見えます。でもちょっと待ってください。
アプリケーションを開発する上では、コードは厳密に表現していかないと思わぬバグを生みます。
クリスマス という日が始まるのは 12月25日の00時 からではないでしょうか? コード上ではクリスマスはすでに始まっており、既に9時間も経過しています。
これでは、コードでクリスマスと言う日を正しく表現したことにはならない可能性があります。
では、それを考慮し あと3日 を あと2日と15時間 と表現するべきではないでしょうか?会話の文章を変えてみましょう。
後2日と15時間経過するとクリスマスと呼ばれる日が始まりますね。 (記事の投稿日は2018年12月22日09:00)
これをコードで表現してみます。
scala> import java.time.{Duration, LocalDateTime} import java.time.{Duration, LocalDateTime} scala> val today = LocalDateTime.of(2018, 12, 22, 9, 0, 0) today: java.time.LocalDateTime = 2018-12-22T09:00 scala> val christmasDay = today.plusDays(2).plusHours(15) christmasDay: java.time.LocalDateTime = 2018-12-25T00:00
コードは確かに正確になりました。
しかし、今度は別の問題が発生します。日常会話で 後2日と15時間でクリスマス
とか普通は言いません。ならば、コードの方に問題があるのではないでしょうか?
このあたりの勘所が強い方達は最初のコードの時点でお気づきになられたと思いますが、LocalDateTime
-> LocalDate
にしてみます。
scala> import java.time.{Duration, LocalDate} import java.time.{Duration, LocalDate} scala> val today = LocalDate.of(2018, 12, 22) today: java.time.LocalDate = 2018-12-22 scala> val christmasDay = today.plusDays(3) christmasDay: java.time.LocalDate = 2018-12-25
良さそうですね。つまり、最初の文章は正確には
今日(2018-12-22)から後3日経過(+3 day)するとクリスマスと呼ばれる日(2018-12-25)になる
と表現していました。コードでも無理なく表現できています。
しかし、往々にして後ろに余計な情報 2018年12月22日09:00
などがあるとそちらに目がくらんで時間も扱うクラスでコードを書いてしまったり、書かれたコードを見たことがあるのではないでしょうか?
また、 良さそう と書いたのはここにタイムゾーンという情報が増えると、 日本ではクリスマスだがアメリカはクリスマスではない 、といった状況が発生しプログラムで予期せぬバグを生むこともあるからです。(※LocalDate,LocalDateTimeではTimezoneを扱えません)
ということで、今回はプログラミングをする際、時間を扱う時にどういった観点が必要か、どういった点に気をつけなければいけないかを考えていきたいと思います。
続きを読むDataDog 用の jolokia AgentCheck書いてみた
記事名のとおりなんですが、AgentCheck用のインストール方法とか特に無くてファイルを置くだけっぽいので、 gist にあげておきます。コードが酷かったり、項目増やせなかったりするのは今後の課題。
モチベーションとか
そもそもの話、JVMの監視をするときには極々単純に以下の項目を監視したいわけです。
また、メモリの状態と言ってもJVMって Heap/NonHeap みたいな単純なものではありません。EdenとかSurvivorとかOldとかがあります。 (※G1GCではちょっと違う)
GCも、 Java8 のデフォルトでは ParallelGC Java9ではG1GCだったりで、見なければいけない項目も少しずつ違います。
JVMのメモリについては、詳しくはこちらの方のブログが分かりやすかったので、是非参照してください。
それなのに、監視サービスの某鯖とか某犬とかのJVM監視の項目を見ると、全然足りないんですよ。
鯖の方は、jstatで監視するなら十二分に満たしているんですが。
mackerel-agent-plugins/mackerel-plugin-jvm at master · mackerelio/mackerel-agent-plugins · GitHub
Jolokia(JMX)で監視する際には、鯖の方も項目を増やすプラグインを書いてみたりとかしています。
本題に戻って、DataDogのJmx監視のためのプロセスなんですが、JolokiaではなくてJmxFetchといって、JMX取得のためのJavaプロセスを内部で動かす方式となっています。
これが、dd-agentをDockerで動かして、AutoDiscoverを前提にすると Xmx512M
固定で起動します。公式のDockerのメモリ推奨量が256MBなのに。
https://github.com/DataDog/dd-agent/blob/5cad70dd33b58716ed102070c95edbd557bf3816/jmxfetch.py#L48
_JVM_DEFAULT_SD_MAX_MEMORY_ALLOCATION = " -Xmx512m"
コード見つつ動かしながら挙動調べた感じ、AutoDiscoverじゃなくて監視用のファイルを置いた場合にはプロセスのメモリ使用量を変更できそうでしたが、それだとAWSのECSとかでなんのタスクが動くかわからないものに対しては使えそうにありません。
そもそも、監視のプロセスだけで512MBも必要とか尋常じゃないです。AWSのECS上のTaskとして動かした場合、t2.small なんて1GBしかないんだから、残りの使えるメモリがもう半分以下しかないです。
サポートとかに聞いてもAutoDiscoverとかDocker上のdd-agentとかあんまり考慮されていなさそうだったのと、Docker起動時に外部の環境変数から変更できそうな感じじゃなかったので、Jolokiaで監視すればいいんじゃないかなと思い立った次第。
で、探したけどJolokiaようのAgentCheckスクリプトが見つからなかったので作ったのが冒頭のスクリプトになります。
使い方とか
dd-agentをDockerで動かしているので、Dockerfile置いておきます。
サーバー上で普通に動かしているのであれば、 check.d
ディレクトリにファイルを置くだけでいいはずです。
FROM datadog/docker-dd-agent:latest ADD jolokia.py /opt/datadog-agent/agent/checks.d/jolokia.py
buildして、
docker build -t dd-agent-jolokia .
dd-agentを起動(MacOSの場合。cgroupを通して監視するので、詳細は以下のリンクを参照)
docker run -d \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /proc:/host/proc:ro \ -v /sys/fs/cgroup:/host/sys/fs/cgroup:ro \ -e API_KEY=<API_KEY> \ -e SD_BACKEND=docker \ -e DD_LOGS_STDOUT=yes \ dd-agent-jolokia
https://hub.docker.com/r/datadog/docker-dd-agent/
監視対象のDockerはラベルを付けて起動 JavaagentでJolokiaを指定してポートを開けるようにすること。
docker run -d \ -p 13333 \ -l com.datadoghq.ad.check_names='["jolokia"]' \ -l com.datadoghq.ad.init_configs='[{}]' \ -l com.datadoghq.ad.instances='[{"host": "%%host%%", "port": "%%port%%" }]' \ jvm_application -javaagent /paht/to/jolokia-jvm-agent.jar,port=13333,host=0.0.0.0
起動したDockerContainerの監視はLabelをつければ自動で監視対象に追加してくれる仕組み。このあたり、DataDogの仕組みは素晴らしいですね。
https://docs.datadoghq.com/guides/autodiscovery/docs.datadoghq.com
ParallelGCの場合、監視項目はこれだけ増えます。 MemoryPoolから動的に項目取得してくるので、GCの種別を変えれば項目も変わります。
jolokia.class_load.loaded jolokia.class_load.total jolokia.class_load.unloaded jolokia.gc.count jolokia.gc.time jolokia.memory.code_cache.committed jolokia.memory.code_cache.init jolokia.memory.code_cache.max jolokia.memory.code_cache.used jolokia.memory.compressed_class_space.committed jolokia.memory.compressed_class_space.init jolokia.memory.compressed_class_space.max jolokia.memory.compressed_class_space.used jolokia.memory.heap.committed jolokia.memory.heap.init jolokia.memory.heap.max jolokia.memory.heap.used jolokia.memory.metaspace.committed jolokia.memory.metaspace.init jolokia.memory.metaspace.max jolokia.memory.metaspace.used jolokia.memory.non_heap.committed jolokia.memory.non_heap.init jolokia.memory.non_heap.max jolokia.memory.non_heap.used jolokia.memory.ps_eden_space.committed jolokia.memory.ps_eden_space.init jolokia.memory.ps_eden_space.max jolokia.memory.ps_eden_space.used jolokia.memory.ps_old_gen.committed jolokia.memory.ps_old_gen.init jolokia.memory.ps_old_gen.max jolokia.memory.ps_old_gen.used jolokia.memory.ps_survivor_space.committed jolokia.memory.ps_survivor_space.init jolokia.memory.ps_survivor_space.max jolokia.memory.ps_survivor_space.used jolokia.thread.count
雑感
勢い余って書いてしまったけど、本当に他に困っている人いないのかどうか・・・ みんなそこまでJVM内の項目気にしていないのかなぁ。 あと、いくらなんでも雑に書きすぎたかもしれない。
KinesisStreamにKPLで入れたデータをKinesisFirehoseを通し、Lambdaで加工してからS3に保存してみたり
つい先月、待ちに待った KinesisFirehoseがついに東京リージョン (ap-norhteast-1) に来ました。
なので、今までKinesisStreamのデータをS3に保存する処理をLambdaで直接取得して定期的に実行していたやつを、Firehose経由で実行するようにしてみました。
構成とか
- fluentd-kinesis-pluginでKPLを利用してKinesisStreamsに送信しています。
- KinesisFirehoseのSourceにKinesisStreamを設定し、Lambdaで加工してからS3に保存します。
- Athena でテーブルを作成し、保存されたデータをSQLで参照できるようにしています。
(※KinesisStreamsを利用している理由は、他にもAnalyticsやKCLを使ったものを設定したい為です)
KinesisFirehoseの設定方法とか
KinesisStreamsはすでに存在する前提 Lambdaも予め作っておいたほうがよいです。(Lambdaについては後述します) (※コンソールが英語で申し訳ありません)
- KinesisFirehose Consoleに行きます。
- 新規Streamを作成します(FIrehoseだけど Create Delivery Streamです)
- 名前を適当に決め、Sourceに
Kinesis stream
を選択し 元データとなるKinesisStreamを選択します。 Record transformation
をEnabled
にし、変換要のLambda Functionを選択します。(ここで作ることも出来ますが、予め作っておいたほうがよいと思います)Amazon S3
を選択して送信先のS3バケットやPrefixを選択します。Prefixの後ろには自動でyyyy/MM/dd/HH/
が付与されます。/kinesis-firehose/
のように/
(スラッシュ) をつけておくといいと思います。S3バックアップは加工前のレコードをS3に保存するかどうかです。利用状況に応じて選択してください。- バッファーサイズやインターバルは適当に設定してください。Lambdaには一度にバッファーサイズ分のレコード数が送信されてきますので、Lambdaの実行時間を見つつLambdaのメモリ設定やバッファーを変更してください。インターバルは個人的には
60sec
でいい気がします。圧縮はGZIP or Snappy でいいと思います(Snappyは試していないです)。 - 設定したら、
Create delivery stream
で作成できます。
設定後、しばらくするとS3の設定したバケットに ${prefix}/yyyy/MM/dd/HH/${kinesis-firehose-name}-${yyyy-MM-dd-HH-mm-ss-uuid}.gz
みたいなファイルが定期的に保存されるようになります。
レコードの変換に失敗した場合、${prefix}
の下に失敗したレコードが格納されます。
Lambdaとか
LambdaのBluePrintもあるし、別にBluePrint使わなくてもこのドキュメントの通り作れば良いから非常に簡単です。 KPLを使って送信したレコードでも、KCLを必要とせず、単純なレコードとして送信されて来ます。
ざっくりいうと、 handle(event, context)
の event
に以下のようなデータが来るので
{ "records": [ { "recordId": "xxxxx", "data": "base64 encoded data" }, .... ] }
records
を for
で回して加工した後、以下のようなデータにしてレスポンスにしてあげればOK
{ "records": [ { "recordId": "xxxxx", "result": "Ok", "data": "base64 encoded data" }, .... ] }
コードのサンプルは、PythonでもNodeでもBluePrint見るのが一番いいと思います。
Athenaとか
KinesisFirehoseでGZIP形式で保存していれば、Athenaでテーブルを作成すれば割りとそのまま読めます。パーティション作った場合は別途 alter table xxx add partition ...
とかする必要はあったりしますが。
パーティションと実レコードのズレとか
一番ハマったのがここ。 KinesisFirehoseは処理した時間ごとにパスを分けてレコードを保存してくれるのですが、特にレコード内のデータを参照してパスを分けたりとかそういった機能はありません。 ただ単純に、KinesisStreamが処理した時間のパスに保存するだけです。
どうなるかというと、
- fluentdで
time: 2017-09-10 13:00:10
と記録したレコードの場合、 - 期待としては、
s3://$prefix/2017/09/10/13/xxxxxx.gz
内にそのレコードが入っていてほしいのですが、 - 実態は
s3://$prefix/2017/09/10/12/xxxxxx.gz
に含まれている 場合があります
場合があります
ってのはバッファーサイズやレコードサイズ次第で、Kinesisがどの時間に処理をしたかってだけで決まります。
なのでAthenaでテーブルを作って select * from sample where year='2017' and month=
'09' and day='10' and hour='13'
を実行しても time: 2017-09-10 13:00:10
は含まれていない時があります。
こればっかりはFirehoseの仕様(Firehoseはレコードのフォーマットやデータについては関与しない)なので、LambdaなりEMRなりを利用して、別テーブルに期待通りのパーティションの場所に保存されるよう移動してあげる必要があります。
ずれても1時間程度別の場所に格納されるだけなのでSQLで工夫するというてもありますが、一手間が面倒でなければ、期待通りの結果になるように移動したほうがよいかと。
この為だけにEMR起動するのはなんか負けた気がするので妥協も大事ですが。
ElasticSearchServiceやRedshiftにDestinationを指定した場合に、どうなるかは非常に気になります。(まだ試せていません)
実査に使ったときは、他のテーブルとJOINしてETL処理しなければいけなかったので結局EMRが必要で、クラスターを起動しているので合わせて移動する処理を行っています。
所感
KPLで格納したレコードに対して、KCLを必要とせずにレコードの変換やS3への保存が出来るのが便利でした(小並感)
しかし、保存パスには気をつける必要がありますので、あくまで一次処理がKCLを使わずにAWSサービス上で実行できるという観点で利用したほうがよいかなと。
それでもKCLがいらないというだけでだいぶ楽だとは思いますが(Python3.6が利用できるようになるし)
(タイトル長い・・・)
RDS(MySQL)のSlowQueryログをFluentdで収集したり
件名のとおりなんですが、同じような挙動をするプラグインが公式のリスト中にいくつもあって、どれを使えばいいの、ってなります。(なりました)
SlowQueryが取れそうなのは、
の3つが見つかりました。
どれでも良かったのですが、rds-log を使うことにしました。
rds-log
自体rds-slowlog
からforkされているのですが、選んだ理由は、一番ダウンロード数が多かったのと、hostも取得できたのと、genlogも取得できそうだったからです。(お寿司食べたかったから)
使い方とか
githubにある README.md 見ればわかる気がしますが一応。
gem install fluent-plugin-rds-log
<source> @type rds_log @id in_slow_query_log_sample_rds @label @slowquery log_type slow_log host sample-rds.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com username sample_user password sample_password refresh_interval 60 auto_reconnect true tag rds-slowquery-log add_host true </source> <label @slowquery> <match rds-slowquery-log> @type stdout </match> </label>
集めたログはKibanaに送ってあげれば良いのですが、そのまま送るとちょっと扱いづらいので加工してあげます。
ログの加工など
MySQLのスロークエリーログの何が扱いづらいかといいますと、 query_time
と lock_time
です。
このフィールドのフォーマットがMySQL5.6だと HH:mm:SS
。
MySQL5.7だと、 HH:mm:SS.ssssss
にパワーアップします。
知りたいのは秒数だし、ElasticSearchには date
型はあるけど、 time
型はないっぽいので、これを加工します。あとついでにIPやUserも user_host
から抜き出します。
ログの加工は record_transformer で行います。enable_ruby
をtrue
にして強引にパースすることにしました。
<label @slowquery> <filter> @type record_transformer enable_ruby true <record> source_ip ${user_host[/@.*?\[([0-9\.]+)\]/,1]} user ${user_host[/^.+?\[(.+?)\].*?@/,1]} query_second ${m = query_time.match(/(?<hour>\d+):(?<minute>\d+):(?<second>\d+)(?:\.(?<milli>\d+))?/); m[:milli].to_i.to_f / 1000000 + m[:hour].to_i * 3600 + m[:minute].to_i * 60 + m[:second].to_i} lock_second ${m = lock_time.match(/(?<hour>\d+):(?<minute>\d+):(?<second>\d+)(?:\.(?<milli>\d+))?/); m[:milli].to_i.to_f / 1000000 + m[:hour].to_i * 3600 + m[:minute].to_i * 60 + m[:second].to_i} </record> </filter> <match> .... </match> </label>
あとから見てもだいぶ強引ですが、これで晴れてquery_second
フィールドに浮動小数点でかかった時間が入るようになりました。
色々見せれない箇所にはモザイク入れていますが、 query_time
からパースしたものが query_second
に入っています。
おまけとか
fluent-plugin-rds-log
、前のバージョンである 0.1.9
まではMySQL5.7に接続したあとCloseする際の処理がうまくいっておらず、Closeすると必ず error-log
に
2017-07-04T09:58:45.968034Z 703463 [Note] Aborted connection 703463 to db: 'mysql' user: 'user_name' host: '10.x.x.x' (Got an error reading communication packets)
みたいなログが吐かれていました。
害はなさそうだったのですが、気になったので PullRequest を送ったところ、無事マージされました。ありがとうございました。
mysql2
のバージョンが 0.4.1
以上じゃないと発生するようなので、他のプラグインでも同じエラーが出るかもしれません。
今見たら、fluentd-plugin-rds-slowlog はgem.add_dependency "mysql2", "~> 0.3.11"
とあるので発生しそうですね・・・
時間があるときに動作確認してPRおくろうかな・・・
AWSのパラメータストアから環境変数にセットしたり任意の形で出力したり
AWSのパラメータストア って便利ですね。環境ごと変数やCredential情報を設定しておいて、使う前に取りだせばよいし。 Vault みたいに使えますし。
とはいえ、Dockerのような環境変数やパラメータに渡したいときに、毎回AWS SDK 使うのも面倒くさいので、ワンライナーでさくっと取れるコマンドラインツールを作りました。
使い方など
Parameter Storeに以下のキーが設定されているとして
/path/to/key/EXAMPLE1 = VALUE1 /path/to/key/EXAMPLE2 = VALUE2
以下のコマンドを実行すると
$(aws-ps load --path /path/to/key --region ap-northeast-1)
以下の環境変数がexportされます
export EXAMPLE1=VALUE export EXAMPLE2=VALUE
指定したpathで ssm.getParametersByPath
が呼ばれpathは取り除かれて出力されます。
内部的には export A=B;export C=D
を出力しているだけなので、 $()
で囲ってしまえば実行されるという単純な仕掛けです。
テンプレートとか
今のプロジェクトでは、 Play Framework(Scala) を結構使っているのでJavaプロセスに起動オプションを渡したい時も結構あります。そこで、テンプレートを渡せるようにして、任意の出力を得ることが出来るようにしてみました。
↑と設定されている内容は同じとして、以下のコマンドを実行すると
aws-ps load --path /path/to/key --region ap-northeast-1 \ --template "-D{{ .Name }}={{ .Value }}" --delimiter " "
以下の出力が得られます。
-DEXAMPLE1=VALUE -DEXAMPLE2=VALUE
テンプレートは、取得できたパラメータごとに展開され Name
にパラメータ名、 Value
にパラメータの値が入っています。delimiter
はパラメータごとの区切り文字です。go-templateを利用しているだけですので、何かやろうと思えば難しいことが出来る気がします。
Prefixとか
--path
オプションは、2017/06のアップデートで追加された階層に対応するものです。
Amazon EC2 Systems Manager のパラメータストアで階層、タグ付け、および通知を追加サポート
それ以前に作ったものや、パラメータ名でグルーピングをしたいときのために --prefix
オプションも用意しています。
例えば、以下のパラメータがあるとして
a.b.c.d = foo a.b.c.e = bar a.f.g.h = baz
以下のコマンドを実行すると
$(aws-ps load --prefix a.b.c. --delimiter "\n")
こうなります。
export d=foo export e=bar
pathと違い、prefix
が先頭から一致するかどうかしか見ていませんので、最後の.
を忘れると .d=foo
のようになってしまうことに注意してください。
未実装など
- テストがないです。
- タグで取れるオプション
--tags
も実装したほうが良い気がする。
おまけ
Githubリポジトリを parameter store
とかで検索すると他にも似たようなツール作っている人はちらほら見かけましたが、golang使って作ってみたかったのと、環境変数だけじゃなくて、コマンドの引数にも渡したかったけど、そういうのがなかったので作ってみました。
3時間くらいかかったけど、実際のコード書くより周り(GOPATHとかcobraの使い方とかIntelliJとかTravisCIとか)の方にかかった時間のほうが長い気がする。
(ていうか、ECSの環境変数にParameterStoreのキー名を直接設定する機能が欲しい)