Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: hamba/cmd
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.7.2
Choose a base ref
...
head repository: hamba/cmd
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Loading
Showing with 925 additions and 682 deletions.
  1. +11 −0 .github/PULL_REQUEST_TEMPLATE.md
  2. +8 −0 .github/dependabot.yml
  3. +9 −16 .github/workflows/test.yml
  4. +5 −12 .golangci.yml
  5. +1 −1 LICENCE
  6. +23 −1 Makefile
  7. +75 −5 README.md
  8. +12 −0 cmd.go
  9. +21 −0 example_test.go
  10. +2 −16 flags.go
  11. +45 −28 go.mod
  12. +111 −529 go.sum
  13. +16 −10 log.go
  14. +38 −0 observe/doc.go
  15. +68 −0 observe/example_test.go
  16. +152 −0 observe/observer.go
  17. +146 −0 profile.go
  18. +83 −0 profile_test.go
  19. +20 −13 stats.go
  20. +2 −2 stats_test.go
  21. +63 −31 trace.go
  22. +14 −18 trace_test.go
11 changes: 11 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## Goal of this PR

<!-- A brief description of the change being made with this pull request. -->

<!--
Fixes #
-->

## How did I test it?

<!-- A brief description the steps taken to test this pull request. -->
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -4,9 +4,17 @@ updates:
directory: "/"
schedule:
interval: weekly
groups:
all:
patterns:
- "*"
open-pull-requests-limit: 10
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: weekly
groups:
all:
patterns:
- "*"
open-pull-requests-limit: 10
25 changes: 9 additions & 16 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -10,31 +10,23 @@ jobs:

strategy:
matrix:
go-version: [ 1.19, "1.20" ]
go-version: [ "1.23", "1.24" ]
runs-on: ubuntu-latest
env:
GOLANGCI_LINT_VERSION: v1.51.0
GOLANGCI_LINT_VERSION: v1.64.2

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Go
if: success()
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Checkout code
uses: actions/checkout@v3

- name: Cache Go modules
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run linter
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
with:
version: ${{ env.GOLANGCI_LINT_VERSION }}
args: --go ${{ matrix.go-version }}
@@ -44,8 +36,9 @@ jobs:

- name: Convert coverage.out to coverage.lcov
uses: jandelgado/gcov2lcov-action@v1

- name: Coveralls
uses: coverallsapp/github-action@v2.0.0
uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: coverage.lcov
17 changes: 5 additions & 12 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -12,24 +12,17 @@ linters-settings:
linters:
enable-all: true
disable:
- interfacer # deprecated
- scopelint # deprecated
- maligned # deprecated
- golint # deprecated
- exhaustivestruct # deprecated
- ifshort # deprecated
- deadcode # deprecated
- nosnakecase # deprecated
- structcheck # deprecated
- varcheck # deprecated
- tenv # deprecated
- depguard
- err113
- exhaustive
- exhaustruct
- forcetypeassert
- funlen
- gochecknoglobals
- gochecknoinits
- goerr113
- gomnd
- ireturn
- mnd
- nlreturn
- varnamelen
- wrapcheck
2 changes: 1 addition & 1 deletion LICENCE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 Nicholas Wiersma
Copyright (c) 2025 Nicholas Wiersma

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
24 changes: 23 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
include github.com/hamba/make/golang
# Format all files
fmt:
@echo "==> Formatting source"
@gofmt -s -w $(shell find . -type f -name '*.go' -not -path "./vendor/*")
@echo "==> Done"
.PHONY: fmt

# Tidy the go.mod file
tidy:
@echo "==> Cleaning go.mod"
@go mod tidy
@echo "==> Done"
.PHONY: tidy

# Run all tests
test:
@go test -cover -race ./...
.PHONY: test

# Lint the project
lint:
@golangci-lint run ./...
.PHONY: lint
80 changes: 75 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -19,8 +19,6 @@ Install with:
go get github.com/hamba/cmd/v2
```

**Note:** This project has renamed the default branch from `master` to `main`. You will need to update your local environment.

## Example

```go
@@ -46,8 +44,11 @@ func yourAction(c *cli.Context) error {
defer tracer.Shutdown(context.Background())

// Run your application here...

return nil
}
```

## Flags

### Logger
@@ -78,7 +79,7 @@ The statter flags are used by `cmd.NewStatter` to create a new `hamba.Statter.

#### FlagStatsDSN: *--stats.dsn, $STATS_DSN*

This flag sets the DSN describing the stats reporter to use. The available options are `statsd`, `prometheus`, `l2met`.
This flag sets the DSN describing the stats reporter to use. The available options are `statsd`, `prometheus`, `l2met`, `victoriametrics`.

The DSN can in some situations specify the host and configuration values as shown in the below examples:

@@ -133,24 +134,93 @@ This flag sets tag key value pairs to set on all stats. This flag can be specifi

Example: `--stats.tags="app=my-app" --stats.tags="zone=eu-west"`

### Profiler

The profiler flags are used by `cmd.NewProfiler` to create a Pyroscope `*pyroscope.Profiler`.

#### FlagProfilingDSN: *--profiling.dsn, $PROFILING_DSN*

This flag configures the URL, authentication and optionally the Tenant ID for Pyroscope.

