1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! # Structed Logger API for Android
16 
17 use log_event_list::__private_api::{LogContext, LogContextError};
18 
19 /// Add functionality to a type to log to a LogContext
20 pub trait Value {
21     /// Apend value to provided context
add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError>22     fn add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError>;
23 }
24 
25 impl Value for f32 {
add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError>26     fn add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError> {
27         ctx.append_f32(*self)
28     }
29 }
30 
31 impl Value for i32 {
add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError>32     fn add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError> {
33         ctx.append_i32(*self)
34     }
35 }
36 
37 impl Value for i64 {
add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError>38     fn add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError> {
39         ctx.append_i64(*self)
40     }
41 }
42 
43 impl Value for &str {
add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError>44     fn add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError> {
45         ctx.append_str(self)
46     }
47 }
48 
49 impl Value for String {
add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError>50     fn add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError> {
51         ctx.append_str(self.as_str())
52     }
53 }
54 
55 /// Enum provides values to control sections.
56 #[derive(PartialEq)]
57 pub enum StructuredLogSection {
58     /// Start a new section
59     SubsectionStart,
60     /// End a section
61     SubsectionEnd,
62 }
63 
64 impl Value for StructuredLogSection {
add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError>65     fn add_to_context(&self, ctx: LogContext) -> Result<LogContext, LogContextError> {
66         match *self {
67             StructuredLogSection::SubsectionStart => ctx.begin_list(),
68             StructuredLogSection::SubsectionEnd => ctx.end_list(),
69         }
70     }
71 }
72 
73 // The following uses global crate names to make the usage of the macro
74 // as simple as possible as the using code does not need to use the
75 // dependencies explicitly.
76 // Using imported crate names would require using code to import all our
77 // internal dependencies without using them manually.
78 
79 /// Events log buffer.
80 /// C++ implementation always logs to events, and used by default for consistent behavior between Rust and C++.
81 pub const LOG_ID_EVENTS: u32 = log_event_list_bindgen::log_id_LOG_ID_EVENTS;
82 /// Security log buffer.
83 pub const LOG_ID_SECURITY: u32 = log_event_list_bindgen::log_id_LOG_ID_SECURITY;
84 /// Statistics log buffer.
85 pub const LOG_ID_STATS: u32 = log_event_list_bindgen::log_id_LOG_ID_STATS;
86 
87 /// Add a structured log entry to buffer $log_id.
88 /// Should not be used directly, but the macros below.
89 /// Warning: Since this macro is internal, it may change any time.
90 /// Usage: __structured_log_internal!(LOG_ID, TAG, value1, value2, ...)
91 /// Returns Result:
92 ///   Ok if entry was written successfully
93 ///   Err(str) with an error description
94 #[doc(hidden)]
95 #[macro_export]
96 macro_rules! __structured_log_internal {
97     ($log_id:expr, $tag:expr, $($entry:expr),+) => (
98         {
99             let mut ctx =
100                 log_event_list::__private_api::LogContext::new($log_id, $tag)
101                     .ok_or(log_event_list::__private_api::LogContextError).map_err(|_|
102                 "Unable to create a log context");
103             $(ctx = ctx.and_then(|c| $crate::Value::add_to_context(&$entry, c)
104                 .map_err(|_| "unable to log value"));)+;
105             ctx.and_then(|c| c.write()
106                 .map_err(|_| "unable to write log message"))
107         }
108     )
109 }
110 
111 /// Add a structured log entry to events.
112 /// Usage: structured_log!(TAG, value1, value2, ...)
113 /// To use a different log buffer, you can specify it with log_id.
114 /// Usage: structured_log!(log_id: LOG_ID, TAG, value1, value2, ...)
115 /// Returns Result:
116 ///   Ok if entry was written successfully
117 ///   Err(str) with an error description
118 #[macro_export]
119 macro_rules! structured_log {
120     (log_id: $log_id:expr, $tag:expr, $($entry:expr),+) => (
121         {
122             $crate::__structured_log_internal!($log_id, $tag, $($entry),+)
123         }
124     );
125     ($tag:expr, $($entry:expr),+) => (
126         {
127             $crate::__structured_log_internal!($crate::LOG_ID_EVENTS, $tag, $($entry),+)
128         }
129     )
130 }
131