Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: [#347] Switch logging channels on runtime #482

Merged
merged 11 commits into from
May 26, 2024
4 changes: 4 additions & 0 deletions contracts/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ const (
type Log interface {
// WithContext adds a context to the logger.
WithContext(ctx context.Context) Writer
// Channel return a writer for a specific channel.
Channel(channel string) Writer
// Stack return a writer for multiple channels.
Stack(channels []string) Writer
Writer
}

Expand Down
48 changes: 41 additions & 7 deletions log/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ import (
type Application struct {
log.Writer
instance *logrus.Logger
config config.Config
}

func NewApplication(config config.Config) *Application {
instance := logrus.New()
instance.SetLevel(logrus.DebugLevel)

if config != nil {
if logging := config.GetString("logging.default"); logging != "" {
if err := registerHook(config, instance, logging); err != nil {
if channel := config.GetString("logging.default"); channel != "" {
if err := registerHook(config, instance, channel); err != nil {
color.Red().Println("Init facades.Log error: " + err.Error())

return nil
}
}
Expand All @@ -32,14 +32,48 @@ func NewApplication(config config.Config) *Application {
return &Application{
instance: instance,
Writer: NewWriter(instance.WithContext(context.Background())),
config: config,
}
}

func (r *Application) WithContext(ctx context.Context) log.Writer {
switch r.Writer.(type) {
case *Writer:
return NewWriter(r.instance.WithContext(ctx))
default:
return NewWriter(r.instance.WithContext(ctx))
}

func (r *Application) Channel(channel string) log.Writer {
if channel == "" || r.config == nil {
return r.Writer
}

instance := logrus.New()
instance.SetLevel(logrus.DebugLevel)

if err := registerHook(r.config, instance, channel); err != nil {
color.Red().Println("Init facades.Log error: " + err.Error())
return nil
}

return NewWriter(instance.WithContext(context.Background()))
}

func (r *Application) Stack(channels []string) log.Writer {
if r.config == nil || len(channels) == 0 {
return r.Writer
}

instance := logrus.New()
instance.SetLevel(logrus.DebugLevel)

for _, channel := range channels {
if channel == "" {
continue
}

if err := registerHook(r.config, instance, channel); err != nil {
color.Red().Println("Init facades.Log error: " + err.Error())
return nil
}
}

return NewWriter(instance.WithContext(context.Background()))
}
81 changes: 81 additions & 0 deletions log/application_test.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package log

import (
"io"
"testing"

"github.com/stretchr/testify/assert"

mocksconfig "github.com/goravel/framework/mocks/config"
"github.com/goravel/framework/support/color"
)

func TestNewApplication(t *testing.T) {
app := NewApplication(nil)
assert.NotNil(t, app)

mockConfig := &mocksconfig.Config{}
mockConfig.On("GetString", "logging.default").Return("test")
mockConfig.On("GetString", "logging.channels.test.driver").Return("single")
mockConfig.On("GetString", "logging.channels.test.path").Return("test")
mockConfig.On("GetString", "logging.channels.test.level").Return("debug")
mockConfig.On("GetBool", "logging.channels.test.print").Return(true)
app = NewApplication(mockConfig)
assert.NotNil(t, app)

mockConfig = &mocksconfig.Config{}
mockConfig.On("GetString", "logging.default").Return("test")
mockConfig.On("GetString", "logging.channels.test.driver").Return("test")
assert.Contains(t, color.CaptureOutput(func(w io.Writer) {
assert.Nil(t, NewApplication(mockConfig))
}), "Init facades.Log error: Error logging channel: test")
}

func TestApplication_Channel(t *testing.T) {
mockConfig := &mocksconfig.Config{}
mockConfig.On("GetString", "logging.default").Return("test")
mockConfig.On("GetString", "logging.channels.test.driver").Return("single")
mockConfig.On("GetString", "logging.channels.test.path").Return("test")
mockConfig.On("GetString", "logging.channels.test.level").Return("debug")
mockConfig.On("GetBool", "logging.channels.test.print").Return(true)
app := NewApplication(mockConfig)
assert.NotNil(t, app)
assert.NotNil(t, app.Channel(""))

mockConfig.On("GetString", "logging.channels.dummy.driver").Return("daily")
mockConfig.On("GetString", "logging.channels.dummy.path").Return("dummy")
mockConfig.On("GetString", "logging.channels.dummy.level").Return("debug")
mockConfig.On("GetBool", "logging.channels.dummy.print").Return(true)
mockConfig.On("GetInt", "logging.channels.dummy.days").Return(1)
writer := app.Channel("dummy")
assert.NotNil(t, writer)

mockConfig.On("GetString", "logging.channels.test2.driver").Return("test2")
assert.Contains(t, color.CaptureOutput(func(w io.Writer) {
assert.Nil(t, app.Channel("test2"))
}), "Init facades.Log error: Error logging channel: test2")
}

func TestApplication_Stack(t *testing.T) {
mockConfig := &mocksconfig.Config{}
mockConfig.On("GetString", "logging.default").Return("test")
mockConfig.On("GetString", "logging.channels.test.driver").Return("single")
mockConfig.On("GetString", "logging.channels.test.path").Return("test")
mockConfig.On("GetString", "logging.channels.test.level").Return("debug")
mockConfig.On("GetBool", "logging.channels.test.print").Return(true)
app := NewApplication(mockConfig)
assert.NotNil(t, app)
assert.NotNil(t, app.Stack([]string{}))

mockConfig.On("GetString", "logging.channels.test2.driver").Return("test2")
assert.Contains(t, color.CaptureOutput(func(w io.Writer) {
assert.Nil(t, app.Stack([]string{"", "test2", "daily"}))
}), "Init facades.Log error: Error logging channel: test2")

mockConfig.On("GetString", "logging.channels.dummy.driver").Return("daily")
mockConfig.On("GetString", "logging.channels.dummy.path").Return("dummy")
mockConfig.On("GetString", "logging.channels.dummy.level").Return("debug")
mockConfig.On("GetBool", "logging.channels.dummy.print").Return(true)
mockConfig.On("GetInt", "logging.channels.dummy.days").Return(1)
assert.NotNil(t, app.Stack([]string{"dummy"}))
}
6 changes: 5 additions & 1 deletion log/logrus_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@

func (r *Writer) withStackTrace(message string) {
erisNew := eris.New(message)
if erisNew == nil {
return

Check warning on line 201 in log/logrus_writer.go

View check run for this annotation

Codecov / codecov/patch

log/logrus_writer.go#L201

Added line #L201 was not covered by tests
}

r.message = erisNew.Error()
format := eris.NewDefaultJSONFormat(eris.FormatOptions{
InvertOutput: true,
Expand Down Expand Up @@ -297,7 +301,7 @@
case log.StackDriver:
for _, stackChannel := range config.Get(channelPath + ".channels").([]string) {
if stackChannel == channel {
return errors.New("stack drive can't include self channel")
return errors.New("stack driver can't include self channel")

Check warning on line 304 in log/logrus_writer.go

View check run for this annotation

Codecov / codecov/patch

log/logrus_writer.go#L304

Added line #L304 was not covered by tests
}

if err := registerHook(config, instance, stackChannel); err != nil {
Expand Down
96 changes: 96 additions & 0 deletions mocks/log/Log.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions testing/mock/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ func (r *TestLog) WithContext(ctx context.Context) log.Writer {
return NewTestLogWriter()
}

func (r *TestLog) Channel(channel string) log.Writer {
return NewTestLogWriter()
}

func (r *TestLog) Stack(channels []string) log.Writer {
return NewTestLogWriter()
}

type TestLogWriter struct {
data map[string]any
}
Expand Down
Loading