耳をふさがないワイヤレスイヤホン

耳をふさがないワイヤレスイヤホン

耳をふさがないワイヤレスイヤホン、買い増しして三つになったので感想を並べてみる。

  • よく聞く音楽はクラシック。交響曲、ピアノ
  • 音源はCDをiPhoneにコピーしたもの(Xtrainerzは単体で使用可能)
  • ウォーキングの時に使用
    • 屋外のウォーキングのため音漏れはあまり気にしていない。
  • 聴力にやや難あり。健康診断で高周波が聞こえなくて引っかかる。
  • 耳垢がジクジクして湿っている体質で、カナル型だと疲れたり、耳が蒸れてトラブルになりやすいため、耳をふさがない方式のものを愛用している。

Shokz Xtrainerz

  • 骨伝導。頭の中で音が聞こえる感じがする
  • Bluetooth接続ではなく、これ単体で音楽を保存して再生できる
  • 音質が豊かってことはないけど、屋外では一番聞きやすいかも。骨伝導のせいだろうか

オーディオテクニカ ATH-CC500BT

  • 骨伝導という方式を謳っている
  • 音が豊かに響く。音質は一番いいかな。多少、不自然にこもったように感じる時もあるけど。(イコライザー設定はOriginal)
  • 音量を最大にしても、屋外では不足気味に感じる。風の強い日や交通量の多い道路などでは聞こえにくい。これが一番の欠点。

Victor HA‐NP50T

  • nearphonesといって、骨電動ではなく、耳の穴の近くで音を出しているみたい
  • この中では一番音質は落ちるかも。響きとかつややかさみたいなものがない感じ
    • アプリで音質をBASSモードにするとややましになる
  • 音量はATH-CC500BTよりあるけど、骨伝導でないせいか、周囲の音の影響を受けやすい。風の強い日や交通量の多い道路などでは聞こえにくい。
  • タッチボタンが敏感すぎて操作性が悪い

memo

Thunderbird 115 と tbkeys アドオンのキーバインドのカスタマイズ

Thunderbird 115 と tbkeys アドオン

Thunderbird 115 は割と大きなアップデートだったようで、アドオンの tbkeys が一部動かなくなってしまった。 tbkeys は キーバインドのカスタマイズができて、便利なアドオン。

私の環境で影響があったのは2点。

画面スクロール系

以下の公式ドキュメントに書いてあるが、Thunderbird 115以降は設定変更する必要がある。

https://github.com/wshanks/tbkeys#common-key-bindings

115以降は以下のようになる

window.gTabmail.currentAboutMessage.getMessagePaneBrowser().contentWindow.scrollBy(0, 100)
LoadMsgWithRemoteContent (メッセージのリモートコンテンツを表示する)

こちらは以下のURLのイシューで議論されていた。

[QUESTION] Is there a command for showing remote content? · Issue #136 · wshanks/tbkeys · GitHub

結論から言うと以下のように書けばいいみたい

window.gTabmail.currentAboutMessage.LoadMsgWithRemoteContent()

設定メモ

設定内容を退避しておく

{
    "0": "unset",
    "1": "unset",
    "2": "unset",
    "3": "unset",
    "4": "unset",
    "5": "unset",
    "6": "unset",
    "7": "unset",
    "8": "unset",
    "9": "unset",
    "h": "cmd:cmd_previousMsg",
    "j": "window.gTabmail.currentAboutMessage.getMessagePaneBrowser().contentWindow.scrollBy(0, 25)",
    "k": "window.gTabmail.currentAboutMessage.getMessagePaneBrowser().contentWindow.scrollBy(0, -25)",
    "l": "cmd:cmd_nextMsg",
    "n": "cmd:cmd_nextUnreadMsg",
    "p": "cmd:cmd_previousUnreadMsg",
    "u": "cmd:cmd_toggleRead",
    "d": "cmd:cmd_delete",
    "f": "cmd:cmd_forward",
    "r": "cmd:cmd_reply",
    "a": "cmd:cmd_replyall",
    "c": "func:MsgNewMessage",
    "v": "window.gTabmail.currentAboutMessage.LoadMsgWithRemoteContent()",
    "ctrl+r": "tbkeys:closeMessageAndRefresh",
    "/": "cmd:cmd_toggleQuickFilterBar",
    "b": "unset",
    "m": "unset",
    "o": "unset",
    "s": "unset",
    "t": "unset",
    "w": "unset",
    "x": "unset",
    "#": "unset",
    "]": "unset",
    "[": "unset"
}

