1f029d52cf377299f45b995c27cc8efc55ff4161
[master-thesis.git] / Parasitemia / Logger / Logger.fs
1 namespace Logger
2
3 open System
4 open System.Text
5 open System.IO
6 open System.Diagnostics
7 open System.Threading
8 open System.Collections.Generic
9
10 type Severity = DEBUG = 1 | USER = 2 | WARNING = 3 | ERROR = 4 | FATAL = 5
11
12 type IListener = abstract NewEntry : Severity -> string -> unit
13
14 [<Sealed>]
15 type Log () =
16 let maxSizeFile = 10L * 1024L * 1024L // [byte] (10 MB).
17 let nbEntriesCheckSize = 100; // Each 100 entries added we check the size of the log file to test if it is greater than 'MAX_SIZE_FILE'.
18 let LogDefaultDirectory = "Parasitemia\\Log"
19 let filenameFormat = "{0:D4}.log"
20 let encoding = Encoding.GetEncoding("UTF-8")
21
22 let moduleName = System.Diagnostics.StackFrame(1).GetMethod().Module.Name
23
24 let mutable stream: StreamWriter = null
25
26 let mutable logDir: string = null
27 let mutable absoluteDir: string = null
28
29 let mutable nbEntries = 0L
30
31 let monitor = Object()
32
33 let listeners = List<IListener>()
34
35 let debug =
36 #if DEBUG
37 true
38 #else
39 false
40 #endif
41
42 static let instance = new Log()
43
44 let setLogDirectory (dir: string) =
45 lock monitor (fun () ->
46 logDir <- dir
47 absoluteDir <- Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), logDir)
48
49 if stream <> null
50 then
51 stream.Close()
52 stream <- null
53
54 try
55 if not <| Directory.Exists(absoluteDir)
56 then
57 Directory.CreateDirectory(absoluteDir) |> ignore
58 with
59 | _ as ex -> Console.Error.WriteLine("Unable to create the log directory: {0}", absoluteDir))
60
61 let openLogFile () =
62 try
63 if stream = null || (nbEntries % (int64 nbEntriesCheckSize) = 0L) && stream.BaseStream.Length > maxSizeFile
64 then
65 if stream <> null
66 then
67 stream.Close()
68
69 let mutable n = 1
70 for existingFile in Directory.GetFiles(absoluteDir) do
71 let current_n = ref 0
72 if Int32.TryParse(existingFile.Remove(existingFile.LastIndexOf('.')), current_n) && !current_n > n
73 then
74 n <- !current_n
75
76 let mutable filename = Path.Combine(absoluteDir, String.Format(filenameFormat, n))
77 try
78 if (FileInfo(filename).Length > maxSizeFile)
79 then
80 filename <- Path.Combine(absoluteDir, String.Format(filenameFormat, n + 1))
81 with
82 | :? FileNotFoundException -> () // The file may not exist.
83
84 stream <- new StreamWriter(filename, true, encoding)
85 with
86 | _ as ex -> Console.Error.WriteLine("Can't open the file log: {0}", ex)
87
88 do
89 setLogDirectory LogDefaultDirectory
90
91 interface IDisposable with
92 member this.Dispose () =
93 if stream <> null
94 then
95 stream.Dispose()
96
97 member private this.Write (message: string, severity: Severity) =
98 lock monitor (fun () ->
99 nbEntries <- nbEntries + 1L
100 openLogFile ()
101
102 if stream <> null
103 then
104 let mutable moduleNameCaller = moduleName
105 match StackTrace().GetFrames() |> Array.tryPick (fun frame -> let name = frame.GetMethod().Module.Name
106 if name <> moduleName then Some name else None) with
107 | Some name -> moduleNameCaller <- name
108 | _ -> ()
109
110 let threadName = Thread.CurrentThread.Name
111
112 for listener in listeners do
113 listener.NewEntry severity message
114
115 try
116 stream.WriteLine(
117 "{0:yyyy-MM-dd HH:mm:ss.fff} [{1}] {{{2}}} ({3}) : {4}",
118 TimeZone.CurrentTimeZone.ToLocalTime(DateTime.UtcNow),
119 severity.ToString(),
120 moduleNameCaller,
121 (if String.IsNullOrEmpty(threadName) then Thread.CurrentThread.ManagedThreadId.ToString() else String.Format("{0}-{1}", threadName, Thread.CurrentThread.ManagedThreadId)),
122 message
123 )
124 stream.Flush()
125 with
126 | :? IOException as ex -> Console.Error.WriteLine("Unable to write to the log file: {0}", ex))
127
128
129 member private this.AddListener (listener: IListener) =
130 lock monitor (fun () ->
131 if not <| listeners.Contains(listener)
132 then
133 listeners.Add(listener))
134
135 member private this.RmListener (listener: IListener) =
136 lock monitor (fun () ->
137 listeners.Remove(listener) |> ignore)
138
139 static member AddListener (listener: IListener) = instance.AddListener(listener)
140 static member RmListener (listener: IListener) = instance.RmListener(listener)
141
142 static member LogWithTime (message: string, severity: Severity, f: unit -> 'a option, [<ParamArray>] args: Object[]) : 'a option =
143 let sw = Stopwatch()
144 sw.Start()
145 let res = f ()
146 sw.Stop()
147 if res.IsSome
148 then
149 instance.Write(String.Format(message, args) + sprintf " (time: %d ms)" sw.ElapsedMilliseconds, severity)
150 res
151
152 static member Debug (message: string, [<ParamArray>] args: Object[]) =
153 #if DEBUG
154 instance.Write(String.Format(message, args), Severity.DEBUG)
155 #else
156 ()
157 #endif
158
159 static member User (message: string, [<ParamArray>] args: Object[]) =
160 instance.Write(String.Format(message, args), Severity.USER)
161
162 static member Warning (message: string, [<ParamArray>] args: Object[]) =
163 instance.Write(String.Format(message, args), Severity.WARNING)
164
165 static member Error (message: string, [<ParamArray>] args: Object[]) =
166 instance.Write(String.Format(message, args), Severity.ERROR)
167
168 static member Fatal (message: string, [<ParamArray>] args: Object[]) =
169 instance.Write(String.Format(message, args), Severity.FATAL)
170