ジョギング
- 香椎浜 x 2 周
日課
- (腕立て x 30 + 腹筋 x 30) x3
なんさん
- 10 年以上前に同じ会社だったなんさんと博多で会う
- 同じ会社だった時はあまり話しをしたことが無かったけど、当時のことを色々と覚えていて会話は弾んだと思う
- 次回はなんさんの旦那さんも交えて博多で呑む予定
夕飯
- 串かつの串匠
- 美味しかったけど服に付く臭いが凄いという…
Python の Logging モジュールで CloudWatch Logs にログを転送出来るやつ(ハンドラ)があればいいのになーと思っていたら既に在ったので嬉しかったです。
pip install watchtower
import watchtower, logging import boto3 session = boto3.Session(profile_name='your-profile') logging.basicConfig(level=logging.WARN) logger = logging.getLogger(__name__) logger.addHandler(watchtower.CloudWatchLogHandler( boto3_session=session, log_group='your-log-group', stream_name='your-log-stream') ) logger.info(dict(level="info", details={"foo":"info"})) logger.warn(dict(level="warn", details={"foo":"warn"})) logger.error(dict(level="error", details={"foo":"error"}))
$ python --version Python 3.6.0 $ python log_test.py WARNING:__main__:{'level': 'warn', 'details': {'foo': 'warn'}} ERROR:__main__:{'level': 'error', 'details': {'foo': 'error'}}
CloudWatch Logs を確認してみます。
$ aws --profile your-profile --region ap-northeast-1 logs get-log-events \ --log-group-name your-log-group \ --log-stream-name your-log-stream \ | jq '.events|sort_by(.timestamp)|.[-2,0]' { "ingestionTime": 1490399839605, "timestamp": 1490399841112, "message": "{'level': 'warn', 'details': {'foo': 'warn'}}" } { "ingestionTime": 1490399839605, "timestamp": 1490399841619, "message": "{'level': 'error', 'details': {'foo': 'error'}}" }
そこまで野球に興味は無いが、ワールドベースボールクラシックで日本が準決勝敗退。残念。ただ、アメリカと 2-1 という僅差で敗れたということで、次に繋げて欲しいと思う。(何を上から…)
以下のようなサンプルコードがあるとする。
def foobar(foo = nil) if foo.nil? return 'foo' else return 'bar' end end puts foobar(ARGV[0])
一応、以下のように動く。
$ bundle exec ruby test.rb foo $ bundle exec ruby test.rb a bar
Rubocop の手に掛かると…
$ bundle exec rubocop test.rb Inspecting 1 file C Offenses: test.rb:2:3: C: Use a guard clause instead of wrapping the code inside a conditional expression. if foo.nil? ^^ test.rb:3:5: C: Redundant return detected. return 'foo' ^^^^^^ test.rb:5:5: C: Redundant return detected. return 'bar' ^^^^^^ 1 file inspected, 3 offenses detected
無期懲役。
Use a guard clause instead of wrapping the code inside a conditional expression.
条件分岐のネストが深くなるのはいかんざき。guard clause を利用しましょうとのこと。後は return
が冗長とのこと。
冒頭のウンコードは以下のようにリファクタリング出来る。
def foobar(foo = nil) return 'foo' if foo.nil? 'bar' end puts foobar(ARGV[0])
Rubocop も以下のように無事釈放。
$ bundle exec rubocop test.rb Inspecting 1 file . 1 file inspected, no offenses detected
条件分岐のネストは要注意です。
AWS SDK を使ってツールを作ったりする場合、動作確認で直接リソースに対してアクセスしていいのは 30 代まで。40 代はリソースへのアクセスを止めてスタブを使いたいところです。
ということで、上記の記事を参考にして awspec の generator サンプルを試作したいと思います。
awspec には既存のリソースの状態を書き出してくれる generator という機能があり、以下のように実行することで各リソースの状態を spec ファイルに書き出してくれます。
$ awspec generate ec2 vpc-ab123cde >> spec/ec2_spec.rb
詳細については、以下の記事をご一読ください。
awspec の各リソースタイプにおける generator 実装は以下のようにとてもシンプルに出来ています(と勝手に思っていますが、間違っていたらすいません)。
describe
や list
系のメソッドで対象となるリソースを全件取得する以下の点について検討しました。
next_token
を使って、次のリクエストを生成しなければいけない)limit
オプションを使えば、任意の件数毎に取得出来る)AWS SDK for Ruby のスタブは aws-sdk-core を導入すれば利用出来るようです。
以下のようにインスタンスを生成する際のオプションとして stub_responses: true
を付与するだけ。簡単ですな。
require 'aws-sdk' cw_logs_client = Aws::CloudWatchLogs::Client.new(stub_responses: true) p cw_logs_client
実行するとレスポンスが得られます。
$ bundle exec ruby sample.rb #<Aws::CloudWatchLogs::Client>
例えば、#describe_log_groups のレスポンスをスタブで返したい場合には以下のように書きます。
require 'aws-sdk' cw_logs_client = Aws::CloudWatchLogs::Client.new(stub_responses: true) cw_logs_client.stub_responses(:describe_log_groups, log_groups:[{log_group_name:'log1'},{log_group_name:'log2'},{log_group_name:'log3'}], next_token: '12345' ) res = cw_logs_client.describe_log_groups p res
実行すると以下のようなレスポンスが得られます。
$ bundle exec ruby test3.rb #<struct Aws::CloudWatchLogs::Types::DescribeLogGroupsResponse log_groups=[#<struct Aws::CloudWatchLogs::Types::LogGroup log_group_name="log1", creation_time=nil, retention_in_days=nil, metric_filter_count=nil, arn=nil, stored_bytes=nil>, #<struct Aws::CloudWatchLogs::Types::LogGroup log_group_name="log2", creation_time=nil, retention_in_days=nil, metric_filter_count=nil, arn=nil, stored_bytes=nil>, #<struct Aws::CloudWatchLogs::Types::LogGroup log_group_name="log3", creation_time=nil, retention_in_days=nil, metric_filter_count=nil, arn=nil, stored_bytes=nil>], next_token="12345">
ちゃんと、それっぽいレスポンスを返せているようです。
スタブを利用して以下のようなサンプルを実装してみました。
#!/usr/bin/env ruby require 'aws-sdk' require 'erb' cw_logs_client = Aws::CloudWatchLogs::Client.new(stub_responses: true) cw_logs_client.stub_responses(:describe_log_groups, { log_groups:[{log_group_name:'log1'},{log_group_name:'log2'},{log_group_name:'log3'}], next_token: '12345' }, { log_groups:[{log_group_name:'log4'},{log_group_name:'log5'}] } ) # refer to: https://github.com/k1LoW/awspec/blob/master/lib/awspec/helper/finder/ecs.rb#L24-L34 req = {} log_groups = [] loop do res = cw_logs_client.describe_log_groups(req) log_groups.push(*res.log_groups) break if res.next_token.nil? req[:next_token] = res.next_token end template = <<-'EOF' <% log_groups.each do |log_group| %> describe cloudwatch_logs('<%= log_group.log_group_name %>') do it { should exist } <%- unless log_group.retention_in_days == nil -%> its(:retention_in_days) { should eq '<%= log_group.retention_in_days %>' } <% end -%> end <% end %> EOF puts ERB.new(template, nil, '-').result(binding).chomp
実行すると以下のように出力されます。
$ bundle exec ruby sample.rb describe cloudwatch_logs('log1') do it { should exist } end describe cloudwatch_logs('log2') do it { should exist } end describe cloudwatch_logs('log3') do it { should exist } end describe cloudwatch_logs('log4') do it { should exist } end describe cloudwatch_logs('log5') do it { should exist } end
ちゃんと next_token
によるページ送りの処理も動作確認できました。
このサンプルを利用して awspec の generator に実装を加えていきたいと思います。
stub_responses: true
だけで簡単に使えます