memo

memo

自分もまさにこれだったな。 めまいで起き上がったとたんに、横に倒れた。

Thunderbird で最後のタブを閉じても終了しないようにする

Thunderbird で最後のタブを閉じても終了しないようにする

Firefoxと同じ設定がThunderbird にもあった。これで ctrl+Wを連打しても大丈夫。

  • [設定] → [設定エディター]
  • mail.tabs.closeWindowWithLastTab を false にする

Firefox で最後のタブを閉じても終了しないようにする

Firefoxのほうもメモ

  • about:config
  • browser.tabs.closeWindowWithLastTab を false

Windows エクスプローラーで ctrl+D

そういえば、エクスプローラーで ctrl+D でファイル削除っていつからできるようになっていたのだろうか。最近気が付いた。 Windows 10までは、マウスで右クリック→コンテクストメニューの (D) で削除していたんだけど、Windows 11でできなくなった。 要は、ファイルを右手のマウスで選択して、左手のキー操作で削除したいんだよね。 これからは ctrl+D だな。

PrometheusとmtailのNG例

PrometheusとmtailのNG例

Prometheusとmtail-exporterでログ監視をやるときのダメな例

最初に書いておきますが、以下は動かない例です。

要点

  • Prometheusのメトリクスは数値のみで文字列は不可
  • 文字列はラベルに設定できるが、多様な文字列をラベルに設定するのは、時系列DBには適さない
  • ラベルを設定した連想配列のメトリクスは初期化されないため、rate() や delta() などで最初の変化を検出できない

やりたかったこと

  • ログに特定の文字列("error"とか)が出現したらアラートを出す → これは普通にできる
  • そのログの内容もアラートに出したいな → これはできない

やったこと

mtailの設定

counter log_error_count
counter log_error by log_file, log_line limit 1

/(.*)((?i)error)(.*)/ {
  log_error_count++
  log_error[getfilename()][$1 + $2 + $3]++
  del log_error[getfilename()][$1 + $2 + $3] after 1h
}

Prometheusのルール設定

  • 単純なカウンター、連想配列のカウンター、どちらかが増加したらアラートのつもり
  - alert: ErrorLog1
    expr: rate(log_error_count[1m]) != 0
    for: 0s
    labels:
      severity: error
    annotations:
      summary: "Instance {{ $labels.instance }} error log"
      description: "{{ $labels.instance }} of job {{ $labels.job }} error log found."

  - alert: ErrorLog2
    expr: rate(log_error[1m]) != 0
    for: 0s
    labels:
      severity: error
    annotations:
      summary: "Instance {{ $labels.instance }} error log"
      description: "{{ $labels.instance }} of job {{ $labels.job }} error log found."

うまくいかないこと

  • 単純なカウンターはいいけど、連想配列の方はrate() や delta() がうまく動作しない
    • メトリクスが未定義の状態から "1" になっても値の変化が認識されないようだ
    • まったく同じログ(上記の例では、同じログファイル名に、全く同じログの文字列)が出れば、"1" → "2" にカウントアップされるから、それは認識される
    • でもログの文字列にはタイムスタンプが付いていることが多いから、まったく同じ文字列の行のログが複数というのはまぁない

ということで、以上の試みはお蔵入り

参考

  • Is there any plan for prometheus to support string type metrics? · Issue #2227 · prometheus/prometheus · GitHub
    https://github.com/prometheus/prometheus/issues/2227
    • Prometheusのメトリクスは数値だけです。だって文字列はメトリクスじゃないから。ラベルに色々な任意の文字列を入れるのも良くないです。ELKでも使えば?

jq の復習

jq の復習

jq コマンドってやりたいことができたら満足しがちで、基本的な部分を理解しないまま使っているので、少し振り返ってみる。

要はマニュアルを読めばいいのだけど、ちゃんと理解していなかった部分を抜き出してみる。気になっていたのは

  • 英語の単語が使われているものはまだいいんだけど、記号の理解があいまい(フィルター |、カンマ ,、カッコ ()など)
    • 代入(=|=)もなんか癖がありそう
  • デバッグ用の機能

jq はフィルター

