Datadog ダッシュボードを壁紙にして監視する

# はじめに
弊社のサービス[F-Portal/M-Manage](https://v-manage.infomart.co.jp/)の運用監視のためにDatadogを導入しました。
Datadogのダッシュボードにシステムの監視項目をいろいろ設定し、便利に使っています。
しかしこのダッシュボード、当たり前のことなのですがわざわざブラウザでアクセスしないと見ることができません。
不要なPCがあれば、適当なディスプレイに表示させっぱなしにしておけばよいのですが、在宅勤務などで適当な表示環境を用意できないことがあります。

そこで、ダッシュボードのキャプチャ画像をWinowsの壁紙にして、定期的に更新していくようにすれば、作業の合間にチラ見できて便利なのでは。と思いやってみました。

# 構成

| ファイル| 役割 | 説明 |
| --- | --- | --- |
|C:\適当な場所\datadogWallpaper\ ||
|　　datadogWallpaper.vbs|datadogWallpaper.pyを呼び出す|有|
|　　datadogWallpaper.py|処理本体|有|
|　　datadogWallpaper.log|ログ（実行後生成）||
|　　dashboard.png|壁紙画像（実行後生成）||
|　　setup.bat|スケジューラにセットするバッチ|有|
|　　setup.xml.template|setup.batで利用|有|
|　　setup.xml|setup.batを実行すると生成される||

# ファイルの解説

## datadogWallpaper.py
メイン処理。ダッシュボードを画像化して壁紙にセット。

```python:datadogWallpaper.py
# datadogのdashboardを壁紙化する
import ctypes
import datetime
import os
import time
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# ローカル設定
WALLPAPER_SIZE = [1920/1.25, 1080/1.25]  # 1920x1080の125%表示
WAIT = 10  # 描画が終わるまでの待機時間（秒）

# グローバル設定
DATADOG_URL = 'https://p.datadoghq.com/sb/xxxxx?from_ts=0&to_ts=86400000&tv_mode=true&theme=dark'


def main():
    print(f"start {datetime.now()}")
    print(f"WALLPAPER_SIZE={WALLPAPER_SIZE}")
    print(f"WAIT={WAIT}秒")
    print(f"DATADOG_URL={DATADOG_URL}")

    # 画像取得
    img_file = capture_dashboard()

    # 壁紙に設定
    change_wallpaper(img_file)

    print(f"end {datetime.now()}")
    return


# Datadogの公開ダッシュボードをキャプチャ
def capture_dashboard():
    print("----- capture_dashboard -----")
    img_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dashboard.png")
    # print(f"img_file={img_file}")
    # Selenium初期化
    option = Options()
    option.add_argument("--headless")
    option.add_argument("--hide-scrollbars")
    option.add_argument("--incognito")  # シークレッドモード。閲覧履歴を残さないため
    option.add_argument("--disable-extensions")
    option.add_argument(f"--window-size={WALLPAPER_SIZE[0]:.0f},{WALLPAPER_SIZE[1]:.0f}")
    option.add_experimental_option('excludeSwitches', ['enable-logging'])  # debugポートを開けない
    
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=option)

    # 公開ダッシュボードに接続してキャプチャ
    driver.get(DATADOG_URL)
    time.sleep(WAIT)  # 描画が終わるまで待機
    driver.save_screenshot(img_file)

    driver.quit()
    return img_file


# 画像を壁紙にセットする
def change_wallpaper(img_file):
    print("----- change_wallpaper -----")
    SPI_SETDESKWALLPAPER = 20  # 壁外変更の定数
    ctypes.windll.user32.SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, img_file , 0)
    return


main()
```

### ソースコード中の設定について

```
WALLPAPER_SIZE = [1920/1.25, 1080/1.25]  # 1920x1080の125%表示
WAIT = 10  # 描画が終わるまでの待機時間（秒）
```
WALLPAPER_SIZEは自PCの画面解像度を入力します。
100%で利用しているなら解像度の値そのままですが、125%表示などにしている場合は例のように入力します。

WAITは描画が終わるまでの待機時間です。
Datadogのダッシュボードにアクセスしてもグラフの描画に時間がかかる場合があります。
そのための待ち時間です。
お使いの環境に合わせて適当な値を設定してください。

```
DATADOG_URL = 'https://p.datadoghq.com/sb/xxxxx?from_ts=0&to_ts=86400000&tv_mode=true&theme=dark'
```
ダッシュボードを公開したURLを入力します。
パラメータとして
`?from_ts=0&to_ts=86400000&tv_mode=true&theme=dark`
も付与しています。
to_tsの値によって表示期間を調整することができます。
１日間表示したい場合は86400000ミリ秒(=24時間*60分*60秒*1000ミリ秒)を設定します。

### キャプチャ処理
chrome.exe でキャプチャをするだけならseleniumを使わなくても
`chrome.exe –-headless ––screenshot=c:\temp.png http://example.com`
で実行することも可能です。
ただ、その場合アクセス完了と同時にキャプチャされてしまうようで、Datadogダッシュボードでは描画待ちの画像になってしまいました。
`--virtual-time-budget`のオプションでうまく回避できないかと試したのですが、今回の場合はダメでした。

そのため、seleniumでアクセスして、途中でWAIT秒スリープすることで完全に描画されたものをキャプチャするようにしています。
もっとスマートに描画完了を判断する方法があるのかもしれないですが、このあたりは知識が薄くて調べていません。

### chromeの起動オプション
chromeをバックグラウンドで動かすためにheadlessモードで動かすサンプルを探していると`disable-gpu`も指定されているものが多く見つかります。
以前はWindows環境でheadlessを使うときはエラーになるので必須だったようなのですが
これは現在では不要になり、つけなくても動くようになっています。
https://github.com/rails/rails/pull/32488

### Seleniumドライバ
Seleniumを使うときにはchromeのバージョンとdriverのバージョンとを一致させる必要があります。
バージョンが一致していないと
`This version of ChromeDriver has not been tested with Chrome version XXX`
といったエラーになってしまいます。
そこで[webdriver_manager](https://github.com/SergeyPirogov/webdriver_manager)を使うことで自動的にバージョンを合わせるようにしています。
ちなみにこのツールを最初に作った時は、Windows標準環境のままで動かせるようPowerShellで作ったのですが、
このバージョン一致問題をうまく解決する方法がわからず、このライブラリで回避することにしました。

## datadogWallpaper.vbs
datadogWallpaper.py をバックグラウンドで動かすためのラッパー。
タスクスケジューラからこれを呼び出します。

```visualbasic:datadogWallpaper.vbs
Dim ws
set ws = CreateObject("Wscript.Shell")
ws.CurrentDirectory = createObject("Scripting.FileSystemObject").getParentFolderName(WScript.ScriptFullName)
ws.run "cmd /C python ./datadogWallpaper.py >./datadogWallpaper.log ", 0, True
set ws = Nothing
```

## setup.bat, setup.xml.template
GUIで設定することもできるのですが、定型の設定なのでbatで設定しています

```batch:setup.bat
: タスクスケジューラにタスクを設定する

: 設定
INTERVAL=PT5M
WORKINGDIRECTORY=%~dp0

: setup.xmlの生成
echo "" > setup.xml
for /f "delims=" %%line in (setup.xml.template) do (
    set line = %%line
    set line = !line:__INTERVAL__=%INTERVAL%!
    set line = !line:__WORKINGDIRECTORY__=%WORKINGDIRECTORY%!
    echo !line! >> setup.xml
)
SCHTASKS /Create /TN Datadog壁紙 /XML setup.xml
```
setup.xml.template の `__INTERVAL__` , `__WORKINGDIRECTORY__` を置換してsetup.xmlを作成し、
それをもとにタスクスケジューラを設定します。

`INTERBAL=PT5M` は5分間隔の意味です。
`WORKINGDIRECTORY=%~dp0` はbatの実行ディレクトリを取得しています。

<details><summary>setup.xml.template（見るべきところがなく、長いので折りたたんでいます）</summary><div>

```xml:setup.xml.template
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <Triggers>
    <TimeTrigger>
      <Repetition>
        <Interval>__INTERVAL__</Interval>
        <StopAtDurationEnd>false</StopAtDurationEnd>
      </Repetition>
      <StartBoundary>2022-01-01T00:00:00</StartBoundary>
      <Enabled>true</Enabled>
    </TimeTrigger>
  </Triggers>
  <Principals>
    <Principal>
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>false</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>PT72H</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>wscript.exe</Command>
      <Arguments>datadogWallpaper.vbs</Arguments>
      <WorkingDirectory>__WORKINGDIRECTORY__</WorkingDirectory>
    </Exec>
  </Actions>
</Task>
```
</div></details>

# 使い方
## 環境
Windows10環境にて検証しています。

## Datadogでの準備
ダッシュボードを作成し、公開します。
生成されたURLを `DATADOG_URL` に設定します。

このダッシュボードURLには[接続元のIP制限](https://docs.datadoghq.com/ja/dashboards/sharing/#%E5%88%B6%E9%99%90%E3%81%AE%E9%81%A9%E7%94%A8)をかけることもできます。


## 自PCでの準備
Pythonをインストールします。
今回はPython3.10を使っていますが3.xxであれば動くと思います。

pipで以下をインストールします。
```
pip install selenium
pip install webdriver_manager
```

なお、Windows環境ではpythonをインストールしただけではpipへのパスが通っていないことがあります。
以下を参考に環境変数PATHを設定します。
https://and-engineer.com/articles/YcJyaRAAACMAl3BX#heading3-7

## ファイルの配置
ローカルの適当な場所に、上記構成のようにファイルを配置します。

## タスクスケジューラへの設定
タスクスケジューラにdatadogWallpaper.vbsを5分間隔で起動するように設定をします。
GUI上で設定してもよいのですが、設定バッチ(setup.bat)を用意したのでそれを実行することで設定します。


# 課題
マルチディスプレイ環境の場合、すべて同じ壁紙がセットされます。
こちら↓のようにすれば、別々のものを設定できるようです。
[壁紙を変更する (マルチディスプレイ対応版)](https://smdn.jp/programming/tips/setdeskwallpaper_multidisplay/)

Pythonで動かすため、WindowsにPython環境をインストールする必要があります。
seleniumドライバのところにも記載しましたが、PowerShell等を使って、追加で実行環境をインストールすることなくできればベター。

# おわりに
この設定をしてから、Datadogのダッシュボードを目にする機会が圧倒的に増え、
いつもよりもアクセス数が多いなどの細かな変化に気が付けるようになりました。

今回はDatadogのダッシュボードを壁紙化しましたが、株価表示などほかにも応用できると思います。
ぜひお試しあれ！