vague memory

うろ覚えを無くしていこうともがき苦しむ人の備忘録

New Relic infrastructure agent の guided install には User key も必要

New Relicinfrastructure agent をインストールする際に license key 無効で失敗したのでメモ。

FATAL could not fetch license key for account 9999999: 401 response returned: Invalid credentials provided. Missing API key or an invalid API key was provided.


結論

記載の手順に沿って実施すれば発生しません。

ガイドを勧めていくと表示されるコマンドを実行する際に既存の API key を利用してインストールしようとした所発生しました。 原因は User key ではなく License key を使っていたからです。

コマンド一撃でインストールできる親切設計ですが、エラーメッセージは若干不親切だと思いました。

以上

guided install について

最初に表示されるのは License key です。

次にインストールコマンドが案内され、こちらを監視対象ホストで実行すると infrastructure agent がインストールされます。 ここで変数 NEW_RELIC_API_KEY が入っていますが、これは User key です。

User Key を作成していない状態で上記の画面まで進めると、自動的に User key が生成されます。手動で User Key を生成する必要がありません。 Installer API Key という名称で User key が作成されていました。

"Customize your installation" の部分で proxy URL の指定を追加できます。

監視対象ホストでコマンドを実行すると、経過状況が出力され、

インストールが完了します。

guided install で設定される License key

インストールが完了すると各設定ファイル群が作成されています。License key は newrelic-infra.yml に設定されます。

$ cat /etc/newrelic-infra.yml
enable_process_metrics: true
status_server_enabled: true
status_server_port: 18003
license_key: *****

ガイドの手順を実行する際に既に複数の License key を持っている場合、 必ずしもガイドの最初に表示されていた License key が設定されるわけではないようです。

明示的に特定の License key にしたい場合は、インストール後に newrelic-infra.yml の書き換えが必要になります。 または、インストールコマンドに環境変数 NRIA_LICENSE_KEY を付与することで指定できるようです。

API Key の種類について

License key

ライセンスキーは、ほぼすべてのデータの報告に使用されます

Browser key

ブラウザキーは、 ブラウザモニタリング データを報告するために使用されます。

Mobile app token

モバイルアプリトークンは、 モバイルモニタリング のデータを報告するために使用されます。

User key

NerdGraph 、データの問い合わせや機能の設定に使用するGraphQL APIを使用する際に、ユーザーキーが必要となります。

上の3つは ingest type (データを New Relic に取り込むためのもの)で、 User key だけ API 利用のためのキーとなっています。

User key が必要な理由

よくよく見ると、インストールコマンドで指定している Key の書式が License key (小文字) ではなく User key (大文字ハイフン有り) です。

また、ガイドの Customize の説明部分に infrastructure agent に加え、CLI もインストールするとの記載があります。 インストールコマンドの URL も newrelic-cli/script.sh となっています。

guided install は New Relic CLI を使用している

New Relic command line interface (CLI) の使用には User key が必要です。

For this guide you just need:

Your New Relic user key. An instrumented application in your New Relic account.

guided install は New Relic CLI で行われるため User key の指定が必要ということになります。

Twingate Headless Client

前回 に続き Twingate ネタです。

Twingate では CI/CD 向けの Headless Client が利用できます。

Linux Headless Mode | Docs | Twingate

CUI で使えるということで試してみます。
GUI同様特に詰まらず、簡単に導入できました。



事前準備

Headless mode requires a Service Key

まず、Service Key が必要になります。 払い出しの方法は公式ドキュメントに記載があります。

Web UI での操作は上記ドキュメント参照です。

今回は API を利用して作成します。 API実行には tg-cli (v0.0.53) を使用しました。
尚、Terraform の Twingate Provider は、現時点で service 関連はサポートしていないようです。

サービスの作成

例として、"test" サービスに2つのリソースを付与して作成します。

#   Usage:   tg service create <name> [resourceNamesOrIds...]

$ tg service create "test" '1.example.com' '2.example.com'

[SUCCESS] New service named 'test' created with id '***' with added resources '1.example.com: *****==' '2.example.com: *****=='

作成したサービスの確認

$ tg service list

