--- /dev/null
+namespace Logger
+
+open System
+open System.Text
+open System.IO
+open System.Diagnostics
+open System.Threading
+open System.Collections.Generic
+
+type Severity = DEBUG = 1 | USER = 2 | WARNING = 3 | ERROR = 4 | FATAL = 5
+
+type IListener = abstract NewEntry : Severity -> string -> unit
+
+[<Sealed>]
+type Log () =
+ let maxSizeFile = 10L * 1024L * 1024L // [byte] (10 MB).
+ 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'.
+ let LogDefaultDirectory = "Parasitemia\\Log"
+ let filenameFormat = "{0:D4}.log"
+ let encoding = Encoding.GetEncoding("UTF-8")
+
+ let moduleName = System.Diagnostics.StackFrame(1).GetMethod().Module.Name
+
+ let mutable stream: StreamWriter = null
+
+ let mutable logDir: string = null
+ let mutable absoluteDir: string = null
+
+ let mutable nbEntries = 0L
+
+ let monitor = Object()
+
+ let listeners = List<IListener>()
+
+ let debug =
+#if DEBUG
+ true
+#else
+ false
+#endif
+
+ static let instance = new Log()
+
+ let setLogDirectory (dir: string) =
+ lock monitor (fun () ->
+ logDir <- dir
+ absoluteDir <- Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), logDir)
+
+ if stream <> null
+ then
+ stream.Close()
+ stream <- null
+
+ try
+ if not <| Directory.Exists(absoluteDir)
+ then
+ Directory.CreateDirectory(absoluteDir) |> ignore
+ with
+ | _ as ex -> Console.Error.WriteLine("Unable to create the log directory: {0}", absoluteDir))
+
+ let openLogFile () =
+ try
+ if stream = null || (nbEntries % (int64 nbEntriesCheckSize) = 0L) && stream.BaseStream.Length > maxSizeFile
+ then
+ if stream <> null
+ then
+ stream.Close()
+
+ let mutable n = 1
+ for existingFile in Directory.GetFiles(absoluteDir) do
+ let current_n = ref 0
+ if Int32.TryParse(existingFile.Remove(existingFile.LastIndexOf('.')), current_n) && !current_n > n
+ then
+ n <- !current_n
+
+ let mutable filename = Path.Combine(absoluteDir, String.Format(filenameFormat, n))
+ try
+ if (FileInfo(filename).Length > maxSizeFile)
+ then
+ filename <- Path.Combine(absoluteDir, String.Format(filenameFormat, n + 1))
+ with
+ | :? FileNotFoundException -> () // The file may not exist.
+
+ stream <- new StreamWriter(filename, true, encoding)
+ with
+ | _ as ex -> Console.Error.WriteLine("Can't open the file log: {0}", ex)
+
+ do
+ setLogDirectory LogDefaultDirectory
+
+ interface IDisposable with
+ member this.Dispose () =
+ if stream <> null
+ then
+ stream.Dispose()
+
+ member private this.Write (message: string, severity: Severity) =
+ lock monitor (fun () ->
+ nbEntries <- nbEntries + 1L
+ openLogFile ()
+
+ if stream <> null
+ then
+ let mutable moduleNameCaller = moduleName
+ match StackTrace().GetFrames() |> Array.tryPick (fun frame -> let name = frame.GetMethod().Module.Name
+ if name <> moduleName then Some name else None) with
+ | Some name -> moduleNameCaller <- name
+ | _ -> ()
+
+ let threadName = Thread.CurrentThread.Name
+
+ for listener in listeners do
+ listener.NewEntry severity message
+
+ try
+ stream.WriteLine(
+ "{0:yyyy-MM-dd HH:mm:ss.fff} [{1}] {{{2}}} ({3}) : {4}",
+ TimeZone.CurrentTimeZone.ToLocalTime(DateTime.UtcNow),
+ severity.ToString(),
+ moduleNameCaller,
+ (if String.IsNullOrEmpty(threadName) then Thread.CurrentThread.ManagedThreadId.ToString() else String.Format("{0}-{1}", threadName, Thread.CurrentThread.ManagedThreadId)),
+ message
+ )
+ stream.Flush()
+ with
+ | :? IOException as ex -> Console.Error.WriteLine("Unable to write to the log file: {0}", ex))
+
+
+ member private this.AddListener (listener: IListener) =
+ lock monitor (fun () ->
+ if not <| listeners.Contains(listener)
+ then
+ listeners.Add(listener))
+
+ member private this.RmListener (listener: IListener) =
+ lock monitor (fun () ->
+ listeners.Remove(listener) |> ignore)
+
+ static member AddListener (listener: IListener) = instance.AddListener(listener)
+ static member RmListener (listener: IListener) = instance.RmListener(listener)
+
+ static member LogWithTime (message: string, severity: Severity, f: unit -> 'a, [<ParamArray>] args: Object[]) : 'a =
+ let sw = Stopwatch()
+ sw.Start()
+ let res = f ()
+ sw.Stop()
+ instance.Write(String.Format(message, args) + sprintf " (time: %d ms)" sw.ElapsedMilliseconds, severity)
+ res
+
+ static member Debug (message: string, [<ParamArray>] args: Object[]) =
+#if DEBUG
+ instance.Write(String.Format(message, args), Severity.DEBUG)
+#else
+ ()
+#endif
+
+ static member User (message: string, [<ParamArray>] args: Object[]) =
+ instance.Write(String.Format(message, args), Severity.USER)
+
+ static member Warning (message: string, [<ParamArray>] args: Object[]) =
+ instance.Write(String.Format(message, args), Severity.WARNING)
+
+ static member Error (message: string, [<ParamArray>] args: Object[]) =
+ instance.Write(String.Format(message, args), Severity.ERROR)
+
+ static member Fatal (message: string, [<ParamArray>] args: Object[]) =
+ instance.Write(String.Format(message, args), Severity.FATAL)
+