<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title>watermint.org</title> <link href="https://watermint.org/atom.xml" rel="self"/> <link href="https://watermint.org/"/> <updated>2025-11-27T00:21:43+09:00</updated> <id>https://watermint.org</id> <author> <name>Takayuki Okazaki</name> <uri>https://github.com/watermint</uri> </author> <rights>(c) 2005-2025 Takayuki Okazaki</rights> <entry> <title>JJUG CCC 2025 Fall - Lightning Talkで話してきました</title> <link href="https://watermint.org/2025/11/26/jjugccc-lt/"/> <updated>2025-11-26T00:00:00+09:00</updated> <id>https://watermint.org/2025/11/26/jjugccc-lt</id> <content type="html">&lt;p&gt;先日JJUG CCC 2025 Fallに参加してきました。また今回は十数年ぶり(記録が正しければ最後は2013年のJJUG CCC 2013 Fall)にLightning Talk (LT)で話をしてきました。 LTではあまり時間がありませんでしたので、その内容をこのブログで少し注釈付きで紹介したいと思います。&lt;/p&gt; &lt;p&gt;ITの話題で言うと、2025年はまさにAIエージェントの年と言っていいと思います。流れに乗って4月ごろからだったとおもいますがCursorやClaudeなどさまざまなエージェントを試し始めましたが、 エージェントに課金をする一方、まだ一円の儲けにもなっていません😅&lt;/p&gt; &lt;p&gt;いまはだいたい週に5億トークンぐらい使ってコードを書いていますので比較的ヘビーユーザーだと自認しています。 ですからエージェントにせめてたくさん働いてもらおうと言うのがこのLTでのテーマです。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00001.jpg&quot; alt=&quot;Title slide - AI should earn money for us&quot; /&gt;&lt;/p&gt; &lt;p&gt;AIエージェントを使うようになってから劇的にコードを多く書くように（書くように指示するように）なりました。 次のスライドはプライベートレポジトリを含むGitHubでのアクティビティを図示したものですが、2025年4月から急激にアクティビティが活発になり、 10月からは劇的にあがっていることがご覧いただけます。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00002.jpg&quot; alt=&quot;GitHubでのアクティビティ&quot; /&gt;&lt;/p&gt; &lt;p&gt;記憶を頼りに大まかに自分が今まで使ってきたプログラミング言語の変遷をまとめてみました。 これを見ると、Javaをやっていたのは15年前ぐらいがピークで、JJUGでの登壇が最後13年前というのもよくわかります。 そして最近はRustおよびSwiftを書いていますが、Swiftに至ってはこの登壇時点でまだ経験は6週間ほどです。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00003.jpg&quot; alt=&quot;これまで利用してきた主なプログラミング言語の変遷&quot; /&gt;&lt;/p&gt; &lt;p&gt;次のグラフは&lt;a href=&quot;https://github.com/watermint/&quot;&gt;個人GitHubレポジトリ&lt;/a&gt;をすべてcloneして、言語ごとにコード行数をカウントしたものです。 このレポジトリに仕事としてつくったものは含まれていませんので、実際に関わったものという意味ではJavaやPHPなどはもっと多いのですが、直近12〜13年程度は仕事では ほぼコードを書いていませんので概ね12〜13年の傾向としては、自分の作ったコード全体を示しているものと考えられます。&lt;/p&gt; &lt;p&gt;それでも、わずか6週間ほどで作ったSwiftによるコードが他のどの言語のものよりも多くなっています。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00004.jpg&quot; alt=&quot;個人GitHubレポジトリのコードを言語ごとに集計&quot; /&gt;&lt;/p&gt; &lt;p&gt;これはもちろん、AIエージェントの登場によるものです。&lt;/p&gt; &lt;p&gt;Rustもグラフでは20万行弱ありますが、おおよそ18万行程度はAIエージェントによるものです。 9年ほどかけて作ったGoのプロジェクトを数週間で軽く抜き去るようなインパクトがありました。 コード行数だけでなく、コミット数など他の指数をみても同様に圧倒的な差があります。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00005.jpg&quot; alt=&quot;AI エージェントやアシスト&quot; /&gt;&lt;/p&gt; &lt;p&gt;AIエージェントでなんでも！と言いたいところですが、AIエージェントがまだ持っていない機能もあります。&lt;/p&gt; &lt;p&gt;AIエージェントとプログラマーを比べれば、コードの記述や調べ物に関してはもう人間のプログラマーでは太刀打ちできない性能差があると言っていいと思います。&lt;/p&gt; &lt;p&gt;設計能力、複数コンポーネントをまたがる複雑なワークフローといったまだまだAIエージェントでは対応が難しい分野もありますがある程度仕様が決まったコードを書く、テストを書くといったときに、 もはやAIエージェントを使わないという選択肢を取れないレベルの経済的差が生まれています。&lt;/p&gt; &lt;p&gt;調べ物もそうですね。Web検索も複数あっという間に実施してくれますし、MCPなどを通じて社内レポジトリを検索ということもできますから、人間が一つずつ検索サービスにいくつか キーワードを試行錯誤するということと比べると比べ物にならないスピードの違いがあります。&lt;/p&gt; &lt;p&gt;これも同様に行間を深く読み解くといったようなことはAIエージェントには難しいですが、API仕様など意味がはっきりしたものを調べて、次のタスクに反映するということでは人間には勝ち目はないぐらいの違いがあります。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00006.jpg&quot; alt=&quot;AIエージェントとプログラマーの対比(1)&quot; /&gt;&lt;/p&gt; &lt;p&gt;一方で、道具の面で見てみるとAIエージェントとプログラマーでは執筆時点においてまだ大きな差があると思います。 ひとつはリファクタリング機能の利用(名前の変更や、安全な削除、メソッドシグネチャの変更など)やデバッガの利用です。&lt;/p&gt; &lt;p&gt;リファクタリングでたとえば、クラス名を変えたいという時に、AIエージェントはsed/awkなどのコマンドを使いテキストを置換で編集しようとします。 これはかなり誤操作も多く、同じクラス名/変数名で別パッケージ/文脈のものも書き換えてしまったり、継承などにより名称が異なる実装には変更が波及できないと言った課題があります。&lt;/p&gt; &lt;p&gt;デバッガも同様で、AIエージェントにできるのはprint()を使って変数をダンプするという操作を追記するぐらいです。 圧倒的に非効率ですしコードベースがprint()だらけになって汚染されてしまいます。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00008.jpg&quot; alt=&quot;AIエージェントとプログラマーの対比(2)&quot; /&gt;&lt;/p&gt; &lt;p&gt;実際に、Pythonやsedを使って一括置換されてコードベースが大きく破壊されてしまったこともあるので、 「Pythonを使ってソースコード編集しないで」とプロンプトに入れていても、AIエージェントはPython大好きですので😅 やらないでといった直後にもPythonで一括置換を試みてくれたりします。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00009.jpg&quot; alt=&quot;実際のやり取り - エージェントはPythonを使おうとする&quot; /&gt;&lt;/p&gt; &lt;p&gt;これはプロンプトの作り方の問題もあります。人とコミュニケーションするときには、 理由や背景の説明、誤解を丁寧に解消すると言った努力が大事です。 また「もっとこうすればいいよ」というような相対的な表現を使うことが多いです。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00010.jpg&quot; alt=&quot;人とのコミュニケーション&quot; /&gt;&lt;/p&gt; &lt;p&gt;正確性に欠けますがイメージとしてAIの動きを説明します。 LLMなど多くのAIはプロンプトをベクトルに変換して処理します。プロンプトで指示されたベクトルに続く答えを生成していると考えてください。&lt;/p&gt; &lt;p&gt;この前提において、AIとの会話において相対的な表現は危険な場合があります。 たとえば東京駅から新宿駅に移動したいとします。 東京駅からみて新宿は西方面ですから、「もう少し西の方に移動してください」というようにプロンプトで指示したとします。&lt;/p&gt; &lt;p&gt;これらのプロンプトはすべてベクトルに変換されて方向と強さが決められるのですが、 時間や空間の把握は現在のAIはあまり得意ではありませんので、「どのぐらい西なのか」という距離をうまく把握できていません。&lt;/p&gt; &lt;p&gt;これまでの会話の中で大阪から東京にきて、そこから新宿に行こうとしている会話の中で「もう少し西へ」といったら極端な話、 「大阪は東京の西にある」という連想から「大阪経由で新宿に向かいます」と突拍子も無い答えを返してしまう場合があります。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00011.jpg&quot; alt=&quot;AIとのコミュニケーション - もう少し西へと指示し大阪経由で新宿へと答えるAI&quot; /&gt;&lt;/p&gt; &lt;p&gt;前述のような誤解を避けるために、AIに指示を出す時には絶対的な指示をしなるべく余計な情報を与えないことが肝心です。 東京駅から新宿駅の例でいば、「現在地を確認して新宿に移動してください」という指示をだせば、AIがいま東京駅にいるということを認識していなくても、 ではまず現在地を調べようというタスクを作って実行してから次のタスクに移ってくれますから、意図した通りに動作しやすいです。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00012.jpg&quot; alt=&quot;AIとのコミュニケーション - 指示を絶対的なものにする&quot; /&gt;&lt;/p&gt; &lt;p&gt;大規模言語モデル(LLM)などのAIではコンテキストと呼ばれるメモリ領域のような働きの機能があります。 このコンテキストの節約は非常に重要です。特に指示はなるべく短く簡潔に、誤解のないように磨き上げる必要があります。&lt;/p&gt; &lt;p&gt;AIエージェントで使われる言語モデルだと25万〜100万トークンといったかなり大規模な領域がありますから、いろいろと節約しなくても良いと考えられるかもしれません。 しっかりと閾値を検査しているわけではないのですが、コーディング規約などAGENTS.mdのようなエージェントへの指示書で20〜30程度のルールを定義していても、 実際に覚えているなと感じるのは数個程度ではないかと思っています。 ですから、コードはたくさん読み込めるのですが、それを行動に移す時にはかなり限定的なルールを複数回に分けて指示する方が確実です。&lt;/p&gt; &lt;p&gt;たとえば(1) DDDをつかって設計し、(2) すべてイミュータブルに書いて欲しい、(3) print()は使わず規定のロガーを使って欲しい。 という場合があったとします（実際にはこれが数十個必要になりますが例のため3つにしておきます）。&lt;/p&gt; &lt;p&gt;(1), (2), (3)を一度に指示した時には一つぐらい忘れてしまって、print()をたくさん生成してしまうということは頻繁に発生してしまいます。&lt;/p&gt; &lt;p&gt;これを設計を(1)を使って実行してください。(2)を使って実装してください、(3)のようにprint()がないか確認してください。と3回に分ければかなり精度良く思った通りのコードに近づきます。&lt;/p&gt; &lt;p&gt;また意図しないベクトルが入らないように雑音となるような指示をなるべく避けるといいでしょう。 たとえば「XXXはやらないで」といったような否定的な表現よりは「YYYしてください」という表現で肯定的にする方が経験上うまくいきます。&lt;/p&gt; &lt;p&gt;そして最後に人とのコミュニケーションでは相対的にしていたコミュニケーションを、AI向けには冗長でも絶対的コミュニケーションにすると良いでしょう。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00013.jpg&quot; alt=&quot;AIとのコミュニケーション - 絶対的ゴールの提示&quot; /&gt;&lt;/p&gt; &lt;p&gt;AIへの指示ですが、AIが意図通り指示を解釈してくれないとイライラしてしまいます。 そうしたイライラをさけるには、定型文を準備しておくと良いでしょう。&lt;/p&gt; &lt;p&gt;定型文を選ぶだけですから感情を入れずに指示できますし、何より何度も同じような指示を書くのは面倒なので定型文＋少し微修正というやり方がいいです。&lt;/p&gt; &lt;p&gt;スペック駆動開発をされている場合は、スペック駆動のワークフローを説明するプロンプトをいくつか局面ごとに準備しておくといいでしょう。 今試しているのはDESIGN (デザインドキュメントだけ書く) -&amp;gt; PLAN (タスク一覧と依存関係だけを書く) -&amp;gt; CRAFT (コードを書いたりテストを実施) という3つのフェーズで回すスプリントサイクルです。&lt;/p&gt; &lt;p&gt;定型文としてはDESIGN -&amp;gt; PLANへ移ってくださいというような指示や、PLAN -&amp;gt; DESIGNに戻ってくださいというような指示をそれぞれ指定してあり、意図しないコードが生成された時にはすぐにDESIGNに戻って設計を修正するなどしています。&lt;/p&gt; &lt;p&gt;さらには現状のこのルールを守って続けてくださいというプロンプトも非常に便利で良く使います。たとえば、DESIGNフェーズならデザインを続けてください。というような指示です。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00014.jpg&quot; alt=&quot;定型文を使って効率アップ&quot; /&gt;&lt;/p&gt; &lt;p&gt;MCPを使えばリファクタリングやデバッガの利用などできそうですが、この発表時点ではまだ実験的で安定的にできそうな印象でしたのでこれはまだもう数ヶ月待たなければいけないかもしれません。 ですので、AIエージェントを使った開発はまだ十分モダンな開発ツールを使いこなせている生産性の高い状態とは言い難い状況です。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00015.jpg&quot; alt=&quot;AIエージェントはまだprint()の挿入、デベロッパーは開発ツールのデバッガでトレースなど実施&quot; /&gt;&lt;/p&gt; &lt;p&gt;このため今実施している直近の開発プロジェクトではログの強化を先に実施しています。 これはGoで書いていた&lt;a href=&quot;https://github.com/watermint/toolbox&quot;&gt;watermint toolbox&lt;/a&gt;の時から利用している手法で、AIエージェントに限らず人間がログを見る時にもかなり有効でした。&lt;/p&gt; &lt;p&gt;構造化されていないログたとえば、次のようなログがあったとします。基本はgrepなどを使って、WARN, ERRORといったエラーレベルで絞り込み、モジュール名などで絞り込んでいきますがなかなかに冗長ですし、特定条件のログだけを抽出するというのは意外に面倒です。 タイムスタンプについても、2025-11-26 22:15:02〜2025-11-26 22:15:15区間のみ欲しいというような取得も難しいです。&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2025-11-26 22:15:01,234 INFO com.example.app.Main - Application started. version=1.2.3 2025-11-26 22:15:02,017 DEBUG com.example.service.UserService - Loading user. id=42 2025-11-26 22:15:02,056 WARN com.example.service.UserService - User not found. id=42 2025-11-26 22:15:02,059 ERROR com.example.web.UserController - Failed to handle request /users/42 &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Goで書いていた&lt;a href=&quot;https://github.com/watermint/toolbox&quot;&gt;watermint toolbox&lt;/a&gt;のログは今では独自実装を使っていますがもともとは、Uberが開発している&lt;a href=&quot;https://github.com/uber-go/zap&quot;&gt;uber-go/zap&lt;/a&gt;のAPIを参考にしています。&lt;/p&gt; &lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;info&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1732636801.234&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;logger&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http.server&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;msg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;request received&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/api/v1/users/42&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;request_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;8c1d7e0f4d8b4e1b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;remote_ip&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;203.0.113.10&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;user_agent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Mozilla/5.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;latency_ms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;debug&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1732636801.245&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;logger&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;service.user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;msg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;loading user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;user_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cache_hit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;trace_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a7c9b3b9c2f9404e8e3f0f5c2c716c3c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;warn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1732636801.251&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;logger&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;service.user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;msg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;user not found&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;user_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;USER_NOT_FOUND&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;trace_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a7c9b3b9c2f9404e8e3f0f5c2c716c3c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1732636801.260&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;logger&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http.handler&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;msg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;failed to handle request&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/api/v1/users/42&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;request_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;8c1d7e0f4d8b4e1b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;user not found_ &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;JSON形式で上記のようなログになっていれば、jqなどのコマンドを使い warn, error のタイムスタンプ 1732636800〜1732636815 区間のみ取得というのもさほど難しくありません。 クエリを自分で作るのはなかなかに大変ですがAIエージェントまたはAIチャットに聞けばすぐに作ってくれます。&lt;/p&gt; &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;app.log &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; | jq &lt;span class=&quot;s1&quot;&gt;&apos;select( .ts &amp;gt;= 1732636800 and .ts &amp;lt;= 1732636815 and (.level == &quot;warn&quot; or .level == &quot;error&quot;) ) | {ts, level, msg, user_id, error_code, trace_id}&apos;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;既存のログAPIでもし同様のことができるのであれば、かなりおすすめです。&lt;/p&gt; &lt;p&gt;ログフレームワークの変更や改変はかなり大変ですから、もし今のログフレームワークに追加的な設定で同等の出力ができるのであればという条件付きにはなりますが、非常にパワフルです。 なぜなら、これによりAIにログの検索からすべてお願いできるからです。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00016.jpg&quot; alt=&quot;ログを強化して、単純なメッセージではなくJSONなど構造化し文脈を保存できるようなログに変更&quot; /&gt;&lt;/p&gt; &lt;p&gt;もしこれから新しくプロジェクトを始められたりする計画がある場合は、ログフレームワークもAI向けに設計し直されてからでもいいと思います。&lt;/p&gt; &lt;p&gt;実際に手元のプロジェクトではそのようにしました。ログになるべくトレースに近い文脈が残るようにAPIを設計し、AIエージェントにはそのログAPIを文脈が残るように使いなさいと指示を出します。 プログラマーが書くにはかなり冗長で面倒なコードですが、AIエージェントに全て書いてもらうのであれば面倒臭さはありませんし、なによりAIにログを見てもらうときの精度が上がるのはとてもお得です。&lt;/p&gt; &lt;p&gt;AIエージェントがデバッガーを使える時代になっても、デバッガーが接続できない環境やシチュエーションというのは幾つでもありますから、ログの充実は今後かなり効いてくると思います。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00017.jpg&quot; alt=&quot;AIエージェントに細かなコーディングは任せ人がコードを書かないので面倒なAPI設計でも許容できる&quot; /&gt;&lt;/p&gt; &lt;p&gt;前述の通り、jqなどのツールを使ってログを検索してもらうようにし、AIにはそのコマンドを使うように指示すればあとは複雑なクエリでもすぐに作ってくれます。&lt;/p&gt; &lt;p&gt;手元のプログラムではCLIアプリの場合、CLIフレームワークにログ取得およびログクエリフレームワークを取り込みjq互換のクエリを実行できるようにし、さらにプログラム終了時にはこのUUIDでクエリできるよと説明までつけているので、 AIエージェントにエラー訂正を頼むときはその行を含む出力をコピペすれば良いだけですから指示の手間が大幅に省けます。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00018.jpg&quot; alt=&quot;AIエージェントに細かなコーディングは任せ人がコードを書かないので面倒なAPI設計でも許容できる&quot; /&gt;&lt;/p&gt; &lt;p&gt;このようにいくつかテクニックを適用していけばAIエージェントを使った開発はストレスなくスムーズにすすめられます。 まだまだたくさんの改良を施して、またAIエージェントを開発している各社の進展を見守りながら最も効率の良い開発を目指して、AIエージェントに支払っている費用が回収できるように使いこなしたいですね。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-11-26-jjugccc-00019.jpg&quot; alt=&quot;Happy Hacking!&quot; /&gt;&lt;/p&gt; &lt;p&gt;それではHappy Hacking!&lt;/p&gt; &lt;p&gt;発表の機会をくださったJJUG運営の皆さん、LTに参加して発表を聞いてくださった皆さん、いつも&lt;a href=&quot;https://yujisoftware.hatenablog.com/entry/jjug_ccc_2025_fall&quot;&gt;JJUGの発表資料をまとめてくださるYujiSoftwareさん&lt;/a&gt;ありがとうございました。&lt;/p&gt; </content> </entry> <entry> <title>watermint toolbox開発を振り返る</title> <link href="https://watermint.org/2025/09/18/watermint-toolbox/"/> <updated>2025-09-18T00:00:00+09:00</updated> <id>https://watermint.org/2025/09/18/watermint-toolbox</id> <content type="html">&lt;p&gt;先日&lt;a href=&quot;/2025/09/01/new-chapter/&quot;&gt;Dropboxを退職したこと&lt;/a&gt;は前回のエントリーでご報告しました。 Dropboxに在籍している約10年間で最も長く取り組んだプロジェクトが&lt;a href=&quot;https://github.com/watermint/toolbox&quot;&gt;watermint toolbox&lt;/a&gt;です。 このプロジェクトも取り組んだのは9年ほどになります。 これは主にDropbox for Teamsの管理者向けのCLIツールで、Windows、macOS、およびLinux環境で動作します。&lt;/p&gt; &lt;h1 id=&quot;プロジェクトの終息&quot;&gt;プロジェクトの終息&lt;/h1&gt; &lt;p&gt;&lt;img src=&quot;/images/2025-09-18-watermint-toolbox.jpg&quot; alt=&quot;プロジェクト終息&quot; /&gt;&lt;/p&gt; &lt;p&gt;このプロジェクトも、先日(2025年8月)&lt;a href=&quot;https://github.com/watermint/toolbox/discussions/911&quot;&gt;アクティブな開発終了をアナウンス&lt;/a&gt;しましたとおり、一旦の終息とすることにしました。 にツールが使えなくなることはありませんので、ご利用いただいている皆様はご安心ください。 自身もいまだに週に数回は使うユーザーの一人ですので、重大なセキュリティ上の課題があれば対応しようとは思っています。&lt;/p&gt; &lt;p&gt;プロジェクト終息の理由はいくつかありますが、主な理由は3つほどあります。&lt;/p&gt; &lt;p&gt;ひとつめの理由は自身が利用する理由が減少したことでしょう。先述の通りwatermint toolboxの主な機能はDropbox for Teamsの管理であり、Dropboxを退職したいま、利用するケースは個人用のDropboxを整理整頓したり、データの棚卸しをするために使ったりと限定的です。 ある意味自身のデータ整理のためにまた新しく拡張を作るかもしれませんが、現状それなりに事足りているので拡張を作る可能性も低そうだなと思っています。&lt;/p&gt; &lt;p&gt;ふたつめの理由はGoでのプログラミングに一区切りをつけようと思ったことです。技術的な詳細は後述しますが、AIでコーディングすることが当たり前になった今、よりAIが効率的に使える言語、そしてAIでコーディングしても間違いにくい言語を選択していきたいという技術的視点を持っています。 そのため、100% Goで書かれたこのプロジェクトを続けるのはある種、私のなかでは技術負債にどう立ち向かうかという種類の課題であり、それはこのwatermint toolboxプロジェクトの原点である実験的姿勢とは異なってくるという理解をしているためです。&lt;/p&gt; &lt;p&gt;みっつめの理由は今後10年、20年と自分自身で使い・発展させていくにはフレームワークが不十分だと感じていることと、ある意味、それらフレームワークに必要な要素が実験的過程で概ね目標を果たせたということがあります。 それらの要素はこれも後述しますが新しいプロジェクトの中で発展させていこうと思っています。&lt;/p&gt; &lt;h1 id=&quot;成功したこと&quot;&gt;成功したこと&lt;/h1&gt; &lt;p&gt;プロジェクトの最初のコミットの2026年11月ですが、前身となるいくつかのプロジェクトを含めると2016年6月ごろが開発の始まりです。&lt;/p&gt; &lt;p&gt;必要は発明の母といいますが、このプロジェクトは必要から始まりました。Dropboxチームのメンバーを一括して招待したい、メールアドレスのドメインが変わるので一括変更したい、グループとメンバーの一覧をCSVで取得したい、など。 そんな調子でDropboxチーム向けのコマンドは120以上、個人向けDropboxのコマンドは60以上と、かなり広い範囲のやりたいことをカバーできるようになりました。&lt;/p&gt; &lt;p&gt;成功したと考えている点は次のようなことがあります。&lt;/p&gt; &lt;h2 id=&quot;1-シングル-バイナリでの配布&quot;&gt;(1) シングル バイナリでの配布&lt;/h2&gt; &lt;p&gt;なるべく導入の敷居を下げるために、利用開始前にPythonをインストールしたり、Javaランタイムをインストールする必要のないシングル バイナリ構成で配布できるようにしたこと。このため、当時最も安定していると思えたシングル バイナリでの開発環境であるGoを選びました。結果的にユーザー数も順調に増えましたし、導入の際に他ライブラリとの依存関係がないことから、それらにまつわるサポートQ&amp;amp;Aとも無縁だったことは大きな効果でした。&lt;/p&gt; &lt;h2 id=&quot;2-リリース-プロセス&quot;&gt;(2) リリース プロセス&lt;/h2&gt; &lt;p&gt;リリース プロセス自体もwatermint toolboxのコマンドとして実装すること。国際化対応するために、UI上のメッセージは全てリソースファイルで管理していますが、各言語ファイルでリソースの不足があればビルドが停止するようにしていたり、リリース プロセスをコマンドで自動化したことにより配布バイナリのテスト、Githubでの公開、Webページの公開など一連のプロセスが自動化されましたのでかなりの工数削減と、手戻り防止に効果がありました。&lt;/p&gt; &lt;h2 id=&quot;3-コマンドの内容調整&quot;&gt;(3) コマンドの内容調整&lt;/h2&gt; &lt;p&gt;Dropbox用だけでも180コマンド以上ありますが、それでも足りない要望というのはさまざまあるものです。しかし、コマンドが何かの環境に特化したものになりすぎないように、可能な限り一般化してからデザイン・実装しました。一度あることは二度あるわけで、その時にはまた別の環境で少しだけ違うことをやりたいというリクエストが来ることが多いです。その時、最初に一般化をしていなかったらまた同じようなコマンドを別に作る必要が出てメンテナンスコストがどんどん肥大化していったことでしょう。&lt;/p&gt; &lt;h2 id=&quot;4-ドキュメントの自動生成&quot;&gt;(4) ドキュメントの自動生成&lt;/h2&gt; &lt;p&gt;このプロジェクトに使える時間は限られていました。コマンドごとの利用マニュアルを毎回作る時間もありませんでしたので、途中で各コマンドの最低限のマニュアルが生成されるようにドキュメントを自動生成するプログラムを作成してリリースプロセスに含めました。 これにより、ドキュメントがすべてのコマンドに対して網羅的に生成されるようにしました。ドキュメントに必要な説明があればリリースプロセスがエラーで停止し、どのような説明追加が必要かを教えてくれるようにしました。&lt;/p&gt; &lt;h2 id=&quot;5-ログを重視する&quot;&gt;(5) ログを重視する&lt;/h2&gt; &lt;p&gt;watermint toolboxは非常に多くのログを取得しています。実行した時のオプション、バージョン、OSバージョンなどから、Goのヒープ状態、トレースログ、APIコールまでさまざまあります。なお、それらのログにはたとえばOAuthトークンなど秘匿性の高い情報は自動的にマスクされるようになっており、また、サーバに転送などする機構はありませんのであくまで実行したユーザーのディスク上に格納されるだけですので安全に利用できます。 クラウドといっても、アカウントの状態や利用しているプランなどによって同じAPIを同じパラメータで実行しても期待した結果が得られない場合があります。そのような時に、APIコールすべてのパラメータやエラーを含むログは大きな時間短縮につながりました。&lt;/p&gt; &lt;p&gt;ログを使ってプログラムを自動テストする仕組みも作りましたので、再現テストやバグの再発防止に役立ちました。 ログはかなり早い段階ですべてJSON形式(正確にはJSON Lines)にしました。これにより、APIコールのリトライやパフォーマンスについて統計的分析がとてもやりやすくなりました。特定パラメータが含まれたリクエストの条件のログだけ取得するということも簡単でした。&lt;/p&gt; &lt;h2 id=&quot;6-公式sdkを使わずrestレイヤーを自作したこと&quot;&gt;(6) 公式SDKを使わずRESTレイヤーを自作したこと&lt;/h2&gt; &lt;p&gt;watermint toolboxの最初の頃は&lt;a href=&quot;https://github.com/dropbox/dropbox-sdk-go-unofficial&quot;&gt;Dropbox unofficial Go SDK&lt;/a&gt;を使っていました。Unofficialということもありますが、自作に至った経緯としては制御のやりにくさがきっかけです。 ログ出力、エラー発生時の自動再実行（QoS制御付き）、テスト用のモックコールなどさまざまやりたいことが増えた結果、SDKはもはや足枷でした。 RESTレイヤーを自作したことによりログや再実行などの制御を簡単にできるようになりました。&lt;/p&gt; &lt;h1 id=&quot;改善の余地があること&quot;&gt;改善の余地があること&lt;/h1&gt; &lt;h2 id=&quot;a-ドキュメント生成プロセス&quot;&gt;(A) ドキュメント生成プロセス&lt;/h2&gt; &lt;p&gt;ドキュメントの自動生成は生産性をあげてくれましたが、一方で制約事項もあります。 テンプレートに沿ったドキュメントしか生成できない構造だったので、少しの注釈を入れたり、複数のコマンドについてのユースケースごとの使い分けなどの説明をするドキュメントを作るのにかなり手間がかかり、場合によっては一時的にこのプロジェクト外で別のドキュメントを作って利用したこともあります。&lt;/p&gt; &lt;p&gt;これは、いまならAIを使ったコーディングをすれば、テンプレートの拡張など簡単にお願いできるので、でさほど大きな課題とは言えないものですが、AIなしでテンプレート作成からコード変更、ドキュメント作成とプレビューという一連の流れを実行するのにはかなりの工数がかかりました。&lt;/p&gt; &lt;h2 id=&quot;b-利用拡大のための努力をあまりしなかったこと&quot;&gt;(B) 利用拡大のための努力をあまりしなかったこと&lt;/h2&gt; &lt;p&gt;利用者数はこの9年でおおよそ360〜500アカウント程度(2025-09時点、Dropbox APIのIDが歴史的経緯で複数あるため正確な数字は分かりません)、GitHub starは85 (2025-09時点)とまあまあ悪くない数字ですが、もっと伸びてもいい数字だとも思っています。 利用者拡大のためにやれることはもっとあったとは思いますが、一方であまり増えすぎてサポートの手が回らなくなる心配もありました。&lt;/p&gt; &lt;p&gt;ChatGPTのGPTsなどを使って簡単にAIチャットbotを作れるような時代になりましたので、このサポートに関する問題もいまはもう少し影響が小さいと考えてもいいかもしれません。&lt;/p&gt; &lt;h1 id=&quot;プロジェクトを通じた発見&quot;&gt;プロジェクトを通じた発見&lt;/h1&gt; &lt;p&gt;このプロジェクトは単に役に立つツールを作るというだけでなく自身にとっての技術的な発見も目標の一つでした。&lt;/p&gt; &lt;h2 id=&quot;プログラミング言語&quot;&gt;プログラミング言語&lt;/h2&gt; &lt;p&gt;プロジェクトでGo言語を選んでよかったと思えることは次のようなことです。&lt;/p&gt; &lt;ul&gt; &lt;li&gt;シングルバイナリへの出力の容易さと安定性。&lt;/li&gt; &lt;li&gt;非常に広く強力なエコシステム。&lt;/li&gt; &lt;li&gt;開発ツールのサポート。&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;一方で苦労したのは次のようなことです。&lt;/p&gt; &lt;ul&gt; &lt;li&gt;型の制約が弱く、インタフェースの使い方になれるまでかなりの時間を要した。&lt;/li&gt; &lt;li&gt;リストの和など、基本的なコレクション操作もないため、毎回同じようなコードを書かざるを得ず、隠れたバグを探すのにかなりの時間を使った。&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;このプロジェクト開始時点で一番詳しかったのはやはり一番長く触っているJavaでした。他にはScalaも当時よく使っていたので、強い型でコードを書くことに慣れていました。 この辺りは言語の特徴を把握してからのデザインの慣れでしょう。&lt;/p&gt; &lt;p&gt;ホビープログラムということで、ある程度集中してプログラムをできる時期もあれば、数ヶ月全く触らないという時期もありましたので、そういった環境では深いレベルでのプログラミング言語への理解と、ハイレベルデザインへの適用というのは難しく感じました。 最近一番使っているRustはまだ理解しきれているとは思っていませんが、強い型に慣れている身からするとかなりやりやすい言語だと思います。&lt;/p&gt; &lt;p&gt;どのようなプログラミング言語でいかに実業務レベルの複雑なユースケースを、考えに考え抜いてデザインに適用してきたかという経験値なのかなとも思います。 最近はほとんどCodex, Claude Code, Cursorなど色々AIを使ってコードを書いていますが、やはりどのAIもPythonが得意でPythonで書きたがる。という癖があると思っています。 そういう意味では、人間もAIも経験値を共通的に高いレベルで持っているプログラミング言語を選ぶというのがいいのかもしれませんが、これは今後の研究課題です。&lt;/p&gt; &lt;h2 id=&quot;開発リリースプロセス&quot;&gt;開発・リリースプロセス&lt;/h2&gt; &lt;p&gt;SunやGREEで働いていた頃は、趣味のプログラムもありますが、基本的にプログラムといえばチーム開発でした。 チームで開発するには当然いろいろな決まりごとが必要になります。&lt;/p&gt; &lt;p&gt;今回のプロジェクトは一人で研究的に進めてきたプロジェクトでしたが、チーム開発をしていた経験はかなり役に立ちました。 数ヶ月コードを見ない・触らないという時期があると、バグ修正をしたりコードを拡張しようとしてももはや他人が書いたコードかのようにほとんど忘れてしまっています。&lt;/p&gt; &lt;p&gt;まとまった休みや土日を使ってプロセスを整備するために時間を使ったからこそ、他人が書いたコードに見えるような状況でも開発が滞りなく勧められたのだと思います。 ですから、趣味のプログラムでもプロジェクトのサイズに合わせて最初は軽量でも良いので、リリースプロセスは先に決めて、しっかり自分でそれを守るようにするというのが今後の自分のベストプラクティスになりそうです。&lt;/p&gt; &lt;h2 id=&quot;マニュアルの整備&quot;&gt;マニュアルの整備&lt;/h2&gt; &lt;p&gt;「Done is better than nothing」という言葉があります。これはこのプロジェクトを進めたり、ツールを紹介する中で何度も感じたことです。 マニュアルがあると無いとでは、全く世界が変わります。 開発者の方の中にはせっかく時間をかけてマニュアルを作ったのに、マニュアルを読んでくれないという嘆きも多くあることかと思います。&lt;/p&gt; &lt;p&gt;しかしそれでも、マニュアルがあることによって読もうとしてくれる人がいたり、理解してくれた人が他の人に説明しようとしてくれます。 さらには今ならAIにマニュアルを読み込ませればサポート用のチャットボットも簡単に作れます。&lt;/p&gt; &lt;p&gt;マニュアルの品質が十分で無いなと気になっていたとしても、全くゼロよりは遥かに良いのです。 ですから、新しいコマンドを作る時にコマンドに必要なリソースをすべて準備しなければならないように制約をつけ、マニュアルが自動生成されるようにしたことは自分にとって一番大きな大きな成功だったと感じています。&lt;/p&gt; &lt;h1 id=&quot;最後に&quot;&gt;最後に&lt;/h1&gt; &lt;p&gt;watermint toolboxのDropboxに関するお話を中心にしましたが、Figma、GitHub、Asana、DeepL、Slack、Google Workspaceとさまざまなサービスへの対応もこのプロジェクトでは進めていました。 複数サービスに展開するに当たっても、ここまで紹介した内容が非常に役に立ちました。そういったDropbox以外のサービス向けでも使っていただいた皆様ありがとうございます。&lt;/p&gt; &lt;p&gt;watermint toolboxをこれまで使ってくださった皆様、他の方に紹介してくださった皆様、マニュアルを読み込んでサポートをしてくださった皆様本当にありがとうございました。 冒頭に書きました通り、リリース済みのものはまだ利用できますので引き続き皆様のお役に立つことを願っています。&lt;/p&gt; </content> </entry> <entry> <title>A new chapter</title> <link href="https://watermint.org/2025/09/01/new-chapter/"/> <updated>2025-09-01T00:00:00+09:00</updated> <id>https://watermint.org/2025/09/01/new-chapter</id> <content type="html">&lt;p&gt;(日本語が後に続きます)&lt;/p&gt; &lt;p&gt;As of the end of August 2025, I have left Dropbox after working there for about 10 years. I have been fortunate to work in an exceptional environment with an amazing team, where I have learned a great amount and experienced significant growth.&lt;/p&gt; &lt;p&gt;Given such a fortunate situation, it was a very difficult decision to change my story, not to continue my role at Dropbox, nor to pursue a new job, but to take some time to focus on my own goals.&lt;/p&gt; &lt;p&gt;However, I believe this is something I can only do now, and if I miss this opportunity, achieving my goals could be pushed beyond the horizon. While I am aware that this decision will bring significant short-term pain, after considering the next 10 to 20 years, I have concluded that now is the time to focus on the direction I want to take.&lt;/p&gt; &lt;p&gt;I am very grateful to all of our customers, partners, and team members at Dropbox. I would especially like to express my deepest gratitude and respect to everyone on the team who supported my decision, even though they were sad to see me go.&lt;/p&gt; &lt;p&gt;My driving force has always been to improve my own productivity and that of my team, both during my 10 years at Dropbox and before that.&lt;/p&gt; &lt;p&gt;At my previous company and at Dropbox, I focused on improving team productivity as a manager. I believe I achieved a certain level of success in this area, but at the same time, I realized that it would be difficult to take the next step forward by simply solving problems as they arose.&lt;/p&gt; &lt;p&gt;So for the next while, I plan to focus on building my personal capabilities. This includes my own growth, as well as focusing on several projects I already have in the pipeline.&lt;/p&gt; &lt;p&gt;I hope to continue providing useful information to you all, so please continue to follow my updates if you find them helpful.&lt;/p&gt; &lt;p&gt;Once again, I would like to express my gratitude to everyone I worked with at Dropbox, and especially to the team.&lt;/p&gt; &lt;p&gt;Takayuki Okazaki&lt;/p&gt; &lt;p&gt;2025年8月末をもって約10年働いたDropboxを退職しました。これ以上ないほどすばらしい働く環境、チームに恵まれているなか、多くの学びがあり、多くの成長を経験しました。&lt;/p&gt; &lt;p&gt;これほどの幸運な状況に対して、自ら状況を変えること、しかも、Dropboxでの役割を続けることでもなく、新しい仕事を得るのではなく、自分の目的のためにしばらく時間を使うという決断をしたことは非常に難しい決断でした。&lt;/p&gt; &lt;p&gt;しかしこれは今しかできないことであると思っていますし、今を逃すと自身のゴール達成は予想もできないほど遠い未来になることが予想できました。この決断が少なくとも短期的には大きな痛みを伴うことはわかっていましたが、長い時間をかけて次の10年、20年を考えた時、やはり今自身の思う方向に向かうべきだと結論づけました。&lt;/p&gt; &lt;p&gt;Dropboxでお世話になったお客様、パートナーそしてチームのみなさまには大変感謝しています。特に、私の決断を惜しみながらも支援してくれたチーム全員にとても大きな感謝と敬意を表します。&lt;/p&gt; &lt;p&gt;私の原動力は、Dropboxでの10年間もそうですし、それ以前もそうですが、自分自身やチームの生産性をあげることです。&lt;/p&gt; &lt;p&gt;以前の会社やDropboxではマネージャーとして、チームの生産性を向上させることにフォーカスしていました。これは一定の成功をえたと自分では評価していますし、一方で、この先もう一歩先に進むためには走りながら問題を解決するだけでは難しいことを実感しました。&lt;/p&gt; &lt;p&gt;ですのでこの先しばらくは、個人として一度力を蓄えるつもりです。それは自分自身の成長もそうですし、いくつかすでに計画している自分自身のプロジェクトを推進することを優先します。&lt;/p&gt; &lt;p&gt;引き続きみなさんに有用な情報が提供できればと考えていますので、よろしければ引き続きご愛読いただければ幸いです。&lt;/p&gt; &lt;p&gt;あらためて、Dropboxで関わりのあったすべての方、そして特にチームに感謝いたします。&lt;/p&gt; &lt;p&gt;岡崎 隆之&lt;/p&gt; </content> </entry> <entry> <title>Command line tool for batch output of Figma projects, frames and nodes</title> <link href="https://watermint.org/2023/09/25/figma-export-cli-en/"/> <updated>2023-09-25T00:00:00+09:00</updated> <id>https://watermint.org/2023/09/25/figma-export-cli-en</id> <content type="html">&lt;p&gt;Sometimes you want to export a batch of files created in Figma as a PDF, png or other format. For example, you may want to search for a memo you wrote on a sticky somewhere among several FigJams. This kind of search does not work because FigJam’s search does not look at the contents of the file, although it can search for file names and people.&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2023-09-24-figma-export.jpg&quot; alt=&quot;tbx services figma file export all page&quot; /&gt;&lt;/p&gt; &lt;p&gt;At least you can know which file you wrote it in, so if you export all your project files as PDFs, you can manage the rest. Using the OS’s full-text search of files or cloud storage searches such as Dropbox is a solution. Automation is essential to establish this kind of workflow, as Figma fortunately uses an API to handle these export processes, I have integrated the functionality into my own open source tool &lt;a href=&quot;https://toolbox.watermint.org/ja&quot;&gt;watermint toolbox&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;This tool is a command-line tool, so it may seem difficult for those unfamiliar with it, but once you automate it, you can’t go back, so it’s not too much of a loss to learn it.&lt;/p&gt; &lt;h1 id=&quot;commands-for-figma&quot;&gt;Commands for Figma&lt;/h1&gt; &lt;p&gt;The watermint toolbox provides commands for several supported services, such as Dropbox and Github, to perform the process. Commands for Figma include.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Command&lt;/th&gt; &lt;th&gt;Description&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;/commands/services-figma-account-info.html&quot;&gt;services figma account info&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Retrieve current user information&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;/commands/services-figma-file-export-all-page.html&quot;&gt;services figma file export all page&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Export all files/pages under the team&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;/commands/services-figma-file-export-frame.html&quot;&gt;services figma file export frame&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Export all frames of the Figma file&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;/commands/services-figma-file-export-node.html&quot;&gt;services figma file export node&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Export Figma document Node&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;/commands/services-figma-file-export-page.html&quot;&gt;services figma file export page&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Export all pages of the Figma file&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;/commands/services-figma-file-info.html&quot;&gt;services figma file info&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Show information of the figma file&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;/commands/services-figma-file-list.html&quot;&gt;services figma file list&lt;/a&gt;&lt;/td&gt; &lt;td&gt;List files in the Figma Project&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;/commands/services-figma-project-list.html&quot;&gt;services figma project list&lt;/a&gt;&lt;/td&gt; &lt;td&gt;List projects of the team&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h1 id=&quot;usage&quot;&gt;Usage&lt;/h1&gt; &lt;p&gt;Figma has the following structure from an API perspective.&lt;/p&gt; &lt;p&gt;Team -&amp;gt; Project -&amp;gt; File -&amp;gt; Page -&amp;gt; Frame -&amp;gt; Node&lt;/p&gt; &lt;p&gt;Each is managed by an ID number, and to export a file or page, you first need to know the starting team ID. The team ID can be found in the URL of the link to the team, which is the number following the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;team/&lt;/code&gt; in the address when you select a team in Figma and copy the link.&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2023-09-24-figma-team-id.png&quot; alt=&quot;team_id&quot; /&gt;&lt;/p&gt; &lt;p&gt;Download the &lt;a href=&quot;https://github.com/watermint/toolbox/releases/latest&quot;&gt;latest version of the watermint toolbox&lt;/a&gt;. Extract the downloaded zip file and you will find an executable file called tbx or tbx.exe, which should be placed in an easily accessible location, such as a desktop folder.&lt;/p&gt; &lt;p&gt;Open PowerShell for Windows or Terminal.app for macOS. Once opened, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd Desktop&lt;/code&gt; to the desktop (or the folder with the tbx executable) and execute the command as follows.&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./tbx services figma project list -team-id xxxxxxxxxxxxxxxxxxxxx &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;(replace xxxxxxxxxxxxxxxxxxxxxxxxxxxx with the team ID you have just obtained from the team address).&lt;/p&gt; &lt;p&gt;The first time you run the application, you will be asked to authorise it as follows, so press Enter to launch the browser.&lt;/p&gt; &lt;p&gt;If you are happy, authorise the application. Then, when “Success” is displayed on the screen, authentication is complete.&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% watermint toolbox 122.8.52 ========================== © 2016-2023 Takayuki Okazaki Licensed under open source licenses. Use the `license` command for more detail. Testing network connection... Done Opening the authorization URL: https://www.figma.com/oauth?client_id=XXXXXXXXXXXXXXX&amp;amp;redirect_uri=http%3A%2F%2Flocalhost%3A7800%2Fconnect%2Fauth&amp;amp;response_type=code&amp;amp;scope=file_read&amp;amp;state=XXXXXXXX Please press ENTER to open the auth page on the browser. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;When execution is completed, a list of projects and their IDs will be displayed as follows. If there are a large number of projects, some will be omitted, so please refer to the CSV or xlsx file in the path displayed at the end of the run.&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% ./tbx services figma project list -team-id xxxxxxxxxxxxxxxxxxxxx watermint toolbox 122.8.52 ========================== © 2016-2023 Takayuki Okazaki Licensed under open source licenses. Use the `license` command for more detail. Testing network connection... Done | id | name | |----------|---------------| | xxxxxxxx | Design System | | xxxxxxxx | Workspace | | xxxxxxxx | toolbox | &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Similarly, if you want to get a list of the project’s files, run the following command.&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% ./tbx services figma file list -project-id xxxxxxxx watermint toolbox 122.8.52 ========================== © 2016-2023 Takayuki Okazaki Licensed under open source licenses. Use the `license` command for more detail. Testing network connection... Done | key | name | thumbnailUrl | lastModified | |------------------------|--------------|--------------|--------------| | xxxxxxxxxxxxxxxxxxxxxx | essentials | | | | xxxxxxxxxxxxxxxxxxxxxx | web | | | &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;If you have the required file, get the key of this file and run the following command. This will export the file as a PDF in the folder where you run it. You can also choose to output png, svg or jpg with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-format&lt;/code&gt; option.&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% tbx services figma file export page -key xxxxxxxxxxxxxxxxxxxxxx -path ./ watermint toolbox 122.8.52 ========================== © 2016-2023 Takayuki Okazaki Licensed under open source licenses. Use the `license` command for more detail. Testing network connection... Done 1 page(s) found in the Figma file page 00:02 [==========================================================================] 1/ 1 DONE The command finished: 7.302s &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;This command is per file, but there are also commands to output nodes and to export all files under the team, so you can use them as needed.&lt;/p&gt; </content> </entry> <entry> <title>Figmaのプロジェクトやフレーム、ノードを一括出力するコマンドライン ツール</title> <link href="https://watermint.org/2023/09/24/figma-export-cli/"/> <updated>2023-09-24T00:00:00+09:00</updated> <id>https://watermint.org/2023/09/24/figma-export-cli</id> <content type="html">&lt;p&gt;Figmaで作成したファイルをPDFやpngなどの形式として一括してエクスポートしておきたいことがあります。 たとえば、複数あるFigJamの中でどこかのスティッキーに書いたメモを検索したいとします。 Figmaの検索はファイル名や人の検索はできますが、ファイルの中身までは見てくれないためこういった検索がうまくいきません。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2023-09-24-figma-export.jpg&quot; alt=&quot;tbx services figma file export all page&quot; /&gt;&lt;/p&gt; &lt;p&gt;せめてどのファイルに書いたのかわかればよいので、プロジェクトファイルをPDFとしてすべてエクスポートしておけば後はなんとでもなります。 OSのファイル全文検索やDropboxなどのクラウドストレージの検索を使えば解決です。 こういったワークフローを確立するには、自動化が欠かせません。Figmaでは幸いAPIを利用してこれらエクスポート処理ができるようなので、 自作オープンソース ツールである &lt;a href=&quot;https://toolbox.watermint.org/ja&quot;&gt;watermint toolbox&lt;/a&gt;に機能を組み込んでみました。&lt;/p&gt; &lt;p&gt;このツールはコマンドライン ツールですのでなじみのない方には難しく感じるかもしれませんが、一度自動化してしまうと戻れなくなりますので、習得しておいてもさほど損ではないかと思います。&lt;/p&gt; &lt;h1 id=&quot;figma用のコマンド&quot;&gt;Figma用のコマンド&lt;/h1&gt; &lt;p&gt;watermint toolboxはDropboxやGithubなどいくつかの対応サービスに対して、コマンドを指定して処理を実行するようになっています。 Figma向けのコマンドは次のようなものがあります。&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;コマンド&lt;/th&gt; &lt;th&gt;説明&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/services-figma-account-info.html&quot;&gt;services figma account info&lt;/a&gt;&lt;/td&gt; &lt;td&gt;現在のユーザー情報を取得する&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/services-figma-file-export-all-page.html&quot;&gt;services figma file export all page&lt;/a&gt;&lt;/td&gt; &lt;td&gt;チーム配下のすべてのファイル/ページをエクスポートする&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/services-figma-file-export-frame.html&quot;&gt;services figma file export frame&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Figmaファイルの全フレームを書き出す&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/services-figma-file-export-node.html&quot;&gt;services figma file export node&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Figmaドキュメント・ノードの書き出し&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/services-figma-file-export-page.html&quot;&gt;services figma file export page&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Figmaファイルの全ページを書き出す&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/services-figma-file-info.html&quot;&gt;services figma file info&lt;/a&gt;&lt;/td&gt; &lt;td&gt;figmaファイルの情報を表示する&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/services-figma-file-list.html&quot;&gt;services figma file list&lt;/a&gt;&lt;/td&gt; &lt;td&gt;Figmaプロジェクト内のファイル一覧&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/services-figma-project-list.html&quot;&gt;services figma project list&lt;/a&gt;&lt;/td&gt; &lt;td&gt;チームのプロジェクト一覧&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h1 id=&quot;使い方&quot;&gt;使い方&lt;/h1&gt; &lt;p&gt;FigmaはAPI視点で見ると次のような構造になっています。&lt;/p&gt; &lt;p&gt;チーム -&amp;gt; プロジェクト -&amp;gt; ファイル -&amp;gt; ページ -&amp;gt; フレーム -&amp;gt; ノード&lt;/p&gt; &lt;p&gt;それぞれID番号で管理されており、ファイルやページをエクスポートするにはまず起点となるチームIDを知る必要があります。 チームIDは、チームへのリンクURLから知ることができます。Figmaのチームを選択して、リンクをコピーしたときの、アドレスの &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;team/&lt;/code&gt; に続く数字がチームIDです。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2023-09-24-figma-team-id.png&quot; alt=&quot;team_id&quot; /&gt;&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://github.com/watermint/toolbox/releases/latest&quot;&gt;最新版のwatermint toolbox&lt;/a&gt;をダウンロードします。ダウンロードしたzipファイルを展開するとtbxまたはtbx.exeという実行ファイルがありますので、これをデスクトップフォルダなどわかりやすい場所に置いてください。 Windowsの場合はPowerShell、macOSの場合はTerminal.appを開きます。開いたら &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd Desktop&lt;/code&gt; としてデスクトップ（またはtbxの実行ファイルを置いたフォルダ)に移動して次のようにコマンドを実行します。&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./tbx services figma project list -team-id xxxxxxxxxxxxxxxxxxxxx &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;(xxxxxxxxxxxxxxxxxxxxxは先ほどチームのアドレスから取得したチームIDに読み替えてください)&lt;/p&gt; &lt;p&gt;初回実行時には次のように認証が求められますので、エンターを押してブラウザを起動します。アプリケーションのアクセス範囲など確認が表示されますのでよければ認可します。画面に「Success」と表示されれば認証完了です。&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% watermint toolbox 122.8.52 ========================== © 2016-2023 Takayuki Okazaki Licensed under open source licenses. Use the `license` command for more detail. Testing network connection... Done Opening the authorization URL: https://www.figma.com/oauth?client_id=XXXXXXXXXXXXXXX&amp;amp;redirect_uri=http%3A%2F%2Flocalhost%3A7800%2Fconnect%2Fauth&amp;amp;response_type=code&amp;amp;scope=file_read&amp;amp;state=XXXXXXXX Please press ENTER to open the auth page on the browser. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;実行完了すると次のようにプロジェクトの一覧とIDが表示されます。プロジェクト数が多い場合、一部省略されますので実行時の最後に表示されるパスのCSVやxlsxファイルを参照してください。&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% ./tbx services figma project list -team-id xxxxxxxxxxxxxxxxxxxxx watermint toolbox 122.8.52 ========================== © 2016-2023 Takayuki Okazaki Licensed under open source licenses. Use the `license` command for more detail. Testing network connection... Done | id | name | |----------|---------------| | xxxxxxxx | Design System | | xxxxxxxx | Workspace | | xxxxxxxx | toolbox | &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;同様にプロジェクトのファイル一覧を取得したい場合には、次のコマンドを実行します。&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% ./tbx services figma file list -project-id xxxxxxxx watermint toolbox 122.8.52 ========================== © 2016-2023 Takayuki Okazaki Licensed under open source licenses. Use the `license` command for more detail. Testing network connection... Done | key | name | thumbnailUrl | lastModified | |------------------------|--------------|--------------|--------------| | xxxxxxxxxxxxxxxxxxxxxx | essentials | | | | xxxxxxxxxxxxxxxxxxxxxx | web | | | &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;必要なファイルがあれば、このファイルのkeyを取得して次のようなコマンドを実行します。 これで実行したフォルダにファイルがPDFとしてエクスポートされます。 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-format&lt;/code&gt;オプションで png, svg, jpgの出力も選択可能です。&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% tbx services figma file export page -key xxxxxxxxxxxxxxxxxxxxxx -path ./ watermint toolbox 122.8.52 ========================== © 2016-2023 Takayuki Okazaki Licensed under open source licenses. Use the `license` command for more detail. Testing network connection... Done 1 page(s) found in the Figma file page 00:02 [==========================================================================] 1/ 1 DONE The command finished: 7.302s &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;このコマンドはファイルごとですが、ノードを出力するコマンドやチーム配下のファイルをすべてエクスポートするコマンドも用意してありますので必要に応じてお使いください。&lt;/p&gt; </content> </entry> <entry> <title>趣味のプログラム watermint toolbox と tbx</title> <link href="https://watermint.org/2022/12/31/hobbyists-program/"/> <updated>2022-12-31T00:00:00+09:00</updated> <id>https://watermint.org/2022/12/31/hobbyists-program</id> <content type="html">&lt;p&gt;趣味とある程度の実用性を備えたプログラムとして &lt;a href=&quot;https://github.com/watermint/toolbox&quot;&gt;watermint toolbox&lt;/a&gt;と最近始めた&lt;a href=&quot;https://github.com/watermint/tbx&quot;&gt;tbx&lt;/a&gt;というプロジェクトがあります。watermint toolboxは既に何度か紹介していますが、Dropbox向けのコマンドラインツールとして開発を始め、今はそれ以外にも多様なコマンドを備えるプログラムとして成長しました。執筆時点で、最初のコミットからおおよそ6年（最初のコミットが2016年11月）、toolboxとして集約する前のいくつかのサブプロジェクトも含めると6年半ほどになります。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2022-12-31-wicklow.jpg&quot; alt=&quot;Wicklow&quot; /&gt;&lt;/p&gt; &lt;p&gt;プログラムの仕様想定として、WindowsやmacOSなどの環境で追加ライブラリ等を必要としない、いわゆるシングルバイナリ配布できることを重視していたのでプログラミング環境としてはGoを選択しました。当時も他の言語選択肢はありましたが、Better Cとして名高いこと、開発環境の成熟度（≒IntelliJ Goプラグインの成熟度）、学習環境（Stackoverflowや書籍等の情報源）の充実度を考慮して決めたのだと思います。&lt;/p&gt; &lt;p&gt;この6年間、Goでプログラムを作ってみた成果として「Goらしくプログラムする」こともそれなりに成功したと思っています。フルタイムの仕事としてプログラムをしていたときは、言語仕様を読んだり著名なライブラリ・プログラムの設計解説や、コードを読んだりして自身の設計や開発に適用したものですが、趣味としてプログラミングを継続させようとすると違ったアプローチが必要になります。&lt;/p&gt; &lt;p&gt;趣味として成立させるにはある程度短期的に達成できる成功体験が不可欠と考えています（仕事として成立させるにも重要ですが場合により、必須ではない）。具体的にはプログラミング言語のチュートリアルとしてHello Worldの出力に始まり、ファイル入出力、簡単なモックアップやプロトタイプ作成、少し本格的な設計の取り込み、開発規模拡大に従う課題への対応と進んでいくかと思います。この一つ一つの段階があまり飛躍し過ぎてしまうと趣味としての継続が難しいと考えています。仕事であれば、ある程度段階が飛躍したとしても時間や費用をかけ習得したり、先達の助けを借りてこの飛躍を乗り切ることもできるでしょう。&lt;/p&gt; &lt;p&gt;継続は力なりと言いますが、一方で継続を実現するにはある程度の成功体験を繰り返せるための計画性も必要になってくると思いますし、実際watermint toolbox開発でもそう実感しました。この実感には裏づけとなる失敗・成功を含む別例があります。&lt;/p&gt; &lt;p&gt;結果的には同じようなプログラムを何度も作っているのですが、2013年から2015年にかけて&lt;a href=&quot;https://github.com/watermint/Semester&quot;&gt;Scalaで開発していたプロジェクト&lt;/a&gt;があります。このプログラムはもともとDDDやScala言語の理解を深めるために始めたものですが、ある程度の複雑性がある具体的なユースケースが欲しいと思い日常的に必要な課題解決（チャット操作の効率化やクラウドストレージへのファイルアップロードなど）を実装したものです。DDDやScalaの理解という意味ではある程度進んだのですが、設計がやや壮大過ぎたこともありDDD・Scalaの理解がある程度進んだという最初の（やや曖昧な）ゴールを達成したことで自然消滅的にプロジェクトが終了しました。&lt;/p&gt; &lt;p&gt;当初ゴール達成という意味では成功なのですが、失敗だと思っているのはせっかく2弱年もかけて作ったプログラムがあまり自身の資産になっていないというところが大きな理由です。その点、watermint toolboxではプログラムとしての綺麗さやGo言語の習得という以上に、実用性をより重視して短期的な問題解決を優先したこともあり短期的に成功体験が得られ、より継続的な開発が進められるようになりました。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2022-12-31-dublin.jpg&quot; alt=&quot;Dublin&quot; /&gt;&lt;/p&gt; &lt;p&gt;今年2022年は、コロナ禍がある程度定常的なリスク・コストと認識され仕事上でも出張が再開された年でした。今年は海外へ行く機会があり、長時間フライト中は普段できない考えの整理ができるということでwatermint toolboxについても今後どうするか考えることにしました。&lt;/p&gt; &lt;p&gt;watermint toolboxは趣味と実用という意味ではなかなかの成功を収めたと思っています。これをさらに10年・20年とライフワーク的に開発し、資産として形成するにはどうすればよいか考えました。一つの議論はこのままGo言語で開発を進めるかということです。&lt;/p&gt; &lt;p&gt;Goは手軽さやエコシステムの充実といった意味で非常に優れていると思っています。一方でいくつかの理由によりある程度の大きさのプログラムを保守するのも難しそうだとも感じています。理由をある程度絞ると次の二つが挙げられます。&lt;/p&gt; &lt;p&gt;一つ目は型システムがJavaやScalaなどと比較しあまり充実しておらず、特にインタフェースの設計と実装ならびに保守がなかなか手間がかかることです。Go 1.18では待望のGenericsが導入されましたが、誤解を恐れず言えば適用範囲は限定的で関数の定義をマクロ的に複数型対応にコンパイル時に展開してくれる。という程度のもので、変数の宣言や構造体にGenericsのフィールドを定義できないなど型情報を資産として形成できるほどの機能はありません。このため、たとえばある型の配列から条件に合う値のみを抽出して別の配列を作成するという処理もGoでは毎回forループを書かなければなりません。そのforループにバグがあったりテストを書いたりしなければならないコストは趣味のプログラムには無視できない大きさです。&lt;/p&gt; &lt;p&gt;二つ目はエラー処理です。前述の型とも関連しますが現状のGo Genericsでは Javaでいう&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java.util.Optional&amp;lt;T&amp;gt;&lt;/code&gt;・Scalaでいう&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option[+A]&lt;/code&gt;といったnullに頼らないライブラリ群の構築ができません。Goでのエラー処理は戻り値リストの最後にerrorを返すというのが慣例です。この、errorもerrorインタフェースを実装したポインタということで、毎回型を調べてキャストしたり、別関数で判定したりと統一感もなく注意深くドキュメントを読んだとしてもエラー処理にまつわる不具合を生じやすいことが大きな問題だと感じています。&lt;/p&gt; &lt;p&gt;たとえばファイルが指定パスに存在するかどうかはGoでは次のように判定します。&lt;/p&gt; &lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lstat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/path/to/file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsExist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// 存在する場合の処理&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;このLstatが返すエラーは PathErrorという構造体のものですが、このエラーの詳細を知ろうとする場合は次のようにキャストして調べる必要があります。errorは実際にはどのような型のものかドキュメントやソースを見なければ分からず、Javaでいうところの、全てjava.lang.Exceptionとして例外を扱っているようなものです。議論の余地はあるでしょうけれど、Goで6年プログラムしてみて有益と感じたことはありませんでした。&lt;/p&gt; &lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lstat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/path/to/file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsNotExist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// ファイルが存在しない場合の処理&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PathError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Op[%s] Path[%s] Error[%s]&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Goのエコシステム、プログラミング環境の充実度はすばらしく、たとえば&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/util-qrcode-create.html&quot;&gt;QRコードを作るプログラム&lt;/a&gt;を作りたいなと思ったとき、&lt;a href=&quot;https://github.com/boombuler/barcode&quot;&gt;boombuler/barcode&lt;/a&gt;のようなライブラリがすぐに見つかります。 短期的な成功体験を得るという趣味のプログラムを支えるにはぴったりです。&lt;/p&gt; &lt;p&gt;しかし、10年後に資産となるプログラムという意味では少し言語機能が不足していると感じるのと、ある程度プログラムが大きくなってきた時に駆られる「全部書き直したい」というモチベーションを考慮して並行して新しいプロジェクトを始めることにしました。&lt;/p&gt; &lt;p&gt;新しいプロジェクトを始めるにあたって、6年前と比べればプログラミングの環境も大きく変わったように見えます。GraalVMやKotlin Native、Scala Nativeなどの登場・成熟で実行ファイルのバイナリ配布の敷居が下がり、選択肢が増えました。&lt;a href=&quot;https://www.tiobe.com/tiobe-index/&quot;&gt;TIOBEのプログラミング言語コミュニティ指標&lt;/a&gt;を見てみると、Pythonがこの5〜6年で急成長しトップになり、Javaは今月発表された結果ではついにトップ3から陥落し4位になるなど様変わりしたようです。&lt;/p&gt; &lt;p&gt;新しいプロジェクトの言語をどれにするかは2ヶ月ほど悩んだ結果、Rustを使うことにしました。選定理由はGoではない言語にしようと考えた理由である(1) 型周りが充実していることが最重要で、(2) エコシステムがある程度大きく必要なライブラリが探せること、(3) どうせなら本格的にプログラミングで使ったことのない言語といった理由からです。&lt;/p&gt; &lt;p&gt;趣味のプログラムとしてRustを始めるにあたっては、バランスの問題でwatermint toolboxを始めた頃と比べて違う計画を立てました。watermint toolboxは短期的な成功体験を継続することで成長させてきましたが、新しいプロジェクトで同じことをやると新しいプロジェクト側の方が当然楽しくなってしまい、旧プロジェクトを触らなくなってしまいます。&lt;/p&gt; &lt;p&gt;これを避けるために新プロジェクト側はある程度長期的なゴール設定をし2つのプロジェクトを並行して進めることにしました。watermint toolboxは引き続き短期的な問題解決のために、tbxは10年後を見据えた資産にしていくことに。tbxでは、具体的な実行可能プログラムよりはライブラリ群を最初に整備していくことでRustを習得しつつ、ある程度加速的に開発できる状態までライブラリ群が成熟した段階でwatermint toolboxの機能を逐次取り込み置き換えを目指すというものです。&lt;/p&gt; &lt;p&gt;Rustを習得するにあたっては、いくつか順番をおって実装していくことにしました。まずは文字列操作、続いて&lt;a href=&quot;https://github.com/watermint/tbx/blob/0.2/tbx_essential/src/text/uuid.rs&quot;&gt;UUIDなど今後利用するであろうライブラリ&lt;/a&gt;の実装、乱数など外部ライブラリをラッピングしたライブラリの構築といった順番です。細かくテストできる範囲から実装することで、所有権などRustならではのコンセプトを学びます。既存の優れたライブラリをラッピングすることは漢字の書き取りのような感じで、読むだけでは思いつかないテクニックや設計が学べます。&lt;/p&gt; &lt;p&gt;おそらくこのようなライブラリ群で文字列、数値、時間、KVSやデータベース、ログなどを実装またはラッパーを実装することで学びを深め、1〜2年後ごろから本格的な実装をしていくという予定です。果たしてこのような計画で進めるかはわかりませんが、継続を優先し楽しく来年もコードを書いていきたいと思います。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2022-12-31-dublin2.jpg&quot; alt=&quot;Dublin&quot; /&gt;&lt;/p&gt; &lt;p&gt;皆様もよいお年をお迎えください。&lt;/p&gt; </content> </entry> <entry> <title>watermint toolboxでもっと手軽に負荷モニタリングしたい (ログ取得編)</title> <link href="https://watermint.org/2022/09/18/monitoring-by-watermint-toolbox/"/> <updated>2022-09-18T00:00:00+09:00</updated> <id>https://watermint.org/2022/09/18/monitoring-by-watermint-toolbox</id> <content type="html">&lt;p&gt;色々と検証作業をしていると様々な情報が欲しくなる。プログラムがうまくいかなかったのがCPUを使い切ったからなのか、ネットワーク帯域が足りなかったからなのか大まかには知りたい。&lt;/p&gt; &lt;p&gt;こういう時、まずOSごとに装備されているモニタリングツールが違うのでWindows、macOS、Linuxを混在環境で使っているとこの時点で毎回この差異に気をつかう。データを時系列で残すのも難しくはないが、ちゃんとやるとなかなか面倒だ。かといって、本格的なモニタリングツールの導入は監視サーバを立てたり監視サーバまでのネットワーク疎通はどうしようかと悩んでいると結局そこまで準備するぐらいなら今回は諦めようとなる。&lt;/p&gt; &lt;p&gt;CPU負荷やネットワーク状況、ストレージIOなど欲を言えばIOPSが欲しいとか、毎秒の分解能が欲しいとか、プロセスごとのCPU・メモリ利用が欲しいとか様々欲しくなるが、まずそういった細かな情報よりも最低限の情報がささっと手軽にとれるツールが欲しい。&lt;/p&gt; &lt;p&gt;なければ作れば良いということで、2016年よりコツコツ開発している&lt;a href=&quot;https://github.com/watermint/toolbox&quot;&gt;watermint toolbox&lt;/a&gt;というプロジェクトのコマンドの一つとして実装した。&lt;/p&gt; &lt;p&gt;今回はまだモニタリングして情報収集するところまでで、分析をするツールまでは作っていない。取得するデータはJSON形式データで集まるのでまずはJSON加工ツールや簡単なスクリプトを書いて分析するつもりのため、分析はまた将来的な拡張のお楽しみに。&lt;/p&gt; &lt;h1 id=&quot;概要&quot;&gt;概要&lt;/h1&gt; &lt;p&gt;今回は&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/util-monitor-client.html&quot;&gt;util monitor client&lt;/a&gt;というコマンドとして実装した。 watermint toolboxはマルチプラットホーム対応でWindows、macOS (x64/M1)、Linux (x64/arm64)に対応した&lt;a href=&quot;https://github.com/watermint/toolbox/releases/latest&quot;&gt;それぞれのバイナリを配布している&lt;/a&gt;。シングルバイナリ動作するようコンパイルしてあるので、ほとんどの場合追加ライブラリは必要なく動作する。(なお、Alpine Linuxの場合は&lt;a href=&quot;https://github.com/watermint/toolbox/discussions/641&quot;&gt;TBX on Alpine Linux&lt;/a&gt;を参照)&lt;/p&gt; &lt;p&gt;このモニタリングツールはデフォルトでは10秒間隔で各種統計を取り、1時間に一度ローカルディスクに蓄積してあるデータをDropboxへ同期する。同期完了後にローカルディスクのデータは削除される。データはすべてDropboxのフォルダに集約されるので、監視サーバを立てたり、監視サーバまでの通信経路に気を遣わなくてもインターネットにさえ出られれば良い。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2022-09-18-monitor-files-on-dbx.png&quot; alt=&quot;Dropboxに保存されたデータ&quot; /&gt;&lt;/p&gt; &lt;p&gt;データは自動的に &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;対象名&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yyyy-mm&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yyyy-mm-dd&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tbx-monitor-対象名-UNIXTIME.log.gz&lt;/code&gt; のようなフォルダ・ファイル名でgzip圧縮形式で保存される。また、watermint toolboxがクラッシュしてファイルがアップロードされていない場合は、再度同じように実行すれば次回実行時の同期処理時にすべてのファイルをアップロードする。なお、同期処理前はgzip圧縮が未実施のためローカルディスクの消費は1時間分で500KiB程度のサイズとなる。&lt;/p&gt; &lt;p&gt;ファイルサイズは概ね圧縮後で50KiB (10秒間隔・1時間分)、1日分でおおよそ1.2MB程度になる。これならば数週間といった単位で数台を監視してもさほど気にならないと思う。将来的には分析ツール部分を作る際にTSDBに格納しながらもう少しコンパクトなデータ形式で格納することになると思うが、今のフォルダ・ファイル形式でも分析までの受け渡しとしてはこの程度で良いだろうと思っている。&lt;/p&gt; &lt;h1 id=&quot;使い方&quot;&gt;使い方&lt;/h1&gt; &lt;p&gt;コマンド実行オプションなど詳細は&lt;a href=&quot;https://toolbox.watermint.org/ja/commands/util-monitor-client.html&quot;&gt;util monitor client&lt;/a&gt;を参照いただきたいが大まかな流れは次の通り。&lt;/p&gt; &lt;ol&gt; &lt;li&gt;初回実行時にログをアップロードするDropboxアカウントへの認証を求められる&lt;/li&gt; &lt;li&gt;表示されたURLをブラウザに貼り付け認可&lt;/li&gt; &lt;li&gt;その後表示されるコードをプログラムに貼り付ける&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;認可で取得した認証トークンは $HOME/.toolbox/secrets/secrets.db に格納される。2度目以降の実行ではここから認証トークンを取得するので再認証は不要である。&lt;/p&gt; &lt;p&gt;また、認証トークンを格納するファイルは &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-auth-database&lt;/code&gt; オプションでも指定可能である。たとえば次のように実行すると $HOME/Desktop/secrets.db に認証情報が保管される。&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ tbx util monitor client -auth-database $HOME/Desktop/secrets.db -name MONITOR_NAME -data-path $HOME/.toolbox/monitor -sync-path /monitor &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;これを応用すれば仮想マシンなどで認証操作をとばして監視を実施したい場合でも、認証データファイルと一緒にデプロイすれば良い。当然ながら、認証データファイルはパスワードと同様のものなのでアクセス権限などをしっかり設定することは前提となる。&lt;/p&gt; &lt;h1 id=&quot;長期間モニタリング&quot;&gt;長期間モニタリング&lt;/h1&gt; &lt;p&gt;watermint toolboxはデバッグのために各種ログを自動的に出力するようになっている。これらのログは問題原因を探るには良いのだが、長期間モニタリングするにはストレージ容量を圧迫する懸念があるかもしれない。watermint toolboxは自動的にログローテートし一定サイズ以上にログファイルが肥大化しないように設計しているが、プログラムのクラッシュ等で回収しきれないログが残存する場合もある。&lt;/p&gt; &lt;p&gt;このような場合は次のように &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-skip-logging&lt;/code&gt; オプションを追加すると良い。このオプションにより、ほとんどのログがストレージへ書き込み処理なく実行されるのでログによるストレージ圧迫の懸念がなくなる。&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ tbx util monitor client -auth-database $HOME/Desktop/secrets.db -name MONITOR_NAME -data-path $HOME/.toolbox/monitor -sync-path /monitor -skip-logging &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;まだログの解析部分についてのプログラムがないので、負荷モニタリングという意味では道半分というところだ。今回のモニタリングプログラムについては、&lt;a href=&quot;https://github.com/shirou/gopsutil&quot;&gt;gopsutil&lt;/a&gt;プロジェクトの成果物を利用させていただいたので、ほぼ技術的な困難性はなかった。あえていうと、認証トークンが簡単にデプロイできるよう認証まわりのフレームワークを大きく書き換えたことが最も困難性が高かった。&lt;/p&gt; &lt;p&gt;またいつになるかはわからないが、解析編として解析プログラムが出来上がったときには紹介したいと思う。s&lt;/p&gt; </content> </entry> </feed>