ようへいの日々精進XP

よかろうもん

2017 年 03 月 25 日(土)

ジョギング

日課

  • (腕立て x 30 + 腹筋 x 30) x3

なんさん

  • 10 年以上前に同じ会社だったなんさんと博多で会う
  • 同じ会社だった時はあまり話しをしたことが無かったけど、当時のことを色々と覚えていて会話は弾んだと思う
  • 次回はなんさんの旦那さんも交えて博多で呑む予定

夕飯

  • 串かつの串匠
  • 美味しかったけど服に付く臭いが凄いという…

小ネタ道場一本勝負 〜 Python Logging で CloudWatch Logs にログを送信したいんですけど 〜

たのもう

Python の Logging モジュールで CloudWatch Logs にログを転送出来るやつ(ハンドラ)があればいいのになーと思っていたら既に在ったので嬉しかったです。

github.com

一本

watchtower のインストール

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'}}"
}

有難うございました

github.com

2017 年 03 月 23 日(木)

ジョギング

家族

お義母さんの調子がイマイチで、それに振り回される奥さんも疲弊している。自分が昔病んでいた時の状況と似ていて、このままだと闇しか見えない気がする。

自分が病んでいた頃、どんな事を考えて、どんな風に病気を克服したかを今一度思い出してみることで、何かの役に立てないかを考えている。

2017 年 03 月 22 日(水)

腹痛

  • だいぶん治まる

WBC

そこまで野球に興味は無いが、ワールドベースボールクラシックで日本が準決勝敗退。残念。ただ、アメリカと 2-1 という僅差で敗れたということで、次に繋げて欲しいと思う。(何を上から…)

小ネタ道場一本勝負 〜 rubocop で俺のウンコードをリファクタするぞ(Guard clause 取締法違反) 〜

容疑者

  • Use a guard clause instead of wrapping the code inside a conditional expression.

たのもう

以下のようなサンプルコードがあるとする。

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

ありがとうございました!

条件分岐のネストは要注意です。

2017 年 03 月 20 日(月)

お義母さん

が遊びに来ていた。

昨晩はゆっくりと寝ることが出来たようで良かった。

これからもちょくちょく遊びに来て欲しい。

プルリクエスト

自分の Github リポジトリの棚卸しをしていたら、いくつかのリポジトリでプルリクエストを頂いていた。

長い間スルーしていたものもあって申し訳ない気持ちで一杯。

慌ててマージしまくった。

2017 年 03 月 19 日(日)

ジョギング

日課

  • (腕立て x 30 + 腹筋 x 30) x 3

こえむさんと呑む

東京在住の際に幾つかの勉強会でご一緒させて頂いていた @koemu さんが福岡に来られるということで博多駅で一杯呑んだ。

お互いのお仕事の状況や家族の事などを肴にあっという間に時間が流れ、最後は名島ラーメンで占めて本当に楽しいお酒だった。

有難うございました。

AWS SDK のスタブを使って awspec の generator サンプルを試作するメモ

時代はスタブ

AWS SDK を使ってツールを作ったりする場合、動作確認で直接リソースに対してアクセスしていいのは 30 代まで。40 代はリソースへのアクセスを止めてスタブを使いたいところです。

ということで、上記の記事を参考にして awspec の generator サンプルを試作したいと思います。

さて、今回は

awspec の generator

awspec には既存のリソースの状態を書き出してくれる generator という機能があり、以下のように実行することで各リソースの状態を spec ファイルに書き出してくれます。

$ awspec generate ec2 vpc-ab123cde >> spec/ec2_spec.rb

詳細については、以下の記事をご一読ください。

qiita.com

awspec の各リソースタイプにおける generator 実装は以下のようにとてもシンプルに出来ています(と勝手に思っていますが、間違っていたらすいません)。

  • describelist 系のメソッドで対象となるリソースを全件取得する
  • 取得したリソース情報を ERB を使って spec ファイルに書き出している

CloudWatch Logs の Generator を作るにあたって

以下の点について検討しました。

  • #describe_log_groups は一回で最大 50 件しか情報を取得出来ない(50 件以上あれば next_token を使って、次のリクエストを生成しなければいけない)
  • 50 件以上の情報を CLI やマネジメントコンソールで作るのはめんどくさい(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 に実装を加えていきたいと思います。

おわり

言いたかったこと

  • 40 代からはスタブを使いましょう
  • スタブはインスタンス作成時に stub_responses: true だけで簡単に使えます
  • スタブで返したいレスポンスの内容はドキュメントを参照しましょう