anonpenguin23 b5f6fb4497 docs: update deployment and serverless documentation
- 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
2026-05-07 07:33:52 +03:00

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)
}
}