ghactions_core/
logging.rs

1//! Logging utilities for GitHub Actions
2use env_logger::Builder;
3use std::io::Write;
4
5/// Initialise and create a `env_logger::Builder` which follows the
6/// GitHub Actions logging syntax.
7///
8pub fn init_logger() -> Builder {
9    let mut builder = Builder::from_default_env();
10
11    // Make sure the target is STDOUT
12    builder.target(env_logger::Target::Stdout);
13
14    // Find and setup the correct log level
15    let log_level = get_log_level();
16    log::debug!("Setting log level to {:?}", log_level);
17
18    builder.filter(None, log_level);
19    builder.write_style(env_logger::WriteStyle::Always);
20
21    // Custom Formatter for Actions
22    builder.format(|buf, record| match record.level().as_str() {
23        "DEBUG" => writeln!(buf, "::debug :: {}", record.args()),
24        "WARN" => writeln!(buf, "::warning :: {}", record.args()),
25        "ERROR" => {
26            writeln!(buf, "::error :: {}", record.args())
27        }
28        _ => writeln!(buf, "{}", record.args()),
29    });
30
31    builder
32}
33
34/// Get the Log Level for the logger
35///
36/// The log level is determined by the presence of the
37/// `DEBUG` or `RUNNER_DEBUG` environment variables.
38fn get_log_level() -> log::LevelFilter {
39    if std::env::var("DEBUG").is_ok() || std::env::var("RUNNER_DEBUG").is_ok() {
40        log::LevelFilter::Debug
41    } else {
42        log::LevelFilter::Info
43    }
44}
45
46/// Error for files (including line and column numbers)
47///
48/// # Examples
49///
50/// ```
51/// use ghactions::errorf;
52///
53/// # fn foo() {
54/// errorf!(
55///     file: "src/main.rs",
56///     line: 0,
57///     column: 0,
58///     "Error checking file"
59/// );
60/// # }
61/// ```
62#[macro_export(local_inner_macros)]
63macro_rules! errorf {
64    // errorf!(file: "./lib.rs", line: 0, column: 0, "Sample Error")
65    (file: $file:expr, line: $line:expr, column: $column:expr, $msg:tt) => {
66        ::log::log!(::log::Level::Info, "::error file={},line={},col={} :: {}", $file, $line, $column, $msg)
67    };
68    // errorf!("a {} event", "log")
69    ($($arg:tt)+) => (::log::log!($crate::Level::Error, $($arg)+))
70}
71
72/// Group Macros
73///
74/// # Examples
75///
76/// ```
77/// use ghactions::group;
78///
79/// # fn foo() {
80/// group!("working group");
81/// # }
82/// ```
83#[macro_export(local_inner_macros)]
84macro_rules! group {
85    // group!("Group name")
86    ($dst:expr $(,)?) => {
87        ::log::log!(log::Level::Info, "::group::{}", $dst)
88    };
89}
90
91/// End Group Macros
92///
93/// # Examples
94///
95/// ```
96/// use ghactions::groupend;
97///
98/// # fn foo() {
99/// groupend!();
100/// # }
101/// ```
102#[macro_export(local_inner_macros)]
103macro_rules! groupend {
104    // group_end!()
105    () => {
106        ::log::log!(log::Level::Info, "::endgroup::")
107    };
108}
109
110/// Sets the output of the Actions which can be used in subsequent Actions.
111///
112/// # Examples
113///
114/// ```rust
115/// use ghactions::setoutput;
116///
117/// # fn foo() {
118/// setoutput!("hello", "world");
119/// # }
120/// ```
121#[macro_export(local_inner_macros)]
122macro_rules! setoutput {
123    // setoutput!("name", "value")
124    ($($arg:tt)+) => {
125        {
126            use std::io::Write;
127            let output = ::std::format!("::set-output name={}::{}", $($arg)+);
128            #[cfg(feature = "log")]
129            {
130                ::log::log!(::log::Level::Info, "{}", output);
131            }
132
133            let output_file = std::env::var("GITHUB_OUTPUT").unwrap_or_else(|_| "/tmp/github_actions.env".to_string());
134            // Append to the file
135            let mut file = std::fs::OpenOptions::new()
136                .create(true)
137                .append(true)
138                .open(output_file)
139                .unwrap();
140            // Append to end of file
141            ::std::writeln!(file, "{}", output).unwrap();
142        }
143    }
144}