Compare commits

..

161 Commits

Author SHA1 Message Date
re 22f067d83b fix repos 2022-12-21 15:59:06 +03:00
re b06ab7d9aa upd 2022-12-21 15:31:06 +03:00
re e451092dba Update 'go.mod' 2022-12-12 14:27:58 +03:00
David Bariod f8bf7650dc
Merge pull request #1343 from sirupsen/dbd-upd-dep
update dependencies
2022-07-19 09:08:54 +02:00
David Bariod ebc9029252 update dependencies 2022-07-19 08:45:10 +02:00
Simon Eskildsen 56c843c73d
Merge pull request #1337 from izhakmo/fix-cve
update gopkg.in/yaml.v3 to v3.0.1
2022-06-13 07:17:07 -04:00
izhakmo 41b4ee686d update gopkg.in/yaml.v3 to v3.0.1 2022-06-06 18:41:45 +03:00
David Bariod f98ed3eb76
Merge pull request #1333 from nathanejohnson/bumpxsys
bump version of golang.org/x/sys dependency
2022-06-06 06:16:01 +02:00
Nathan Johnson 2b8f60a012 bump version of golangci-lint 2022-06-02 09:52:03 -05:00
Nathan Johnson 0db10ef84a bump version of golang.org/x/sys dependency
fixes #1332
2022-06-01 20:17:29 -05:00
Simon Eskildsen 85981c0459
Merge pull request #1263 from rubensayshi/fix-race 2022-01-12 18:45:10 -05:00
David Bariod 79c5ab66aa
Merge pull request #1283 from sirupsen/dbd-log-doc
Improve Log methods documentation
2021-09-12 16:09:16 +02:00
David Bariod 5f8c666a13 Improve Log methods documentation 2021-09-12 16:03:49 +02:00
David Bariod 5418b6e7a4
Merge pull request #1282 from sirupsen/dbd-ci-no-cross
reduce the list of cross build target
2021-09-12 16:02:09 +02:00
David Bariod 25e89b7d23 do not run the linter on windows 2021-09-12 15:59:08 +02:00
David Bariod f25cd754cf remove duplicated build constraints line 2021-09-12 15:58:50 +02:00
David Bariod 51f2599bdd reduce the list of cross build target 2021-09-12 15:52:09 +02:00
David Bariod accc7da667
Merge pull request #1277 from anajavi/patch-1
ci: add go 1.17 to test matrix
2021-09-12 08:16:54 +02:00
anajavi 0926db15e5
ci: run only on go 1.17 2021-09-12 08:49:26 +03:00
David Bariod 22d63b740b
Merge pull request #1281 from sirupsen/dbd-auto-stale-issues
indicates issues as stale automatically
2021-09-11 15:01:29 +02:00
David Bariod 526e535580 indicates issues as stale automatically 2021-09-11 15:00:32 +02:00
David Bariod b53d94c8ad
Merge pull request #1266 from runphp/patch-1
Update README.md
2021-09-11 14:47:43 +02:00
David Bariod de2d2027ff
Merge pull request #1280 from sirupsen/bug-1275
bump golang.org/x/sys depency version
2021-09-11 14:20:32 +02:00
David Bariod dff9872c76 bump golang.org/x/sys depency version 2021-09-11 14:09:47 +02:00
anajavi 15b98b1d72
ci: add go 1.17 to test matrix 2021-09-02 13:45:20 +03:00
heui f5f6a033d3
Update README.md 2021-06-24 09:52:04 +08:00
Ruben de Vries 78f838918d
fix race condition for SetFormatter and properly fix SetReportCaller race as well 2021-06-16 11:57:31 +02:00
David Bariod b50299cfaa
Merge pull request #1253 from edoger/buffer-pool
Add support for the logger private buffer pool.
2021-04-22 15:34:36 +02:00
Qingshan Luo 1818363d79 Add support for the logger private buffer pool. 2021-04-20 10:48:30 +08:00
David Bariod fdf1618bf7
Merge pull request #1249 from injustease/docs/badge
Change godoc badge to pkg.go.dev badge
2021-03-18 10:57:23 +01:00
Billy Zaelani Malik b1c1cea8f6 Change godoc badge to pkg.go.dev badge 2021-03-18 08:53:31 +07:00
David Bariod bde44a27f3
Merge pull request #1246 from thaJeztah/bump_testify
go.mod: github.com/stretchr/testify v1.7.0
2021-03-12 16:18:03 +01:00
Sebastiaan van Stijn 9b555f4fd7
go.mod: github.com/stretchr/testify v1.7.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-12 15:51:52 +01:00
David Bariod fe9e9fcbba
Merge pull request #1239 from thaJeztah/move_to_gha
CI: use GitHub Actions
2021-03-09 13:46:41 +01:00
David Bariod bdc0db8ead
Merge pull request #1244 from sirupsen/dbd-release
update changelog
2021-03-09 11:28:17 +01:00
David Bariod 1bfef4b986 update changelog 2021-03-09 11:27:33 +01:00
David Bariod 7a997b9285 improve documentation about timestamp format 2021-03-08 18:02:30 +01:00
Sebastiaan van Stijn cbd14ede4d
CI: use GitHub Actions
Use GitHub actions to run golang-ci-lint, cross, and test.

The "Test()" target in Mage was a plain "go test -v ./...", and should be
portable to other CI systems if needed; running it through the mage file
effectively resulted in "compile a go binary to run go".

The "Lint()" target in Mage relied on Travis CI setting up Golang-CI lint
before it was executed, which required bash. Moving it to GitHub actions
also allowed it to be run on Windows. Golang CI can still be run locally
either through the mage file (which is kept for now), or running
`golangci-lint run ./...` after installing golangci-lint.

The "CrossBuild() Mage target is still used to perform cross-compile, as it
contains code to generate the GOOS/GOARCH matrix, which makes it easier
to run locally.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-19 14:17:47 +01:00
Sebastiaan van Stijn bdb7d4c531
go.mod: update golang.org/x/sys to fix openbsd/mips64 on Go 1.16
This should hopefully fix cross-compile on openbsd/mips64 on Go 1.16

    Building for GOOS=openbsd GOARCH=mips64
    # golang.org/x/sys/unix
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/fcntl.go:26:42: undefined: Flock_t
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/ioctl.go:26:47: undefined: Winsize
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/ioctl.go:37:47: undefined: Termios
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/ioctl.go:55:42: undefined: Winsize
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/ioctl.go:61:42: undefined: Termios
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/syscall_openbsd.go:34:6: missing function body
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/syscall_unix_gc.go:12:6: missing function body
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/syscall_unix_gc.go:13:6: missing function body
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/syscall_unix_gc.go:14:6: missing function body
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/syscall_unix_gc.go:15:6: missing function body
    Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20191026070338-33540a1f6037/unix/ioctl.go:61:42: too many errors

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-19 13:57:50 +01:00
David Bariod f104497f2b
Merge pull request #1238 from thaJeztah/move_mage
CI: run mage with "-v" to not discard output, and move mage to a submodule
2021-02-19 13:54:12 +01:00
Sebastiaan van Stijn 1d8091a7e9
move "mage" to a separate module
Move the magefile related files to a submodule, so that it
does not become a dependency for logrus itself.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-19 12:47:40 +01:00
Sebastiaan van Stijn feebf74e97
travis: run mage with -v to not discard output
Before this:

    $ go run mage.go lint
    Error: running "/Users/sebastiaan/go/bin/golangci-lint run ./..." failed with exit code 1
    exit status 1

    $ go run mage.go test
    Error: running "go test -race -v ./..." failed with exit code 1
    exit status 1

