chmonitor · ClickHouse monitoring dashboard and CLI
0 people used it yesterday · 0 this week · 0 this month.
- /chmonitor
- chmonitor.dev
- since
- Stats JSON
2 users and 18 events so far today, live. 5.4k events in the last 365 days.
Daily users
1k users1k events
Oct 20Oct 19
Breakdown
| Events | User-days | Events |
|---|---|---|
| ping | 4.8k | 4.8k |
| app_loaded | 0 | 265 |
| queries_viewed | 0 | 210 |
| health_viewed | 0 | 168 |
| ai_query_sent | 0 | 50 |
| cluster_connected | 0 | 1 |
| Versions | User-days | Events |
|---|---|---|
| 0.2.15 | 1 | 1 |
| Operating systems | User-days | Events |
|---|---|---|
| macos | 311 | 311 |
| linux | 217 | 217 |
| windows | 118 | 118 |
| unknown | 31 | 31 |
| android | 11 | 11 |
| ios | 9 | 9 |
| Countries | User-days | Events |
|---|---|---|
| UNKNOWN | 198 | 198 |
| IN | 94 | 94 |
| SG | 61 | 61 |
| VN | 52 | 52 |
| US | 44 | 44 |
| DE | 35 | 35 |
| ZA | 32 | 32 |
| KZ | 31 | 31 |
| RU | 20 | 20 |
| KE | 14 | 14 |
| ID | 13 | 13 |
| ES | 12 | 12 |
| MX | 11 | 11 |
| RS | 8 | 8 |
| CN | 6 | 6 |
| RO | 6 | 6 |
| NL | 5 | 5 |
| TH | 5 | 5 |
| GB | 4 | 4 |
| TR | 4 | 4 |
| prop · ch_flavor | User-days | Events |
|---|---|---|
| unknown | 494 | 1.2k |
| oss | 200 | 201 |
| altinity | 3 | 3 |
| prop · ch_version | User-days | Events |
|---|---|---|
| 26.3 | 61 | 61 |
| 26.4 | 30 | 30 |
| 25.11 | 24 | 24 |
| 25.8 | 17 | 17 |
| 26.5 | 17 | 17 |
| 26.7 | 12 | 12 |
| 25.3 | 11 | 11 |
| 24.3 | 7 | 7 |
| 24.10 | 6 | 6 |
| 24.8 | 4 | 4 |
| 26.6 | 4 | 4 |
| 25.10 | 2 | 2 |
| 24.6 | 2 | 2 |
| 25.12 | 2 | 2 |
| 26.2 | 2 | 2 |
| 26.8 | 1 | 2 |
| 99.9 | 1 | 1 |
| 23.8 | 1 | 1 |
| 25.6 | 1 | 1 |
| prop · deploy_target | User-days | Events |
|---|---|---|
| unknown | 4.1k | 4.2k |
| docker | 401 | 706 |
| cf | 228 | 483 |
| dev | 1 | 21 |
Integrate
Ingest URL https://telemetry.anyr.workers.dev/chmonitor, OTLP endpoint https://telemetry.anyr.workers.dev/chmonitor/v1. Pick a client below, or let an agent do it.
curl -fsS -m 1 -X POST https://telemetry.anyr.workers.dev/chmonitor \
-H 'content-type: application/json' \
-d '{"event":"ping","id":"<random-install-id>","version":"1.0.0","os":"linux","arch":"x64"}'# Anonymous install ping. Put it near the end of install.sh. No id: counts daily users only.
# An env var is "set" for any value except empty, 0 or false.
truthy() { case $(printf %s "$1" | tr '[:upper:]' '[:lower:]') in ""|0|false) return 1 ;; *) return 0 ;; esac; }
if ! truthy "${DO_NOT_TRACK-}" && ! truthy "${CHMONITOR_TELEMETRY_DISABLED-}" \
&& { ! truthy "${CI-}" || truthy "${CHMONITOR_TELEMETRY_CI-}"; }; then
os=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m | tr '[:upper:]' '[:lower:]')
if truthy "${CI-}"; then ci=true; else ci=false; fi
body=$(printf '{"event":"install","os":"%s","arch":"%s","ci":%s}' "$os" "$arch" "$ci")
if truthy "${CHMONITOR_TELEMETRY_DEBUG-}"; then
echo "[telemetry] $body" >&2
else
(curl -fsS -m 1 -X POST -H 'content-type: application/json' -d "$body" https://telemetry.anyr.workers.dev/chmonitor >/dev/null 2>&1 &)
fi
fiimport { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { dirname, join } from "node:path";
const ENDPOINT = "https://telemetry.anyr.workers.dev/chmonitor";
const VERSION = "0.0.0"; // set from your package metadata
const env = process.env;
// "Set" means any value except empty, 0 or false.
const truthy = (v?: string): boolean => !!v && !["0", "false"].includes(v.toLowerCase());
function enabled(): boolean {
if (truthy(env.DO_NOT_TRACK) || truthy(env.CHMONITOR_TELEMETRY_DISABLED)) return false;
return !truthy(env.CI) || truthy(env.CHMONITOR_TELEMETRY_CI);
}
// Returns undefined when the event must be skipped: the id cannot be saved (fail closed).
// Debug mode never writes. It reads the id, or shows a placeholder.
function installId(debug: boolean): string | undefined {
const file = join(env.XDG_CONFIG_HOME || join(homedir(), ".config"), "chmonitor", "telemetry-id");
try {
const id = readFileSync(file, "utf8").trim();
if (id) return id;
} catch {}
if (debug) return "new-install-id";
try {
const id = crypto.randomUUID();
mkdirSync(dirname(file), { recursive: true });
writeFileSync(file, id);
console.error("chmonitor collects anonymous usage data. Opt out: CHMONITOR_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1. Details: https://telemetry.anyr.workers.dev/chmonitor"); // stderr suits a CLI. A daemon or plugin should use its log channel.
return id;
} catch {
return undefined;
}
}
export function track(event: string, props: Record<string, string | number | boolean> = {}): void {
try {
if (!enabled()) return;
const debug = truthy(env.CHMONITOR_TELEMETRY_DEBUG);
const id = installId(debug);
if (!id) return;
const body = JSON.stringify({
event,
id,
version: VERSION,
os: process.platform,
arch: process.arch,
ci: truthy(env.CI),
props,
});
if (debug) {
console.error("[telemetry]", body);
return;
}
fetch(ENDPOINT, {
method: "POST",
headers: { "content-type": "application/json" },
body,
signal: AbortSignal.timeout(1000),
}).catch(() => {});
} catch {}
}// Cargo.toml: ureq = "2"
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hasher};
use std::path::PathBuf;
use std::{env, fs, thread, time::Duration};
const ENDPOINT: &str = "https://telemetry.anyr.workers.dev/chmonitor";
/// "Set" means any value except empty, 0 or false.
fn truthy(key: &str) -> bool {
env::var(key).map_or(false, |v| !matches!(v.to_ascii_lowercase().as_str(), "" | "0" | "false"))
}
fn enabled() -> bool {
if truthy("DO_NOT_TRACK") || truthy("CHMONITOR_TELEMETRY_DISABLED") {
return false;
}
!truthy("CI") || truthy("CHMONITOR_TELEMETRY_CI")
}
fn random_id() -> String {
// 128 random bits from the std hasher seed, no extra crate needed.
(0..2).map(|_| format!("{:016x}", RandomState::new().build_hasher().finish())).collect()
}
/// None means skip the event: the id cannot be saved (fail closed).
/// Debug mode never writes. It reads the id, or shows a placeholder.
fn install_id(debug: bool) -> Option<String> {
let file = env::var_os("XDG_CONFIG_HOME")
.map(PathBuf::from)
.or_else(|| env::var_os("HOME").map(|h| PathBuf::from(h).join(".config")))
.or_else(|| env::var_os("APPDATA").map(PathBuf::from))?
.join("chmonitor")
.join("telemetry-id");
if let Ok(id) = fs::read_to_string(&file) {
if !id.trim().is_empty() {
return Some(id.trim().to_string());
}
}
if debug {
return Some("new-install-id".to_string());
}
let id = random_id();
fs::create_dir_all(file.parent()?).ok()?;
fs::write(&file, &id).ok()?;
eprintln!("chmonitor collects anonymous usage data. Opt out: CHMONITOR_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1. Details: https://telemetry.anyr.workers.dev/chmonitor"); // stderr suits a CLI. A daemon or plugin should use its log channel.
Some(id)
}
/// Fire and forget. Event names must be plain literals. A short-lived CLI can
/// join the returned handle before exit: the request is bounded by the 1 s timeout.
pub fn track(event: &str) -> Option<thread::JoinHandle<()>> {
if !enabled() {
return None;
}
let debug = truthy("CHMONITOR_TELEMETRY_DEBUG");
let id = install_id(debug)?;
let body = format!(
r#"{{"event":"{event}","id":"{id}","version":"{}","os":"{}","arch":"{}","ci":{}}}"#,
env!("CARGO_PKG_VERSION"),
env::consts::OS,
env::consts::ARCH,
truthy("CI")
);
if debug {
eprintln!("[telemetry] {body}");
return None;
}
Some(thread::spawn(move || {
let _ = ureq::post(ENDPOINT)
.timeout(Duration::from_secs(1))
.set("content-type", "application/json")
.send_string(&body);
}))
}package telemetry
import (
"bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"time"
)
const endpoint = "https://telemetry.anyr.workers.dev/chmonitor"
const version = "0.0.0" // set from your build, for example with -ldflags
// truthy: "set" means any value except empty, 0 or false.
func truthy(key string) bool {
switch strings.ToLower(os.Getenv(key)) {
case "", "0", "false":
return false
}
return true
}
func enabled() bool {
if truthy("DO_NOT_TRACK") || truthy("CHMONITOR_TELEMETRY_DISABLED") {
return false
}
return !truthy("CI") || truthy("CHMONITOR_TELEMETRY_CI")
}
// installID returns "" when the event must be skipped: the id cannot be saved (fail closed).
// Debug mode never writes. It reads the id, or shows a placeholder.
func installID(debug bool) string {
dir, err := os.UserConfigDir()
if err != nil {
return ""
}
file := filepath.Join(dir, "chmonitor", "telemetry-id")
if b, err := os.ReadFile(file); err == nil {
if id := strings.TrimSpace(string(b)); id != "" {
return id
}
}
if debug {
return "new-install-id"
}
buf := make([]byte, 16)
if _, err := rand.Read(buf); err != nil {
return ""
}
id := hex.EncodeToString(buf)
if os.MkdirAll(filepath.Dir(file), 0o755) != nil || os.WriteFile(file, []byte(id), 0o600) != nil {
return ""
}
fmt.Fprintln(os.Stderr, "chmonitor collects anonymous usage data. Opt out: CHMONITOR_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1. Details: https://telemetry.anyr.workers.dev/chmonitor") // stderr suits a CLI. A daemon or plugin should use its log channel.
return id
}
// Track sends one event in the background. The channel closes when the attempt
// ends (at most 1 s), so a short-lived CLI can wait on it before exit.
func Track(event string, props map[string]any) <-chan struct{} {
done := make(chan struct{})
if !enabled() {
close(done)
return done
}
debug := truthy("CHMONITOR_TELEMETRY_DEBUG")
id := installID(debug)
if id == "" {
close(done)
return done
}
payload := map[string]any{
"event": event,
"id": id,
"version": version,
"os": runtime.GOOS,
"arch": runtime.GOARCH,
"ci": truthy("CI"),
}
if len(props) > 0 {
payload["props"] = props
}
body, err := json.Marshal(payload)
if err != nil {
close(done)
return done
}
if debug {
fmt.Fprintf(os.Stderr, "[telemetry] %s\n", body)
close(done)
return done
}
go func() {
defer close(done)
client := http.Client{Timeout: time.Second}
if resp, err := client.Post(endpoint, "application/json", bytes.NewReader(body)); err == nil {
resp.Body.Close()
}
}()
return done
}import json
import os
import platform
import sys
import threading
import urllib.request
import uuid
from pathlib import Path
ENDPOINT = "https://telemetry.anyr.workers.dev/chmonitor"
VERSION = "0.0.0" # set from your package metadata
def _truthy(key):
# "Set" means any value except empty, 0 or false.
return os.environ.get(key, "").lower() not in ("", "0", "false")
def _enabled():
if _truthy("DO_NOT_TRACK") or _truthy("CHMONITOR_TELEMETRY_DISABLED"):
return False
return not _truthy("CI") or _truthy("CHMONITOR_TELEMETRY_CI")
def _install_id(debug):
# Returns None when the event must be skipped: the id cannot be saved (fail closed).
# Debug mode never writes. It reads the id, or shows a placeholder.
try:
base = Path(os.environ.get("XDG_CONFIG_HOME") or Path.home() / ".config")
file = base / "chmonitor" / "telemetry-id"
try:
found = file.read_text().strip()
if found:
return found
except OSError:
pass
if debug:
return "new-install-id"
install_id = uuid.uuid4().hex
file.parent.mkdir(parents=True, exist_ok=True)
file.write_text(install_id)
# stderr suits a CLI. A daemon or plugin should use its log channel.
print("chmonitor collects anonymous usage data. Opt out: CHMONITOR_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1. Details: https://telemetry.anyr.workers.dev/chmonitor", file=sys.stderr)
return install_id
except Exception:
return None
def track(event, **props):
try:
if not _enabled():
return
debug = _truthy("CHMONITOR_TELEMETRY_DEBUG")
install_id = _install_id(debug)
if not install_id:
return
body = {
"event": event,
"id": install_id,
"version": VERSION,
"os": platform.system().lower(),
"arch": platform.machine().lower(),
"ci": _truthy("CI"),
}
if props:
body["props"] = props
if debug:
print("[telemetry]", json.dumps(body), file=sys.stderr)
return
def send():
try:
req = urllib.request.Request(
ENDPOINT,
json.dumps(body).encode(),
{"content-type": "application/json"},
)
urllib.request.urlopen(req, timeout=1).close()
except Exception:
pass
# Daemon thread: never delays exit. A very short script may exit before it sends.
threading.Thread(target=send, daemon=True).start()
except Exception:
passconst ENDPOINT = "https://telemetry.anyr.workers.dev/chmonitor";
const ID_KEY = "chmonitor-telemetry-id";
const OFF_KEY = "chmonitor-telemetry-disabled";
export function track(event, props) {
try {
if (navigator.doNotTrack === "1" || navigator.globalPrivacyControl === true) return;
if (localStorage.getItem(OFF_KEY) === "1") return;
let id = localStorage.getItem(ID_KEY);
if (!id) {
id = crypto.randomUUID();
localStorage.setItem(ID_KEY, id);
}
const body = JSON.stringify({ event, id, props });
navigator.sendBeacon(ENDPOINT, new Blob([body], { type: "application/json" }));
} catch {}
}
// track("ping"); // once per page load or once per day. Never send URLs, paths or user input.export OTEL_EXPORTER_OTLP_ENDPOINT=https://telemetry.anyr.workers.dev/chmonitor
export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
export OTEL_SERVICE_NAME=chmonitor
# The install id keeps users distinct. Generate it once and persist it.
export OTEL_RESOURCE_ATTRIBUTES=service.instance.id=<random-install-id>,service.version=1.0.0With an agent
Copy this prompt into Claude Code, Codex or Cursor. It points the agent at the project playbook.
Add anonymous usage telemetry to this repo. Follow https://telemetry.anyr.workers.dev/chmonitor/install.md exactly. Respect DO_NOT_TRACK, add an opt-out env var and a Telemetry section to the README, never send PII, never block the main path.