13 Commits

Author SHA1 Message Date
3c8c83caa8 test commit to kick off new ci settings
All checks were successful
check / check (push) Successful in 13s
check / check (pull_request) Successful in 1m6s
2026-03-02 21:08:05 +01:00
4abd40d8e2 fix: split Dockerfile with pinned images and add CI workflow (#14)
## Summary

Rewrites the Dockerfile to use sha256-pinned images and proper multi-stage build structure. Adds missing Makefile targets and a Gitea CI workflow.

## Changes

### Dockerfile
- **Lint stage**: `golangci/golangci-lint` v1.64.8 pinned by sha256 — runs `make fmt-check` + `make lint`
- **Test stage**: `golang` 1.22.12 pinned by sha256 — runs `make test` with dependency on lint stage
- Removed redundant final stage (this is a library with no binary to build)
- Both images pinned by digest with version+date comments

### Makefile
- Added `fmt-check` target: verifies `gofmt` compliance without modifying files
- Added `check` target: runs `fmt-check`, `lint`, `test` in sequence
- Added `hooks` target: installs a pre-commit hook that runs `make check`
- Separated `gofmt` check from `lint` target (was previously bundled)
- Changed default target from `test` to `check`

### CI
- Added `.gitea/workflows/check.yml`: runs `docker build .` on push to main and on PRs

## Verification

`docker build --progress plain .` passes — all stages complete successfully.

closes #9

<!-- session: agent:sdlc-manager:subagent:fffa0a5a-5127-4489-a2e0-314c5eaaed68 -->

Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de>
Reviewed-on: #14
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-03-02 21:06:53 +01:00
9121da9aae Merge pull request 'fix: JSONHandler deadlock from recursive log.Println (closes #3)' (#4) from clawbot/simplelog:fix/json-handler-deadlock into main
Reviewed-on: #4
2026-02-08 18:29:55 +01:00
74ce052b77 Merge branch 'main' into fix/json-handler-deadlock 2026-02-08 18:29:12 +01:00
1eef38a5fa Merge pull request 'test: add deadlock regression test for JSONHandler (issue #3)' (#7) from clawbot/simplelog:test/jsonhandler-deadlock into main
Reviewed-on: #7
2026-02-08 18:27:15 +01:00
97a82e9b2c test: add deadlock regression test for JSONHandler
Reproduces issue #3 — JSONHandler.Handle() calling log.Println() causes
a deadlock when slog.SetDefault redirects log output back through slog.

This test hangs/fails on main and should pass once #4 is merged.
2026-02-08 09:21:08 -08:00
user
869b7ca4c3 fix: replace log.Println with fmt.Fprintln in JSONHandler to prevent deadlock 2026-02-08 09:15:17 -08:00
31c9ed52cb preparing for 1.0 2024-06-14 05:53:22 -07:00
28d0d041b0 lint and check fmt on docker build 2024-06-14 05:47:35 -07:00
278cb73053 fmt 2024-06-14 05:47:29 -07:00
ea0c84547f add example script 2024-06-14 05:39:08 -07:00
a852d938e7 ignore binary 2024-06-14 05:39:03 -07:00
a660203e8f fix call stack depth bug to show log location correctly 2024-06-14 05:38:29 -07:00
11 changed files with 158 additions and 13 deletions

View File

@@ -0,0 +1,12 @@
name: check
on:
push:
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: docker build .

1
.gitignore vendored
View File

@@ -1 +1,2 @@
.aider*
cmd/example/example

20
Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
# Lint stage: format check + golangci-lint
# golangci-lint v1.64.8 (2025-02-18)
FROM golangci/golangci-lint@sha256:2987913e27f4eca9c8a39129d2c7bc1e74fbcf77f181e01cea607be437aa5cb8 AS lint
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN make fmt-check
RUN make lint
# Test stage: run full test suite
# golang 1.22.12 (2025-02-04)
FROM golang@sha256:1cf6c45ba39db9fd6db16922041d074a63c935556a05c5ccb62d181034df7f02 AS test
# Depend on lint stage so both stages always run
COPY --from=lint /src/go.sum /dev/null
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN make test

14
LICENSE Normal file
View File

@@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@@ -1,6 +1,6 @@
.PHONY: test
.PHONY: test fmt fmt-check lint check docker hooks
default: test
default: check
test:
@go test -v ./...
@@ -9,6 +9,20 @@ fmt:
goimports -l -w .
golangci-lint run --fix
fmt-check:
@test -z "$$(gofmt -l .)" || { echo "gofmt would reformat:"; gofmt -l .; exit 1; }
lint:
golangci-lint run
sh -c 'test -z "$$(gofmt -l .)"'
check: fmt-check lint test
docker:
docker build --progress plain .
hooks:
@echo "Installing git hooks..."
@mkdir -p .git/hooks
@printf '#!/bin/sh\nmake check\n' > .git/hooks/pre-commit
@chmod +x .git/hooks/pre-commit
@echo "Pre-commit hook installed."

View File

@@ -11,15 +11,18 @@ stdlib `log/slog` default handler, and solve the 90% case for logging.
## Current Status
Pre-1.0, not working yet.
Released v1.0.0 2024-06-14. Works as intended. No known bugs.
## Features
- if output is a tty, outputs pretty color logs
- if output is not a tty, outputs json
- supports delivering logs via tcp RELP (e.g. to remote rsyslog using imrelp)
- supports delivering each log message via a webhook
## Planned Features
- supports delivering logs via tcp RELP (e.g. to remote rsyslog using imrelp)
## Installation
To use simplelog, first ensure your project is set up with Go modules:
@@ -36,7 +39,9 @@ go get sneak.berlin/go/simplelog
## Usage
Below is an example of how to use SimpleLog in a Go application. This example is provided in the form of a `main.go` file, which demonstrates logging at various levels using structured logging syntax.
Below is an example of how to use SimpleLog in a Go application. This
example is provided in the form of a `main.go` file, which demonstrates
logging at various levels using structured logging syntax.
```go
package main
@@ -54,3 +59,8 @@ func main() {
slog.Error("Failed to save data", slog.String("reason", "permission denied"))
}
```
## License
[WTFPL](./LICENSE)

26
cmd/example/main.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"log/slog"
_ "sneak.berlin/go/simplelog"
)
func main() {
// log structured data with slog as usual:
slog.Info(
"User login attempt",
slog.String("user", "JohnDoe"),
slog.Int("attempt", 3),
)
slog.Warn(
"Configuration mismatch",
slog.String("expected", "config.json"),
slog.String("found", "config.dev.json"),
)
slog.Error(
"Failed to save data",
slog.String("reason", "permission denied"),
)
}

View File

@@ -16,7 +16,10 @@ func NewConsoleHandler() *ConsoleHandler {
return &ConsoleHandler{}
}
func (c *ConsoleHandler) Handle(ctx context.Context, record slog.Record) error {
func (c *ConsoleHandler) Handle(
ctx context.Context,
record slog.Record,
) error {
timestamp := time.Now().UTC().Format("2006-01-02T15:04:05.000Z07:00")
var colorFunc func(format string, a ...interface{}) string
@@ -32,16 +35,28 @@ func (c *ConsoleHandler) Handle(ctx context.Context, record slog.Record) error {
}
// Get the caller information
_, file, line, ok := runtime.Caller(5)
_, file, line, ok := runtime.Caller(4)
if !ok {
file = "???"
line = 0
}
fmt.Println(colorFunc("%s [%s] %s:%d: %s", timestamp, record.Level, file, line, record.Message))
fmt.Println(
colorFunc(
"%s [%s] %s:%d: %s",
timestamp,
record.Level,
file,
line,
record.Message,
),
)
return nil
}
func (c *ConsoleHandler) Enabled(ctx context.Context, level slog.Level) bool {
func (c *ConsoleHandler) Enabled(
ctx context.Context,
level slog.Level,
) bool {
return true
}

View File

@@ -15,7 +15,6 @@ type Event struct {
Data json.RawMessage `json:"data"`
}
func NewEvent(level, message string, data json.RawMessage) Event {
return Event{
ID: uuid.New(),

View File

@@ -3,8 +3,9 @@ package simplelog
import (
"context"
"encoding/json"
"log"
"fmt"
"log/slog"
"os"
)
type JSONHandler struct{}
@@ -15,7 +16,7 @@ func NewJSONHandler() *JSONHandler {
func (j *JSONHandler) Handle(ctx context.Context, record slog.Record) error {
jsonData, _ := json.Marshal(record)
log.Println(string(jsonData))
fmt.Fprintln(os.Stdout, string(jsonData))
return nil
}

33
json_handler_test.go Normal file
View File

@@ -0,0 +1,33 @@
package simplelog
import (
"log/slog"
"testing"
"time"
)
// TestJSONHandlerDeadlock verifies that JSONHandler.Handle does not deadlock
// when the default slog handler routes log.Println back through slog.
// On the unfixed code this test will hang (deadlock); with the fix it completes.
func TestJSONHandlerDeadlock(t *testing.T) {
handler := NewJSONHandler()
// Set our handler as the default so log.Println routes through slog
logger := slog.New(handler)
slog.SetDefault(logger)
done := make(chan struct{})
go func() {
// This call deadlocks on unfixed code because Handle() calls
// log.Println() which re-enters slog → Handle() → log.Println() …
slog.Info("test message")
close(done)
}()
select {
case <-done:
// success
case <-time.After(5 * time.Second):
t.Fatal("JSONHandler.Handle deadlocked: timed out after 5 seconds")
}
}