Skip to content

Commit

Permalink
merge: logging framework placeholder
Browse files Browse the repository at this point in the history
  • Loading branch information
gvwilson committed Apr 21, 2024
1 parent 6b00502 commit 5744717
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ that most programmers have never encountered.
| Stuart Hinson | stuarth | redis data store | redis |
| Nathaniel Knight | nathanielknight | random number generation | prng |
| Monica McGuigan | monmcguigan | JSON codec | json |
| Noel Rivas | noelrivasc | logging framework | logging |
| Abhinav Sarkar | abhin4v | pretty printing | pretty |
| Fabian Schmalzried | FabHof | binary data packing | binary |
| Isaac Van Doren | isaacvando | compression | compress |
Expand Down
4 changes: 4 additions & 0 deletions _data/contrib.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
personal: "Monica"
github: "monmcguigan"

- family: "Rivas"
personal: "Noel"
github: "noelrivasc"

- family: "Sarkar"
personal: "Abhinav"
github: "abhin4v"
Expand Down
1 change: 1 addition & 0 deletions _data/order.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ chapters:
- diff
- json
- pretty
- logger
- prng
- binary
- ci
Expand Down
5 changes: 5 additions & 0 deletions _data/topic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
- slug: "license"
title: "License"

- slug: "logger"
title: "A Logging Framework"
github:
- "noelrivasc"

- slug: "match"
title: "Pattern Matching"
github:
Expand Down
1 change: 1 addition & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ <h1>Software Design by Example in Roc</h1>
<li><a href="./diff/">File Diffing</a></li>
<li><a href="./json/">JSON Encoding and Decoding</a></li>
<li><a href="./pretty/">Pretty Printing</a></li>
<li><a href="./logger/">A Logging Framework</a></li>
<li><a href="./prng/">Pseudorandom Number Generator</a></li>
<li><a href="./binary/">Binary Data Packing</a></li>
<li><a href="./ci/">Continuous Integration</a></li>
Expand Down
130 changes: 130 additions & 0 deletions docs/logger/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Software Design by Example in Roc</title>
<link rel="shortcut icon" type="image/x-icon" href="/book-of-examples/favicon.svg">
<link rel="stylesheet" href="/book-of-examples/bw.css" type="text/css">
<link rel="stylesheet" href="/book-of-examples/site.css" type="text/css">

</head>
<body>
<main>
<h1>A Logging Framework</h1>
<p class="author">Written by <a href="https://github.com/noelrivasc">Noel Rivas</a>
</p>

<p>This chapter goes through the building of a simple logging library with the following capabilities:</p>

<ul>
<li>Log level filtering</li>
<li>Multiple channels (stderr, append to file)</li>
<li>JSON encoding</li>
<li>Ability to use multiple logging configurations in the same program</li>
<li>Logger configuration through environment vars</li>
</ul>

<h2 id="chapter-structure">Chapter structure</h2>

<p>The chapter follows a <em>challenge and response</em> pattern. The first iteration builds the simplest logger imaginable (just write to stdout) and each iteration improves on the previous one. After each implementation, its downsides are commented, and the next set of improvements are outlined.</p>

<p>After the final implementation, a recap of the iterations, and the concepts that were explained in each of them, is offered.</p>

<p><em>NOTE: On each of the iterations below, I’ll list some concepts that could be explained using the iteration code.</em></p>

<p><em>The list is offered as a suggestion: whether we actually flesh out those concepts in this chapter or not will depend on the editorial decisions on chapter ordering and interdependence.</em></p>

<h3 id="0-minimum-viable-logger">0. Minimum viable logger</h3>

<p>An extremely simple, single-function “logger” that takes simple arguments (A message and a value) and outputs to Stdout.</p>

<p>This iteration is only pertinent if the chapter introduces the concept of platform. It may be overkill in terms of paring down the first iteration.</p>

<pre><code class="language-roc">main =
log "This is a value:" 42

log = \msg, val -&gt;
Stdout.line "$(msg): $(Inspect.toStr val)"
</code></pre>

<p><em>Concepts</em>:</p>

<ul>
<li>Capabilities defined by platform</li>
<li>Importing from platform</li>
</ul>

<h3 id="1-append-to-file">1. Append to file</h3>

<p>At the end of this iteration, the logger:</p>

<ul>
<li>Is composed by just a couple of functions in the main file (no library)</li>
<li>Appends to a file</li>
<li>Includes timestamps</li>
<li>Has a hardcoded output path</li>
<li>Needs the log file to be created manually</li>
</ul>

<p>The idea behind its simplicity is to have the least amount of moving parts possible, so we can explore tasks, error handling, and the syntax around them in detail, without the burden of a larger program.</p>

<p>I would like to present the code for this iteration with / without syntax sugar, and take steps to guide the reader to make the connection between type annotations in the documentation, the function calls without syntactic sugar, and the code with sugar.</p>

<p><em>Concepts</em>:</p>

<ul>
<li>Task</li>
<li>Error handling</li>
<li>Using Inspect</li>
<li>Backpassing syntax</li>
<li>Pipe syntax (first pass, simplest case, <code class="language-plaintext highlighter-rouge">f |&gt; a</code>)</li>
<li>Reading type annotations</li>
</ul>

<h3 id="2-a-usable-library">2. A usable library</h3>

<p>This iteration is a small step from the previous one in terms of complexity of the logger. It mostly expands on the previous one as a reinforcement.</p>

<p>An important step is that the logger becomes a module, and a more realistic use case is presented.</p>

<p>At the end of this iteration, the logger:</p>

