Skip to content
Open
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ plain text):
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
or Splunk:

```json
```text
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}

Expand Down Expand Up @@ -384,7 +384,7 @@ Third party logging formatters:
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo.
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files.
* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Save log to files.
* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added.

You can define your formatter by implementing the `Formatter` interface,
Expand Down
11 changes: 8 additions & 3 deletions entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
)

type contextKeyType string

func TestEntryWithError(t *testing.T) {

assert := assert.New(t)
Expand All @@ -36,7 +38,8 @@ func TestEntryWithError(t *testing.T) {

func TestEntryWithContext(t *testing.T) {
assert := assert.New(t)
ctx := context.WithValue(context.Background(), "foo", "bar")
var contextKey contextKeyType = "foo"
ctx := context.WithValue(context.Background(), contextKey, "bar")

assert.Equal(ctx, WithContext(ctx).Context)

Expand All @@ -56,11 +59,13 @@ func TestEntryWithContextCopiesData(t *testing.T) {
parentEntry := NewEntry(logger).WithField("parentKey", "parentValue")

// Create two children Entry objects from the parent in different contexts
ctx1 := context.WithValue(context.Background(), "foo", "bar")
var contextKey1 contextKeyType = "foo"
ctx1 := context.WithValue(context.Background(), contextKey1, "bar")
childEntry1 := parentEntry.WithContext(ctx1)
assert.Equal(ctx1, childEntry1.Context)

ctx2 := context.WithValue(context.Background(), "bar", "baz")
var contextKey2 contextKeyType = "bar"
ctx2 := context.WithValue(context.Background(), contextKey2, "baz")
childEntry2 := parentEntry.WithContext(ctx2)
assert.Equal(ctx2, childEntry2.Context)
assert.NotEqual(ctx1, ctx2)
Expand Down
3 changes: 1 addition & 2 deletions logrus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,7 @@ func TestParseLevel(t *testing.T) {
}

func TestLevelString(t *testing.T) {
var loggerlevel Level
loggerlevel = 32000
var loggerlevel Level = 32000

_ = loggerlevel.String()
}
Expand Down
2 changes: 1 addition & 1 deletion terminal_check_bsd.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build darwin dragonfly freebsd netbsd openbsd
// +build darwin dragonfly freebsd netbsd openbsd hurd
// +build !js

package logrus
Expand Down
2 changes: 2 additions & 0 deletions terminal_check_unix.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//go:build (linux || aix || zos) && !js && !wasi
// +build linux aix zos
// +build !js
// +build !wasi

package logrus

Expand Down
8 changes: 8 additions & 0 deletions terminal_check_wasi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build wasi
// +build wasi

package logrus

func isTerminal(fd int) bool {
return false
}
8 changes: 8 additions & 0 deletions terminal_check_wasip1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build wasip1
// +build wasip1

package logrus

func isTerminal(fd int) bool {
return false
}
34 changes: 33 additions & 1 deletion writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"io"
"runtime"
"strings"
)

// Writer at INFO level. See WriterLevel for details.
Expand All @@ -20,15 +21,18 @@ func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
return NewEntry(logger).WriterLevel(level)
}

// Writer returns an io.Writer that writes to the logger at the info log level
func (entry *Entry) Writer() *io.PipeWriter {
return entry.WriterLevel(InfoLevel)
}

// WriterLevel returns an io.Writer that writes to the logger at the given log level
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
reader, writer := io.Pipe()

var printFunc func(args ...interface{})

// Determine which log function to use based on the specified log level
switch level {
case TraceLevel:
printFunc = entry.Trace
Expand All @@ -48,23 +52,51 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
printFunc = entry.Print
}

// Start a new goroutine to scan the input and write it to the logger using the specified print function.
// It splits the input into chunks of up to 64KB to avoid buffer overflows.
go entry.writerScanner(reader, printFunc)

// Set a finalizer function to close the writer when it is garbage collected
runtime.SetFinalizer(writer, writerFinalizer)

return writer
}

// writerScanner scans the input from the reader and writes it to the logger
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
scanner := bufio.NewScanner(reader)

// Set the buffer size to the maximum token size to avoid buffer overflows
scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize)

// Define a split function to split the input into chunks of up to 64KB
chunkSize := bufio.MaxScanTokenSize // 64KB
splitFunc := func(data []byte, atEOF bool) (int, []byte, error) {
if len(data) >= chunkSize {
return chunkSize, data[:chunkSize], nil
}

return bufio.ScanLines(data, atEOF)
}

// Use the custom split function to split the input
scanner.Split(splitFunc)

// Scan the input and write it to the logger using the specified print function
for scanner.Scan() {
printFunc(scanner.Text())
printFunc(strings.TrimRight(scanner.Text(), "\r\n"))
}

// If there was an error while scanning the input, log an error
if err := scanner.Err(); err != nil {
entry.Errorf("Error while reading from Writer: %s", err)
}

// Close the reader when we are done
reader.Close()
}

// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected
func writerFinalizer(writer *io.PipeWriter) {
writer.Close()
}
64 changes: 64 additions & 0 deletions writer_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package logrus_test

import (
"bufio"
"bytes"
"log"
"net/http"
"strings"
"testing"
"time"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)

func ExampleLogger_Writer_httpServer() {
Expand Down Expand Up @@ -32,3 +38,61 @@ func ExampleLogger_Writer_stdlib() {
// Not logrus imported under the name `log`.
log.SetOutput(logger.Writer())
}

func TestWriterSplitNewlines(t *testing.T) {
buf := bytes.NewBuffer(nil)
logger := logrus.New()
logger.Formatter = &logrus.TextFormatter{
DisableColors: true,
DisableTimestamp: true,
}
logger.SetOutput(buf)
writer := logger.Writer()

const logNum = 10

for i := 0; i < logNum; i++ {
_, err := writer.Write([]byte("bar\nfoo\n"))
assert.NoError(t, err, "writer.Write failed")
}
writer.Close()
// Test is flaky because it writes in another goroutine,
// we need to make sure to wait a bit so all write are done.
time.Sleep(500 * time.Millisecond)

lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
assert.Len(t, lines, logNum*2, "logger printed incorrect number of lines")
}

func TestWriterSplitsMax64KB(t *testing.T) {
buf := bytes.NewBuffer(nil)
logger := logrus.New()
logger.Formatter = &logrus.TextFormatter{
DisableColors: true,
DisableTimestamp: true,
}
logger.SetOutput(buf)
writer := logger.Writer()

// write more than 64KB
const bigWriteLen = bufio.MaxScanTokenSize + 100
output := make([]byte, bigWriteLen)
// lets not write zero bytes
for i := 0; i < bigWriteLen; i++ {
output[i] = 'A'
}

for i := 0; i < 3; i++ {
len, err := writer.Write(output)
assert.NoError(t, err, "writer.Write failed")
assert.Equal(t, bigWriteLen, len, "bytes written")
}
writer.Close()
// Test is flaky because it writes in another goroutine,
// we need to make sure to wait a bit so all write are done.
time.Sleep(500 * time.Millisecond)

lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
// we should have 4 lines because we wrote more than 64 KB each time
assert.Len(t, lines, 4, "logger printed incorrect number of lines")
}