Example: `--profiling.dsn=https://user:pass@host/path?token=auth-token&tenantid=my-tenant-id`

#### FlagProfileUploadRate: *--profiling.upload-rate, $PROFILING_UPLOAD_RATE*

This flag configures the rate at which profiles are uploaded.

Example: `--profiling.upload-rate=10s`

#### FlagProfilingTags: *--profiling.tags, $PROFILING_TAGS*

This configures a list of tags appended to every profile. This flag can be specified multiple times.

Example: `--profiling.tags="app=my-app" --profiling.tags="zone=eu-west"`

#### FlagProfilingTypes: *--profiling.types, $PROFILING_TYPES*

This configures the profile types that are captured. By default all supported types are captured. This flag can be specified multiple times.

Example: `--profiling.types=cpu --profiling.types=inuse_object`

### Tracer

The tracing flags are used by `cmd.NewTracer` to create a new open telemetry `trace.TraceProvider`.

#### FlagTracingExporter: *--tracing.exporter, $TRACING_EXPORTER*

This flag sets the exporter to send spans to. The available options are `jaeger` and `zipkin`.
This flag sets the exporter to send spans to. The available options are `zipkin`, `otlphttp` and `otlpgrpc`.

Example: `--tracing.exporter=jaeger`
Example: `--tracing.exporter=otlphttp`

#### FlagTracingEndpoint: *--tracing.endpoint, $TRACING_ENDPOINT*

This flag sets the endpoint the exporter should send traces to.

Example: `--tracing.endpoint="agent-host:port"` or `--tracing.endpoint="http://host:port/api/v2"`

#### FlagTracingEndpointInsecure: *--tracing.endpoint-insecure, $TRACING_ENDPOINT_INSECURE*

This flag sets the endpoint the exporter should send traces to.

Example: `--tracing.endpoint-insecure`

#### FlagTracingRatio: *--tracing.ratio, $TRACING_RATIO*

This flag sets the sample ratio of spans that will be reported to the exporter. This should be between 0 and 1.

Example: `--tracing.ratio=0.2`

#### FlagTracingTags: *--tracing.tags, $TRACING_TAGS*

This flag sets a list of tags appended to every trace. This flag can be specified multiple times.

Example: `--tracing.tags="app=my-app" --tracing.tags="zone=eu-west"`

### Observer

The observe package exposes an `Observer` type which is essentially a helper that combines a logger, tracer and statter.
It is useful if you use all three for your services and want to avoid carrying around many arguments.

Here is an example of how one might use it:

```go
func yourAction(c *cli.Context) error {
obsvr, err := observe.NewFromCLI(c, "my-service", &observe.Options{
LogTimestamps: true,
StatsRuntime: true,
TracingAttrs: []attribute.KeyValue{
semconv.ServiceVersionKey.String("1.0.0"),
},
})
if err != nil {
return err
}
defer obsvr.Close()

// Run your application here...

return nil
}
```

It also exposes `NewFake` which allows you to pass fake loggers, tracers and statters in your tests easily.
12 changes: 12 additions & 0 deletions cmd.go
Original file line number Diff line number Diff line change
@@ -20,3 +20,15 @@ func Split(slice []string, sep string) ([][2]string, error) {

return res, nil
}

func sliceToMap(s []string) (map[string]string, error) {
m := make(map[string]string, len(s))
kvs, err := Split(s, "=")
if err != nil {
return nil, err
}
for _, kv := range kvs {
m[kv[0]] = kv[1]
}
return m, nil
}
21 changes: 21 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -40,6 +40,27 @@ func ExampleNewStatter() {
_ = stats
}

func ExampleNewProfiler() {
var c *cli.Context // Get this from your action

log, err := cmd.NewLogger(c)
if err != nil {
// Handle error.
return
}

prof, err := cmd.NewProfiler(c, "my-service", log)
if err != nil {
// Handle error.
return
}
if prof != nil {
defer func() { _ = prof.Stop() }()
}

_ = prof
}

func ExampleNewTracer() {
var c *cli.Context // Get this from your action

18 changes: 2 additions & 16 deletions flags.go
Original file line number Diff line number Diff line change
@@ -4,9 +4,6 @@ import (
"github.com/urfave/cli/v2"
)

// FlagPort contains the flag name for a server port.
const FlagPort = "port"

// Flags represents a set of CLI flags.
type Flags []cli.Flag

@@ -21,16 +18,5 @@ func (f Flags) Merge(flags ...Flags) Flags {
return m
}

// ServerFlags are flags that configure a server.
// Deprecated: There is no standardisation around this. It is preferred to make your own flag.
var ServerFlags = Flags{
&cli.StringFlag{
Name: FlagPort,
Value: "80",
Usage: "Port for HTTP server to listen on",
EnvVars: []string{"PORT"},
},
}

// MonitoringFlags are flags that configure logging, stats and tracing.
var MonitoringFlags = Flags{}.Merge(LogFlags, StatsFlags, TracingFlags)
// MonitoringFlags are flags that configure logging, stats, profiling and tracing.
var MonitoringFlags = Flags{}.Merge(LogFlags, StatsFlags, ProfilingFlags, TracingFlags)
Loading