FsLibLog is a single file you can copy paste or add through Paket Github dependencies to provide your F# library with a logging abstraction. This is a port of the C# LibLog.
Copy/paste FsLibLog.fs into your library
Read over Paket Github dependencies.
Add the following line to your paket.depedencies
file.
github TheAngryByrd/FsLibLog src/FsLibLog/FsLibLog.fs
Then add the following line to projects with paket.references
file you want FsLibLog to be available to.
File: FsLibLog.fs
To alleviate potential naming conflicts, it's best to replace FsLibLog namespace with your own.
Here is an example with FAKE 5:
Target.create "Replace" <| fun _ ->
Shell.replaceInFiles
[ "FsLibLog", "MyLib.Logging" ]
(!! "paket-files/TheAngryByrd/FsLibLog/src/FsLibLog/FsLibLog.fs")
open FsLibLog
open FsLibLog.Types
There are currently three ways to get a logger.
getCurrentLogger
- Deprecated because inferring the correct StackFrame is too difficult. Creates a logger. It's name is based on the current StackFrame.getLoggerByFunc
- Creates a logger based onReflection.MethodBase.GetCurrentMethod
call. This is only useful for calls within functions.getLoggerByQuotation
- Creates a logger given a Quotations.Expr type. This is only useful for module level declarations.getLoggerFor
- Creates a logger given a'a
type.getLoggerByType
- Creates a logger given aType
.getLoggerByName
- Creates a logger given astring
.
Choose a LogLevel. (Fatal|Error|Warn|Info|Debug|Trace).
There are helper methods on the logger instance, such as logger.warn
.
These helper functions take a (Log -> Log)
which allows you to amend the log record easily with functions in the Log
module. You can use function composition to set the fields much easier.
logger.warn(
Log.setMessage "{name} Was said hello to"
>> Log.addParameter name
)
The set of functions to augment the Log
record are
Log.setMessage
- Amends aLog
with a messageLog.setMessageThunk
- Amends aLog
with a message thunk. Useful for "expensive" string construction scenarios.Log.addParameter
- Amends aLog
with a parameter.Log.addParameters
- Amends aLog
with a list of parameters.Log.addContext
- Amends aLog
with additional named parameters for context. This helper adds more context to a log. This DOES NOT affect the parameters set for a message template. This is the same calling OpenMappedContext right before logging.Log.addContextDestructured
- Amends aLog
with additional named parameters for context. This helper adds more context to a log. This DOES NOT affect the parameters set for a message template. This is the same calling OpenMappedContext right before logging. This destructures an object rather than callingToString()
on it. WARNING: Destructring can be expensiveLog.addException
- Amends aLog
with an exception.
namespace SomeLib
open FsLibLog
open FsLibLog.Types
module Say =
let logger = LogProvider.getCurrentLogger()
type AdditionalData = {
Name : string
}
// Example Log Output:
// 16:23 [Information] <SomeLib.Say> () "Captain" Was said hello to - {"UserContext": {"Name": "User123", "$type": "AdditionalData"}, "FunctionName": "hello"}
let hello name =
// Starts the log out as an Informational log
logger.info(
Log.setMessage "{name} Was said hello to"
// MessageTemplates require the order of parameters to be consistent with the tokens to replace
>> Log.addParameter name
// This adds additional context to the log, it is not part of the message template
// This is useful for things like MachineName, ProcessId, ThreadId, or anything that doesn't easily fit within a MessageTemplate
// This is the same as calling `LogProvider.openMappedContext` right before logging.
>> Log.addContext "FunctionName" "hello"
// This is the same as calling `LogProvider.openMappedContextDestucturable` right before logging.
>> Log.addContextDestructured "UserContext" {Name = "User123"}
)
sprintf "hello %s." name
// Example Log Output:
// 16:23 [Debug] <SomeLib.Say> () In nested - {"DestructureTrue": {"Name": "Additional", "$type": "AdditionalData"}, "DestructureFalse": "{Name = \"Additional\";}", "Value": "bar"}
// [Information] <SomeLib.Say> () "Commander" Was said hello to - {"UserContext": {"Name": "User123", "$type": "AdditionalData"}, "FunctionName": "hello", "DestructureTrue": {"Name": "Additional", "$type": "AdditionalData"}, "DestructureFalse": "{Name = \"Additional\";}", "Value": "bar"}
let nestedHello name =
// This sets additional context to any log within scope
// This is useful if you want to add this to all logs within this given scope
use x = LogProvider.openMappedContext "Value" "bar"
// This doesn't destructure the record and calls ToString on it
use x = LogProvider.openMappedContext "DestructureFalse" {Name = "Additional"}
// This does destructure the record, Destructuring can be expensive depending on how big the object is.
use x = LogProvider.openMappedContextDestucturable "DestructureTrue" {Name = "Additional"} true
logger.debug(
Log.setMessage "In nested"
)
// The log in `hello` should also have these additional contexts added
hello name
// Example Log Output:
// 16:23 [Error] <SomeLib.Say> () "DaiMon" was rejected. - {}
// System.Exception: Sorry DaiMon isnt valid
// at Microsoft.FSharp.Core.PrintfModule.PrintFormatToStringThenFail@1647.Invoke(String message)
// at SomeLib.Say.fail(String name) in /Users/jimmybyrd/Documents/GitHub/FsLibLog/examples/SomeLib/Library.fs:line 57
let fail name =
try
failwithf "Sorry %s isnt valid" name
with e ->
// Starts the log out as an Error log
logger.error(
Log.setMessage "{name} was rejected."
// MessageTemplates require the order of parameters to be consistent with the tokens to replace
>> Log.addParameter name
// Adds an exception to the log
>> Log.addException e
)
MacOS/Linux | Windows |
---|---|
Make sure the following requirements are installed in your system:
- dotnet SDK 2.0 or higher
- Mono if you're on Linux or macOS.
> build.cmd // on windows
$ ./build.sh // on unix
CONFIGURATION
will set the configuration of the dotnet commands. If not set it will default to Release.CONFIGURATION=Debug ./build.sh
will result in things likedotnet build -c Debug
GITHUB_TOKEN
will be used to upload release notes and nuget packages to github.- Be sure to set this before releasing
The WatchTests
target will use dotnet-watch to watch for changes in your lib or tests and re-run your tests on all TargetFrameworks
./build.sh WatchTests
git add .
git commit -m "Scaffold"
git remote add origin origin https://github.com/user/MyCoolNewLib.git
git push -u origin master
paket config add-token "https://www.nuget.org" 4003d786-cc37-4004-bfdf-c4f3e8ef9b3a
-
- You can then set the
GITHUB_TOKEN
to upload release notes and artifacts to github - Otherwise it will fallback to username/password
- You can then set the
-
Then update the
RELEASE_NOTES.md
with a new version, date, and release notes ReleaseNotesHelper
#### 0.2.0 - 2017-04-20
* FEATURE: Does cool stuff!
* BUGFIX: Fixes that silly oversight
- You can then use the
Release
target. This will:- make a commit bumping the version:
Bump version to 0.2.0
and add the release notes to the commit - publish the package to nuget
- push a git tag
- make a commit bumping the version:
./build.sh Release
To format code run the following target
./build.sh FormatCode
This uses Fantomas to do code formatting. Please report code formatting bugs to that repository.