charmbracelet/log: a colorful Go logger for terminals, not for log pipelines
A minimal, colorful Go logging library 🪵
At a glance
- What is it?
- charmbracelet/log is a small leveled logging library for Go that renders human readable output with Lip Gloss styling. It is a good fit for CLI tools and local development, and a poor fit for high-throughput services that need machine-parsed logs.
- Who is it for?
- Adopt charmbracelet/log when the primary reader of your logs is a person at a terminal: CLI tools, local development, or a small service where readable output matters more than parse cost. Do not adopt it as the only logging path for a high-volume service that ships logs to an aggregator, because every call renders a styled string before it reaches the writer.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 27 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What charmbracelet/log solves, and for whom
The Go standard library's log package writes plain lines. That is enough for a daemon, and awkward for anything a person watches while it runs. charmbracelet/log targets that second case. Its README describes it as "a minimal and colorful Go logging library" and contrasts it directly with standard log: the Charm logger adds "customizable colorful human readable logging with batteries included."
The intended audience is Go developers building command line tools and interactive programs, which is consistent with the rest of the Charm ecosystem (the related searches around this project mostly point at other Charm tools such as huh, glamour and fang). The library is not a log shipping system. It has no network transport, no sampling, no rotation and no batching. It writes to an io.Writer, and what you do with those bytes is your problem.
That boundary is the whole value proposition. If your program is a CLI that a human runs and reads, color and alignment help. If your program is a service whose logs are consumed by a query engine, color is noise that you then have to strip.
The mechanism: a global logger, options, and three formatters
The package ships a package-level logger with timestamps on and the level set to info. The README shows that log.Debug prints nothing by default while log.Info does, which means level filtering happens before formatting. Levels are DebugLevel, InfoLevel, WarnLevel, ErrorLevel and FatalLevel, and Fatal calls os.Exit(1).
Every logging call accepts optional key/value pairs, so structured data rides along with the message: log.Error("failed to bake cookies", "err", err). The message itself can be any type, and non-string values are rendered with quotes, as in the README's ingredients example where a slice prints as ingredients="[flour butter sugar chocolate]".
Output formatting is delegated to formatters. The README lists text, JSON and Logfmt, and the repository layout matches that with text.go, json.go and logfmt.go sitting next to formatter.go. Styling comes from Lip Gloss, which is a direct dependency in go.mod (charm.land/lipgloss/v2). Color capability detection is handled by github.com/charmbracelet/colorprofile, also a direct dependency. That means the color decision is made by the library based on the terminal, not by you.
Customization runs through log.NewWithOptions with a log.Options struct. The README example sets ReportCaller, ReportTimestamp, TimeFormat and Prefix, and shows logger setters as an alternative path. There is also context support (context.go), a slog handler, and an adapter for the standard log package (stdlog.go).
Installing charmbracelet/log and logging your first structured line
The README gives the module path as charm.land/log/v2 and the install command as go get with the @latest suffix. Note that this is v2, so the import path carries the /v2 suffix; the repository also ships UPGRADE_GUIDE_V2.md for code moving from the older path.
go get charm.land/log/v2@latestImport it under the conventional name log. If you already import the standard log package in the same file, alias one of them.
import "charm.land/log/v2"With the import in place, the global logger works immediately. Debug output is suppressed at the default info level, so only the second line appears.
log.Debug("Cookie") // won't print anything
log.Info("Hello World!")To attach structured fields, pass key/value pairs after the message. The README uses an error value here, and the logger renders it as a key alongside the message text.
err := fmt.Errorf("too much sugar")
log.Error("failed to bake cookies", "err", err)For a logger you control, construct one with options rather than mutating the global. This example enables caller reporting and a kitchen-format timestamp with a prefix.
logger := log.NewWithOptions(os.Stderr, log.Options{
ReportCaller: true,
ReportTimestamp: true,
TimeFormat: time.Kitchen,
Prefix: "Baking ",
})
logger.Info("Starting oven!", "degree", 375)The examples/ directory in the repository contains runnable programs for options, styles, slog, error handling and formatting, which is the fastest way to see the rendered output without writing your own harness.
Where charmbracelet/log is the wrong tool
The color path is the first constraint. Styling goes through Lip Gloss, and go.mod pulls in a chain of indirect dependencies for terminal handling (x/ansi, x/term, x/termios, x/windows, go-runewidth, uniseg and others). For a small CLI that is a reasonable trade. For a library that only wants to emit a few diagnostics, it is a heavy tree to inherit, and every consumer of your module inherits it too.
The second constraint is that the human readable format is the default. A styled line with a timestamp, a level, a caller and color escape codes is more expensive to produce and harder to parse than a bare JSON object. If your logs land in an aggregator, you should be using the JSON formatter, and at that point the styling that distinguishes this library stops mattering. The README does not document a benchmark or a throughput figure, so there is no number here to quote either way; the point is structural, not measured.
The third is scope. There is no built-in rotation, no sampling, no async writer and no rate limiting. Fatal exits the process, which is fine for a CLI and dangerous in a server where one bad request should not kill the process. The README does not document rollback or recovery behavior for any of this, because there is none to document.
How it differs from the standard library and from slog
The obvious alternative is the standard library's log package, and the README frames the comparison itself: unlike standard log, the Charm logger provides customizable colorful human readable logging. The practical difference is that standard log gives you a writer and a prefix and nothing else, while charmbracelet/log gives you levels, key/value pairs, three output formats and terminal-aware styling. If you want none of those, adding this dependency buys you nothing.
The more interesting comparison is with log/slog, which arrived in the standard library with leveled structured logging and its own handler interface. charmbracelet/log does not compete with slog so much as wrap it: the README lists "slog handler" as a feature, and the repository has examples/slog/. The difference in approach is that slog defines the interface and leaves rendering to handlers, while charmbracelet/log ships its own logger and its own renderers, and additionally offers a handler so slog calls can be routed through Charm's formatting. If your codebase is already standardized on slog, the handler is the integration point to evaluate first; if you are starting fresh and want colored output with minimal setup, the native API is shorter.
A third path is to keep standard log and write your own formatter. That is viable only if your needs are close to plain text, because you would be rebuilding level filtering and structured fields by hand.
Maintenance, the v2 upgrade, and the MIT licence
The repository is not archived, and the last push was on 2026-09-03, the same day as the v2.0.1 release. The release history shows v2.0.0 on 2026-03-09 and v0.4.2 back on 2025-05-12, so the v2 line is recent and the v1-to-v2 transition is the upgrade you are most likely to face. UPGRADE_GUIDE_V2.md exists at the repository root for that purpose, and the module path in go.mod is charm.land/log/v2, which confirms the major version is part of the import.
The module declares go 1.25.8 in go.mod, so a toolchain at least that new is required to build it. That is a real constraint if you maintain a library that supports older Go versions, because your go.mod directive will be pulled upward.
Licensing is MIT, which is permissive and imposes no source disclosure on your own code. The dependencies are a mix: Lip Gloss and the Charm terminal packages, go-logfmt, testify, and golang.org/x/exp. If your organisation audits transitive licences, that set is what you would need to clear. This is not legal advice; check the actual licence files for your own distribution.
Editorial conclusion
Adopt charmbracelet/log when the primary reader of your logs is a person at a terminal: CLI tools, local development, or a small service where readable output matters more than parse cost. Do not adopt it as the only logging path for a high-volume service that ships logs to an aggregator, because every call renders a styled string before it reaches the writer. Before committing, verify the v2 import path (charm.land/log/v2) matches your module, read UPGRADE_GUIDE_V2.md if you are coming from v0.x, and check whether you need the slog handler or the standard log adapter rather than the native API.
Frequently asked questions
Which Go logger is the best?
There is no single answer, and the README does not rank loggers. charmbracelet/log positions itself as a minimal, colorful option for human readable output, and it also exposes a slog handler and a standard log adapter, so it can sit alongside code that already uses those interfaces.
What is a logger used for?
In this library, a logger emits leveled messages with optional key/value pairs to an io.Writer such as os.Stderr. charmbracelet/log adds level filtering, timestamps, caller reporting and text, JSON or Logfmt formatting on top of that.
What is an example of logging?
The README's first example is log.Info("Hello World!") against the package-level logger, and log.Debug on the line above it, which prints nothing because the default level is info.
What are logs in coding?
They are the records a program writes while it runs. In charmbracelet/log each record carries a level, an optional timestamp, a message and any key/value pairs you pass, and is rendered by the selected formatter to the writer you supplied.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/charmbracelet-log)