<ul>
<li>Becomes a module</li>
<li>Handles log file creation</li>
<li>Handles permission errors</li>
<li>Takes a configuration record as argument (which makes it possible to have multiple channels)</li>
<li>Reads configuration overrides from environment variables</li>
</ul>

<p><em>Concepts</em>:</p>

<ul>
<li>Modules</li>
<li>Types (configuration record)</li>
</ul>

<h3 id="3-log-level">3. Log level</h3>

<p>This iteration adds JSON encoding and log level configuration.</p>

<p><em>NOTE</em>: JSON encoding would depend on <a href="https://github.com/lukewilliamboswell/roc-json">roc-json</a>. If the library is too much, we can do something like rudimentary CSV without escaping or headers: just join a list with commas.</p>

<p><em>Concepts</em>:</p>

<ul>
<li>Tags and payloads</li>
<li>Pattern matching</li>
</ul>

</main>
<footer>
Copyright © the contributors 2024
&middot;
<a href="https://github.com/roc-lang/book-of-examples">GitHub repository</a>
</footer>

</body>
</html>
11 changes: 11 additions & 0 deletions docs/logger/iterations/0-minimal-logger/main.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
app "log-minimal" packages {
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.8.1/x8URkvfyi9I0QhmVG98roKBUs_AZRkLFwFJVJ3942YA.tar.br"
}
imports [cli.Stdout]
provides [ main ] to cli

main =
log "This is a value:" 42

log = \msg, val ->
Stdout.line "$(msg): $(Inspect.toStr val)"
34 changes: 34 additions & 0 deletions docs/logger/iterations/1-append-to-file/main.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
app "log-append" packages {
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.8.1/x8URkvfyi9I0QhmVG98roKBUs_AZRkLFwFJVJ3942YA.tar.br"
}
imports [
cli.Stdout,
cli.File,
cli.Task,
cli.Path,
cli.Utc,
]
provides [ main ] to cli

log = \msg, val ->
path = Path.fromStr "logFile"
Task.await Utc.now \now ->
millis = Utc.toMillisSinceEpoch now
seconds = Num.round (Num.toFrac millis / Num.toFrac 1000)
time = Num.toStr seconds
appendToFile path "$(time) $(msg): $(Inspect.toStr val)\n"

# TODO: figure out type annotation, and its pertinence for this iteration
# appendToFile : Path, Str -> Task {} [FileReadErr Path.Path InternalFile.ReadErr, FileWriteErr Path.Path InternalFile.WriteErr]
appendToFile = \path, msg ->
newBytes = Str.toUtf8 msg
Task.await (File.readBytes path) \existingBytes ->
File.writeBytes path (List.concat newBytes existingBytes)

main =
Task.onErr
(log "This is a value:" 42)
handleErr

handleErr = \err ->
Stdout.line "We found an error: $(Inspect.toStr err)"
35 changes: 35 additions & 0 deletions docs/logger/iterations/1-append-to-file/main.sweet.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
app "log-append" packages {
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.8.1/x8URkvfyi9I0QhmVG98roKBUs_AZRkLFwFJVJ3942YA.tar.br"
}
imports [
cli.Stdout,
cli.File,
cli.Task,
cli.Path,
cli.Utc,
]
provides [ main ] to cli

log = \msg, val ->
path = Path.fromStr "logFile"
now <- Utc.now |> Task.await
millis = Utc.toMillisSinceEpoch now
seconds = Num.round (Num.toFrac millis / Num.toFrac 1000)
time = Num.toStr seconds
appendToFile path "$(time) $(msg): $(Inspect.toStr val)\n"

# TODO: figure out type annotation, and its pertinence for this iteration
# appendToFile : Path, Str -> Task {} [FileReadErr Path.Path InternalFile.ReadErr, FileWriteErr Path.Path InternalFile.WriteErr]
appendToFile = \path, msg ->
newBytes = Str.toUtf8 msg
existingBytes <- File.readBytes path |> Task.await
File.writeBytes path (List.concat newBytes existingBytes)

main =
log "This is a value:"
Task.onErr
(log "This is a value:" 42)
handleErr

handleErr = \err ->
Stdout.line "We found an error: $(Inspect.toStr err)"
9 changes: 6 additions & 3 deletions docs/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<loc>http://localhost:4000/book-of-examples/des/</loc>
</url>
<url>
<loc>http://localhost:4000/book-of-examples/logger/</loc>
</url>
<url>
<loc>http://localhost:4000/book-of-examples/intro/</loc>
</url>
<url>
Expand Down Expand Up @@ -37,10 +40,10 @@
<loc>http://localhost:4000/book-of-examples/diff/</loc>
</url>
<url>
<loc>http://localhost:4000/book-of-examples/parser/</loc>
<loc>http://localhost:4000/book-of-examples/svg/</loc>
</url>
<url>
<loc>http://localhost:4000/book-of-examples/svg/</loc>
<loc>http://localhost:4000/book-of-examples/prng/</loc>
</url>
<url>
<loc>http://localhost:4000/book-of-examples/json/</loc>
Expand Down Expand Up @@ -70,7 +73,7 @@
<loc>http://localhost:4000/book-of-examples/</loc>
</url>
<url>
<loc>http://localhost:4000/book-of-examples/prng/</loc>
<loc>http://localhost:4000/book-of-examples/parser/</loc>
</url>
<url>
<loc>http://localhost:4000/book-of-examples/ftp/</loc>
Expand Down
3 changes: 2 additions & 1 deletion logger/index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Logging Library
---
---

This chapter goes through the building of a simple logging library with the following capabilities:

Expand Down

0 comments on commit 5744717

Please sign in to comment.