2019年10月27日日曜日

マイクラで 3D 酔いしないための努力

概要

FPS 視点のゲームはよく 3D 酔いすると言われています
自分も 3D 酔いになりやすく長時間続けるのが大変です
自分なりに少しでも軽減させる方法があったので紹介します

環境

  • MacBookAir 14inch
    • CPU Dual-Core Intel Core i7
    • メモリ 8GB
    • GPU Intel HD Graphics 5000 (オンボード)
  • macOS 10.15
  • Minecraft JavaEdition 1.14

方法

ラップトップではやらない

なぜか自分はラップトップ上でやるとすぐに酔います
Mac Book Air の 14inch で起動しラップトップのキーボードと無線のマウスを使ってプレイしています
おそらく画面が近いのが酔いの原因ではないのかと思っています

近くで見ない、大画面で離れてプレイ

プレイする場合はラップトップでプレイせずにクラムシェルにして thunderbult で外部ディスプレイに接続してプレイ画面を移すようにしています
そしてキーボードは MagicKeyboard2 にしています
ディスプレイのサイズは 32inch でそのディスプレイから 3 - 4m くらい離れてプレイしています
この状況にしてからはラップトップでプレイしている時よりか症状が改善しました

頭を固定する

またプレイするときはなるべく頭を動かさないようにしています
理由はわかりませんが頭が動く状態だと酔いやすかったです
自分の場合はハイバックっぽい背もたれ付きの椅子を使っているので頭を背もたれに接着させることでなるべく動かないように固定しています

画面がカクつく止まる場合は酔いやすい

マシンスペックが低いことで描画が遅れ本来思っていた描画と異なることで酔いやすくなるようです
酔いやすい場合は 60fps 出ているか確認しましょう

改善しない場合ははマイクラの設定を見直しましょう
それでもダメな場合は思い切ってゲーミング PC レベルのハイスペックなマシン (特に CPU と GPU を積んだマシン) に買い換えるのもありかなと思います

ちなみに自分の環境のスペックでも 60fps は常に出ません
Mob が多かったりパーティクルが激しい場所では 20 - 30 fps 程度に落ちます
そういう状況の場所ではなるべくプレイしないようにしています

Optifine などの Mod を入れてシェーダーなどの設定を変更してマシンの負荷を下げるのも良いと思います

設定関連

マイクラの画面設定などを見直しましょう
個人的に fps に特に影響するのが「最大描画チャンク」かなと思います
これが少なかければ少ないほど fps が上がりました
しかし最大描画チャンク数が小さすぎるほどプレイに影響するので最低 8 or 9 にはしましょう
具体的には値が小さすぎると近くにある食物などが育たなくなったりします
またその状態で 60fps 以上出ないと酔いやすくなるかなと思います

あとは

  • ビデオ設定 -> グラフィックス -> 処理優先
  • ビデオ設定 -> アニメーションの設定 -> すべてオフ
  • ビデオ設定 -> 画面の揺れ -> オフ

などなどビデオ設定を負荷がかからないように調整するのがポイントです

臨場感のある視野角をオンにする

個人的にはこれも効果があると思います
ゲームへの没入感を高まるので酔いにくくなるかなと思います

慣れ

人によっては全然酔わないという人もいるようです
そういった人は実はハイスペックマシンで常に 60fps 以上出ているのかもしれませんが、、
fps が出ていない状態でカクカクの状況でも酔わない人は酔わないのかもしれません

あとはどうしてもプレイを続けたいのであれば酔い止めなどの薬に頼るのも手だと思いますが、そこまでして頑張る必要はないと思います

2019年10月26日土曜日

マインクラフト 1.14 やりこみ要素

概要

攻略後にやりこめる要素をまとめてみました
個人的にやってみた結果から「かかる時間」と「難易度 (Max★5)」「回数」を記載しています
やり込み要素なので順番などはないので好きなものからやって OK です

環境

  • macOS 10.15
  • Minecraft JavaEdition 1.14

スライムトラップタワー作成 (★★★)

  • かかる時間・・・5 - 6 時間程度
  • 難易度・・・★★★
  • 回数・・・基本 1 度きり

攻略後であれば効率強化V とビーコンの採掘速度上昇II が使えるので、チャンク堀りはあっさり終わると思います
あとは水流を使ってスライムを流すのと処理するのにマグマブロックが必要なくらいかなと思います

