Skip to content

Commit

Permalink
api: add prometheus format to endpoints
Browse files Browse the repository at this point in the history
api,prometheus: fix lint errors

api: remove debug logging, remove panic
  • Loading branch information
n8maninger committed Jan 15, 2024
1 parent f6d2e4e commit 40e2843
Show file tree
Hide file tree
Showing 5 changed files with 325 additions and 10 deletions.
8 changes: 4 additions & 4 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ type (
UpdateDDNS(force bool) error
}

// Metrics retrieves metrics related to the host
Metrics interface {
// A MetricManager retrieves metrics related to the host
MetricManager interface {
// PeriodMetrics returns metrics for n periods starting at start.
PeriodMetrics(start time.Time, periods int, interval metrics.Interval) (period []metrics.Metrics, err error)
// Metrics returns aggregated metrics for the host as of the timestamp.
Expand Down Expand Up @@ -147,7 +147,7 @@ type (
contracts ContractManager
volumes VolumeManager
wallet Wallet
metrics Metrics
metrics MetricManager
settings Settings
sessions RHPSessionReporter

Expand All @@ -157,7 +157,7 @@ type (
)

// NewServer initializes the API
func NewServer(name string, hostKey types.PublicKey, a Alerts, wh WebHooks, g Syncer, chain ChainManager, tp TPool, cm ContractManager, am AccountManager, vm VolumeManager, rsr RHPSessionReporter, m Metrics, s Settings, w Wallet, log *zap.Logger) http.Handler {
func NewServer(name string, hostKey types.PublicKey, a Alerts, wh WebHooks, g Syncer, chain ChainManager, tp TPool, cm ContractManager, am AccountManager, vm VolumeManager, rsr RHPSessionReporter, m MetricManager, s Settings, w Wallet, log *zap.Logger) http.Handler {
api := &api{
hostKey: hostKey,
name: name,
Expand Down
41 changes: 36 additions & 5 deletions api/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"go.sia.tech/hostd/host/settings"
"go.sia.tech/hostd/host/storage"
"go.sia.tech/hostd/internal/disk"
"go.sia.tech/hostd/internal/prometheus"
"go.sia.tech/hostd/webhooks"
"go.sia.tech/jape"
"go.sia.tech/siad/modules"
Expand All @@ -38,14 +39,42 @@ func (a *api) checkServerError(c jape.Context, context string, err error) bool {
return err == nil
}

func (a *api) writeResponse(c jape.Context, code int, resp any) {
var responseFormat string
if err := c.DecodeForm("response", &responseFormat); err != nil {
return
}

if resp != nil {
switch responseFormat {
case "prometheus":
v, ok := resp.(prometheus.Marshaller)
if !ok {
err := fmt.Errorf("response does not implement prometheus.Marshaller %T", resp)
c.Error(err, http.StatusInternalServerError)
a.log.Error("response does not implement prometheus.Marshaller", zap.Stack("stack"), zap.Error(err))
return
}

enc := prometheus.NewEncoder(c.ResponseWriter)
if err := enc.Append(v); err != nil {
a.log.Error("failed to marshal prometheus response", zap.Error(err))
return
}
default:
c.Encode(resp)
}
}
}

func (a *api) handleGETHostState(c jape.Context) {
announcement, err := a.settings.LastAnnouncement()
if err != nil {
c.Error(err, http.StatusInternalServerError)
return
}

c.Encode(HostState{
a.writeResponse(c, http.StatusOK, HostState{
Name: a.name,
PublicKey: a.hostKey,
WalletAddress: a.wallet.Address(),
Expand All @@ -62,7 +91,7 @@ func (a *api) handleGETHostState(c jape.Context) {
}

func (a *api) handleGETConsensusState(c jape.Context) {
c.Encode(ConsensusState{
a.writeResponse(c, http.StatusOK, ConsensusState{
Synced: a.chain.Synced(),
ChainIndex: a.chain.TipState().Index,
})
Expand Down Expand Up @@ -123,7 +152,8 @@ func (a *api) handlePOSTAnnounce(c jape.Context) {
}

func (a *api) handleGETSettings(c jape.Context) {
c.Encode(a.settings.Settings())
hs := HostSettings(a.settings.Settings())
a.writeResponse(c, http.StatusOK, hs)
}

func (a *api) handlePATCHSettings(c jape.Context) {
Expand Down Expand Up @@ -186,7 +216,8 @@ func (a *api) handleGETMetrics(c jape.Context) {
if !a.checkServerError(c, "failed to get metrics", err) {
return
}
c.Encode(metrics)

a.writeResponse(c, http.StatusOK, Metrics(metrics))
}

func (a *api) handleGETPeriodMetrics(c jape.Context) {
Expand Down Expand Up @@ -339,7 +370,7 @@ func (a *api) handleGETWallet(c jape.Context) {
if !a.checkServerError(c, "failed to get wallet", err) {
return
}
c.Encode(WalletResponse{
a.writeResponse(c, http.StatusOK, WalletResponse{
ScanHeight: a.wallet.ScanHeight(),
Address: a.wallet.Address(),
Spendable: spendable,
Expand Down
273 changes: 273 additions & 0 deletions api/prometheus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
package api

import (
"time"

rhp2 "go.sia.tech/core/rhp/v2"
"go.sia.tech/hostd/internal/prometheus"
)

// PrometheusMetric returns a Prometheus metric for the host state.
func (hs HostState) PrometheusMetric() []prometheus.Metric {
return []prometheus.Metric{
{
Name: "hostd_host_state",
Labels: map[string]any{
"name": hs.Name,
"public_key": hs.PublicKey,
"wallet_address": hs.WalletAddress,
"network": hs.Network,
"version": hs.Version,
"commit": hs.Commit,
"os": hs.OS,
"build_time": hs.BuildTime,
},
},
{
Name: "hostd_start_time",
Value: 1,
Timestamp: hs.StartTime,
},
{
Name: "hostd_last_announcement",
Labels: map[string]any{"address": hs.LastAnnouncement.Address, "id": hs.LastAnnouncement.Index.ID},
Value: float64(hs.LastAnnouncement.Index.Height),
Timestamp: time.Now(),
},
}
}

// PrometheusMetric returns a Prometheus metric for the consensus state.
func (cs ConsensusState) PrometheusMetric() []prometheus.Metric {
return []prometheus.Metric{
{
Name: "hostd_consensus_state_synced",
Value: func() float64 {
if cs.Synced {
return 1
}
return 0
}(),
Timestamp: time.Now(),
},
{
Name: "hostd_consensus_state_index",
Labels: map[string]any{"id": cs.ChainIndex.ID},
Value: float64(cs.ChainIndex.Height),
Timestamp: time.Now(),
},
}
}

// PrometheusMetric returns Prometheus samples for the host settings.
func (hs HostSettings) PrometheusMetric() []prometheus.Metric {
return []prometheus.Metric{
{
Name: "hostd_settings",
Labels: map[string]any{
"accepting_contracts": hs.AcceptingContracts,
},
Timestamp: time.Now(),
},
{
Name: "hostd_settings_max_contract_duration",
Value: float64(hs.MaxContractDuration),
Timestamp: time.Now(),
},
{
Name: "hostd_settings_contract_price",
Value: hs.ContractPrice.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_settings_base_rpc_price",
Value: hs.BaseRPCPrice.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_settings_sector_access_price",
Value: hs.SectorAccessPrice.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_settings_storage_price",
Value: hs.StoragePrice.Mul64(1e12).Mul64(4320).Siacoins(), // price * 1TB * 4320 blocks per month
Timestamp: time.Now(),
},
{
Name: "hostd_settings_ingress_price",
Value: hs.IngressPrice.Mul64(1e12).Siacoins(), // price * 1TB
Timestamp: time.Now(),
},
{
Name: "hostd_settings_egress_price",
Value: hs.EgressPrice.Mul64(1e12).Siacoins(), // price * 1TB
Timestamp: time.Now(),
},
}
}

// PrometheusMetric returns Prometheus samples for the host metrics.
func (m Metrics) PrometheusMetric() []prometheus.Metric {
return []prometheus.Metric{
{
Name: "host_metrics_accounts_active",
Value: float64(m.Accounts.Active),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_accounts_balance",
Value: m.Accounts.Balance.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_revenue_potential_rpc",
Value: m.Revenue.Potential.RPC.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_revenue_potential_storage",
Value: m.Revenue.Potential.Storage.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_revenue_potential_ingress",
Value: m.Revenue.Potential.Ingress.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_revenue_potential_egress",
Value: m.Revenue.Potential.Egress.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_revenue_earned_rpc",
Value: m.Revenue.Earned.RPC.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_revenue_earned_storage",
Value: m.Revenue.Earned.Storage.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_revenue_earned_ingress",
Value: m.Revenue.Earned.Ingress.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_revenue_earned_egress",
Value: m.Revenue.Earned.Egress.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_contracts_active",
Value: float64(m.Contracts.Active),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_contracts_pending",
Value: float64(m.Contracts.Pending),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_contracts_rejected",
Value: float64(m.Contracts.Rejected),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_contracts_failed",
Value: float64(m.Contracts.Failed),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_contracts_successful",
Value: float64(m.Contracts.Successful),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_contracts_locked_collateral",
Value: m.Contracts.LockedCollateral.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_metrics_contracts_risked_collateral",
Value: m.Contracts.RiskedCollateral.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_storage_total_bytes",
Value: float64(m.Storage.TotalSectors * rhp2.SectorSize),
Timestamp: time.Now(),
},
{
Name: "hostd_storage_physical_used_bytes",
Value: float64(m.Storage.PhysicalSectors * rhp2.SectorSize),
Timestamp: time.Now(),
},
{
Name: "hostd_storage_contract_used_bytes",
Value: float64(m.Storage.ContractSectors * rhp2.SectorSize),
Timestamp: time.Now(),
},
{
Name: "hostd_storage_temp_used_bytes",
Value: float64(m.Storage.TempSectors * rhp2.SectorSize),
Timestamp: time.Now(),
},
{
Name: "hostd_storage_reads",
Value: float64(m.Storage.Reads),
Timestamp: time.Now(),
},
{
Name: "hostd_storage_writes",
Value: float64(m.Storage.Writes),
Timestamp: time.Now(),
},
{
Name: "hostd_data_rhp_ingress_bytes",
Value: float64(m.Data.RHP.Ingress),
Timestamp: time.Now(),
},
{
Name: "hostd_data_rhp_egress_bytes",
Value: float64(m.Data.RHP.Egress),
Timestamp: time.Now(),
},
{
Name: "hostd_wallet_balance",
Value: m.Balance.Siacoins(),
Timestamp: time.Now(),
},
}
}

// PrometheusMetric returns Prometheus samples for the host wallet.
func (wr WalletResponse) PrometheusMetric() []prometheus.Metric {
return []prometheus.Metric{
{
Name: "hostd_wallet",
Labels: map[string]any{
"address": wr.Address,
},
Value: float64(wr.ScanHeight),
Timestamp: time.Now(),
},
{
Name: "hostd_wallet_spendable",
Value: wr.Spendable.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_wallet_confirmed",
Value: wr.Confirmed.Siacoins(),
Timestamp: time.Now(),
},
{
Name: "hostd_wallet_unconfirmed",
Value: wr.Unconfirmed.Siacoins(),
Timestamp: time.Now(),
},
}
}
Loading

0 comments on commit 40e2843

Please sign in to comment.