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

Cloudwatch Logs producer #166

Merged
merged 27 commits into from
Feb 28, 2018
Merged

Cloudwatch Logs producer #166

merged 27 commits into from
Feb 28, 2018

Conversation

luqasz
Copy link
Contributor

@luqasz luqasz commented Jul 9, 2017

Hi

I'd like to add cloudwatch logs producer to gollum. AWS puts some limitations on this service which are written in this plugin constants. I have couple of problems here.

  • Which type of base producer to use. AWS limits uploads to 5 / second.
  • I'd like to use templates for outgoing messages. Some one might want to send only syslog message itself and ignore stuff like priority and facility. I need to parse messages into a struct to be able to use templates. Any formatter can do that ?

Can you please give some advice / guide ?

@coveralls
Copy link

Coverage Status

Coverage remained the same at 65.035% when pulling 66eff2d on luqasz:master into fe234d9 on trivago:master.

Copy link
Contributor

@arnecls arnecls left a comment

Choose a reason for hiding this comment

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

This code does not compile.

Also:

  • Don't use "_" in filenames please. Go has special meanings for those postfixes. All lowercase, one word.
  • Please provide an integration test if possible

// Configure initializes this producer with values from a plugin config.
func (prod *CloudwatchLogs) Configure(conf core.PluginConfigReader) error {
prod.SetStopCallback(prod.flush)
stream := conf.GetString("stream", "")
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider using struct tags for stream and group (non-pointer) and only leave the checks on empty in the config method

// do anything reasonable with rejected logs. Ignore it.
// Meybe expose some statistics for rejected counters.
resp, err := dst.svc.PutLogEvents(prod.cloudwatchLogsParams)
if err == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

if err != nil there should be an error message logged


type CloudwatchLogs struct {
core.BufferedProducer `gollumdoc:"embed_type"`
stream *string
Copy link
Contributor

Choose a reason for hiding this comment

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

Make these strings non-pointer.

  • It will make it easier to process your config
  • Strings are not scattered around in memory (cache locality, memory fragmentation, etc - won't hurt you here but in general I would consider it a good practice)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've created them as pointers to easily set them in upload params.

}
err = prod.createStream()
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Fine like this, but you could also do it like this:

func (prod *CloudwatchLogs) create() error {
    if err := prod.createGroup(); err != nil {
        return err
    }
    return prod.createStream()
}

I'll leave it up to you which version you like more.

// http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogStream.html
func (prod *CloudwatchLogs) createStream() error {
params := &cloudwatchlogs.CreateLogStreamInput{
LogGroupName: &prod.group,
Copy link
Contributor

Choose a reason for hiding this comment

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

This should not compile ...?
LogGroupName is a *string, not a **string.

}
}

func (prod *Console) Produce(workers *sync.WaitGroup) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be func (prod *CloudwatchLogs) ...

func (prod *Console) Produce(workers *sync.WaitGroup) {
defer prod.WorkerDone()
prod.AddMainWorker(workers)
prod.MessageControlLoop(prod.printMessage)
Copy link
Contributor

Choose a reason for hiding this comment

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

printMessage function does not exist.
You probably want to call upload here?
If you use the buffered producer you will only get single messages though.
If you want to send batches of messages you should have a look at core.BatchProducer

"time"
)

/*
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use // when doing comments.
Some people use /* .. */ to temporarily remove code and its super annoying if you have block comments inside your code that force you to create multiple blocks.

// Put log events and update sequence token.
// Possible errors http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
func (prod *CloudwatchLogs) upload() {
logevents := make([]*cloudwatchlogs.InputLogEvent, 0, len(events))
Copy link
Contributor

Choose a reason for hiding this comment

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

where does events come from?

@coveralls
Copy link

Coverage Status

Coverage increased (+0.6%) to 65.655% when pulling f956fe1 on luqasz:master into fe234d9 on trivago:master.

@luqasz
Copy link
Contributor Author

luqasz commented Jul 10, 2017

Where should I put code to:

  • get instance metadata (which will communicate with AWS external resources)
  • get token (which will communicate with AWS external resources)

It should be launched before first upload to cloudwatch. Is Produce() a good place ?

@coveralls
Copy link

Coverage Status

Coverage increased (+0.6%) to 65.655% when pulling 0281b8a on luqasz:master into fe234d9 on trivago:master.

Copy link
Contributor

@msiebeneicher msiebeneicher left a comment

Choose a reason for hiding this comment

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

Please also check the current linter issues: https://travis-ci.org/trivago/gollum/jobs/252132692

// PutLogEvents 5 requests/second/log stream.
putLogEventsDelay = 200 * time.Millisecond
)

Copy link
Contributor

Choose a reason for hiding this comment

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

Please provide a documentation and a config example . Look here for an example: https://github.com/trivago/gollum/blob/master/producer/elasticsearch.go#L31


type CloudwatchLogs struct {
core.BufferedProducer `gollumdoc:"embed_type"`
stream string `config:"Stream" default:""`
Copy link
Contributor

Choose a reason for hiding this comment

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

"Stream" is quite close to the standard "Streams".
As by line 93 I would rename this to "LogStreamPrefix". Avoids ambiguity in the config.

type CloudwatchLogs struct {
core.BufferedProducer `gollumdoc:"embed_type"`
stream string `config:"Stream" default:""`
group string `config:"Group" default:""`
Copy link
Contributor

Choose a reason for hiding this comment

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

To align with my previous proposal I would rename this to "LogGroup".

// Put log events and update sequence token.
// Possible errors http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
func (prod *CloudwatchLogs) upload(msg *core.Message) {
logevents := make([]*cloudwatchlogs.InputLogEvent, 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

You create logevents here and upload them later, but there is no data added to it ...?
Also this looks like you actually want to upload more than one event at a time.
As of this I would suggest you base this plugin on core.BatchProducer which gives you a list of messages instead of just one message.

I would suggest to have a look at producer.Kinesis for that.

if conf.GetString("group", "") == "" {
prod.Logger.Error("group name can not be empty")
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

prod.service is not initialized at all.
If you need help about creation of the session and config object have a look at producer.Kinesis, producer.S3 or consumer.Kinesis (and of course the AWS SDK docs)

@arnecls
Copy link
Contributor

arnecls commented Jul 12, 2017

Which type of base producer to use. AWS limits uploads to 5 / second.

Like written in my code comments - for AWS a core.BatchProducer is normally the best option as you get a batch of messages instead of single messages. As an advice - have a look at the other AWS producers (Kinesis and S3). Most of the common stuff is in there.

I'd like to use templates for outgoing messages. Some one might want to send only syslog message itself and ignore stuff like priority and facility. I need to parse messages into a struct to be able to use templates. Any formatter can do that?

Normally we use TextToJSON or write a custom formatter for that. After that you can use the different JSON processors (processJSON, templateJSON) to modify your message. This process will change with 0.6.0 (see #171) but for now this is how it works.

Also - please test your plugin on AWS and notify us when this actually works.
Until then I will mark this as "work in progress".

@arnecls arnecls added the WIP label Jul 12, 2017
@luqasz
Copy link
Contributor Author

luqasz commented Jul 12, 2017

Thx for your feedback. I will let you know when I am finished.

@luqasz
Copy link
Contributor Author

luqasz commented Jul 18, 2017

How can I set default max batch size ? In kinesis producer dosc state that BatchMaxMessages is set to 500 but there is no code for that in producer/kinesis.go.

@msiebeneicher
Copy link
Contributor

The batch settings comes from the embedded batchedproducer.
Here the embed producer: https://github.com/trivago/gollum/blob/master/producer/kinesis.go#L98
And here the settings comes from: https://github.com/trivago/gollum/blob/master/core/batchedproducer.go#L44

But the docs are not correct. The new configuration have to look like

Batch:
  MaxCount: 8192
  FlushCount: 4096
  TimeoutSec: 5

We will update all docs by #100

@luqasz
Copy link
Contributor Author

luqasz commented Jul 19, 2017

Ok. Is there any way to set this in code ? I would like to set max limits based on AWS limits so they will not be exceeded by user configuring gollum.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 65.291% when pulling 0b8298b on luqasz:master into 2be9190 on trivago:master.

@msiebeneicher
Copy link
Contributor

@luqasz:

  1. The unit test fails because the TestProducerInterface try to instantiate all producers.
    Because of the empty LogStream and LogGroup settings we get this error.

To fix this i would recommend this change to fix the current issue:

// Configure initializes this producer with values from a plugin config.
func (prod *AwsCloudwatchLogs) Configure(conf core.PluginConfigReader) {
	if prod.stream == "" {
		prod.Logger.Error("LogStream can not be empty")
	}
	if prod.group == "" {
		prod.Logger.Error("LogGroup can not be empty")
	}
	if conf.GetInt("Batch/MaxCount", maxBatchEvents) > maxBatchEvents {
		conf.Errors.Pushf("Batch/MaxCount must be below %d", maxBatchEvents)
	}
}
  1. Please check the new guidelines of how we document the plugins now: http://gollum.readthedocs.io/en/latest/src/instructions/writingPlugins/docs.html
    It is important to use the correct format because we auto-generate the documentation out of the code.

In this case the doc part should look like this example:

// AwsCloudwatchLogs producer plugin
//
// The AwsCloudwatchLogs producer plugin sends messages to
// AWS Cloudwatch Logs service.
//
// Parameters
//
// - LogStream: xxxx
//
// - LogGroup: xxx
//
// Examples
//
// This example ...:
//
//	CwLogs:
//		Type: AwsCloudwatchLogs:
//			LogStream: stream_name
//			LogGroup: group_name
//    Credential:
//      Type: shared
//

After the requested changes this PR should be mergeable! :)
thx for your work! 👍

@luqasz
Copy link
Contributor Author

luqasz commented Aug 9, 2017

I will catch you later. I am busy now. Thx for reply.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.02%) to 70.752% when pulling e714a05 on luqasz:master into 706e082 on trivago:master.

@luqasz
Copy link
Contributor Author

luqasz commented Sep 5, 2017

Hi. Do you need any thing else from this PR ?

@msiebeneicher
Copy link
Contributor

@luqasz : Sorry for the delay! We will merge this PR after the final v0.5.0 release. Last time I saw no further issues with this PR - so i think we are able to merge this directly after the release.

Łukasz Kostka added 2 commits September 8, 2017 12:19
Handle errors in a separate function. Create group, stream if needed.
Fixes errors when no group / stream is present when uploading.
@luqasz
Copy link
Contributor Author

luqasz commented Sep 8, 2017

I've allowed myself to add a separate function to handle upload results. Usefull when group / stream was removed during program runtime. Gollum will create group / stream (without uploading batch) so next uploads will succeed.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 71.244% when pulling 3611427 on luqasz:master into 7feb8ac on trivago:master.

@luqasz
Copy link
Contributor Author

luqasz commented Feb 13, 2018

@arnecls @msiebeneicher Do you need anything from me to merge this PR ?

@arnecls
Copy link
Contributor

arnecls commented Feb 25, 2018

LGTM. If @msiebeneicher does not find anything I'll do the merge on Wednesday when I'm back at the office.

@arnecls arnecls modified the milestones: v0.6.0, v0.5.x Feb 25, 2018
@arnecls arnecls merged commit 3b7caa5 into trivago:master Feb 28, 2018
@arnecls
Copy link
Contributor

arnecls commented Feb 28, 2018

Merged.
Thank you for contributing and for your patience 🍺

Will be released along with 0.5.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants