diff --git a/oak/proto/BUILD b/oak/proto/BUILD index a6c808eecee..cabb3460ba3 100644 --- a/oak/proto/BUILD +++ b/oak/proto/BUILD @@ -90,6 +90,17 @@ cc_proto_library( deps = [":policy_proto"], ) +proto_library( + name = "logger_proto", + srcs = ["logger.proto"], + deps = [], +) + +cc_proto_library( + name = "logger_cc_proto", + deps = [":logger_proto"], +) + proto_library( name = "storage_channel_proto", srcs = ["storage_channel.proto"], diff --git a/oak/proto/logger.proto b/oak/proto/logger.proto new file mode 100644 index 00000000000..0706f4bcac1 --- /dev/null +++ b/oak/proto/logger.proto @@ -0,0 +1,42 @@ +// +// Copyright 2019 The Project Oak Authors +// +// Licensed 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. +// + +syntax = "proto3"; + +package oak.logging; + +message LogMessage { + // The source file containing the message. + string file = 1; + + // The line containing the message. + uint32 line = 2; + + // The verbosity level of the message. + Level level = 3; + + // The message body. + string message = 4; +} + +// Logging levels as defined in https://docs.rs/log/0.4.10/log/enum.Level.html. +enum Level { + ERROR = 0; + WARN = 1; + INFO = 2; + DEBUG = 3; + TRACE = 4; +} \ No newline at end of file diff --git a/oak/server/BUILD b/oak/server/BUILD index 681cd5d35d1..10606d77768 100644 --- a/oak/server/BUILD +++ b/oak/server/BUILD @@ -206,6 +206,7 @@ cc_library( ":channel", ":node_thread", "//oak/common:handles", + "//oak/proto:logger_cc_proto", "@com_google_absl//absl/memory", "@com_google_asylo//asylo/util:logging", ], diff --git a/oak/server/logging_node.cc b/oak/server/logging_node.cc index 8405a438042..cb1860b18d5 100644 --- a/oak/server/logging_node.cc +++ b/oak/server/logging_node.cc @@ -18,9 +18,12 @@ #include "absl/memory/memory.h" #include "asylo/util/logging.h" +#include "oak/proto/logger.pb.h" namespace oak { +const std::string LoggingNode::log_levels[5] = {"ERROR", "WARN", "INFO", "DEBUG", "TRACE"}; + void LoggingNode::Run(Handle handle) { // Borrow pointer to the channel half. MessageChannelReadHalf* channel = BorrowReadChannel(handle); @@ -47,8 +50,12 @@ void LoggingNode::Run(Handle handle) { if (result.msg == nullptr) { break; } + oak::logging::LogMessage log_msg; + std::string msg_str(result.msg->data.begin(), result.msg->data.end()); + log_msg.ParseFromString(msg_str); LOG(INFO) << "{" << name_ << "} " - << "LOG: " << std::string(result.msg->data.data(), result.msg->data.size()); + << "LOG: " << log_levels[log_msg.level()] << " " << log_msg.file() << ":" + << log_msg.line() << ": " << log_msg.message(); // Any channel references included with the message will be dropped. } } diff --git a/oak/server/logging_node.h b/oak/server/logging_node.h index 094ec9120a6..ed50ebcc356 100644 --- a/oak/server/logging_node.h +++ b/oak/server/logging_node.h @@ -32,6 +32,7 @@ class LoggingNode final : public NodeThread { explicit LoggingNode(BaseRuntime* runtime, const std::string& name) : NodeThread(runtime, name) {} private: + static const std::string log_levels[5]; void Run(Handle handle) override; }; diff --git a/sdk/rust/oak/build.rs b/sdk/rust/oak/build.rs index ea47bf77417..236ea4e2bcc 100644 --- a/sdk/rust/oak/build.rs +++ b/sdk/rust/oak/build.rs @@ -24,6 +24,7 @@ fn main() { "../../../oak/proto/policy.proto", "../../../oak/proto/storage.proto", "../../../oak/proto/storage_channel.proto", + "../../../oak/proto/logger.proto", ], includes: &["../../.."], customize: protoc_rust::Customize::default(), diff --git a/sdk/rust/oak/src/logger/mod.rs b/sdk/rust/oak/src/logger/mod.rs index f5beda035f2..df76452f187 100644 --- a/sdk/rust/oak/src/logger/mod.rs +++ b/sdk/rust/oak/src/logger/mod.rs @@ -24,25 +24,7 @@ use log::{Level, Log, Metadata, Record, SetLoggerError}; struct OakChannelLogger { - channel: crate::io::Sender, -} - -/// An object representing a log entry. Currently just a wrapper around a string, but it may be -/// extended in the future with additional fields, e.g. level or file / line information, though -/// that would probably require defining a cross-language schema such as protobuf or FIDL for it, -/// rather than just a Rust struct. -struct LogEntry { - message: String, -} - -/// Trivial implementation of [`oak::io::Encodable`], just converting the log entry message to bytes -/// and no handles. -impl crate::io::Encodable for LogEntry { - fn encode(&self) -> Result { - let bytes = self.message.as_bytes().into(); - let handles = vec![]; - Ok(crate::io::Message { bytes, handles }) - } + channel: crate::io::Sender, } impl Log for OakChannelLogger { @@ -53,17 +35,15 @@ impl Log for OakChannelLogger { if !self.enabled(record.metadata()) { return; } - let log_entry = LogEntry { - // We add a newline to the message to force flushing when printed by the host. - message: format!( - "{} {} : {} : {}\n", - record.level(), - record.file().unwrap_or_default(), - record.line().unwrap_or_default(), - record.args() - ), + let log_msg = crate::proto::logger::LogMessage { + file: record.file().unwrap_or_default().to_string(), + line: record.line().unwrap_or_default(), + level: map_level(record.level()), + message: format!("{}", record.args()), + cached_size: ::std::default::Default::default(), + unknown_fields: ::std::default::Default::default(), }; - match self.channel.send(&log_entry) { + match self.channel.send(&log_msg) { Ok(()) => (), Err(crate::OakError::OakStatus(crate::OakStatus::ErrTerminated)) => (), Err(e) => panic!("could not send log message over log channel: {}", e), @@ -72,6 +52,16 @@ impl Log for OakChannelLogger { fn flush(&self) {} } +fn map_level(level: Level) -> crate::proto::logger::Level { + match level { + Level::Error => crate::proto::logger::Level::ERROR, + Level::Warn => crate::proto::logger::Level::WARN, + Level::Info => crate::proto::logger::Level::INFO, + Level::Debug => crate::proto::logger::Level::DEBUG, + Level::Trace => crate::proto::logger::Level::TRACE, + } +} + /// Default name for predefined node configuration that corresponds to a logging /// pseudo-Node. pub const DEFAULT_CONFIG_NAME: &str = "log"; diff --git a/sdk/rust/oak/src/proto/logger.rs b/sdk/rust/oak/src/proto/logger.rs new file mode 100644 index 00000000000..c30fca0a152 --- /dev/null +++ b/sdk/rust/oak/src/proto/logger.rs @@ -0,0 +1,394 @@ +// This file is generated by rust-protobuf 2.10.1. Do not edit +// @generated + +// https://github.com/rust-lang/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![cfg_attr(rustfmt, rustfmt_skip)] + +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unsafe_code)] +#![allow(unused_imports)] +#![allow(unused_results)] +//! Generated file from `oak/proto/logger.proto` + +use protobuf::Message as Message_imported_for_functions; +use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions; + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_10_1; + +#[derive(PartialEq,Clone,Default)] +pub struct LogMessage { + // message fields + pub file: ::std::string::String, + pub line: u32, + pub level: Level, + pub message: ::std::string::String, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a LogMessage { + fn default() -> &'a LogMessage { + ::default_instance() + } +} + +impl LogMessage { + pub fn new() -> LogMessage { + ::std::default::Default::default() + } + + // string file = 1; + + + pub fn get_file(&self) -> &str { + &self.file + } + pub fn clear_file(&mut self) { + self.file.clear(); + } + + // Param is passed by value, moved + pub fn set_file(&mut self, v: ::std::string::String) { + self.file = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_file(&mut self) -> &mut ::std::string::String { + &mut self.file + } + + // Take field + pub fn take_file(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.file, ::std::string::String::new()) + } + + // uint32 line = 2; + + + pub fn get_line(&self) -> u32 { + self.line + } + pub fn clear_line(&mut self) { + self.line = 0; + } + + // Param is passed by value, moved + pub fn set_line(&mut self, v: u32) { + self.line = v; + } + + // .oak.logging.Level level = 3; + + + pub fn get_level(&self) -> Level { + self.level + } + pub fn clear_level(&mut self) { + self.level = Level::ERROR; + } + + // Param is passed by value, moved + pub fn set_level(&mut self, v: Level) { + self.level = v; + } + + // string message = 4; + + + pub fn get_message(&self) -> &str { + &self.message + } + pub fn clear_message(&mut self) { + self.message.clear(); + } + + // Param is passed by value, moved + pub fn set_message(&mut self, v: ::std::string::String) { + self.message = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_message(&mut self) -> &mut ::std::string::String { + &mut self.message + } + + // Take field + pub fn take_message(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.message, ::std::string::String::new()) + } +} + +impl ::protobuf::Message for LogMessage { + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.file)?; + }, + 2 => { + if wire_type != ::protobuf::wire_format::WireTypeVarint { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + let tmp = is.read_uint32()?; + self.line = tmp; + }, + 3 => { + ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.level, 3, &mut self.unknown_fields)? + }, + 4 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.message)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if !self.file.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.file); + } + if self.line != 0 { + my_size += ::protobuf::rt::value_size(2, self.line, ::protobuf::wire_format::WireTypeVarint); + } + if self.level != Level::ERROR { + my_size += ::protobuf::rt::enum_size(3, self.level); + } + if !self.message.is_empty() { + my_size += ::protobuf::rt::string_size(4, &self.message); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if !self.file.is_empty() { + os.write_string(1, &self.file)?; + } + if self.line != 0 { + os.write_uint32(2, self.line)?; + } + if self.level != Level::ERROR { + os.write_enum(3, self.level.value())?; + } + if !self.message.is_empty() { + os.write_string(4, &self.message)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> LogMessage { + LogMessage::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, + }; + unsafe { + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "file", + |m: &LogMessage| { &m.file }, + |m: &mut LogMessage| { &mut m.file }, + )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeUint32>( + "line", + |m: &LogMessage| { &m.line }, + |m: &mut LogMessage| { &mut m.line }, + )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum>( + "level", + |m: &LogMessage| { &m.level }, + |m: &mut LogMessage| { &mut m.level }, + )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "message", + |m: &LogMessage| { &m.message }, + |m: &mut LogMessage| { &mut m.message }, + )); + ::protobuf::reflect::MessageDescriptor::new::( + "LogMessage", + fields, + file_descriptor_proto() + ) + }) + } + } + + fn default_instance() -> &'static LogMessage { + static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const LogMessage, + }; + unsafe { + instance.get(LogMessage::new) + } + } +} + +impl ::protobuf::Clear for LogMessage { + fn clear(&mut self) { + self.file.clear(); + self.line = 0; + self.level = Level::ERROR; + self.message.clear(); + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for LogMessage { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for LogMessage { + fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef { + ::protobuf::reflect::ProtobufValueRef::Message(self) + } +} + +#[derive(Clone,PartialEq,Eq,Debug,Hash)] +pub enum Level { + ERROR = 0, + WARN = 1, + INFO = 2, + DEBUG = 3, + TRACE = 4, +} + +impl ::protobuf::ProtobufEnum for Level { + fn value(&self) -> i32 { + *self as i32 + } + + fn from_i32(value: i32) -> ::std::option::Option { + match value { + 0 => ::std::option::Option::Some(Level::ERROR), + 1 => ::std::option::Option::Some(Level::WARN), + 2 => ::std::option::Option::Some(Level::INFO), + 3 => ::std::option::Option::Some(Level::DEBUG), + 4 => ::std::option::Option::Some(Level::TRACE), + _ => ::std::option::Option::None + } + } + + fn values() -> &'static [Self] { + static values: &'static [Level] = &[ + Level::ERROR, + Level::WARN, + Level::INFO, + Level::DEBUG, + Level::TRACE, + ]; + values + } + + fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor { + static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::reflect::EnumDescriptor, + }; + unsafe { + descriptor.get(|| { + ::protobuf::reflect::EnumDescriptor::new("Level", file_descriptor_proto()) + }) + } + } +} + +impl ::std::marker::Copy for Level { +} + +impl ::std::default::Default for Level { + fn default() -> Self { + Level::ERROR + } +} + +impl ::protobuf::reflect::ProtobufValue for Level { + fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef { + ::protobuf::reflect::ProtobufValueRef::Enum(self.descriptor()) + } +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\x16oak/proto/logger.proto\x12\x0boak.logging\"x\n\nLogMessage\x12\x12\ + \n\x04file\x18\x01\x20\x01(\tR\x04file\x12\x12\n\x04line\x18\x02\x20\x01\ + (\rR\x04line\x12(\n\x05level\x18\x03\x20\x01(\x0e2\x12.oak.logging.Level\ + R\x05level\x12\x18\n\x07message\x18\x04\x20\x01(\tR\x07message*<\n\x05Le\ + vel\x12\t\n\x05ERROR\x10\0\x12\x08\n\x04WARN\x10\x01\x12\x08\n\x04INFO\ + \x10\x02\x12\t\n\x05DEBUG\x10\x03\x12\t\n\x05TRACE\x10\x04b\x06proto3\ +"; + +static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::descriptor::FileDescriptorProto, +}; + +fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto { + ::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap() +} + +pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + unsafe { + file_descriptor_proto_lazy.get(|| { + parse_descriptor_proto() + }) + } +} diff --git a/sdk/rust/oak/src/proto/mod.rs b/sdk/rust/oak/src/proto/mod.rs index f27f79b316d..a554d65b803 100644 --- a/sdk/rust/oak/src/proto/mod.rs +++ b/sdk/rust/oak/src/proto/mod.rs @@ -21,3 +21,4 @@ pub mod policy; pub mod status; pub mod storage; pub mod storage_channel; +pub mod logger;