Files
nginx-logtail/Makefile
Pim van Pelt 143aad9063 PRE-RELEASE 0.9.1: Makefile, Debian packaging, versioned UDP
Build and release tooling:
- Makefile with help as default; targets: build/build-amd64/build-arm64,
  test, lint, proto, pkg-deb, docker, docker-push, clean, plus
  install-deps (+ three sub-targets for apt / Go toolchain / Go tools).
- internal/version package; -ldflags -X injects Version/Commit/Date into
  every binary. -version flag on all four binaries (nginx-logtail version
  for the CLI).
- Dockerfile takes VERSION/COMMIT/DATE build-args and forwards them.
- .deb output lands in build/; .gitignore ignores /build/.

Debian package:
- debian/build-deb.sh packages all four static binaries into a single
  nginx-logtail_<ver>_<arch>.deb using dpkg-deb.
- Binary layout: /usr/sbin/nginx-logtail-{collector,aggregator,frontend}
  and /usr/bin/nginx-logtail.
- nginx-logtail(8) manpage.
- Three systemd units (collector, aggregator, frontend) shipped under
  /lib/systemd/system/. Installed but never enabled or started — the
  operator opts in per host.
- Collector runs as _logtail:www-data (log access); aggregator and
  frontend as _logtail:_logtail. postinst creates the system user/group
  idempotently.
- Single shared env file /etc/default/nginx-logtail rendered from a
  template at first install with %HOSTNAME% substituted. Sensible
  defaults for every COLLECTOR_*, AGGREGATOR_*, FRONTEND_* variable;
  plus COLLECTOR_ARGS / AGGREGATOR_ARGS / FRONTEND_ARGS escape hatches
  appended to ExecStart. Not a dpkg conffile: operator edits survive
  upgrades and dpkg --purge removes it.

Versioned UDP wire format:
- ParseUDPLine dispatches on a leading "v<N>\t" tag; v1 routes to the
  existing 12-field parser. Unknown/missing versions fail closed so
  future v2 parsers can land before emitters are upgraded.
- Tests updated; design.md FR-2.2 rewritten to make the version tag
  normative.

Docs:
- README.md gains a Quick Start (Debian / Docker Compose / from source).
- user-guide.md rewritten around Installation and Configuration: full
  env-var table, UDP-only default explained, precise file/UDP log_format
  layouts, note that operators can emit "0" for unknown \$is_tor / \$asn.
- Drilldown cycle, frontend filter table, and CLI --group-by list all
  include source_tag. UDP counters documented in the Prometheus section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 10:35:08 +02:00

202 lines
8.8 KiB
Makefile

