Skip to content

Commit

Permalink
resolve merge conflict of 68e4012 and 3def774
Browse files Browse the repository at this point in the history
  • Loading branch information
Hafpaf committed Mar 11, 2024
2 parents bbb0902 + 68e4012 commit 2c183d4
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 26 deletions.
22 changes: 8 additions & 14 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package config

import (
"fmt"
"time"

multierror "github.com/hashicorp/go-multierror"
)
Expand Down Expand Up @@ -60,19 +59,14 @@ type ListTargets map[string]*Module

// Module defines the configuration parameters of a modbus module.
type Module struct {
Name string `yaml:"name"`
Protocol ModbusProtocol `yaml:"protocol"`
Timeout int `yaml:"timeout"`
Baudrate int `yaml:"baudrate"`
Databits int `yaml:"databits"`
Stopbits int `yaml:"stopbits"`
Parity string `yaml:"parity"`
Metrics []MetricDef `yaml:"metrics"`
Workarounds Workarounds `yaml:"workarounds"`
}

type Workarounds struct {
SleepAfterConnect time.Duration `yaml:"sleepAfterConnect"`
Name string `yaml:"name"`
Protocol ModbusProtocol `yaml:"protocol"`
Timeout int `yaml:"timeout"`
Baudrate int `yaml:"baudrate"`
Databits int `yaml:"databits"`
Stopbits int `yaml:"stopbits"`
Parity string `yaml:"parity"`
Metrics []MetricDef `yaml:"metrics"`
}

// RegisterAddr specifies the register in the possible output of _digital
Expand Down
4 changes: 0 additions & 4 deletions modbus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ modules:
# Module name, needs to be passed as parameter by Prometheus.
- name: "fake"
protocol: 'tcp/ip'
# Certain modbus devices need special timing workarounds
workarounds:
# Sleep a certain time after the TCP connection is established
sleepAfterConnect: "1s"
metrics:
# Name of the metric.
- name: "power_consumption_total"
Expand Down
6 changes: 3 additions & 3 deletions modbus/modbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
// Exporter represents a Prometheus exporter converting modbus information
// retrieved from remote targets via TCP as Prometheus style metrics.
type Exporter struct {
config config.Config
Config config.Config
}

// NewExporter returns a new modbus exporter.
Expand All @@ -39,15 +39,15 @@ func NewExporter(config config.Config) *Exporter {

// GetConfig loads the config file
func (e *Exporter) GetConfig() *config.Config {
return &e.config
return &e.Config
}

// Scrape scrapes the given target via TCP based on the configuration of the
// specified module returning a Prometheus gatherer with the resulting metrics.
func (e *Exporter) Scrape(targetAddress string, subTarget byte, moduleName string) (prometheus.Gatherer, error) {
reg := prometheus.NewRegistry()

module := e.config.GetModule(moduleName)
module := e.Config.GetModule(moduleName)
if module == nil {
return nil, fmt.Errorf("failed to find '%v' in config", moduleName)
}
Expand Down
42 changes: 37 additions & 5 deletions modbus_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"os"
"strconv"
"strings"
"time"

"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
Expand Down Expand Up @@ -117,13 +118,47 @@ func scrapeHandler(e *modbus.Exporter, w http.ResponseWriter, r *http.Request, l
}

level.Info(logger).Log("msg", "got scrape request", "module", moduleName, "target", target, "sub_target", subTarget)
gatherer, err := e.Scrape(target, byte(subTarget), moduleName) // Scrape

gatherer, err := e.Scrape(target, byte(subTarget), moduleName)
// No errors, export data to Prometheus
if err == nil {
promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}).ServeHTTP(w, r)
return
}

// In case of scraping error: sleep ScrapeErrorWait time and try again for ScrapeErrorRetryCount times.
// Try again if a race condition if happens where the same target is queried on different sub-targets,
// before a previous query has gotten a response.
ScrapeErrorRetryCount := e.Config.GetModule(moduleName).ScrapeErrorRetryCount // int cannot be nil, can arise issue if user wants to set it to 0
ScrapeErrorWait := e.Config.GetModule(moduleName).ScrapeErrorWait

if ScrapeErrorRetryCount == 0 { // if unset or 0, set to 3 retries
ScrapeErrorRetryCount = 3
level.Error(logger).Log("msg", "ScrapeErrorRetryCount: Scrape retry count is unset, using default value 3", "target", target, "module", moduleName, "err", err)
}
if ScrapeErrorWait == 0 { // If unset or 0, wait 100 milliseconds
ScrapeErrorWait = 100
level.Error(logger).Log("msg", "ScrapeErrorWait: Scrape retry waiting time is unset, using default value 100", "target", target, "module", moduleName, "err", err)
}

for i := 1; i <= ScrapeErrorRetryCount; i++ { // Retry x times until giving up and returning error
time.Sleep(time.Duration(ScrapeErrorWait) * time.Millisecond) // sleep for y milliseconds

// Another attempt at scraping
gatherer, err := e.Scrape(target, byte(subTarget), moduleName)
if err == nil {
promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}).ServeHTTP(w, r)
return
}
}

// Return error to Prometheus and log if it still persists.
if err != nil {
httpStatus := http.StatusInternalServerError
if strings.Contains(fmt.Sprintf("%v", err), "unable to connect with target") {
httpStatus = http.StatusServiceUnavailable
} else if strings.Contains(fmt.Sprintf("%v", err), "i/o timeout") {
// Throw HTTP 504 StatusGatewayTimeout error in case of module returning modbus exception 11
} else if strings.Contains(fmt.Sprintf("%v", err), "i/o timeout") || strings.Contains(fmt.Sprintf("%v", err), "exception '11' (gateway target device failed to respond)") {
httpStatus = http.StatusGatewayTimeout
}
http.Error(
Expand All @@ -132,8 +167,5 @@ func scrapeHandler(e *modbus.Exporter, w http.ResponseWriter, r *http.Request, l
httpStatus,
)
level.Error(logger).Log("msg", "failed to scrape", "target", target, "module", moduleName, "err", err)
return
}

promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}).ServeHTTP(w, r)
}

0 comments on commit 2c183d4

Please sign in to comment.