Expert · Standard library essentials · topic 34 of 46 · 40 min
Files, time and structured logs
Explanation
Reading and writing files
data, err := os.ReadFile("menu.txt") // whole file as []byte
if err != nil {
if errors.Is(err, os.ErrNotExist) {
fmt.Println("no menu yet")
}
return err
}
fmt.Println(string(data))
err = os.WriteFile("report.txt", []byte("Total: 4200\n"), 0o644)f, err := os.Open("orders.csv")
if err != nil {
return err
}
defer f.Close()
sc := bufio.NewScanner(f)
line := 0
for sc.Scan() { // one line per loop, without the newline
line++
fmt.Println(line, sc.Text())
}
if err := sc.Err(); err != nil {
return err
}- os.Create(name) makes (or empties) a file for writing; wrap it in bufio.NewWriter for many small writes, and call Flush at the end.
- filepath.Join("data", "2026", "jan.csv") builds paths with the right separator for Windows or Linux.
- os.MkdirAll("data/2026", 0o755) creates folders, including missing parents.
- io.Copy(dst, src) streams from any Reader to any Writer: file to HTTP response, HTTP body to file.
- encoding/csv reads and writes CSV files correctly, including quoted commas.
- //go:embed (Go 1.16+) bakes files such as templates into your binary at build time.
Time and durations
start := time.Now()
// ... work ...
elapsed := time.Since(start) // a time.Duration
fmt.Println(elapsed.Milliseconds(), "ms")
d := 90 * time.Minute
fmt.Println(d) // 1h30m0s
tomorrow := time.Now().Add(24 * time.Hour)
deadline := time.Now().AddDate(0, 0, 7) // 7 days later
fmt.Println(tomorrow.Before(deadline)) // trueFormatting dates: the reference time
Go does not use YYYY-MM-DD codes. Instead you write how one special moment would look: Monday, January 2, 2006 at 3:04:05 PM, time zone MST (UTC-7). The numbers count up: month 1, day 2, hour 3 (15 in 24-hour form), minute 4, second 5, year 2006, zone -0700.
t := time.Date(2026, time.August, 15, 9, 30, 0, 0, time.UTC)
fmt.Println(t.Format("2006-01-02")) // 2026-08-15
fmt.Println(t.Format("02/01/2006 15:04")) // 15/08/2026 09:30 (Indian style date)
fmt.Println(t.Format("Mon, 02 Jan 2006")) // Sat, 15 Aug 2026
fmt.Println(t.Format(time.RFC3339)) // 2026-08-15T09:30:00Z
d, err := time.Parse("2006-01-02", "2026-01-26")
fmt.Println(d.Weekday(), err) // Monday <nil>Time zones
ist, err := time.LoadLocation("Asia/Kolkata")
if err != nil {
ist = time.FixedZone("IST", 5*60*60+30*60) // +05:30 fallback
}
fmt.Println(time.Now().In(ist).Format("15:04 MST"))Structured logging with log/slog
The old log package prints free text: log.Printf("payment %s done", id). That is hard to search across millions of lines. log/slog (Go 1.21+) writes structured logs: a message plus key-value pairs, as text or JSON, with levels (Debug, Info, Warn, Error). Log platforms (Loki, ELK, CloudWatch and others) can then filter by order_id or level.
slog.Info("payment captured", "order_id", "ORD-77", "paise", 49900)
// 2026/08/15 09:30:00 INFO payment captured order_id=ORD-77 paise=49900
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
slog.SetDefault(logger)
slog.Warn("bank slow", "bank", "BankA", "latency_ms", 2300)
// {"time":"...","level":"WARN","msg":"bank slow","bank":"BankA","latency_ms":2300}
reqLog := logger.With("request_id", "a1b2") // attach fields to every line
reqLog.Error("charge failed", "err", err)Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Files, time and structured logs · 1/7
Three everyday tools: files, time, and logs.
Prefer a video by a teacher? Find free videos on “golang read file bufio scanner time format layout slog structured logging” on YouTube ↗
In real life: A daily sales report from a CSV file
A kirana store's billing software exports each day's sales to a CSV file. A small Go program reads it line by line, totals the amount, stamps the report with the date in IST format, and logs a structured summary line that the shop owner's dashboard can pick up.
package main
import (
"bufio"
"fmt"
"log/slog"
"strconv"
"strings"
"time"
)
func main() {
csv := "item,paise\nrice,26500\ndal,16000\noil,18500\n"
sc := bufio.NewScanner(strings.NewReader(csv)) // a real program would use os.Open
total := 0
first := true
for sc.Scan() {
if first { // skip the header line
first = false
continue
}
parts := strings.Split(sc.Text(), ",")
p, err := strconv.Atoi(parts[1])
if err != nil {
slog.Warn("bad line", "line", sc.Text())
continue
}
total += p
}
ist := time.FixedZone("IST", 5*60*60+30*60)
day := time.Date(2026, 8, 15, 21, 0, 0, 0, ist)
fmt.Printf("Sales on %s: Rs %d.%02d\n", day.Format("02-01-2006"), total/100, total%100)
slog.Info("daily report", "date", day.Format("2006-01-02"), "total_paise", total)
}Practice (0/4 done)
Which layout formats 15 August 2026 as 2026-08-15?
What does fmt.Println(90 * time.Minute) print?
time.Sleep(3) is called. How long does it sleep?
Write a program that appends a line "2026-08-15 09:30 IST checked in" (using the current time in IST) to a file attendance.log, then reads the whole file back and prints how many lines it has. Log the result with slog.Info including the file name and line count.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Calling other APIs safely with the HTTP clientStandard library essentials · same module
- An HTTP server with net/httpStandard library essentials · same module
- Files and a tiny server in NodeJavaScript Programming (language in depth) · Performance and Node.js
- Logging: structured, safe and centralisedDevOps: Git, Docker, Kubernetes and CI/CD · Monitoring and logging
- Logging and monitoringBackend Development · Performance and reliability
- Dates and timesJavaScript Programming (language in depth) · Modules, errors, JSON and dates
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
How do you check whether os.ReadFile failed because the file does not exist?
Answer: errors.Is(err, os.ErrNotExist)
os.ErrNotExist (also fs.ErrNotExist) is the sentinel error.
Which reads a 2 GB log file without loading it all into memory?
Answer: bufio.Scanner over an opened file
Scanner reads in chunks, one line at a time.
What is log/slog for?
Answer: Structured logs with levels and key-value pairs
slog arrived in Go 1.21 for structured logging.
Which of these should never appear in logs?
Answer: A user's OTP or password
Secrets in logs are a serious security leak.
Why use a stream for a 5 GB log file?Related
Answer: It processes chunks without loading the whole file into memory
Streams keep memory use small.
A payment retry succeeded on the second attempt. Which level fits best?Related
Answer: WARN
Something odd happened but the system coped, which is what WARN is for.
Why add a requestId to every log line?Related
Answer: To find every line about one request quickly
Searching one request id shows the full story of that request across all log lines.
Two Date objects for the same moment: a === b is…Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Standard library essentials”. The module exam comes next.
- Module 7 exam: Standard library essentials · 14 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Building a REST API with a database. 46 topics left in the course before the final exam.