After this:

    $ go run mage.go -v lint
    Running target: Lint
    exec: /Users/sebastiaan/go/bin/golangci-lint run ./...
    entry.go:89:6: `iamNotUsed` is unused (deadcode)
    func iamNotUsed() {
         ^
    Error: running "/Users/sebastiaan/go/bin/golangci-lint run ./..." failed with exit code 1
    exit status 1

    $ go run mage.go -v test
    Running target: Test
    exec: go test -race -v ./...
    === RUN   TestRegister
    ...
    ?   	github.com/sirupsen/logrus/internal/testutils	[no test files]
    FAIL
    Error: running "go test -race -v ./..." failed with exit code 1
    exit status 1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-19 12:42:03 +01:00
David Bariod 6cff360233
Merge pull request #1234 from sirupsen/dbd-cleanup
fix race condition AddHook and traces
2021-02-18 08:49:52 +01:00
David Bariod d172886045 fix race condition AddHook and traces 2021-02-17 18:14:05 +01:00
David Bariod d59e5619da
Merge pull request #1231 from sirupsen/dbd-cleanup
cleanup
2021-02-17 17:48:57 +01:00
David Bariod 35ab8d8fef update changelog 2021-02-16 18:18:17 +01:00
David Bariod 5cb4bf65c6 code and comments clean up 2021-02-16 18:13:03 +01:00
David Bariod 15ca3c0694
Merge pull request #1230 from sirupsen/new-release
update changelog
2021-02-16 11:21:45 +01:00
David Bariod 67c117ceb0 update changelog 2021-02-16 11:20:51 +01:00
David Bariod 88d56b69b5
Merge pull request #1229 from sirupsen/fix-data-entry-race
fix for entry data field race condition
2021-02-16 11:14:48 +01:00
David Bariod ac6e35b4c2 fix for entry data field race condition 2021-02-16 10:31:51 +01:00
David Bariod f513f99c15
Merge pull request #1228 from sirupsen/travis_upd
update travis scripts
2021-02-14 16:57:45 +01:00
David Bariod 003c63ac69 undo golang version bump 2021-02-14 16:46:49 +01:00
David Bariod 6a61186a64 update travis scripts
* bump to go 1.16
* remove unneeded part in travis/install.sh
2021-02-13 07:07:38 +01:00
David Bariod c6da0523dd
Merge pull request #1208 from sirupsen/magefile
migrate ci target from bash scripts to magefile
2021-02-13 07:05:01 +01:00
David Bariod 3986c92379 add a test target in the magefile 2021-02-13 06:45:48 +01:00
David Bariod cd4bf4ef8d
Merge pull request #1212 from alecbz/alec/remove-dead-panic
Remove dead panic in Entry.Panic
2020-12-17 17:18:52 +01:00
David Bariod 44d983dbc2
Merge pull request #1185 from CreativeCactus/patch-1
Replace %v with %w for error
2020-12-17 16:44:23 +01:00
David Bariod b02b418f8f migrate lint script to a mage target 2020-12-17 15:58:09 +01:00
Alec Benzer 02fcb16005 Remove dead panic in Entry.Panic
[Entry.log itself panics][0] when the log level is set to PanicLevel, (and
PanicLevel is always eneabled) so this second panic will never be reached.

[0]: 8ae478eb8a/entry.go (L253)
2020-12-15 18:25:34 -05:00
David Bariod 4b818a50d4 migrate cross build target from bash script to mage 2020-11-26 06:33:19 +01:00
David Bariod 8ae478eb8a
Merge pull request #1197 from sirupsen/travis_build
bump go and golangci-lint versions in travis ci
2020-11-26 06:17:10 +01:00
David Bariod 6121f5c019
Merge pull request #1207 from sirupsen/remove_stale_bot
desactivate stale bot
2020-11-24 10:53:45 +01:00
David Bariod e3e79b6306 desactivate stale bot 2020-11-24 08:47:25 +01:00
David Bariod a752a62f5e
Merge pull request #1206 from l-lindsay/zosbuild
Add build tag to enable a successful build for zos
2020-11-21 19:30:40 +01:00
l-lindsay be16a81096 Add build tag to enable a successful build for zos 2020-11-20 14:25:07 -05:00
David Bariod 89b92b94dd one more linter error fixed 2020-11-08 07:19:28 +01:00
David Bariod e328a4e3f4 fix linter errors 2020-11-08 07:07:05 +01:00
David Bariod 581900062e bump golangci-lint version 2020-11-06 13:26:15 +01:00
David Bariod 0d28e29335 bump golang versions in travis ci 2020-11-06 13:14:07 +01:00
CreativeCactus c81a54c5aa
Replace %v with %w for error
https://golang.org/pkg/fmt/#Errorf
2020-10-01 17:36:44 +10:00
David Bariod d131c24e23
Merge pull request #1183 from shogo82148/update-changelog-1.7.0
update changelog with v1.7.0
2020-09-30 13:26:25 +02:00
Ichinose Shogo c6b865f1d2
update changelog with v1.7.0 2020-09-29 15:47:36 +09:00
David Bariod 6699a89a23
Merge pull request #1145 from sirupsen/custom_buffer_pool
Add an API to plug a custom buffer free item mangement system
2020-05-28 10:56:38 +02:00
David Bariod 64a59449f3 Add an API to plug a custom buffer free item mangement system 2020-05-28 10:47:50 +02:00
David Bariod 42baed85eb
Merge pull request #1144 from sohel-sheikh/patch-1
Update doc for new logger
2020-05-26 09:04:19 +02:00
Sohel 630ea450e6
Update doc for new logger
Update default formatter for new logger from JsonFormatter to TextFormatter
2020-05-26 10:26:28 +05:30
David Bariod 20dcf91051
Merge pull request #1142 from sirupsen/function-log
Function log
2020-05-19 17:10:51 +02:00
David Bariod ba4da53cff Improve tests for logger.*Fn functions 2020-05-19 17:02:33 +02:00
David Bariod d7edea4451 Merge branch 'feature/function-log' of https://github.com/Azer0s/logrus into Azer0s-feature/function-log 2020-05-19 16:58:31 +02:00
David Bariod e8fa988410
Merge pull request #1077 from dmgk/master
Add support for freebsd/arm64
2020-05-19 16:53:15 +02:00
David Bariod 0de04f1584
Merge pull request #1088 from tklauser/simplify-windows
Simplify checkIfTerminal for Windows
2020-05-19 16:51:28 +02:00
Tobias Klauser 86657918d4 Simplify checkIfTerminal for Windows
Instead of relying on EnableVirtualTerminalProcessing from
github.com/konsorten/go-windows-terminal-sequences which just calls
GetConsoleMode, sets ENABLE_VIRTUAL_TERMINAL_PROCESSING and calls
SetConsoleMode with the new modified mode, implement it directly inside
checkIfTerminal. This also avoids the duplicate call to GetConsoleMode.
2020-05-06 15:58:24 +02:00
David Bariod 60c74ad9be update CHANGELOG.md with 1.5.0 and 1.6.0 version contents 2020-05-02 15:06:20 +02:00
David Bariod e8e563a823 Merge remote-tracking branch 'origin/master' into thlacroix-disable-quotes 2020-04-29 09:21:39 +02:00
David Bariod 0fd458a22e complete documetation on TextFormatter.DisableQuote 2020-04-29 09:15:21 +02:00
David Bariod 4d96c600d9 Merge branch 'disable-quotes' of https://github.com/thlacroix/logrus into thlacroix-disable-quotes 2020-04-29 09:10:49 +02:00
David Bariod a5b02471f8
Merge pull request #1136 from ialidzhikov/nit/line-endings
Change CRLF line endings to LF
2020-04-29 09:09:56 +02:00
David Bariod 163c051d4a
Merge pull request #1137 from sirupsen/fix_crash_windows
update github.com/konsorten/go-windows-terminal-sequences dependency …
2020-04-29 09:07:31 +02:00
David Bariod e79215d3d5 update github.com/konsorten/go-windows-terminal-sequences dependency to v1.0.3 2020-04-29 08:56:01 +02:00
ialidzhikov 4989a3fd5d Change CRLF line endings to LF
Signed-off-by: ialidzhikov <i.alidjikov@gmail.com>
2020-04-29 00:25:29 +03:00
Thomas Lacroix aff00feb0a Adds additional test cases for DisableQuote 2020-04-23 17:16:50 +02:00
Thomas Lacroix c7455de10a Adds flag to disable quotes in TextFormatter 2020-04-23 14:02:38 +02:00
Mark Phelps 91ef3ab5d5
Merge pull request #1131 from sirupsen/revert-1047
Revert #1047
2020-04-16 11:24:31 -04:00
Mark Phelps 03155c5499 Revert #1047 2020-04-16 11:13:51 -04:00
Ariel Simulevski 7d248fa1b1
Add loggers that take functions as input 2020-04-10 12:42:19 +02:00
Mark Phelps d417be0fe6
Merge pull request #1108 from cirelli94/fix-wrong-caller
Fix wrong caller
2020-03-22 09:43:59 -04:00
Mark Phelps 32fd107816
Merge pull request #1103 from sirupsen/fix-go114
fix logrus for go 1.14
2020-03-22 09:36:45 -04:00
Mark Phelps ddb57a2a54
Merge pull request #1110 from Dattax/patch-1
Title updates
2020-03-22 09:32:25 -04:00
Mark Phelps a635f0489d
Merge pull request #1113 from davidraleigh/html-escape
resolved conflicts for DisableHTMLEscape in json_formatter.go pull request #524
2020-03-22 09:27:13 -04:00
Mark Phelps 4ddc9cf62e
Merge pull request #1116 from admacleod/master
Resolve race condition with SetReportCaller() and Entry
2020-03-22 09:24:01 -04:00
Alisdair MacLeod e3e40605a2 remove errant whitespace 2020-03-19 10:02:20 +00:00
Alisdair MacLeod ba670baee1 fix deadlock in previous entry race condition fix 2020-03-19 10:01:29 +00:00
Alisdair MacLeod b28acda22d fix race condition in entry 2020-03-19 09:32:08 +00:00
Alisdair MacLeod e76a5c4450 create test to prove issue sirupsen/logrus#954 2020-03-19 09:29:19 +00:00
David Raleigh 0fb945b034 resolved conflicts 2020-03-13 13:22:47 -04:00
Deep Datta 0882384258
Title updates
Removed the non-breaking spaces in the ReadMe
2020-03-11 17:26:45 -07:00
Fabrizio Cirelli fa25593b15 Removed useless files 2020-03-09 14:45:58 +01:00
Fabrizio Cirelli af6ac8cee6 Fix wrong caller 2020-03-09 12:39:00 +01:00
David Bariod 7ea96a3284
Merge pull request #1102 from hlcfan/get-right-logrus-pkg-name
Fix caller package name
2020-03-06 11:24:46 +01:00
Mark Phelps 53000c4c0f
Merge pull request #921 from sosiska/patch-1
Rewrite if-else-if-else to switch statements
2020-03-04 09:42:50 -05:00
Mark Phelps 334dd7729c
Merge pull request #1060 from sirupsen/improve_withfield_doc
improve Logger.WithField documentation
2020-03-04 09:32:56 -05:00
Mark Phelps 1e936e2d75
Merge pull request #1091 from nolleh/master
add caption-json-formatter
2020-03-04 09:14:34 -05:00
David Bariod ab4d0e6ead run CI for go 1.13 and 1.14 2020-03-03 09:10:14 +01:00
Alex S 86a84a9d18 Get right logrus package name 2020-03-02 21:47:38 +08:00
Mark Phelps 77ab282a06
Merge pull request #1047 from lwsanty/fix-race-conditions-on-entry
fix race conditions on entry
2020-02-26 18:44:58 -05:00
Mark Phelps 7fab003954
Merge pull request #633 from bogem/patch-1
Fix typos in docs for New()
2020-02-26 18:21:58 -05:00
Mark Phelps 494ec951d1
Only mark issues as stale for now until we go through backlog of PRs
Only mark issues as stale for now until we go through backlog of PRs
2020-02-26 18:18:33 -05:00
Sébastien Lavoie 24566a3fc4
Merge pull request #924 from bunyk/master
Add hook to send logs to custom writer #678
2020-02-26 17:31:00 -05:00
Mark Phelps f5d95b63a6
Update stale.yml 2020-02-26 12:01:50 -05:00
Mark Phelps eef122e96e
Create stale.yml 2020-02-26 11:58:33 -05:00
Mark Phelps 2f069ddd45
Merge pull request #1072 from taywrobel/bugfix/dataBleedAcrossEntities
Fix entry data bleed when using WithContext and WithTime
2020-02-25 12:31:25 -05:00
Mark Phelps 63d9911443
Merge pull request #1082 from hlcfan/patch-1
Fix typo
2020-02-25 10:53:46 -05:00
Simon Eskildsen 4ecd9a62bd readme: maintenance-mode 2020-02-25 07:40:03 -05:00
Simon Eskildsen 947831125f
Merge pull request #1094 from devil418/fix-readme
Remove annoying punctuation in Readme for better screen reader accesssibility
2020-02-04 17:46:15 -05:00
Mikolaj Holysz 6895a36b17 Remove annoying punctuation in Readme for better screen reader accessibility.
One entry in the Logrus formatters list in the readme contained
a lot of extraneous punctuation.

When read with a screen reader, nothing but a bunch of question marks and weird symbol names could be heard,
making the line impossible to understand.
2020-01-28 19:39:15 +01:00
nolleh 779e0e214d add caption-json-formatter 2020-01-21 00:42:07 +09:00
Alex Shi b70d15e202
Fix typo 2019-12-18 14:10:06 +08:00
Dmitri Goutnik 28e212178a
Add support for freebsd/arm64 2019-12-09 07:44:42 -05:00
Taylor Wrobel 8fbaf3dbd0 Make Entry WithContext and WithTime copy tests more clear
Clarifies the data used in the EntryWithContextCopiesData test and
adds an equivalent test to verify the behavior of WithTime.
2019-12-03 13:50:59 -08:00
Taylor Wrobel bcc146f96b Fix entity data bleed when using WithContext and WithTime
Creates a copy of the data map when using WithContext to create a
child entity.  Without this, the data map of the parent entitiy,
which is exposed in the entity struct, is shared between a parent
and all children instances.

This can create bugs of shared or overwritten data when a parent
entity is used to make children in differing contexts, and behaves
differently than `WithField` and its diritivites which does make
a copy of the data.

Additionally implements the same logic for WithTime, for API
consistency in behavior.
2019-11-27 20:20:42 -08:00
David Bariod 4fd274e0b8 improve Logger.WithField documentation 2019-10-28 19:22:23 +01:00
David Bariod 67a7fdcf74
Merge pull request #1054 from sirupsen/del_old_doc
remove obsolete documentation
2019-10-26 13:39:18 +02:00
David Bariod 9746113fa8 remove obsolete documentation 2019-10-26 08:50:02 +02:00
David Bariod f4ece9c82f
Merge pull request #1052 from sirupsen/activate_linter
run golangci-lint on travis
2019-10-25 17:13:36 +02:00
David Bariod 12176f2f72
Merge pull request #1053 from sirupsen/travis_rm_go111
remove go1.11.x from travis build matrix
2019-10-25 17:12:32 +02:00
David Bariod 9df6f6aa0b add x rights on travis/lint.sh 2019-10-25 14:57:57 +02:00
David Bariod 88d44306be remove go1.11.x from travis build matrix 2019-10-25 14:53:19 +02:00
David Bariod b77b626665 run golangci-lint on travis 2019-10-25 14:49:48 +02:00
lwsanty c7278b2d7a fix race conditions on entry
closes #1046
2019-10-23 20:55:57 +03:00
Edward Muller d5d4df1108
Merge pull request #1040 from sirupsen/ffz/Travis
Clean up travis
2019-10-22 18:09:08 -07:00
Edward Muller b6a9e5632b
Merge pull request #1042 from sirupsen/ffz/ForceQuote
ForceQuote option to TextFormatter
2019-10-22 18:08:39 -07:00
Edward Muller 007cacdd34
Force Quote
Closed #1005
2019-10-14 22:53:51 -07:00
Edward Muller fb62dbe2f2
fix broken test 2019-10-14 13:23:44 -07:00
Edward Muller 843e0aaa75
Merge pull request #1016 from freeformz/ffz/returnEarly
return early
2019-10-14 11:32:52 -07:00
Edward Muller 68e6dbbcb7
Exclude go1.13.x from modules off, only build all on go1.13 modules on 2019-10-13 17:30:04 -07:00
Edward Muller 08cf62cb80
This should make gox a little nicer 2019-10-13 17:15:32 -07:00
Edward Muller ad9f41a0cd
pull all the install into a single location 2019-10-13 16:56:28 -07:00
Edward Muller 75440f2ebe
get some other deps 2019-10-13 16:51:57 -07:00
Edward Muller 8ec9a493ec
Enable all of these to see what fails 2019-10-13 16:47:33 -07:00
Edward Muller d30efdb30d
go mod verify; go mod tidy 2019-10-13 16:39:54 -07:00
Edward Muller 7b6c0d11ad
Disable modules, run on osx 2019-10-13 16:39:38 -07:00
Edward Muller 60320cbc2c
return early
This makes it easier to read / understand and is more idiomatic.
2019-09-05 16:09:16 -07:00
tbunyk 8b0b8a88f2 Merge remote-tracking branch 'upstream/master' 2019-09-03 15:29:19 +03:00
tbunyk c88f8de1fe Add Bytes() method to Entry, and use it to avoid double type cast 2019-03-18 16:07:31 +02:00
tbunyk 470f2e08fc Fix some test conditions 2019-03-15 13:19:10 +02:00
tbunyk d8e3add56f Add hook to send logs to custom writer #678 2019-03-15 13:09:11 +02:00
Kirill Motkov 1487633cc3 Rewrite if-else-if-else to switch statements 2019-03-13 12:38:09 +03:00
Albert Nigmatzianov 6137e6b13d Fix typo in docs for New() 2017-09-02 15:05:25 +05:00
Albert Nigmatzianov e59e5eaa92 Fix typo in docs for New() 2017-09-02 15:04:38 +05:00
Albert Nigmatzianov 1fb8c53680 Fix typo in docs for New() 2017-09-02 15:03:37 +05:00
48 changed files with 1159 additions and 234 deletions

61
.github/workflows/ci.yaml vendored Normal file
View File

@ -0,0 +1,61 @@
name: CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
lint:
name: Golang-CI Lint
timeout-minutes: 10
strategy:
matrix:
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: golangci/golangci-lint-action@v2
with:
# must be specified without patch version
version: v1.46
cross:
name: Cross
timeout-minutes: 10
strategy:
matrix:
go-version: [1.17.x]
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Cross
working-directory: ci
run: go run mage.go -v -w ../ crossBuild
test:
name: Unit test
timeout-minutes: 10
strategy:
matrix:
go-version: [1.17.x]
platform: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Test
run: go test -race -v ./...

22
.github/workflows/stale.yaml vendored Normal file
View File

@ -0,0 +1,22 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v3
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
logrus logrus
vendor vendor
.idea/

40
.golangci.yml Normal file
View File

@ -0,0 +1,40 @@
run:
# do not run on test files yet
tests: false
# all available settings of specific linters
linters-settings:
errcheck:
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
# default is false: such cases aren't reported by default.
check-type-assertions: false
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: false
lll:
line-length: 100
tab-width: 4
prealloc:
simple: false
range-loops: false
for-loops: false
whitespace:
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
linters:
enable:
- megacheck
- govet
disable:
- maligned
- prealloc
disable-all: false
presets:
- bugs
- unused
fast: false

View File

@ -1,25 +1,15 @@
language: go language: go
go_import_path: github.com/sirupsen/logrus go_import_path: git.internal/re/logrus
git: git:
depth: 1 depth: 1
env: env:
- GO111MODULE=on - GO111MODULE=on
- GO111MODULE=off go: 1.15.x
go: [ 1.11.x, 1.12.x, 1.13.x ] os: linux
os: [ linux, osx ]
matrix:
exclude:
- go: 1.12.x
env: GO111MODULE=off
- go: 1.11.x
os: osx
install: install:
- ./travis/install.sh - ./travis/install.sh
- if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi
- if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi
script: script:
- ./travis/cross_build.sh - cd ci
- export GOMAXPROCS=4 - go run mage.go -v -w ../ crossBuild
- export GORACE=halt_on_error=1 - go run mage.go -v -w ../ lint
- go test -race -v ./... - go run mage.go -v -w ../ test
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi

View File

@ -1,9 +1,68 @@
# 1.8.1
Code quality:
* move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer
* improve timestamp format documentation
Fixes:
* fix race condition on logger hooks
# 1.8.0
Correct versioning number replacing v1.7.1.
# 1.7.1
Beware this release has introduced a new public API and its semver is therefore incorrect.
Code quality:
* use go 1.15 in travis
* use magefile as task runner
Fixes:
* small fixes about new go 1.13 error formatting system
* Fix for long time race condiction with mutating data hooks
Features:
* build support for zos
# 1.7.0
Fixes:
* the dependency toward a windows terminal library has been removed
Features:
* a new buffer pool management API has been added
* a set of `<LogLevel>Fn()` functions have been added
# 1.6.0
Fixes:
* end of line cleanup
* revert the entry concurrency bug fix whic leads to deadlock under some circumstances
* update dependency on go-windows-terminal-sequences to fix a crash with go 1.14
Features:
* add an option to the `TextFormatter` to completely disable fields quoting
# 1.5.0
Code quality:
* add golangci linter run on travis
Fixes:
* add mutex for hooks concurrent access on `Entry` data
* caller function field for go1.14
* fix build issue for gopherjs target
Feature:
* add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level
* add a `DisableHTMLEscape` option in the `JSONFormatter`
* add `ForceQuote` and `PadLevelText` options in the `TextFormatter`
# 1.4.2 # 1.4.2
* Fixes build break for plan9, nacl, solaris * Fixes build break for plan9, nacl, solaris
# 1.4.1 # 1.4.1
This new release introduces: This new release introduces:
* Enhance TextFormatter to not print caller information when they are empty (#944) * Enhance TextFormatter to not print caller information when they are empty (#944)
* Remove dependency on golang.org/x/crypto (#932, #943) * Remove dependency on golang.org/x/crypto (#932, #943)
Fixes: Fixes:
* Fix Entry.WithContext method to return a copy of the initial entry (#941) * Fix Entry.WithContext method to return a copy of the initial entry (#941)
@ -11,7 +70,7 @@ Fixes:
# 1.4.0 # 1.4.0
This new release introduces: This new release introduces:
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911) * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911)
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919). * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
Fixes: Fixes:

View File

@ -1,19 +1,39 @@
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus)&nbsp;[![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus) # Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://git.internal/re/logrus/workflows/CI/badge.svg)](https://git.internal/re/logrus/actions?query=workflow%3ACI) [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![Go Reference](https://pkg.go.dev/badge/git.internal/re/logrus.svg)](https://pkg.go.dev/git.internal/re/logrus)
Logrus is a structured logger for Go (golang), completely API compatible with Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger. the standard library logger.
**Logrus is in maintenance-mode.** We will not be introducing new features. It's
simply too hard to do in a way that won't break many people's projects, which is
the last thing you want from your Logging library (again...).
This does not mean Logrus is dead. Logrus will continue to be maintained for
security, (backwards compatible) bug fixes, and performance (where we are
limited by the interface).
I believe Logrus' biggest contribution is to have played a part in today's
widespread use of structured logging in Golang. There doesn't seem to be a
reason to do a major, breaking iteration into Logrus V2, since the fantastic Go
community has built those independently. Many fantastic alternatives have sprung
up. Logrus would look like those, had it been re-designed with what we know
about structured logging in Go today. Check out, for example,
[Zerolog][zerolog], [Zap][zap], and [Apex][apex].
[zerolog]: https://github.com/rs/zerolog
[zap]: https://github.com/uber-go/zap
[apex]: https://github.com/apex/log
**Seeing weird case-sensitive problems?** It's in the past been possible to **Seeing weird case-sensitive problems?** It's in the past been possible to
import Logrus as both upper- and lower-case. Due to the Go package environment, import Logrus as both upper- and lower-case. Due to the Go package environment,
this caused issues in the community and we needed a standard. Some environments this caused issues in the community and we needed a standard. Some environments
experienced problems with the upper-case variant, so the lower-case was decided. experienced problems with the upper-case variant, so the lower-case was decided.
Everything using `logrus` will need to use the lower-case: Everything using `logrus` will need to use the lower-case:
`github.com/sirupsen/logrus`. Any package that isn't, should be changed. `git.internal/re/logrus`. Any package that isn't, should be changed.
To fix Glide, see [these To fix Glide, see [these
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). comments](https://git.internal/re/logrus/issues/553#issuecomment-306591437).
For an in-depth explanation of the casing issue, see [this For an in-depth explanation of the casing issue, see [this
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276). comment](https://git.internal/re/logrus/issues/570#issuecomment-313933276).
Nicely color-coded in development (when a TTY is attached, otherwise just Nicely color-coded in development (when a TTY is attached, otherwise just
plain text): plain text):
@ -89,7 +109,7 @@ go test -bench=.*CallerTracing
The organization's name was changed to lower-case--and this will not be changed The organization's name was changed to lower-case--and this will not be changed
back. If you are getting import conflicts due to case sensitivity, please use back. If you are getting import conflicts due to case sensitivity, please use
the lower-case import: `github.com/sirupsen/logrus`. the lower-case import: `git.internal/re/logrus`.
#### Example #### Example
@ -99,7 +119,7 @@ The simplest way to use Logrus is simply the package-level exported logger:
package main package main
import ( import (
log "github.com/sirupsen/logrus" log "git.internal/re/logrus"
) )
func main() { func main() {
@ -110,7 +130,7 @@ func main() {
``` ```
Note that it's completely api-compatible with the stdlib logger, so you can Note that it's completely api-compatible with the stdlib logger, so you can
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"` replace your `log` imports everywhere with `log "git.internal/re/logrus"`
and you'll now have the flexibility of Logrus. You can customize it all you and you'll now have the flexibility of Logrus. You can customize it all you
want: want:
@ -119,7 +139,7 @@ package main
import ( import (
"os" "os"
log "github.com/sirupsen/logrus" log "git.internal/re/logrus"
) )
func init() { func init() {
@ -170,7 +190,7 @@ package main
import ( import (
"os" "os"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
) )
// Create a new instance of the logger. You can have any number of instances. // Create a new instance of the logger. You can have any number of instances.
@ -245,9 +265,9 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
```go ```go
import ( import (
log "github.com/sirupsen/logrus" log "git.internal/re/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake" "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" logrus_syslog "git.internal/re/logrus/hooks/syslog"
"log/syslog" "log/syslog"
) )
@ -267,7 +287,7 @@ func init() {
``` ```
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) A list of currently known service hooks can be found in this wiki [page](https://git.internal/re/logrus/wiki/Hooks)
#### Level logging #### Level logging
@ -318,10 +338,10 @@ could do:
```go ```go
import ( import (
log "github.com/sirupsen/logrus" log "git.internal/re/logrus"
) )
init() { func init() {
// do something here to set environment depending on an environment variable // do something here to set environment depending on an environment variable
// or command-line flag // or command-line flag
if Environment == "production" { if Environment == "production" {
@ -350,9 +370,9 @@ The built-in logging formatters are:
* When colors are enabled, levels are truncated to 4 characters by default. To disable * When colors are enabled, levels are truncated to 4 characters by default. To disable
truncation set the `DisableLevelTruncation` field to `true`. truncation set the `DisableLevelTruncation` field to `true`.
* When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text. * When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text.
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter). * All options are listed in the [generated docs](https://godoc.org/git.internal/re/logrus#TextFormatter).
* `logrus.JSONFormatter`. Logs fields as JSON. * `logrus.JSONFormatter`. Logs fields as JSON.
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter). * All options are listed in the [generated docs](https://godoc.org/git.internal/re/logrus#JSONFormatter).
Third party logging formatters: Third party logging formatters:
@ -360,9 +380,10 @@ Third party logging formatters:
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html). * [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. * [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. * [`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 P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. * [`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. * [`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; Sava 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, You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
@ -381,7 +402,7 @@ func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
// source of the official loggers. // source of the official loggers.
serialized, err := json.Marshal(entry.Data) serialized, err := json.Marshal(entry.Data)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
} }
return append(serialized, '\n'), nil return append(serialized, '\n'), nil
} }
@ -439,8 +460,8 @@ Logrus has a built in facility for asserting the presence of log messages. This
```go ```go
import( import(
"github.com/sirupsen/logrus" "git.internal/re/logrus"
"github.com/sirupsen/logrus/hooks/test" "git.internal/re/logrus/hooks/test"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"testing" "testing"
) )

View File

@ -1,14 +1,14 @@
version: "{build}" version: "{build}"
platform: x64 platform: x64
clone_folder: c:\gopath\src\github.com\sirupsen\logrus clone_folder: c:\gopath\src\github.com\sirupsen\logrus
environment: environment:
GOPATH: c:\gopath GOPATH: c:\gopath
branches: branches:
only: only:
- master - master
install: install:
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH% - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- go version - go version
build_script: build_script:
- go get -t - go get -t
- go test - go test

43
buffer_pool.go Normal file
View File

@ -0,0 +1,43 @@
package logrus
import (
"bytes"
"sync"
)
var (
bufferPool BufferPool
)
type BufferPool interface {
Put(*bytes.Buffer)
Get() *bytes.Buffer
}
type defaultPool struct {
pool *sync.Pool
}
func (p *defaultPool) Put(buf *bytes.Buffer) {
p.pool.Put(buf)
}
func (p *defaultPool) Get() *bytes.Buffer {
return p.pool.Get().(*bytes.Buffer)
}
// SetBufferPool allows to replace the default logrus buffer pool
// to better meets the specific needs of an application.
func SetBufferPool(bp BufferPool) {
bufferPool = bp
}
func init() {
SetBufferPool(&defaultPool{
pool: &sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
},
})
}

5
ci/go.mod Normal file
View File

@ -0,0 +1,5 @@
module git.internal/re/logrus/ci
go 1.15
require github.com/magefile/mage v1.11.0

2
ci/go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/magefile/mage v1.11.0 h1:C/55Ywp9BpgVVclD3lRnSYCwXTYxmSppIgLeDYlNuls=
github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=

10
ci/mage.go Normal file
View File

@ -0,0 +1,10 @@
// +build ignore
package main
import (
"github.com/magefile/mage/mage"
"os"
)
func main() { os.Exit(mage.Main()) }

123
ci/magefile.go Normal file
View File

@ -0,0 +1,123 @@
//go:build mage
package main
import (
"encoding/json"
"fmt"
"os"
"path"
"sort"
"github.com/magefile/mage/mg"
"github.com/magefile/mage/sh"
)
func intersect(a, b []string) []string {
sort.Strings(a)
sort.Strings(b)
res := make([]string, 0, func() int {
if len(a) < len(b) {
return len(a)
}
return len(b)
}())
for _, v := range a {
idx := sort.SearchStrings(b, v)
if idx < len(b) && b[idx] == v {
res = append(res, v)
}
}
return res
}
// getBuildMatrix returns the build matrix from the current version of the go compiler
func getFullBuildMatrix() (map[string][]string, error) {
jsonData, err := sh.Output("go", "tool", "dist", "list", "-json")
if err != nil {
return nil, err
}
var data []struct {
Goos string
Goarch string
}
if err := json.Unmarshal([]byte(jsonData), &data); err != nil {
return nil, err
}
matrix := map[string][]string{}
for _, v := range data {
if val, ok := matrix[v.Goos]; ok {
matrix[v.Goos] = append(val, v.Goarch)
} else {
matrix[v.Goos] = []string{v.Goarch}
}
}
return matrix, nil
}
func getBuildMatrix() (map[string][]string, error) {
minimalMatrix := map[string][]string{
"linux": []string{"amd64"},
"darwin": []string{"amd64", "arm64"},
"freebsd": []string{"amd64"},
"js": []string{"wasm"},
"solaris": []string{"amd64"},
"windows": []string{"amd64", "arm64"},
}
fullMatrix, err := getFullBuildMatrix()
if err != nil {
return nil, err
}
for os, arches := range minimalMatrix {
if fullV, ok := fullMatrix[os]; !ok {
delete(minimalMatrix, os)
} else {
minimalMatrix[os] = intersect(arches, fullV)
}
}
return minimalMatrix, nil
}
func CrossBuild() error {
matrix, err := getBuildMatrix()
if err != nil {
return err
}
for os, arches := range matrix {
for _, arch := range arches {
env := map[string]string{
"GOOS": os,
"GOARCH": arch,
}
if mg.Verbose() {
fmt.Printf("Building for GOOS=%s GOARCH=%s\n", os, arch)
}
if err := sh.RunWith(env, "go", "build", "./..."); err != nil {
return err
}
}
}
return nil
}
func Lint() error {
gopath := os.Getenv("GOPATH")
if gopath == "" {
return fmt.Errorf("cannot retrieve GOPATH")
}
return sh.Run(path.Join(gopath, "bin", "golangci-lint"), "run", "./...")
}
// Run the test suite
func Test() error {
return sh.RunWith(map[string]string{"GORACE": "halt_on_error=1"},
"go", "test", "-race", "-v", "./...")
}

28
doc.go
View File

@ -1,26 +1,26 @@
/* /*
Package logrus is a structured logger for Go, completely API compatible with the standard library logger. Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
The simplest way to use Logrus is simply the package-level exported logger: The simplest way to use Logrus is simply the package-level exported logger:
package main package main
import ( import (
log "github.com/sirupsen/logrus" log "git.internal/re/logrus"
) )
func main() { func main() {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"animal": "walrus", "animal": "walrus",
"number": 1, "number": 1,
"size": 10, "size": 10,
}).Info("A walrus appears") }).Info("A walrus appears")
} }
Output: Output:
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
For a full guide visit https://github.com/sirupsen/logrus time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
For a full guide visit https://git.internal/re/logrus
*/ */
package logrus package logrus

135
entry.go
View File

@ -13,7 +13,6 @@ import (
) )
var ( var (
bufferPool *sync.Pool
// qualified package name, cached at first use // qualified package name, cached at first use
logrusPackage string logrusPackage string
@ -31,12 +30,6 @@ const (
) )
func init() { func init() {
bufferPool = &sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
// start at the bottom of the stack before the package-name cache is primed // start at the bottom of the stack before the package-name cache is primed
minimumCallerDepth = 1 minimumCallerDepth = 1
} }
@ -85,10 +78,23 @@ func NewEntry(logger *Logger) *Entry {
} }
} }
func (entry *Entry) Dup() *Entry {
data := make(Fields, len(entry.Data))
for k, v := range entry.Data {
data[k] = v
}
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
}
// Returns the bytes representation of this entry from the formatter.
func (entry *Entry) Bytes() ([]byte, error) {
return entry.Logger.Formatter.Format(entry)
}
// Returns the string representation from the reader and ultimately the // Returns the string representation from the reader and ultimately the
// formatter. // formatter.
func (entry *Entry) String() (string, error) { func (entry *Entry) String() (string, error) {
serialized, err := entry.Logger.Formatter.Format(entry) serialized, err := entry.Bytes()
if err != nil { if err != nil {
return "", err return "", err
} }
@ -103,7 +109,11 @@ func (entry *Entry) WithError(err error) *Entry {
// Add a context to the Entry. // Add a context to the Entry.
func (entry *Entry) WithContext(ctx context.Context) *Entry { func (entry *Entry) WithContext(ctx context.Context) *Entry {
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx} dataCopy := make(Fields, len(entry.Data))
for k, v := range entry.Data {
dataCopy[k] = v
}
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
} }
// Add a single field to the Entry. // Add a single field to the Entry.
@ -121,11 +131,9 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
for k, v := range fields { for k, v := range fields {
isErrField := false isErrField := false
if t := reflect.TypeOf(v); t != nil { if t := reflect.TypeOf(v); t != nil {
switch t.Kind() { switch {
case reflect.Func: case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
isErrField = true isErrField = true
case reflect.Ptr:
isErrField = t.Elem().Kind() == reflect.Func
} }
} }
if isErrField { if isErrField {
@ -144,7 +152,11 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
// Overrides the time of the Entry. // Overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry { func (entry *Entry) WithTime(t time.Time) *Entry {
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context} dataCopy := make(Fields, len(entry.Data))
for k, v := range entry.Data {
dataCopy[k] = v
}
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
} }
// getPackageName reduces a fully qualified function name to the package name // getPackageName reduces a fully qualified function name to the package name
@ -165,15 +177,20 @@ func getPackageName(f string) string {
// getCaller retrieves the name of the first non-logrus calling function // getCaller retrieves the name of the first non-logrus calling function
func getCaller() *runtime.Frame { func getCaller() *runtime.Frame {
// cache this package's fully-qualified name // cache this package's fully-qualified name
callerInitOnce.Do(func() { callerInitOnce.Do(func() {
pcs := make([]uintptr, 2) pcs := make([]uintptr, maximumCallerDepth)
_ = runtime.Callers(0, pcs) _ = runtime.Callers(0, pcs)
logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name())
// now that we have the cache, we can skip a minimum count of known-logrus functions // dynamic get the package name and the minimum caller depth
// XXX this is dubious, the number of frames may vary for i := 0; i < maximumCallerDepth; i++ {
funcName := runtime.FuncForPC(pcs[i]).Name()
if strings.Contains(funcName, "getCaller") {
logrusPackage = getPackageName(funcName)
break
}
}
minimumCallerDepth = knownLogrusFrames minimumCallerDepth = knownLogrusFrames
}) })
@ -187,7 +204,7 @@ func getCaller() *runtime.Frame {
// If the caller isn't part of this package, we're done // If the caller isn't part of this package, we're done
if pkg != logrusPackage { if pkg != logrusPackage {
return &f return &f //nolint:scopelint
} }
} }
@ -201,49 +218,66 @@ func (entry Entry) HasCaller() (has bool) {
entry.Caller != nil entry.Caller != nil
} }
// This function is not declared with a pointer value because otherwise func (entry *Entry) log(level Level, msg string) {
// race conditions will occur when using multiple goroutines
func (entry Entry) log(level Level, msg string) {
var buffer *bytes.Buffer var buffer *bytes.Buffer
// Default to now, but allow users to override if they want. newEntry := entry.Dup()
//
// We don't have to worry about polluting future calls to Entry#log() if newEntry.Time.IsZero() {
// with this assignment because this function is declared with a newEntry.Time = time.Now()
// non-pointer receiver.
if entry.Time.IsZero() {
entry.Time = time.Now()
} }
entry.Level = level newEntry.Level = level
entry.Message = msg newEntry.Message = msg
if entry.Logger.ReportCaller {
entry.Caller = getCaller() newEntry.Logger.mu.Lock()
reportCaller := newEntry.Logger.ReportCaller
bufPool := newEntry.getBufferPool()
newEntry.Logger.mu.Unlock()
if reportCaller {
newEntry.Caller = getCaller()
} }
entry.fireHooks() newEntry.fireHooks()
buffer = bufPool.Get()
buffer = bufferPool.Get().(*bytes.Buffer) defer func() {
newEntry.Buffer = nil
buffer.Reset()
bufPool.Put(buffer)
}()
buffer.Reset() buffer.Reset()
defer bufferPool.Put(buffer) newEntry.Buffer = buffer
entry.Buffer = buffer
entry.write() newEntry.write()
entry.Buffer = nil newEntry.Buffer = nil
// To avoid Entry#log() returning a value that only would make sense for // To avoid Entry#log() returning a value that only would make sense for
// panic() to use in Entry#Panic(), we avoid the allocation by checking // panic() to use in Entry#Panic(), we avoid the allocation by checking
// directly here. // directly here.
if level <= PanicLevel { if level <= PanicLevel {
panic(&entry) panic(newEntry)
} }
} }
func (entry *Entry) getBufferPool() (pool BufferPool) {
if entry.Logger.BufferPool != nil {
return entry.Logger.BufferPool
}
return bufferPool
}
func (entry *Entry) fireHooks() { func (entry *Entry) fireHooks() {
var tmpHooks LevelHooks
entry.Logger.mu.Lock() entry.Logger.mu.Lock()
defer entry.Logger.mu.Unlock() tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
err := entry.Logger.Hooks.Fire(entry.Level, entry) for k, v := range entry.Logger.Hooks {
tmpHooks[k] = v
}
entry.Logger.mu.Unlock()
err := tmpHooks.Fire(entry.Level, entry)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
} }
@ -255,14 +289,16 @@ func (entry *Entry) write() {
serialized, err := entry.Logger.Formatter.Format(entry) serialized, err := entry.Logger.Formatter.Format(entry)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
} else { return
_, err = entry.Logger.Out.Write(serialized) }
if err != nil { if _, err := entry.Logger.Out.Write(serialized); err != nil {
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
}
} }
} }
// Log will log a message at the level given as parameter.
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
// For this behaviour Entry.Panic or Entry.Fatal should be used instead.
func (entry *Entry) Log(level Level, args ...interface{}) { func (entry *Entry) Log(level Level, args ...interface{}) {
if entry.Logger.IsLevelEnabled(level) { if entry.Logger.IsLevelEnabled(level) {
entry.log(level, fmt.Sprint(args...)) entry.log(level, fmt.Sprint(args...))
@ -304,7 +340,6 @@ func (entry *Entry) Fatal(args ...interface{}) {
func (entry *Entry) Panic(args ...interface{}) { func (entry *Entry) Panic(args ...interface{}) {
entry.Log(PanicLevel, args...) entry.Log(PanicLevel, args...)
panic(fmt.Sprint(args...))
} }
// Entry Printf family functions // Entry Printf family functions

View File

@ -47,6 +47,82 @@ func TestEntryWithContext(t *testing.T) {
assert.Equal(ctx, entry.WithContext(ctx).Context) assert.Equal(ctx, entry.WithContext(ctx).Context)
} }
func TestEntryWithContextCopiesData(t *testing.T) {
assert := assert.New(t)
// Initialize a parent Entry object with a key/value set in its Data map
logger := New()
logger.Out = &bytes.Buffer{}
parentEntry := NewEntry(logger).WithField("parentKey", "parentValue")
// Create two children Entry objects from the parent in different contexts
ctx1 := context.WithValue(context.Background(), "foo", "bar")
childEntry1 := parentEntry.WithContext(ctx1)
assert.Equal(ctx1, childEntry1.Context)
ctx2 := context.WithValue(context.Background(), "bar", "baz")
childEntry2 := parentEntry.WithContext(ctx2)
assert.Equal(ctx2, childEntry2.Context)
assert.NotEqual(ctx1, ctx2)
// Ensure that data set in the parent Entry are preserved to both children
assert.Equal("parentValue", childEntry1.Data["parentKey"])
assert.Equal("parentValue", childEntry2.Data["parentKey"])
// Modify data stored in the child entry
childEntry1.Data["childKey"] = "childValue"
// Verify that data is successfully stored in the child it was set on
val, exists := childEntry1.Data["childKey"]
assert.True(exists)
assert.Equal("childValue", val)
// Verify that the data change to child 1 has not affected its sibling
val, exists = childEntry2.Data["childKey"]
assert.False(exists)
assert.Empty(val)
// Verify that the data change to child 1 has not affected its parent
val, exists = parentEntry.Data["childKey"]
assert.False(exists)
assert.Empty(val)
}
func TestEntryWithTimeCopiesData(t *testing.T) {
assert := assert.New(t)
// Initialize a parent Entry object with a key/value set in its Data map
logger := New()
logger.Out = &bytes.Buffer{}
parentEntry := NewEntry(logger).WithField("parentKey", "parentValue")
// Create two children Entry objects from the parent with two different times
childEntry1 := parentEntry.WithTime(time.Now().AddDate(0, 0, 1))
childEntry2 := parentEntry.WithTime(time.Now().AddDate(0, 0, 2))
// Ensure that data set in the parent Entry are preserved to both children
assert.Equal("parentValue", childEntry1.Data["parentKey"])
assert.Equal("parentValue", childEntry2.Data["parentKey"])
// Modify data stored in the child entry
childEntry1.Data["childKey"] = "childValue"
// Verify that data is successfully stored in the child it was set on
val, exists := childEntry1.Data["childKey"]
assert.True(exists)
assert.Equal("childValue", val)
// Verify that the data change to child 1 has not affected its sibling
val, exists = childEntry2.Data["childKey"]
assert.False(exists)
assert.Empty(val)
// Verify that the data change to child 1 has not affected its parent
val, exists = parentEntry.Data["childKey"]
assert.False(exists)
assert.Empty(val)
}
func TestEntryPanicln(t *testing.T) { func TestEntryPanicln(t *testing.T) {
errBoom := fmt.Errorf("boom time") errBoom := fmt.Errorf("boom time")
@ -91,6 +167,28 @@ func TestEntryPanicf(t *testing.T) {
entry.WithField("err", errBoom).Panicf("kaboom %v", true) entry.WithField("err", errBoom).Panicf("kaboom %v", true)
} }
func TestEntryPanic(t *testing.T) {
errBoom := fmt.Errorf("boom again")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panic("kaboom")
}
const ( const (
badMessage = "this is going to panic" badMessage = "this is going to panic"
panicMessage = "this is broken" panicMessage = "this is broken"
@ -134,7 +232,7 @@ func TestEntryWithIncorrectField(t *testing.T) {
fn := func() {} fn := func() {}
e := Entry{} e := Entry{Logger: New()}
eWithFunc := e.WithFields(Fields{"func": fn}) eWithFunc := e.WithFields(Fields{"func": fn})
eWithFuncPtr := e.WithFields(Fields{"funcPtr": &fn}) eWithFuncPtr := e.WithFields(Fields{"funcPtr": &fn})
@ -162,8 +260,42 @@ func TestEntryLogfLevel(t *testing.T) {
entry := NewEntry(logger) entry := NewEntry(logger)
entry.Logf(DebugLevel, "%s", "debug") entry.Logf(DebugLevel, "%s", "debug")
assert.NotContains(t, buffer.String(), "debug", ) assert.NotContains(t, buffer.String(), "debug")
entry.Logf(WarnLevel, "%s", "warn") entry.Logf(WarnLevel, "%s", "warn")
assert.Contains(t, buffer.String(), "warn", ) assert.Contains(t, buffer.String(), "warn")
} }
func TestEntryReportCallerRace(t *testing.T) {
logger := New()
entry := NewEntry(logger)
// logging before SetReportCaller has the highest chance of causing a race condition
// to be detected, but doing it twice just to increase the likelyhood of detecting the race
go func() {
entry.Info("should not race")
}()
go func() {
logger.SetReportCaller(true)
}()
go func() {
entry.Info("should not race")
}()
}
func TestEntryFormatterRace(t *testing.T) {
logger := New()
entry := NewEntry(logger)
// logging before SetReportCaller has the highest chance of causing a race condition
// to be detected, but doing it twice just to increase the likelyhood of detecting the race
go func() {
entry.Info("should not race")
}()
go func() {
logger.SetFormatter(&TextFormatter{})
}()
go func() {
entry.Info("should not race")
}()
}

View File

@ -3,13 +3,13 @@ package logrus_test
import ( import (
"os" "os"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
) )
func Example_basic() { func Example_basic() {
var log = logrus.New() log := logrus.New()
log.Formatter = new(logrus.JSONFormatter) log.Formatter = new(logrus.JSONFormatter)
log.Formatter = new(logrus.TextFormatter) //default log.Formatter = new(logrus.TextFormatter) // default
log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors
log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output
log.Level = logrus.TraceLevel log.Level = logrus.TraceLevel

View File

@ -6,7 +6,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
) )
func ExampleJSONFormatter_CallerPrettyfier() { func ExampleJSONFormatter_CallerPrettyfier() {
@ -24,5 +24,5 @@ func ExampleJSONFormatter_CallerPrettyfier() {
} }
l.Info("example of custom format caller") l.Info("example of custom format caller")
// Output: // Output:
// {"file":"example_custom_caller_test.go","func":"ExampleCustomFormatter","level":"info","msg":"example of custom format caller"} // {"file":"example_custom_caller_test.go","func":"ExampleJSONFormatter_CallerPrettyfier","level":"info","msg":"example of custom format caller"}
} }

View File

@ -3,7 +3,7 @@ package logrus_test
import ( import (
"os" "os"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
) )
type DefaultFieldHook struct { type DefaultFieldHook struct {

31
example_function_test.go Normal file
View File

@ -0,0 +1,31 @@
package logrus_test
import (
"testing"
log "git.internal/re/logrus"
"github.com/stretchr/testify/assert"
)
func TestLogger_LogFn(t *testing.T) {
log.SetFormatter(&log.JSONFormatter{})
log.SetLevel(log.WarnLevel)
notCalled := 0
log.InfoFn(func() []interface{} {
notCalled++
return []interface{}{
"Hello",
}
})
assert.Equal(t, 0, notCalled)
called := 0
log.ErrorFn(func() []interface{} {
called++
return []interface{}{
"Oopsi",
}
})
assert.Equal(t, 1, called)
}

View File

@ -3,15 +3,12 @@ package logrus_test
import ( import (
"os" "os"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
) )
var ( var mystring string
mystring string
)
type GlobalHook struct { type GlobalHook struct{}
}
func (h *GlobalHook) Levels() []logrus.Level { func (h *GlobalHook) Levels() []logrus.Level {
return logrus.AllLevels return logrus.AllLevels

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package logrus_test package logrus_test
@ -6,13 +7,13 @@ import (
"log/syslog" "log/syslog"
"os" "os"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
slhooks "github.com/sirupsen/logrus/hooks/syslog" slhooks "git.internal/re/logrus/hooks/syslog"
) )
// An example on how to use a hook // An example on how to use a hook
func Example_hook() { func Example_hook() {
var log = logrus.New() log := logrus.New()
log.Formatter = new(logrus.TextFormatter) // default log.Formatter = new(logrus.TextFormatter) // default
log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors log.Formatter.(*logrus.TextFormatter).DisableColors = true // remove colors
log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output

View File

@ -80,7 +80,7 @@ func WithFields(fields Fields) *Entry {
return std.WithFields(fields) return std.WithFields(fields)
} }
// WithTime creats an entry from the standard logger and overrides the time of // WithTime creates an entry from the standard logger and overrides the time of
// logs generated with it. // logs generated with it.
// //
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal // Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
@ -134,6 +134,51 @@ func Fatal(args ...interface{}) {
std.Fatal(args...) std.Fatal(args...)
} }
// TraceFn logs a message from a func at level Trace on the standard logger.
func TraceFn(fn LogFunction) {
std.TraceFn(fn)
}
// DebugFn logs a message from a func at level Debug on the standard logger.
func DebugFn(fn LogFunction) {
std.DebugFn(fn)
}
// PrintFn logs a message from a func at level Info on the standard logger.
func PrintFn(fn LogFunction) {
std.PrintFn(fn)
}
// InfoFn logs a message from a func at level Info on the standard logger.
func InfoFn(fn LogFunction) {
std.InfoFn(fn)
}
// WarnFn logs a message from a func at level Warn on the standard logger.
func WarnFn(fn LogFunction) {
std.WarnFn(fn)
}
// WarningFn logs a message from a func at level Warn on the standard logger.
func WarningFn(fn LogFunction) {
std.WarningFn(fn)
}
// ErrorFn logs a message from a func at level Error on the standard logger.
func ErrorFn(fn LogFunction) {
std.ErrorFn(fn)
}
// PanicFn logs a message from a func at level Panic on the standard logger.
func PanicFn(fn LogFunction) {
std.PanicFn(fn)
}
// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1.
func FatalFn(fn LogFunction) {
std.FatalFn(fn)
}
// Tracef logs a message at level Trace on the standard logger. // Tracef logs a message at level Trace on the standard logger.
func Tracef(format string, args ...interface{}) { func Tracef(format string, args ...interface{}) {
std.Tracef(format, args...) std.Tracef(format, args...)

11
go.mod
View File

@ -1,10 +1,9 @@
module github.com/sirupsen/logrus module git.internal/re/logrus
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.1 github.com/stretchr/testify v1.7.0
github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
github.com/stretchr/objx v0.1.1 // indirect
github.com/stretchr/testify v1.2.2
golang.org/x/sys v0.0.0-20190422165155-953cdadca894
) )
go 1.13

22
go.sum
View File

@ -1,16 +1,14 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs=
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -3,14 +3,16 @@ package logrus_test
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"sync" "sync"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
. "github.com/sirupsen/logrus" . "git.internal/re/logrus"
. "github.com/sirupsen/logrus/internal/testutils" "git.internal/re/logrus/hooks/test"
. "git.internal/re/logrus/internal/testutils"
) )
type TestHook struct { type TestHook struct {
@ -47,8 +49,7 @@ func TestHookFires(t *testing.T) {
}) })
} }
type ModifyHook struct { type ModifyHook struct{}
}
func (hook *ModifyHook) Fire(entry *Entry) error { func (hook *ModifyHook) Fire(entry *Entry) error {
entry.Data["wow"] = "whale" entry.Data["wow"] = "whale"
@ -191,6 +192,20 @@ func TestAddHookRace(t *testing.T) {
}) })
} }
func TestAddHookRace2(t *testing.T) {
t.Parallel()
for i := 0; i < 3; i++ {
testname := fmt.Sprintf("Test %d", i)
t.Run(testname, func(t *testing.T) {
t.Parallel()
_ = test.NewGlobal()
Info(testname)
})
}
}
type HookCallFunc struct { type HookCallFunc struct {
F func() F func()
} }

View File

@ -5,8 +5,8 @@
```go ```go
import ( import (
"log/syslog" "log/syslog"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
lSyslog "github.com/sirupsen/logrus/hooks/syslog" lSyslog "git.internal/re/logrus/hooks/syslog"
) )
func main() { func main() {
@ -24,8 +24,8 @@ If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "
```go ```go
import ( import (
"log/syslog" "log/syslog"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
lSyslog "github.com/sirupsen/logrus/hooks/syslog" lSyslog "git.internal/re/logrus/hooks/syslog"
) )
func main() { func main() {

View File

@ -1,3 +1,4 @@
//go:build !windows && !nacl && !plan9
// +build !windows,!nacl,!plan9 // +build !windows,!nacl,!plan9
package syslog package syslog
@ -7,7 +8,7 @@ import (
"log/syslog" "log/syslog"
"os" "os"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
) )
// SyslogHook to send logs via syslog. // SyslogHook to send logs via syslog.

View File

@ -1,3 +1,4 @@
//go:build !windows && !nacl && !plan9
// +build !windows,!nacl,!plan9 // +build !windows,!nacl,!plan9
package syslog package syslog
@ -6,13 +7,12 @@ import (
"log/syslog" "log/syslog"
"testing" "testing"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
) )
func TestLocalhostAddAndPrint(t *testing.T) { func TestLocalhostAddAndPrint(t *testing.T) {
log := logrus.New() log := logrus.New()
hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil { if err != nil {
t.Errorf("Unable to connect to local syslog.") t.Errorf("Unable to connect to local syslog.")
} }

View File

@ -1,13 +1,12 @@
// The Test package is used for testing logrus. It is here for backwards // The Test package is used for testing logrus.
// compatibility from when logrus' organization was upper-case. Please use // It provides a simple hooks which register logged messages.
// lower-case logrus and the `null` package instead of this one.
package test package test
import ( import (
"io/ioutil" "io/ioutil"
"sync" "sync"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
) )
// Hook is a hook designed for dealing with logs in test scenarios. // Hook is a hook designed for dealing with logs in test scenarios.
@ -21,32 +20,26 @@ type Hook struct {
// NewGlobal installs a test hook for the global logger. // NewGlobal installs a test hook for the global logger.
func NewGlobal() *Hook { func NewGlobal() *Hook {
hook := new(Hook) hook := new(Hook)
logrus.AddHook(hook) logrus.AddHook(hook)
return hook return hook
} }
// NewLocal installs a test hook for a given local logger. // NewLocal installs a test hook for a given local logger.
func NewLocal(logger *logrus.Logger) *Hook { func NewLocal(logger *logrus.Logger) *Hook {
hook := new(Hook) hook := new(Hook)
logger.Hooks.Add(hook) logger.Hooks.Add(hook)
return hook return hook
} }
// NewNullLogger creates a discarding logger and installs the test hook. // NewNullLogger creates a discarding logger and installs the test hook.
func NewNullLogger() (*logrus.Logger, *Hook) { func NewNullLogger() (*logrus.Logger, *Hook) {
logger := logrus.New() logger := logrus.New()
logger.Out = ioutil.Discard logger.Out = ioutil.Discard
return logger, NewLocal(logger) return logger, NewLocal(logger)
} }
func (t *Hook) Fire(e *logrus.Entry) error { func (t *Hook) Fire(e *logrus.Entry) error {

View File

@ -6,7 +6,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -40,7 +40,6 @@ func TestAllHooks(t *testing.T) {
} }
func TestLoggingWithHooksRace(t *testing.T) { func TestLoggingWithHooksRace(t *testing.T) {
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
unlocker := rand.Int() % 100 unlocker := rand.Int() % 100

43
hooks/writer/README.md Normal file
View File

@ -0,0 +1,43 @@
# Writer Hooks for Logrus
Send logs of given levels to any object with `io.Writer` interface.
## Usage
If you want for example send high level logs to `Stderr` and
logs of normal execution to `Stdout`, you could do it like this:
```go
package main
import (
"io/ioutil"
"os"
log "git.internal/re/logrus"
"git.internal/re/logrus/hooks/writer"
)
func main() {
log.SetOutput(ioutil.Discard) // Send all logs to nowhere by default
log.AddHook(&writer.Hook{ // Send logs with level higher than warning to stderr
Writer: os.Stderr,
LogLevels: []log.Level{
log.PanicLevel,
log.FatalLevel,
log.ErrorLevel,
log.WarnLevel,
},
})
log.AddHook(&writer.Hook{ // Send info and debug logs to stdout
Writer: os.Stdout,
LogLevels: []log.Level{
log.InfoLevel,
log.DebugLevel,
},
})
log.Info("This will go to stdout")
log.Warn("This will go to stderr")
}
```

29
hooks/writer/writer.go Normal file
View File

@ -0,0 +1,29 @@
package writer
import (
"io"
log "git.internal/re/logrus"
)
// Hook is a hook that writes logs of specified LogLevels to specified Writer
type Hook struct {
Writer io.Writer
LogLevels []log.Level
}
// Fire will be called when some logging function is called with current hook
// It will format log entry to string and write it to appropriate writer
func (hook *Hook) Fire(entry *log.Entry) error {
line, err := entry.Bytes()
if err != nil {
return err
}
_, err = hook.Writer.Write(line)
return err
}
// Levels define on which log levels this hook would trigger
func (hook *Hook) Levels() []log.Level {
return hook.LogLevels
}

View File

@ -0,0 +1,38 @@
package writer
import (
"bytes"
"io/ioutil"
"testing"
log "git.internal/re/logrus"
"github.com/stretchr/testify/assert"
)
func TestDifferentLevelsGoToDifferentWriters(t *testing.T) {
var a, b bytes.Buffer
log.SetFormatter(&log.TextFormatter{
DisableTimestamp: true,
DisableColors: true,
})
log.SetOutput(ioutil.Discard) // Send all logs to nowhere by default
log.AddHook(&Hook{
Writer: &a,
LogLevels: []log.Level{
log.WarnLevel,
},
})
log.AddHook(&Hook{ // Send info and debug logs to stdout
Writer: &b,
LogLevels: []log.Level{
log.InfoLevel,
},
})
log.Warn("send to a")
log.Info("send to b")
assert.Equal(t, a.String(), "level=warning msg=\"send to a\"\n")
assert.Equal(t, b.String(), "level=info msg=\"send to b\"\n")
}

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"testing" "testing"
. "github.com/sirupsen/logrus" . "git.internal/re/logrus"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )

View File

@ -23,11 +23,17 @@ func (f FieldMap) resolve(key fieldKey) string {
// JSONFormatter formats logs into parsable json // JSONFormatter formats logs into parsable json
type JSONFormatter struct { type JSONFormatter struct {
// TimestampFormat sets the format used for marshaling timestamps. // TimestampFormat sets the format used for marshaling timestamps.
// The format to use is the same than for time.Format or time.Parse from the standard
// library.
// The standard Library already provides a set of predefined format.
TimestampFormat string TimestampFormat string
// DisableTimestamp allows disabling automatic timestamps in output // DisableTimestamp allows disabling automatic timestamps in output
DisableTimestamp bool DisableTimestamp bool
// DisableHTMLEscape allows disabling html escaping in output
DisableHTMLEscape bool
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key. // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
DataKey string DataKey string
@ -60,7 +66,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
switch v := v.(type) { switch v := v.(type) {
case error: case error:
// Otherwise errors are ignored by `encoding/json` // Otherwise errors are ignored by `encoding/json`
// https://github.com/sirupsen/logrus/issues/137 // https://git.internal/re/logrus/issues/137
data[k] = v.Error() data[k] = v.Error()
default: default:
data[k] = v data[k] = v
@ -110,11 +116,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
} }
encoder := json.NewEncoder(b) encoder := json.NewEncoder(b)
encoder.SetEscapeHTML(!f.DisableHTMLEscape)
if f.PrettyPrint { if f.PrettyPrint {
encoder.SetIndent("", " ") encoder.SetIndent("", " ")
} }
if err := encoder.Encode(data); err != nil { if err := encoder.Encode(data); err != nil {
return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err) return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
} }
return b.Bytes(), nil return b.Bytes(), nil

View File

@ -344,3 +344,29 @@ func TestJSONEnableTimestamp(t *testing.T) {
t.Error("Timestamp not present", s) t.Error("Timestamp not present", s)
} }
} }
func TestJSONDisableHTMLEscape(t *testing.T) {
formatter := &JSONFormatter{DisableHTMLEscape: true}
b, err := formatter.Format(&Entry{Message: "& < >"})
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !strings.Contains(s, "& < >") {
t.Error("Message should not be HTML escaped", s)
}
}
func TestJSONEnableHTMLEscape(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(&Entry{Message: "& < >"})
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !(strings.Contains(s, "u0026") && strings.Contains(s, "u003e") && strings.Contains(s, "u003c")) {
t.Error("Message should be HTML escaped", s)
}
}

View File

@ -5,7 +5,7 @@ import (
"encoding/json" "encoding/json"
"testing" "testing"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )

View File

@ -9,6 +9,11 @@ import (
"time" "time"
) )
// LogFunction For big messages, it can be more efficient to pass a function
// and only call it if the log level is actually enables rather than
// generating the log message and then checking if the level is enabled
type LogFunction func() []interface{}
type Logger struct { type Logger struct {
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
// file, or leave it default which is `os.Stderr`. You can also set this to // file, or leave it default which is `os.Stderr`. You can also set this to
@ -39,6 +44,9 @@ type Logger struct {
entryPool sync.Pool entryPool sync.Pool
// Function to exit the application, defaults to `os.Exit()` // Function to exit the application, defaults to `os.Exit()`
ExitFunc exitFunc ExitFunc exitFunc
// The buffer pool used to format the log. If it is nil, the default global
// buffer pool will be used.
BufferPool BufferPool
} }
type exitFunc func(int) type exitFunc func(int)
@ -68,10 +76,10 @@ func (mw *MutexWrap) Disable() {
// `Out` and `Hooks` directly on the default logger instance. You can also just // `Out` and `Hooks` directly on the default logger instance. You can also just
// instantiate your own: // instantiate your own:
// //
// var log = &Logger{ // var log = &logrus.Logger{
// Out: os.Stderr, // Out: os.Stderr,
// Formatter: new(JSONFormatter), // Formatter: new(logrus.TextFormatter),
// Hooks: make(LevelHooks), // Hooks: make(logrus.LevelHooks),
// Level: logrus.DebugLevel, // Level: logrus.DebugLevel,
// } // }
// //
@ -100,8 +108,9 @@ func (logger *Logger) releaseEntry(entry *Entry) {
logger.entryPool.Put(entry) logger.entryPool.Put(entry)
} }
// Adds a field to the log entry, note that it doesn't log until you call // WithField allocates a new entry and adds a field to it.
// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry. // Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
// this new returned entry.
// If you want multiple fields, use `WithFields`. // If you want multiple fields, use `WithFields`.
func (logger *Logger) WithField(key string, value interface{}) *Entry { func (logger *Logger) WithField(key string, value interface{}) *Entry {
entry := logger.newEntry() entry := logger.newEntry()
@ -186,6 +195,9 @@ func (logger *Logger) Panicf(format string, args ...interface{}) {
logger.Logf(PanicLevel, format, args...) logger.Logf(PanicLevel, format, args...)
} }
// Log will log a message at the level given as parameter.
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
// For this behaviour Logger.Panic or Logger.Fatal should be used instead.
func (logger *Logger) Log(level Level, args ...interface{}) { func (logger *Logger) Log(level Level, args ...interface{}) {
if logger.IsLevelEnabled(level) { if logger.IsLevelEnabled(level) {
entry := logger.newEntry() entry := logger.newEntry()
@ -194,6 +206,14 @@ func (logger *Logger) Log(level Level, args ...interface{}) {
} }
} }
func (logger *Logger) LogFn(level Level, fn LogFunction) {
if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
entry.Log(level, fn()...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Trace(args ...interface{}) { func (logger *Logger) Trace(args ...interface{}) {
logger.Log(TraceLevel, args...) logger.Log(TraceLevel, args...)
} }
@ -233,6 +253,45 @@ func (logger *Logger) Panic(args ...interface{}) {
logger.Log(PanicLevel, args...) logger.Log(PanicLevel, args...)
} }
func (logger *Logger) TraceFn(fn LogFunction) {
logger.LogFn(TraceLevel, fn)
}
func (logger *Logger) DebugFn(fn LogFunction) {
logger.LogFn(DebugLevel, fn)
}
func (logger *Logger) InfoFn(fn LogFunction) {
logger.LogFn(InfoLevel, fn)
}
func (logger *Logger) PrintFn(fn LogFunction) {
entry := logger.newEntry()
entry.Print(fn()...)
logger.releaseEntry(entry)
}
func (logger *Logger) WarnFn(fn LogFunction) {
logger.LogFn(WarnLevel, fn)
}
func (logger *Logger) WarningFn(fn LogFunction) {
logger.WarnFn(fn)
}
func (logger *Logger) ErrorFn(fn LogFunction) {
logger.LogFn(ErrorLevel, fn)
}
func (logger *Logger) FatalFn(fn LogFunction) {
logger.LogFn(FatalLevel, fn)
logger.Exit(1)
}
func (logger *Logger) PanicFn(fn LogFunction) {
logger.LogFn(PanicLevel, fn)
}
func (logger *Logger) Logln(level Level, args ...interface{}) { func (logger *Logger) Logln(level Level, args ...interface{}) {
if logger.IsLevelEnabled(level) { if logger.IsLevelEnabled(level) {
entry := logger.newEntry() entry := logger.newEntry()
@ -349,3 +408,10 @@ func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
logger.mu.Unlock() logger.mu.Unlock()
return oldHooks return oldHooks
} }
// SetBufferPool sets the logger buffer pool.
func (logger *Logger) SetBufferPool(pool BufferPool) {
logger.mu.Lock()
defer logger.mu.Unlock()
logger.BufferPool = pool
}

View File

@ -25,7 +25,7 @@ func TestFieldValueError(t *testing.T) {
t.Error("unexpected error", err) t.Error("unexpected error", err)
} }
_, ok := data[FieldKeyLogrusError] _, ok := data[FieldKeyLogrusError]
require.True(t, ok) require.True(t, ok, `cannot found expected "logrus_error" field: %v`, data)
} }
func TestNoFieldValueError(t *testing.T) { func TestNoFieldValueError(t *testing.T) {
@ -67,3 +67,31 @@ func TestWarninglnNotEqualToWarning(t *testing.T) {
assert.NotEqual(t, buf.String(), bufln.String(), "Warning() and Wantingln() should not be equal") assert.NotEqual(t, buf.String(), bufln.String(), "Warning() and Wantingln() should not be equal")
} }
type testBufferPool struct {
buffers []*bytes.Buffer
get int
}
func (p *testBufferPool) Get() *bytes.Buffer {
p.get++
return new(bytes.Buffer)
}
func (p *testBufferPool) Put(buf *bytes.Buffer) {
p.buffers = append(p.buffers, buf)
}
func TestLogger_SetBufferPool(t *testing.T) {
out := &bytes.Buffer{}
l := New()
l.SetOutput(out)
pool := new(testBufferPool)
l.SetBufferPool(pool)
l.Info("test")
assert.Equal(t, pool.get, 1, "Logger.SetBufferPool(): The BufferPool.Get() must be called")
assert.Len(t, pool.buffers, 1, "Logger.SetBufferPool(): The BufferPool.Put() must be called")
}

View File

@ -15,8 +15,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
. "github.com/sirupsen/logrus" . "git.internal/re/logrus"
. "github.com/sirupsen/logrus/internal/testutils" . "git.internal/re/logrus/internal/testutils"
) )
// TestReportCaller verifies that when ReportCaller is set, the 'func' field // TestReportCaller verifies that when ReportCaller is set, the 'func' field
@ -40,7 +40,7 @@ func TestReportCallerWhenConfigured(t *testing.T) {
assert.Equal(t, "testWithCaller", fields["msg"]) assert.Equal(t, "testWithCaller", fields["msg"])
assert.Equal(t, "info", fields["level"]) assert.Equal(t, "info", fields["level"])
assert.Equal(t, assert.Equal(t,
"github.com/sirupsen/logrus_test.TestReportCallerWhenConfigured.func3", fields[FieldKeyFunc]) "git.internal/re/logrus_test.TestReportCallerWhenConfigured.func3", fields[FieldKeyFunc])
}) })
LogAndAssertJSON(t, func(log *Logger) { LogAndAssertJSON(t, func(log *Logger) {
@ -328,7 +328,6 @@ func TestTimeOverrideMultipleLogs(t *testing.T) {
} }
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
var buffer bytes.Buffer var buffer bytes.Buffer
var fields Fields var fields Fields
@ -356,7 +355,6 @@ func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
assert.Equal(t, "omg it is!", fields["msg"]) assert.Equal(t, "omg it is!", fields["msg"])
assert.Equal(t, "eating raw fish", fields["context"]) assert.Equal(t, "eating raw fish", fields["context"])
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
} }
func TestNestedLoggingReportsCorrectCaller(t *testing.T) { func TestNestedLoggingReportsCorrectCaller(t *testing.T) {
@ -379,7 +377,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) {
assert.Equal(t, "looks delicious", fields["msg"]) assert.Equal(t, "looks delicious", fields["msg"])
assert.Equal(t, "eating raw fish", fields["context"]) assert.Equal(t, "eating raw fish", fields["context"])
assert.Equal(t, assert.Equal(t,
"github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) "git.internal/re/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"])
cwd, err := os.Getwd() cwd, err := os.Getwd()
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, filepath.ToSlash(fmt.Sprintf("%s/logrus_test.go:%d", cwd, line-1)), filepath.ToSlash(fields["file"].(string))) assert.Equal(t, filepath.ToSlash(fmt.Sprintf("%s/logrus_test.go:%d", cwd, line-1)), filepath.ToSlash(fields["file"].(string)))
@ -410,7 +408,7 @@ func TestNestedLoggingReportsCorrectCaller(t *testing.T) {
assert.Equal(t, "The hardest workin' man in show business", fields["msg"]) assert.Equal(t, "The hardest workin' man in show business", fields["msg"])
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
assert.Equal(t, assert.Equal(t,
"github.com/sirupsen/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"]) "git.internal/re/logrus_test.TestNestedLoggingReportsCorrectCaller", fields["func"])
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, filepath.ToSlash(fmt.Sprintf("%s/logrus_test.go:%d", cwd, line-1)), filepath.ToSlash(fields["file"].(string))) assert.Equal(t, filepath.ToSlash(fmt.Sprintf("%s/logrus_test.go:%d", cwd, line-1)), filepath.ToSlash(fields["file"].(string)))
@ -588,15 +586,48 @@ func TestLoggingRaceWithHooksOnEntry(t *testing.T) {
logger.AddHook(hook) logger.AddHook(hook)
entry := logger.WithField("context", "clue") entry := logger.WithField("context", "clue")
var wg sync.WaitGroup var (
wg sync.WaitGroup
mtx sync.Mutex
start bool
)
cond := sync.NewCond(&mtx)
wg.Add(100) wg.Add(100)
for i := 0; i < 100; i++ { for i := 0; i < 50; i++ {
go func() { go func() {
entry.Info("info") cond.L.Lock()
for !start {
cond.Wait()
}
cond.L.Unlock()
for j := 0; j < 100; j++ {
entry.Info("info")
}
wg.Done() wg.Done()
}() }()
} }
for i := 0; i < 50; i++ {
go func() {
cond.L.Lock()
for !start {
cond.Wait()
}
cond.L.Unlock()
for j := 0; j < 100; j++ {
entry.WithField("another field", "with some data").Info("info")
}
wg.Done()
}()
}
cond.L.Lock()
start = true
cond.L.Unlock()
cond.Broadcast()
wg.Wait() wg.Wait()
} }

View File

@ -1,4 +1,4 @@
// +build linux aix // +build linux aix zos
// +build !js // +build !js
package logrus package logrus

View File

@ -5,30 +5,23 @@ package logrus
import ( import (
"io" "io"
"os" "os"
"syscall"
sequences "github.com/konsorten/go-windows-terminal-sequences" "golang.org/x/sys/windows"
) )
func initTerminal(w io.Writer) {
switch v := w.(type) {
case *os.File:
sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true)
}
}
func checkIfTerminal(w io.Writer) bool { func checkIfTerminal(w io.Writer) bool {
var ret bool
switch v := w.(type) { switch v := w.(type) {
case *os.File: case *os.File:
handle := windows.Handle(v.Fd())
var mode uint32 var mode uint32
err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode) if err := windows.GetConsoleMode(handle, &mode); err != nil {
ret = (err == nil) return false
default: }
ret = false mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
if err := windows.SetConsoleMode(handle, mode); err != nil {
return false
}
return true
} }
if ret { return false
initTerminal(w)
}
return ret
} }

View File

@ -34,6 +34,14 @@ type TextFormatter struct {
// Force disabling colors. // Force disabling colors.
DisableColors bool DisableColors bool
// Force quoting of all values
ForceQuote bool
// DisableQuote disables quoting for all values.
// DisableQuote will have a lower priority than ForceQuote.
// If both of them are set to true, quote will be forced on all values.
DisableQuote bool
// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
EnvironmentOverrideColors bool EnvironmentOverrideColors bool
@ -45,7 +53,10 @@ type TextFormatter struct {
// the time passed since beginning of execution. // the time passed since beginning of execution.
FullTimestamp bool FullTimestamp bool
// TimestampFormat to use for display when a full timestamp is printed // TimestampFormat to use for display when a full timestamp is printed.
// The format to use is the same than for time.Format or time.Parse from the standard
// library.
// The standard Library already provides a set of predefined format.
TimestampFormat string TimestampFormat string
// The fields are sorted by default for a consistent output. For applications // The fields are sorted by default for a consistent output. For applications
@ -107,11 +118,10 @@ func (f *TextFormatter) isColored() bool {
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows")) isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
if f.EnvironmentOverrideColors { if f.EnvironmentOverrideColors {
if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" { switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); {
case ok && force != "0":
isColored = true isColored = true
} else if ok && force == "0" { case ok && force == "0", os.Getenv("CLICOLOR") == "0":
isColored = false
} else if os.Getenv("CLICOLOR") == "0" {
isColored = false isColored = false
} }
} }
@ -228,6 +238,8 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
levelColor = yellow levelColor = yellow
case ErrorLevel, FatalLevel, PanicLevel: case ErrorLevel, FatalLevel, PanicLevel:
levelColor = red levelColor = red
case InfoLevel:
levelColor = blue
default: default:
levelColor = blue levelColor = blue
} }
@ -268,11 +280,12 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
} }
} }
if f.DisableTimestamp { switch {
case f.DisableTimestamp:
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message) fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
} else if !f.FullTimestamp { case !f.FullTimestamp:
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message) fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
} else { default:
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message) fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
} }
for _, k := range keys { for _, k := range keys {
@ -283,9 +296,15 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
} }
func (f *TextFormatter) needsQuoting(text string) bool { func (f *TextFormatter) needsQuoting(text string) bool {
if f.ForceQuote {
return true
}
if f.QuoteEmptyFields && len(text) == 0 { if f.QuoteEmptyFields && len(text) == 0 {
return true return true
} }
if f.DisableQuote {
return false
}
for _, ch := range text { for _, ch := range text {
if !((ch >= 'a' && ch <= 'z') || if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') || (ch >= 'A' && ch <= 'Z') ||

View File

@ -59,6 +59,7 @@ func TestQuoting(t *testing.T) {
checkQuoting(false, "foo@bar") checkQuoting(false, "foo@bar")
checkQuoting(false, "foobar^") checkQuoting(false, "foobar^")
checkQuoting(false, "+/-_^@f.oobar") checkQuoting(false, "+/-_^@f.oobar")
checkQuoting(true, "foo\n\rbar")
checkQuoting(true, "foobar$") checkQuoting(true, "foobar$")
checkQuoting(true, "&foobar") checkQuoting(true, "&foobar")
checkQuoting(true, "x y") checkQuoting(true, "x y")
@ -70,7 +71,30 @@ func TestQuoting(t *testing.T) {
tf.QuoteEmptyFields = true tf.QuoteEmptyFields = true
checkQuoting(true, "") checkQuoting(true, "")
checkQuoting(false, "abcd") checkQuoting(false, "abcd")
checkQuoting(true, "foo\n\rbar")
checkQuoting(true, errors.New("invalid argument")) checkQuoting(true, errors.New("invalid argument"))
// Test forcing quotes.
tf.ForceQuote = true
checkQuoting(true, "")
checkQuoting(true, "abcd")
checkQuoting(true, "foo\n\rbar")
checkQuoting(true, errors.New("invalid argument"))
// Test forcing quotes when also disabling them.
tf.DisableQuote = true
checkQuoting(true, "")
checkQuoting(true, "abcd")
checkQuoting(true, "foo\n\rbar")
checkQuoting(true, errors.New("invalid argument"))
// Test disabling quotes
tf.ForceQuote = false
tf.QuoteEmptyFields = false
checkQuoting(false, "")
checkQuoting(false, "abcd")
checkQuoting(false, "foo\n\rbar")
checkQuoting(false, errors.New("invalid argument"))
} }
func TestEscaping(t *testing.T) { func TestEscaping(t *testing.T) {

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
if [[ "$TRAVIS_GO_VERSION" =~ ^1.\12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then if [[ "$TRAVIS_GO_VERSION" =~ ^1\.13\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$GO111MODULE" == "on" ]]; then
/tmp/gox/gox -build-lib -all $(go env GOPATH)/bin/gox -build-lib
fi fi

View File

@ -2,10 +2,7 @@
set -e set -e
if [[ "$TRAVIS_GO_VERSION" =~ ^1.\12\. ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then # Install golanci 1.32.2
git clone https://github.com/dgsb/gox.git /tmp/gox if [[ "$TRAVIS_GO_VERSION" =~ ^1\.15\. ]]; then
pushd /tmp/gox curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.32.2
git checkout new_master
go build ./
popd
fi fi

View File

@ -4,7 +4,7 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/sirupsen/logrus" "git.internal/re/logrus"
) )
func ExampleLogger_Writer_httpServer() { func ExampleLogger_Writer_httpServer() {