Add new example: logging with the log and log/slog packages

This commit is contained in:
Eli Bendersky 2023-08-22 13:45:00 -07:00
parent 146bd9cce2
commit 15d8fe75b8
8 changed files with 379 additions and 3 deletions

View File

@ -71,6 +71,7 @@ Command-Line Arguments
Command-Line Flags
Command-Line Subcommands
Environment Variables
Logging
HTTP Client
HTTP Server
Context

View File

@ -0,0 +1,77 @@
// The Go standard library provides straightforward
// tools for outputting logs from Go programs, with
// the [log](https://pkg.go.dev/log) package for
// free-form output and the
// [log/slog](https://pkg.go.dev/log/slog) package for
// structured output.
package main
import (
"bytes"
"fmt"
"log"
"os"
"log/slog"
)
func main() {
// Simply invoking functions like `Println` from the
// `log` package uses the _standard_ logger, which
// is already pre-configured for reasonable logging
// output to `os.Stderr`. Additional methods like
// `Fatal*` or `Panic*` will exit the program after
// logging.
log.Println("standard logger")
// Loggers can be configured with _flags_ to set
// their output format. By default, the standard
// logger has the `log.Ldate` and `log.Ltime` flags
// set, and these are collected in `log.LstdFlags`.
// We can change its flags to emit time with
// microsecond accuracy, for example.
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
log.Println("with micro")
// It also supports emitting the file name and
// line from which the `log` function is called.
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.Println("with file/line")
// It may be useful to create a custom logger and
// pass it around. When creating a new logger, we
// can set a _prefix_ to distinguish its output
// from other loggers.
mylog := log.New(os.Stdout, "my:", log.LstdFlags)
mylog.Println("from mylog")
// We can set the prefix
// on existing loggers (including the standard one)
// with the `SetPrefix` method.
mylog.SetPrefix("ohmy:")
mylog.Println("from mylog")
// Loggers can have custom output targets;
// any `io.Writer` works.
var buf bytes.Buffer
buflog := log.New(&buf, "buf:", log.LstdFlags)
// This call writes the log output into `buf`.
buflog.Println("hello")
// This will actually show it on standard output.
fmt.Print("from buflog:", buf.String())
// The `slog` package provides
// _structured_ log output. For example, logging
// in JSON format is straightforward.
jsonHandler := slog.NewJSONHandler(os.Stderr, nil)
myslog := slog.New(jsonHandler)
myslog.Info("hi there")
// In addition to the message, `slog` output can
// contain an arbitrary number of key=value
// pairs.
myslog.Info("hello again", "key", "val", "age", 25)
}

View File

@ -0,0 +1,2 @@
38a7ef451859bb4c163df938b3a9d0e5ac293bef
Qd0uCqBlYUn

View File

@ -0,0 +1,18 @@
# Sample output; the date and time
# emitted will depend on when the example ran.
$ go run logging.go
2023/08/22 10:45:16 standard logger
2023/08/22 10:45:16.904141 with micro
2023/08/22 10:45:16 logging.go:40: with file/line
my:2023/08/22 10:45:16 from mylog
ohmy:2023/08/22 10:45:16 from mylog
from buflog:buf:2023/08/22 10:45:16 hello
# These are wrapped for clarity of presentation
# on the website; in reality they are emitted
# on a single line.
{"time":"2023-08-22T10:45:16.904166391-07:00",
"level":"INFO","msg":"hi there"}
{"time":"2023-08-22T10:45:16.904178985-07:00",
"level":"INFO","msg":"hello again",
"key":"val","age":25}

View File

@ -14,7 +14,7 @@
if (e.key == "ArrowRight") {
window.location.href = 'http-client';
window.location.href = 'logging';
}
}
@ -162,7 +162,7 @@ program picks that value up.</p>
<p class="next">
Next example: <a href="http-client">HTTP Client</a>.
Next example: <a href="logging">Logging</a>.
</p>

2
public/http-client generated
View File

