Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: write logs to stderr instead of stdout, fixes #753 #759

Merged
merged 1 commit into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.707
0.2.708
49 changes: 17 additions & 32 deletions cmd/templ/fmtcmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,26 @@ import (
"time"

"github.com/a-h/templ/cmd/templ/processor"
"github.com/a-h/templ/cmd/templ/sloghandler"
parser "github.com/a-h/templ/parser/v2"
"github.com/natefinch/atomic"
)

type Arguments struct {
ToStdout bool
Files []string
LogLevel string
WorkerCount int
}

func Run(w io.Writer, args Arguments) (err error) {
func Run(log *slog.Logger, stdin io.Reader, stdout io.Writer, args Arguments) (err error) {
// If no files are provided, read from stdin and write to stdout.
if len(args.Files) == 0 {
return format(writeToStdout, readFromStdin)
}

level := slog.LevelInfo.Level()
switch args.LogLevel {
case "debug":
level = slog.LevelDebug.Level()
case "warn":
level = slog.LevelWarn.Level()
case "error":
level = slog.LevelError.Level()
}
log := slog.New(sloghandler.NewHandler(w, &slog.HandlerOptions{
AddSource: args.LogLevel == "debug",
Level: level,
}))
if args.ToStdout {
log = slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{}))
return format(writeToWriter(stdout), readFromReader(stdin))
}
process := func(fileName string) error {
read := readFromFile(fileName)
write := writeToFile
if args.ToStdout {
write = writeToStdout
write = writeToWriter(stdout)
}
return format(write, read)
}
Expand Down Expand Up @@ -101,12 +82,14 @@ func (f *Formatter) Run() (err error) {

type reader func() (fileName, src string, err error)

func readFromStdin() (fileName, src string, err error) {
b, err := io.ReadAll(os.Stdin)
if err != nil {
return "", "", fmt.Errorf("failed to read stdin: %w", err)
func readFromReader(r io.Reader) func() (fileName, src string, err error) {
return func() (fileName, src string, err error) {
b, err := io.ReadAll(r)
if err != nil {
return "", "", fmt.Errorf("failed to read stdin: %w", err)
}
return "stdin.templ", string(b), nil
}
return "stdin.templ", string(b), nil
}

func readFromFile(name string) reader {
Expand All @@ -123,11 +106,13 @@ type writer func(fileName, tgt string) error

var mu sync.Mutex

func writeToStdout(fileName, tgt string) error {
mu.Lock()
defer mu.Unlock()
_, err := os.Stdout.Write([]byte(tgt))
return err
func writeToWriter(w io.Writer) func(fileName, tgt string) error {
return func(fileName, tgt string) error {
mu.Lock()
defer mu.Unlock()
_, err := w.Write([]byte(tgt))
return err
}
}

func writeToFile(fileName, tgt string) error {
Expand Down
16 changes: 8 additions & 8 deletions cmd/templ/generatecmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"net/url"
Expand Down Expand Up @@ -55,9 +54,14 @@ func (cmd Generate) Run(ctx context.Context) (err error) {
if cmd.Args.Watch && cmd.Args.FileName != "" {
return fmt.Errorf("cannot watch a single file, remove the -f or -watch flag")
}
if cmd.Args.FileName == "" && cmd.Args.ToStdout {
writingToWriter := cmd.Args.FileWriter != nil
if cmd.Args.FileName == "" && writingToWriter {
return fmt.Errorf("only a single file can be output to stdout, add the -f flag to specify the file to generate code for")
}
// Default to writing to files.
if cmd.Args.FileWriter == nil {
cmd.Args.FileWriter = FileWriter
}
if cmd.Args.PPROFPort > 0 {
go func() {
_ = http.ListenAndServe(fmt.Sprintf("localhost:%d", cmd.Args.PPROFPort), nil)
Expand All @@ -81,10 +85,6 @@ func (cmd Generate) Run(ctx context.Context) (err error) {
opts = append(opts, generator.WithTimestamp(time.Now()))
}

if cmd.Args.ToStdout {
cmd.Log = slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{}))
}

// Check the version of the templ module.
if err := modcheck.Check(cmd.Args.Path); err != nil {
cmd.Log.Warn("templ version check: " + err.Error())
Expand All @@ -97,7 +97,7 @@ func (cmd Generate) Run(ctx context.Context) (err error) {
opts,
cmd.Args.GenerateSourceMapVisualisations,
cmd.Args.KeepOrphanedFiles,
cmd.Args.ToStdout,
cmd.Args.FileWriter,
)

// If we're processing a single file, don't bother setting up the channels/multithreaing.
Expand Down Expand Up @@ -182,7 +182,7 @@ func (cmd Generate) Run(ctx context.Context) (err error) {
opts,
cmd.Args.GenerateSourceMapVisualisations,
cmd.Args.KeepOrphanedFiles,
cmd.Args.ToStdout,
cmd.Args.FileWriter,
)
errorCount.Store(0)
if err := watcher.WalkFiles(ctx, cmd.Args.Path, events); err != nil {
Expand Down
36 changes: 19 additions & 17 deletions cmd/templ/generatecmd/eventhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"go/format"
"go/scanner"
"go/token"
"io"
"log/slog"
"os"
"path"
Expand All @@ -23,14 +24,27 @@ import (
"github.com/fsnotify/fsnotify"
)

type FileWriterFunc func(name string, contents []byte) error

func FileWriter(fileName string, contents []byte) error {
return os.WriteFile(fileName, contents, 0o644)
}

func WriterFileWriter(w io.Writer) FileWriterFunc {
return func(_ string, contents []byte) error {
_, err := w.Write(contents)
return err
}
}

func NewFSEventHandler(
log *slog.Logger,
dir string,
devMode bool,
genOpts []generator.GenerateOpt,
genSourceMapVis bool,
keepOrphanedFiles bool,
toStdout bool,
fileWriter FileWriterFunc,
) *FSEventHandler {
if !path.IsAbs(dir) {
dir, _ = filepath.Abs(dir)
Expand All @@ -48,10 +62,7 @@ func NewFSEventHandler(
genSourceMapVis: genSourceMapVis,
DevMode: devMode,
keepOrphanedFiles: keepOrphanedFiles,
writer: writeToFile,
}
if toStdout {
fseh.writer = writeToStdout
writer: fileWriter,
}
if devMode {
fseh.genOpts = append(fseh.genOpts, generator.WithExtractStrings())
Expand All @@ -77,15 +88,6 @@ type FSEventHandler struct {
writer func(string, []byte) error
}

func writeToFile(fileName string, contents []byte) error {
return os.WriteFile(fileName, contents, 0o644)
}

func writeToStdout(_ string, contents []byte) error {
_, err := os.Stdout.Write(contents)
return err
}

func (h *FSEventHandler) HandleEvent(ctx context.Context, event fsnotify.Event) (goUpdated, textUpdated bool, err error) {
// Handle _templ.go files.
if !event.Has(fsnotify.Remove) && strings.HasSuffix(event.Name, "_templ.go") {
Expand Down Expand Up @@ -223,8 +225,8 @@ func (h *FSEventHandler) generate(ctx context.Context, fileName string) (goUpdat

formattedGoCode, err := format.Source(b.Bytes())
if err != nil {
err = remapErrorList(err, sourceMap, fileName, targetFileName)
return false, false, nil, fmt.Errorf("%s source formatting error %w", fileName, err)
err = remapErrorList(err, sourceMap, fileName)
return false, false, nil, fmt.Errorf("% source formatting error %w", fileName, err)
}

// Hash output, and write out the file if the goCodeHash has changed.
Expand Down Expand Up @@ -262,7 +264,7 @@ func (h *FSEventHandler) generate(ctx context.Context, fileName string) (goUpdat

// Takes an error from the formatter and attempts to convert the positions reported in the target file to their positions
// in the source file.
func remapErrorList(err error, sourceMap *parser.SourceMap, fileName string, targetFileName string) error {
func remapErrorList(err error, sourceMap *parser.SourceMap, fileName string) error {
list, ok := err.(scanner.ErrorList)
if !ok || len(list) == 0 {
return err
Expand Down
21 changes: 2 additions & 19 deletions cmd/templ/generatecmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ package generatecmd
import (
"context"
_ "embed"
"io"
"log/slog"

_ "net/http/pprof"

"github.com/a-h/templ/cmd/templ/sloghandler"
)

type Arguments struct {
FileName string
ToStdout bool
FileWriter FileWriterFunc
Path string
Watch bool
OpenBrowser bool
Expand All @@ -26,25 +23,11 @@ type Arguments struct {
GenerateSourceMapVisualisations bool
IncludeVersion bool
IncludeTimestamp bool
LogLevel string
// PPROFPort is the port to run the pprof server on.
PPROFPort int
KeepOrphanedFiles bool
}

func Run(ctx context.Context, w io.Writer, args Arguments) (err error) {
level := slog.LevelInfo.Level()
switch args.LogLevel {
case "debug":
level = slog.LevelDebug.Level()
case "warn":
level = slog.LevelWarn.Level()
case "error":
level = slog.LevelError.Level()
}
log := slog.New(sloghandler.NewHandler(w, &slog.HandlerOptions{
AddSource: args.LogLevel == "debug",
Level: level,
}))
func Run(ctx context.Context, log *slog.Logger, args Arguments) (err error) {
return NewGenerate(log, args).Run(ctx)
}
3 changes: 2 additions & 1 deletion cmd/templ/generatecmd/test-eventhandler/eventhandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func TestErrorLocationMapping(t *testing.T) {
}

slog := slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{}))
fseh := generatecmd.NewFSEventHandler(slog, ".", false, []generator.GenerateOpt{}, false, false, true)
var fw generatecmd.FileWriterFunc
fseh := generatecmd.NewFSEventHandler(slog, ".", false, []generator.GenerateOpt{}, false, false, fw)
for _, test := range tests {
// The raw files cannot end in .templ because they will cause the generator to fail. Instead,
// we create a tmp file that ends in .templ only for the duration of the test.
Expand Down
33 changes: 22 additions & 11 deletions cmd/templ/generatecmd/testwatch/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"context"
"embed"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"os"
Expand Down Expand Up @@ -368,16 +370,23 @@ func Setup(gzipEncoding bool) (args TestArgs, teardown func(t *testing.T), err e
command += " -gzip true"
}

cmdErr = generatecmd.Run(ctx, os.Stdout, generatecmd.Arguments{
Path: appDir,
Watch: true,
Command: command,
ProxyBind: proxyBind,
ProxyPort: proxyPort,
Proxy: args.AppURL,
IncludeVersion: false,
IncludeTimestamp: false,
KeepOrphanedFiles: false,
log := slog.New(slog.NewJSONHandler(io.Discard, nil))

cmdErr = generatecmd.Run(ctx, log, generatecmd.Arguments{
Path: appDir,
Watch: true,
OpenBrowser: false,
Command: command,
ProxyBind: proxyBind,
ProxyPort: proxyPort,
Proxy: args.AppURL,
NotifyProxy: false,
WorkerCount: 0,
GenerateSourceMapVisualisations: false,
IncludeVersion: false,
IncludeTimestamp: false,
PPROFPort: 0,
KeepOrphanedFiles: false,
})
}()

Expand Down Expand Up @@ -460,8 +469,10 @@ func TestGenerateReturnsErrors(t *testing.T) {
t.Errorf("failed to replace text in file: %v", err)
}

log := slog.New(slog.NewJSONHandler(io.Discard, nil))

// Run.
err = generatecmd.Run(context.Background(), os.Stdout, generatecmd.Arguments{
err = generatecmd.Run(context.Background(), log, generatecmd.Arguments{
Path: appDir,
Watch: false,
IncludeVersion: false,
Expand Down
6 changes: 3 additions & 3 deletions cmd/templ/lspcmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type Arguments struct {
HTTPDebug string
}

func Run(w io.Writer, args Arguments) (err error) {
func Run(stdin io.Reader, stdout, stderr io.Writer, args Arguments) (err error) {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
signalChan := make(chan os.Signal, 1)
Expand Down Expand Up @@ -61,14 +61,14 @@ func Run(w io.Writer, args Arguments) (err error) {
}
log, err = cfg.Build()
if err != nil {
_, _ = fmt.Fprintf(w, "failed to create logger: %v\n", err)
_, _ = fmt.Fprintf(stderr, "failed to create logger: %v\n", err)
os.Exit(1)
}
}
defer func() {
_ = log.Sync()
}()
templStream := jsonrpc2.NewStream(newStdRwc(log, "templStream", w, os.Stdin))
templStream := jsonrpc2.NewStream(newStdRwc(log, "templStream", stdout, stdin))
return run(ctx, log, templStream, args)
}

Expand Down
Loading
Loading