Skip to content

Commit e1f6e65

Browse files
authored
Merge pull request #437 from duyet/chore/ui-update
feat: add support for excluding monitoring users and enhance query history documentation
2 parents a8b5918 + d812b5c commit e1f6e65

File tree

12 files changed

+152
-149
lines changed

12 files changed

+152
-149
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ CLICKHOUSE_PASSWORD=
99
CLICKHOUSE_NAME=
1010
CLICKHOUSE_TZ=
1111
CLICKHOUSE_MAX_EXECUTION_TIME=60
12-
NEXT_QUERY_CACHE_TTL=86400
12+
NEXT_QUERY_CACHE_TTL=86400
13+
CLICKHOUSE_EXCLUDE_USER_DEFAULT=

README.md

Lines changed: 9 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ Features:
2121
**Documentation**:
2222

2323
- https://duyet.github.io/clickhouse-monitoring/
24+
- [Getting Started](https://duyet.github.io/clickhouse-monitoring/getting-started)
25+
- [Local Development](https://duyet.github.io/clickhouse-monitoring/getting-started/local)
26+
- [User Role and Profile](https://duyet.github.io/clickhouse-monitoring/getting-started/clickhouse-requirements)
27+
- [Enable System Tables](https://duyet.github.io/clickhouse-monitoring/getting-started/clickhouse-enable-system-tables)
28+
- [Deployments](https://duyet.github.io/clickhouse-monitoring/deploy)
29+
- [Vercel](https://duyet.github.io/clickhouse-monitoring/deploy/vercel)
30+
- [Docker](https://duyet.github.io/clickhouse-monitoring/deploy/docker)
31+
- [Kubernetes Helm Chart](https://duyet.github.io/clickhouse-monitoring/deploy/k8s)
32+
- [Advanced](https://duyet.github.io/clickhouse-monitoring/advanced)
2433

2534
**Screenshots**:
2635

@@ -33,130 +42,6 @@ Features:
3342
![](.github/screenshots/screenshot_7.png)
3443
![](.github/screenshots/screenshot_8.png)
3544

36-
## Getting Started
37-
38-
To get the project up and running on your local machine, follow these steps:
39-
40-
1. Clone the repository
41-
2. Install dependencies using `npm install` or `yarn install`
42-
3. Create a `.env.local` file by copying the `.env.example` file and filling in the required environment variables:
43-
44-
- `CLICKHOUSE_HOST`: ClickHouse host(s), for example `http://localhost:8123` or `http://ch-1:8123,http://ch-2:8123`
45-
- `CLICKHOUSE_NAME`: (Optional) Name of ClickHouse instance, must match the number of hosts in `CLICKHOUSE_HOST`, for example `localhost` or `ch-1,ch-2`.
46-
- `CLICKHOUSE_USER`: ClickHouse user with permission to query the `system` database.
47-
- `CLICKHOUSE_PASSWORD`: ClickHouse password for the specified user.
48-
- `CLICKHOUSE_MAX_EXECUTION_TIME`: [`max_execution_time`](https://clickhouse.com/docs/en/operations/settings/query-complexity#max-execution-time) timeout in seconds. Default is `10`.
49-
- `CLICKHOUSE_TZ`: ClickHouse server timezone. Default: `''`.
50-
- `NEXT_QUERY_CACHE_TTL`: TTL of [`unstable_cache`](https://nextjs.org/docs/app/api-reference/functions/unstable_cache) - cache the results of most charts to speed up and reuse them across multiple requests. Default: `86400`.
51-
- `NEXT_PUBLIC_LOGO`: (Optional) HTTP path to logo image.
52-
- `EVENTS_TABLE_NAME`: The table name for storing dashboard self-tracking events. Default: `system.monitoring_events`
53-
54-
4. Run the development server with `npm run dev` or `yarn dev`
55-
5. Open [http://localhost:3000](http://localhost:3000) in your browser to see the dashboard.
56-
57-
## ClickHouse Requirements
58-
59-
### 1. Monitoring user role
60-
61-
Suggested role for "monitoring" user must have these privileges on `system` database:
62-
63-
```xml
64-
# File: users.d/monitoring_role.xml
65-
<clickhouse>
66-
<users>
67-
<monitoring>
68-
<password><!-- define password here --></password>
69-
<profile>monitoring_profile</profile>
70-
<networks><ip>::/0</ip></networks>
71-
<grants>
72-
<query>GRANT monitoring_role</query>
73-
</grants>
74-
</monitoring>
75-
</users>
76-
77-
<roles>
78-
<monitoring_role>
79-
<grants>
80-
<query>REVOKE ALL ON *.*</query>
81-
<query>GRANT SELECT,SHOW,dictGet,REMOTE ON *.*</query>
82-
<query>GRANT SELECT,INSERT,ALTER,CREATE,DROP,TRUNCATE,OPTIMIZE,SHOW,dictGet ON system.*</query>
83-
<query>GRANT CREATE TEMPORARY TABLE ON *.*</query>
84-
</grants>
85-
</monitoring_role>
86-
</roles>
87-
</clickhouse>
88-
```
89-
90-
`CREATE TEMPORARY TABLE` is needed because the UI using `FROM merge(system, '^query_log')` allows retrieving all the data from old tables that were renamed during the upgrade.
91-
92-
### 2. Monitoring user profile
93-
94-
```xml
95-
# File: users.d/monitoring_profile.xml
96-
<clickhouse>
97-
<profiles>
98-
<monitoring_profile>
99-
<allow_experimental_analyzer>1</allow_experimental_analyzer>
100-
101-
<!-- Optional: query cache to avoid hit too much queries on database -->
102-
<use_query_cache>1</use_query_cache>
103-
<query_cache_ttl>50</query_cache_ttl>
104-
<query_cache_max_entries>0</query_cache_max_entries>
105-
<query_cache_system_table_handling>save</query_cache_system_table_handling>
106-
<query_cache_nondeterministic_function_handling>save</query_cache_nondeterministic_function_handling>
107-
</monitoring_profile>
108-
</profiles>
109-
</clickhouse>
110-
```
111-
112-
## Deployment
113-
114-
### 1. Vercel
115-
116-
For easy deployment, use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme), created by the makers of Next.js. Refer to the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
117-
118-
### 2. Docker
119-
120-
Using the latest image here: https://github.com/duyet/clickhouse-monitoring/pkgs/container/clickhouse-monitoring
121-
122-
```bash
123-
docker run -it \
124-
-e CLICKHOUSE_HOST='http://localhost' \
125-
-e CLICKHOUSE_USER='default' \
126-
-e CLICKHOUSE_PASSWORD='' \
127-
-e CLICKHOUSE_TZ='Asia/Ho_Chi_Minh' \
128-
-e CLICKHOUSE_MAX_EXECUTION_TIME='15' \
129-
-e NEXT_QUERY_CACHE_TTL='86400' \
130-
--name clickhouse-monitoring \
131-
ghcr.io/duyet/clickhouse-monitoring:main
132-
```
133-
134-
### 3. Kubernetes Helm Chart
135-
136-
Using the latest helm chart here: https://github.com/duyet/charts/tree/master/clickhouse-monitoring
137-
138-
```bash
139-
helm repo add duyet https://duyet.github.io/charts
140-
141-
cat <<EOF >> values.yaml
142-
env:
143-
- name: CLICKHOUSE_HOST
144-
value: http://localhost:8123
145-
- name: CLICKHOUSE_USER
146-
value: default
147-
- name: CLICKHOUSE_PASSWORD
148-
value: ''
149-
- name: CLICKHOUSE_TZ
150-
value: 'Asia/Ho_Chi_Minh'
151-
- name: CLICKHOUSE_MAX_EXECUTION_TIME
152-
value: '15'
153-
- name: NEXT_QUERY_CACHE_TTL
154-
value: '86400'
155-
EOF
156-
157-
helm install -f values.yaml clickhouse-monitoring-release duyet/clickhouse-monitoring
158-
```
159-
16045
## Feedback and Contributions
16146

16247
Feedback and contributions are welcome! Feel free to open issues or submit pull requests.

app/[host]/[query]/queries/history-queries.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const historyQueriesConfig: QueryConfig = {
3737
AND if (notEmpty({event_time: String}), toDate(event_time) = {event_time: String}, true)
3838
AND if ({database: String} != '' AND {table: String} != '', has(tables, format('{}.{}', {database: String}, {table: String})), true)
3939
AND if ({user: String} != '', user = {user: String}, true)
40+
AND if ({excluded_users: String} != '', not(has(splitByChar(',', {excluded_users: String}), user)), true)
4041
ORDER BY event_time DESC
4142
LIMIT 1000
4243
`,
@@ -86,6 +87,7 @@ export const historyQueriesConfig: QueryConfig = {
8687
database: '',
8788
table: '',
8889
user: '',
90+
excluded_users: process.env.CLICKHOUSE_EXCLUDE_USER_DEFAULT || '',
8991
},
9092

9193
filterParamPresets: [
@@ -114,6 +116,11 @@ export const historyQueriesConfig: QueryConfig = {
114116
key: 'duration_1m',
115117
value: '1',
116118
},
119+
{
120+
name: `user != ${process.env.CLICKHOUSE_EXCLUDE_USER_DEFAULT || ''}`,
121+
key: 'excluded_users',
122+
value: process.env.CLICKHOUSE_EXCLUDE_USER_DEFAULT || '',
123+
},
117124
],
118125

119126
relatedCharts: [

components/data-table/data-table-faceted-filter.tsx

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,25 @@ export function DataTableFacetedFilter({
3030
const pathname = usePathname()
3131
const { filterParamPresets = [], defaultParams = {} } = queryConfig
3232

33-
const selected = useMemo(
34-
() => new URLSearchParams(searchParams),
35-
[searchParams]
36-
)
33+
const selected = useMemo(() => {
34+
const params = new URLSearchParams(searchParams)
35+
36+
// Add default params have not null value
37+
Object.entries(defaultParams).forEach(([key, value]) => {
38+
if (value !== '' && !params.has(key)) {
39+
params.set(key, value as string)
40+
}
41+
})
42+
43+
return params
44+
}, [searchParams, defaultParams])
45+
console.log('selected', selected.toString())
3746

3847
const filters = useMemo<
3948
NonNullable<QueryConfig['filterParamPresets']>
4049
>(() => {
4150
const filterNotFromPreset = Object.keys(defaultParams)
42-
// key in URL Params
51+
// Key in URL Params
4352
.filter((key) => selected.has(key))
4453
// And custom that value is not in presets
4554
.filter(
@@ -50,8 +59,10 @@ export function DataTableFacetedFilter({
5059
)
5160
)
5261
.map((key) => ({
53-
name: `${key} = ${selected.get(key)} *`,
5462
key,
63+
name: selected.get(key)
64+
? filterParamPresets.find((preset) => preset.key === key)?.name
65+
: `${key} = N/A`,
5566
value: selected.get(key),
5667
})) as NonNullable<QueryConfig['filterParamPresets']>
5768
console.log('filterNotFromPreset', filterNotFromPreset)
@@ -90,7 +101,13 @@ export function DataTableFacetedFilter({
90101
name={name}
91102
value={value}
92103
isSelected={selected.get(key) === value}
93-
href={getUpdatedHref(pathname, searchParams, key, value)}
104+
href={getUpdatedHref(
105+
pathname,
106+
searchParams,
107+
key,
108+
value,
109+
defaultParams
110+
)}
94111
icon={preset?.icon}
95112
filterKey={key}
96113
filterValue={value}
@@ -133,9 +150,8 @@ function FilterMenuItem({
133150
}: FilterMenuItemProps) {
134151
return (
135152
<DropdownMenuItem>
136-
<Link
153+
<a
137154
href={href}
138-
replace={true}
139155
data-selected={isSelected}
140156
className={cn(
141157
'flex flex-row content-between items-center gap-3',
@@ -144,7 +160,7 @@ function FilterMenuItem({
144160
>
145161
{Icon && <Icon className="mr-2 size-4 text-muted-foreground" />}
146162
<span>{name}</span>
147-
</Link>
163+
</a>
148164
</DropdownMenuItem>
149165
)
150166
}
@@ -154,12 +170,34 @@ function getUpdatedHref(
154170
pathname: string,
155171
searchParams: URLSearchParams,
156172
key: string,
157-
value: string
173+
value: string,
174+
defaultParams: QueryConfig['defaultParams']
158175
) {
159176
const newParams = new URLSearchParams(searchParams)
160177
if (newParams.get(key) === value) {
161-
newParams.delete(key)
178+
/**
179+
* For example the query config has defaultParams = { type: 'abc' }
180+
* and the URL has ?type=abc. If the user clicks on the filter to toggle it off,
181+
* We should set the URL to ?type= instead of removing the key completely,
182+
* as the default value is still 'abc'.
183+
*/
184+
if (defaultParams?.[key] !== '') {
185+
newParams.set(key, '')
186+
} else {
187+
/**
188+
* If the default value is an empty string, we can just remove the key from the URL
189+
* as there is no default value to fall back to.
190+
*
191+
* For example, if the query config has defaultParams = { type: '' }
192+
* and the URL has ?type=abc. If the user clicks on the filter to toggle it off,
193+
* We can set the URL to ? instead of ?type=abc.
194+
*/
195+
newParams.delete(key)
196+
}
162197
} else {
198+
/**
199+
* If the filter is not selected, we should set the URL to ?key=value
200+
*/
163201
newParams.set(key, value)
164202
}
165203
return `${pathname}?${newParams.toString()}`

docs/pages/advanced/_meta.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
export const meta = {
22
'multiple-hosts': 'Multiple Hosts',
3-
'custom-name': 'Custom ClickHouse Name',
3+
'custom-name': 'Custom Host Name',
4+
'queries-history': 'Queries History',
5+
'self-tracking': 'Self-Tracking',
46
}
57

68
export default meta

docs/pages/advanced/multiple-hosts.mdx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,26 @@ All these hosts will share the same `CLICKHOUSE_USER` and `CLICKHOUSE_PASSWORD`.
1818

1919
<Tabs items={['Docker (same user)', 'Docker (different user)']}>
2020
<Tabs.Tab>
21-
```bash docker run -it \ -e
22-
CLICKHOUSE_HOST='http://ch-1:8123,http://ch-2:8123' \ -e
23-
CLICKHOUSE_NAME='ch-1,ch-2' \ -e CLICKHOUSE_USER='default' \ -e
24-
CLICKHOUSE_PASSWORD='' \ --name clickhouse-monitoring \
25-
ghcr.io/duyet/clickhouse-monitoring:main ```
21+
```bash
22+
docker run -it \
23+
-e CLICKHOUSE_HOST='http://ch-1:8123,http://ch-2:8123' \
24+
-e CLICKHOUSE_NAME='ch-1,ch-2' \
25+
-e CLICKHOUSE_USER='default' \
26+
-e CLICKHOUSE_PASSWORD='' \
27+
--name clickhouse-monitoring \
28+
ghcr.io/duyet/clickhouse-monitoring:main
29+
```
2630
</Tabs.Tab>
2731
<Tabs.Tab>
28-
```bash docker run -it \ -e
29-
CLICKHOUSE_HOST='http://ch-1:8123,http://ch-2:8123' \ -e
30-
CLICKHOUSE_NAME='ch-1,ch-2' \ -e CLICKHOUSE_USER='user1,user2' \ -e
31-
CLICKHOUSE_PASSWORD='password1,password2' \ --name clickhouse-monitoring \
32-
ghcr.io/duyet/clickhouse-monitoring:main ```
32+
```bash
33+
docker run -it \
34+
-e CLICKHOUSE_HOST='http://ch-1:8123,http://ch-2:8123' \
35+
-e CLICKHOUSE_NAME='ch-1,ch-2' \
36+
-e CLICKHOUSE_USER='user1,user2' \
37+
-e CLICKHOUSE_PASSWORD='password1,password2' \
38+
--name clickhouse-monitoring \
39+
ghcr.io/duyet/clickhouse-monitoring:main
40+
```
3341
</Tabs.Tab>
3442
</Tabs>
3543

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Tabs } from 'nextra/components'
2+
3+
# Query History
4+
5+
## 1. Excluding monitoring users by default
6+
7+
![](/excluding-users.png)
8+
9+
You can configured the [`/history-queries`](http://clickhouse-monitoring.vercel.app/0/history-queries) page to filter out the `monitoring` users by default.
10+
To do this, using these environment variables:
11+
12+
- `CLICKHOUSE_EXCLUDE_USER_DEFAULT`: for example `monitoring,healthcheck`.
13+
14+
Examples
15+
16+
<Tabs items={['Docker', 'Kubernetes']}>
17+
<Tabs.Tab>
18+
```bash
19+
docker run -it \
20+
-e CLICKHOUSE_HOST='http://ch-1:8123' \
21+
-e CLICKHOUSE_NAME='ch-1' \
22+
-e CLICKHOUSE_USER='monitoring' \
23+
-e CLICKHOUSE_PASSWORD='' \
24+
-e CLICKHOUSE_EXCLUDE_USER_DEFAULT='monitoring,healthcheck' \
25+
--name clickhouse-monitoring \
26+
ghcr.io/duyet/clickhouse-monitoring:main
27+
```
28+
</Tabs.Tab>
29+
<Tabs.Tab>
30+
```bash
31+
helm repo add duyet https://duyet.github.io/charts
32+
33+
cat <<EOF >> values.yaml
34+
env:
35+
- name: CLICKHOUSE_HOST
36+
value: http://ch-1:8123
37+
- name: CLICKHOUSE_NAME
38+
value: ch-1
39+
- name: CLICKHOUSE_USER
40+
value: monitoring
41+
- name: CLICKHOUSE_PASSWORD
42+
value: ''
43+
- name: CLICKHOUSE_EXCLUDE_USER_DEFAULT
44+
value: monitoring,healthcheck
45+
EOF
46+
47+
helm install -f values.yaml clickhouse-monitoring-release duyet/clickhouse-monitoring
48+
```
49+
</Tabs.Tab>
50+
</Tabs>

0 commit comments

Comments
 (0)