Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nodemonitor
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
nodemonitor
Commits
57081b96
Commit
57081b96
authored
Aug 26, 2025
by
vicotor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update disk info
parent
319ff52e
Pipeline
#887
canceled with stages
Changes
4
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
132 additions
and
50 deletions
+132
-50
main.go
agent/main.go
+37
-14
metrics.go
agent/metrics.go
+44
-4
main.go
server/main.go
+32
-19
types.go
types/types.go
+19
-13
No files found.
agent/main.go
View file @
57081b96
...
...
@@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
"log"
"net/http"
"strings"
"time"
"nodemonitor/types"
...
...
@@ -17,32 +18,56 @@ var (
serverURL
=
flag
.
String
(
"server"
,
"http://localhost:8080"
,
"Server URL"
)
interval
=
flag
.
Duration
(
"interval"
,
5
*
time
.
Second
,
"Report interval"
)
localRpc
=
flag
.
String
(
"local-rpc"
,
""
,
"Local Node RPC URL"
)
diskPaths
=
flag
.
String
(
"disks"
,
"/"
,
"Comma-separated list of disk paths to monitor (e.g., /,/data,/logs)"
)
nameTag
=
flag
.
String
(
"name-tag"
,
""
,
"Name tag"
)
)
func
main
()
{
type
AgentConfig
struct
{
ServerURL
string
Interval
time
.
Duration
LocalRpc
string
DiskPaths
[]
string
NameTag
string
}
func
parseConfig
()
*
AgentConfig
{
flag
.
Parse
()
if
*
nameTag
==
""
{
paths
:=
strings
.
Split
(
*
diskPaths
,
","
)
for
i
,
path
:=
range
paths
{
paths
[
i
]
=
strings
.
TrimSpace
(
path
)
}
return
&
AgentConfig
{
ServerURL
:
*
serverURL
,
Interval
:
*
interval
,
LocalRpc
:
*
localRpc
,
DiskPaths
:
paths
,
NameTag
:
*
nameTag
,
}
}
func
main
()
{
config
:=
parseConfig
()
if
config
.
NameTag
==
""
{
log
.
Fatal
(
"name-tag is required"
)
}
nodeID
:=
*
nameTag
log
.
Printf
(
"Starting agent with node ID: %s"
,
nodeID
)
log
.
Printf
(
"Starting agent with node ID: %s"
,
config
.
NameTag
)
log
.
Printf
(
"Reporting to server: %s"
,
*
serverURL
)
ticker
:=
time
.
NewTicker
(
*
i
nterval
)
ticker
:=
time
.
NewTicker
(
config
.
I
nterval
)
defer
ticker
.
Stop
()
var
nodeClient
*
ethclient
.
Client
var
err
error
if
len
(
*
l
ocalRpc
)
>
0
{
nodeClient
,
err
=
ethclient
.
Dial
(
*
l
ocalRpc
)
if
len
(
config
.
L
ocalRpc
)
>
0
{
nodeClient
,
err
=
ethclient
.
Dial
(
config
.
L
ocalRpc
)
if
err
!=
nil
{
log
.
Fatalf
(
"Failed to connect to local node RPC: %v"
,
err
)
}
}
for
{
status
:=
collectNodeStatus
(
nodeID
,
nodeClient
)
status
:=
collectNodeStatus
(
config
,
nodeClient
)
if
err
:=
reportToServer
(
status
);
err
!=
nil
{
log
.
Printf
(
"Failed to report to server: %v"
,
err
)
}
else
{
...
...
@@ -53,23 +78,21 @@ func main() {
}
}
func
collectNodeStatus
(
nodeID
strin
g
,
nodeClient
*
ethclient
.
Client
)
types
.
NodeStatus
{
func
collectNodeStatus
(
conf
*
AgentConfi
g
,
nodeClient
*
ethclient
.
Client
)
types
.
NodeStatus
{
cpuUsage
:=
getCPUUsage
()
memUsage
,
memTotal
,
memFree
:=
getMemoryInfo
()
disk
Usage
,
diskTotal
,
diskFree
:=
getDiskInfo
(
)
disk
Infos
:=
getDiskInfo
(
conf
.
DiskPaths
)
privateIP
:=
getPrivateIP
()
publicIP
:=
getPublicIP
()
nodeHeight
:=
getNodeHeight
(
nodeClient
)
return
types
.
NodeStatus
{
NodeID
:
nodeID
,
NodeID
:
conf
.
NameTag
,
CPUUsage
:
cpuUsage
,
MemoryUsage
:
memUsage
,
MemoryTotal
:
memTotal
,
MemoryFree
:
memFree
,
DiskUsage
:
diskUsage
,
DiskTotal
:
diskTotal
,
DiskFree
:
diskFree
,
DiskInfos
:
diskInfos
,
PrivateIP
:
privateIP
,
PublicIP
:
publicIP
,
Height
:
nodeHeight
,
...
...
agent/metrics.go
View file @
57081b96
...
...
@@ -7,6 +7,7 @@ import (
"io"
"net"
"net/http"
"nodemonitor/types"
"os"
"strconv"
"strings"
...
...
@@ -74,13 +75,52 @@ func getMemoryInfo() (float64, uint64, uint64) {
return
v
.
UsedPercent
,
v
.
Total
,
v
.
Available
}
// getDiskInfo gets disk usage information
func
getDiskInfo
()
(
float64
,
uint64
,
uint64
)
{
// getDiskInfo gets disk usage information for multiple paths
func
getDiskInfo
(
paths
[]
string
)
[]
types
.
DiskInfo
{
var
diskInfos
[]
types
.
DiskInfo
// Default paths if none provided
if
len
(
paths
)
==
0
{
paths
=
[]
string
{
"/"
}
}
for
_
,
path
:=
range
paths
{
usage
,
err
:=
disk
.
Usage
(
path
)
if
err
!=
nil
{
// Add error entry for failed disk
diskInfos
=
append
(
diskInfos
,
types
.
DiskInfo
{
Path
:
path
,
UsedPercent
:
0
,
Total
:
0
,
Free
:
0
,
})
continue
}
diskInfos
=
append
(
diskInfos
,
types
.
DiskInfo
{
Path
:
path
,
UsedPercent
:
usage
.
UsedPercent
,
Total
:
usage
.
Total
,
Free
:
usage
.
Free
,
})
}
return
diskInfos
}
// getSystemDiskInfo gets system disk (root) usage
func
getSystemDiskInfo
()
types
.
DiskInfo
{
usage
,
err
:=
disk
.
Usage
(
"/"
)
if
err
!=
nil
{
return
0
,
0
,
0
return
types
.
DiskInfo
{
Path
:
"/"
,
UsedPercent
:
0
,
Total
:
0
,
Free
:
0
}
}
return
types
.
DiskInfo
{
Path
:
"/"
,
UsedPercent
:
usage
.
UsedPercent
,
Total
:
usage
.
Total
,
Free
:
usage
.
Free
,
}
return
usage
.
UsedPercent
,
usage
.
Total
,
usage
.
Free
}
// getNodeID generates a unique node identifier
...
...
server/main.go
View file @
57081b96
...
...
@@ -50,7 +50,6 @@ func (s *Server) handleReport(w http.ResponseWriter, r *http.Request) {
log
.
Printf
(
"Received report from node: %s"
,
status
.
NodeID
)
w
.
WriteHeader
(
http
.
StatusOK
)
}
func
(
s
*
Server
)
handleNodes
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
s
.
mutex
.
RLock
()
nodes
:=
make
([]
*
types
.
NodeStatus
,
0
,
len
(
s
.
nodes
))
...
...
@@ -75,6 +74,7 @@ func (s *Server) handleNodes(w http.ResponseWriter, r *http.Request) {
th { background-color: #f2f2f2; }
.offline { background-color: #ffebee; }
.online { background-color: #e8f5e8; }
.disk-item { margin-bottom: 5px; }
</style>
</head>
<body>
...
...
@@ -84,12 +84,11 @@ func (s *Server) handleNodes(w http.ResponseWriter, r *http.Request) {
<tr>
<th>Node ID</th>
<th>Status</th>
<th>Height</th>
<th>Height</th>
<th>CPU Usage (%)</th>
<th>Memory Usage (%)</th>
<th>Memory (Free/Total)</th>
<th>Disk Usage (%)</th>
<th>Disk (Free/Total)</th>
<th>Disk Information</th>
<th>Private IP</th>
<th>Public IP</th>
<th>Last Report</th>
...
...
@@ -98,12 +97,11 @@ func (s *Server) handleNodes(w http.ResponseWriter, r *http.Request) {
<tr class="{{.StatusClass}}">
<td>{{.NodeID}}</td>
<td>{{.Status}}</td>
<td>{{.Height}}</td>
<td>{{.Height}}</td>
<td>{{printf "%.2f" .CPUUsage}}</td>
<td>{{printf "%.2f" .MemoryUsage}}</td>
<td>{{.MemoryInfo}}</td>
<td>{{printf "%.2f" .DiskUsage}}</td>
<td>{{.DiskInfo}}</td>
<td>{{.AllDisksInfo}}</td>
<td>{{.PrivateIP}}</td>
<td>{{.PublicIP}}</td>
<td>{{.LastReport}}</td>
...
...
@@ -116,11 +114,11 @@ func (s *Server) handleNodes(w http.ResponseWriter, r *http.Request) {
type
NodeView
struct
{
*
types
.
NodeStatus
Status
string
StatusClass
string
MemoryInfo
string
DiskInfo
string
LastReport
string
Status
string
StatusClass
string
MemoryInfo
string
AllDisksInfo
string
LastReport
string
}
nodeViews
:=
make
([]
NodeView
,
len
(
nodes
))
...
...
@@ -133,13 +131,29 @@ func (s *Server) handleNodes(w http.ResponseWriter, r *http.Request) {
statusClass
=
"offline"
}
// Format all disk information
var
allDisksInfo
string
if
len
(
node
.
DiskInfos
)
>
0
{
for
_
,
diskInfo
:=
range
node
.
DiskInfos
{
mountPoint
:=
diskInfo
.
Path
usage
:=
diskInfo
.
UsedPercent
*
100
allDisksInfo
+=
fmt
.
Sprintf
(
"<div class='disk-item'><strong>%s:</strong> %.2f%% (%.2f GB / %.2f GB)</div>"
,
mountPoint
,
usage
,
float64
(
diskInfo
.
Free
)
/
(
1024
*
1024
*
1024
),
float64
(
diskInfo
.
Total
)
/
(
1024
*
1024
*
1024
))
}
}
else
{
allDisksInfo
=
"No disk information available"
}
nodeViews
[
i
]
=
NodeView
{
NodeStatus
:
node
,
Status
:
status
,
StatusClass
:
statusClass
,
MemoryInfo
:
fmt
.
Sprintf
(
"%.2f GB / %.2f GB"
,
float64
(
node
.
MemoryFree
)
/
(
1024
*
1024
*
1024
),
float64
(
node
.
MemoryTotal
)
/
(
1024
*
1024
*
1024
)),
DiskInfo
:
fmt
.
Sprintf
(
"%.2f GB / %.2f GB"
,
float64
(
node
.
DiskFree
)
/
(
1024
*
1024
*
1024
),
float64
(
node
.
DiskTotal
)
/
(
1024
*
1024
*
1024
))
,
LastReport
:
node
.
Timestamp
.
Format
(
"2006-01-02 15:04:05"
),
NodeStatus
:
node
,
Status
:
status
,
StatusClass
:
statusClass
,
MemoryInfo
:
fmt
.
Sprintf
(
"%.2f GB / %.2f GB"
,
float64
(
node
.
MemoryFree
)
/
(
1024
*
1024
*
1024
),
float64
(
node
.
MemoryTotal
)
/
(
1024
*
1024
*
1024
)),
AllDisksInfo
:
allDisksInfo
,
LastReport
:
node
.
Timestamp
.
Format
(
"2006-01-02 15:04:05"
),
}
}
...
...
@@ -163,7 +177,6 @@ func (s *Server) handleNodes(w http.ResponseWriter, r *http.Request) {
return
}
}
func
main
()
{
flag
.
Parse
()
...
...
types/types.go
View file @
57081b96
...
...
@@ -4,17 +4,23 @@ import "time"
// NodeStatus represents the status information of a node
type
NodeStatus
struct
{
NodeID
string
`json:"node_id"`
CPUUsage
float64
`json:"cpu_usage"`
// CPU usage percentage
MemoryUsage
float64
`json:"memory_usage"`
// Memory usage percentage
MemoryTotal
uint64
`json:"memory_total"`
// Total memory in bytes
MemoryFree
uint64
`json:"memory_free"`
// Free memory in bytes
DiskUsage
float64
`json:"disk_usage"`
// Disk usage percentage
DiskTotal
uint64
`json:"disk_total"`
// Total disk space in bytes
DiskFree
uint64
`json:"disk_free"`
// Free disk space in bytes
PrivateIP
string
`json:"private_ip"`
// Internal IP address
PublicIP
string
`json:"public_ip"`
// External IP address
Height
string
`json:"height"`
// Node height (e.g., blockchain height)
Timestamp
time
.
Time
`json:"timestamp"`
// Report timestamp
LastHeartbeat
time
.
Time
`json:"last_heartbeat"`
// Last heartbeat time
NodeID
string
`json:"node_id"`
CPUUsage
float64
`json:"cpu_usage"`
// CPU usage percentage
MemoryUsage
float64
`json:"memory_usage"`
// Memory usage percentage
MemoryTotal
uint64
`json:"memory_total"`
// Total memory in bytes
MemoryFree
uint64
`json:"memory_free"`
// Free memory in bytes
DiskInfos
[]
DiskInfo
`json:"disk_infos"`
// List of disk usage info
PrivateIP
string
`json:"private_ip"`
// Internal IP address
PublicIP
string
`json:"public_ip"`
// External IP address
Height
string
`json:"height"`
// Node height (e.g., blockchain height)
Timestamp
time
.
Time
`json:"timestamp"`
// Report timestamp
LastHeartbeat
time
.
Time
`json:"last_heartbeat"`
// Last heartbeat time
}
// DiskInfo represents disk usage information for a single disk
type
DiskInfo
struct
{
Path
string
`json:"path"`
UsedPercent
float64
`json:"used_percent"`
Total
uint64
`json:"total"`
Free
uint64
`json:"free"`
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment