-
Notifications
You must be signed in to change notification settings - Fork 28
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
Syslog drain app error messages in app log stream #633
base: main
Are you sure you want to change the base?
Changes from all commits
2db1096
0a4a3c2
24a73ee
65e1956
e06d381
10b3a6a
7336dcf
7a487f0
e216c5a
8660c84
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package testhelper | ||
|
||
import "code.cloudfoundry.org/loggregator-agent-release/src/pkg/egress/syslog" | ||
|
||
type SpyAppLogEmitter struct { | ||
} | ||
|
||
func (emitter *SpyAppLogEmitter) EmitLog(appID string, message string) { | ||
|
||
} | ||
|
||
func NewSpyAppEmitter() SpyAppLogEmitter { | ||
return SpyAppLogEmitter{} | ||
} | ||
|
||
type SpyAppLogEmitterFactory struct { | ||
logClient syslog.LogClient | ||
sourceIndex string | ||
} | ||
|
||
func (factory *SpyAppLogEmitterFactory) LogClient() syslog.LogClient { | ||
return factory.logClient | ||
} | ||
|
||
func (factory *SpyAppLogEmitterFactory) SourceIndex() string { | ||
return factory.sourceIndex | ||
} | ||
|
||
func (factory *SpyAppLogEmitterFactory) NewAppLogEmitter(logClient syslog.LogClient, sourceIndex string) syslog.AppLogEmitter { | ||
factory.logClient = logClient | ||
factory.sourceIndex = sourceIndex | ||
emitter := NewSpyAppEmitter() | ||
return &emitter | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package testhelper | ||
|
||
import ( | ||
"code.cloudfoundry.org/go-loggregator/v10" | ||
v2 "code.cloudfoundry.org/go-loggregator/v10/rpc/loggregator_v2" | ||
"sync" | ||
) | ||
|
||
type spyLogClient struct { | ||
mu sync.Mutex | ||
_message []string | ||
_appID []string | ||
|
||
// We use maps to ensure that we can query the keys | ||
_sourceType map[string]struct{} | ||
_sourceInstance map[string]struct{} | ||
} | ||
|
||
func NewSpyLogClient() *spyLogClient { | ||
return &spyLogClient{ | ||
_sourceType: make(map[string]struct{}), | ||
_sourceInstance: make(map[string]struct{}), | ||
} | ||
} | ||
|
||
func (s *spyLogClient) EmitLog(message string, opts ...loggregator.EmitLogOption) { | ||
s.mu.Lock() | ||
defer s.mu.Unlock() | ||
|
||
env := &v2.Envelope{ | ||
Tags: make(map[string]string), | ||
} | ||
|
||
for _, o := range opts { | ||
o(env) | ||
} | ||
|
||
s._message = append(s._message, message) | ||
s._appID = append(s._appID, env.SourceId) | ||
s._sourceType[env.GetTags()["source_type"]] = struct{}{} | ||
s._sourceInstance[env.GetInstanceId()] = struct{}{} | ||
} | ||
|
||
func (s *spyLogClient) Message() []string { | ||
s.mu.Lock() | ||
defer s.mu.Unlock() | ||
|
||
return s._message | ||
} | ||
|
||
func (s *spyLogClient) AppID() []string { | ||
s.mu.Lock() | ||
defer s.mu.Unlock() | ||
|
||
return s._appID | ||
} | ||
|
||
func (s *spyLogClient) SourceType() map[string]struct{} { | ||
s.mu.Lock() | ||
defer s.mu.Unlock() | ||
|
||
// Copy map so the orig does not escape the mutex and induce a race. | ||
m := make(map[string]struct{}) | ||
for k := range s._sourceType { | ||
m[k] = struct{}{} | ||
} | ||
|
||
return m | ||
} | ||
|
||
func (s *spyLogClient) SourceInstance() map[string]struct{} { | ||
s.mu.Lock() | ||
defer s.mu.Unlock() | ||
|
||
// Copy map so the orig does not escape the mutex and induce a race. | ||
m := make(map[string]struct{}) | ||
for k := range s._sourceInstance { | ||
m[k] = struct{}{} | ||
} | ||
|
||
return m | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package syslog | ||
|
||
import ( | ||
"code.cloudfoundry.org/go-loggregator/v10" | ||
) | ||
|
||
// LogClient is used to emit logs. | ||
type LogClient interface { | ||
EmitLog(message string, opts ...loggregator.EmitLogOption) | ||
} | ||
|
||
// AppLogEmitter abstracts the sending of a log to the application log stream. | ||
type AppLogEmitter interface { | ||
EmitLog(appID string, message string) | ||
} | ||
|
||
// DefaultAppLogEmitter is an implementation of AppLogEmitter which sends logs to an instance of a LogClient | ||
type DefaultAppLogEmitter struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we rename this to something else please? The word Usually in the Loggregator's code we have a single struct, from which the default implementation is create and then we apply options... We should be consistent here as well with the naming and the implementation. If the |
||
logClient LogClient | ||
sourceIndex string | ||
} | ||
|
||
// EmitLog writes a message in the application log stream using a LogClient. | ||
func (appLogEmitter *DefaultAppLogEmitter) EmitLog(appID string, message string) { | ||
if appLogEmitter.logClient == nil || appID == "" { | ||
return | ||
} | ||
|
||
option := loggregator.WithAppInfo(appID, "LGR", "") | ||
appLogEmitter.logClient.EmitLog(message, option) | ||
|
||
option = loggregator.WithAppInfo( | ||
appID, | ||
"SYS", | ||
appLogEmitter.sourceIndex, | ||
) | ||
appLogEmitter.logClient.EmitLog(message, option) | ||
} | ||
|
||
// AppLogEmitterFactory is used to create new instances of AppLogEmitter | ||
type AppLogEmitterFactory interface { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this interface and the If you've used interface for better testing, you might also use reflection in the test and check if the created instance has its fields properly configured. This will also simplify the other code which calls this method and the need of passing another factory around. |
||
NewAppLogEmitter(logClient LogClient, sourceIndex string) AppLogEmitter | ||
} | ||
|
||
// DefaultAppLogEmitterFactory implementation of AppLogEmitterFactory to produce DefaultAppLogEmitter. | ||
type DefaultAppLogEmitterFactory struct { | ||
} | ||
|
||
// NewAppLogEmitter creates a new DefaultAppLogEmitter. | ||
func (factory *DefaultAppLogEmitterFactory) NewAppLogEmitter(logClient LogClient, sourceIndex string) AppLogEmitter { | ||
return &DefaultAppLogEmitter{ | ||
logClient: logClient, | ||
sourceIndex: sourceIndex, | ||
} | ||
} | ||
|
||
func NewDefaultAppLogEmitterFactory() DefaultAppLogEmitterFactory { | ||
return DefaultAppLogEmitterFactory{} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a need for an interface here? Who else implements the
EmitLog
method? Could this interface be deleted and the method attached to theDefaultAppLogEmitter
?