blob: 9237c812f2200a13a44a536d75c9aa690ebd0471 [file] [log] [blame]
// Package main is the entry point for the cos_node_profiler application that
// imports the cloudlogger, profiler, and utils packages that respectively
// write logs to Google Cloud Logging backend, fetch debugging information from
// a Linux system and provide the interface between cloudlogger and profiler
// packages.
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"time"
"cloud.google.com/go/logging"
"cos.googlesource.com/cos/tools.git/src/pkg/nodeprofiler/cloudlogger"
"cos.googlesource.com/cos/tools.git/src/pkg/nodeprofiler/profiler"
log "github.com/sirupsen/logrus"
)
const cloudLoggerName = "cos_node_profiler"
var (
configFile = flag.String("config-file", "", "specifies the path of the configuration file. If path is not set, then it is assumed that command line flags will be passed to configure the Node Profiler.")
projID = flag.String("project", "", "specifies the GCP project where logs will be added.")
command = flag.String("cmd", "", "specifies raw commands for which to log output.")
cmdCount = flag.Int("cmd-count", 0, "specifies the number of times to run an arbitrary shell command.")
cmdInterval = flag.Int("cmd-interval", 0, "specifies the interval (in seconds) separating the number of times the user runs an arbitrary shell command.")
cmdTimeOut = flag.Int("cmd-timeout", 300, "specifies the amount of time (in seconds) it will take for the a raw command to timeout and be killed.")
profilerCount = flag.Int("profiler-count", 1, "specifies the number of times to collect USE Report.")
profilerInterval = flag.Int("profiler-interval", 0, "specifies the interval (in seconds) separating the number of times the user collects USE Report.")
)
func main() {
var opts *cloudlogger.LoggerOpts
var err error
flag.Parse()
if *configFile != "" {
opts, err = loadConfig(*configFile)
if err != nil {
log.Fatalf("%v", err)
}
} else {
opts = loadFlags()
}
// [START client setup]
ctx := context.Background()
client, err := logging.NewClient(ctx, opts.ProjID)
if err != nil {
log.Fatalf("failed to create logging client: %v", err)
}
defer client.Close()
client.OnError = func(err error) {
// Log an error to the local log if any function call failed.
// For example, print an error if Flush() failed.
log.Errorf("client.OnError: %v", err)
}
// [END client setup]
log.Info("Begin logging profiler report...")
logger := client.Logger(cloudLoggerName)
if err = cloudlogger.LogProfilerReport(logger, opts); err != nil {
log.Fatalf("%v", err)
}
log.Info("Successfully logged profiler report.")
}
// generateProfilerOpts is a helper function used to generate the components
// array as well as the profiler options used to call the
// profiler.GenerateUSEReport function from the profiler package.
func generateProfilerOpts() ([]profiler.Component, []profiler.Command) {
// [Begin generating ProfilerOpts from Profiler Package]
// Getting Components
cpu := profiler.NewCPU("CPU")
memcap := profiler.NewMemCap("MemCap")
sDevIO := profiler.NewStorageDevIO("StorageDevIO")
sCap := profiler.NewStorageCap("StorageCap")
components := []profiler.Component{cpu, memcap, sDevIO, sCap}
// End Getting Components
// Getting Commands
vmstat := profiler.NewVMStat("vmstat", 1, 5, []string{"us", "sy", "st", "si", "so", "r"})
lscpu := profiler.NewLscpu("lscpu", []string{"CPU(s)"})
free := profiler.NewFree("free", []string{"Mem:used", "Mem:total", "Swap:used", "Swap:total"})
iostat := profiler.NewIOStat("iostat", "-xdz", 1, 5, []string{"aqu-sz", "%util"})
df := profiler.NewDF("df", "-k", []string{})
commands := []profiler.Command{vmstat, lscpu, free, iostat, df}
// End Getting Commands
// [End generating ProfilerOpts from Profiler Package]
return components, commands
}
// loadflags helps to use command line flags as configuration to the Node
// Profiler tool.
func loadFlags() *cloudlogger.LoggerOpts {
// Getting Profiler Options.
components, commands := generateProfilerOpts()
shCmds := []cloudlogger.ShellCmdOpts{
cloudlogger.ShellCmdOpts{
Command: *command,
CmdCount: *cmdCount,
CmdInterval: time.Duration(*cmdInterval) * time.Second,
CmdTimeOut: time.Duration(*cmdTimeOut) * time.Second,
},
}
// populating LoggerOpts struct with configurations from user.
opts := &cloudlogger.LoggerOpts{
ProjID: *projID,
ShCmds: shCmds,
ProfilerCount: *profilerCount,
ProfilerInterval: time.Duration(*profilerInterval) * time.Second,
Components: components,
ProfilerCmds: commands,
}
return opts
}
// loadConfig helps to use a json configuration file in the current directory
// as configuration to the Node Profiler Tool.
func loadConfig(filename string) (*cloudlogger.LoggerOpts, error) {
var logger cloudlogger.LoggerOpts
configFile, err := os.Open(filename)
defer configFile.Close()
if err != nil {
return &logger, fmt.Errorf("failed to open config file %v: %v", filename, err)
}
jsonParser := json.NewDecoder(configFile)
if err = jsonParser.Decode(&logger); err != nil {
return &logger, fmt.Errorf("failed to parse config file %v: %v", filename, err)
}
for i := 0; i < len(logger.ShCmds); i++ {
logger.ShCmds[i].CmdInterval = logger.ShCmds[i].CmdInterval * time.Second
logger.ShCmds[i].CmdTimeOut = logger.ShCmds[i].CmdTimeOut * time.Second
}
logger.ProfilerInterval = logger.ProfilerInterval * time.Second
components, commands := generateProfilerOpts()
logger.Components = components
logger.ProfilerCmds = commands
return &logger, err
}