package monitor import ( "bytes" "fmt" "os" "time" "github.com/kataras/iris/v12/context" "github.com/shirou/gopsutil/v3/process" ) func init() { context.SetHandlerName("iris/middleware/monitor.*", "iris.monitor") } // Options holds the optional fields for the Monitor structure. type Options struct { // Optional process id, defaults to the current one. PID int32 `json:"pid" yaml:"PID"` RefreshInterval time.Duration `json:"refresh_interval" yaml:"RefreshInterval"` ViewRefreshInterval time.Duration `json:"view_refresh_interval" yaml:"ViewRefreshInterval"` // If more than zero enables line animation. Defaults to zero. ViewAnimationInterval time.Duration `json:"view_animation_interval" yaml:"ViewAnimationInterval"` // The title of the monitor HTML document. ViewTitle string `json:"view_title" yaml:"ViewTitle"` } // Monitor tracks and renders the server's process and operating system statistics. // // Look its `Stats` and `View` methods. // Initialize with the `New` package-level function. type Monitor struct { opts Options Holder *StatsHolder viewBody []byte } // New returns a new Monitor. // Metrics stored through expvar standard package: // - pid_cpu // - pid_ram // - pid_conns // - os_cpu // - os_ram // - os_total_ram // - os_load_avg // - os_conns // // Check https://github.com/iris-contrib/middleware/tree/master/expmetric // which can be integrated with datadog or other platforms. func New(opts Options) *Monitor { if opts.PID == 0 { opts.PID = int32(os.Getpid()) } if opts.RefreshInterval <= 0 { opts.RefreshInterval = 2 * opts.RefreshInterval } if opts.ViewRefreshInterval <= 0 { opts.ViewRefreshInterval = opts.RefreshInterval } viewRefreshIntervalBytes := []byte(fmt.Sprintf("%d", opts.ViewRefreshInterval.Milliseconds())) viewBody := bytes.Replace(defaultViewBody, viewRefreshIntervalTmplVar, viewRefreshIntervalBytes, 1) viewAnimationIntervalBytes := []byte(fmt.Sprintf("%d", opts.ViewAnimationInterval.Milliseconds())) viewBody = bytes.Replace(viewBody, viewAnimationIntervalTmplVar, viewAnimationIntervalBytes, 2) viewTitleBytes := []byte(opts.ViewTitle) viewBody = bytes.Replace(viewBody, viewTitleTmplVar, viewTitleBytes, 2) proc, err := process.NewProcess(opts.PID) if err != nil { panic(err) } sh := startNewStatsHolder(proc, opts.RefreshInterval) m := &Monitor{ opts: opts, Holder: sh, viewBody: viewBody, } return m } // Stop terminates the retrieve stats loop for // the process and the operating system statistics. // No other monitor instance should be initialized after the first Stop call. func (m *Monitor) Stop() { m.Holder.Stop() } // Stats sends the stats as json. func (m *Monitor) Stats(ctx *context.Context) { ctx.JSON(m.Holder.GetStats()) } // View renders a default view for the stats. func (m *Monitor) View(ctx *context.Context) { ctx.ContentType("text/html") ctx.Write(m.viewBody) }