Cloud Logging向け構造化ログ Python編

Cloud Loggingでは構造化ログがサポートされており、JSON形式のログを出力する事で構造化データとして読み取ってくれる。
構造化ログを出力すると、Cloud Loggingのクエリでのフィルタリングが簡単になるため、
GCP上で実行されるアプリケーションのログはCloud Loggingフレンドリーにしておきたい。

要件

  • Cloud Loggingのログエントリ形式に合わせる
    • 特にseverity, message, timestampを合わせると、Cloud Logging Viewer上の表示がいい感じになる
  • 標準出力/標準エラー出力からログを出力する
    • Cloud Logging API等を使うと、ローカル環境のテストがやりづらくなる。
  • 任意のkey-valueを追加できる
    • message だけでなく任意の変数を埋め込めると、ログ分析をする際に文字列パースをしなくて済む

実装

Python標準のLoggingモジュールに、Cloud Logging用のFormatterを設定する。
python-json-logger を使う事で、JSON形式のFormatterを簡単に作ることができる。

""" cloud logging logger """

from datetime import datetime
from datetime import timezone
import sys
import logging

from pythonjsonlogger import jsonlogger


class CloudLoggingFormatter(jsonlogger.JsonFormatter):
    def parse(self):
        return ["name", "message", "stack_info"]

    def add_fields(self, log_record, record, message_dict):
        super().add_fields(log_record, record, message_dict)

        if not log_record.get("timestamp"):
            # RFC3339形式にフォーマットする
            log_record["time"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%S.%fZ")

        if log_record.get("level"):
            # Pythonのログレベルのアッパーケースを流用
            log_record["severity"] = log_record["level"].upper()
        else:
            log_record["severity"] = record.levelname
            

formatter = CloudLoggingFormatter()

stream = logging.StreamHandler(stream=sys.stdout)
stream.setFormatter(formatter)

logger = logging.getLogger("app-name")
logger.setLevel(logging.INFO)
logger.addHandler(stream)

使い方

$ cat main.py
from logger import logger

logger.info("info log", extra={"extra-key1": "fuga", "extra-key2": "fuga"})
logger.error("error log", extra={"extra-key2": "fuga"})
$ python main.py | jq
{
  "name": "app-name",
  "message": "info log",
  "stack_info": null,
  "extra-key1": "fuga",
  "extra-key2": "fuga",
  "time": "2020-12-28T13:54.096049Z",
  "severity": "INFO"
}
{
  "name": "app-name",
  "message": "error log",
  "stack_info": null,
  "extra-key2": "fuga",
  "time": "2020-12-28T13:54.096245Z",
  "severity": "ERROR"
}

Cloud Logging Viewerだと以下のようになる。
severityに合わせて左側のアイコンが変化するため、見やすくなる。 f:id:acata:20201228232657p:plainf:id:acata:20201228232700p:plain

参考

【Golang】GCP用のフォーマットでログ出力する - Qiita

Structured logging  |  Cloud Logging  |  Google Cloud

GCP Cloud Logging で構造化ログを扱う - Qiita

Homebrewで+clipboardなvimをインストールする方法

linuxbrewでやってみました。

brewの設定情報を編集するエディタを指定する

$ export HOMEBREW_EDITOR=vim

vimの設定情報を編集する

$ brew edit vim

configureの引数に--enable-clipboard を追加

    system "./configure", "--prefix=#{HOMEBREW_PREFIX}",
                          "--mandir=#{man}",
                          "--enable-multibyte",
                          "--with-tlib=ncurses",
                          "--with-compiledby=Homebrew",
                          "--enable-cscope",
                          "--enable-terminal",
                          "--enable-perlinterp",
                          "--enable-rubyinterp",
                          "--enable-python3interp",
                          "--enable-gui=auto", # <- `no` から `auto` に変更
                          "--without-x",
                          "--enable-luainterp",
                          "--with-lua-prefix=#{Formula["lua"].opt_prefix}",
                          "--enable-clipboard" # <- 行を追加

ビルドに必要なライブラリをインストール

$ sudo apt install -y liblua5.1-dev luajit libluajit-5.1 python-dev ruby-dev libperl-dev libncurses5-dev libatk1.0-dev libx11-dev libxpm-dev libxt-dev

ソースコードからビルドしてインストール

$ brew install vim --build-from-source

+clipboard になっているか確認

$ vim --version | grep clipboard
+clipboard         +jumplist          +popupwin          +user_commands
+ex_extra          -mouse_jsbterm     -sun_workshop      +xterm_clipboard

参考

How To Install Vim 8.2 On Ubuntu

Pythonでgkeのクラスタ認証情報を取得する

Pythonでgkeのクラスタ認証情報を取得する

gcloud container clusters get-credentialsPythonGoogle Cloud SDKでやる方法

import base64
from tempfile import NamedTemporaryFile

import google.auth
import google.auth.transport.requests
from google.cloud import container_v1
from kubernetes import client


def create_k8s_client() -> client.Configuration:
    project_id = "your-project-name"
    zone = "us-central1-b"
    cluster_name = "your-cluster-name"

    container_client = container_v1.ClusterManagerClient()
    response = container_client.get_cluster(
        name=f"projects/{project_id}/locations/{zone}/clusters/{cluster_name}"
    )
    creds, _ = google.auth.default()
    auth_req = google.auth.transport.requests.Request()
    creds.refresh(auth_req)
    configuration = client.Configuration()
    configuration.host = f"https://{response.endpoint}"
    with NamedTemporaryFile(delete=False) as ca_cert:
        ca_cert.write(base64.b64decode(response.master_auth.cluster_ca_certificate))
    configuration.ssl_ca_cert = ca_cert.name
    configuration.api_key_prefix["authorization"] = "Bearer"
    configuration.api_key["authorization"] = creds.token

    return configuration


if __name__ == "__main__":
    config = create_k8s_client()
    k8s_client = client.BatchV1Api(
        client.ApiClient(config)
    )
    ret = k8s_client.list_job_for_all_namespaces()

参考

Python method for `gcloud container clusters get-credentials` · Issue #6 · googleapis/python-container · GitHub

Client for Google Container Engine API — google-cloud-container documentation

WSL2でvimからクリップボードを利用する

忘れないようにメモ

クリップボードオプション付きvimをインストールする

vim --version | grep clipboard して+clipboard が出ない場合、
+clipboardvimをインストールする。

sudo apt purge vim
sudo apt install vim-gtk

brewでインストールする方法は こちら

VcXsrv Windows X Serverをインストールする

  • Extra settingsにて Clipboard, Disable access control にチェックをつける
  • Save configuration ボタンから、設定を適当なフォルダに保存する
  • 起動時のオプションで設定ファイルを読み込む
    • C:\ProgramData\Microsoft\Windows\Start Menu\Programs\VcXsrv\XLaunch を開く
    • リンク先を "C:\Program Files\VcXsrv\xlaunch.exe" -ac -run C:\path\to\config.xlaunch にする

Windows DefenderのFWでVcXsrvを許可する

WSL2に環境変数を設定する

export DISPLAY=$(grep -oP "(?<=nameserver ).+" /etc/resolv.conf):0.0

yankした時に"*レジスタに記録させる

set clipboard=unnamed

参考:

react-map-gl + node-mapnikでWebベースのGISアプリケーションを作った

react-map-gl + node-mapnikでのGIS Webアプリケーションを作った

この記事はFOSS4G Advent Calendar 2018の11日目の記事です。

Lgis
Lgisという、WebベースのGISアプリケーションを作りました。

github.com

デモサイト的なものはまだありません。。近々作る予定です。

機能

  • ブラウザ上で動作します。
  • PostGIS上に保存されている位置情報データを可視化する事ができます。
    • PostGISのテーブルに対してSQLを書く事で、観点に合わせてデータを可視化する事ができます。
      • 例) WHERE句を使い、任意の地域に限定してデータを可視化
      • GROUP BY句で、任意の市区町村データを集計して可視化

