apache_avro_test_helper/
logger.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18#![cfg(not(target_arch = "wasm32"))]
19
20use crate::LOG_MESSAGES;
21use log::{LevelFilter, Log, Metadata};
22use std::sync::OnceLock;
23
24struct TestLogger {
25    delegate: env_logger::Logger,
26}
27
28impl Log for TestLogger {
29    fn enabled(&self, _metadata: &Metadata) -> bool {
30        true
31    }
32
33    fn log(&self, record: &log::Record) {
34        if self.enabled(record.metadata()) {
35            LOG_MESSAGES.with(|msgs| msgs.borrow_mut().push(format!("{}", record.args())));
36
37            self.delegate.log(record);
38        }
39    }
40
41    fn flush(&self) {}
42}
43
44fn test_logger() -> &'static TestLogger {
45    // Lazy static because the Logger has to be 'static
46    static TEST_LOGGER_ONCE: OnceLock<TestLogger> = OnceLock::new();
47    TEST_LOGGER_ONCE.get_or_init(|| TestLogger {
48        delegate: env_logger::Builder::from_default_env()
49            .filter_level(LevelFilter::Off)
50            .parse_default_env()
51            .build(),
52    })
53}
54
55/// Clears all log messages of this thread.
56pub fn clear_log_messages() {
57    LOG_MESSAGES.with_borrow_mut(Vec::clear);
58}
59
60/// Asserts that the message is not in the log for this thread.
61///
62/// # Panics
63/// Will panic if the provided message is an exact match for any message in the log.
64///
65/// # Example
66/// ```should_panic
67/// # use apache_avro_test_helper::logger::assert_not_logged;
68/// #
69/// log::error!("Unexpected Error");
70///
71/// // This will not panic as it is not an exact match
72/// assert_not_logged("No Unexpected Error");
73///
74/// // This will panic
75/// assert_not_logged("Unexpected Error");
76/// ```
77#[track_caller]
78pub fn assert_not_logged(unexpected_message: &str) {
79    LOG_MESSAGES.with_borrow(|msgs| {
80        let is_logged = msgs.iter().any(|msg| msg == unexpected_message);
81        assert!(
82            !is_logged,
83            "The following log message should not have been logged: '{unexpected_message}'"
84        );
85    });
86}
87
88/// Asserts that the message has been logged and removes it from the log of this thread.
89///
90/// # Panics
91/// Will panic if the message does not appear in the log.
92///
93/// # Example
94/// ```should_panic
95/// # use apache_avro_test_helper::logger::assert_logged;
96/// #
97/// log::error!("Something went wrong");
98/// log::info!("Something happened");
99/// log::error!("Something went wrong");
100///
101/// // This will not panic as the message was logged
102/// assert_logged("Something went wrong");
103///
104/// // This will not panic as the message was logged
105/// assert_logged("Something happened");
106///
107/// // This will not panic as the first call removed only the first matching log message
108/// assert_logged("Something went wrong");
109///
110/// // This will panic as all matching log messages have been removed
111/// assert_logged("Something went wrong");
112/// ```
113#[track_caller]
114pub fn assert_logged(expected_message: &str) {
115    let mut deleted = false;
116    LOG_MESSAGES.with_borrow_mut(|msgs| {
117        if let Some(pos) = msgs.iter().position(|msg| msg == expected_message) {
118            msgs.remove(pos);
119            deleted = true;
120        }
121    });
122
123    assert!(
124        deleted,
125        "Expected log message has not been logged: '{expected_message}'"
126    );
127}
128
129pub(crate) fn install() {
130    log::set_logger(test_logger())
131        .map(|()| log::set_max_level(LevelFilter::Trace))
132        .map_err(|err| {
133            eprintln!("Failed to set the custom logger: {err:?}");
134        })
135        .expect("Failed to set the custom TestLogger");
136}