Created
March 13, 2012 03:07
-
-
Save ppanyukov/2026328 to your computer and use it in GitHub Desktop.
Asynchronous log writer implemented using F# agent
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module LogAgent = | |
// Log writer implementation using F# asynchronous agents | |
// Written as an excersise | |
open System | |
open System.IO | |
type private Message = | |
| Debug of string | |
| Info of string | |
| Warn of string | |
| Error of string | |
| Fatal of string | |
with | |
static member toString logMessage = | |
match logMessage with | |
| Debug m -> m |> sprintf "DEBUG:%s" | |
| Info m -> m |> sprintf "INFO:%s" | |
| Warn m -> m |> sprintf "WARN:%s" | |
| Error m -> m |> sprintf "ERROR:%s" | |
| Fatal m -> m |> sprintf "FATAL:%s" | |
override this.ToString() = | |
Message.toString this | |
type private LogCommand = | |
| Log of Message | |
| Flush | |
| Close of AsyncReplyChannel<unit> | |
type LogAgent(logFile:string) as this = | |
let writer = lazy(File.AppendText logFile) | |
let agent = MailboxProcessor.Start (fun agent -> | |
// Do the loop until the Stop command is received | |
// Keep the number of lines written to the log | |
let rec loop(count) = async { | |
let! command = agent.Receive() | |
match command with | |
| Log message -> | |
let count = count + 1 | |
let message = Message.toString message | |
writer.Value.WriteLine message | |
return! loop(count) | |
| Flush -> | |
if writer.IsValueCreated then | |
writer.Value.Flush() | |
return! loop(count) | |
| Close reply -> | |
let message = sprintf "%d messages written into log" count | |
Console.WriteLine message | |
this.doClose() | |
reply.Reply(ignore()) | |
return ignore() | |
} | |
loop(0)) | |
interface IDisposable with | |
member this.Dispose() = this.doClose() | |
member private this.doClose() = | |
let message = sprintf "Discarding %d messages in the queue" (agent.CurrentQueueLength) | |
Console.WriteLine(message) | |
let d = agent :> IDisposable | |
d.Dispose() | |
if writer.IsValueCreated then | |
writer.Value.Dispose() | |
member private this.log objToMessage obj = | |
obj |> objToMessage |> LogCommand.Log |> agent.Post | |
member this.fatal = this.log Fatal; | |
member this.error = this.log Error | |
member this.warn = this.log Warn | |
member this.info = this.log Info | |
member this.debug = this.log Debug | |
member this.queueLength = agent.CurrentQueueLength | |
member this.flush() = LogCommand.Flush |> agent.Post | |
member this.close() = LogCommand.Close |> agent.PostAndReply | |
module Demo = | |
open LogAgent | |
let usage() = | |
// Create log. | |
// Can bind with "use" if we want to dispose of it automatically. | |
// Otherwise can bind with let and call "close" manually. | |
use log = new LogAgent(@"c:\temp\foo.log") | |
// pump lots of log messages asynchronously | |
let pump = async { | |
[0..20000] | |
|> Seq.iter (fun i -> sprintf "Log #%d" i |> log.info) | |
} | |
Async.Start pump | |
// wait for a bit for the pump to push through some messages and then close | |
System.Threading.Thread.Sleep(100) | |
log.close() | |
// Sleep a bit and see that the pump is still going, but messages | |
// are not written anywhere. No error reported. Is this the behaviour | |
// we want? | |
printfn "Sleeping for 2 seconds" | |
System.Threading.Thread.Sleep(2000) | |
[<EntryPoint>] | |
let main(_) = | |
usage() | |
0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment