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)

fredmaggiowski
  • 2,232
  • 3
  • 25
  • 44
  • 2
    Why not just use a timer? – eduncan911 Jun 23 '16 at 13:32
  • @eduncan911 honestly it seems to me to be a pretty heavy solution, am I right? Would you like to provide a lightweight example? – fredmaggiowski Jun 23 '16 at 13:53
  • Idea: a tiny library that adds a HTTP handler like [net/http/pprof](https://golang.org/pkg/net/http/pprof/) does exposing the number of (nano/milli)seconds an application has been up. – toqueteos Jun 23 '16 at 14:27
  • 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 packages – Plato Jun 23 '16 at 14:30

5 Answers5

5

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())
}
fredmaggiowski
  • 2,232
  • 3
  • 25
  • 44
Noisee
  • 306
  • 2
  • 7
  • I've decided to accept this answer because you provided a full code example. Thanks to everyone who helped with comments and answers! Cheers! – fredmaggiowski Jun 24 '16 at 07:24
  • If the clock changes this might be wrong... syscall uptime isn't affected by clock changes. – Gabriel Nov 08 '16 at 21:54
  • 1
    Not really @Gabriel. Golang's time.Now() is both a monotonic and a wall clock. This idiom is thus resistant to clock resets. https://golang.org/pkg/time/ – Nuno Cruces Oct 18 '19 at 22:46
  • What if I need to library load time that takes place before `init` can start? – 0xbe5077ed Nov 04 '21 at 16:14
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)

Melih Mucuk
  • 6,988
  • 6
  • 37
  • 56
  • 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 ;) – fredmaggiowski Jun 23 '16 at 19:00
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.

toqueteos
  • 771
  • 1
  • 6
  • 14
1

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)
Harald Rudell
  • 787
  • 6
  • 7
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
Harald Rudell
  • 787
  • 6
  • 7