モチベーション

位置情報データから何らかの知見を得たい時、QGIS等のGISアプリを使って地図上に可視化するのが一般的だと思います。
QGISは、データの表現力高く、対応するデータソースも多い素晴らしいアプリケーションですが、
処理速度が遅かったり、ドリルダウン向きの機能が弱かったりと、仮説検証を繰り返しながらデータ分析をする上では痒い所に手が届かない印象があります。
最近では、Kepler.gl というモダンなGISアプリもありますが、データソースがテキストのみというのが厳しい。
また、Re:DashやTableau等のBIツールは簡単にデータをドリルダウンする事ができますが、地図描画の機能自体がまだまだ弱いです。
そこで、両者の良さを活かしたツールを作れないかと思い、開発に至りました。

利用した技術

バックエンド

  • node-mapnik
    • ベクタタイルを使いたかったので、対応している地図サーバの中で一番人気そうなこちらを選びました

フロントエンド

  • react-map-gl
    • Reactで書いたほうが後々拡張しやすいかなと思い、Mapbox-GL-JSのReactラッパーのこちらを採用しました。
  • Redux
  • TypeScript

使い方

yarn install
yarn build
yarn start

react-map-glを使っている関係上、Mapbox Access Tokenが必要になります。

今後

ベクタータイルはPostGISにかける負荷が結構多いようで、手元の環境では地物が500万レコードぐらいを超えるとサーバーが固まります。
これを解消するために、タイルのキャッシュ機能を追加してあげる必要があるかなと思います。