Skip to content
Open
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
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,18 @@ Flags:
--[no-]version Show application version.

```
Visit http://localhost:9602/modbus?target=1.2.3.4:502&module=fake&sub_target=1 where 1.2.3.4:502 is the IP and port number of the modbus IP device to get metrics from,
while module and sub_target parameters specify which module and subtarget to use from the config file.
Visit http://localhost:9602/modbus?module=fake&target=1.2.3.4:502&sub_target=1 where:

- the `module` parameter specifies which module to use from the config file;
- the `target` parameter specifies the IP and port number of the modbus IP device to get metrics from, e.g. `1.2.3.4:502`;
- the `sub_target` specifies the slave address/id to use in the modbus request;

If your device doesn't use sub-targets you can usually just set it to 1.

Note `target` and `sub_target` may be provided per-module in the config,
in which case the respective value in the request is ignored.
This is useful e.g. if you only have one device per module and don't want to accept the IP address in the request.

Visit http://localhost:9602/metrics to get the metrics of the exporter itself.

## Configuration File
Expand Down
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type ListTargets map[string]*Module
type Module struct {
Name string `yaml:"name"`
Protocol ModbusProtocol `yaml:"protocol"`
Target *string `yaml:"target"`
SubTarget *uint8 `yaml:"subTarget"`
Timeout int `yaml:"timeout"`
Baudrate int `yaml:"baudrate"`
Databits int `yaml:"databits"`
Expand Down
9 changes: 9 additions & 0 deletions modbus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ modules:
# Module name, needs to be passed as parameter by Prometheus.
- name: "fake"
protocol: 'tcp/ip'

# IP and port number of the modbus IP device to get metrics from.
# Optinal. If specified, this value is used and the target HTTP request parameter is ignored.
# target: "1.2.3.4:502"

# Modbus slave address/id to use
# Optional. If specified, this value is used and the sub_target HTTP request parameter is ignored.
# subTarget: 0

# Certain modbus devices need special timing workarounds
workarounds:
# Sleep a certain time after the TCP connection is established
Expand Down
51 changes: 32 additions & 19 deletions modbus_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,32 +88,45 @@ func scrapeHandler(e *modbus.Exporter, w http.ResponseWriter, r *http.Request, l
http.Error(w, "'module' parameter must be specified", http.StatusBadRequest)
return
}

if !e.GetConfig().HasModule(moduleName) {
module := e.GetConfig().GetModule(moduleName)
if module == nil {
http.Error(w, fmt.Sprintf("module '%v' not defined in configuration file", moduleName), http.StatusBadRequest)
return
}

target := r.URL.Query().Get("target")
if target == "" {
http.Error(w, "'target' parameter must be specified", http.StatusBadRequest)
return
var target string
if module.Target == nil {
target = r.URL.Query().Get("target")
if target == "" {
http.Error(w, "'target' parameter must be specified", http.StatusBadRequest)
return
}
} else {
target = *module.Target
}

sT := r.URL.Query().Get("sub_target")
if sT == "" {
http.Error(w, "'sub_target' parameter must be specified", http.StatusBadRequest)
return
}
var subTarget uint8
if module.SubTarget == nil {
sT := r.URL.Query().Get("sub_target")
if sT == "" {
http.Error(w, "'sub_target' parameter must be specified", http.StatusBadRequest)
return
}

subTarget, err := strconv.ParseUint(sT, 10, 32)
if err != nil {
http.Error(w, fmt.Sprintf("'sub_target' parameter must be a valid integer: %v", err), http.StatusBadRequest)
return
}
if subTarget > 255 {
http.Error(w, fmt.Sprintf("'sub_target' parameter must be from 0 to 255. Invalid value: %d", subTarget), http.StatusBadRequest)
return
parsedSubTarget, err := strconv.ParseUint(sT, 10, 32)
if err != nil {
http.Error(w, fmt.Sprintf("'sub_target' parameter must be a valid integer: %v", err), http.StatusBadRequest)
return
}

if parsedSubTarget > 255 {
http.Error(w, fmt.Sprintf("'sub_target' parameter must be from 0 to 255. Invalid value: %d", parsedSubTarget), http.StatusBadRequest)
return
}

subTarget = byte(parsedSubTarget)
} else {
subTarget = *module.SubTarget
}

level.Info(logger).Log("msg", "got scrape request", "module", moduleName, "target", target, "sub_target", subTarget)
Expand Down