mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-06-17 08:44:12 +00:00
- bump version to 0.122.2 - document schema migration invariants and push notification configuration - add serverless host function aliases and v2 database API documentation - introduce schema roundtrip test to prevent migration drift
160 lines
4.1 KiB
Go
160 lines
4.1 KiB
Go
package functions
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
logsLimit int
|
|
logsWASMOnly bool
|
|
)
|
|
|
|
// LogsCmd retrieves function invocation history.
|
|
//
|
|
// Default view: invocation history (always populated when the function has
|
|
// been invoked) — request_id, status, duration, error_message, plus any
|
|
// WASM-emitted log entries nested per record.
|
|
//
|
|
// --wasm-only switches to the legacy view that returns ONLY entries
|
|
// emitted by the function via log_info / log_error (often empty).
|
|
var LogsCmd = &cobra.Command{
|
|
Use: "logs <name>",
|
|
Short: "Get invocation history for a function",
|
|
Long: `Retrieves the most recent invocations for a deployed function.
|
|
|
|
Each invocation record shows: timestamp, request_id, status, duration_ms,
|
|
and (if any) the error message. WASM functions that emit log entries via
|
|
log_info / log_error have those entries nested under each record.
|
|
|
|
Pass --wasm-only to retrieve only the WASM-emitted log lines (legacy
|
|
behavior; rarely useful on functions that don't call log_info).`,
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: runLogs,
|
|
}
|
|
|
|
func init() {
|
|
LogsCmd.Flags().IntVar(&logsLimit, "limit", 50, "Maximum number of records to retrieve")
|
|
LogsCmd.Flags().BoolVar(&logsWASMOnly, "wasm-only", false,
|
|
"Show only WASM-emitted log entries (legacy view)")
|
|
}
|
|
|
|
func runLogs(cmd *cobra.Command, args []string) error {
|
|
name := args[0]
|
|
|
|
endpoint := "/v1/functions/" + name + "/logs"
|
|
q := []string{}
|
|
if logsLimit > 0 {
|
|
q = append(q, "limit="+strconv.Itoa(logsLimit))
|
|
}
|
|
if logsWASMOnly {
|
|
q = append(q, "wasm_only=1")
|
|
}
|
|
if len(q) > 0 {
|
|
endpoint += "?" + strings.Join(q, "&")
|
|
}
|
|
|
|
result, err := apiGet(endpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if logsWASMOnly {
|
|
return printWASMLogs(name, result)
|
|
}
|
|
return printInvocations(name, result)
|
|
}
|
|
|
|
// printInvocations renders the default invocation-history view.
|
|
func printInvocations(name string, result map[string]interface{}) error {
|
|
invs, ok := result["invocations"].([]interface{})
|
|
if !ok || len(invs) == 0 {
|
|
fmt.Printf("No invocations found for function %q.\n", name)
|
|
return nil
|
|
}
|
|
|
|
for _, entry := range invs {
|
|
inv, ok := entry.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
started := valStr(inv, "started_at")
|
|
status := valStr(inv, "status")
|
|
reqID := valStr(inv, "request_id")
|
|
duration := valNumberAsString(inv, "duration_ms")
|
|
errMsg := valStr(inv, "error_message")
|
|
|
|
// Header line per invocation.
|
|
fmt.Printf("[%s] %s request=%s duration=%sms\n",
|
|
started, strings.ToUpper(status), reqID, duration)
|
|
if errMsg != "" {
|
|
fmt.Printf(" error: %s\n", errMsg)
|
|
}
|
|
|
|
// Nested WASM logs (if any).
|
|
if wasmLogs, ok := inv["wasm_logs"].([]interface{}); ok {
|
|
for _, l := range wasmLogs {
|
|
le, ok := l.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
fmt.Printf(" %s [%s] %s\n",
|
|
valStr(le, "timestamp"),
|
|
strings.ToUpper(valStr(le, "level")),
|
|
valStr(le, "message"))
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Printf("\nShowing %d invocation(s). Use --wasm-only for the legacy log-line view.\n",
|
|
len(invs))
|
|
return nil
|
|
}
|
|
|
|
// printWASMLogs renders the legacy WASM-only view.
|
|
func printWASMLogs(name string, result map[string]interface{}) error {
|
|
logs, ok := result["logs"].([]interface{})
|
|
if !ok || len(logs) == 0 {
|
|
fmt.Printf("No WASM-emitted logs found for function %q. "+
|
|
"Tip: drop --wasm-only to see invocation history.\n", name)
|
|
return nil
|
|
}
|
|
|
|
for _, entry := range logs {
|
|
log, ok := entry.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
ts := valStr(log, "timestamp")
|
|
level := valStr(log, "level")
|
|
msg := valStr(log, "message")
|
|
fmt.Printf("[%s] %s: %s\n", ts, level, msg)
|
|
}
|
|
|
|
fmt.Printf("\nShowing %d log(s)\n", len(logs))
|
|
return nil
|
|
}
|
|
|
|
// valNumberAsString formats a JSON number field as a clean integer string.
|
|
func valNumberAsString(m map[string]interface{}, key string) string {
|
|
v, ok := m[key]
|
|
if !ok || v == nil {
|
|
return "0"
|
|
}
|
|
switch n := v.(type) {
|
|
case float64:
|
|
return strconv.FormatInt(int64(n), 10)
|
|
case int:
|
|
return strconv.Itoa(n)
|
|
case int64:
|
|
return strconv.FormatInt(n, 10)
|
|
case string:
|
|
return n
|
|
default:
|
|
return fmt.Sprintf("%v", n)
|
|
}
|
|
}
|