天空トラップタワー (水流式) (★★★★)

  • かかる時間・・・6 - 7 時間程度,
  • 難易度・・・ ★★★★
  • 回数・・・基本 1 度きり

海上に作成するのがベストなのでまずは海上探しから始めましょう
あとは建築するだけですが規模が割りと大きいので時間はかかると思います
水流式はディスペンサーが一つで済む方式がシンプルで良いと思います

洞窟探検 (★★)

  • かかる時間・・・4 - 5 時間程度,
  • 難易度・・・★★
  • 回数・・・何度でも

強化済みのダイヤ装備であれば Mob にやられることはないと思います
気をつけるのは溶岩くらいです
新しいスポナー探しや炭鉱探しをしても良いと思います

村人ゾンビ治療 (★★★★)

  • かかる時間・・・6 - 7 時間程度
  • 難易度・・・★★★★
  • 回数・・・何度でも

まずは Mob が狩りやすい整地された暗闇を作成しましょう
そして適当にトラップドアで落とし穴を作ってそこで村人ゾンビが治療できる空間を作成しておきます
落とし穴は浅すぎると日光を浴びて村人ゾンビが焼けてしまうので屋根などを設置して焼けないようにしましょう
またある程度離れてしまうとデスポーンしてしまうので治療するための「弱化のスプラッシュポーション」と「金のりんご」は落とし穴のトラップ近くのチェストに事前に置いておきましょう

村人ゾンビのスポーンの確率が低いのと必要なアイテムがやや多いので難易度を 4 にしています
運悪くニートが出た場合は処分しましょう

エリトラで地図拡大、巨大地図作成 (★★)

  • かかる時間・・・5 - 6 時間程度,
  • 難易度・・・★★
  • 回数・・・基本 1 度きり

エリトラに修繕は必須かなと思います
ロケット花火も必須なので大量の火薬も必要です
ない場合は先に天空トラップタワーを作るか村人ゾンビ探しで同時にクリーパーをドロップ増加III で狩って火薬を集めましょう

素材集め、村人と交易可能な素材を集め続けてエメラルドを大量に手に入れる (★)

  • かかる時間・・・2 - 3 時間程度
  • 難易度・・・★
  • 回数・・・何度でも

ファームビル的な感じの作業です
集めやすいおすすめの素材と職業は

  • さとうきび・・・司書
  • にんじん、じゃがいも・・・農民
  • ゾンビ肉・・・司祭

かなと思います
特に決めは無いので好きな職業の村人を作成して交易すれば OK です
素材の収集を自動化しても良いと思います

ブランチマイニング (★)

  • かかる時間・・・2 - 3 時間程度
  • 難易度・・・★
  • 回数・・・ 何度でも

Y=11 で掘るのがオススメです
効率強化V + ビーコンで掘るのを忘れないようにしましょう
またほしい鉱石 (特にダイヤ) は「幸運III」で採掘するのを忘れないようにしましょう
運が良ければダイヤ 100 個くらいは 1 時間くらいで手に入ります

村拡大 (★★)

  • かかる時間・・・5 - 6 時間程度
  • 難易度・・・★★
  • 回数・・・何度でも

整地して村を拡大していきましょう
単純作業なので簡単ですが単純に時間がかかります
拡大後は建築物を作成したり素材集めの効率化のために農場や牧場を拡大しても良いと思います

建築 (★★★★)

  • かかる時間・・・4 - 20 時間程度,
  • 難易度・・・★★★★
  • 回数・・・何度でも

センスが問われます
建築するものによって時間はかなり変わってくると思います
ちなみに自分は建築はまったくやりません

トライデント入手 (★★★★)

  • かかる時間・・・4 - 5 時間程度
  • 難易度・・・★★★★
  • 回数・・・基本は 1 度のみ

ドラウンドからのドロップのみで入手することができるレアアイテムです
ドラウンドは海底に湧くゾンビです
オススメの狩り方は「海底遺跡」の付近です
海底遺跡はイルカにタラやシャケを与えると場所を教えてくれます (参考)
またイルカは大海原でないとスポーンしていないのでまずは大海原を探す必要があります

海底遺跡が見つかったあとはドラウンドをひたすら狩るだけですが水中での戦闘になるので「暗視のポーション」と「水中呼吸のポーション」が必須になると思います
この 2 つのポーションも結構取得が大変です

