-
Notifications
You must be signed in to change notification settings - Fork 55
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
Reporter package #116
Reporter package #116
Conversation
x/reporter/reporter.go
Outdated
return true | ||
} | ||
} | ||
|
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.
TODO: WriteCollector
that takes a io.Writer
x/reporter/reporter.go
Outdated
log.Printf("Error encoding JSON: %s\n", err) | ||
return err |
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.
log.Printf("Error encoding JSON: %s\n", err) | |
return err | |
return fmt.Errorf("failed to marshal JSON: %w", err) |
x/reporter/reporter.go
Outdated
"time" | ||
) | ||
|
||
var debugLog log.Logger = *log.New(io.Discard, "", 0) |
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.
We should remove all logging from this library.
var debugLog log.Logger = *log.New(io.Discard, "", 0) |
x/reporter/reporter.go
Outdated
if random < samplingRate { | ||
err := c.collector.Collect(ctx, report) | ||
if err != nil { | ||
log.Printf("Error collecting report: %v", err) |
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.
log.Printf("Error collecting report: %v", err) |
x/reporter/reporter.go
Outdated
) | ||
|
||
var debugLog log.Logger = *log.New(io.Discard, "", 0) | ||
var httpClient = &http.Client{} |
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.
Pass the client to the RemoteReporter
var httpClient = &http.Client{} |
x/reporter/reporter.go
Outdated
} | ||
|
||
type RemoteCollector struct { | ||
collectorEndpoint *url.URL |
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.
collectorEndpoint *url.URL | |
httpClient *http.Client | |
collectorEndpoint *url.URL |
x/reporter/reporter.go
Outdated
} | ||
} | ||
|
||
type RetryCollector struct { |
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.
All public types and function must have comments.
x/reporter/reporter.go
Outdated
} | ||
} | ||
|
||
type RetryCollector struct { |
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.
type RetryCollector struct { | |
// RetryCollector is a [Collector] that retries in case of [BadRequestErr]. | |
type RetryCollector struct { |
x/reporter/reporter.go
Outdated
type BadRequestErr struct { | ||
StatusCode int | ||
Message string | ||
} | ||
|
||
func (e BadRequestErr) Error() string { | ||
return e.Message | ||
} |
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.
type BadRequestErr struct { | |
StatusCode int | |
Message string | |
} | |
func (e BadRequestErr) Error() string { | |
return e.Message | |
} | |
type BadRequestError { | |
error | |
} | |
func (e BadRequestError) Unwrap() error { | |
return e.error | |
} |
Then you can do:
return BadRequestError{err}
x/reporter/reporter.go
Outdated
debugLog.Printf("Error reading the HTTP response body: %s\n", err) | ||
return err | ||
} | ||
if resp.StatusCode >= 400 { |
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.
if resp.StatusCode >= 400 { | |
if 400 <= resp.StatusCode && resp.StatusCode < 500 { |
x/reporter/reporter.go
Outdated
return BadRequestErr{ | ||
StatusCode: resp.StatusCode, | ||
Message: fmt.Sprintf("HTTP request failed with status code %d", resp.StatusCode), | ||
} |
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.
return BadRequestErr{ | |
StatusCode: resp.StatusCode, | |
Message: fmt.Sprintf("HTTP request failed with status code %d", resp.StatusCode), | |
} | |
return BadRequestErr{err} |
x/reporter/reporter.go
Outdated
var debugLog log.Logger = *log.New(io.Discard, "", 0) | ||
var httpClient = &http.Client{} | ||
|
||
type BadRequestErr struct { |
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.
It seems the standard library uses the Err
prefix for error constants and the Error
suffix for type, so we should use BadRequestError
here.
x/report/report.go
Outdated
// MultiCollector represents a collector that combines multiple collectors. | ||
type MultiCollector struct { | ||
collectors []Collector | ||
} | ||
|
||
// Collect implements [Collector] interface on [MultiCollector] type. | ||
// It collects the report using all the provided collectors in the [MultiCollector]. | ||
// It returns an error if all collectors fail to collect the report. | ||
func (c *MultiCollector) Collect(ctx context.Context, report Report) error { | ||
success := false | ||
for i := range c.collectors { | ||
err := c.collectors[i].Collect(ctx, report) | ||
if err != nil { | ||
success = success || false | ||
} else { | ||
success = success || true | ||
} | ||
} | ||
if success { | ||
// At least one collector succeeded | ||
return nil | ||
} | ||
return errors.New("all collectors failed") | ||
} | ||
|
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.
Let's drop this for now, since the semantics can be confusing and it's not clear we will use it.
// MultiCollector represents a collector that combines multiple collectors. | |
type MultiCollector struct { | |
collectors []Collector | |
} | |
// Collect implements [Collector] interface on [MultiCollector] type. | |
// It collects the report using all the provided collectors in the [MultiCollector]. | |
// It returns an error if all collectors fail to collect the report. | |
func (c *MultiCollector) Collect(ctx context.Context, report Report) error { | |
success := false | |
for i := range c.collectors { | |
err := c.collectors[i].Collect(ctx, report) | |
if err != nil { | |
success = success || false | |
} else { | |
success = success || true | |
} | |
} | |
if success { | |
// At least one collector succeeded | |
return nil | |
} | |
return errors.New("all collectors failed") | |
} |
x/report/report.go
Outdated
if errors.As(err, &e) { | ||
break | ||
} else { | ||
time.Sleep(c.waitBetweenRetry) |
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.
This should do exponential backoff: https://harish-bhattbhatt.medium.com/best-practices-for-retry-pattern-f29d47cd5117
@fortuna I refactored the send report functionality with random sampling into a separate
reporter
package to be imported when needed in other apps. Since I am using this logic in bothoutline-connectivity
andoutline-connectivity-app
, it would be repetitive to have the code block in both places. I recommend dropping the other PR and get this PR merged first:#112
I can then do another PR for refactored main.go for for both connectivity apps that uses this package for reporting.