logger/src/lib.rs (70 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#![feature(strict_provenance)]
mod task_logger;
use task_logger::TaskLogger;
use std::sync::RwLock;
use log::{LevelFilter, Log, Metadata, Record};
struct TeaclaveLogger<T> {
task_logger: RwLock<Option<TaskLogger>>,
secondary_logger: Option<T>,
}
impl<T: Log> TeaclaveLogger<T> {
pub fn log_task(&self, task_logger: TaskLogger) {
let mut lock = self.task_logger.write().unwrap();
assert!(lock.is_none(), "only one task is allowed to be logged");
if let Some(sl) = &self.secondary_logger {
sl.flush()
}
*lock = Some(task_logger);
}
pub fn end_logging_task(&self) {
let mut lock = self.task_logger.write().unwrap();
assert!(lock.is_some(), "task should be logged before being ended");
*lock = None;
}
}
impl<T: Log> Log for TeaclaveLogger<T> {
fn enabled(&self, metadata: &Metadata) -> bool {
if let Some(tl) = &*self.task_logger.read().unwrap() {
tl.enabled(metadata)
} else if let Some(sl) = &self.secondary_logger {
sl.enabled(metadata)
} else {
false
}
}
fn log(&self, record: &Record) {
let kv = record.key_values();
if let Some(v) = kv.get("buffer".into()) {
let addr = v.to_u64().unwrap();
if addr == 0 {
self.end_logging_task();
} else {
let task_logger = TaskLogger::new(addr);
self.log_task(task_logger);
}
// Ignore the message when task_id is set
return;
}
let mut lock = self.task_logger.write().unwrap();
if let Some(ref mut tl) = *lock {
tl.log(record);
return;
}
if let Some(sl) = &self.secondary_logger {
sl.log(record)
}
}
fn flush(&self) {
let mut lock = self.task_logger.write().unwrap();
if let Some(ref mut tl) = *lock {
tl.flush();
return;
}
if let Some(sl) = &self.secondary_logger {
sl.flush()
}
}
}
pub struct Builder<T> {
secondary_logger: Option<T>,
}
impl<T: Log + 'static> Builder<T> {
pub fn new() -> Self {
Default::default()
}
pub fn secondary_logger(mut self, logger: T) -> Self {
self.secondary_logger = Some(logger);
self
}
fn build(self) -> TeaclaveLogger<T> {
let task_logger = RwLock::new(None);
TeaclaveLogger {
task_logger,
secondary_logger: self.secondary_logger,
}
}
pub fn init(self) {
// Two loggers may be used, so we set the level to trace, and filter inside function log.
log::set_max_level(LevelFilter::Trace);
let logger = self.build();
log::set_boxed_logger(Box::new(logger)).unwrap();
}
}
impl<T> Default for Builder<T> {
fn default() -> Self {
Self {
secondary_logger: None,
}
}
}
#[cfg(feature = "enclave_unit_test")]
pub mod tests {
use teaclave_test_utils::*;
pub fn run_tests() -> bool {
run_tests!(test_log,)
}
// The logs are not sent to the storage service as the service client is not configured in
// teaclave_unit_tests.
fn test_log() {
log::trace!(task_id = "dummy"; "you should not see this line");
log::info!("you should see this line from the secondary logger");
log::warn!(task_id = ""; "");
}
}