Overview
The Analytics Service provides metrics and analytics for users and workflows.
Service Details:
- Package:
analytics
- Port:
50055
- Proto:
proto/analytics/analytics.proto
Service Definition
service AnalyticsService {
rpc GetUserAnalytics(GetUserAnalyticsRequest) returns (GetUserAnalyticsResponse) {}
rpc GetWorkflowAnalytics(GetWorkflowAnalyticsRequest) returns (GetWorkflowAnalyticsResponse) {}
}
RPC Methods
GetUserAnalytics
Retrieve aggregated analytics for a user across all workflows.
Request:
ID of the user whose analytics to retrieve
Response:
Total workflows ever created by the user (including active, terminated, and deleted)
Total jobs ever created by the user’s workflows (including all states)
Total number of job logs generated across all jobs
total_job_execution_duration
Sum of job execution durations for all jobs (in seconds)
Example:
req := &analytics.GetUserAnalyticsRequest{
UserId: "user-123",
}
resp, err := client.GetUserAnalytics(ctx, req)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User Analytics:\n")
fmt.Printf(" Total Workflows: %d\n", resp.TotalWorkflows)
fmt.Printf(" Total Jobs: %d\n", resp.TotalJobs)
fmt.Printf(" Total Job Logs: %d\n", resp.TotalJoblogs)
fmt.Printf(" Total Execution Time: %d seconds\n", resp.TotalJobExecutionDuration)
// Calculate average execution time
if resp.TotalJobs > 0 {
avgDuration := float64(resp.TotalJobExecutionDuration) / float64(resp.TotalJobs)
fmt.Printf(" Average Job Duration: %.2f seconds\n", avgDuration)
}
# Using grpcurl
grpcurl -plaintext -d '{
"user_id": "user-123"
}' localhost:50055 analytics.AnalyticsService/GetUserAnalytics
GetWorkflowAnalytics
Retrieve detailed analytics for a specific workflow.
Request:
ID of the user who owns the workflow
ID of the workflow to analyze
Response:
total_job_execution_duration
Total execution time for all jobs in this workflow (in seconds)
Total jobs ever created by this workflow
Total number of log entries generated by this workflow’s jobs
Example:
req := &analytics.GetWorkflowAnalyticsRequest{
UserId: "user-123",
WorkflowId: "workflow-456",
}
resp, err := client.GetWorkflowAnalytics(ctx, req)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Workflow Analytics (ID: %s):\n", resp.WorkflowId)
fmt.Printf(" Total Jobs: %d\n", resp.TotalJobs)
fmt.Printf(" Total Job Logs: %d\n", resp.TotalJoblogs)
fmt.Printf(" Total Execution Time: %d seconds\n", resp.TotalJobExecutionDuration)
if resp.TotalJobs > 0 {
avgDuration := float64(resp.TotalJobExecutionDuration) / float64(resp.TotalJobs)
avgLogs := float64(resp.TotalJoblogs) / float64(resp.TotalJobs)
fmt.Printf(" Average Job Duration: %.2f seconds\n", avgDuration)
fmt.Printf(" Average Logs per Job: %.2f\n", avgLogs)
}
# Using grpcurl
grpcurl -plaintext -d '{
"user_id": "user-123",
"workflow_id": "workflow-456"
}' localhost:50055 analytics.AnalyticsService/GetWorkflowAnalytics
Use Cases
Dashboard Metrics
Display user-level statistics on a dashboard:
func getUserDashboard(client analytics.AnalyticsServiceClient, userID string) {
ctx := context.Background()
resp, err := client.GetUserAnalytics(ctx, &analytics.GetUserAnalyticsRequest{
UserId: userID,
})
if err != nil {
log.Fatal(err)
}
// Display metrics
dashboard := map[string]interface{}{
"workflows": resp.TotalWorkflows,
"jobs": resp.TotalJobs,
"logs": resp.TotalJoblogs,
"execution_time": resp.TotalJobExecutionDuration,
}
// Render dashboard...
}
Compare performance across multiple workflows:
func compareWorkflows(client analytics.AnalyticsServiceClient, userID string, workflowIDs []string) {
ctx := context.Background()
for _, wfID := range workflowIDs {
resp, err := client.GetWorkflowAnalytics(ctx, &analytics.GetWorkflowAnalyticsRequest{
UserId: userID,
WorkflowId: wfID,
})
if err != nil {
log.Printf("Failed to get analytics for %s: %v", wfID, err)
continue
}
efficiency := float64(0)
if resp.TotalJobs > 0 {
efficiency = float64(resp.TotalJobExecutionDuration) / float64(resp.TotalJobs)
}
fmt.Printf("%s: %d jobs, %.2fs avg duration\n",
resp.WorkflowId, resp.TotalJobs, efficiency)
}
}
Cost Estimation
Estimate costs based on execution time:
func estimateCosts(client analytics.AnalyticsServiceClient, userID string) {
ctx := context.Background()
resp, err := client.GetUserAnalytics(ctx, &analytics.GetUserAnalyticsRequest{
UserId: userID,
})
if err != nil {
log.Fatal(err)
}
// Example: $0.0001 per second of execution
costPerSecond := 0.0001
totalCost := float64(resp.TotalJobExecutionDuration) * costPerSecond
fmt.Printf("Total Execution Time: %d seconds\n", resp.TotalJobExecutionDuration)
fmt.Printf("Estimated Cost: $%.4f\n", totalCost)
}
Resource Usage Monitoring
Monitor and alert on resource usage:
func monitorResourceUsage(client analytics.AnalyticsServiceClient, userID string) {
ctx := context.Background()
resp, err := client.GetUserAnalytics(ctx, &analytics.GetUserAnalyticsRequest{
UserId: userID,
})
if err != nil {
log.Fatal(err)
}
// Define thresholds
const (
maxJobs = 10000
maxLogs = 1000000
maxDuration = 86400 // 24 hours in seconds
)
// Check limits
if resp.TotalJobs > maxJobs {
fmt.Printf("WARNING: Job count exceeded: %d > %d\n", resp.TotalJobs, maxJobs)
}
if resp.TotalJoblogs > maxLogs {
fmt.Printf("WARNING: Log count exceeded: %d > %d\n", resp.TotalJoblogs, maxLogs)
}
if resp.TotalJobExecutionDuration > maxDuration {
fmt.Printf("WARNING: Execution time exceeded: %d > %d seconds\n",
resp.TotalJobExecutionDuration, maxDuration)
}
}
Connection Example
package main
import (
"context"
"log"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "github.com/hitesh22rana/chronoverse/pkg/proto/go/analytics"
)
func main() {
conn, err := grpc.Dial(
"localhost:50055",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
client := pb.NewAnalyticsServiceClient(conn)
ctx := context.Background()
// Get user analytics
userResp, err := client.GetUserAnalytics(ctx, &pb.GetUserAnalyticsRequest{
UserId: "user-123",
})
if err != nil {
log.Fatalf("GetUserAnalytics failed: %v", err)
}
log.Printf("User has %d workflows and %d jobs",
userResp.TotalWorkflows, userResp.TotalJobs)
// Get workflow analytics
wfResp, err := client.GetWorkflowAnalytics(ctx, &pb.GetWorkflowAnalyticsRequest{
UserId: "user-123",
WorkflowId: "workflow-456",
})
if err != nil {
log.Fatalf("GetWorkflowAnalytics failed: %v", err)
}
log.Printf("Workflow %s has %d jobs", wfResp.WorkflowId, wfResp.TotalJobs)
}
Metrics Reference
Duration Calculations
Job execution duration is calculated as:
duration = completed_at - started_at
- Includes only completed jobs (success or failed)
- Excludes pending, cancelled, or running jobs
- Measured in seconds
Count Aggregations
All counts include:
- Active: Currently running or scheduled
- Terminated: Stopped due to failure threshold
- Deleted: Removed from the system
Analytics provide historical data even for deleted resources to maintain accurate metrics.