ing
This commit is contained in:
parent
2e31ae10eb
commit
806f406ee0
|
@ -2,6 +2,7 @@ package crawler
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"git.loafle.net/overflow/ssh_crawler/ssh"
|
||||
"git.loafle.net/overflow/ssh_crawler/stat"
|
||||
)
|
||||
|
@ -29,33 +30,43 @@ func (c *Crawler) connectSSH(ip, port, user, pw string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Crawler) CPUStat(ch chan interface{}) {
|
||||
func (c *Crawler) CPUStat(ch chan interface{}, keys []string) {
|
||||
go func() {
|
||||
cpu := &stat.CPUStat{}
|
||||
b, err := c.sshCli.RunCommand(cpu.Command())
|
||||
if err != nil {
|
||||
ch <- err
|
||||
}
|
||||
res, err := cpu.Parse(bytes.NewReader(b))
|
||||
res, err := cpu.Read(bytes.NewReader(b), keys)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
}
|
||||
ch <- res
|
||||
jsonData, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
}
|
||||
ch <- string(jsonData)
|
||||
close(ch)
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Crawler) MemStat(ch chan interface{}) {
|
||||
func (c *Crawler) MemStat(ch chan interface{}, keys []string) {
|
||||
go func() {
|
||||
mem := &stat.MemStat{}
|
||||
b, err := c.sshCli.RunCommand(mem.Command())
|
||||
if err != nil {
|
||||
ch <- err
|
||||
}
|
||||
res, err := mem.Parse(bytes.NewReader(b))
|
||||
res, err := mem.Read(bytes.NewReader(b), keys)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
}
|
||||
ch <- res
|
||||
jsonData, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
}
|
||||
ch <- string(jsonData)
|
||||
close(ch)
|
||||
}()
|
||||
}
|
||||
|
||||
|
@ -73,6 +84,7 @@ func (c *Crawler) DiskIOStat(ch chan interface{}) {
|
|||
ch <- err
|
||||
}
|
||||
ch <- res
|
||||
close(ch)
|
||||
}()
|
||||
}
|
||||
|
||||
|
@ -88,6 +100,7 @@ func (c *Crawler) DiskFreeStat(ch chan interface{}) {
|
|||
ch <- err
|
||||
}
|
||||
ch <- res
|
||||
close(ch)
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
93
main.go
93
main.go
|
@ -3,11 +3,9 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"git.loafle.net/overflow/ssh_crawler/crawler"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
runtime.GOMAXPROCS(4)
|
||||
|
||||
const ip = "192.168.1.15"
|
||||
const port = "22"
|
||||
|
@ -19,45 +17,70 @@ func main() {
|
|||
fmt.Println(err)
|
||||
}
|
||||
|
||||
//CPU
|
||||
//사용자가 cpu.usage.total을 선택했을 때의 수집항목들 (..출력 항목도 포함시켜서 계산결과 리턴해야하나?)
|
||||
cpukeys := []string{
|
||||
"cpu.usage.sum",
|
||||
"cpu.usage.user",
|
||||
"cpu.usage.system",
|
||||
"cpu.usage.nice",
|
||||
"cpu.usage.idle",
|
||||
"cpu.usage.iowait",
|
||||
"cpu.usage.irq",
|
||||
"cpu.usage.softirq",
|
||||
"cpu.usage.steal",
|
||||
"cpu.usage.guest",
|
||||
"cpu.usage.guestNice",
|
||||
}
|
||||
cpuCh := make(chan interface{})
|
||||
cr.CPUStat(cpuCh)
|
||||
cr.CPUStat(cpuCh, cpukeys)
|
||||
fmt.Println(<-cpuCh)
|
||||
close(cpuCh)
|
||||
|
||||
//Memory
|
||||
memkeys := []string {
|
||||
"mem.usage.total",
|
||||
"mem.usage.free",
|
||||
"mem.usage.available",
|
||||
"mem.usage.buffers",
|
||||
"mem.usage.cached",
|
||||
"mem.swap.usage.total",
|
||||
"mem.swap.usage.free",
|
||||
"mem.swap.usage.cached",
|
||||
}
|
||||
memCh := make(chan interface{})
|
||||
cr.MemStat(memCh)
|
||||
cr.MemStat(memCh, memkeys)
|
||||
fmt.Println(<-memCh)
|
||||
close(memCh)
|
||||
|
||||
//Disk IO
|
||||
diskioCh := make(chan interface{})
|
||||
cr.DiskIOStat(diskioCh)
|
||||
fmt.Println(<-diskioCh)
|
||||
close(diskioCh)
|
||||
//memCh := make(chan interface{})
|
||||
//cr.MemStat(memCh, keys)
|
||||
//fmt.Println(<-memCh)
|
||||
|
||||
//Disk Free
|
||||
diskFreeCh := make(chan interface{})
|
||||
cr.DiskFreeStat(diskFreeCh)
|
||||
fmt.Println(<-diskFreeCh)
|
||||
close(diskFreeCh)
|
||||
|
||||
//Network
|
||||
netCh := make(chan interface{})
|
||||
cr.NetworkStat(netCh)
|
||||
fmt.Println(<-netCh)
|
||||
close(netCh)
|
||||
|
||||
//Load Avg.
|
||||
loadCh := make(chan interface{})
|
||||
cr.LoadAvgStat(loadCh)
|
||||
fmt.Println(<-loadCh)
|
||||
close(loadCh)
|
||||
|
||||
//Process
|
||||
psCh := make(chan interface{})
|
||||
cr.ProcessStat(psCh)
|
||||
fmt.Println(<-psCh)
|
||||
close(psCh)
|
||||
////Memory
|
||||
//memCh := make(chan interface{})
|
||||
//cr.MemStat(memCh)
|
||||
//fmt.Println(<-memCh)
|
||||
//
|
||||
////Disk IO
|
||||
//diskioCh := make(chan interface{})
|
||||
//cr.DiskIOStat(diskioCh)
|
||||
//fmt.Println(<-diskioCh)
|
||||
//
|
||||
////Disk Free
|
||||
//diskFreeCh := make(chan interface{})
|
||||
//cr.DiskFreeStat(diskFreeCh)
|
||||
//fmt.Println(<-diskFreeCh)
|
||||
//
|
||||
////Network
|
||||
//netCh := make(chan interface{})
|
||||
//cr.NetworkStat(netCh)
|
||||
//fmt.Println(<-netCh)
|
||||
//
|
||||
////Load Avg.
|
||||
//loadCh := make(chan interface{})
|
||||
//cr.LoadAvgStat(loadCh)
|
||||
//fmt.Println(<-loadCh)
|
||||
//
|
||||
////Process
|
||||
//psCh := make(chan interface{})
|
||||
//cr.ProcessStat(psCh)
|
||||
//fmt.Println(<-psCh)
|
||||
}
|
||||
|
|
110
stat/cpu.go
110
stat/cpu.go
|
@ -4,29 +4,31 @@ import (
|
|||
"bufio"
|
||||
"io"
|
||||
"strings"
|
||||
"git.loafle.net/overflow/ssh_crawler/util"
|
||||
)
|
||||
|
||||
type CPUStat struct {
|
||||
device,
|
||||
user,
|
||||
nice,
|
||||
system,
|
||||
idle,
|
||||
iowait,
|
||||
irq,
|
||||
softIrq,
|
||||
steal, // (over 2.6.11)
|
||||
guest, // (over 2.6.24)
|
||||
guestNice string //(over 2.6.33)
|
||||
Device,
|
||||
User,
|
||||
Nice,
|
||||
System,
|
||||
Idle,
|
||||
Iowait,
|
||||
Irq,
|
||||
SoftIrq,
|
||||
Steal, // (over 2.6.11)
|
||||
Guest, // (over 2.6.24)
|
||||
GuestNice, //(over 2.6.33)
|
||||
Sum float64
|
||||
}
|
||||
|
||||
func (cpu CPUStat) Command() string {
|
||||
return "cat /proc/stat"
|
||||
}
|
||||
|
||||
func (cpu CPUStat) Parse(r io.Reader) (*[]CPUStat, error) {
|
||||
func (cpu CPUStat) Read(r io.Reader, keys []string) (*map[string]string, error) {
|
||||
var (
|
||||
CPUStats = []CPUStat{}
|
||||
stats = []CPUStat{}
|
||||
scanner = bufio.NewScanner(r)
|
||||
)
|
||||
|
||||
|
@ -37,31 +39,79 @@ func (cpu CPUStat) Parse(r io.Reader) (*[]CPUStat, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
steal, guest, guestNice := "", "", ""
|
||||
var steal, guest, guestNice float64
|
||||
if len(parts) > 8 {
|
||||
steal = parts[8]
|
||||
steal = util.StringToFloat64(parts[8])
|
||||
}
|
||||
if len(parts) > 9 {
|
||||
guest = parts[9]
|
||||
guest = util.StringToFloat64(parts[9])
|
||||
}
|
||||
if len(parts) > 10 {
|
||||
guestNice = parts[10]
|
||||
guestNice = util.StringToFloat64(parts[10])
|
||||
}
|
||||
|
||||
CPUStats = append(CPUStats, CPUStat{
|
||||
device: parts[0],
|
||||
user: parts[1],
|
||||
nice: parts[2],
|
||||
system: parts[3],
|
||||
idle: parts[4],
|
||||
iowait: parts[5],
|
||||
irq: parts[6],
|
||||
softIrq: parts[7],
|
||||
steal: steal,
|
||||
guest: guest,
|
||||
guestNice: guestNice,
|
||||
stats = append(stats, CPUStat{
|
||||
Device: util.StringToFloat64(parts[0]),
|
||||
User: util.StringToFloat64(parts[1]),
|
||||
Nice: util.StringToFloat64(parts[2]),
|
||||
System: util.StringToFloat64(parts[3]),
|
||||
Idle: util.StringToFloat64(parts[4]),
|
||||
Iowait: util.StringToFloat64(parts[5]),
|
||||
Irq: util.StringToFloat64(parts[6]),
|
||||
SoftIrq: util.StringToFloat64(parts[7]),
|
||||
Steal: steal,
|
||||
Guest: guest,
|
||||
GuestNice: guestNice,
|
||||
})
|
||||
}
|
||||
|
||||
return &CPUStats, scanner.Err()
|
||||
res, err :=cpu.parse(keys, stats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res, scanner.Err()
|
||||
}
|
||||
|
||||
func (cpu CPUStat) parse(keys []string, data []CPUStat) (map[string]string, error) {
|
||||
resMap := make(map[string]string)
|
||||
|
||||
for _, key := range keys {
|
||||
resMap[key] = cpu.calc(key, data[0])
|
||||
}
|
||||
|
||||
return resMap, nil
|
||||
}
|
||||
|
||||
func (cpu CPUStat) calc(key string, d CPUStat) string {
|
||||
var value float64 = 0
|
||||
sum := d.User + d.Nice + d.System + d.Idle + d.Iowait + d.Irq + d.SoftIrq + d.Steal + d.Guest + d.GuestNice
|
||||
switch key {
|
||||
case "cpu.usage.sum":
|
||||
value = sum
|
||||
case "cpu.usage.user":
|
||||
value = d.User / sum * 100
|
||||
case "cpu.usage.nice":
|
||||
value = d.Nice / sum * 100
|
||||
case "cpu.usage.system":
|
||||
value = d.System / sum * 100
|
||||
case "cpu.usage.idle":
|
||||
value = d.Idle / sum * 100
|
||||
case "cpu.usage.iowait":
|
||||
value = d.Iowait / sum * 100
|
||||
case "cpu.usage.irq":
|
||||
value = d.Irq / sum * 100
|
||||
case "cpu.usage.softirq":
|
||||
value = d.SoftIrq / sum * 100
|
||||
case "cpu.usage.steal":
|
||||
value = d.Steal / sum * 100
|
||||
case "cpu.usage.guest":
|
||||
value = d.Guest / sum * 100
|
||||
case "cpu.usage.gnice":
|
||||
value = d.GuestNice / sum * 100
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
return util.Float64ToString(value)
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@ import (
|
|||
)
|
||||
|
||||
type DiskFreeStat struct {
|
||||
filesystem,
|
||||
size,
|
||||
used,
|
||||
available,
|
||||
usePerc,
|
||||
mountedOn string
|
||||
Filesystem,
|
||||
Size,
|
||||
Used,
|
||||
Available,
|
||||
UsePerc,
|
||||
MountedOn string
|
||||
}
|
||||
|
||||
func (diskFree DiskFreeStat) Command() string {
|
||||
|
@ -28,12 +28,12 @@ func (diskio DiskFreeStat) Parse(r io.Reader) (*[]DiskFreeStat, error) {
|
|||
for scanner.Scan() {
|
||||
parts := strings.Fields(scanner.Text())
|
||||
stats = append(stats, DiskFreeStat{
|
||||
filesystem: parts[0],
|
||||
size: parts[1],
|
||||
used: parts[2],
|
||||
available: parts[3],
|
||||
usePerc: removePercUnit(parts[4]),
|
||||
mountedOn: parts[5],
|
||||
Filesystem: parts[0],
|
||||
Size: parts[1],
|
||||
Used: parts[2],
|
||||
Available: parts[3],
|
||||
UsePerc: removePercUnit(parts[4]),
|
||||
MountedOn: parts[5],
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -8,18 +8,18 @@ import (
|
|||
)
|
||||
|
||||
type DiskIOStat struct {
|
||||
device,
|
||||
readCompleted,
|
||||
readMerged,
|
||||
sectorRead,
|
||||
timeSpentReading,
|
||||
writesCompleted,
|
||||
writesMerged,
|
||||
sectorsWritten,
|
||||
timeSpentWriting,
|
||||
ioInProg,
|
||||
timeSpentIO,
|
||||
weightedTimeSpentIO string
|
||||
Device,
|
||||
ReadCompleted,
|
||||
ReadMerged,
|
||||
SectorRead,
|
||||
TimeSpentReading,
|
||||
WritesCompleted,
|
||||
WritesMerged,
|
||||
SectorsWritten,
|
||||
TimeSpentWriting,
|
||||
IoInProg,
|
||||
TimeSpentIO,
|
||||
WeightedTimeSpentIO string
|
||||
}
|
||||
|
||||
func (diskio DiskIOStat) Command() string {
|
||||
|
@ -42,18 +42,18 @@ func (diskio DiskIOStat) Parse(r io.Reader) (*[]DiskIOStat, error) {
|
|||
continue
|
||||
}
|
||||
stats = append(stats, DiskIOStat{
|
||||
device: deviceName,
|
||||
readCompleted: parts[3],
|
||||
readMerged: parts[4],
|
||||
sectorRead: parts[5],
|
||||
timeSpentReading: parts[6],
|
||||
writesCompleted: parts[7],
|
||||
writesMerged: parts[8],
|
||||
sectorsWritten: parts[9],
|
||||
timeSpentWriting: parts[10],
|
||||
ioInProg: parts[11],
|
||||
timeSpentIO: parts[12],
|
||||
weightedTimeSpentIO: parts[13],
|
||||
Device: deviceName,
|
||||
ReadCompleted: parts[3],
|
||||
ReadMerged: parts[4],
|
||||
SectorRead: parts[5],
|
||||
TimeSpentReading: parts[6],
|
||||
WritesCompleted: parts[7],
|
||||
WritesMerged: parts[8],
|
||||
SectorsWritten: parts[9],
|
||||
TimeSpentWriting: parts[10],
|
||||
IoInProg: parts[11],
|
||||
TimeSpentIO: parts[12],
|
||||
WeightedTimeSpentIO: parts[13],
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ import (
|
|||
)
|
||||
|
||||
type LoadAvg struct {
|
||||
min1,
|
||||
min5,
|
||||
min15 string
|
||||
Min1,
|
||||
Min5,
|
||||
Min15 string
|
||||
}
|
||||
|
||||
func (loadavg LoadAvg) Command() string {
|
||||
|
@ -25,9 +25,9 @@ func (loadavg LoadAvg) Parse(r io.Reader) (*LoadAvg, error) {
|
|||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
load.min1 = parts[0]
|
||||
load.min5 = parts[1]
|
||||
load.min15 = parts[2]
|
||||
load.Min1 = parts[0]
|
||||
load.Min5 = parts[1]
|
||||
load.Min15 = parts[2]
|
||||
}
|
||||
return &load, scanner.Err()
|
||||
}
|
||||
|
|
43
stat/mem.go
43
stat/mem.go
|
@ -13,9 +13,9 @@ func (mem MemStat) Command() string {
|
|||
return "cat /proc/meminfo"
|
||||
}
|
||||
|
||||
func (mem MemStat) Parse(r io.Reader) (*map[string]string, error) {
|
||||
func (mem MemStat) Read(r io.Reader, keys []string) (*map[string]string, error) {
|
||||
var (
|
||||
memInfo = map[string]string{}
|
||||
stats = map[string]string{}
|
||||
scanner = bufio.NewScanner(r)
|
||||
)
|
||||
|
||||
|
@ -23,8 +23,43 @@ func (mem MemStat) Parse(r io.Reader) (*map[string]string, error) {
|
|||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
key := parts[0][:len(parts[0])-1]
|
||||
memInfo[key] = parts[1]
|
||||
|
||||
stats[key] = parts[1]
|
||||
}
|
||||
|
||||
return &memInfo, scanner.Err()
|
||||
res, err := mem.parse(keys, stats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res, scanner.Err()
|
||||
}
|
||||
|
||||
func (mem MemStat) parse(keys []string, data map[string]string) (map[string]string, error) {
|
||||
resMap := make(map[string]string)
|
||||
|
||||
for _, key := range keys {
|
||||
switch key {
|
||||
case "mem.usage.total":
|
||||
resMap[key] = data["MemTotal"]
|
||||
case "mem.usage.free":
|
||||
resMap[key] = data["MemFree"]
|
||||
case "mem.usage.available":
|
||||
resMap[key] = data["MemAvailable"]
|
||||
case "mem.usage.buffers":
|
||||
resMap[key] = data["Buffers"]
|
||||
case "mem.usage.cached":
|
||||
resMap[key] = data["Cached"]
|
||||
case "mem.swap.usage.total":
|
||||
resMap[key] = data["SwapTotal"]
|
||||
case "mem.swap.usage.free":
|
||||
resMap[key] = data["SwapFree"]
|
||||
case "mem.swap.usage.cached":
|
||||
resMap[key] = data["SwapCached"]
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return resMap, nil
|
||||
}
|
||||
|
|
|
@ -9,23 +9,23 @@ import (
|
|||
)
|
||||
|
||||
type NetDevStat struct {
|
||||
iface,
|
||||
recvByte,
|
||||
recvPacket,
|
||||
recvErr,
|
||||
recvDrop,
|
||||
recvFifo,
|
||||
recvFrame,
|
||||
recvCompressed,
|
||||
recvMulticast,
|
||||
transByte,
|
||||
transPacket,
|
||||
transErr,
|
||||
transDrop,
|
||||
transFifo,
|
||||
transFrame,
|
||||
transCompressed,
|
||||
transMulticast string
|
||||
Iface,
|
||||
RecvByte,
|
||||
RecvPacket,
|
||||
RecvErr,
|
||||
RecvDrop,
|
||||
RecvFifo,
|
||||
RecvFrame,
|
||||
RecvCompressed,
|
||||
RecvMulticast,
|
||||
TransByte,
|
||||
TransPacket,
|
||||
TransErr,
|
||||
TransDrop,
|
||||
TransFifo,
|
||||
TransFrame,
|
||||
TransCompressed,
|
||||
TransMulticast string
|
||||
}
|
||||
|
||||
func (net *NetDevStat) Command() string {
|
||||
|
@ -56,23 +56,23 @@ func (net *NetDevStat) Parse(r io.Reader) (*[]NetDevStat, error) {
|
|||
|
||||
dev := parts[0][:len(parts[0])]
|
||||
netDevStats = append(netDevStats, NetDevStat{
|
||||
iface: dev,
|
||||
recvByte: parts[1],
|
||||
recvPacket: parts[2],
|
||||
recvErr: parts[3],
|
||||
recvDrop: parts[4],
|
||||
recvFifo: parts[5],
|
||||
recvFrame: parts[6],
|
||||
recvCompressed: parts[7],
|
||||
recvMulticast: parts[8],
|
||||
transByte: parts[9],
|
||||
transPacket: parts[10],
|
||||
transErr: parts[11],
|
||||
transDrop: parts[12],
|
||||
transFifo: parts[13],
|
||||
transFrame: parts[14],
|
||||
transCompressed: parts[15],
|
||||
transMulticast: parts[16],
|
||||
Iface: dev,
|
||||
RecvByte: parts[1],
|
||||
RecvPacket: parts[2],
|
||||
RecvErr: parts[3],
|
||||
RecvDrop: parts[4],
|
||||
RecvFifo: parts[5],
|
||||
RecvFrame: parts[6],
|
||||
RecvCompressed: parts[7],
|
||||
RecvMulticast: parts[8],
|
||||
TransByte: parts[9],
|
||||
TransPacket: parts[10],
|
||||
TransErr: parts[11],
|
||||
TransDrop: parts[12],
|
||||
TransFifo: parts[13],
|
||||
TransFrame: parts[14],
|
||||
TransCompressed: parts[15],
|
||||
TransMulticast: parts[16],
|
||||
})
|
||||
}
|
||||
return &netDevStats, scanner.Err()
|
||||
|
|
|
@ -7,12 +7,12 @@ import (
|
|||
)
|
||||
|
||||
type ProcessStat struct {
|
||||
user,
|
||||
pid,
|
||||
size,
|
||||
pcpu,
|
||||
pmem,
|
||||
cmd string
|
||||
User,
|
||||
Pid,
|
||||
Size,
|
||||
Pcpu,
|
||||
Pmem,
|
||||
Cmd string
|
||||
}
|
||||
|
||||
func (ps *ProcessStat) Command() string {
|
||||
|
@ -29,12 +29,12 @@ func (ps *ProcessStat) Parse(r io.Reader) (*[]ProcessStat, error) {
|
|||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
psStats = append(psStats, ProcessStat{
|
||||
user: parts[0],
|
||||
pid: parts[1],
|
||||
size: parts[2],
|
||||
pcpu: parts[3],
|
||||
pmem: parts[4],
|
||||
cmd: parts[5],
|
||||
User: parts[0],
|
||||
Pid: parts[1],
|
||||
Size: parts[2],
|
||||
Pcpu: parts[3],
|
||||
Pmem: parts[4],
|
||||
Cmd: parts[5],
|
||||
})
|
||||
}
|
||||
return &psStats, scanner.Err()
|
||||
|
|
1
stat/uptime.go
Normal file
1
stat/uptime.go
Normal file
|
@ -0,0 +1 @@
|
|||
package stat
|
26
util/util.go
Normal file
26
util/util.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func StringToFloat64(str string) float64 {
|
||||
n, _ := strconv.ParseFloat(str, 64)
|
||||
return n
|
||||
}
|
||||
|
||||
func Float64ToString(num float64) string {
|
||||
|
||||
return fmt.Sprintf("%v", toFixed(num, 2))
|
||||
}
|
||||
|
||||
func round(num float64) int {
|
||||
return int(num + math.Copysign(0.5, num))
|
||||
}
|
||||
|
||||
func toFixed(num float64, precision int) float64 {
|
||||
output := math.Pow(10, float64(precision))
|
||||
return float64(round(num*output)) / output
|
||||
}
|
Loading…
Reference in New Issue
Block a user