Skip to main content

forge_runtime/function/
execution_log.rs

1//! Structured logging for function execution outcomes.
2//!
3//! Pulled out of [`super::router::FunctionRouter`] so the router only owns
4//! dispatch concerns. The logger is a pure function over the execution
5//! outcome and the function's configured log level.
6
7use std::time::Duration;
8
9use forge_core::{FunctionInfo, FunctionKind, LogLevel};
10use serde_json::Value;
11use tracing::{debug, error, info, trace, warn};
12
13/// Resolve the effective log level for a function: explicit override on the
14/// function info wins, otherwise mutations/webhooks default to `Info` and
15/// queries default to `Debug` (high volume).
16pub fn level_for(info: Option<&FunctionInfo>) -> LogLevel {
17    info.map(|i| {
18        i.log_level.unwrap_or(match i.kind {
19            FunctionKind::Mutation => LogLevel::Info,
20            FunctionKind::Query => LogLevel::Debug,
21            FunctionKind::Webhook => LogLevel::Info,
22            _ => LogLevel::Info,
23        })
24    })
25    .unwrap_or(LogLevel::Info)
26}
27
28/// Emit a structured log line for a function execution.
29///
30/// Failures always log at `error` regardless of the configured level so that
31/// operator dashboards never miss a failed call. Successes log at the
32/// configured level. The input payload is logged at `debug` so callers can
33/// opt out of payload visibility by raising the global filter.
34#[allow(clippy::too_many_arguments)]
35pub fn log_completion(
36    log_level: LogLevel,
37    function_name: &str,
38    kind: &str,
39    input: &Value,
40    duration: Duration,
41    success: bool,
42    error_msg: Option<&str>,
43) {
44    if !success {
45        error!(
46            function = function_name,
47            kind = kind,
48            duration_ms = duration.as_millis() as u64,
49            error = error_msg,
50            "Function failed"
51        );
52        debug!(
53            function = function_name,
54            input = %input,
55            "Function input"
56        );
57        return;
58    }
59
60    macro_rules! log_fn {
61        ($level:ident) => {{
62            $level!(
63                function = function_name,
64                kind = kind,
65                duration_ms = duration.as_millis() as u64,
66                "Function executed"
67            );
68            debug!(
69                function = function_name,
70                input = %input,
71                "Function input"
72            );
73        }};
74    }
75
76    match log_level {
77        LogLevel::Off => {}
78        LogLevel::Error => log_fn!(error),
79        LogLevel::Warn => log_fn!(warn),
80        LogLevel::Info => log_fn!(info),
81        LogLevel::Debug => log_fn!(debug),
82        LogLevel::Trace => log_fn!(trace),
83        _ => log_fn!(trace),
84    }
85}