またドラウンドがトライデントをドロップする確率もかなり低いので愚直にやっていると 4-5 時間では終わらない可能性もあります
トライデントを持ったドラウンドが湧けばチャンスです
なので面倒な場合はドラウンドトラップを作っちゃっても良いかなと思います
自分は狩るのが楽しいのでトラップは作りません

特殊ダンジョン/バイオーム探し (★★★)

  • かかる時間・・・4 -5 時間程度
  • 難易度・・・★★★
  • 回数・・・何度でも

とりあえず探してみましょう
エリトラ+ロケット花火は必須かなと思います
海底神殿や森の洋館は村人と交易して地図を入手しそれを元に探しても良いと思います


    • 海底神殿
    • 難破船
    • 海底遺跡
    • サンゴ礁
  • 地上
    • 森の洋館
    • ピリジャーの前哨基地
    • ジャングル
    • メサバイオーム
    • ウィッチの小屋

などなど

宝探し (★★★)

  • かかる時間・・・2 -3 時間程度
  • 難易度・・・★★★
  • 回数・・・何度でも

まずは海底遺跡や難破船の宝箱にある「宝の地図」を探しましょう
それを使って宝箱を見つけます
宝箱には「海洋の心」やダイヤなど貴重なアイテムがあります
宝箱は海底の砂の中や土の中に埋まっています
周りには砂岩などがあるので水中を採掘して砂岩が見えたら宝箱が近い証拠です

ちなみに海洋の心は「オウムガイの殻」とクラフトすることでコンジットを入手することができます

動物手なづけ (★★)

  • かかる時間・・・2 - 3 時間程度
  • 難易度・・・★★
  • 回数・・・何度でも

ネコ、オオカミ、ウマあたりでしょうか
それ以外にもあるので興味があればという感じです (参考)

カメの甲羅入手 (★★★)

  • かかる時間・・・3 - 4 時間程度
  • 難易度・・・★★★
  • 回数・・・何度でも

まず海藻が必要なのでハサミを持って海に潜り回収しましょう
水源に骨粉をまけば海藻を手動で生成することも可能です

次にカメを 2 匹以上見つけましょう
砂浜に生息しやすいので海の周りを探索しましょう
おそらくカメを探す作業が一番大変だと思います
カメの繁殖は海藻を上げれば OK です
海藻を上げて暫くすると砂浜に産卵し始めます
カメの卵ができたらシルクタッチのピッケルでカメの卵を回収しましょう

回収したら拠点に戻ってカメの卵を孵化させます
卵を設置できるのは砂の上だけなので適当に砂を置いてカメの卵を配置しましょう
あとはひたすら孵化するのを待つだけです
カメの卵が成長するタイミングは夜明けのみです
寝てしまうと成長のタイミングを逃すので夜通し起きている必要があります

孵化してカメの子供が生まれたら海藻で成長速度を早めましょう
子ガメから大人ガメに成長すればそのタイミングで「カメのウロコ」をドロップします
カメのウロコが 5 つ集まったら「カメの甲羅」をクラフトすることができます
あとは好きなエンチャントをしてあげましょう

襲撃イベント (★★★★)

  • かかる時間・・・3 - 4 時間程度
  • 難易度・・・★★★★
  • 回数・・・何度でも

まずは「不吉な予感」の効果を得る必要があります
これは襲撃の前段階でビリジャーなどがとりあえず攻めてきた状態です
旗を持ったビリジャーを倒せば付「不吉な予感」の効果が付与されます

この効果が付与された状態で村に近づくと襲撃イベントが開始されます
フェーズが 6 つくらいに分かれており襲撃のゲージが 0 になると次のフェースが始まります
すべてのフェーズで湧くビリジャーなどをすべて倒せばクリアです
敵も強く武器や防具はちゃんとエンチャントしていないと苦戦すると思います

襲撃イベントクリア後は「村の英雄」の効果が一定時間付与されます
この効果が付与されている間は村人との交易が割安になります

村人職業ガチャ (★★)

  • かかる時間・・・2 - 3 時間程度
  • 難易度・・・★★
  • 回数・・・何度でも

職業ブロックを置く -> 壊すを繰り返すだけです
「修繕」意外のオススメは以下の通りです

  • 耐久力III

最強武器防具作成 (★★★)

  • かかる時間・・・4 - 5 時間程度
  • 難易度・・・★★★
  • 回数・・・何度でも