ある程度、jq コマンドを使ってからマニュアルの冒頭を読むと味わい深い。

  • フィルターは入力を受け取り、出力を生成する
  • フィルターは組み合わせることができる。パイプでつないだり、フィルターの出力を配列にまとめたり
  • いくつかのフィルターは複数の結果を出力する。配列を次のフィルターに渡すことで、配列の各要素に次のフィルターが適用される。→他の言語ではループやイテレーションで行われることが、jq では大体フィルターをつなげることで実現される
  • すべてのフィルターには入力と出力がある
    • 文字列や数値の定数ですらフィルター。入力を受け取り、通常は自身と同じ値を出力する
    • フィルターを組み合わせる場合、どのフィルターにも同じ入力が適用され、結果が結合される
      • 平均を求める add / length に配列を入力した場合、配列の各要素が addlength に入力されて除算が行われる

Invoking jq / jq の起動

-f filename / --from-file filename:

awk と同じでスクリプトを別のファイルから読み込める。'#' でコメントもかける。 ワンライナーで長いものを書いていると時々、わけわからなくなるので助かるかも

Basic filters / 基本的なフィルター

Comma: ,

2つのフィルターがカンマで区切られていた場合、同じ入力がどちらにも入力されて、それぞれの出力が連結される。 配列の添え字のカンマ区切りもOK。'.[4,2]' とか。

Pipe: |

左側のフィルタの出力を右側のフィルタの入力に供給する

Parenthesis () / 括弧

括弧は、一般的なプログラミング言語と同様に、グループ化演算子として機能します。

Types and Values

Recursive Descent: .. / 再帰的下降

. の配下を再帰的に探索して、すべての値を生成する。これは、引数なしの recurse 組み込み関数と同じです。これは、XPath// 演算子に似せて作りました。 ..a は動かないので、代わりに ..|.a を使用してください。以下の例では、..|.a? を使って、. の「下」にあるオブジェクトのキー "a " のすべての値を検索しています。

..は、path(EXP)? 演算子と併用すると特に便利です。

Builtin operators and functions / 組み込み演算子と関数

empty

何も返さない。nullすら返さないで空を返す。

error(message)

エラーを発生させる(例外をスローする)

halt

jq プログラムを終了

halt_error, halt_error(exit_code)

jq プログラムをエラー終了

String interpolation: \\(exp) / 文字列への差し込み

文字列リテラルの中に動的な値を入れ込む

Conditionals and Comparisons / 条件と比較

Alternative operator: // / 代替演算子

falsenull だった場合に代わりの値/デフォルトを設定

Error Suppression / Optional Operator: ? / エラー抑止/オプションの演算子

エラーが出ても無視される。try を付けるのと一緒。

Advanced features / 高度な機能

Variable / Symbolic Binding Operator: ... as $identifier | ...

変数宣言

Destructuring Alternative Operator: ?// / 構造分割の代替演算子

オブジェクトの構造が何パターンかあるうちの一つを取る場合に、構造の変化に対応できる。 マニュアルに書いてあるのは、あるAPIが一つのオブジェクトの場合はオブジェクト、複数のオブジェクトの場合はオブジェクトの配列を返すような場合の対応に使えるとある。

Scoping

変数名や関数名のスコープについて説明あり(必要になったら見る…)

I/O

input

一つの入力を出力する

inputs

残りの入力をすべて出力する

debug, debug(msgs)

stderr にメッセージを出力。

stderr

入力をstderrに出力

input_filename

処理中のファイル名

input_line_number

行番号

Assignment / 代入

他の言語と異なり、jq は参照とコピーを区別しません。二つのオブジェクトまたは配列は、それらが同一のオブジェクトか違うオブジェクトかという概念とは無関係に、同じであるか/同じではないかのどちらかになります。

.bar = .foo と代入した後に、.foo に何かを加えたとしても、.barは大きくなりません。他の言語に慣れているなら、jq は代入の前にfull deep copy をやっていると考えていいでしょう。(性能の観点から実際にはfull deep copy をやっているわけではありませんが、概念的にはそう考えることができます。)

これは、jq で循環値 (最初の要素がそれ自体である配列など) を構築することは不可能であることを意味します。これは非常に意図的なもので、jq プログラムが生成するものはすべて JSON で表現できるようにしています。

jq のすべての代入演算子の左側 (LHS: left-hand side) にパス式があります。右側 (RHS: right-hand side) は、LHS パス式で指定されたパスに設定する値を指定します。

jq の値は常に不変(immutable)です。内部的な動作では、代入は、リダクション(reduction) 1 を使用して、必要なすべての代入が . に適用された新たな置換値を計算し、更新された値を出力します。これは、次の例を見ると明らかになるでしょう。{a:{b:{c:1}}} | (.a.b|=3), . これは、 {"a":{"b":3}}{"a":{"b":{"c":1}}} を出力します。最後の部分式 . が変更された値ではなく元の値を参照するためです。

