feat: add processors.http plugin for enrichment#19220
Conversation
|
Thanks so much for the pull request! |
|
!signed-cla |
531e22d to
ac0b7c3
Compare
processors.http plugin for enrichment
Add sample.conf.in as the source template for shared HTTP client options.
The config_includer generator expands {{template}} directives into
sample.conf, matching outputs/http and fixing embed_readme_processors.
Co-authored-by: Daniel Falk <daniel.falk.1@fixedit.ai>
Run make docs to sync sample.conf and README.md with the source template and fix the dirty-repo CI check. Co-authored-by: Daniel Falk <daniel.falk.1@fixedit.ai>
Update sample.conf.in with accurate keep/drop behavior comments and regenerate sample.conf and README.md. Co-authored-by: Daniel Falk <daniel.falk.1@fixedit.ai>
Report serialization and parser instantiation failures as processing_error instead of connection_failed when on_error=keep, since no network I/O occurred for serialization and parser setup is distinct from response parsing (body_read_error). Add unit tests for both cases. Co-authored-by: Daniel Falk <daniel.falk.1@fixedit.ai>
|
I'm a bit unsure about the AWS and GCP authentication options. I kept them just to stay as similar as possible with the input and output http plugins, but considering that there is no reuse in the code between the plugins (as I understand it, that's intentional?), it might be better to remove it from this PR and add it when someone actually need it? |
|
Download PR build artifacts for linux_amd64.tar.gz, darwin_arm64.tar.gz, and windows_amd64.zip. 📦 Click here to get additional PR build artifactsArtifact URLs |
|
First of all thank you very much for your contribution @daniel-falk! Much appreciated! I do have some concerns with the general way this is heading to so let me try to understand what you are trying to achieve.
Wouldn't it be better to add a processor doing exactly this, adding IP details like the geo-location? Adding e.g. a
And this is my main concern: You are building a "general purpose make this API call" thing which tries to introduce coding into a no-code structure. Soon people need something combining multiple calls, e.g. to login first or to do a first API call to lookup some data used in another API call. This will the trigger issues like data-conversion, parsing etc and the plugin complexity will grow as you are already seeing when talking about the data-formats. Such complexity will not be maintainable in the long-run as it tends to creep into a "general interpreter language". :-) This is also the reason we are rejecting "multi-API call" PRs for the http input plugin. If you need tailored stuff, write a plugin! However, I do understand the need for "custom" general metric enrichment but then let's solve it in a general way. Currently this can be done via external plugins where the "algorithm" for enriching the metric lives outside of Telegraf. So my suggestion is, if you want geo-location lookup, please write a processor plugin doing exactly this! If you want "general purpose processing of metrics" please come up with a specification or at least an issue describing a plugin allowing to embed custom code. What do you think? |
|
@srebhan Thanks for your quick response! I created issue #19250 to describe a way to embed custom interpreted code. Generally, I do agree with you regarding the With the processor, I saw the potential to create something that is generic enough to kill multiple birds with one stone, but simple enough to make the design and implementation close to trivial. A generic script runner with I/O support like #19250 would be great for solving bespoke and complex needs, but for many things I think it is overkill. The configuration in the TOML would be much more straight forward and simple with this Regarding the I also do not like that it depends on things being installed in the external system. It makes versioning and portability complex. GRPC is way to much implementation overhead for this. The ability to load external plugins from |
Summary
This PR adds a processor for HTTP enrichment. It takes a metric as input, makes an HTTP request and merges the response with the original metric before emitting it. This is great for two use cases:
Design rationale
The plugin is essentially a combination of
inputs.httpandoutputs.http.I have reused as much functionality as possible and kept the style and terminology from the other plugins.
For functionality like merging, this is done in the same style as in the
processors.parserplugin.Even if I don't like it, I kept the enforcement of same input and output
data_formatas was done in theprocessors.execdplugin. I think that is a more severe limitation in this plugin (and I'm not happy about it in theexecdprocessor either), but I wanted to keep the PR as small as possible. Later, it would be good to add a PR that allows us to use e.g. thetemplateserializer while selecting e.g.jsonorjson_v2as the output parser.Error reporting with
status_codeandresulttags is similar toinput.http_responsebut adapted a bit for this use case. Since this is used for enrichment, I did not want to add any status fields on success, but it does add anhttp_errorfield on error. This keeps the enrichment clean. The two tags are added both on success and failure (unless `on_error = "drop") in which case there won't be any output if we didn't get any response.I'm intentionally not using
bufio.Scannerfor the default response parsing to avoid the fixed size buffer limit it would produce (in some cases we might need to be able to parse very large responses). It is, however, used to parse error responses since I think it's safe to assume they are smaller than 64KB.Example workflow
Running this outputs the following three metrics:
{ "fields": { "as": "AS14593 Space Exploration Technologies Corporation", "city": "Oslo", "country": "Norway", "countryCode": "NO", "ip_address": "209.198.157.145", "isp": "Space Exploration Technologies Corporation", "lat": 59.9138, "lon": 10.7522, "org": "Starlink Frntdeu1", "query": "209.198.157.145", "region": "03", "regionName": "Oslo County", "status": "success", "zip": "" }, "name": "ip_lookup", "tags": { "result": "success", "source": "starlark", "status_code": "200" } } { "fields": { "as": "AS15169 Google LLC", "city": "Ashburn", "country": "United States", "countryCode": "US", "ip_address": "8.8.8.8", "isp": "Google LLC", "lat": 39.03, "lon": -77.5, "org": "Google Public DNS", "query": "8.8.8.8", "region": "VA", "regionName": "Virginia", "status": "success", "zip": "20149" }, "name": "ip_lookup", "tags": { "result": "success", "source": "starlark", "status_code": "200" } } { "fields": { "as": "AS13335 Cloudflare, Inc.", "city": "South Brisbane", "country": "Australia", "countryCode": "AU", "ip_address": "1.1.1.1", "isp": "Cloudflare, Inc", "lat": -27.4766, "lon": 153.0166, "org": "APNIC and Cloudflare DNS Resolver project", "query": "1.1.1.1", "region": "QLD", "regionName": "Queensland", "status": "success", "zip": "4101" }, "name": "ip_lookup", "tags": { "result": "success", "source": "starlark", "status_code": "200" } }With
drop_original = falsewe would also get all the unchanged input metrics:{ "fields": { "ip_address": "209.198.157.145" }, "name": "ip_lookup", "tags": { "source": "starlark" } }Error example with invalid host and
on_error = "keep":{ "fields": { "http_error": "Post \"http://i-do-not-exist.com\": dial tcp: lookup i-do-not-exist.com: no such host", "ip_address": "1.1.1.1" }, "name": "ip_lookup", "tags": { "result": "dns_error", "source": "starlark" } }Error when the parsing fails (and
on_error = "keep"):{ "fields": { "http_error": "parsing response failed: invalid character '\u003c' looking for beginning of value", "ip_address": "1.1.1.1" }, "name": "ip_lookup", "tags": { "result": "body_read_error", "source": "starlark", "status_code": "200" } }Response code not in
success_status_codes(andon_error = "keep"):{ "fields": { "http_error": "received status code 500 (Internal Server Error), expected any value out of [200]. body: \"There was an error processing the request.\"", "ip_address": "1.1.1.1" }, "name": "ip_lookup", "tags": { "result": "response_status_code_mismatch", "source": "starlark", "status_code": "500" } }Checklist
TODO:
parentmerge behavior with multiple metrics in the responseGo isn't my native language, so it would be great if anyone skilled in Go can take a look at the code.
Ideas for future follow-up PRs
data_formatGETLater, I would want to be able to do like:
or perhaps like this:
The templatable URL and query args probably also applies for the
outputs.httpplugin too where the same features would make sense (except fromGETwhich would be a bit strange in an output even if I think there are use cases for it..)Related issues
None: This is a new feature request.