1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
//! Logging demo application
//!
//! This contains three components, from the most universal to the most demo-like:
//!
//! * A ringbuffered implementation of the [log] interface (based on [scroll-ring])
//! * A way of accessing messages logged into this from CoAP
//! * A way of injecting arbitrary log messages through CoAP
//!
//! It's implemented in a rather stupid-but-straightforward fashion; a better implementation would
//! would likely use [defmt](https://docs.rs/defmt/).
//!
//!
//! There is no separate handler builder for all the implemented handlers; see the top-level
//! [crate::full_application_tree] builder for one that includes everything from here.
use coap_message_utils::Error;
use coap_message::{Code, MinimalWritableMessage};
use coap_numbers::code;
/// A ring-buffered log structure
pub struct Log {
data: scroll_ring::Buffer<1024>,
}
impl Log {
pub fn new() -> Self {
Self {
data: Default::default(),
}
}
pub fn init(&'static self) -> Result<(), log::SetLoggerError> {
log::set_logger(self)?;
log::set_max_level(log::LevelFilter::Info);
Ok(())
}
/// Allocate a log in static memory, and set it up
pub fn start_once() -> &'static Self {
use static_cell::StaticCell;
static S: StaticCell<Log> = StaticCell::new();
let s = S.init_with(|| Self::new());
s.init()
.expect("No other log must be set before calling start_once");
s
}
/// Obtain a [coap_handler::Handler] implementation from the Log
///
/// This is exposed as a separate type rather than on Log in order to reuse the same handler
/// implementation for more than just the log.
pub fn handler(&self) -> coap_scroll_ring_server::BufferHandler<'_, 1024> {
coap_scroll_ring_server::BufferHandler::new(&self.data)
}
}
impl log::Log for Log {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() <= log::Level::Info
}
fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) {
// This is terribly lock-unlock-y; not doing anything about it because both structured
// logging and getting-an-actual-lock-on-something are better options -- this is, after
// all, primarily emulating what stdout does.
struct WriteWrapper<'a>(&'a scroll_ring::Buffer<1024>);
impl<'a> core::fmt::Write for WriteWrapper<'a> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.0.write(s.as_bytes());
Ok(())
}
}
use core::fmt::Write;
writeln!(
WriteWrapper(&self.data),
"{} {}",
record.level(),
record.args()
)
.unwrap();
}
}
fn flush(&self) {}
}
/// CoAP handler that accepts POST requests and dumps the request's plain-text payload into the log
/// at the configured log level
pub struct LogMessagePostHandler {
level: log::Level,
}
impl LogMessagePostHandler {
pub fn new_info() -> Self {
Self {
level: log::Level::Info,
}
}
pub fn new_warn() -> Self {
Self {
level: log::Level::Warn,
}
}
}
impl coap_handler::Handler for LogMessagePostHandler {
type ExtractRequestError = Error;
type BuildResponseError<M: MinimalWritableMessage> = M::UnionError;
type RequestData = ();
fn extract_request_data<M: coap_message::ReadableMessage>(
&mut self,
request: &M,
) -> Result<Self::RequestData, Error> {
use coap_message_utils::OptionsExt;
match request.code().into() {
code::POST => (),
_ => Err(Error::method_not_allowed())?,
}
request.options().ignore_elective_others()?;
let payload = core::str::from_utf8(request.payload())
.map_err(|e| Error::bad_request_with_rbep(e.valid_up_to()))?;
use log::log;
log!(self.level, "{}", payload);
Ok(())
}
fn estimate_length(&mut self, _: &Self::RequestData) -> usize {
8
}
fn build_response<M: coap_message::MutableWritableMessage>(
&mut self,
response: &mut M,
_request: (),
) -> Result<(), M::UnionError> {
response.set_code(M::Code::new(code::CHANGED)?);
Ok(())
}
}