Skip to content

Metrics #1820

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft

Metrics #1820

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 36 additions & 29 deletions cli/spam.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/cli/prompt"
"github.com/ava-labs/hypersdk/codec"
"github.com/ava-labs/hypersdk/consts"
"github.com/ava-labs/hypersdk/crypto/ed25519"
"github.com/ava-labs/hypersdk/throughput"
)
Expand Down Expand Up @@ -73,38 +72,46 @@ func (h *Handler) BuildSpammer(sh throughput.SpamHelper, spamKey string, default
return throughput.NewSpammer(sc, sh)
}
// Collect parameters
numAccounts, err := prompt.Int("number of accounts", consts.MaxInt)
if err != nil {
return nil, err
}
numAccounts := 500
// numAccounts, err := prompt.Int("number of accounts", consts.MaxInt)
// if err != nil {
// return nil, err
// }
if numAccounts < 2 {
return nil, ErrInsufficientAccounts
}
sZipf, err := prompt.Float("s (Zipf distribution = [(v+k)^(-s)], Default = 1.01)", consts.MaxFloat64)
if err != nil {
return nil, err
}
vZipf, err := prompt.Float("v (Zipf distribution = [(v+k)^(-s)], Default = 2.7)", consts.MaxFloat64)
if err != nil {
return nil, err
}

txsPerSecond, err := prompt.Int("txs to try and issue per second", consts.MaxInt)
if err != nil {
return nil, err
}
minTxsPerSecond, err := prompt.Int("minimum txs to issue per second", consts.MaxInt)
if err != nil {
return nil, err
}
txsPerSecondStep, err := prompt.Int("txs to increase per second", consts.MaxInt)
if err != nil {
return nil, err
}
numClients, err := prompt.Int("number of clients per node", consts.MaxInt)
if err != nil {
return nil, err
}
sZipf := 1.01
// sZipf, err := prompt.Float("s (Zipf distribution = [(v+k)^(-s)], Default = 1.01)", consts.MaxFloat64)
// if err != nil {
// return nil, err
// }
vZipf := 2.7
// vZipf, err := prompt.Float("v (Zipf distribution = [(v+k)^(-s)], Default = 2.7)", consts.MaxFloat64)
// if err != nil {
// return nil, err
// }

txsPerSecond := 5000
// txsPerSecond, err := prompt.Int("txs to try and issue per second", consts.MaxInt)
// if err != nil {
// return nil, err
// }
minTxsPerSecond := 100
// minTxsPerSecond, err := prompt.Int("minimum txs to issue per second", consts.MaxInt)
// if err != nil {
// return nil, err
// }
txsPerSecondStep := 50
// txsPerSecondStep, err := prompt.Int("txs to increase per second", consts.MaxInt)
// if err != nil {
// return nil, err
// }
numClients := 100
// numClients, err := prompt.Int("number of clients per node", consts.MaxInt)
// if err != nil {
// return nil, err
// }

