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

Promtail: Add compressed files support #6708

Merged
merged 37 commits into from
Sep 27, 2022

Conversation

DylanGuedes
Copy link
Contributor

@DylanGuedes DylanGuedes commented Jul 18, 2022

What this PR does / why we need it:
Adds to Promtail the ability to read compressed files. It works by:

  1. Infer which compression format to use based on the file extension
  2. Uncompress the file with the native golang/compress packages
  3. Iterate over uncompressed lines and send them to Loki

Its usage is the same as our current file tailing. In the example below all files under logfiles folder are parsed/processed, regardless of being compressed or not. file1 and file2 are first uncompressed with Gunzip; file3 is parsed as it is.

scrape_configs:
  - job_name: simplejob
    static_configs:
      - targets: [localhost]
        labels:
          job: compressedfolder
          __path__: /logfiles/**.*
$ ls /logfiles
     file1.tar.gz file2.tar.gz file3.txt

Which issue(s) this PR fixes:
Fixes #5956

Special notes for your reviewer:
Another approach is to infer the compression format based on the header bytes but this approach is much simpler; in the future we can improve it if we think the community needs it.

Checklist

  • Documentation added
  • Tests updated
  • Is this an important fix or new feature? Add an entry in the CHANGELOG.md.
  • Changes that require user attention or interaction to upgrade are documented in docs/sources/upgrading/_index.md

@DylanGuedes DylanGuedes requested a review from a team as a code owner July 18, 2022 15:20
@DylanGuedes DylanGuedes changed the title Add compressed files support for Promtail Add compressed files support to Promtail Jul 18, 2022
@DylanGuedes DylanGuedes changed the title Add compressed files support to Promtail Promtail: Add compressed files support to Promtail Jul 18, 2022
@DylanGuedes DylanGuedes changed the title Promtail: Add compressed files support to Promtail Promtail: Add compressed files support Jul 18, 2022
@DylanGuedes DylanGuedes force-pushed the promtail-compressed-support branch 3 times, most recently from eeffa05 to d18a0a1 Compare July 18, 2022 18:09
- The new Reader interface is used across source code instead of the `tailer`
- `tailer` is an implementation of Reader
- Decompresser implements the file.Reader interface
- Decompresser infers which protocol to use to unzip file based on its
  extension
@DylanGuedes DylanGuedes force-pushed the promtail-compressed-support branch from d18a0a1 to fd6162d Compare July 19, 2022 13:20
@DylanGuedes DylanGuedes force-pushed the promtail-compressed-support branch from fd6162d to f94e7e2 Compare July 19, 2022 13:44
@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0%
+               loki	0%

Copy link
Contributor

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

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

Overall looks awesome @DylanGuedes!
I'm a bit concerned by the potential here for resource overuse by reading the whole decompressed file into memory, from what I can see.

clients/pkg/promtail/targets/file/decompresser.go Outdated Show resolved Hide resolved
clients/pkg/promtail/targets/file/decompresser.go Outdated Show resolved Hide resolved
Comment on lines +152 to +154
// It first decompress the file as a whole using a reader and then it will iterate
// over its chunks, separated by '\n'.
// During each iteration, the parsed and decoded log line is then sent to the API with the current timestamp.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is what I was kinda worried about seeing...
If the file is compressed, it could potentially store a huge amount of data which we need to entirely load into memory to iterate over.

Is there a more resource-efficient way we could do this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Totally @dannykopping ... compression ratios >99% are common when thinking about uniformly structured logs like access or event logs. Reading a whole file into memory would totally not be an option. When suggesting implementing this along the lines of piping data via zcat into an existing tool (#5956 (comment)). And isn't that exactly what the mountReader method does? It gets the right type of reader (which is able to read a compressed data stream of various algorithms) and then hands that off to be read from line by line. But there is not slurping into memory, is there?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great observation, thanks. I'm relying on whatever the underlying readers are doing; I'm not sure if all of them (like flatten/zip) supports doing inline uncompression without buffering, but I'm not either explicitly decompressing things as a whole with something like tar -xzvf myfile.tar.gz, so it might be supported. I'm planning on doing a few tests with this this week, one of them will be to uncompressed big files and another one will be to rotate files to see how it behaves.

Copy link
Contributor

Choose a reason for hiding this comment

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

We tested this together, and found that it is indeed allocating a lot of memory but the GC acts as it should, and the memory doesn't grow unbounded. Dylan also verified that the files are decompressed progressively, so at no time do we try to load the whole file into memory at once, so the overall memory usage will not spike very high (we use small buffers when reading the compressed files). We should add a note in the docs to specify that GC frequency will increase dramatically when decompressing large files, and this will cause CPU usage to rise.

clients/pkg/promtail/targets/file/decompresser.go Outdated Show resolved Hide resolved
clients/pkg/promtail/targets/file/filetarget.go Outdated Show resolved Hide resolved
Comment on lines +152 to +154
// It first decompress the file as a whole using a reader and then it will iterate
// over its chunks, separated by '\n'.
// During each iteration, the parsed and decoded log line is then sent to the API with the current timestamp.
Copy link
Contributor

Choose a reason for hiding this comment

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

Totally @dannykopping ... compression ratios >99% are common when thinking about uniformly structured logs like access or event logs. Reading a whole file into memory would totally not be an option. When suggesting implementing this along the lines of piping data via zcat into an existing tool (#5956 (comment)). And isn't that exactly what the mountReader method does? It gets the right type of reader (which is able to read a compressed data stream of various algorithms) and then hands that off to be read from line by line. But there is not slurping into memory, is there?

clients/pkg/promtail/targets/file/decompresser.go Outdated Show resolved Hide resolved
clients/pkg/promtail/targets/file/decompresser.go Outdated Show resolved Hide resolved
@DylanGuedes
Copy link
Contributor Author

@dannykopping @frittentheke thank you both for the fantastic reviews, I'll be working on those suggestions and other ones that might come along the week and I plan to try testing a few things with this too.

Just one thing, I think even if this doesn't add decent support for compressed log rotations, we should still proceed to at least have this, as a simpler implementation that is to be improved in a follow-up PR (I can start working on it exactly after this gets merged). My argument is that as of now Loki isn't supporting even the most straightforward compression scenarios, so this would at least add some support without a lot of burdens.
For instance, I liked that with this PR you can now tell Promtail to scrape from a folder that has tons of compressed files and that you can keep appending new compressed files to it and all of them will be scraped.

@frittentheke
Copy link
Contributor

For instance, I liked that with this PR you can now tell Promtail to scrape from a folder that has tons of compressed files and that you can keep appending new compressed files to it and all of them will be scraped.

Yes. Question is though, in what order those files will be picked up? Does Promtail sort them by modification date? That would then at least keep as much order as possible.

@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0%
+               loki	0%

DylanGuedes and others added 2 commits July 25, 2022 10:28
- It tells that the file was compressed when it actually just mounted its reader
- Add file extension to it
- Change message log level from Info to Debug
- Enhance grammar of log message (with -> has)

Co-authored-by: Danny Kopping <dannykopping@gmail.com>
@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0%
+               loki	0%

@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0%
+               loki	0%

@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0.4%
+               loki	0%

@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0%
+               loki	0%

@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0%
+               loki	0%

@github-actions github-actions bot added the type/docs Issues related to technical documentation; the Docs Squad uses this label across many repositories label Sep 21, 2022
@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0%
+               loki	0%

@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0%
+               loki	0%

Copy link
Contributor

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

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

LGTM, some small doc changes and I think we should change that buffer size - but after that, merge at will!

Comment on lines +152 to +154
// It first decompress the file as a whole using a reader and then it will iterate
// over its chunks, separated by '\n'.
// During each iteration, the parsed and decoded log line is then sent to the API with the current timestamp.
Copy link
Contributor

Choose a reason for hiding this comment

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

We tested this together, and found that it is indeed allocating a lot of memory but the GC acts as it should, and the memory doesn't grow unbounded. Dylan also verified that the files are decompressed progressively, so at no time do we try to load the whole file into memory at once, so the overall memory usage will not spike very high (we use small buffers when reading the compressed files). We should add a note in the docs to specify that GC frequency will increase dramatically when decompressing large files, and this will cause CPU usage to rise.

clients/pkg/promtail/targets/file/decompresser.go Outdated Show resolved Hide resolved
docs/sources/clients/promtail/_index.md Outdated Show resolved Hide resolved
docs/sources/clients/promtail/_index.md Outdated Show resolved Hide resolved
docs/sources/clients/promtail/_index.md Outdated Show resolved Hide resolved
docs/sources/clients/promtail/_index.md Outdated Show resolved Hide resolved
docs/sources/clients/promtail/_index.md Outdated Show resolved Hide resolved
docs/sources/clients/promtail/_index.md Outdated Show resolved Hide resolved
DylanGuedes and others added 2 commits September 27, 2022 10:04
Co-authored-by: Danny Kopping <dannykopping@gmail.com>
@grafanabot
Copy link
Collaborator

./tools/diff_coverage.sh ../loki-main/test_results.txt test_results.txt ingester,distributor,querier,querier/queryrange,iter,storage,chunkenc,logql,loki

Change in test coverage per package. Green indicates 0 or positive change, red indicates that test coverage for a package fell.

+           ingester	0%
+        distributor	0%
+            querier	0%
+ querier/queryrange	0%
+               iter	0%
+            storage	0%
+           chunkenc	0%
+              logql	0%
+               loki	0%

@DylanGuedes
Copy link
Contributor Author

Benchmark results with 3MB buffer size for decompression:

Running tool: /usr/local/go/bin/go test -benchmem -run=^$ -tags integration,requires_docker -bench ^BenchmarkReadlines$ github.com/grafana/loki/clients/pkg/promtail/targets/file

goos: linux
goarch: amd64
pkg: github.com/grafana/loki/clients/pkg/promtail/targets/file
cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
BenchmarkReadlines/2000_lines_of_log_.tar.gz_compressed-12         	     768	   1559020 ns/op	 3063606 B/op	     239 allocs/op
BenchmarkReadlines/100000_lines_of_log_.gz_compressed-12           	      18	  63992058 ns/op	 5666200 B/op	   21157 allocs/op
PASS
ok  	github.com/grafana/loki/clients/pkg/promtail/targets/file	3.221s

@DylanGuedes
Copy link
Contributor Author

Benchmark results with 4096 buffer size:

Running tool: /usr/local/go/bin/go test -benchmem -run=^$ -tags integration,requires_docker -bench ^BenchmarkReadlines$ github.com/grafana/loki/clients/pkg/promtail/targets/file

goos: linux
goarch: amd64
pkg: github.com/grafana/loki/clients/pkg/promtail/targets/file
cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
BenchmarkReadlines/2000_lines_of_log_.tar.gz_compressed-12         	     933	   1261038 ns/op	   60287 B/op	     236 allocs/op
BenchmarkReadlines/100000_lines_of_log_.gz_compressed-12           	      19	  58614711 ns/op	 2536763 B/op	   20279 allocs/op
PASS
ok  	github.com/grafana/loki/clients/pkg/promtail/targets/file	3.167s

@DylanGuedes
Copy link
Contributor Author

@dannykopping Merging as I addressed all your great suggestions. I'll be working on the zip compression on a follow-up PR 😄

@DylanGuedes DylanGuedes merged commit 73bea7e into grafana:main Sep 27, 2022
lxwzy pushed a commit to lxwzy/loki that referenced this pull request Nov 7, 2022
**What this PR does / why we need it**:
Adds to Promtail the ability to read compressed files. It works by:
1. Infer which compression format to use based on the file extension
2. Uncompress the file with the native `golang/compress` packages
3. Iterate over uncompressed lines and send them to Loki

Its usage is the same as our current file tailing.

**Which issue(s) this PR fixes**:
Fixes grafana#5956 

Co-authored-by: Danny Kopping <dannykopping@gmail.com>
changhyuni pushed a commit to changhyuni/loki that referenced this pull request Nov 8, 2022
**What this PR does / why we need it**:
Adds to Promtail the ability to read compressed files. It works by:
1. Infer which compression format to use based on the file extension
2. Uncompress the file with the native `golang/compress` packages
3. Iterate over uncompressed lines and send them to Loki

Its usage is the same as our current file tailing.

**Which issue(s) this PR fixes**:
Fixes grafana#5956 

Co-authored-by: Danny Kopping <dannykopping@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size/XXL type/docs Issues related to technical documentation; the Docs Squad uses this label across many repositories
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Promtail] Support reading from compressed log files
5 participants