4 // Copyright (C) 2004-2005 Novell, Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 using System
.Collections
;
31 using System
.Diagnostics
;
33 namespace Beagle
.Util
{
35 public enum LogLevel
{
42 static public class Log
{
44 static private string log_directory
;
45 static private string log_name_prefix
;
46 static private string program_identifier
;
47 static private string program_identifier_truncated
;
49 // If we don't call Log.Initialize, these defaults ensure that
50 // everything will just get spewed to the console.
51 static private bool running_in_foreground
= true;
52 static private LogLevel cutoff_level
= LogLevel
.Debug
;
54 static private TextWriter log_writer
;
55 static private TextWriter exception_writer
;
56 static private TextWriter foreground_echo_writer
;
58 static public void Initialize (string log_directory
,
59 string program_identifier
,
60 LogLevel cutoff_level
,
61 bool running_in_foreground
)
63 Log
.log_directory
= log_directory
;
64 Log
.program_identifier
= program_identifier
;
65 Log
.cutoff_level
= cutoff_level
;
66 Log
.running_in_foreground
= running_in_foreground
;
70 log_name_prefix
= String
.Format ("{0:yyyy-MM-dd-HH-mm-ss}-", DateTime
.Now
);
71 program_identifier_truncated
= program_identifier
.Substring (0, 6);
73 log_writer
= NewLogWriter (program_identifier
);
74 exception_writer
= NewDelayedLogWriter (program_identifier
+ "Exceptions");
76 TextWriter console_log_writer
;
77 console_log_writer
= NewDelayedLogWriter (program_identifier
+ "Console");
79 TextWriter console_redirect_writer
;
80 if (running_in_foreground
) {
81 foreground_echo_writer
= Console
.Out
;
82 console_redirect_writer
= new TeeTextWriter (Console
.Out
, console_log_writer
);
84 console_redirect_writer
= console_log_writer
;
87 Console
.SetOut (console_redirect_writer
);
88 Console
.SetError (console_redirect_writer
);
90 // If we are running in the background, redirect stdin to /dev/null
91 if (! running_in_foreground
) {
92 FileStream dev_null_stream
= new FileStream ("/dev/null",
96 TextReader dev_null_reader
= new StreamReader (dev_null_stream
);
97 Console
.SetIn (dev_null_reader
);
101 static public void Disable ()
103 cutoff_level
= LogLevel
.Ignored
;
106 static private void PruneOldLogs ()
108 DateTime magic_date
= DateTime
.Now
.AddDays (-7);
109 DirectoryInfo dir
= new DirectoryInfo (log_directory
);
112 current_str
= "current-" + program_identifier
;
114 foreach (FileInfo file
in dir
.GetFiles ()) {
116 // Clean up old symlinks
117 if (file
.Name
.StartsWith (current_str
) && FileSystem
.IsSymLink (file
.FullName
)) {
122 int last_dash
= file
.Name
.LastIndexOf ("-");
124 continue; // skip strange-looking files
126 string date
= file
.Name
.Substring (0, last_dash
);
130 log_date
= DateTime
.ParseExact (date
, "yyyy-MM-dd-HH-mm-ss", null);
131 if (log_date
< magic_date
)
133 } catch (Exception e
) { }
137 static private TextWriter
NewLogWriter (string name
)
140 log_path
= Path
.Combine (log_directory
, log_name_prefix
+ name
);
143 stream
= new FileStream (log_path
,
146 FileShare
.ReadWrite
);
149 writer
= new StreamWriter (stream
);
150 writer
.AutoFlush
= true;
153 log_link
= Path
.Combine (log_directory
, "current-" + name
);
154 Mono
.Unix
.Native
.Syscall
.symlink (log_path
, log_link
);
159 private class DelayedClosure
{
163 public DelayedClosure (string name
)
168 public TextWriter
Build ()
170 return NewLogWriter (name
);
174 static private TextWriter
NewDelayedLogWriter (string name
)
176 DelayedClosure closure
;
177 closure
= new DelayedClosure (name
);
178 return new DelayedTextWriter (new DelayedTextWriter
.Builder (closure
.Build
));
181 /////////////////////////////////////////////////////////////////////////////////////////
183 static object write_lock
= new object ();
185 static private void WriteLine (LogLevel level
, string format
, object [] args
, Exception ex
)
187 if (cutoff_level
< level
)
190 string ex_str
= null;
192 ex_str
= ex
.ToString ();
194 // This only happens if Log.Initialize was never called.
195 if (running_in_foreground
&& foreground_echo_writer
== null)
196 foreground_echo_writer
= Console
.Out
;
198 if (foreground_echo_writer
!= null) {
199 foreground_echo_writer
.Write (level
);
200 foreground_echo_writer
.Write (": ");
202 foreground_echo_writer
.WriteLine (format
, args
);
204 foreground_echo_writer
.WriteLine (ex_str
);
205 foreground_echo_writer
.Flush ();
208 if (log_writer
== null) // i.e. if Log.Initialize has not been called
211 StringBuilder prefix_builder
;
212 prefix_builder
= new StringBuilder ();
213 prefix_builder
.Append ('\n'); // start w/ a newline
214 prefix_builder
.AppendFormat ("{0:yyMMdd HHmmssffff} {1:00000} ",
215 DateTime
.Now
, Process
.GetCurrentProcess ().Id
);
216 prefix_builder
.Append (program_identifier_truncated
);
218 prefix_builder
.Append (' ');
221 prefix_builder
.Append ("ERROR");
224 prefix_builder
.Append (" WARN");
227 prefix_builder
.Append ("DEBUG");
230 prefix_builder
.Append (" HUH?");
235 prefix_builder
.Append (" EX");
236 prefix_builder
.Append (": ");
239 prefix
= prefix_builder
.ToString ();
241 StringBuilder message
;
242 message
= new StringBuilder ();
243 message
.Append (prefix
);
244 message
.Remove (0, 1); // remove leading \n
246 message
.AppendFormat (format
, args
);
247 if (ex_str
!= null) {
249 message
.Append ('\n');
250 message
.Append (ex_str
);
252 message
.Replace ("\n", prefix
);
255 message_str
= message
.ToString ();
258 log_writer
.WriteLine (message_str
);
259 if (ex
!= null && exception_writer
!= null)
260 exception_writer
.WriteLine (message_str
);
264 /////////////////////////////////////////////////////////////////////////////////////////
266 static public void Debug (string message
, params object [] args
)
268 WriteLine (LogLevel
.Debug
, message
, args
, null);
271 static public void Debug (Exception ex
, string message
, params object [] args
)
273 WriteLine (LogLevel
.Debug
, message
, args
, ex
);
276 static public void Debug (Exception ex
)
278 WriteLine (LogLevel
.Debug
, null, null, ex
);
281 static public void Info (string message
, params object [] args
)
283 // The Info log level is deprecated: just map it to Debug.
284 Debug (message
, args
);
287 static public void Info (Exception ex
, string message
, params object [] args
)
289 Debug (ex
, message
, args
);
292 static public void Info (Exception ex
)
297 static public void Warn (string message
, params object [] args
)
299 WriteLine (LogLevel
.Warn
, message
, args
, null);
302 static public void Warn (Exception ex
, string message
, params object [] args
)
304 WriteLine (LogLevel
.Warn
, message
, args
, ex
);
307 static public void Warn (Exception ex
)
309 WriteLine (LogLevel
.Warn
, null, null, ex
);
312 static public void Error (string message
, params object [] args
)
314 WriteLine (LogLevel
.Error
, message
, args
, null);
317 static public void Error (Exception ex
, string message
, params object [] args
)
319 WriteLine (LogLevel
.Error
, message
, args
, ex
);
322 static public void Error (Exception ex
)
324 WriteLine (LogLevel
.Error
, null, null, ex
);