Skip to content

Commit f35beb9

Browse files
committed
feat: Implement core Argus application with OTLP ingestion, storage, and web UI.
1 parent 3c9ca77 commit f35beb9

9 files changed

Lines changed: 918 additions & 248 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Project Argus
22

3+
[![Latest Release](https://img.shields.io/github/v/release/RandomCodeSpace/Project-Argus)](https://github.com/RandomCodeSpace/Project-Argus/releases)
4+
[![Security Audit](https://github.com/RandomCodeSpace/Project-Argus/actions/workflows/audit.yml/badge.svg)](https://github.com/RandomCodeSpace/Project-Argus/actions)
5+
![Go Version](https://img.shields.io/github/go-mod/go-version/RandomCodeSpace/Project-Argus)
6+
![React](https://img.shields.io/badge/frontend-React%20v18-61dafb?logo=react)
7+
38
Project Argus is an integrated observability and AI analysis platform.
49

510
## Getting Started

cmd/argus/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func main() {
6262
slog.Info("📊 Internal telemetry initialized")
6363

6464
// 2. Initialize Storage
65-
repo, err := storage.NewRepository()
65+
repo, err := storage.NewRepository(metrics)
6666
if err != nil {
6767
log.Fatalf("Failed to initialize repository: %v", err)
6868
}
@@ -104,8 +104,8 @@ func main() {
104104
apiServer := api.NewServer(repo, hub, metrics)
105105

106106
// 7. Initialize OTLP Ingestion (gRPC)
107-
traceServer := ingest.NewTraceServer(repo, cfg)
108-
logsServer := ingest.NewLogsServer(repo, cfg)
107+
traceServer := ingest.NewTraceServer(repo, metrics, cfg)
108+
logsServer := ingest.NewLogsServer(repo, metrics, cfg)
109109

110110
// Wire up live log streaming + AI + DLQ metrics
111111
logHandler := func(l storage.Log) {

internal/ingest/otlp.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/RandomCodeSpace/Project-Argus/internal/config"
1212
"github.com/RandomCodeSpace/Project-Argus/internal/storage"
13+
"github.com/RandomCodeSpace/Project-Argus/internal/telemetry"
1314
collogspb "go.opentelemetry.io/proto/otlp/collector/logs/v1"
1415
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
1516
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
@@ -18,6 +19,7 @@ import (
1819

1920
type TraceServer struct {
2021
repo *storage.Repository
22+
metrics *telemetry.Metrics
2123
logCallback func(storage.Log)
2224
minSeverity int
2325
allowedServices map[string]bool
@@ -27,16 +29,18 @@ type TraceServer struct {
2729

2830
type LogsServer struct {
2931
repo *storage.Repository
32+
metrics *telemetry.Metrics
3033
logCallback func(storage.Log)
3134
minSeverity int
3235
allowedServices map[string]bool
3336
excludedServices map[string]bool
3437
collogspb.UnimplementedLogsServiceServer
3538
}
3639

37-
func NewTraceServer(repo *storage.Repository, cfg *config.Config) *TraceServer {
40+
func NewTraceServer(repo *storage.Repository, metrics *telemetry.Metrics, cfg *config.Config) *TraceServer {
3841
return &TraceServer{
3942
repo: repo,
43+
metrics: metrics,
4044
minSeverity: parseSeverity(cfg.IngestMinSeverity),
4145
allowedServices: parseServiceList(cfg.IngestAllowedServices),
4246
excludedServices: parseServiceList(cfg.IngestExcludedServices),
@@ -48,9 +52,10 @@ func (s *TraceServer) SetLogCallback(cb func(storage.Log)) {
4852
s.logCallback = cb
4953
}
5054

51-
func NewLogsServer(repo *storage.Repository, cfg *config.Config) *LogsServer {
55+
func NewLogsServer(repo *storage.Repository, metrics *telemetry.Metrics, cfg *config.Config) *LogsServer {
5256
return &LogsServer{
5357
repo: repo,
58+
metrics: metrics,
5459
minSeverity: parseSeverity(cfg.IngestMinSeverity),
5560
allowedServices: parseServiceList(cfg.IngestAllowedServices),
5661
excludedServices: parseServiceList(cfg.IngestExcludedServices),
@@ -106,6 +111,7 @@ func (s *TraceServer) Export(ctx context.Context, req *coltracepb.ExportTraceSer
106111
StartTime: startTime,
107112
EndTime: endTime,
108113
Duration: duration,
114+
ServiceName: serviceName,
109115
AttributesJSON: string(attrs),
110116
}
111117
spansToInsert = append(spansToInsert, sModel)
@@ -226,6 +232,9 @@ func (s *TraceServer) Export(ctx context.Context, req *coltracepb.ExportTraceSer
226232
return nil, err
227233
}
228234
// slog.Debug("✅ Successfully persisted spans", "count", len(spansToInsert))
235+
if s.metrics != nil {
236+
s.metrics.RecordIngestion(len(spansToInsert))
237+
}
229238
}
230239

231240
return &coltracepb.ExportTraceServiceResponse{}, nil
@@ -304,6 +313,9 @@ func (s *LogsServer) Export(ctx context.Context, req *collogspb.ExportLogsServic
304313
return nil, err
305314
}
306315
// slog.Debug("✅ Successfully persisted logs", "count", len(logsToInsert))
316+
if s.metrics != nil {
317+
s.metrics.RecordIngestion(len(logsToInsert))
318+
}
307319
}
308320

309321
// Notify listener

internal/storage/models.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ type Span struct {
3333
OperationName string `gorm:"size:255;index" json:"operation_name"`
3434
StartTime time.Time `json:"start_time"`
3535
EndTime time.Time `json:"end_time"`
36-
Duration int64 `json:"duration"` // Microseconds
37-
AttributesJSON string `gorm:"type:text" json:"attributes_json"` // Stored as JSON string
36+
Duration int64 `json:"duration"` // Microseconds
37+
ServiceName string `gorm:"size:255;index" json:"service_name"` // Originating service
38+
AttributesJSON string `gorm:"type:text" json:"attributes_json"` // Stored as JSON string
3839
}
3940

4041
// Log represents a log entry associated with a trace.

internal/storage/repository.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@ import (
77
"strings"
88
"time"
99

10+
"github.com/RandomCodeSpace/Project-Argus/internal/telemetry"
1011
"gorm.io/gorm"
1112
"gorm.io/gorm/clause"
1213
)
1314

1415
// Repository wraps the GORM database handle for all data access operations.
1516
type Repository struct {
16-
db *gorm.DB
17-
driver string
17+
db *gorm.DB
18+
driver string
19+
metrics *telemetry.Metrics
1820
}
1921

2022
// NewRepository initializes the database connection using environment variables and migrates the schema.
21-
func NewRepository() (*Repository, error) {
23+
func NewRepository(metrics *telemetry.Metrics) (*Repository, error) {
2224
driver := os.Getenv("DB_DRIVER")
2325
dsn := os.Getenv("DB_DSN")
2426

@@ -36,7 +38,30 @@ func NewRepository() (*Repository, error) {
3638
return nil, err
3739
}
3840

39-
return &Repository{db: db, driver: driver}, nil
41+
// Register GORM Callback for DB Latency Metrics
42+
if metrics != nil {
43+
db.Callback().Query().Before("gorm:query").Register("telemetry:before_query", func(d *gorm.DB) {
44+
d.Set("telemetry:start_time", time.Now())
45+
})
46+
db.Callback().Query().After("gorm:query").Register("telemetry:after_query", func(d *gorm.DB) {
47+
if start, ok := d.Get("telemetry:start_time"); ok {
48+
duration := time.Since(start.(time.Time)).Seconds()
49+
metrics.ObserveDBLatency(duration)
50+
}
51+
})
52+
// Also measure Create/Update/Delete if desired, but Query is most frequent for "Latency"
53+
db.Callback().Create().Before("gorm:create").Register("telemetry:before_create", func(d *gorm.DB) {
54+
d.Set("telemetry:start_time", time.Now())
55+
})
56+
db.Callback().Create().After("gorm:create").Register("telemetry:after_create", func(d *gorm.DB) {
57+
if start, ok := d.Get("telemetry:start_time"); ok {
58+
duration := time.Since(start.(time.Time)).Seconds()
59+
metrics.ObserveDBLatency(duration)
60+
}
61+
})
62+
}
63+
64+
return &Repository{db: db, driver: driver, metrics: metrics}, nil
4065
}
4166

4267
// BatchCreateSpans inserts multiple spans in batches.

0 commit comments

Comments
 (0)