Console output
go's fmt package. more explicit than console.log but more powerful. three families — print, sprintf, fprintf — each for a different job.
go uses the fmt package for output. no console.log — you pick the right tool for the job.
the three families
// Print family — writes to stdout
fmt.Print("hello") // no newline, no spaces between args
fmt.Println("hello") // adds newline + spaces between args automatically
fmt.Printf("port: %d\n", 8080) // formatted, no auto newline
// Sprintf — returns a formatted string (doesn't print)
msg := fmt.Sprintf("user %s logged in", "alice")
// Fprintf — writes to any writer (stderr, files, http responses)
fmt.Fprintf(os.Stderr, "error: %s\n", "db failed")
fmt.Fprintf(os.Stdout, "server started on port %d\n", 8080)print vs println vs printf
fmt.Print("hello", "world") // helloworld — no space, no newline
fmt.Println("hello", "world") // hello world\n — space + newline
fmt.Printf("hello world") // hello world — no newline, supports verbswhen to use what:
Println— everyday logging, quick debug printsPrintf— structured/formatted output, log linesPrint— rarely, when you're manually controlling spacingSprintf— building strings before using them elsewhereFprintf— writing to specific streams (stderr, files, http)
format verbs
method := "POST"
path := "/api/users"
status := 201
dur := 12.45
auth := true
fmt.Printf("[REQUEST] %s %s | status: %d | duration: %.2fms | auth: %t\n",
method, path, status, dur, auth)
// [REQUEST] POST /api/users | status: 201 | duration: 12.45ms | auth: true| verb | type | example |
|---|---|---|
%s | string | "alice" |
%d | integer | 42 |
%f / %.2f | float | 3.14 |
%t | bool | true |
%v | any (default) | {alice 30} |
%+v | struct with field names | {Name:alice Age:30} |
%#v | go syntax repr | main.User{Name:"alice"} |
%T | type of variable | main.User |
%% | literal % sign | 50% |
sprintf — build strings, don't print
useful when you need to construct a string for logging, error messages, or returning from a function.
func formatLog(level, message string) string {
return fmt.Sprintf("[%s] %s", level, message)
}
fmt.Println(formatLog("ERROR", "db connection failed"))
// [ERROR] db connection failedfprintf — write to specific streams
like console.log vs console.error in js — same terminal, different streams. docker, systemd, log aggregators treat them differently.
import "os"
fmt.Fprintf(os.Stdout, "server started\n") // like console.log
fmt.Fprintf(os.Stderr, "fatal: %s\n", "db failed") // like console.errordebug verbs for structs
type User struct {
Name string
Email string
Age int
}
u := User{"alice", "alice@example.com", 30}
fmt.Printf("%v\n", u) // {alice alice@example.com 30}
fmt.Printf("%+v\n", u) // {Name:alice Email:alice@example.com Age:30}
fmt.Printf("%#v\n", u) // main.User{Name:"alice", Email:"alice@example.com", Age:30}
fmt.Printf("%T\n", u) // main.Usergotchas
// wrong verb = runtime formatting error, NOT compile error
fmt.Printf("%d", "hello") // prints %!d(string=hello) — code keeps running
// silent bug in logs, hard to catch
// literal % must be escaped
fmt.Printf("success rate: 50%%\n") // prints: success rate: 50%
fmt.Printf("success rate: 50%\n") // prints: success rate: 50%!(NOVERB)
// Print doesn't support format verbs
fmt.Print("%d", 42) // prints: %d42 — not what you want
fmt.Printf("%d", 42) // prints: 42Arithmetic operators
mostly the same as js but go is strict about types. integer division truncates, no implicit coercion, and ++ is a statement not an expression.
Data types & variables
go is statically typed. types are checked at compile time, not runtime. no undefined, no null surprises, no implicit coercion.