エンチャント台をベースに足りないエンチャントをエンチャント本で補う感じです
エンチャント本は司書ガチャが一番簡単だと思います
運も絡むので結構時間がかかるかもしれません
武器防具の最強エンチャント一覧はこちらを参考にしてください

トライデントやカメの甲羅などのレア防具も最強エンチャントして上げましょう

釣り自動化 (★)

  • かかる時間・・・1 - 2 時間程度
  • 難易度・・・★
  • 回数・・・基本は 1 度きり

スポナーの待ち時間やカメの羽化の待ち時間にオススメです
簡単に設置できるので待つところにそれぞれ設置しても良いかもしれません
魚類はポーションやネコの手なづけ、イルカにあげるなどいろいろな使い道があるのである程度確保しておくと良いと思います
作り方はこちらです

ネザーワープ作成 (★★)

  • かかる時間・・・2 - 3 時間程度
  • 難易度・・・★★
  • 回数・・・基本は 1 度きり

実世界の拠点と移動先をネザーの世界でつなぐことでレールの量を格段に減らせます
実際の世界とネザーの世界の座標は 1/8 になっているのでその分節約できるという仕組みです

あらかじめ拠点にはネザーゲートを作成しておきます
そして実世界の移動先に行き黒曜石と火打ち石でネザーゲートを作成します
するとネザーの世界に 2 つのネザーゲートが出現することになるので、ネザー上でそれらのネザーゲート同士をレールでつなげば OK です

森の洋館 (★★★★)

  • かかる時間・・・3- 4 時間程度
  • 難易度・・・★★★★
  • 回数・・・基本は 1 度きり

製図家の村人から「探検家の地図」を入手してからのほうが探索の手間が省けるので楽です
目的は「死のトーテム」とお宝チェストです
注意するのはエヴォーカーです
洋館内に 4 - 5 体ほどいるのですべて倒しましょう
エヴォーカーにヴェックスを召喚されたら距離を取って弓矢を使って倒しましょう
ちなみにヴェックスはどこまでも追ってきます
ネザーに逃げてもゲートでワープして追跡してくるので倒しましょう

海底神殿 (★★★★)

  • かかる時間・・・3- 4 時間程度
  • 難易度・・・★★★★
  • 回数・・・基本は 1 度きり

地図は製図家からもらうと簡単です
目的は「スポンジ」くらいかなと思います
金ブロックもありますがそこまで魅力なアイテムでもないかなと思います
ボスは「エルダーガーディアン」で 3 体湧いています

エルダーガーディアンは採掘速度低下の効果を付与してきます
攻略前にコンジットを設置するためにプリズマイン系のブロックを先に採掘したい場合は採掘速度低下の効果を付与されると面倒なので「牛乳」を持っていくと良いでしょう
コンジットの設置場所は海底神殿の入り口がおすすめです

武器は「水性特攻 IV」「忠誠 III」のエンチャントが付与されたトライデントがあると便利です

2019年10月18日金曜日

Ruby で競技プログラミングを始める方法 (Codeforces 編)

概要

Codeforces という Telegram 社が進める競技プログラミングサービスの使い方を紹介します
アカウント登録から実際にコンテストに参加してコードをサブミットするところまで紹介します

環境

  • macOS 10.15
  • Ruby 2.0.0p645

ユーザ登録

まずはユーザ登録します
トップページにアクセスして右上の「Register」を選択します

ハンドルネームとメールアドレス、パスワードを入力します
「Register」ボタンを押すと確認のメールが届きます

メールに記載されているアドレスをクリックすれば登録完了です

トップページの右上の「Enter」からログインできることを確認しましょう

参加したいコンテスト確認

上部のメニューに「Calendar」があるのでそれを使うのが簡単です
コンテストがいつ開催されるのかひと目でわかります

参加したいコンテストをクリックすれば開催の時間や内容が確認できます
コンテストにはいろいろな種類があります
基本は 2 時間で終わる「Codeforces Round」をやるとい良いかなと思います
Div1 から Div3 でレイティングによって参加可能なコンテストも分かれています
その他にも数週間かけて行うコンテストや言語が限定されているコンテストなどもあります

コンテスト登録

参加したいコンテストが見つかったらまずは登録しましょう
写真は登録開始前の写真になります
登録可能な状態になると一番右の部分に「Register」ボタンが表示されるので登録しましょう