@ -9,7 +9,7 @@
onkeydown = (e) => {
if (e.key == "ArrowLeft") {
window.location.href = 'environment-variables';
window.location.href = 'logging';
}

2
public/index.html generated
View File

@ -173,6 +173,8 @@
<li><a href="environment-variables">Environment Variables</a></li>
<li><a href="logging">Logging</a></li>
<li><a href="http-client">HTTP Client</a></li>
<li><a href="http-server">HTTP Server</a></li>

276
public/logging generated Normal file
View File

@ -0,0 +1,276 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Go by Example: Logging</title>
<link rel=stylesheet href="site.css">
</head>
<script>
onkeydown = (e) => {
if (e.key == "ArrowLeft") {
window.location.href = 'environment-variables';
}
if (e.key == "ArrowRight") {
window.location.href = 'http-client';
}
}
</script>
<body>
<div class="example" id="logging">
<h2><a href="./">Go by Example</a>: Logging</h2>
<table>
<tr>
<td class="docs">
<p>The Go standard library provides straightforward
tools for outputting logs from Go programs, with
the <a href="https://pkg.go.dev/log">log</a> package for
free-form output and the
<a href="https://pkg.go.dev/log/slog">log/slog</a> package for
structured output.</p>
</td>
<td class="code leading">
<a href="https://go.dev/play/p/Qd0uCqBlYUn"><img title="Run code" src="play.png" class="run" /></a><img title="Copy code" src="clipboard.png" class="copy" />
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;bytes&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;log&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="s">&#34;log/slog&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>Simply invoking functions like <code>Println</code> from the
<code>log</code> package uses the <em>standard</em> logger, which
is already pre-configured for reasonable logging
output to <code>os.Stderr</code>. Additional methods like
<code>Fatal*</code> or <code>Panic*</code> will exit the program after
logging.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;standard logger&#34;</span><span class="p">)</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>Loggers can be configured with <em>flags</em> to set
their output format. By default, the standard
logger has the <code>log.Ldate</code> and <code>log.Ltime</code> flags
set, and these are collected in <code>log.LstdFlags</code>.
We can change its flags to emit time with
microsecond accuracy, for example.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">SetFlags</span><span class="p">(</span><span class="nx">log</span><span class="p">.</span><span class="nx">LstdFlags</span> <span class="p">|</span> <span class="nx">log</span><span class="p">.</span><span class="nx">Lmicroseconds</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;with micro&#34;</span><span class="p">)</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>It also supports emitting the file name and
line from which the <code>log</code> function is called.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">SetFlags</span><span class="p">(</span><span class="nx">log</span><span class="p">.</span><span class="nx">LstdFlags</span> <span class="p">|</span> <span class="nx">log</span><span class="p">.</span><span class="nx">Lshortfile</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;with file/line&#34;</span><span class="p">)</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>It may be useful to create a custom logger and
pass it around. When creating a new logger, we
can set a <em>prefix</em> to distinguish its output
from other loggers.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">mylog</span> <span class="o">:=</span> <span class="nx">log</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="s">&#34;my:&#34;</span><span class="p">,</span> <span class="nx">log</span><span class="p">.</span><span class="nx">LstdFlags</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">mylog</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;from mylog&#34;</span><span class="p">)</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>We can set the prefix
on existing loggers (including the standard one)
with the <code>SetPrefix</code> method.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">mylog</span><span class="p">.</span><span class="nf">SetPrefix</span><span class="p">(</span><span class="s">&#34;ohmy:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">mylog</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;from mylog&#34;</span><span class="p">)</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>Loggers can have custom output targets;
any <code>io.Writer</code> works.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">buf</span> <span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span>
</span></span><span class="line"><span class="cl"> <span class="nx">buflog</span> <span class="o">:=</span> <span class="nx">log</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">buf</span><span class="p">,</span> <span class="s">&#34;buf:&#34;</span><span class="p">,</span> <span class="nx">log</span><span class="p">.</span><span class="nx">LstdFlags</span><span class="p">)</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>This call writes the log output into <code>buf</code>.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">buflog</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">)</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>This will actually show it on standard output.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="s">&#34;from buflog:&#34;</span><span class="p">,</span> <span class="nx">buf</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>The <code>slog</code> package provides
<em>structured</em> log output. For example, logging
in JSON format is straightforward.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">jsonHandler</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">NewJSONHandler</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">myslog</span> <span class="o">:=</span> <span class="nx">slog</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">jsonHandler</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">myslog</span><span class="p">.</span><span class="nf">Info</span><span class="p">(</span><span class="s">&#34;hi there&#34;</span><span class="p">)</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>In addition to the message, <code>slog</code> output can
contain an arbitrary number of key=value
pairs.</p>
</td>
<td class="code">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">myslog</span><span class="p">.</span><span class="nf">Info</span><span class="p">(</span><span class="s">&#34;hello again&#34;</span><span class="p">,</span> <span class="s">&#34;key&#34;</span><span class="p">,</span> <span class="s">&#34;val&#34;</span><span class="p">,</span> <span class="s">&#34;age&#34;</span><span class="p">,</span> <span class="mi">25</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
</td>
</tr>
</table>
<table>
<tr>
<td class="docs">
<p>Sample output; the date and time
emitted will depend on when the example ran.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"><span class="gp">$</span> go run logging.go
</span></span><span class="line"><span class="cl"><span class="go">2023/08/22 10:45:16 standard logger
</span></span></span><span class="line"><span class="cl"><span class="go">2023/08/22 10:45:16.904141 with micro
</span></span></span><span class="line"><span class="cl"><span class="go">2023/08/22 10:45:16 logging.go:40: with file/line
</span></span></span><span class="line"><span class="cl"><span class="go">my:2023/08/22 10:45:16 from mylog
</span></span></span><span class="line"><span class="cl"><span class="go">ohmy:2023/08/22 10:45:16 from mylog
</span></span></span><span class="line"><span class="cl"><span class="go">from buflog:buf:2023/08/22 10:45:16 hello</span></span></span></code></pre>
</td>
</tr>
<tr>
<td class="docs">
<p>These are wrapped for clarity of presentation
on the website; in reality they are emitted
on a single line.</p>
</td>
<td class="code">
<pre class="chroma"><code><span class="line"><span class="cl"><span class="go">{&#34;time&#34;:&#34;2023-08-22T10:45:16.904166391-07:00&#34;,
</span></span></span><span class="line"><span class="cl"><span class="go"> &#34;level&#34;:&#34;INFO&#34;,&#34;msg&#34;:&#34;hi there&#34;}
</span></span></span><span class="line"><span class="cl"><span class="go">{&#34;time&#34;:&#34;2023-08-22T10:45:16.904178985-07:00&#34;,
</span></span></span><span class="line"><span class="cl"><span class="go"> &#34;level&#34;:&#34;INFO&#34;,&#34;msg&#34;:&#34;hello again&#34;,
</span></span></span><span class="line"><span class="cl"><span class="go"> &#34;key&#34;:&#34;val&#34;,&#34;age&#34;:25}</span></span></span></code></pre>
</td>
</tr>
</table>
<p class="next">
Next example: <a href="http-client">HTTP Client</a>.
</p>
<p class="footer">
by <a href="https://markmcgranaghan.com">Mark McGranaghan</a> and <a href="https://eli.thegreenplace.net">Eli Bendersky</a> | <a href="https://github.com/mmcgrana/gobyexample">source</a> | <a href="https://github.com/mmcgrana/gobyexample#license">license</a>
</p>
</div>
<script>
var codeLines = [];
codeLines.push('package main\u000A');codeLines.push('import (\u000A \"bytes\"\u000A \"fmt\"\u000A \"log\"\u000A \"os\"\u000A');codeLines.push(' \"log/slog\"\u000A)\u000A');codeLines.push('func main() {\u000A');codeLines.push(' log.Println(\"standard logger\")\u000A');codeLines.push(' log.SetFlags(log.LstdFlags | log.Lmicroseconds)\u000A log.Println(\"with micro\")\u000A');codeLines.push(' log.SetFlags(log.LstdFlags | log.Lshortfile)\u000A log.Println(\"with file/line\")\u000A');codeLines.push(' mylog :\u003D log.New(os.Stdout, \"my:\", log.LstdFlags)\u000A mylog.Println(\"from mylog\")\u000A');codeLines.push(' mylog.SetPrefix(\"ohmy:\")\u000A mylog.Println(\"from mylog\")\u000A');codeLines.push(' var buf bytes.Buffer\u000A buflog :\u003D log.New(\u0026buf, \"buf:\", log.LstdFlags)\u000A');codeLines.push(' buflog.Println(\"hello\")\u000A');codeLines.push(' fmt.Print(\"from buflog:\", buf.String())\u000A');codeLines.push(' jsonHandler :\u003D slog.NewJSONHandler(os.Stderr, nil)\u000A myslog :\u003D slog.New(jsonHandler)\u000A myslog.Info(\"hi there\")\u000A');codeLines.push(' myslog.Info(\"hello again\", \"key\", \"val\", \"age\", 25)\u000A}\u000A');codeLines.push('');codeLines.push('');
</script>
<script src="site.js" async></script>
</body>
</html>