Skip to content

Commit 9124c85

Browse files
authored
fix: Stream info command (#67)
fixes #64
1 parent b81882c commit 9124c85

File tree

1 file changed

+117
-76
lines changed

1 file changed

+117
-76
lines changed

cmd/stream.go

Lines changed: 117 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -59,36 +59,48 @@ type StreamRetentionData []struct {
5959
Duration string `json:"duration"`
6060
}
6161

62-
// StreamAlertData is the data structure for stream alerts
63-
type StreamAlertData struct {
64-
Alerts []struct {
65-
Message string `json:"message"`
66-
Name string `json:"name"`
67-
Rule struct {
68-
Config struct {
69-
Column string `json:"column"`
70-
Operator string `json:"operator"`
71-
Repeats int `json:"repeats"`
72-
Value int `json:"value"`
73-
} `json:"config"`
74-
Type string `json:"type"`
75-
} `json:"rule"`
76-
Targets []struct {
77-
Endpoint string `json:"endpoint"`
78-
Password string `json:"password,omitempty"`
79-
Repeat struct {
80-
Interval string `json:"interval"`
81-
Times int `json:"times"`
82-
} `json:"repeat"`
83-
SkipTLSCheck bool `json:"skip_tls_check,omitempty"`
84-
Type string `json:"type"`
85-
Username string `json:"username,omitempty"`
86-
Headers struct {
87-
Authorization string `json:"Authorization"`
88-
} `json:"headers,omitempty"`
89-
} `json:"targets"`
90-
} `json:"alerts"`
91-
Version string `json:"version"`
62+
// AlertConfig structure
63+
type AlertConfig struct {
64+
Version string `json:"version"`
65+
Alerts []Alert `json:"alerts"`
66+
}
67+
68+
// Alert structure
69+
type Alert struct {
70+
Targets []Target `json:"targets"`
71+
Name string `json:"name"`
72+
Message string `json:"message"`
73+
Rule Rule `json:"rule"`
74+
}
75+
76+
// Target structure
77+
type Target struct {
78+
Type string `json:"type"`
79+
Endpoint string `json:"endpoint"`
80+
Headers map[string]string `json:"headers"`
81+
SkipTLSCheck bool `json:"skip_tls_check"`
82+
Repeat Repeat `json:"repeat"`
83+
}
84+
85+
// Repeat structure
86+
type Repeat struct {
87+
Interval string `json:"interval"`
88+
Times int `json:"times"`
89+
}
90+
91+
// Rule structure
92+
type Rule struct {
93+
Type string `json:"type"`
94+
Config RuleConfig `json:"config"`
95+
}
96+
97+
// RuleConfig structure
98+
type RuleConfig struct {
99+
Column string `json:"column"`
100+
Operator string `json:"operator"`
101+
IgnoreCase bool `json:"ignoreCase"`
102+
Value interface{} `json:"value"`
103+
Repeats int `json:"repeats"`
92104
}
93105

94106
// AddStreamCmd is the parent command for stream
@@ -132,10 +144,11 @@ var StatStreamCmd = &cobra.Command{
132144
Example: " pb stream info backend_logs",
133145
Short: "Get statistics for a stream",
134146
Args: cobra.ExactArgs(1),
135-
RunE: func(_ *cobra.Command, args []string) error {
147+
RunE: func(cmd *cobra.Command, args []string) error {
136148
name := args[0]
137149
client := DefaultClient()
138150

151+
// Fetch stats data
139152
stats, err := fetchStats(&client, name)
140153
if err != nil {
141154
return err
@@ -144,68 +157,96 @@ var StatStreamCmd = &cobra.Command{
144157
ingestionCount := stats.Ingestion.Count
145158
ingestionSize, _ := strconv.Atoi(strings.TrimRight(stats.Ingestion.Size, " Bytes"))
146159
storageSize, _ := strconv.Atoi(strings.TrimRight(stats.Storage.Size, " Bytes"))
160+
compressionRatio := 100 - (float64(storageSize) / float64(ingestionSize) * 100)
147161

162+
// Fetch retention data
148163
retention, err := fetchRetention(&client, name)
149164
if err != nil {
150165
return err
151166
}
152167

153-
isRetentionSet := len(retention) > 0
154-
155-
fmt.Println(StyleBold.Render("\nInfo:"))
156-
fmt.Printf(" Event Count: %d\n", ingestionCount)
157-
fmt.Printf(" Ingestion Size: %s\n", humanize.Bytes(uint64(ingestionSize)))
158-
fmt.Printf(" Storage Size: %s\n", humanize.Bytes(uint64(storageSize)))
159-
fmt.Printf(
160-
" Compression Ratio: %.2f%s\n",
161-
100-(float64(storageSize)/float64(ingestionSize))*100, "%")
162-
fmt.Println()
163-
164-
if isRetentionSet {
165-
fmt.Println(StyleBold.Render("Retention:"))
166-
for _, item := range retention {
167-
fmt.Printf(" Action: %s\n", StyleBold.Render(item.Action))
168-
fmt.Printf(" Duration: %s\n", StyleBold.Render(item.Duration))
169-
fmt.Println()
170-
}
171-
} else {
172-
fmt.Println(StyleBold.Render("No retention period set on stream\n"))
173-
}
174-
168+
// Fetch alerts data
175169
alertsData, err := fetchAlerts(&client, name)
176170
if err != nil {
177171
return err
178172
}
179-
alerts := alertsData.Alerts
180-
181-
isAlertsSet := len(alerts) > 0
182-
183-
if isAlertsSet {
184-
fmt.Println(StyleBold.Render("Alerts:"))
185-
for _, alert := range alerts {
186-
fmt.Printf(" Alert: %s\n", StyleBold.Render(alert.Name))
187-
ruleFmt := fmt.Sprintf(
188-
"%s %s %s repeated %d times",
189-
alert.Rule.Config.Column,
190-
alert.Rule.Config.Operator,
191-
fmt.Sprint(alert.Rule.Config.Value),
192-
alert.Rule.Config.Repeats,
193-
)
194-
fmt.Printf(" Rule: %s\n", ruleFmt)
195-
fmt.Printf(" Targets: ")
196-
for _, target := range alert.Targets {
197-
fmt.Printf("%s, ", target.Type)
198-
}
199-
fmt.Print("\n\n")
173+
174+
// Check output format
175+
output, _ := cmd.Flags().GetString("output")
176+
if output == "json" {
177+
// Prepare JSON response
178+
data := map[string]interface{}{
179+
"info": map[string]interface{}{
180+
"event_count": ingestionCount,
181+
"ingestion_size": humanize.Bytes(uint64(ingestionSize)),
182+
"storage_size": humanize.Bytes(uint64(storageSize)),
183+
"compression_ratio": fmt.Sprintf("%.2f%%", compressionRatio),
184+
},
185+
"retention": retention,
186+
"alerts": alertsData.Alerts,
187+
}
188+
189+
jsonData, err := json.MarshalIndent(data, "", " ")
190+
if err != nil {
191+
return err
200192
}
193+
fmt.Println(string(jsonData))
201194
} else {
202-
fmt.Println(StyleBold.Render("No alerts set on stream\n"))
195+
// Default text output
196+
isRetentionSet := len(retention) > 0
197+
isAlertsSet := len(alertsData.Alerts) > 0
198+
199+
fmt.Println(StyleBold.Render("\nInfo:"))
200+
fmt.Printf(" Event Count: %d\n", ingestionCount)
201+
fmt.Printf(" Ingestion Size: %s\n", humanize.Bytes(uint64(ingestionSize)))
202+
fmt.Printf(" Storage Size: %s\n", humanize.Bytes(uint64(storageSize)))
203+
fmt.Printf(
204+
" Compression Ratio: %.2f%s\n",
205+
compressionRatio, "%")
206+
fmt.Println()
207+
208+
if isRetentionSet {
209+
fmt.Println(StyleBold.Render("Retention:"))
210+
for _, item := range retention {
211+
fmt.Printf(" Action: %s\n", StyleBold.Render(item.Action))
212+
fmt.Printf(" Duration: %s\n", StyleBold.Render(item.Duration))
213+
fmt.Println()
214+
}
215+
} else {
216+
fmt.Println(StyleBold.Render("No retention period set on stream\n"))
217+
}
218+
219+
if isAlertsSet {
220+
fmt.Println(StyleBold.Render("Alerts:"))
221+
for _, alert := range alertsData.Alerts {
222+
fmt.Printf(" Alert: %s\n", StyleBold.Render(alert.Name))
223+
ruleFmt := fmt.Sprintf(
224+
"%s %s %s repeated %d times",
225+
alert.Rule.Config.Column,
226+
alert.Rule.Config.Operator,
227+
fmt.Sprint(alert.Rule.Config.Value),
228+
alert.Rule.Config.Repeats,
229+
)
230+
fmt.Printf(" Rule: %s\n", ruleFmt)
231+
fmt.Printf(" Targets: ")
232+
for _, target := range alert.Targets {
233+
fmt.Printf("%s, ", target.Type)
234+
}
235+
fmt.Print("\n\n")
236+
}
237+
} else {
238+
fmt.Println(StyleBold.Render("No alerts set on stream\n"))
239+
}
203240
}
204241

205242
return nil
206243
},
207244
}
208245

246+
func init() {
247+
StatStreamCmd.Flags().String("output", "text", "Output format: text or json")
248+
}
249+
209250
var RemoveStreamCmd = &cobra.Command{
210251
Use: "remove stream-name",
211252
Aliases: []string{"rm"},
@@ -345,7 +386,7 @@ func fetchRetention(client *HTTPClient, name string) (data StreamRetentionData,
345386
return
346387
}
347388

348-
func fetchAlerts(client *HTTPClient, name string) (data StreamAlertData, err error) {
389+
func fetchAlerts(client *HTTPClient, name string) (data AlertConfig, err error) {
349390
req, err := client.NewRequest("GET", fmt.Sprintf("logstream/%s/alert", name), nil)
350391
if err != nil {
351392
return

0 commit comments

Comments
 (0)