ほとんどのユーザーは、単純な代入=ではなく、|=+=などの更新代入を使用することになるでしょう。

代入演算子の左辺は . の中の値を参照することに注意してください。したがって、 $var.foo = 1 は期待どおりに動作しません ($var.foo.における有効または有用なパス式ではありません)。代わりに以下を使用してください $var | .foo = 1

次の点も注意してください。.a,.b=0 では .a.b は設定されず、両方を設定するには(.a,.b)=0となります。

Update-assignment: |= / 更新代入

|= は「更新」演算子です。これは右辺にフィルタを取り、古い値をこの式に通すことで、. のプロパティの新しい値を計算し、代入します。 たとえば、(.foo, .bar) |= .+1 では、入力fooをプラス 1、入力barをプラス 1したフィールドを持つオブジェクトを構築します。

左側には任意の一般的なパス式を使用できます。path()を参照してください。

代入演算子の左辺は . の中の値を参照することに注意してください。したがって、$var.foo |= . + 1 は期待どおりに動作しません ($var.foo.における有効または有用なパス式ではありません)。代わりに以下を使用してください $var | .foo |= . + 1

右側が値を出力しない場合 (例:empty)、左側のパスは del(path) と同様に削除されます。

右側が複数の値を出力する場合、最初の値のみが使用されます (互換性に関する注意: jq 1.5 以前のリリースでは、最後の値のみが使用されていました)。

Arithmetic update-assignment: +=, -=, *=, /=, %=, //= / 算術更新代入

更新代入と同様になる。例えば、 += 1|= . + 1 と同じ。

Plain assignment: = / 単純代入

これは単純な代入演算子です。他とは異なり、右側 (RHS) への入力は、左側 (LHS) への入力と同じであり、左辺のパスの値が入力ではありません。RHS によって出力されるすべての値が使用されます。

=の右辺が複数の値を生成する場合、jq はそれぞれの値ごとに左側のパスをその値に設定し、変更された . を出力します。例えば、たとえば、(.a,.b) = range(2){"a":0,"b":0} および {"a":1,"b":1} を出力します。

次の例は =|= の違いを示します: 入力データ {"a": {"b": 10}, "b": 20} に以下を行います。

      .a = .b
      .a |= .b

前者は入力の a フィールドに、入力の b フィールドをセットします。結果は {"a": 20, "b": 20} です。 後者は入力の a フィールドに、入力の a フィールドの中の b フィールドをセットします。結果は {"a": 10, "b": 20} です。

Complex assignments / 複雑な代入

jq の代入の左側では、ほとんどの言語よりも多くのものが許可されます。左側で単純なフィールド アクセスをすでに見てきましたが、配列アクセスも同様に機能することは驚くことではありません。

     .posts[0].title = "JQ Manual"

驚かれるかもしれませんが、左側の式は入力ドキュメント内の異なる点を参照して複数の結果を生成する可能性があることです。

    .posts[].comments |= . + ["this is great"]

この例では、入力内の各投稿の「comments」配列に文字列「this is great」を追加します (入力は投稿の配列であるフィールド「posts」を持つオブジェクトです)。

jq が「a = b」のような代入に遭遇すると、a の実行中に入力ドキュメントの一部を選択するために取られた「パス」を記録します。このパスは、割り当ての実行中に変更する入力の部分を見つけるために使用されます。等号の左側では任意のフィルターを使用できます。入力から選択されたパス全てが代入の実行場所になります。

これは非常に強力な操作です。上記と同じ「ブログ」入力を使用して、ブログ投稿にコメントを追加したいとします。今回は「stedolan」さんの投稿のみにコメントしたいと思います。「select」関数を使用して、これらの投稿を見つけることができます。

      .posts[] | select(.author == "stedolan")

この操作によって提供されるパスは、「stedolan」さんが書いた各投稿を指しており、前と同じ方法で各投稿にコメントできます。

      (.posts[] | select(.author == "stedolan") | .comments) |=
          . + ["terrible."]

セミコロン ;

マニュアルにはセミコロンについて書いてない。イシューも出ているようだ。

セミコロンは、import、include、def関数本体の終端や、2つ以上の引数を持つ関数の引数の区切りに必要。


  1. ちょっと reduction がうまく訳せませんでした。