┌──────────────────────────────────────────────────────────────────────┬────────────┬───────────┬───────────┬───────────────────────────────────────────────────────┬────────────────┐
│ id                                                                   │ name       │ createdAt │ updatedAt │ resources                                             │ keysTotalCount │
├──────────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────┼───────────────────────────────────────────────────────┼────────────────┤
│ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx │ test       │ 10/15/22  │ 10/15/22  │ 1.example.com, 2.example.co....                       │ 0              │
└──────────────────────────────────────────────────────────────────────┴────────────┴───────────┴───────────┴───────────────────────────────────────────────────────┴────────────────┘

list コマンドで取得できる id をキーの作成時に使用します。

WebUI 上でもTeam 配下に "test" Service が作成された事を確認できます。

URL に id が入っています。

キーの作成

"test" サービスにキーを追加します。

#   Usage:   tg service key_create <serviceAccountId> <keyName> <expirationTimeInDays>

$ tg service key_create "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "tmp_key" "1"

[SUCCESS] Created key 'tmp_key: XXXXX==' at service 'test: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' with token object:
{"version": "1", "network": "subdomain.twingate.com", "service_account_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----", "key_id": "XXXXX", "expires_at": "YYYY-MM-DDTHH:MM:SS+00:00", "login_path": "/api/v2/headless_node/login"}

出力される JSON 文字列が Headless Client で使用する Service Key になります。 後から確認はできないので忘れずに保存しておきます。

WebUI 上でも Service Key が作成された事を確認できます。

Headless Client インストール

試した環境は Ubuntu 20.04.5 LTS (Focal Fossa) です。

$ curl https://binaries.twingate.com/client/linux/install.sh | sudo bash

作成した Service Key を /etc/twingate/ 配下に置き、 setup を実行します。

$ ls /etc/twingate/service_key.json

/etc/twingate/service_key.json

$ sudo twingate setup --headless /etc/twingate/service_key.json

Twingate Setup 1.0.68.54158 | 0.136.0
Setup is complete.

正常にインストールできており、 Service Key が正しければ online になります。

$ sudo twingate start

Starting Twingate service in headless mode
Twingate has been started
Waiting for status...
online

Service Key の内容が正しくない場合などは起動に失敗します。

$ sudo twingate start

Starting Twingate service in headless mode
Twingate has been started
Waiting for status...
not-running

動作確認

IPアドレス制限があり通常は 403 を返却する URL に対しアクセスしてみると、Headless Client が起動している際は拒否されず正常に接続できることが確認できました。

  • offline の状態
$ sudo twingate status

not-running

$ curl -Ls -o /dev/null  https://1.example.com -w '%{http_code}\n'

403
  • online の状態
$ sudo twingate status

online

$ curl -Ls -o /dev/null  https://1.example.com -w '%{http_code}\n'

200

Service Key を Revoke・Delete した場合、 Headless Client の起動が行えなくなります。
Headless Client が online の状態で、Revoke・Delete した場合、 Headless Client が終了するまでは接続可能でした。(どこかのタイミングで不可になるかもしれませんが、試した範囲では可能でした。)

ログ

  • For live review of logs, the Linux Client runs as a systemd service with logs retrievable via journalctl. Below is an example command:
    • sudo journalctl -u twingate --since "1 hour ago"
  • If journalctl is not installed, the logs will be stored under /var/log/twingated.log. This occasionally is seen when running the Linux Client on a container in headless mode.

上記記事の通りですが、試した環境では syslog に吐かれていました。 プロセスとしては twingated, twingate-cli があるようです。


2022/11/11 追記

Service Key は請求対象です。
アクティブユーザと同じ扱いになり、 Service Key を利用してリソース接続を行うと課金対象になります。
プランによっては 1ユーザで複数デバイス利用できますが、 Headless Client はデバイスとしての利用にはならず、別のユーザとしてカウントされます。 (ドキュメント上で記載が見つけられなかったのでサポートに確認しました。)

Twingate Admin API

TwingateAPI を調べる機会があったのでメモ。



概要

GraphQLベースの Admin API が公開されています。 Web UI 上の操作は一通り行えるようです。

