This library implements printf-style logging functions for any Microsoft.Extensions.Logging.ILogger, allowing you to log in an F# style with the full power of structured logging.
Here's an example, adding logging to the snippet from https://learn.microsoft.com/en-us/dotnet/fsharp/tutorials/async#combine-asynchronous-computations:
// Put this in a project and reference these packages: FSharp.Logf, Microsoft.Extensions.Logging, Microsoft.Extensions.Logging.Console
open System
open System.IO
open Microsoft.Extensions.Logging
open FSharp.Logf
// Type annotation would be inferred if omitted (included here for clarity)
let printTotalFileBytes (ml: ILogger) path =
async {
try
let! bytes = File.ReadAllBytesAsync(path) |> Async.AwaitTask
let fileName = Path.GetFileName(path)
// Log at information level, with fileName and bytesLength as the parameter names for any logging sinks
// supporting structured logging
logfi ml "File %s{fileName} has %d{bytesLength} bytes" fileName bytes.Length
with e ->
// Log at error level, setting an exception
elogfe ml e "Exception accessing file: '%s{path}'" path
}
[<EntryPoint>]
let main argv =
// Create a Microsoft-provided logger. Choose your favorite Logger provider (for example: Serilog, NLog, log4net)
let logger = LoggerFactory.Create(fun builder -> builder.AddConsole().SetMinimumLevel(LogLevel.Debug) |> ignore).CreateLogger()
// Log at debug level. Since the NewLine argument doesn't have a parameter name right after it, it will be baked
// directly into the string. The argv argument, however, will be parameterized like the others.
logfd logger "ARGV:%s%s{argv}" Environment.NewLine ("[|" + (argv |> String.concat ";") + "|]")
argv
|> Seq.map (printTotalFileBytes logger)
|> Async.Parallel
|> Async.Ignore
|> Async.RunSynchronously
0
This library is Fable-compatible. You can take advantage of this like so:
#if !FABLE_COMPILER
open Microsoft.Extensions.Logging
open FSharp.Logf
#else
open Fable.Microsoft.Extensions.Logging
open Fable.FSharp.Logf
#endif
let ml =
#if !FABLE_COMPILER
LoggerFactory.Create(fun builder -> builder.AddConsole().SetMinimumLevel(LogLevel.Debug) |> ignore)
#else
ConsoleLogger()
#endif
logfi ml "Hello, %s{arg}!" "world"
namespace System
namespace System.IO
namespace Microsoft
namespace Microsoft.FSharp
val printTotalFileBytes : ml:'a -> path:string -> Async<unit>
val ml : 'a
val path : string
val async : AsyncBuilder
<summary>Builds an asynchronous workflow using computation expression syntax.</summary>
val bytes : byte []
type File =
static member AppendAllLines : path: string * contents: IEnumerable<string> -> unit + 1 overload
static member AppendAllLinesAsync : path: string * contents: IEnumerable<string> * encoding: Encoding *?cancellationToken: CancellationToken -> Task + 1 overload
static member AppendAllText : path: string * contents: string -> unit + 1 overload
static member AppendAllTextAsync : path: string * contents: string * encoding: Encoding *?cancellationToken: CancellationToken -> Task + 1 overload
static member AppendText : path: string -> StreamWriter
static member Copy : sourceFileName: string * destFileName: string -> unit + 1 overload
static member Create : path: string -> FileStream + 2 overloads
static member CreateSymbolicLink : path: string * pathToTarget: string -> FileSystemInfo
static member CreateText : path: string -> StreamWriter
static member Decrypt : path: string -> unit
...
<summary>Provides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of <see cref="T:System.IO.FileStream" /> objects.</summary>
File.ReadAllBytesAsync(path: string,?cancellationToken: Threading.CancellationToken) : Threading.Tasks.Task<byte []>
Multiple items
type Async =
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task<'T> -> Async<'T> + 1 overload
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member Choice : computations:seq<Async<'T option>> -> Async<'T option>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T> + 3 overloads
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
...
<summary>Holds static members for creating and manipulating asynchronous computations.</summary>
<remarks>
See also <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows">F# Language Guide - Async Workflows</a>.
</remarks>
<category index="1">Async Programming</category>
--------------------
type Async<'T> =
<summary>
An asynchronous computation, which, when run, will eventually produce a value of type T, or else raises an exception.
</summary>
<remarks>
This type has no members. Asynchronous computations are normally specified either by using an async expression
or the static methods in the <see cref="T:Microsoft.FSharp.Control.Async" /> type.
See also <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows">F# Language Guide - Async Workflows</a>.
</remarks>
<namespacedoc><summary>
Library functionality for asynchronous programming, events and agents. See also
<a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows">Asynchronous Programming</a>,
<a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/members/events">Events</a> and
<a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/lazy-expressions">Lazy Expressions</a> in the
F# Language Guide.
</summary></namespacedoc>
<category index="1">Async Programming</category>
static member Async.AwaitTask : task:Threading.Tasks.Task -> Async<unit>
static member Async.AwaitTask : task:Threading.Tasks.Task<'T> -> Async<'T>
val fileName : string
type Path =
static member ChangeExtension : path: string * extension: string -> string
static member Combine : path1: string * path2: string -> string + 3 overloads
static member EndsInDirectorySeparator : path: ReadOnlySpan<char> -> bool + 1 overload
static member GetDirectoryName : path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload
static member GetExtension : path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload
static member GetFileName : path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload
static member GetFileNameWithoutExtension : path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload
static member GetFullPath : path: string -> string + 1 overload
static member GetInvalidFileNameChars : unit -> char []
static member GetInvalidPathChars : unit -> char []
...
<summary>Performs operations on <see cref="T:System.String" /> instances that contain file or directory path information. These operations are performed in a cross-platform manner.</summary>
Path.GetFileName(path: string) : string
Path.GetFileName(path: ReadOnlySpan<char>) : ReadOnlySpan<char>
property Array.Length: int with get
<summary>Gets the total number of elements in all the dimensions of the <see cref="T:System.Array" />.</summary>
<exception cref="T:System.OverflowException">The array is multidimensional and contains more than <see cref="F:System.Int32.MaxValue" /> elements.</exception>
<returns>The total number of elements in all the dimensions of the <see cref="T:System.Array" />; zero if there are no elements in the array.</returns>
val e : exn
Multiple items
type EntryPointAttribute =
inherit Attribute
new : unit -> EntryPointAttribute
<summary>Adding this attribute to a function indicates it is the entrypoint for an application.
If this attribute is not specified for an EXE then the initialization implicit in the
module bindings in the last file in the compilation sequence are used as the entrypoint.</summary>
<category>Attributes</category>
--------------------
new : unit -> EntryPointAttribute
val main : argv:string [] -> int
val argv : string []
val logger : obj
val ignore : value:'T -> unit
<summary>Ignore the passed value. This is often used to throw away results of a computation.</summary>
<param name="value">The value to ignore.</param>
type Environment =
static member Exit : exitCode: int -> unit
static member ExpandEnvironmentVariables : name: string -> string
static member FailFast : message: string -> unit + 1 overload
static member GetCommandLineArgs : unit -> string []
static member GetEnvironmentVariable : variable: string -> string + 1 overload
static member GetEnvironmentVariables : unit -> IDictionary + 1 overload
static member GetFolderPath : folder: SpecialFolder -> string + 1 overload
static member GetLogicalDrives : unit -> string []
static member SetEnvironmentVariable : variable: string * value: string -> unit + 1 overload
static member CommandLine : string
...
<summary>Provides information about, and means to manipulate, the current environment and platform. This class cannot be inherited.</summary>
property Environment.NewLine: string with get
<summary>Gets the newline string defined for this environment.</summary>
<returns><see langword="\r\n" /> for non-Unix platforms, or <see langword="\n" /> for Unix platforms.</returns>
Multiple items
type String =
interface IEnumerable<char>
interface IEnumerable
interface ICloneable
interface IComparable
interface IComparable<string>
interface IConvertible
interface IEquatable<string>
new : value: nativeptr<char> -> unit + 8 overloads
member Clone : unit -> obj
member CompareTo : value: obj -> int + 1 overload
...
<summary>Represents text as a sequence of UTF-16 code units.</summary>
--------------------
String(value: nativeptr<char>) : String
String(value: char []) : String
String(value: ReadOnlySpan<char>) : String
String(value: nativeptr<sbyte>) : String
String(c: char, count: int) : String
String(value: nativeptr<char>, startIndex: int, length: int) : String
String(value: char [], startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String
val concat : sep:string -> strings:seq<string> -> string
<summary>Returns a new string made by concatenating the given strings
with separator <c>sep</c>, that is <c>a1 + sep + ... + sep + aN</c>.</summary>
<param name="sep">The separator string to be inserted between the strings
of the input sequence.</param>
<param name="strings">The sequence of strings to be concatenated.</param>
<returns>A new string consisting of the concatenated strings separated by
the separation string.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when <c>strings</c> is null.</exception>
module Seq
from Microsoft.FSharp.Collections
<summary>Contains operations for working with values of type <see cref="T:Microsoft.FSharp.Collections.seq`1" />.</summary>
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
<summary>Builds a new collection whose elements are the results of applying the given function
to each of the elements of the collection. The given function will be applied
as elements are demanded using the <c>MoveNext</c> method on enumerators retrieved from the
object.</summary>
<remarks>The returned sequence may be passed between threads safely. However,
individual IEnumerator values generated from the returned sequence should not be accessed concurrently.</remarks>
<param name="mapping">A function to transform items from the input sequence.</param>
<param name="source">The input sequence.</param>
<returns>The result sequence.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
static member Async.Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member Async.Parallel : computations:seq<Async<'T>> * ?maxDegreeOfParallelism:int -> Async<'T []>
static member Async.Ignore : computation:Async<'T> -> Async<unit>
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:Threading.CancellationToken -> 'T
val ml : obj