そして実際にコンテストが始まるとコンテストに入ることができるようになり問題を確認することができるようになります

コンテスト開始、コードサブミット

問題は基本英語です
日本語はありません
もともとはロシア語だったので英語があるだけでもありがたいです

画面下のほうにいくとコードをサブミットできるフォームがあります
自分の進め方としてはローカルで開発をして問題がなければそれをサブミットする感じです
ファイルは 1 つしかサブミットできないので class などをわける場合も 1 つのファイルにまとめましょう

また直接コーディングしてサブミットすることもできます
自分は好きなエディタで開発してからサブミットしたいのでこれは使ったことがありません (Submit Code タブ)

サブミットしたコードの結果は「My Submissions」で確認できます
Verdict が「Accepted」になればすべての問題が解けたことなります
問題文中にサンプルの解答があるのですが実際はそれ以外にも問題がありそれらはどんな入力でどんな出力になれば良いかはわかりません
なので途中でコケた場合は何かしらの実装漏れがあることになります

その他の機能

  • Status
    • コンテストに参加している全ユーザの進捗と開発言語が確認できます
  • Hacks
    • 自分は使ったことがないのですが一定の条件をクリアすると他の人のコードを見ることができる機能らしいです (?)

過去問も解ける

過去に行われたコンテストはすべて過去問として公開されています
もし解けてもレイティングは上がりませんが練習したい場合や本番のコンテストで解けなかった問題に再度挑戦したい場合に使えます

またコンテストが終了して過去問になるとそのコンテストにサブミットされたすべてのコードを確認することができます (Status タブ)

アカウントページから過去に参加したコンテストの履歴が見れる

右上のアカウント名をクリックすると自分が過去に参加したコンテストやサブミットしたコードの一覧と結果を確認することができます

画像はアカウントのページから「Submissions」タブでサブミットしたコードの一覧を確認しています

注意事項

  • Codeforces 上で実行される Ruby のバージョンが 2.0.0 にしか対応していない
    • ローカルでは 2.6 など最新のバージョンを使っている場合、Codeforces にサブミットするとエラーになる場合がある
    • 自分は rbenv を使ってローカルでも Codeforces と同じ Ruby のバージョンが使えるようにしている (参考)
  • gem は使えない
    • 使えるのは標準ライブラリだけです
    • gem install したライブラリは Codeforces 上では使えません
  • Web アプリやライブラリを作りたい人には向かないかも
    • 基本はアルゴリズムを鍛えたい、勉強したい人向けの問題になります
    • Class や Module を使ってツールを作る感じではないのでそういうことをしたい人には向かないかもしれません

Tips

  • 問題は基本的に標準入力を受け取るので標準入力の扱い方をマスタしておきましょう

  • スペース区切りの 2 つの標準入力の数値をそれぞれの変数に格納

vals = STDIN.readlines
n, m = vals[0].split(' ').map(&:to_i)
  • 1 行目はスペース区切りの数値で 2 行目はスペース区切りの数値を配列として格納
vals = STDIN.readlines
h, w = vals[0].split(' ').map(&:to_i)
hary = vals[1].split(' ').map(&:to_i)
  • 1 行目で受け取った数値分の配列が入力される場合
vals = STDIN.readlines
n = vals[0].to_i
stones = []
n.times { |i|
  s = vals[i + 1].split(' ').map(&:to_i)
  stones.push(s)
}

2019年10月14日月曜日

Mojave から Catalina にアップグレードしたときのメモ

概要

macOS を Mojave から Catalina にアップグレードしたのでそのとき作業したことを紹介します

環境

  • Mac Book Air 2014 Later
  • macOS Mojave (10.14.6) -> Catalina (10.15)

Catalina インストール

自分は Mac App Store からインストールしました
システム環境 設定-> ソフトウェアアップデートからインストールしようとすると「データベースの要求が時間切れになりました」というエラーが出ます
その場合は Mac App Store を起動してそこから Catalina を検索してダウンロード -> インストールとしましょう

zsh 設定

  • mv .bash_profile .zshrc
  • echo 'export PROMPT="%F{cyan}%n@%m%f %F{blue}%1~%f %# "' >> .zshrc

大文字小文字の区別なく補完

  • vim .zshrc
autoload -Uz compinit && compinit
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'

reattach-to-user-namespace のアップグレード

  • brew upgrade reattach-to-user-namespace
warning: reattach-to-user-namespace: unsupported new OS, trying as if it were 10.10

xcode のアップデート

Catalina インストール後に開くとコンポーネントのインストールが始まるのでそのままインストールしました

Chrome のデフォルトフォントの変更

Catalina にすると「ヒラギノ角ゴPro/ProN」がないためデフォルトの日本語フォントが明朝体になります
ヒラギノ角ゴシックは使えるのでそれに変更しました

  • 設定 -> デザイン -> フォントをカスタマイズ -> 標準フォント -> ヒラギノ角ゴシック

2019年10月7日月曜日

golang で Wrap と Cause を使ったエラーハンドリングの方法

概要

github.com/pkg/errors の Wrap と Cause を使ったエラーハンドリングのサンプルを紹介します
Wrap と Cause 自体はエラーのラップとラップ前の大元のエラーを取得するための関数です

環境

  • macOS 10.14.6
  • golang 1.12.9

普通に構造体で判定する

まずは普通にエラーの構造体を作成して switch 文で判定してみます
errors パッケージも Wrap と Cause が使えない標準の errors パッケージになります

package main

import (
    "errors"
    "fmt"
    "reflect"
)

type Error1 struct{}
type Error2 struct{}

func (e1 *Error1) Error() string {
    return fmt.Sprintf("error1")
}

func (f *Error2) Error() string {
    return fmt.Sprintf("error2")
}

func fetch(t int) error {
    if t == 1 {
        return &Error1{}
    } else if t == 2 {
        return &Error2{}
    } else {
        return errors.New("errorString")
    }
}

func main() {
    err := fetch(0) // 1 or 2 or other number
    fmt.Println(reflect.TypeOf(err))
    switch err.(type) {
    case *Error1:
        fmt.Println("error1 occured")
        fmt.Println(err)
    case *Error2:
        fmt.Println("error2 occured")
        fmt.Println(err)
    default:
        fmt.Println("errorString occured")
        fmt.Println(err)
    }
}

標準の errors パッケージのタイプを見ると *errors.errorString というタイプになっているようです

少し解説

Error() 関数を持った構造体を定義することで自作のエラー構造体として扱うことができます
fetch 関数では error タイプとして返却することができます

あとは受け取ったエラーを err.(type) でタイプ判定するだけです
一番オーソドックスな判定方法かなと思います

Wrap + Cause で大元の構造体を判定する

次に github.com/pkg/errors を使ってみます
これを使うことでエラーをラップしエラーが発生した経路を把握しやすくすることができます

package main

import (
    "fmt"
    "github.com/pkg/errors"
    "reflect"
)

type Error1 struct{}
type Error2 struct{}

func (e1 *Error1) Error() string {
    return fmt.Sprintf("error1")
}

func (f *Error2) Error() string {
    return fmt.Sprintf("error2")
}

func fetch(t int) error {
    if t == 1 {
        e1 := &Error1{}
        e2 := &Error2{}
        return errors.Wrap(e1, e2.Error())
    } else if t == 2 {
        e2 := &Error2{}
        e1 := &Error1{}
        return errors.Wrap(e2, e1.Error())
    } else {
        return errors.New("fundamental")
    }
}

func main() {
    err := fetch(0) // 1 or 2 or other number
    fmt.Println(reflect.TypeOf(err))
    switch errors.Cause(err).(type) {
    case *Error1:
        fmt.Println("error1 occured")
        fmt.Println(err)
    case *Error2:
        fmt.Println("error2 occured")
        fmt.Println(err)
    default:
        fmt.Println("fundamental error occured")
        fmt.Println(err)
    }
}

github.com/pkg/errors パッケージを New すると errors.fundamental というタイプになりました

少し解説

errors.Wrap は既存のエラーにエラー文を追加することができる関数です
単純に末尾にエラー文が追加されます
大元のエラーのタイプは変わりません

大元のエラーのタイプを取り出す場合は errors.Cause を使います
これで大元のエラーを取り出してあとは type でタイプを参照すれば OK です

結局エラーの型を知らないとダメかな

エラーハンドリングする際は結局エラーの型を知らないと判定できないかなと思います
もしくはエラー文で分岐する方法もありますがその方法はほとんど見たことがありません
基本は error で返ってきて .(type) でキャストするとタイプがわかる感じなのでそれを使うのが簡単かなと思います
特にライブラリを使っている場合はこの方法になるかなと思います

