adding ability to write a log file with all output from collector. Executing commands will now log be logged (and when debug is enabled, their output's are also logged).
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/analogj/scrutiny/collector/pkg/collector"
|
"github.com/analogj/scrutiny/collector/pkg/collector"
|
||||||
"github.com/analogj/scrutiny/webapp/backend/pkg/version"
|
"github.com/analogj/scrutiny/webapp/backend/pkg/version"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@@ -85,6 +86,16 @@ OPTIONS:
|
|||||||
logrus.SetLevel(logrus.InfoLevel)
|
logrus.SetLevel(logrus.InfoLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.IsSet("log-file") {
|
||||||
|
logFile, err := os.OpenFile(c.String("log-file"), os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to open log file %s for output: %s", c.String("log-file"), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer logFile.Close()
|
||||||
|
logrus.SetOutput(io.MultiWriter(os.Stderr, logFile))
|
||||||
|
}
|
||||||
|
|
||||||
metricCollector, err := collector.CreateMetricsCollector(
|
metricCollector, err := collector.CreateMetricsCollector(
|
||||||
collectorLogger,
|
collectorLogger,
|
||||||
c.String("api-endpoint"),
|
c.String("api-endpoint"),
|
||||||
@@ -105,6 +116,12 @@ OPTIONS:
|
|||||||
EnvVars: []string{"SCRUTINY_API_ENDPOINT"},
|
EnvVars: []string{"SCRUTINY_API_ENDPOINT"},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "log-file",
|
||||||
|
Usage: "Path to file for logging. Leave empty to use STDOUT",
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
Usage: "Enable debug logging",
|
Usage: "Enable debug logging",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/analogj/scrutiny/collector/pkg/collector"
|
"github.com/analogj/scrutiny/collector/pkg/collector"
|
||||||
"github.com/analogj/scrutiny/webapp/backend/pkg/version"
|
"github.com/analogj/scrutiny/webapp/backend/pkg/version"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@@ -85,6 +86,16 @@ OPTIONS:
|
|||||||
logrus.SetLevel(logrus.InfoLevel)
|
logrus.SetLevel(logrus.InfoLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.IsSet("log-file") {
|
||||||
|
logFile, err := os.OpenFile(c.String("log-file"), os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to open log file %s for output: %s", c.String("log-file"), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer logFile.Close()
|
||||||
|
logrus.SetOutput(io.MultiWriter(os.Stderr, logFile))
|
||||||
|
}
|
||||||
|
|
||||||
stCollector, err := collector.CreateSelfTestCollector(
|
stCollector, err := collector.CreateSelfTestCollector(
|
||||||
collectorLogger,
|
collectorLogger,
|
||||||
c.String("api-endpoint"),
|
c.String("api-endpoint"),
|
||||||
@@ -105,6 +116,12 @@ OPTIONS:
|
|||||||
EnvVars: []string{"SCRUTINY_API_ENDPOINT"},
|
EnvVars: []string{"SCRUTINY_API_ENDPOINT"},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "log-file",
|
||||||
|
Usage: "Path to file for logging. Leave empty to use STDOUT",
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
Usage: "Enable debug logging",
|
Usage: "Enable debug logging",
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func (mc *MetricsCollector) Collect(wg *sync.WaitGroup, deviceWWN string, device
|
|||||||
}
|
}
|
||||||
args = append(args, fmt.Sprintf("%s%s", detect.DevicePrefix(), deviceName))
|
args = append(args, fmt.Sprintf("%s%s", detect.DevicePrefix(), deviceName))
|
||||||
|
|
||||||
result, err := common.ExecCmd("smartctl", args, "", os.Environ())
|
result, err := common.ExecCmd(mc.logger, "smartctl", args, "", os.Environ())
|
||||||
resultBytes := []byte(result)
|
resultBytes := []byte(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if exitError, ok := err.(*exec.ExitError); ok {
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
|||||||
@@ -3,17 +3,19 @@ package common
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExecCmd(cmdName string, cmdArgs []string, workingDir string, environ []string) (string, error) {
|
func ExecCmd(logger *logrus.Entry, cmdName string, cmdArgs []string, workingDir string, environ []string) (string, error) {
|
||||||
|
logger.Infof("Executing command: %s %s", cmdName, strings.Join(cmdArgs, " "))
|
||||||
|
|
||||||
cmd := exec.Command(cmdName, cmdArgs...)
|
cmd := exec.Command(cmdName, cmdArgs...)
|
||||||
var stdBuffer bytes.Buffer
|
var stdBuffer bytes.Buffer
|
||||||
mw := io.MultiWriter(os.Stdout, &stdBuffer)
|
mw := io.MultiWriter(logger.Logger.Out, &stdBuffer)
|
||||||
|
|
||||||
cmd.Stdout = mw
|
cmd.Stdout = mw
|
||||||
cmd.Stderr = mw
|
cmd.Stderr = mw
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package common_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/analogj/scrutiny/collector/pkg/common"
|
"github.com/analogj/scrutiny/collector/pkg/common"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -13,7 +14,7 @@ func TestExecCmd(t *testing.T) {
|
|||||||
//setup
|
//setup
|
||||||
|
|
||||||
//test
|
//test
|
||||||
result, err := common.ExecCmd("echo", []string{"hello world"}, "", nil)
|
result, err := common.ExecCmd(logrus.WithField("exec", "test"), "echo", []string{"hello world"}, "", nil)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -26,7 +27,7 @@ func TestExecCmd_Date(t *testing.T) {
|
|||||||
//setup
|
//setup
|
||||||
|
|
||||||
//test
|
//test
|
||||||
_, err := common.ExecCmd("date", []string{}, "", nil)
|
_, err := common.ExecCmd(logrus.WithField("exec", "test"), "date", []string{}, "", nil)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -56,7 +57,7 @@ func TestExecCmd_InvalidCommand(t *testing.T) {
|
|||||||
//setup
|
//setup
|
||||||
|
|
||||||
//test
|
//test
|
||||||
_, err := common.ExecCmd("invalid_binary", []string{}, "", nil)
|
_, err := common.ExecCmd(logrus.WithField("exec", "test"), "invalid_binary", []string{}, "", nil)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
_, castOk := err.(*exec.ExitError)
|
_, castOk := err.(*exec.ExitError)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type Detect struct {
|
|||||||
// models.Device returned from this function only contain the minimum data for smartctl to execute: device type and device name (device file).
|
// models.Device returned from this function only contain the minimum data for smartctl to execute: device type and device name (device file).
|
||||||
func (d *Detect) smartctlScan() ([]models.Device, error) {
|
func (d *Detect) smartctlScan() ([]models.Device, error) {
|
||||||
//we use smartctl to detect all the drives available.
|
//we use smartctl to detect all the drives available.
|
||||||
detectedDeviceConnJson, err := common.ExecCmd("smartctl", []string{"--scan", "-j"}, "", os.Environ())
|
detectedDeviceConnJson, err := common.ExecCmd(d.Logger, "smartctl", []string{"--scan", "-j"}, "", os.Environ())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.Logger.Errorf("Error scanning for devices: %v", err)
|
d.Logger.Errorf("Error scanning for devices: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -64,7 +64,7 @@ func (d *Detect) smartCtlInfo(device *models.Device) error {
|
|||||||
}
|
}
|
||||||
args = append(args, fmt.Sprintf("%s%s", DevicePrefix(), device.DeviceName))
|
args = append(args, fmt.Sprintf("%s%s", DevicePrefix(), device.DeviceName))
|
||||||
|
|
||||||
availableDeviceInfoJson, err := common.ExecCmd("smartctl", args, "", os.Environ())
|
availableDeviceInfoJson, err := common.ExecCmd(d.Logger, "smartctl", args, "", os.Environ())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.Logger.Errorf("Could not retrieve device information for %s: %v", device.DeviceName, err)
|
d.Logger.Errorf("Could not retrieve device information for %s: %v", device.DeviceName, err)
|
||||||
return err
|
return err
|
||||||
@@ -101,6 +101,7 @@ func (d *Detect) smartCtlInfo(device *models.Device) error {
|
|||||||
Id: availableDeviceInfo.Wwn.ID,
|
Id: availableDeviceInfo.Wwn.ID,
|
||||||
}
|
}
|
||||||
device.WWN = wwn.ToString()
|
device.WWN = wwn.ToString()
|
||||||
|
d.Logger.Debugf("NAA: %d OUI: %d Id: %d => WWN: %s", wwn.Naa, wwn.Oui, wwn.Id, device.WWN)
|
||||||
} else {
|
} else {
|
||||||
d.Logger.Info("Using WWN Fallback")
|
d.Logger.Info("Using WWN Fallback")
|
||||||
d.wwnFallback(device)
|
d.wwnFallback(device)
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ func (d *Detect) wwnFallback(detectedDevice *models.Device) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
for _, disk := range block.Disks {
|
for _, disk := range block.Disks {
|
||||||
if disk.Name == detectedDevice.DeviceName {
|
if disk.Name == detectedDevice.DeviceName {
|
||||||
|
d.Logger.Debugf("Found matching block device. WWN: %s", disk.WWN)
|
||||||
detectedDevice.WWN = disk.WWN
|
detectedDevice.WWN = disk.WWN
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -101,6 +102,7 @@ func (d *Detect) wwnFallback(detectedDevice *models.Device) {
|
|||||||
|
|
||||||
//no WWN found, or could not open Block devices. Either way, fallback to serial number
|
//no WWN found, or could not open Block devices. Either way, fallback to serial number
|
||||||
if len(detectedDevice.WWN) == 0 {
|
if len(detectedDevice.WWN) == 0 {
|
||||||
|
d.Logger.Debugf("WWN is empty, falling back to serial number: %s", detectedDevice.SerialNumber)
|
||||||
detectedDevice.WWN = detectedDevice.SerialNumber
|
detectedDevice.WWN = detectedDevice.SerialNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ func (d *Detect) wwnFallback(detectedDevice *models.Device) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
for _, disk := range block.Disks {
|
for _, disk := range block.Disks {
|
||||||
if disk.Name == detectedDevice.DeviceName {
|
if disk.Name == detectedDevice.DeviceName {
|
||||||
|
d.Logger.Debugf("Found matching block device. WWN: %s", disk.WWN)
|
||||||
detectedDevice.WWN = disk.WWN
|
detectedDevice.WWN = disk.WWN
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -38,6 +39,7 @@ func (d *Detect) wwnFallback(detectedDevice *models.Device) {
|
|||||||
|
|
||||||
//no WWN found, or could not open Block devices. Either way, fallback to serial number
|
//no WWN found, or could not open Block devices. Either way, fallback to serial number
|
||||||
if len(detectedDevice.WWN) == 0 {
|
if len(detectedDevice.WWN) == 0 {
|
||||||
|
d.Logger.Debugf("WWN is empty, falling back to serial number: %s", detectedDevice.SerialNumber)
|
||||||
detectedDevice.WWN = detectedDevice.SerialNumber
|
detectedDevice.WWN = detectedDevice.SerialNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user