-
Notifications
You must be signed in to change notification settings - Fork 145
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
Add loggly hook. #44
base: v2
Are you sure you want to change the base?
Add loggly hook. #44
Conversation
Sorry for the insanely slow turnaround here. LGTM. Do we have thoughts on the following:
|
|
Any update on this? Would like to get it merged :) |
"message": r.Msg, | ||
"level": r.Lvl.String(), | ||
"timestamp": r.Time, | ||
"data": data, |
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.
Since loggly.Message is a map[string]interface{} I am curious why the "data" value contains a nested map instead of flattening the structure?
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.
Loggly provides excellent searching on nested data. I wanted to add level, message and timestamp because that is expected behavior, but I didn't want it to conflict with any data the user might have set.
|
One of the key features of log15 is the way Handlers connect and create a handling tree. I think properties such as async, buffering (with max buffersize and flush interval) should be handled by a Handler, and the hook to a logging service shouldn't have to implement buffering itself and should just send logs to the logging service. Now ofcourse the whole purpose of the buffering in go-loggly is so that can send multiple log entries in a single call to loggly. And this is actually very important, especially when working with huge amounts of logs. But buffering and bulk writes are also important in other handlers. For instance, a colleague of mine found that some of our application spend a huge amount of time in the kernel due to writes to files and Add an extra interface type: type BulkHandler interface{
LogBulk([]*Record) error
} Some Handlers such as the Then a new Handler would be added: type bufferedBulkHandler struct {
buffer []*Record
handler Handler
bufferSize int // buffer is this size large, and will be flushed when it's full
flushInterval time.Duration
forceFlushLevel Level // buffer is flushed directly (while blocking the logging call) after a message with this level (or higher) is added.
}
func BufferedBulkHander(size int, interval time.Duration, forceFlushLevel Level, handler Handler) Handler The Maybe in this case (because of the number of options) the What I propose is:
What I'm not sure about is if |
Thanks for the detailed thoughts, and I like where this is headed! I like how you are breaking this down into nicely composable pieces. I think you have some great ideas here that could add some nice capabilities to log15 as a whole. I would be happy to work with you to polish your ideas and get them into log15. How about we break this into multiple issues. This PR can be for implementing the simple loggly handler. Then let's create a new issue for the bulk logging idea. |
Great! I'll update this PR with the loggly http API asap, probably this evening (CET). |
Some delay: I want to know if the log level (debug, info, crit) should be in a specific format so it is recognized by loggly. Because |
Nevermind, it looks like levels are not defined by loggly. |
ec268ff
to
51b2290
Compare
@ChrisHines I just updated the PR. |
Ah well, there is one external dependency: |
The bulk logging stuff is interesting, I'll comment on that in the separate issue you opened for it. I'm concerned about the exported symbols I'd propose instead that |
@inconshreveable You're right, I'm updating the code right now. |
This handler sends log15 records to loggly. No third party dependencies are required.
There, removed the |
LGTM. Last thing we need is just a test. We could set up an HTTP server in the test and then set up a loggly handler with a custom URL pointing at the local server and validate that we get properly formatted loggly data. |
From Loggly's website:
Which raises the question: Should the LogglyHandler accept a log15.Format rather than only using JSON? |
Loggly strongly recommends sending JSON for this type of thing. They do indeed accept all kind of logs so that you can pipe apache, nginx, syslog, etc. etc. directly to their API. They then analyse the log and parse it as best as they can to make it search-able, so you can put filters on fields and their values. But since we already have structured data in the form of log15.Ctx, it's best to send it as JSON so we're sure the structure is copied into the loggly the same way. It wouldn't make sense to use any other formatter and then just hope loggly manages to parse the text to it's original structure. |
I've pushed a work in progress test, but it's not working.. Looks like a problem with the channel (might be a Go issue on tip). To be continued later. |
I understand, but as library authors I think we should give the application developer that choice. Plus, we already have |
It really makes no sense to send anything other than JSON to Loggly, unless you don't have JSON. For the File, Stream and Net handlers this does make sense, because in some cases you want human-readable data, so thats why you use a Formatter. But in this case Loggly already parses the data and presents it through their webinterface. JSON is a very reliable format for data transfer, especially with the standardized encoding of bytes, times, decimals and strings (escaping), and there's no reason to use anything else. I do like the idea of allowing the user to modify the data sent, but I think the use of that feature will be quite low. And using |
If someone does really require to set a custom Formatter for some reason, then they can either fork the LogglyHandler and add it with a few lines of code. And when there is a greater demand (which I would be willing to bet more than a few beers on that it won't happen), a Formatter field can always be added to the LogglyHandler later 👍 What do you think about |
Agreed, I see no reason to send anything over than JSON. The format is irrelevant in this case because we're sending to a machine. The goal is no loss of fidelity, and JSON does that well, and is recommended by loggly. Even if we're wrong on that, it covers the 90% case and we'll learn from someone if we're wrong. I'm ready to merge when we have a passing test. |
I concede the point on making JSON the default format, but I'm still not convinced that the formatting should be hard coded into the handler. I will make my case below, and then I am fine with the majority opinion. Suppose someone constructs the following handler tree. h := FailoverHandler(
LogglyHandler(),
FileHandler("path", JsonFormat()),
) The intention is to save logs to disk if Loggly is unreachable and batch upload the files later when Loggly is again reachable. The problem is that the logs on disk are in a different flavor of JSON than logs sent on the happy path. So I would like to see one of two outcomes before we merge:
|
Agreed. I'd prefer to use JsonFormat internally, if possible. Are we using the go generated JSON encoding just for speed, or is there another reason? Otherwise I'm fine with exposing a |
@ChrisHines Good point, I can definitely see that as a use-case, especially in larger cluster systems that should be able to handle connectivity downtime without losing information. @inconshreveable I would prefer a Maybe having a type FormattingWriter interface {
WriteFormatted(w io.Writer, r *Record)
} TerminalFormat and LogfmtFormat already use a bytes.Buffer{} internally so handing it a different io.Writer should work. JsonFormat now uses json.Marshal which in turn wraps a json.Encoder + bytes.Buffer{}. So in the end, every formatter could actually work with an io.Writer without much change, and that makes a lot of sense with the buffering in #49. Anyway, this should probably be a new issue. |
I'm eagerly awaiting this merge. Any movement on this? |
This is what we were waiting on: "I've pushed a work in progress test, but it's not working.. Looks like a problem with the channel (might be a Go issue on tip). To be continued later." If you'd like to write a test that accurately tests the functionality and works I'm happy to merge this in. |
Fixes #42