Golang retrieve application uptime
Asked Answered
H

5

7

I'm trying to retrieve the current uptime of my Go application.

I've seen there's a package syscall which provides a type Sysinfo_t and a method Sysinfo(*Sysinfo_t) which apparently allows you to retrieve the Uptime (since it's a field of the Sysinfo_t struct)

What I've done so far is:

sysi := &syscall.Sysinfo_t{}

if err := syscall.Sysinfo(sysi); err != nil {
    return http.StatusInternalServerError, nil
}

The problem is that at compile time I get this:

/path/to/file/res_system.go:43: undefined: syscall.Sysinfo_t
/path/to/file/res_system.go:45: undefined: syscall.Sysinfo

I've searched a bit and apparently that method and type are available only on Linux and I need the application to run both on Linux and OsX (which I'm currently using).

Is there a cross-compatible way to retrieve the application uptime?

NOTE: I'd rather not use any third party libraries (unless they're absolutely necessary)

Hectoliter answered 23/6, 2016 at 13:10 Comment(4)
Why not just use a timer?Medullated
@Medullated honestly it seems to me to be a pretty heavy solution, am I right? Would you like to provide a lightweight example?Hectoliter
Idea: a tiny library that adds a HTTP handler like net/http/pprof does exposing the number of (nano/milli)seconds an application has been up.Kristopher
a lightweight solution is to set a startTime time.Time on application init, then call time.Now().Sub(startTime).Seconds(). the hard part is probably passing startTime around between packagesMcgrath
E
6

Simple way to get uptime is to store service start time:

https://play.golang.org/p/by_nkvhzqD

package main

import (
    "fmt"
    "time"
)

var startTime time.Time

func uptime() time.Duration {
    return time.Since(startTime)
}

func init() {
    startTime = time.Now()
}

func main() {
    fmt.Println("started")

    time.Sleep(time.Second * 1)
    fmt.Printf("uptime %s\n", uptime())

    time.Sleep(time.Second * 5)
    fmt.Printf("uptime %s\n", uptime())
}
Etna answered 23/6, 2016 at 15:23 Comment(4)
I've decided to accept this answer because you provided a full code example. Thanks to everyone who helped with comments and answers! Cheers!Hectoliter
If the clock changes this might be wrong... syscall uptime isn't affected by clock changes.Crary
Not really @Gabriel. Golang's time.Now() is both a monotonic and a wall clock. This idiom is thus resistant to clock resets. golang.org/pkg/timeLaudian
What if I need to library load time that takes place before init can start?Ossicle
L
4

You should use Since function from time package.

create time value when application start:

startTime := time.Now()

then ask whenever you want:

uptime := time.Since(startTime)

Lowis answered 23/6, 2016 at 14:47 Comment(1)
I think I'm going with this solution, thought I really don't like to use global variables around my application.. Tomorrow (italian time) I'll let you know ;)Hectoliter
N
2

The unix package in Go Standard Library go1.19.4 on macOS 13.1 Darwin xnu can now determine process start time using unix.SysctlKinfoProc

I have an open source Go library doing this here: https://github.com/haraldrudell/parl/blob/main/mains/process-start.go

ie.

import "github.com/haraldrudell/parl/mains"
println(mains.ProcessStartTime())

unix.SysctlKinfoProc uses macOS libSystem ie. it is supported by Apple, Inc. and uses direct kernel calls and no dumbities

Code is basically:

if unixKinfoProc, err = unix.SysctlKinfoProc(kernProcPid, os.Getpid()); perrors.Is(&err, "unix.SysctlKinfoProc: %T %+[1]v", err) {
    panic(err)
}
var unixTimeval unix.Timeval = unixKinfoProc.Proc.P_starttime
sec, nsec := unixTimeval.Unix()
createTime = time.Unix(sec, nsec)
Neckline answered 30/12, 2022 at 1:58 Comment(0)
K
1

Package syscall was frozen on Go 1.4.

NOTE: This package is locked down. Code outside the standard Go repository should be migrated to use the corresponding package in the golang.org/x/sys repository. That is also where updates required by new systems or versions should be applied. See https://golang.org/s/go1.4-syscall for more information.

Use Sysinfo from golang.org/x/sys it should support this in a cross-platform way, at least on Unix.

Kristopher answered 23/6, 2016 at 13:35 Comment(0)
N
0

Difficulties

import "syscall" has been starved on most of its functionality which has been extracted to platform specific code in import "golang.org/x/sys/unix" and import "golang.org/x/sys/windows".

macOS GOOS==Darwin sorts under unix. The code in unix and windows is platform-specific, ie. if windows is imported on unix, the result is

error while importing golang.org/x/sys/windows: build constraints exclude all Go files in …

This means the program has to have a portable layer defining a portable function name, and that function is implemented for each supported platform like _darwin.go _linux.go and _windows.go which has to be tested on the real operating system.

The alternative is to use a third-party package where portability is already implemented. What you do then is to browse to Go Package search and pick a well-written candidate.

Solution

I browsed to Go Package search for Sysinfo: https://pkg.go.dev/search?q=sysinfo

Top result is gosysinfo "github.com/elastic/go-sysinfo". This package is awkwardly written as can be seen by a hyphen in its name and a peculiar package structure. It works, and the code goes like:

import (
  gosysinfo "github.com/elastic/go-sysinfo"
  "github.com/elastic/go-sysinfo/types"
  "github.com/haraldrudell/parl"
)

func goSysinfo() {
  var process types.Process
  var err error
  if process, err = gosysinfo.Self(); err != nil {
    panic(parl.Errorf("go-sysinfo.Self: %w", err))
  }
  var processInfo types.ProcessInfo
  if processInfo, err = process.Info(); err != nil {
    panic(parl.Errorf("go-sysinfo.Info: %w", err))
  }
  startTime := processInfo.StartTime
  fmt.Printf("Process start time: %s\n", startTime.Format(parl.Rfc3339s))
}
→
Process start time: 2022-03-22 10:15:05-07:00
Neckline answered 22/3, 2022 at 18:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.