どんなタイプのエラーが返ってくるはさすがにリファレンスやコードを読むしかないかなと思います
もしくはエラーを発生させられる環境があるのであれば サンプルにあるように fmt.Println(reflect.TypeOf(err)) でタイプを見ても良いかなと思います

もしくはインタフェースを実装するか

Cause とインタフェースを組み合わせてエラーを判定する方法もあるようです (参考)
自作のエラー構造体が特定のインタフェースを実装しているかどうかでエラーのタイプを判定します

2019年10月4日金曜日

Kibana でフィールドを分割して新しいフィールドとして登録する方法

概要

普通にやろうとすれば td-agent 側の format を使ってフィールドに分割すると思います
何らかの理由で td-agent 側が操作できない場合は Kibana 側だけでも解決することができます
Kibana の Scripted field という機能を使います

環境

  • macOS 10.14.6
  • Kibana 7.4.0

Scripted field とは

簡単に言えば特定のフィールに対して Groovy を使って値を抽出する方法です
文字列を分割したり数値のフィールド同士を計算した結果を新しいフィールドとして登録することができます

fielddata=true にする

今回は Scripted field でテキストのフィールドを扱おうと思います
実は ElasticSearch はデフォルトだとテキストのフィールドに対して Scripted field を使うことができます
なので ElasticSearch のマッピングに fielddata=true フラグを設定する必要があります

例えば fluentd という名前のインデックスのマッピングで log というテキストフィールドがある場合にこのフィールドに対して fielddata=true を設定する場合は以下のようなクエリを ElasticSearch に対して投げます

curl -X PUT "localhost:9200/fluentd/_mapping?pretty" -H 'Content-Type: application/json' -d'
{
  "properties": {
    "log": { 
      "type":     "text",
      "fielddata": true
    }
  }
}
'

インデックス名やフィールド名を適宜変更して fielddata=true にしましょう

Scripted field の作成

まずは Kibana の Index Patterns にアクセスします
そして該当のインデックスを選択し「Scripted fields」タブを選択します
そして右側にある「Add scripted field」 を選択します

スクリプトで解析したあとの新しいフィールドの情報を登録します
以下のように登録しましょう

  • Name・・・status
  • Language・・・painless
  • Type・・・number
  • Format・・・Default
  • Popularity・・・0

今回は文字列からステータスコードを取得してみます
なので結果としては数値が返却されるスクリプトを作成します

スクリプトをコーディング

その下にスクリプトを登録するテキストフィールドがあるので登録します
今回は以下のようなスクリプトになります

def log = doc['log.keyword'].value;
if (log != null) {
    // Debug.explain(log);
    def splited = log.splitOnToken(' ');
    try {
        return Integer.parseInt(splited[8]);
    } catch (Exception e) {
    }
}
return 0;

スクリプトを実行できる環境も備わっておりコーディングしながらデバッグすることができます
「Get help with the syntax and preview the results of your script.」をクリックすると右ペインにスクリプトを実行する環境が表示されます
シンタックスエラーやコンパイルエラーの場合はその旨が右ペインに表示されます

エラーが出なければ「Create field」を選択して完了です

スクリプト解説

少し解説します
スクリプトは Kibana が独自で作成している painless という言語になりますがほぼ Groovy っぽく書けます
Groovy なので Java の API も使えます
また painless として独自で作成している関数などもあるようです (splitOnToken はおそらくそれ)
詳しくは参考サイトにもある API リファレンスを参照してください

既存のフィールドを参照するには doc['hoge'].value という感じで参照します
テキストフィールドの場合には .keyword を付与しないとテキスト情報を取得できないので注意してください

あとは状況に応じて数値の情報を return すれば OK です

注意事項

このページの一番下にもあるのですが Scripted field を追加した瞬間にすべてのデータに対してスクリプトが適用されるため負荷が高まる可能性があります
そのせいで ElasticSearch がダウンする可能性もあるので追加する場合は慎重に追加してください

最後に

Scripted field を使って既存のフィールドをパースして新たなフィールドを追加してみました
td-agent などのロガーサイドに手を加える必要がなく Kibana だけで完結できるのは嬉しい点かなと思います
フィールドを使う場合には最初に fielddata=true を投入する必要があるのでそこは注意が必要です