sc := throughput.NewConfig(
uris,
Expand Down
109 changes: 61 additions & 48 deletions internal/prometheus/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,87 +171,98 @@ rm -rf prometheus-2.43.0.darwin-amd64
func generateChainPanels(chainID ids.ID) []string {
panels := []string{}

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_empty_block_built[5s])", chainID))
utils.Outf("{{yellow}}empty blocks built (5s):{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_build_capped[5s])", chainID))
utils.Outf("{{yellow}}build time capped (5s):{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("avalanche_%s_blks_processing", chainID))
utils.Outf("{{yellow}}blocks processing:{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_blks_accepted_count[5s])/5", chainID))
utils.Outf("{{yellow}}blocks accepted per second:{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_blks_rejected_count[5s])/5", chainID))
utils.Outf("{{yellow}}blocks rejected per second:{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_vm_deleted_blocks[5s])/5", chainID))
utils.Outf("{{yellow}}blocks deleted per second:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_bandwidth_price", chainID))
utils.Outf("{{yellow}}bandwidth unit price:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_bandwidth_price", chainID))
// utils.Outf("{{yellow}}bandwidth unit price:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_compute_price", chainID))
utils.Outf("{{yellow}}compute unit price:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_compute_price", chainID))
// utils.Outf("{{yellow}}compute unit price:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_storage_read_price", chainID))
utils.Outf("{{yellow}}storage read unit price:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_storage_read_price", chainID))
// utils.Outf("{{yellow}}storage read unit price:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_storage_create_price", chainID))
utils.Outf("{{yellow}}storage create unit price:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_storage_create_price", chainID))
// utils.Outf("{{yellow}}storage create unit price:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_storage_modify_price", chainID))
utils.Outf("{{yellow}}storage modify unit price:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_storage_modify_price", chainID))
// utils.Outf("{{yellow}}storage modify unit price:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_vm_txs_submitted[5s])/5", chainID))
utils.Outf("{{yellow}}transactions submitted per second:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_vm_txs_submitted[5s])/5", chainID))
// utils.Outf("{{yellow}}transactions submitted per second:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_vm_txs_gossiped[5s])/5", chainID))
utils.Outf("{{yellow}}transactions gossiped per second:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_vm_txs_gossiped[5s])/5", chainID))
// utils.Outf("{{yellow}}transactions gossiped per second:{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_vm_txs_received[5s])/5", chainID))
utils.Outf("{{yellow}}transactions received per second:{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_vm_seen_txs_received[5s])/5", chainID))
utils.Outf("{{yellow}}seen transactions received per second:{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_vm_txs_verified[5s])/5", chainID))
utils.Outf("{{yellow}}transactions verified per second:{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_vm_txs_accepted[5s])/5", chainID))
utils.Outf("{{yellow}}transactions accepted per second:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_state_operations[5s])/5", chainID))
utils.Outf("{{yellow}}state operations per second:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_state_operations[5s])/5", chainID))
// utils.Outf("{{yellow}}state operations per second:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_state_changes[5s])/5", chainID))
utils.Outf("{{yellow}}state changes per second:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_state_changes[5s])/5", chainID))
// utils.Outf("{{yellow}}state changes per second:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_root_calculated_sum[5s])/1000000/5", chainID))
utils.Outf("{{yellow}}root calcuation (ms/s):{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_root_calculated_sum[5s])/1000000/5", chainID))
// utils.Outf("{{yellow}}root calcuation (ms/s):{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_wait_root_sum[5s])/1000000/5", chainID))
utils.Outf("{{yellow}}wait root calculation (ms/s):{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_wait_root_sum[5s])/1000000/5", chainID))
// utils.Outf("{{yellow}}wait root calculation (ms/s):{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_wait_signatures_sum[5s])/1000000/5", chainID))
utils.Outf("{{yellow}}signature verification wait (ms/s):{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_wait_signatures_sum[5s])/1000000/5", chainID))
// utils.Outf("{{yellow}}signature verification wait (ms/s):{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_cleared_mempool[5s])/5", chainID))
utils.Outf("{{yellow}}cleared mempool per second:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_cleared_mempool[5s])/5", chainID))
// utils.Outf("{{yellow}}cleared mempool per second:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_mempool_size", chainID))
utils.Outf("{{yellow}}mempool size:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("avalanche_%s_vm_hypersdk_chain_mempool_size", chainID))
// utils.Outf("{{yellow}}mempool size:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, "avalanche_resource_tracker_cpu_usage")
utils.Outf("{{yellow}}CPU usage:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, "avalanche_resource_tracker_cpu_usage")
// utils.Outf("{{yellow}}CPU usage:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, "avalanche_go_memstats_alloc_bytes")
utils.Outf("{{yellow}}memory (avalanchego) usage:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, "avalanche_go_memstats_alloc_bytes")
// utils.Outf("{{yellow}}memory (avalanchego) usage:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("avalanche_%s_vm_go_memstats_alloc_bytes", chainID))
utils.Outf("{{yellow}}memory (morpheusvm) usage:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("avalanche_%s_vm_go_memstats_alloc_bytes", chainID))
// utils.Outf("{{yellow}}memory (morpheusvm) usage:{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_handler_chits_sum[5s])/1000000/5 + increase(avalanche_%s_handler_notify_sum[5s])/1000000/5 + increase(avalanche_%s_handler_get_sum[5s])/1000000/5 + increase(avalanche_%s_handler_push_query_sum[5s])/1000000/5 + increase(avalanche_%s_handler_put_sum[5s])/1000000/5 + increase(avalanche_%s_handler_pull_query_sum[5s])/1000000/5 + increase(avalanche_%s_handler_query_failed_sum[5s])/1000000/5", chainID, chainID, chainID, chainID, chainID, chainID, chainID))
utils.Outf("{{yellow}}consensus engine processing (ms/s):{{/}} %s\n", panels[len(panels)-1])

Expand All @@ -276,32 +287,34 @@ func generateChainPanels(chainID ids.ID) []string {
panels = append(panels, OutboundFailed)
utils.Outf("{{yellow}}outbound failed:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_build_sum[5s])/1000000/5", chainID))
utils.Outf("{{yellow}}block build (ms/s):{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_build_sum[5s])/1000000/5", chainID))
// utils.Outf("{{yellow}}block build (ms/s):{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_parse_sum[5s])/1000000/5", chainID))
utils.Outf("{{yellow}}block parse (ms/s):{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_parse_sum[5s])/1000000/5", chainID))
// utils.Outf("{{yellow}}block parse (ms/s):{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_verify_sum[5s])/1000000/5", chainID))
utils.Outf("{{yellow}}block verify (ms/s):{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_verify_sum[5s])/1000000/5", chainID))
// utils.Outf("{{yellow}}block verify (ms/s):{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_accept_sum[5s])/1000000/5", chainID))
utils.Outf("{{yellow}}block accept (ms/s):{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_accept_sum[5s])/1000000/5", chainID))
// utils.Outf("{{yellow}}block accept (ms/s):{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_process_sum[5s])/1000000/5", chainID))
utils.Outf("{{yellow}}block process [async] (ms/s):{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_block_process_sum[5s])/1000000/5", chainID))
// utils.Outf("{{yellow}}block process [async] (ms/s):{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_state_merkleDB_intermediate_node_cache_hit[5s])/(increase(avalanche_%s_vm_state_merkleDB_intermediate_node_cache_miss[5s]) + increase(avalanche_%s_vm_state_merkleDB_intermediate_node_cache_hit[5s]))", chainID, chainID, chainID))
utils.Outf("{{yellow}}intermediate node cache hit rate:{{/}} %s\n", panels[len(panels)-1])

