@@ -32,6 +32,7 @@ use value::{
32
32
HeapSize ,
33
33
WithHeapSize ,
34
34
} ,
35
+ obj,
35
36
remove_boolean,
36
37
remove_int64,
37
38
remove_nullable_object,
@@ -44,6 +45,7 @@ use value::{
44
45
use crate :: runtime:: UnixTimestamp ;
45
46
46
47
pub const TRUNCATED_LINE_SUFFIX : & str = " (truncated due to length)" ;
48
+ pub const MAX_LOG_LINE_LENGTH : usize = 32768 ;
47
49
/// List of log lines from a Convex function execution.
48
50
pub type LogLines = WithHeapSize < Vec < LogLine > > ;
49
51
pub type RawLogLines = WithHeapSize < Vec < String > > ;
@@ -95,7 +97,7 @@ impl HeapSize for LogLevel {
95
97
#[ derive( Clone , Debug , PartialEq ) ]
96
98
#[ cfg_attr( any( test, feature = "testing" ) , derive( proptest_derive:: Arbitrary ) ) ]
97
99
pub struct SystemLogMetadata {
98
- code : String ,
100
+ pub code : String ,
99
101
}
100
102
101
103
impl HeapSize for SystemLogMetadata {
@@ -114,6 +116,14 @@ impl TryFrom<ConvexObject> for SystemLogMetadata {
114
116
}
115
117
}
116
118
119
+ impl TryFrom < SystemLogMetadata > for ConvexValue {
120
+ type Error = anyhow:: Error ;
121
+
122
+ fn try_from ( value : SystemLogMetadata ) -> Result < Self , Self :: Error > {
123
+ Ok ( ConvexValue :: Object ( obj ! ( "code" => value. code) ?) )
124
+ }
125
+ }
126
+
117
127
#[ derive( Clone , Debug , PartialEq ) ]
118
128
pub enum LogLine {
119
129
Unstructured ( String ) ,
@@ -141,17 +151,21 @@ impl Arbitrary for LogLine {
141
151
prop:: collection:: vec( any:: <String >( ) , 1 ..4 ) ,
142
152
any:: <LogLevel >( ) ,
143
153
any:: <bool >( ) ,
144
- any:: <u64 >( ) ,
154
+ any:: <i64 >( ) ,
145
155
any:: <Option <SystemLogMetadata >>( )
146
156
)
147
- . prop_map(
157
+ . prop_filter_map(
158
+ "Invalid LogLine" ,
148
159
|( messages, level, is_truncated, timestamp_ms, system_metadata) | {
149
- LogLine :: Structured {
150
- messages: messages. into( ) ,
151
- level,
152
- is_truncated,
153
- timestamp: UnixTimestamp :: from_millis( timestamp_ms) ,
154
- system_metadata,
160
+ match u64 :: try_from( timestamp_ms) {
161
+ Ok ( timestamp_ms) => Some ( LogLine :: Structured {
162
+ messages: messages. into( ) ,
163
+ level,
164
+ is_truncated,
165
+ timestamp: UnixTimestamp :: from_millis( timestamp_ms) ,
166
+ system_metadata,
167
+ } ) ,
168
+ Err ( _) => None ,
155
169
}
156
170
}
157
171
)
@@ -178,6 +192,61 @@ impl LogLine {
178
192
} ,
179
193
}
180
194
}
195
+
196
+ pub fn new_developer_log_line (
197
+ level : LogLevel ,
198
+ messages : Vec < String > ,
199
+ timestamp : UnixTimestamp ,
200
+ ) -> Self {
201
+ // total length of messages joined by a space
202
+ let total_length = messages. iter ( ) . map ( |m| m. len ( ) + 1 ) . sum :: < usize > ( ) - 1 ;
203
+ if total_length <= MAX_LOG_LINE_LENGTH {
204
+ return LogLine :: Structured {
205
+ messages : messages. into ( ) ,
206
+ level,
207
+ is_truncated : false ,
208
+ timestamp,
209
+ system_metadata : None ,
210
+ } ;
211
+ }
212
+ let mut total_length = 0 ;
213
+ let mut truncated_messages: Vec < String > = vec ! [ ] ;
214
+ for message in messages {
215
+ let remaining_space = MAX_LOG_LINE_LENGTH - TRUNCATED_LINE_SUFFIX . len ( ) - total_length;
216
+ if message. len ( ) <= remaining_space {
217
+ total_length += message. len ( ) + 1 ;
218
+ truncated_messages. push ( message) ;
219
+ } else {
220
+ let last_message =
221
+ message[ ..message. floor_char_boundary ( remaining_space) ] . to_string ( ) ;
222
+ truncated_messages. push ( last_message) ;
223
+ break ;
224
+ }
225
+ }
226
+ LogLine :: Structured {
227
+ messages : truncated_messages. into ( ) ,
228
+ level,
229
+ is_truncated : true ,
230
+ timestamp,
231
+ system_metadata : None ,
232
+ }
233
+ }
234
+
235
+ pub fn new_system_log_line (
236
+ level : LogLevel ,
237
+ messages : Vec < String > ,
238
+ timestamp : UnixTimestamp ,
239
+ system_log_metadata : SystemLogMetadata ,
240
+ ) -> Self {
241
+ // Never truncate system log lines
242
+ LogLine :: Structured {
243
+ messages : messages. into ( ) ,
244
+ level,
245
+ is_truncated : false ,
246
+ timestamp,
247
+ system_metadata : Some ( system_log_metadata) ,
248
+ }
249
+ }
181
250
}
182
251
183
252
impl HeapSize for LogLine {
@@ -216,7 +285,8 @@ impl TryFrom<ConvexValue> for LogLine {
216
285
let level = remove_string ( & mut fields, "level" ) ?;
217
286
218
287
let timestamp = remove_int64 ( & mut fields, "timestamp" ) ?;
219
- let system_metadata = remove_nullable_object ( & mut fields, "system_metadata" ) ?;
288
+ let system_metadata: Option < SystemLogMetadata > =
289
+ remove_nullable_object ( & mut fields, "system_metadata" ) ?;
220
290
221
291
LogLine :: Structured {
222
292
messages : messages. clone ( ) . into ( ) ,
@@ -236,7 +306,30 @@ impl TryFrom<LogLine> for ConvexValue {
236
306
type Error = anyhow:: Error ;
237
307
238
308
fn try_from ( value : LogLine ) -> Result < Self , Self :: Error > {
239
- Ok ( ConvexValue :: String ( value. to_pretty_string ( ) . try_into ( ) ?) )
309
+ let result = match value {
310
+ LogLine :: Unstructured ( v) => v. try_into ( ) ?,
311
+ LogLine :: Structured {
312
+ messages,
313
+ level,
314
+ is_truncated,
315
+ timestamp,
316
+ system_metadata,
317
+ } => {
318
+ let timestamp_ms: i64 = timestamp. as_ms_since_epoch ( ) ?. try_into ( ) ?;
319
+ let system_metadata_value = match system_metadata {
320
+ Some ( m) => ConvexValue :: try_from ( m) ?,
321
+ None => ConvexValue :: Null ,
322
+ } ;
323
+ ConvexValue :: Object ( obj ! (
324
+ "messages" => messages. into_iter( ) . map( ConvexValue :: try_from) . try_collect:: <Vec <_>>( ) ?,
325
+ "level" => level. to_string( ) ,
326
+ "is_truncated" => is_truncated,
327
+ "timestamp" => timestamp_ms,
328
+ "system_metadata" => system_metadata_value,
329
+ ) ?)
330
+ } ,
331
+ } ;
332
+ Ok ( result)
240
333
}
241
334
}
242
335
@@ -426,9 +519,7 @@ mod tests {
426
519
) ]
427
520
#[ test]
428
521
fn test_structured_round_trips( log_line in any:: <LogLine >( ) ) {
429
- let pretty = log_line. clone( ) . to_pretty_string( ) ;
430
- let val = LogLine :: try_from( ConvexValue :: try_from( log_line) . unwrap( ) ) . unwrap( ) ;
431
- assert_eq!( val, LogLine :: Unstructured ( pretty) ) ;
522
+ assert_roundtrips:: <LogLine , ConvexValue >( log_line) ;
432
523
}
433
524
434
525
#[ test]
0 commit comments