GitHub上で PythonJavascript 製の CLI も公開されています。 若干サポートしている機能の違いが有りました。

Getting Started with the API | Docs | Twingate

どのような情報が取得できるかの例は PostMan Collection が公開されているので、 Postman へ import することで簡単に確認できます。

導入と実行例

各ツールでオプション名が違い若干混乱しますが、基本的には network 名 (URL の https://xxx.twingate.com/ 部分) と API Key を指定することで利用できます。

直接実行(curl)

curl で Connector の Status を取得したいとなったら以下の様な感じで取得できます。

$ NETWORK=""
$ URL="https://${NETWORK}.twingate.com/api/graphql/"
$ API_KEY =""

$ curl -L -X POST "${URL}" \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
--data-raw '{"query":"{\n  connectors(after: null, first:10) {\n    edges {\n      node {\n        id\n        name\n        state\n        lastHeartbeatAt\n\n      }\n    }\n    pageInfo {\n      startCursor\n      hasNextPage\n    }\n  }\n}","variables":{}}'
{
  "data": {
    "connectors": {
      "edges": [
        {
          "node": {
            "id": "IDx8US0zIx7nt3uEibDip",
            "name": "example-connector-01",
            "state": "ALIVE",
            "lastHeartbeatAt": "2022-10-09T21:47:18.762481+00:00"
          }
        },
        {
          "node": {
            "id": "IDxpTR7cwIkECA5l9Q9G",
            "name": "example-connector-02",
            "state": "ALIVE",
            "lastHeartbeatAt": "2022-10-09T21:46:57.830308+00:00"
          }
        }
      ],
      "pageInfo": {
        "startCursor": "CURxv85etCrXeEyOShWN6oXm=",
        "hasNextPage": false
      }
    }
  }
}

Twingate-Labs/Twingate-CLI

A Python based Command Line Interface (CLI)

Usage

requests,pandas を使用しているため事前にインストールが必要です。

# 依存パッケージのインストール
$ pip install -U requests pandas

# Usage
$ python tgcli.py -h
usage: tgcli.py [-h] [-v] [-l DEBUGLEVEL] [-s SESSIONNAME]
                [-f OUTPUTFORMAT]
                {auth,device,connector,user,group,resource,network,account,key,policy}
                ...

positional arguments:
  {auth,device,connector,user,group,resource,network,account,key,policy}

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  -l DEBUGLEVEL, --log DEBUGLEVEL
                        DEBUG,INFO,WARNING,ERROR
  -s SESSIONNAME, --session SESSIONNAME
                        Session Name
  -f OUTPUTFORMAT, --format OUTPUTFORMAT
                        Output Format <JSON,CSV,DF>

Example

SESSIONNAME オプション未指定の場合、自動生成されます。

$ NETWORK=""
$ API_KEY=""

# Get session name
$ python tgcli.py auth login --tenant ${NETWORK} --apikey ${API_KEY}

#Session Created: OrangeEel

作成した SESSIONNAME を使用して実行します。

$ SESSION=OrangeEel
$ python tgcli.py -s ${SESSION} connector list
[
  [
    {
      "node": {
        "id": "IDx8US0zIx7nt3uEibDi",
        "name": "example-connector-01",
        "state": "ALIVE",
        "lastHeartbeatAt": "2022-10-10T22:49:59.988005+00:00",
        "createdAt": "2022-09-25T03:49:01.543217+00:00",
        "updatedAt": "2022-10-03T07:51:03.236550+00:00",
        "hasStatusNotificationsEnabled": false
      }
    },
    {
      "node": {
        "id": "IDxpTR7cwIkECA5l9Q9G",
        "name": "example-connector-02",
        "state": "ALIVE",
        "lastHeartbeatAt": "2022-10-10T22:50:20.686008+00:00",
        "createdAt": "2022-09-25T04:12:10.726199+00:00",
        "updatedAt": "2022-10-03T07:51:47.279982+00:00",
        "hasStatusNotificationsEnabled": false
      }
    }
  ]
]

Twingate-Labs/tg-cli

A javascript based Command Line Interface (CLI)

Usage

バイナリが公開されています。

# ダウンロードとインストール
$ curl -L 'https://github.com/Twingate-Labs/tg-cli/releases/latest/download/cli_linux_x86_64.zip' -o /usr/local/src/tg-cli.zip
$ unzip /usr/local/src/tg-cli.zip -d /usr/local/bin/
$ chmod +x /usr/local/bin/tg

# Usage
$ tg --help

  Usage:   tg
  Version: CLI Version: 0.0.53 | TwingateApiClient Version: 0.1.0

  Description:

    CLI for Twingate

  Options:

    -h, --help                      - Show this help.
    -V, --version                   - Show the version number for this program.
    -a, --account-name  <string>    - Twingate account name
    -l, --log-level     [logLevel]  - Log level                                  (Default: "INFO", Values: "TRACE", "DEBUG", "INFO", "WARN",
                                                                                 "ERROR", "SEVERE", "FATAL", "QUIET", "SILENT")

  Commands:

    export     - Export from account to various formats
    import     - Import from excel file to a Twingate account
    deploy     - Automatically deploy Twingate Connectors to various clouds and platforms
    resource   - Twingate resources
    group      - Twingate groups
    user       - Twingate users
    network    - Twingate networks
    connector  - Twingate connectors
    device     - Twingate devices
    service    - Twingate services
    policy     - Twingate policies

Example

API Key を都度入力する形です。ファイルに保存して使い回すことができます。

$ NETWORK=""

# 初回実行時に API Key を入力
$ tg --account-name ${NETWORK} connector list
 ? Enter Twingate account: (xxxxx) › xxxxx
 ? Enter API key: › **************************************************************************************************************************************

# API Key を `~.tgkeys` に保存するか否か
 ? Save account and API key to file? › Yes
[INFO]    Configuration file saved.
┌──────────────────────┬───────────────────────┬───────────┬───────────┬─────────────────┬────────┬──────────────────────┐
│ id                   │ name                  │ createdAt │ updatedAt │ lastHeartbeatAt │ state  │ remoteNetworkLabel   │
├──────────────────────┼───────────────────────┼───────────┼───────────┼─────────────────┼────────┼──────────────────────┤
│ IDx8US0zIx7nt3uEibDip │ example-connector-01 │ 9/25/22   │ 10/3/22   │ 10/10/22        │ Online │ example-remote-network │
├──────────────────────┼───────────────────────┼───────────┼───────────┼─────────────────┼────────┼──────────────────────┤
│ IDxpTR7cwIkECA5l9Q9G │ example-connector-02 │ 9/25/22   │ 10/3/22   │ 10/10/22        │ Online │ example-remote-network │
└──────────────────────┴───────────────────────┴───────────┴───────────┴─────────────────┴────────┴──────────────────────┘

API Key の保存を行うと、次回以降は Key の入力を省略できます。

$ file .tgkeys
.tgkeys: data
$ tg --account-name ${NETWORK} connector list --output-format json
[INFO]    Using Twingate account: 'xxxxx'
[{"id":"IDx8US0zIx7nt3uEibDip","name":"example-connector-01","createdAt":"2022-09-25T03:49:01.543Z","updatedAt":"2022-10-03T07:51:03.236Z","lastHeartbeatAt":"2022-10-10T23:09:00.021Z","state":"Online","remoteNetworkLabel":"example-remote-network"},{"id":"IDxpTR7cwIkECA5l9Q9G","name":"example-connector-02","createdAt":"2022-09-25T04:12:10.726Z","updatedAt":"2022-10-03T07:51:47.279Z","lastHeartbeatAt":"2022-10-10T23:09:20.694Z","state":"Online","remoteNetworkLabel":"example-remote-network"}]

export

サブコマンドとして export が用意されており、全情報を JSONExcel 形式で出力できます。

$ tg export --help

  Usage:   tg export
  Version: CLI Version: 0.0.53 | TwingateApiClient Version: 0.1.0

  Description:

    Export from account to various formats

  Options:

    -h, --help                         - Show this help.
    -a, --account-name     <string>    - Twingate account name
    -l, --log-level        [logLevel]  - Log level                (Default: "INFO")
    -f, --format           [value]     - Export format            (Default: "xlsx", Values: "xlsx", "json", "dot", "png",
                                                                  "svg")
    -o, --output-file      [value]     - Output filename
    -n, --remote-networks  [boolean]   - Include Remote Networks
    -r, --resources        [boolean]   - Include Resources
    -g, --groups           [boolean]   - Include Groups
    -u, --users            [boolean]   - Include Users
    -d, --devices          [boolean]   - Include Devices (trust)

Twingate/twingate | Terraform Registry

番外編 Terraform の provider です。 現時点ではいくつか対応していない機能があります。

  • Remote Network の "Location" 設定
  • User の Group 割当
  • Connector の "Status emails" 設定

まとめ

使い込んではいませんので所感です。

export/import があり導入も手軽なので、 tg-cli (JavaScript版) の使い勝手が良さそうです。

Twingate-CLI (Python版) の固有機能として DataFrame での出力ができる点がありますが、今の所利用シーンが思い浮かびません。

AWS Support App in Slack で Case を Slack へ通知

AWS Support App が公開されました。

何が嬉しいかと言うと、これまで Support Case を Slack へ通知するのにひと手間掛けていたのが、IAM Role を作成するだけ(正確にはだけではないですが)で通知されるようになった点です。
機能の売りは Slack 上で Case を操作できる事なのですが、個人的には Slack への通知が行える点が気に入りました。

これまで

EventBridge により通知自体はできました (Monitoring AWS Support cases with Amazon EventBridge ) が、この方式だと内容については確認できません。 内容確認のためには Lambda で実装したり、マネジメントコンソールへログインする必要がありました。

AWS Support App in Slack

AWS Support App in Slack を設定すると、 Support Case が作成されると以下のような感じで通知されます。

"See details" ボタン押下で本文が Slack 上で確認できます。

この時点では自分自身にしか見えませんが、末尾に付与される "Share to channel" ボタン押下でチャンネルに投稿され、チャンネル参加メンバーへの共有もできます。

導入

(詳細は公式ブログや公式ドキュメント参照になるのでメモ程度です)

ドキュメントに注意点等も記載されているので一読することをお勧めします。

Slack 上での App は AWS Support です。事前に追加しておく必要があります。 (伴い Slack の管理者権限が必要です)

前提条件として記載されていますが、Support App が利用する IAM Role を作成しておく必要があります。
連携先のSlackチャンネルへ参加しているユーザには一律このRoleの権限が付与されることになります。

Users in your Slack channel have the same permissions that you grant to the IAM role.

ユーザ制限を掛ける方法としては、 Managing access to the AWS Support App - AWS Support に沿い、Appとの連携にプライベートチャンネルを利用し、利用者のみ招待する 形になると思います。

IAM Role に付与する IAM Policy も作成する手順になっていますが、 Full と ReadOnly の manged policy が用意されているのでこちらを利用するのが良いと思います。

制限・未サポートの機能など

公開されたばかりのサービスなのですぐ対応してくれるかもしれませんが、現時点では以下の制限や利用できない機能が存在します。

  • 日本語での Case 作成 (※)
  • 日本語 Case の検索
    • 検索条件に合致する Case が存在していても No cases found となる
  • 1年以上前の Case の検索

(※) Appからの Case 作成は行なえませんが、マネジメントコンソールやAPIで作成した Case についても通知され、その後の日本語での Case 更新やResolve操作は可能です。

Terraform 日時文字列とUnixtimeの相互変換

Unixtime(epoch) と 日時文字列 との変換をTerraformで行う方法を調べました。



Built-in Functions

Terraform 内での日時は、RFC 3339 形式 (YYYY-MM-DD'T'hh:mm:ssZ) に沿った形で扱うようです。

formatdate function が用途に合ってそうですが、日時文字列とUnixtime の変換機能は有りません。

使用例

locals {
  now = timestamp()
}

output "o11-timestamp" {
  value = local.now
}

output "o12-timestamp-jst" {
  value = formatdate("YYYY/MM/DD hh:mm:ss", timeadd(local.now, "9h"))
}

# ---
Outputs:

o11-timestamp = "2022-08-20T08:06:19Z"
o12-timestamp-jst = "2022/08/20 17:06:19"

日時文字列 -> Unixtime

Time Provider の time_static resource で実現できます。

使用例

YYYY-MM-DD'T'hh:mm:ssZ を渡すと、分割した値(year, month, day, ...)や Unixtime (unix) を属性として持つことができます。

resource "time_static" "mountain_day" {
  rfc3339 = "2022-08-11T00:00:00Z"
}

output "o21-mountain_day" {
  value = time_static.mountain_day
}

resource "time_static" "mountain_day_jst" {
  rfc3339 = "2022-08-11T00:00:00+09:00"
}

output "o22-mountain_day_jst" {
  value = time_static.mountain_day_jst.unix
}

# ---
Outputs:

o21-mountain_day = {
  "day" = 11
  "hour" = 0
  "id" = "2022-08-11T00:00:00Z"
  "minute" = 0
  "month" = 8
  "rfc3339" = "2022-08-11T00:00:00Z"
  "second" = 0
  "triggers" = tomap(null) /* of string */
  "unix" = 1660176000
  "year" = 2022
}
o22-mountain_day_jst = 1660143600

Unixtime -> 日時文字列

time_static resource も formatdate function 同様、入力がRFC3339形式のみなので対応していません。 残念ながら現状では Terraform の標準機能・Providerでは実現できないようです。

仕方がないので External Data Source でお茶を濁します。

import sys
import json
import datetime

input = sys.stdin.read()
input_json = json.loads(input)

epoch = int(input_json.get('epoch', 0))
dt = datetime.datetime.fromtimestamp(epoch, datetime.timezone.utc)

output = {
    'iso': dt.isoformat()
}

print(json.dumps(output, indent=2))

使用例

data "external" "mountain_day" {
  program = ["python", "./epoch.py"]
  query = {
    epoch = time_static.mountain_day.unix
  }
}

output "o31-mountain_day" {
  value = data.external.mountain_day.result
}

data "external" "mountain_day_jst" {
  program = ["python", "./epoch.py"]
  query = {
    epoch = time_static.mountain_day_jst.unix
  }
}

output "o32-mountain_day_jst" {
  value = data.external.mountain_day_jst.result.iso
}

# ---
Outputs:

o31-mountain_day = tomap({
  "iso" = "2022-08-11T00:00:00+00:00"
})
o32-mountain_day_jst = "2022-08-10T15:00:00+00:00"

Datadog Logs 特殊文字をキーに含むJSONを処理する


経緯

Datadog の Log Management で Log Facets を作成する際に以下のエラーが発生しました。

Something wrong happened while creating the facet:
The Facet path must contain only letters, digits, or characters -_@$.
Each segments should be separated by '.'.

対象のログはJSON形式で、 facet を作成しようとした属性は記号を含んだ物でした。
例として、以下のようなログで "Size (KB)" から facet を作成しようとすると発生します。 エラーメッセージの通りですが、特殊文字(この場合、スペースと"()") を含んでいるため facet が作成できない状態です。

{
    "title": "JSON Example",
    "ID": "0000",
    "Size (KB)": 100
}

特殊文字と言っても一概には言えず、文字種により許可・非許可のパターンがありましたので、回避した方法と諦めた点をまとめます。

前提

JSON形式

テキスト形式のログと扱いが異なり、 JSON 形式のログは Datadog により自動的に解析されます。

  • Parsing
    • Datadog automatically parses JSON-formatted logs.

  • Preprocessing
    • JSON log preprocessing comes with a default configuration that works for standard log forwarders.

"Logs" -> "Configuration" の Pipeline 設定画面の一番先頭にある Preprocessing for JSON logs が該当します。

Pipelineの先頭で強制的に処理されます。 Remap する属性名の追加・削除を行うことはできますが、処理自体の無効化はできません。

今回この前処理はデフォルトの設定のまま検証しています。
(Pipelineを設定するに当たり、この動作自体がパースの障壁になることもありますが、今回は割愛します。)

特殊文字

ログ検索時には記号はバックスラッシュ(\)を前に置いて、エスケープする必要があります。 スペースはエスケープでなく、ワイルドカード(?)で置き換えて処理させます。

  • Log Search Syntax
    • The following characters are considered special: + - = && || > < ! ( ) { } [ ] ^ " “ ” ~ * ? : \, and / require escaping with the \ character.

    • To match a single special character or space, use the ? wildcard.

上記はあくまでログ検索時の特殊文字の扱いであり、ログのパースや facet 作成時は文字種により動作が変わります。

検証に使用したログ

属性に特殊文字を含むJSON形式のログを使用します。 各属性で facet および measure が作成でき、グラフ化できる状態になる事を目的とします。

{
    "ddsource": "custom",
    "hostname": "i-012345678",
    "message": "2022-08-13T13:44:27 INFO json format log parse test",
    "service": "custom",
    "key_normal": 719,
    "key space": 24,
    "key:colon": 764,
    "key.dot": 518,
    "key,comma": 835,
    "key(parentheses)": 502,
    "key[brackets]": 14,
    "key{curly}": 1,
    "key<angle>": 11,
    "key日本語": 811
}

Datadog に取り込まれると以下のように JSON の各キーが属性としてパースされた状態になります。
この時点では特殊文字の有無での違いはありません。

回避策

本題です。回避策として利用できる手法を順に記載していきます。

(a) 特に何もせず

記号を含んでいるからといって全てが制限に引っかかるわけではなく、そのまま利用できる文字種が存在します。

特に制限の無い文字種では、filter や facet の作成が問題無く行えます。 ドット(.) の場合は、JSONの階層扱いとなるため1階層下となります。

弊害(a-1): 検索する際に filter の自動入力が効かない

Filter by @xxx から作成した場合に Tag 扱いとなる文字種があります。 作成したいのは属性値であり、Tagは作成していないのでそのままでは検索に引っかかりません。

自動入力での filter が行えないので、手動で filter を記述する必要があります。

特殊文字はバックスラッシュでのエスケープが必要です。エスケープしないと以下のエラーとなります。

Invalid search query. Try escaping these special characters with the "\" character:
+ - = && || > < ! ( ) { } [ ] " “ ” * ? : \

弊害(a-2): 検索する際に filter が効かない

スペースを含む要素の検索が行なえません。 バックスラッシュのエスケープは効かず、ワイルドカードはエラーとなりました。

Oops, an error has occurred. Please retry or reload the page.

(b) ログ出力側を修正する

身も蓋もないですが、JSONキーに特殊文字を含まないようログ出力側を修正できるのであれば、それが最善策だと思います。
自分たちで開発したプログラムなどであれば良いですが、利用しているソフトウェアやサービスのログが対象だと中々簡単には行きません。

(c) Remap する

非許可文字を含む属性の facet を作成しようとすると、冒頭のエラー出力となります。

Pipeline を作成、 Remapper を使用して、対象の属性を別の名称に置き換えます。

(d) String Builder で抽出後、 Remap する

Remap 設定を追加しようとすると、属性の検索が行えずエラーが発生し、 Remap を行えない文字種が存在します。

Invalid source(s): '...'

また、カンマ(,) に関しては、複数属性指定の区切りとなるため指定できません。

これらの場合は、属性を指定する Processor は使用できません。
そこで、 ブロック構文を使用できる String builder processor で、対象の属性を別の名称に置き換えます。

String builder の名の通り string 形式で生成されるので、 measure として使用したい場合は、更に Remapper により数値型に変換します。

(e) 諦める

String Builder で抽出不可な文字種が存在します。 今回試した範囲では、 []{} がエラーとなります。また、バックスペースでのエスケープは効かないようです。

Invalid template: %{...}

こちらについては用意されている Processor で処理できる案は出ませんでした。

(f) 諦めきれない...

Grok parser で自前で解析することで抽出は可能です。

しかし、JSON形式のログであるが故、前処理が走るため、キーを直接 Grok でパースすることができません。 JSON以外の形式にする、message 属性にJSON文字列を出力するなど、ログ出力側での変更が必要になってしまうかと思います。

message 属性にJSON文字列 を出力する例

message の内容を Grok parser で処理、必要な属性を抽出し、Remapper により数値型に変換します。

  • ログ例
[
  {
    "ddsource": "custom",
    "hostname": "i-012345678",
    "message": "[{\"ddsource\":\"custom\",\"hostname\":\"i-012345678\",\"message\":\" INFO json format log parse test\",\"service\":\"custom\",\"key_normal\":719,\"key space\":24,\"key:colon\":764,\"key.dot\":518,\"key,comma\":835,\"key(parentheses)\":502,\"key[brackets]\":14,\"key{curly}\":811,\"key<angle>\":556,\"key日本語\":652}]",
    "service": "custom"
  }
]
  • Define parsing rules
rule_brackets_and_curly ^.*key\[brackets\][^0-9]*%{regex("[0-9]*"):key_brackets_grok}.*key\{curly\}[^0-9]*%{regex("[0-9]*"):key_curly_grok}.*$
rule_curly_and_brackets ^.*key\{curly\}[^0-9]*%{regex("[0-9]*"):key_curly_grok}.*key\[brackets\][^0-9]*%{regex("[0-9]*"):key_brackets_grok}.*$
  • Grok 処理結果 を Remapper により数値型に変換
{
  "key_curly_grok": "811",
  "key_brackets_grok": "14"
}

結果

そのまま利用可能な属性と、変換した属性 に対し、 facet(measure) を作成します。

facet が作成され、数値型にした事により measure としても利用可能となりました。

文字種パターン

  • (*1): tag 扱いとなるため自動入力不可
  • (*2): processorで処理せずとも利用可
文字種 変換前Key (a)filter (a)facet(measure) (c)remap (d)string builder + remap
アンダースコア key_normal △(*2) △(*2)
半角スペース key space △(*2)
コロン key:colon △(*1) △(*2)
ドット(ピリオド) key.dot △(*2) △(*2)
カンマ key,comma △(*1)
丸括弧(小括弧) key(parentheses) △(*1)
角括弧(大括弧) key[brackets] △(*1)
波括弧(中括弧) key{curly} △(*1)
山括弧 key<angle> △(*1)
マルチバイト key日本語 △(*1)

シェルスクリプト内の aws cli で実行されるコマンドを出力する

シェルスクリプト(bash)で aws cli を使用する際のデバッグで、実際の処理を実行せずに渡される引数を出力したいと思いました。 開発中に使用したいだけなので極力シンプルにしたいです。


  • 手法検討
  • echo-awscli.sh
  • 使い方
    • 例1: シンプルなスクリプト
      • 実行結果
        • 引数未指定
        • 引数指定
    • 例2: 取得結果を使って後続実行するスクリプト
      • 取得結果を追加
      • 実行結果
    • 例3: 関数が含まれるスクリプト
      • 実行結果
    • 例4) [NG] aws コマンドを変数化しているスクリプト
      • 実行結果
        • NG
        • OK
    • 例5) getopts を使用するスクリプト
      • 実行結果
        • 引数未指定
        • 引数指定

手法検討

シェルスクリプト自体を書き換えるのが楽といえば楽ですが、ソース変更は避けたいです。 Dockerでコンテナ上で動作させる案もありますが、極力 bash だけで完結させたいと思います。

そこで、テストフレームワークで mock 化するのが良いのではと思いました。 bash のテストフレームワークはいくつか候補があります。以下に情報が纏まっており助かりました。

使ってみた感じだと、機能充実している ShellSpec、次点で bats が使い勝手が良さそうです。 とここまで書きながら、結果今回は使用していません。

今回の目的はテスト(aws cli の実行結果の検証) ではなく、実行されるコマンドの内容を知るというシンプルな物です。 echo コマンドに渡せば解決できるので、フレームワークを使うまでもなく最終的には以下の形になりました。

echo-awscli.sh

bashスクリプト内のaws cli のコマンドを出力します。

Output aws cli command in bash script

使い方

対象のシェルスクリプトおよび引数を指定して、 echo-awscli.sh を実行します。

./echo-awscli.sh scriptPath [arg1 arg2 ...]
続きを読む