//FIXME: no data
panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_state_merkleDB_value_node_cache_hit[5s])/(increase(avalanche_%s_vm_state_merkleDB_value_node_cache_miss[5s]) + increase(avalanche_%s_vm_state_merkleDB_value_node_cache_hit[5s]))", chainID, chainID, chainID))
utils.Outf("{{yellow}}value node cache hit rate:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_executor_build_executable[5s]) / (increase(avalanche_%s_vm_hypersdk_chain_executor_build_blocked[5s]) + increase(avalanche_%s_vm_hypersdk_chain_executor_build_executable[5s]))", chainID, chainID, chainID))
utils.Outf("{{yellow}}build txs executable (%%) per second:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_executor_build_executable[5s]) / (increase(avalanche_%s_vm_hypersdk_chain_executor_build_blocked[5s]) + increase(avalanche_%s_vm_hypersdk_chain_executor_build_executable[5s]))", chainID, chainID, chainID))
// utils.Outf("{{yellow}}build txs executable (%%) per second:{{/}} %s\n", panels[len(panels)-1])

panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_hypersdk_chain_executor_verify_executable[5s]) / (increase(avalanche_%s_vm_hypersdk_chain_executor_verify_blocked[5s]) + increase(avalanche_%s_vm_hypersdk_chain_executor_verify_executable[5s]))", chainID, chainID, chainID))
utils.Outf("{{yellow}}verify txs executable (%%) per second:{{/}} %s\n", panels[len(panels)-1])
// panels = append(panels, fmt.Sprintf("increase(avalanche_%s_vm_chain_chain_executor_verify_executable[5s]) / (increase(avalanche_%s_vm_chain_chain_executor_verify_blocked[5s]) + increase(avalanche_%s_vm_chain_chain_executor_verify_executable[5s]))", chainID, chainID, chainID))
// utils.Outf("{{yellow}}verify txs executable (%%) per second:{{/}} %s\n", panels[len(panels)-1])