BINARIES := collector aggregator frontend cli
MODULE := git.ipng.ch/ipng/nginx-logtail
PROTO_DIR := proto
PROTO_FILE := $(PROTO_DIR)/logtail.proto
GEN_FILES := proto/logtailpb/logtail.pb.go proto/logtailpb/logtail_grpc.pb.go
NATIVE_ARCH := $(shell go env GOARCH)
VERSION := 0.9.1
COMMIT_HASH := $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
LDFLAGS := -s -w \
-X '$(MODULE)/internal/version.Version=$(VERSION)' \
-X '$(MODULE)/internal/version.Commit=$(COMMIT_HASH)' \
-X '$(MODULE)/internal/version.Date=$(DATE)'
# CGO_ENABLED=0 produces fully static binaries: no libc dependency, so the
# .deb runs on any Linux-amd64/arm64 host regardless of glibc version.
export CGO_ENABLED := 0
IMAGE := git.ipng.ch/ipng/nginx-logtail
# GO_VERSION is the floor install-deps-go enforces. go.mod currently requires
# 1.24; override on the command line to pull a specific patch release:
# make install-deps GO_VERSION=1.25.0
GO_VERSION ?= 1.24.6
# GOLANGCI_LINT_VERSION is the minimum golangci-lint version install-deps-go-tools
# accepts. Older releases can't parse recent Go syntax.
GOLANGCI_LINT_VERSION ?= 1.64.0
.PHONY: help all build build-amd64 build-arm64 test lint proto pkg-deb docker docker-push clean \
install-deps install-deps-apt install-deps-go install-deps-go-tools
# help is the default target. Keep the list aligned with the .PHONY block above.
help:
@echo "nginx-logtail — make targets (version $(VERSION))"
@echo ""
@echo " build build all four binaries for the native arch into build/$(NATIVE_ARCH)/"
@echo " build-amd64 build all four binaries for linux/amd64 into build/amd64/"
@echo " build-arm64 build all four binaries for linux/arm64 into build/arm64/"
@echo " test run 'go test ./...'"
@echo " lint run 'golangci-lint run ./...'"
@echo " proto regenerate proto/logtailpb/*.pb.go from proto/logtail.proto"
@echo " pkg-deb build amd64 + arm64 .deb packages (requires dpkg-deb)"
@echo " docker buildx --load two tags ($(IMAGE):v$(VERSION), $(IMAGE):latest) for the native arch"
@echo " docker-push buildx multi-arch push (amd64+arm64) to $(IMAGE)"
@echo " clean remove build/ and generated proto files"
@echo ""
@echo " install-deps apt + Go toolchain + Go tools (runs the three sub-targets)"
@echo " install-deps-apt apt-install protobuf-compiler, git, make, dpkg-dev, curl, tar"
@echo " install-deps-go download Go $(GO_VERSION)+ to /usr/local/go if missing"
@echo " install-deps-go-tools go install protoc-gen-go, protoc-gen-go-grpc, golangci-lint"
@echo ""
@echo "Overridable variables:"
@echo " VERSION=$(VERSION) GO_VERSION=$(GO_VERSION) GOLANGCI_LINT_VERSION=$(GOLANGCI_LINT_VERSION)"
all: build
build: $(GEN_FILES)
mkdir -p build/$(NATIVE_ARCH)
$(foreach b,$(BINARIES),go build -trimpath -ldflags "$(LDFLAGS)" -o build/$(NATIVE_ARCH)/$(b) ./cmd/$(b) &&) true
build-amd64: $(GEN_FILES)
mkdir -p build/amd64
$(foreach b,$(BINARIES),GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "$(LDFLAGS)" -o build/amd64/$(b) ./cmd/$(b) &&) true
build-arm64: $(GEN_FILES)
mkdir -p build/arm64
$(foreach b,$(BINARIES),GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "$(LDFLAGS)" -o build/arm64/$(b) ./cmd/$(b) &&) true
test: $(GEN_FILES)
go test ./...
lint:
golangci-lint run ./...
proto: $(GEN_FILES)
# protoc's go_package option places output at the go_package path (not source-relative).
# We invoke protoc from the repo root so the resulting proto/logtailpb/*.pb.go tree
# lands alongside the .proto file.
$(GEN_FILES): $(PROTO_FILE)
protoc \
--go_out=. --go_opt=module=$(MODULE) \
--go-grpc_out=. --go-grpc_opt=module=$(MODULE) \
$(PROTO_FILE)
# pkg-deb builds one package per arch; both contain all four static binaries under
# /usr/local/bin/ with the nginx-logtail- prefix. Each build-<arch> target is a hard
# prerequisite — the packaging script refuses to run without build/<arch>/ populated.
pkg-deb: build-amd64 build-arm64
debian/build-deb.sh amd64 $(VERSION)
debian/build-deb.sh arm64 $(VERSION)
# docker — build one image for the native host arch and --load it into the local
# daemon. Tagged both :v$(VERSION) and :latest in a single build, so bumping
# VERSION is the only change needed to cut a release.
docker:
docker buildx build --load \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT_HASH) \
--build-arg DATE=$(DATE) \
-t $(IMAGE):v$(VERSION) -t $(IMAGE):latest .
# docker-push — build a multi-arch (amd64+arm64) manifest and push it. Buildx
# won't --load a multi-platform result, so this is the only path that produces
# the combined manifest. Assumes the caller is already logged in to the registry.
docker-push:
docker buildx build --platform linux/amd64,linux/arm64 --push \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT_HASH) \
--build-arg DATE=$(DATE) \
-t $(IMAGE):v$(VERSION) -t $(IMAGE):latest .
clean:
rm -rf build/
rm -f $(GEN_FILES)
# install-deps is an opt-in "set up a fresh developer box" target. Tested on
# Debian Trixie; the apt half should also work on Bookworm and recent Ubuntu LTS.
install-deps: install-deps-apt install-deps-go install-deps-go-tools
@echo ""
@echo "==> All build dependencies installed."
@echo " Make sure these are on PATH:"
@echo " /usr/local/go/bin (Go toolchain)"
@echo " \$$(go env GOPATH)/bin (protoc-gen-go, golangci-lint, ...)"
install-deps-apt:
@set -eu; \
if [ "$$(id -u)" = 0 ]; then SUDO=""; else SUDO="sudo"; fi; \
echo "==> Installing apt packages (protoc, git, make, dpkg-dev, curl, tar)"; \
$$SUDO apt-get update; \
$$SUDO apt-get install -y --no-install-recommends \
protobuf-compiler git make dpkg-dev ca-certificates curl tar
# install-deps-go short-circuits when go env GOVERSION already reports a version
# >= GO_VERSION. Otherwise it downloads the official upstream tarball and extracts
# it to /usr/local/go.
install-deps-go:
@set -eu; \
if [ "$$(id -u)" = 0 ]; then SUDO=""; else SUDO="sudo"; fi; \
echo "==> Checking Go toolchain (required: $(GO_VERSION)+)"; \
if command -v go >/dev/null 2>&1; then \
CURRENT=$$(go env GOVERSION 2>/dev/null | sed 's/^go//'); \
OLDEST=$$(printf '%s\n%s\n' "$(GO_VERSION)" "$$CURRENT" | sort -V | head -n1); \
if [ "$$OLDEST" = "$(GO_VERSION)" ] && [ -n "$$CURRENT" ]; then \
echo " go$$CURRENT already installed (>= $(GO_VERSION)), skipping."; \
exit 0; \
fi; \
echo " go$$CURRENT is older than $(GO_VERSION), upgrading."; \
else \
echo " no Go toolchain on PATH, installing."; \
fi; \
DEB_ARCH=$$(dpkg --print-architecture); \
case "$$DEB_ARCH" in \
amd64) GOARCH=amd64 ;; \
arm64) GOARCH=arm64 ;; \
armhf) GOARCH=armv6l ;; \
*) echo " unsupported architecture: $$DEB_ARCH" >&2; exit 1 ;; \
esac; \
TARBALL="go$(GO_VERSION).linux-$$GOARCH.tar.gz"; \
URL="https://go.dev/dl/$$TARBALL"; \
echo " downloading $$URL"; \
curl -fsSL -o "/tmp/$$TARBALL" "$$URL"; \
echo " installing to /usr/local/go"; \
$$SUDO rm -rf /usr/local/go; \
$$SUDO tar -C /usr/local -xzf "/tmp/$$TARBALL"; \
rm -f "/tmp/$$TARBALL"; \
echo " installed $$(/usr/local/go/bin/go version)"
install-deps-go-tools:
@set -eu; \
if ! command -v go >/dev/null 2>&1; then \
export PATH="/usr/local/go/bin:$$PATH"; \
fi; \
echo "==> Installing Go tools via 'go install'"; \
echo " google.golang.org/protobuf/cmd/protoc-gen-go"; \
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest; \
echo " google.golang.org/grpc/cmd/protoc-gen-go-grpc"; \
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest; \
echo " github.com/golangci/golangci-lint/v2/cmd/golangci-lint"; \
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest; \
GOBIN="$$(go env GOBIN)"; \
if [ -z "$$GOBIN" ]; then GOBIN="$$(go env GOPATH)/bin"; fi; \
echo "==> Asserting golangci-lint version >= $(GOLANGCI_LINT_VERSION)"; \
if ! "$$GOBIN/golangci-lint" version >/dev/null 2>&1; then \
echo " ERROR: $$GOBIN/golangci-lint is not executable" >&2; \
exit 1; \
fi; \
INSTALLED=$$("$$GOBIN/golangci-lint" version 2>&1 | sed -En 's/.*has version v?([0-9][0-9.]*).*/\1/p' | head -n1); \
if [ -z "$$INSTALLED" ]; then \
echo " ERROR: could not parse golangci-lint version output" >&2; \
"$$GOBIN/golangci-lint" version >&2; \
exit 1; \
fi; \
OLDEST=$$(printf '%s\n%s\n' "$(GOLANGCI_LINT_VERSION)" "$$INSTALLED" | sort -V | head -n1); \
if [ "$$OLDEST" != "$(GOLANGCI_LINT_VERSION)" ]; then \
echo " ERROR: golangci-lint $$INSTALLED is older than the required $(GOLANGCI_LINT_VERSION)" >&2; \
exit 1; \
fi; \
echo " golangci-lint $$INSTALLED (>= $(GOLANGCI_LINT_VERSION)) OK"