Add database file size and reorganize status page
- Add database file size tracking to Stats struct and GetStats() - Move routing table metrics to separate 'Routing Table' status box - Add IPv4/IPv6 updates per second to routing table metrics - Database box now shows: ASNs, prefixes, peerings, and database size - Routing table box shows: live routes, IPv4/IPv6 counts, and update rates
This commit is contained in:
parent
5bd3add59b
commit
6593a7be76
@ -25,6 +25,7 @@ const dirPermissions = 0750 // rwxr-x---
|
|||||||
type Database struct {
|
type Database struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config holds database configuration
|
// Config holds database configuration
|
||||||
@ -112,7 +113,7 @@ func NewWithConfig(config Config, logger *slog.Logger) (*Database, error) {
|
|||||||
db.SetMaxIdleConns(1)
|
db.SetMaxIdleConns(1)
|
||||||
db.SetConnMaxLifetime(0)
|
db.SetConnMaxLifetime(0)
|
||||||
|
|
||||||
database := &Database{db: db, logger: logger}
|
database := &Database{db: db, logger: logger, path: config.Path}
|
||||||
|
|
||||||
if err := database.Initialize(); err != nil {
|
if err := database.Initialize(); err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize database: %w", err)
|
return nil, fmt.Errorf("failed to initialize database: %w", err)
|
||||||
@ -437,6 +438,16 @@ func (d *Database) GetStats() (Stats, error) {
|
|||||||
return stats, err
|
return stats, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get database file size
|
||||||
|
d.logger.Info("Getting database file size")
|
||||||
|
fileInfo, err := os.Stat(d.path)
|
||||||
|
if err != nil {
|
||||||
|
d.logger.Warn("Failed to get database file size", "error", err)
|
||||||
|
stats.FileSizeBytes = 0
|
||||||
|
} else {
|
||||||
|
stats.FileSizeBytes = fileInfo.Size()
|
||||||
|
}
|
||||||
|
|
||||||
d.logger.Info("Stats collection complete")
|
d.logger.Info("Stats collection complete")
|
||||||
|
|
||||||
return stats, nil
|
return stats, nil
|
||||||
|
@ -6,11 +6,12 @@ import (
|
|||||||
|
|
||||||
// Stats contains database statistics
|
// Stats contains database statistics
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
ASNs int
|
ASNs int
|
||||||
Prefixes int
|
Prefixes int
|
||||||
IPv4Prefixes int
|
IPv4Prefixes int
|
||||||
IPv6Prefixes int
|
IPv6Prefixes int
|
||||||
Peerings int
|
Peerings int
|
||||||
|
FileSizeBytes int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store defines the interface for database operations
|
// Store defines the interface for database operations
|
||||||
|
@ -6,14 +6,21 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// shutdownTimeout is the maximum time allowed for graceful shutdown
|
||||||
|
shutdownTimeout = 60 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
// CLIEntry is the main entry point for the CLI
|
// CLIEntry is the main entry point for the CLI
|
||||||
func CLIEntry() {
|
func CLIEntry() {
|
||||||
app := fx.New(
|
app := fx.New(
|
||||||
getModule(),
|
getModule(),
|
||||||
|
fx.StopTimeout(shutdownTimeout), // Allow 60 seconds for graceful shutdown
|
||||||
fx.Invoke(func(lc fx.Lifecycle, rw *RouteWatch, logger *slog.Logger) {
|
fx.Invoke(func(lc fx.Lifecycle, rw *RouteWatch, logger *slog.Logger) {
|
||||||
lc.Append(fx.Hook{
|
lc.Append(fx.Hook{
|
||||||
OnStart: func(_ context.Context) error {
|
OnStart: func(_ context.Context) error {
|
||||||
|
@ -114,20 +114,23 @@ func (s *Server) handleRoot() http.HandlerFunc {
|
|||||||
func (s *Server) handleStatusJSON() http.HandlerFunc {
|
func (s *Server) handleStatusJSON() http.HandlerFunc {
|
||||||
// Stats represents the statistics response
|
// Stats represents the statistics response
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
Uptime string `json:"uptime"`
|
Uptime string `json:"uptime"`
|
||||||
TotalMessages uint64 `json:"total_messages"`
|
TotalMessages uint64 `json:"total_messages"`
|
||||||
TotalBytes uint64 `json:"total_bytes"`
|
TotalBytes uint64 `json:"total_bytes"`
|
||||||
MessagesPerSec float64 `json:"messages_per_sec"`
|
MessagesPerSec float64 `json:"messages_per_sec"`
|
||||||
MbitsPerSec float64 `json:"mbits_per_sec"`
|
MbitsPerSec float64 `json:"mbits_per_sec"`
|
||||||
Connected bool `json:"connected"`
|
Connected bool `json:"connected"`
|
||||||
ASNs int `json:"asns"`
|
ASNs int `json:"asns"`
|
||||||
Prefixes int `json:"prefixes"`
|
Prefixes int `json:"prefixes"`
|
||||||
IPv4Prefixes int `json:"ipv4_prefixes"`
|
IPv4Prefixes int `json:"ipv4_prefixes"`
|
||||||
IPv6Prefixes int `json:"ipv6_prefixes"`
|
IPv6Prefixes int `json:"ipv6_prefixes"`
|
||||||
Peerings int `json:"peerings"`
|
Peerings int `json:"peerings"`
|
||||||
LiveRoutes int `json:"live_routes"`
|
DatabaseSizeBytes int64 `json:"database_size_bytes"`
|
||||||
IPv4Routes int `json:"ipv4_routes"`
|
LiveRoutes int `json:"live_routes"`
|
||||||
IPv6Routes int `json:"ipv6_routes"`
|
IPv4Routes int `json:"ipv4_routes"`
|
||||||
|
IPv6Routes int `json:"ipv6_routes"`
|
||||||
|
IPv4UpdatesPerSec float64 `json:"ipv4_updates_per_sec"`
|
||||||
|
IPv6UpdatesPerSec float64 `json:"ipv6_updates_per_sec"`
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -197,20 +200,23 @@ func (s *Server) handleStatusJSON() http.HandlerFunc {
|
|||||||
rtStats := s.routingTable.GetDetailedStats()
|
rtStats := s.routingTable.GetDetailedStats()
|
||||||
|
|
||||||
stats := Stats{
|
stats := Stats{
|
||||||
Uptime: uptime,
|
Uptime: uptime,
|
||||||
TotalMessages: metrics.TotalMessages,
|
TotalMessages: metrics.TotalMessages,
|
||||||
TotalBytes: metrics.TotalBytes,
|
TotalBytes: metrics.TotalBytes,
|
||||||
MessagesPerSec: metrics.MessagesPerSec,
|
MessagesPerSec: metrics.MessagesPerSec,
|
||||||
MbitsPerSec: metrics.BitsPerSec / bitsPerMegabit,
|
MbitsPerSec: metrics.BitsPerSec / bitsPerMegabit,
|
||||||
Connected: metrics.Connected,
|
Connected: metrics.Connected,
|
||||||
ASNs: dbStats.ASNs,
|
ASNs: dbStats.ASNs,
|
||||||
Prefixes: dbStats.Prefixes,
|
Prefixes: dbStats.Prefixes,
|
||||||
IPv4Prefixes: dbStats.IPv4Prefixes,
|
IPv4Prefixes: dbStats.IPv4Prefixes,
|
||||||
IPv6Prefixes: dbStats.IPv6Prefixes,
|
IPv6Prefixes: dbStats.IPv6Prefixes,
|
||||||
Peerings: dbStats.Peerings,
|
Peerings: dbStats.Peerings,
|
||||||
LiveRoutes: rtStats.TotalRoutes,
|
DatabaseSizeBytes: dbStats.FileSizeBytes,
|
||||||
IPv4Routes: rtStats.IPv4Routes,
|
LiveRoutes: rtStats.TotalRoutes,
|
||||||
IPv6Routes: rtStats.IPv6Routes,
|
IPv4Routes: rtStats.IPv4Routes,
|
||||||
|
IPv6Routes: rtStats.IPv6Routes,
|
||||||
|
IPv4UpdatesPerSec: rtStats.IPv4UpdatesRate,
|
||||||
|
IPv6UpdatesPerSec: rtStats.IPv6UpdatesRate,
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
@ -228,20 +234,23 @@ func (s *Server) handleStatusJSON() http.HandlerFunc {
|
|||||||
func (s *Server) handleStats() http.HandlerFunc {
|
func (s *Server) handleStats() http.HandlerFunc {
|
||||||
// StatsResponse represents the API statistics response
|
// StatsResponse represents the API statistics response
|
||||||
type StatsResponse struct {
|
type StatsResponse struct {
|
||||||
Uptime string `json:"uptime"`
|
Uptime string `json:"uptime"`
|
||||||
TotalMessages uint64 `json:"total_messages"`
|
TotalMessages uint64 `json:"total_messages"`
|
||||||
TotalBytes uint64 `json:"total_bytes"`
|
TotalBytes uint64 `json:"total_bytes"`
|
||||||
MessagesPerSec float64 `json:"messages_per_sec"`
|
MessagesPerSec float64 `json:"messages_per_sec"`
|
||||||
MbitsPerSec float64 `json:"mbits_per_sec"`
|
MbitsPerSec float64 `json:"mbits_per_sec"`
|
||||||
Connected bool `json:"connected"`
|
Connected bool `json:"connected"`
|
||||||
ASNs int `json:"asns"`
|
ASNs int `json:"asns"`
|
||||||
Prefixes int `json:"prefixes"`
|
Prefixes int `json:"prefixes"`
|
||||||
IPv4Prefixes int `json:"ipv4_prefixes"`
|
IPv4Prefixes int `json:"ipv4_prefixes"`
|
||||||
IPv6Prefixes int `json:"ipv6_prefixes"`
|
IPv6Prefixes int `json:"ipv6_prefixes"`
|
||||||
Peerings int `json:"peerings"`
|
Peerings int `json:"peerings"`
|
||||||
LiveRoutes int `json:"live_routes"`
|
DatabaseSizeBytes int64 `json:"database_size_bytes"`
|
||||||
IPv4Routes int `json:"ipv4_routes"`
|
LiveRoutes int `json:"live_routes"`
|
||||||
IPv6Routes int `json:"ipv6_routes"`
|
IPv4Routes int `json:"ipv4_routes"`
|
||||||
|
IPv6Routes int `json:"ipv6_routes"`
|
||||||
|
IPv4UpdatesPerSec float64 `json:"ipv4_updates_per_sec"`
|
||||||
|
IPv6UpdatesPerSec float64 `json:"ipv6_updates_per_sec"`
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -304,20 +313,23 @@ func (s *Server) handleStats() http.HandlerFunc {
|
|||||||
rtStats := s.routingTable.GetDetailedStats()
|
rtStats := s.routingTable.GetDetailedStats()
|
||||||
|
|
||||||
stats := StatsResponse{
|
stats := StatsResponse{
|
||||||
Uptime: uptime,
|
Uptime: uptime,
|
||||||
TotalMessages: metrics.TotalMessages,
|
TotalMessages: metrics.TotalMessages,
|
||||||
TotalBytes: metrics.TotalBytes,
|
TotalBytes: metrics.TotalBytes,
|
||||||
MessagesPerSec: metrics.MessagesPerSec,
|
MessagesPerSec: metrics.MessagesPerSec,
|
||||||
MbitsPerSec: metrics.BitsPerSec / bitsPerMegabit,
|
MbitsPerSec: metrics.BitsPerSec / bitsPerMegabit,
|
||||||
Connected: metrics.Connected,
|
Connected: metrics.Connected,
|
||||||
ASNs: dbStats.ASNs,
|
ASNs: dbStats.ASNs,
|
||||||
Prefixes: dbStats.Prefixes,
|
Prefixes: dbStats.Prefixes,
|
||||||
IPv4Prefixes: dbStats.IPv4Prefixes,
|
IPv4Prefixes: dbStats.IPv4Prefixes,
|
||||||
IPv6Prefixes: dbStats.IPv6Prefixes,
|
IPv6Prefixes: dbStats.IPv6Prefixes,
|
||||||
Peerings: dbStats.Peerings,
|
Peerings: dbStats.Peerings,
|
||||||
LiveRoutes: rtStats.TotalRoutes,
|
DatabaseSizeBytes: dbStats.FileSizeBytes,
|
||||||
IPv4Routes: rtStats.IPv4Routes,
|
LiveRoutes: rtStats.TotalRoutes,
|
||||||
IPv6Routes: rtStats.IPv6Routes,
|
IPv4Routes: rtStats.IPv4Routes,
|
||||||
|
IPv6Routes: rtStats.IPv6Routes,
|
||||||
|
IPv4UpdatesPerSec: rtStats.IPv4UpdatesRate,
|
||||||
|
IPv6UpdatesPerSec: rtStats.IPv6UpdatesRate,
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
@ -122,6 +122,14 @@
|
|||||||
<span class="metric-label">Peerings</span>
|
<span class="metric-label">Peerings</span>
|
||||||
<span class="metric-value" id="peerings">-</span>
|
<span class="metric-value" id="peerings">-</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="metric">
|
||||||
|
<span class="metric-label">Database Size</span>
|
||||||
|
<span class="metric-value" id="database_size">-</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="status-card">
|
||||||
|
<h2>Routing Table</h2>
|
||||||
<div class="metric">
|
<div class="metric">
|
||||||
<span class="metric-label">Live Routes</span>
|
<span class="metric-label">Live Routes</span>
|
||||||
<span class="metric-value" id="live_routes">-</span>
|
<span class="metric-value" id="live_routes">-</span>
|
||||||
@ -134,6 +142,14 @@
|
|||||||
<span class="metric-label">IPv6 Routes</span>
|
<span class="metric-label">IPv6 Routes</span>
|
||||||
<span class="metric-value" id="ipv6_routes">-</span>
|
<span class="metric-value" id="ipv6_routes">-</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="metric">
|
||||||
|
<span class="metric-label">IPv4 Updates/sec</span>
|
||||||
|
<span class="metric-value" id="ipv4_updates_per_sec">-</span>
|
||||||
|
</div>
|
||||||
|
<div class="metric">
|
||||||
|
<span class="metric-label">IPv6 Updates/sec</span>
|
||||||
|
<span class="metric-value" id="ipv6_updates_per_sec">-</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -180,9 +196,12 @@
|
|||||||
document.getElementById('ipv4_prefixes').textContent = formatNumber(data.ipv4_prefixes);
|
document.getElementById('ipv4_prefixes').textContent = formatNumber(data.ipv4_prefixes);
|
||||||
document.getElementById('ipv6_prefixes').textContent = formatNumber(data.ipv6_prefixes);
|
document.getElementById('ipv6_prefixes').textContent = formatNumber(data.ipv6_prefixes);
|
||||||
document.getElementById('peerings').textContent = formatNumber(data.peerings);
|
document.getElementById('peerings').textContent = formatNumber(data.peerings);
|
||||||
|
document.getElementById('database_size').textContent = formatBytes(data.database_size_bytes);
|
||||||
document.getElementById('live_routes').textContent = formatNumber(data.live_routes);
|
document.getElementById('live_routes').textContent = formatNumber(data.live_routes);
|
||||||
document.getElementById('ipv4_routes').textContent = formatNumber(data.ipv4_routes);
|
document.getElementById('ipv4_routes').textContent = formatNumber(data.ipv4_routes);
|
||||||
document.getElementById('ipv6_routes').textContent = formatNumber(data.ipv6_routes);
|
document.getElementById('ipv6_routes').textContent = formatNumber(data.ipv6_routes);
|
||||||
|
document.getElementById('ipv4_updates_per_sec').textContent = data.ipv4_updates_per_sec.toFixed(1);
|
||||||
|
document.getElementById('ipv6_updates_per_sec').textContent = data.ipv6_updates_per_sec.toFixed(1);
|
||||||
|
|
||||||
// Clear any errors
|
// Clear any errors
|
||||||
document.getElementById('error').style.display = 'none';
|
document.getElementById('error').style.display = 'none';
|
||||||
|
Loading…
Reference in New Issue
Block a user