You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Adds interpolated string POC
* Add example and README docs
* Added anonymous record test
* Added doccomment to setMessageInterpolated and formatted code
* Adds more info about interpolated strings to README
* Update README
Copy file name to clipboardExpand all lines: README.md
+86-40Lines changed: 86 additions & 40 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,9 +1,16 @@
1
1
# FsLibLog
2
2
3
-
FsLibLog is a single file you can copy paste or add through [Paket Github dependencies](https://fsprojects.github.io/Paket/github-dependencies.html) to provide your F# library with a logging abstraction. This is a port of the C# [LibLog](https://github.com/damianh/LibLog).
3
+
## What is this?
4
4
5
+
FsLibLog is a single file you can copy paste or add through [Paket Github dependencies](https://fsprojects.github.io/Paket/github-dependencies.html) to provide your F# library with a logging abstraction. This is a port of the C# [LibLog](https://github.com/damianh/LibLog).
5
6
6
-
## Getting started
7
+
## Why does this exist?
8
+
9
+
When creating a library for .NET, you typically do not want to depend on a logging framework or abstraction. Depending on a logging framework forces your consumers to use that framework, which is not ideal. Depending on an abstraction _can_ work but you can run into the [diamond dependency](https://docs.microsoft.com/en-us/dotnet/standard/library-guidance/dependencies#diamond-dependencies) problem. Since this is just a file you compile into your library, no dependency is taken and is transparent to your consumers.
10
+
11
+
Additionally, loggers aren't particularly friendly for F#, this sets out to resolve that.
12
+
13
+
## How to get started
7
14
8
15
### 1. Put the file into your project
9
16
@@ -17,17 +24,16 @@ Read over [Paket Github dependencies](https://fsprojects.github.io/Paket/github-
17
24
18
25
Add the following line to your `paket.depedencies` file.
-`getCurrentLogger` - __Deprecated__ because inferring the correct StackFrame is too difficult. Creates a logger. It's name is based on the current StackFrame.
63
+
-`getCurrentLogger` - __Deprecated__ because inferring the correct StackFrame is too difficult. Creates a logger. It's name is based on the current StackFrame.
59
64
-`getLoggerByFunc` - Creates a logger based on `Reflection.MethodBase.GetCurrentMethod` call. This is only useful for calls within functions.
60
65
-`getLoggerByQuotation` - Creates a logger given a Quotations.Expr type. This is only useful for module level declarations.
61
66
-`getLoggerFor` - Creates a logger given a `'a` type.
62
67
-`getLoggerByType` - Creates a logger given a `Type`.
63
68
-`getLoggerByName` - Creates a logger given a `string`.
64
69
65
-
66
70
### Set the loglevel, message, exception and parameters
67
71
68
72
Choose a LogLevel. (Fatal|Error|Warn|Info|Debug|Trace).
@@ -81,16 +85,15 @@ logger.warn(
81
85
The set of functions to augment the `Log` record are
82
86
83
87
-`Log.setMessage` - Amends a `Log` with a message
88
+
-`Log.setMessageIntepolated` - Ammends `Log` with a message. Using the syntax of `{variableName:loggerName}` it will automatically convert an intermpolated string into a proper message template.
84
89
-`Log.setMessageThunk` - Amends a `Log` with a message thunk. Useful for "expensive" string construction scenarios.
85
90
-`Log.addParameter` - Amends a `Log` with a parameter.
86
91
-`Log.addParameters` - Amends a `Log` with a list of parameters.
87
92
-`Log.addContext` - Amends a `Log` 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.
88
93
-`Log.addContextDestructured` - Amends a `Log` 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 calling `ToString()` on it. WARNING: Destructring can be expensive
89
94
-`Log.addException` - Amends a `Log` with an exception.
90
95
91
-
92
-
93
-
### Full Example:
96
+
### Full Example
94
97
95
98
```fsharp
96
99
namespace SomeLib
@@ -162,6 +165,17 @@ module Say =
162
165
>> Log.addException e
163
166
)
164
167
168
+
// Example Log Output:
169
+
// 2021-09-15T20:34:14.9060810-04:00 [Information] <SomeLib.Say> () The user {"Name": "Ensign Kim", "$type": "AdditionalData"} has requested a reservation date of "2021-09-16T00:34:14.8853360+00:00"
170
+
let interpolated (person : AdditionalData) (reservationDate : DateTimeOffset) =
171
+
// Starts the log out as an Info log
172
+
logger.info(
173
+
// Generates a message template via a specific string intepolation syntax.
174
+
// Add the name of the property after the expression
175
+
// for example: "person" will be logged as "user" and "reservationDate" as "reservationDate"
176
+
Log.setMessageI $"The user {person:User} has requested a reservation date of {reservationDate:ReservationDate} "
177
+
)
178
+
165
179
// Has the same logging output as `hello`, above, but uses the Operators module.
166
180
let helloWithOperators name =
167
181
// Initiate a log with a message
@@ -178,95 +192,127 @@ module Say =
178
192
179
193
```
180
194
195
+
## Log Providers
181
196
182
-
## Currently supported providers
197
+
Providers are the actual logging framework that sends the logs to some destination (console, file, logging service). FsLibLog uses reflection to inspect the running application and wire these up telling FsLibLog to do it.
198
+
199
+
### Currently supported provider
183
200
184
201
-[Serilog](https://github.com/serilog/serilog)
185
202
203
+
### Custom Providers
204
+
205
+
You can implement and teach FsLibLog about your own custom provider if one is not listed. You have to do 2 things:
206
+
207
+
1. You have to implement the `ILogProvider` interface. [Example Implemenation](https://github.com/TheAngryByrd/FsLibLog/blob/master/examples/ConsoleExample/Program.fs#L5-L90)
208
+
2. You have to tell FsLibLog to use it. [Example calling FsLibLog.LogProvider.setLoggerProvider](https://github.com/TheAngryByrd/FsLibLog/blob/master/examples/ConsoleExample/Program.fs#L94)
209
+
a. One downside to this is you need to do this for every library your application consumes that uses FsLiblog.
210
+
186
211
---
187
212
213
+
## String Interpolation
214
+
215
+
This allows for [string interpolation](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/interpolated-strings) with a special syntax to be convertable to [Message Templates](https://messagetemplates.org/) used in underlying providers (such as Serilog).
216
+
217
+
Typically when one uses string interpolation it works as such:
218
+
219
+
```fsharp
220
+
let favoriteCartoon = "Captain Planet"
221
+
let dayItsOn = "Saturday"
222
+
printfn $"My favorite cartoon is {favoriteCartoon} and airs on {dayItsOn}"
223
+
```
224
+
225
+
F# compiler will create a [FormattableString](https://docs.microsoft.com/en-us/dotnet/api/system.formattablestring?view=net-5.0) where it's `Format` property looks like `My favorite cartoon is {0} and airs on {1}` and the `GetArguments()` are `[| "Captain Planet"; "Saturday" |]`. As you can see, `FormattableString` doesn't have the named parameters that `Message Templates` would want. To make this work the way we want we need to introduce a specific syntax.
226
+
227
+
```fsharp
228
+
let favoriteCartoon = "Captain Planet"
229
+
let dayItsOn = "Saturday"
230
+
printfn $"My favorite cartoon is {favoriteCartoon:CartoonShow} and airs on {dayItsOn:DayAired}"
231
+
```
232
+
233
+
`setMessageInterpolated` will make the template look like `My favorite cartoon is {CartoonShow} and airs on {DayAired}`. This will replace the number arguments with the names after the colon within the interpolated string. This makes a usable message template.
234
+
235
+
- Why aren't we just trying to get the name of the variable/value? Needing to specify dedicate names _is a good thing_ since refactoring your variable names can have drastic effects on your logging queries. Explicit naming separates these concerns.
Make sure the following **requirements** are installed in your system:
202
249
203
-
*[dotnet SDK](https://www.microsoft.com/net/download/core) 2.0 or higher
204
-
*[Mono](http://www.mono-project.com/) if you're on Linux or macOS.
250
+
-[dotnet SDK](https://www.microsoft.com/net/download/core) 2.0 or higher
251
+
-[Mono](http://www.mono-project.com/) if you're on Linux or macOS.
205
252
206
-
```
253
+
```bash
207
254
> build.cmd // on windows
208
255
$ ./build.sh // on unix
209
256
```
210
257
211
258
#### Environment Variables
212
259
213
-
*`CONFIGURATION` will set the [configuration](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-build?tabs=netcore2x#options) of the dotnet commands. If not set it will default to Release.
214
-
*`CONFIGURATION=Debug ./build.sh` will result in things like `dotnet build -c Debug`
215
-
*`GITHUB_TOKEN` will be used to upload release notes and nuget packages to github.
216
-
* Be sure to set this before releasing
260
+
-`CONFIGURATION` will set the [configuration](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-build?tabs=netcore2x#options) of the dotnet commands. If not set it will default to Release.
261
+
-`CONFIGURATION=Debug ./build.sh` will result in things like `dotnet build -c Debug`
262
+
-`GITHUB_TOKEN` will be used to upload release notes and nuget packages to github.
263
+
- Be sure to set this before releasing
217
264
218
265
### Watch Tests
219
266
220
267
The `WatchTests` target will use [dotnet-watch](https://github.com/aspnet/Docs/blob/master/aspnetcore/tutorials/dotnet-watch.md) to watch for changes in your lib or tests and re-run your tests on all `TargetFrameworks`
221
268
222
-
```
269
+
```bash
223
270
./build.sh WatchTests
224
271
```
225
272
226
273
### Releasing
227
-
*[Start a git repo with a remote](https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/)
228
274
229
-
```
275
+
-[Start a git repo with a remote](https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/)
*[Create a GitHub OAuth Token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/)
243
-
* You can then set the `GITHUB_TOKEN` to upload release notes and artifacts to github
244
-
* Otherwise it will fallback to username/password
290
+
-[Create a GitHub OAuth Token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/)
291
+
- You can then set the `GITHUB_TOKEN` to upload release notes and artifacts to github
292
+
- Otherwise it will fallback to username/password
245
293
294
+
- Then update the `RELEASE_NOTES.md` with a new version, date, and release notes [ReleaseNotesHelper](https://fsharp.github.io/FAKE/apidocs/fake-releasenoteshelper.html)
246
295
247
-
* Then update the `RELEASE_NOTES.md` with a new version, date, and release notes [ReleaseNotesHelper](https://fsharp.github.io/FAKE/apidocs/fake-releasenoteshelper.html)
248
-
249
-
```
296
+
```markdown
250
297
#### 0.2.0 - 2017-04-20
251
298
* FEATURE: Does cool stuff!
252
299
* BUGFIX: Fixes that silly oversight
253
300
```
254
301
255
-
* You can then use the `Release` target. This will:
256
-
* make a commit bumping the version: `Bump version to 0.2.0` and add the release notes to the commit
257
-
* publish the package to nuget
258
-
* push a git tag
302
+
- You can then use the `Release` target. This will:
303
+
- make a commit bumping the version: `Bump version to 0.2.0` and add the release notes to the commit
Copy file name to clipboardExpand all lines: examples/SomeLib/Library.fs
+12Lines changed: 12 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,7 @@
1
1
namespaceSomeLib
2
2
openFsLibLog
3
3
openFsLibLog.Types
4
+
openSystem
4
5
5
6
moduleSay =
6
7
// let logger = LogProvider.getLoggerByName "SomeLibrary.Say"
@@ -65,3 +66,14 @@ module Say =
65
66
// Adds an exception to the log
66
67
>> Log.addException e
67
68
)
69
+
70
+
// Example Log Output:
71
+
// 2021-09-15T20:34:14.9060810-04:00 [Information] <SomeLib.Say> () The user {"Name": "Ensign Kim", "$type": "AdditionalData"} has requested a reservation date of "2021-09-16T00:34:14.8853360+00:00"
0 commit comments