参考サイト

2019年10月3日木曜日

efk 環境を docker for Mac でサクっと構築する

概要

この記事この記事を組み合わせてサクっと efk (ElasticSearch + fluentd + Kibana) 環境を構築する方法を紹介します
docker コンテナとして動作させるので VM などは不要です

今回はわかりやすくすべて docker コマンドで起動します
慣れてきたら docker-compose にしても OK だと思います

環境

  • macOS 10.14.6
  • docker for Mac 19.03.2
  • ElasticSearch 7.4.0
  • Kibana 7.4.0

Elasticsearch 起動

  • docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.4.0

Kibana 起動

  • docker run -d -e ELASTICSEARCH_HOSTS=http://192.168.99.1:9200 -p 5601:5601 docker.elastic.co/kibana/kibana:7.4.0

192.168.99.1 の IP アドレスの部分は docker for Mac が動作しているホストマシンの IP アドレスを指定してください
7.4.0 の場合 ELASTICSEARCH_URL ではなく ELASTICSEARCH_HOSTS になっているので注意してください

fluentd 起動

  • vim Dockerfile
FROM fluent/fluentd

RUN apk add --update --virtual .build-deps \
        sudo build-base ruby-dev \
 && sudo gem install \
        fluent-plugin-elasticsearch \
 && sudo gem sources --clear-all \
 && apk del .build-deps \
 && rm -rf /var/cache/apk/* \
           /home/fluent/.gem/ruby/2.4.0/cache/*.gem
  • docker build -t my_fluentd .
  • vim fluent.conf
<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<match docker.**>
  @type copy
  <store>
    @type stdout
  </store>
  <store>
    @type elasticsearch
    host 192.168.99.1
    port 9200
    index_name fluentd
    type_name fluentd
  </store>
</match>
  • docker run -d -p 24224:24224 -p 24224:24224/udp -v $(pwd):/fluentd/etc -e FLUENTD_CONF=fluent.conf my_fluentd

192.168.99.1 の IP アドレスの部分は docker for Mac が動作しているホストマシンの IP アドレスを指定してください

nginx 起動

ログを格納するための確認用のコンテナです
自身のアプリがあればそれでも OK です

  • docker run -d --log-driver=fluentd --log-opt fluentd-address=192.168.99.1:24224 --log-opt tag="docker.{{.Name}}" --name web -p 80:80 nginx

192.168.99.1 の IP アドレスの部分は docker for Mac が動作しているホストマシンの IP アドレスを指定してください

動作確認

動作しているコンテナは以下の通りです

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fc73ffd85690 nginx "nginx -g 'daemon of…" 11 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp web b13985e947df docker.elastic.co/kibana/kibana:7.4.0 "/usr/local/bin/dumb…" 2 minutes ago Up 2 minutes 0.0.0.0:5601->5601/tcp romantic_chebyshev 07213efdf16d my_fluentd "/bin/entrypoint.sh …" 6 minutes ago Up 6 minutes 5140/tcp, 0.0.0.0:24224->24224/tcp, 0.0.0.0:24224->24224/udp nervous_hofstadter ea9a1c8cee93 docker.elastic.co/elasticsearch/elasticsearch:7.4.0 "/usr/local/bin/dock…" 13 minutes ago Up 13 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp distracted_mcclintock

とりあえず動作確認用の nginx にアクセスしてみます

  • curl localhost/hoge

Kibana でログが来ているか確認します

http://192.168.99.1:5601/ にブラウザでアクセスしてください
まずは Kibana 上でインデックスを作成します

左メニューから「Management」を選択します

Kibana の「Index Patterns」を選択します

「Create index pattern」を選択します

今回の場合 fluentd というインデックスでログが飛んできます
fluentd と入力しインデックスが見つかったら「Next step」を選択します

「Create index pattern」を選択します
timestamp などが必要な場合はここでフィールドを選択して作成します

こんな感じで作成できれば OK です

あとは左メニューから「Discovery」を選択し先程アクセスしたログが出ていれば OK です

最後に

docker を使ってサクっと efk 環境を構築する方法を紹介しました
Kibana の挙動などを確認したいときに便利かなと思います

今回は nginx のログを格納していますがアプリがあればそれでも OK です
また format も使っていないので生ログの文字列をそのまま格納しているので適宜 format を使って必要なフィールドごとに格納すると良いかなと思います