diff --git a/collectors/channels_collector.go b/collectors/channels_collector.go index f2ad2ea..4ce4a0b 100644 --- a/collectors/channels_collector.go +++ b/collectors/channels_collector.go @@ -45,6 +45,16 @@ type ChannelsCollector struct { commitWeightDesc *prometheus.Desc commitFeeDesc *prometheus.Desc + localBaseFeeDesc *prometheus.Desc + localFeeRateDesc *prometheus.Desc + localInboundBaseFeeDesc *prometheus.Desc + localInboundFeeRateDesc *prometheus.Desc + + remoteBaseFeeDesc *prometheus.Desc + remoteFeeRateDesc *prometheus.Desc + remoteInboundBaseFeeDesc *prometheus.Desc + remoteInboundFeeRateDesc *prometheus.Desc + // inboundFee is a metric that reflects the fee paid by senders on the // last hop towards this node. inboundFee *prometheus.Desc @@ -187,6 +197,47 @@ func NewChannelsCollector(lnd lndclient.LightningClient, errChan chan<- error, []string{"amount"}, nil, ), + localBaseFeeDesc: prometheus.NewDesc( + "lnd_channels_local_base_fee_msat", + "local base fee in millisatoshis for this channel", + labels, nil, + ), + localFeeRateDesc: prometheus.NewDesc( + "lnd_channels_local_fee_rate", + "local fee rate in millionths for this channel", + labels, nil, + ), + localInboundBaseFeeDesc: prometheus.NewDesc( + "lnd_channels_local_inbound_base_fee_msat", + "local inbound base fee in millisatoshis for this channel", + labels, nil, + ), + localInboundFeeRateDesc: prometheus.NewDesc( + "lnd_channels_local_inbound_fee_rate", + "local inbound fee rate in millionths for this channel", + labels, nil, + ), + remoteBaseFeeDesc: prometheus.NewDesc( + "lnd_channels_remote_base_fee_msat", + "remote base fee in millisatoshis for this channel", + labels, nil, + ), + remoteFeeRateDesc: prometheus.NewDesc( + "lnd_channels_remote_fee_rate", + "remote fee rate in millionths for this channel", + labels, nil, + ), + remoteInboundBaseFeeDesc: prometheus.NewDesc( + "lnd_channels_remote_inbound_base_fee_msat", + "remote inbound base fee in millisatoshis for this channel", + labels, nil, + ), + remoteInboundFeeRateDesc: prometheus.NewDesc( + "lnd_channels_remote_inbound_fee_rate", + "remote inbound fee rate in millionths for this channel", + labels, nil, + ), + lnd: lnd, primaryNode: cfg.PrimaryNode, closedChannelsCache: nil, @@ -267,6 +318,16 @@ func (c *ChannelsCollector) Describe(ch chan<- *prometheus.Desc) { ch <- c.commitFeeDesc ch <- c.inboundFee + + ch <- c.localBaseFeeDesc + ch <- c.localFeeRateDesc + ch <- c.localInboundBaseFeeDesc + ch <- c.localInboundFeeRateDesc + + ch <- c.remoteBaseFeeDesc + ch <- c.remoteFeeRateDesc + ch <- c.remoteInboundBaseFeeDesc + ch <- c.remoteInboundFeeRateDesc } func anchorStateToString(state lndclient.ForceCloseAnchorState) string { @@ -346,6 +407,17 @@ func (c *ChannelsCollector) Collect(ch chan<- prometheus.Metric) { return } + nodeInfo, err := c.lnd.GetNodeInfo(context.Background(), getInfoResp.IdentityPubkey, true) + if err != nil { + c.errChan <- fmt.Errorf("ChannelsCollector GetNodeInfo "+ + "failed with: %v", err) + return + } + channelInfoMap := make(map[uint64]lndclient.ChannelEdge) + for _, c := range nodeInfo.Channels { + channelInfoMap[c.ChannelID] = c + } + // statusLabel is a small helper function returns the proper status // label for a given channel. statusLabel := func(c lndclient.ChannelInfo) string { @@ -439,6 +511,52 @@ func (c *ChannelsCollector) Collect(ch chan<- prometheus.Metric) { initiator, peer, ) + if chanInfo, ok := channelInfoMap[channel.ChannelID]; ok { + localPolicy := chanInfo.Node1Policy + ch <- prometheus.MustNewConstMetric( + c.localBaseFeeDesc, prometheus.GaugeValue, + float64(localPolicy.FeeBaseMsat), + chanIDStr, status, initiator, peer, + ) + ch <- prometheus.MustNewConstMetric( + c.localFeeRateDesc, prometheus.GaugeValue, + float64(localPolicy.FeeRateMilliMsat), + chanIDStr, status, initiator, peer, + ) + ch <- prometheus.MustNewConstMetric( + c.localInboundBaseFeeDesc, prometheus.GaugeValue, + float64(localPolicy.InboundBaseFeeMsat), + chanIDStr, status, initiator, peer, + ) + ch <- prometheus.MustNewConstMetric( + c.localInboundFeeRateDesc, prometheus.GaugeValue, + float64(localPolicy.InboundFeeRatePPM), + chanIDStr, status, initiator, peer, + ) + + remotePolicy := chanInfo.Node2Policy + ch <- prometheus.MustNewConstMetric( + c.remoteBaseFeeDesc, prometheus.GaugeValue, + float64(remotePolicy.FeeBaseMsat), + chanIDStr, status, initiator, peer, + ) + ch <- prometheus.MustNewConstMetric( + c.remoteFeeRateDesc, prometheus.GaugeValue, + float64(remotePolicy.FeeRateMilliMsat), + chanIDStr, status, initiator, peer, + ) + ch <- prometheus.MustNewConstMetric( + c.remoteInboundBaseFeeDesc, prometheus.GaugeValue, + float64(remotePolicy.InboundBaseFeeMsat), + chanIDStr, status, initiator, peer, + ) + ch <- prometheus.MustNewConstMetric( + c.remoteInboundFeeRateDesc, prometheus.GaugeValue, + float64(remotePolicy.InboundFeeRatePPM), + chanIDStr, status, initiator, peer, + ) + } + // Only record uptime if the channel has been monitored. if channel.LifeTime != 0 { ch <- prometheus.MustNewConstMetric( @@ -537,7 +655,7 @@ func (c *ChannelsCollector) Collect(ch chan<- prometheus.Metric) { } // Get all remote policies - remotePolicies, err := c.getRemotePolicies(getInfoResp.IdentityPubkey) + remotePolicies, err := c.getRemotePolicies(getInfoResp.IdentityPubkey, nodeInfo) if err != nil { c.errChan <- fmt.Errorf("ChannelsCollector getRemotePolicies "+ "failed with: %v", err) @@ -663,18 +781,11 @@ func approximateInboundFee(amt btcutil.Amount, remotePolicies map[uint64]*lndcli // getRemotePolicies gets all the remote policies for enabled channels of this // node's peers. -func (c *ChannelsCollector) getRemotePolicies(pubkey route.Vertex) ( +func (c *ChannelsCollector) getRemotePolicies(pubkey route.Vertex, nodeInfo *lndclient.NodeInfo) ( map[uint64]*lndclient.RoutingPolicy, error) { - nodeInfoResp, err := c.lnd.GetNodeInfo( - context.Background(), pubkey, true, - ) - if err != nil { - return nil, err - } - policies := make(map[uint64]*lndclient.RoutingPolicy) - for _, i := range nodeInfoResp.Channels { + for _, i := range nodeInfo.Channels { var policy *lndclient.RoutingPolicy switch { case i.Node1 == pubkey: diff --git a/grafana/provisioning/dashboards/channel_fees.json b/grafana/provisioning/dashboards/channel_fees.json new file mode 100644 index 0000000..29694a1 --- /dev/null +++ b/grafana/provisioning/dashboards/channel_fees.json @@ -0,0 +1,791 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "12.1.1" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 10, + "panels": [], + "title": "General", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ppm" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 7, + "x": 0, + "y": 1 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "editorMode": "code", + "expr": "quantile(0.5, lnd_channels_remote_fee_rate)", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + } + } + ], + "title": "Median Remote Fee Rate", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "msat" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 7, + "x": 7, + "y": 1 + }, + "id": 9, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "editorMode": "code", + "expr": "quantile(0.5, lnd_channels_remote_base_fee_msat)", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + } + } + ], + "title": "Median Remote Base Fee", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ppm" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 14, + "y": 1 + }, + "id": 12, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "editorMode": "code", + "expr": "quantile(0.5, lnd_channels_local_fee_rate)", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + } + } + ], + "title": "Median Local Fee Rate", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "msat" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 1 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "editorMode": "code", + "expr": "quantile(0.5, lnd_channels_local_base_fee_msat)", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + } + } + ], + "title": "Median Local Base Fee", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 2, + "panels": [], + "title": "Local Fees", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "msat" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "lnd_channels_local_base_fee_msat", + "legendFormat": "{{chan_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Base Fee", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ppm" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "lnd_channels_local_fee_rate", + "legendFormat": "{{chan_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Fee PPM", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 3, + "panels": [], + "title": "Remote Fees", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "msat" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "lnd_channels_remote_base_fee_msat", + "legendFormat": "{{chan_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Base Fee", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ppm" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "lnd_channels_remote_fee_rate", + "legendFormat": "{{chan_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Fee PPM", + "type": "timeseries" + } + ], + "refresh": "1m", + "schemaVersion": 41, + "tags": [ + "lightning-network" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Channel Fees", + "uid": "e7a39445-9280-47ad-bc03-30ca514c2aa6", + "version": 12, + "weekStart": "" +} \ No newline at end of file