1+ use crate :: config:: Config ;
12use log:: { Level , LevelFilter , Log , Metadata , Record } ;
23use std:: fmt;
34use std:: sync:: { Arc , Mutex } ;
45
56#[ derive( Clone ) ]
7+ #[ cfg_attr( test, derive( Debug , PartialEq , Eq ) ) ]
68struct StoredRecord {
79 level : Level ,
810 message : String ,
911}
1012
1113struct InnerStorage {
1214 records : Vec < StoredRecord > ,
15+ size : usize ,
16+ truncated : bool ,
1317}
1418
1519#[ derive( Clone ) ]
1620pub struct LogStorage {
1721 inner : Arc < Mutex < InnerStorage > > ,
1822 min_level : LevelFilter ,
23+ max_size : usize ,
24+ max_lines : usize ,
1925}
2026
2127impl LogStorage {
22- pub ( crate ) fn new ( min_level : LevelFilter ) -> Self {
28+ pub ( crate ) fn new ( min_level : LevelFilter , config : & Config ) -> Self {
2329 LogStorage {
2430 inner : Arc :: new ( Mutex :: new ( InnerStorage {
2531 records : Vec :: new ( ) ,
32+ truncated : false ,
33+ size : 0 ,
2634 } ) ) ,
2735 min_level,
36+ max_size : config. sandbox . build_log_max_size . to_bytes ( ) ,
37+ max_lines : config. sandbox . build_log_max_lines ,
2838 }
2939 }
3040
@@ -33,8 +43,12 @@ impl LogStorage {
3343 LogStorage {
3444 inner : Arc :: new ( Mutex :: new ( InnerStorage {
3545 records : inner. records . clone ( ) ,
46+ truncated : inner. truncated ,
47+ size : inner. size ,
3648 } ) ) ,
3749 min_level : self . min_level ,
50+ max_size : self . max_size ,
51+ max_lines : self . max_lines ,
3852 }
3953 }
4054}
@@ -49,9 +63,30 @@ impl Log for LogStorage {
4963 return ;
5064 }
5165 let mut inner = self . inner . lock ( ) . unwrap ( ) ;
66+ if inner. truncated {
67+ return ;
68+ }
69+ if inner. records . len ( ) >= self . max_lines {
70+ inner. records . push ( StoredRecord {
71+ level : Level :: Warn ,
72+ message : "too many lines in the log, truncating it" . into ( ) ,
73+ } ) ;
74+ inner. truncated = true ;
75+ return ;
76+ }
77+ let message = record. args ( ) . to_string ( ) ;
78+ if inner. size + message. len ( ) >= self . max_size {
79+ inner. records . push ( StoredRecord {
80+ level : Level :: Warn ,
81+ message : "too much data in the log, truncating it" . into ( ) ,
82+ } ) ;
83+ inner. truncated = true ;
84+ return ;
85+ }
86+ inner. size += message. len ( ) ;
5287 inner. records . push ( StoredRecord {
5388 level : record. level ( ) ,
54- message : record . args ( ) . to_string ( ) ,
89+ message,
5590 } ) ;
5691 }
5792
@@ -67,3 +102,89 @@ impl fmt::Display for LogStorage {
67102 Ok ( ( ) )
68103 }
69104}
105+
106+ #[ cfg( test) ]
107+ mod tests {
108+ use super :: { LogStorage , StoredRecord } ;
109+ use crate :: config:: Config ;
110+ use crate :: logs;
111+ use crate :: prelude:: * ;
112+ use crate :: utils:: size:: Size ;
113+ use log:: { Level , LevelFilter } ;
114+
115+ #[ test]
116+ fn test_log_storage ( ) {
117+ logs:: init_test ( ) ;
118+ let config = Config :: default ( ) ;
119+
120+ let storage = LogStorage :: new ( LevelFilter :: Info , & config) ;
121+ logs:: capture ( & storage, || {
122+ info ! ( "an info record" ) ;
123+ warn ! ( "a warn record" ) ;
124+ trace ! ( "a trace record" ) ;
125+ } ) ;
126+
127+ assert_eq ! (
128+ storage. inner. lock( ) . unwrap( ) . records,
129+ vec![
130+ StoredRecord {
131+ level: Level :: Info ,
132+ message: "an info record" . to_string( ) ,
133+ } ,
134+ StoredRecord {
135+ level: Level :: Warn ,
136+ message: "a warn record" . to_string( ) ,
137+ } ,
138+ ]
139+ ) ;
140+ }
141+
142+ #[ test]
143+ fn test_too_much_content ( ) {
144+ logs:: init_test ( ) ;
145+
146+ let mut config = Config :: default ( ) ;
147+ config. sandbox . build_log_max_size = Size :: Kilobytes ( 4 ) ;
148+
149+ let storage = LogStorage :: new ( LevelFilter :: Info , & config) ;
150+ logs:: capture ( & storage, || {
151+ let content = ( 0 ..Size :: Kilobytes ( 8 ) . to_bytes ( ) )
152+ . map ( |_| '.' )
153+ . collect :: < String > ( ) ;
154+ info ! ( "{}" , content) ;
155+ } ) ;
156+
157+ let inner = storage. inner . lock ( ) . unwrap ( ) ;
158+ assert_eq ! ( inner. records. len( ) , 1 ) ;
159+ assert ! ( inner
160+ . records
161+ . last( )
162+ . unwrap( )
163+ . message
164+ . contains( "too much data" ) ) ;
165+ }
166+
167+ #[ test]
168+ fn test_too_many_lines ( ) {
169+ logs:: init_test ( ) ;
170+
171+ let mut config = Config :: default ( ) ;
172+ config. sandbox . build_log_max_lines = 100 ;
173+
174+ let storage = LogStorage :: new ( LevelFilter :: Info , & config) ;
175+ logs:: capture ( & storage, || {
176+ for _ in 0 ..200 {
177+ info ! ( "a line" ) ;
178+ }
179+ } ) ;
180+
181+ let inner = storage. inner . lock ( ) . unwrap ( ) ;
182+ assert_eq ! ( inner. records. len( ) , 101 ) ;
183+ assert ! ( inner
184+ . records
185+ . last( )
186+ . unwrap( )
187+ . message
188+ . contains( "too many lines" ) ) ;
189+ }
190+ }
0 commit comments