return panels
}
16 changes: 16 additions & 0 deletions restart_bench.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

set -exu -o pipefail

# Get the absolute path of the script directory
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)

# Kill existing processes
pkill -9 -f avalanchego || true
pkill -9 -f spam || true

# Clean up and restart
rm -rf ~/.tmpnet/
cd "$SCRIPT_DIR/examples/morpheusvm/"
./scripts/run.sh
go run ./cmd/morpheus-cli/ spam run ed25519
29 changes: 29 additions & 0 deletions x/grafana/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Prometheus & Grafana Dashboard for HyperSDK

This setup collects and visualizes metrics from HyperSDK benchmarks.

## Prerequisites
- Docker
- MorpheusVM running locally (default address: localhost:9650)

## Quick Start
1. Run the dashboard using script `./x/grafana/dashboard.sh`
2. Open Grafana at [localhost:3000](http://localhost:3000)
3. Navigate to: [Dashboards > Services > HyperSDK benchmark](http://localhost:3000/d/de4okc3qcxou8f/hypersdk-benchmark)
4. Wait 15 seconds for the first data to appear. If it is not appearing, check that [localhost:9650/ext/metrics](http://localhost:9650/ext/metrics) is accessible and returning metrics.

## Adding New Panels
1. Login to Grafana with:
- Username: admin
- Password: admin
- (Skip the password change prompt)
2. Edit an existing panel
3. Click Save - this will show JSON
4. Copy the JSON to: `./x/grafana//dashboards/hypersdk-bench.json`

5. Restart Grafana to see changes:

```bash
docker rm -f grafana
./x/grafana/dashboard.sh
```
26 changes: 26 additions & 0 deletions x/grafana/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
services:
prometheus:
image: prom/prometheus:v3.0.0
container_name: prometheus
volumes:
- ./configs/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
network_mode: host

grafana:
image: grafana/grafana:11.3.1
container_name: grafana
depends_on:
- prometheus
volumes:
- ./dashboards:/etc/grafana/provisioning/dashboards
- ./datasources:/etc/grafana/provisioning/datasources
network_mode: host
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ORG_ROLE=admin

volumes:
prometheus_data:
7 changes: 7 additions & 0 deletions x/grafana/configs/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
scrape_configs:
- job_name: 'hypersdk'
scrape_interval: 2s

static_configs:
- targets: ['localhost:9650']
metrics_path: '/ext/metrics'
11 changes: 11 additions & 0 deletions x/grafana/dashboard.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
# Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
# See the file LICENSE for licensing terms.


set -exu -o pipefail

SCRIPT_DIR=$(dirname "$0")

docker compose -f "$SCRIPT_DIR/compose.yml" down || true
docker compose -f "$SCRIPT_DIR/compose.yml" up -d
9 changes: 9 additions & 0 deletions x/grafana/dashboards/default.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: 1

providers:
- name: Default # A uniquely identifiable name for the provider
folder: Services # The folder where to place the dashboards
type: file
options:
path:
/etc/grafana/provisioning/dashboards
Loading
Loading