惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
WordPress大学
WordPress大学
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
Vercel News
Vercel News
aimingoo的专栏
aimingoo的专栏
T
Tailwind CSS Blog
罗磊的独立博客
B
Blog
博客园_首页
A
About on SuperTechFans
有赞技术团队
有赞技术团队
V
V2EX
U
Unit 42
I
InfoQ
IT之家
IT之家
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
The Cloudflare Blog
H
Help Net Security

Ivan on Containers, Kubernetes, and Server-Side

A grounded take on agentic coding for production environments Server-Side Playgrounds Reimagined: Build, Boot, and Network Your Own Virtual Labs [not a] Kubernetes 101 - Pods, Deployments, and Services As an Attempt To Automate Age-Old Infra Patterns JavaScript or TypeScript? How To Benefit From the Dichotomy On Software Design... and Good Writing Building a Firecracker-Powered Course Platform To Learn Docker and Kubernetes How To Publish a Port of a Running Container What Actually Happens When You Publish a Container Port A Visual Guide to SSH Tunnels: Local and Remote Port Forwarding Debugging Containers Like a Pro Docker: How To Debug Distroless And Slim Containers How To Extract Container Image Filesystem Using Docker | iximiuz Labs In Pursuit of Better Container Images: Alpine, Distroless, Apko, Chisel, DockerSlim, oh my! How To Start Programming In Go: Advice For Fellow DevOps Engineers Kubernetes Ephemeral Containers and kubectl debug Command How To Develop Kubernetes CLIs Like a Pro Docker Container Commands Explained: Understand, Don't Memorize | iximiuz Labs Learning Docker with Docker - Toying With DinD For Fun And Profit How To Extend Kubernetes API - Kubernetes vs. Django The Influence of Plumbing on Programming How To Call Kubernetes API from Go - Types and Common Machinery How To Call Kubernetes API using Simple HTTP Client Kubernetes API Basics - Resources, Kinds, and Objects OpenFaaS - Run Containerized Functions On Your Own Terms Learning Containers From The Bottom Up Docker Containers vs. Kubernetes Pods - Taking a Deeper Look | iximiuz Labs Learn-by-Doing Platforms for Dev, DevOps, and SRE Folks How HTTP Keep-Alive can cause TCP race condition How to Work with Container Images Using ctr | iximiuz Labs Multiple Containers, Same Port, no Reverse Proxy...
pq - parse and query log files as time series
Ivan Velichko · 2021-07-08 · via Ivan on Containers, Kubernetes, and Server-Side

I often find myself staring at Nginx or Envoy access logs flooding my screens with real-time data. My only wish at such moments is to be able to aggregate these lines somehow and analyze the output at a slower pace, ideally, with some familiar and concise query language. And to my surprise, I haven't met a tool satisfying all my requirements yet. Well, I should be honest here - I haven't done thorough research. But if there would be a tool as widely known as jq for JSON, I wouldn't miss it probably.

So, here we go - my attempt to write a full-fledged parsing and query engine and master Rust at the same time. Yes, I know, it's a bad idea. But who has time for good ones?

First things first - a usage preview:

Level up your server-side game — join 20,000 engineers getting insightful learning materials straight to their inbox.

The project is heavily influenced by jq and PromQL. Recently, I also learned about angle-grinder so I had a chance to incorporate some ideas from that wonderful but less known tool too.

I somehow tend to see logs as time-series data. So, I want to work with logs as with time series. In an ideal world, all the logs would be aggregated, parsed into fields and metrics, stored in ElasticSearch and Prometheus, and queried from Kibana and Grafana. However, we don't live in such a world just yet. So, I want to be able to quickly parse semi-structured files locally. The parsing result should be a stream of timestamped and strongly typed records. Having such a stream, I'd be able to query it with PromQL-like language. While angle-grinder is quite powerful at parsing and manipulating data, I still find it hardly usable to work with time series. On the contrary, I tried to design pq time-series-first. It has rather basic parsing capabilities, almost no transformation features, but extensive query functionality is a must.

Here is how a typical pq command may look like:

pq '/[^\[]+\[([^]]+)]\s+"([^\s]+)[^"]*?"\s+(\d+)\s+(\d+).*/
| map { .0:ts, .1 as method, .2:str as status_code, .3 as content_len } 
| sum(sum_over_time(content_len[1s])) by (method) / 1024'

A program consists of a mandatory decoding step, followed by an optional mapping step, and by another optional query step separated by the | symbol. Below I'll put some screencasted demos, and don't forget to check out the README file for a more technical explanation.

Decoding:

Mapping:

Querying and formatting:

To eat my own dog food, I used pq to analyze the Nginx access.log file of this blog. I hacked a tiny HTML page to visualize the JSON produced by pq:

Monthly page views by post language:

pq '
/([^\s]+).*?\[([^\]]+).*?"([A-Z]+)\s+\/(en|ru)\/posts\/([a-z0-9-]+)\/.*?HTTP.*?(\d+)\s+(\d+)/
| map {
    .0 as ip,
    .1:ts,
    .2 as method,
    .3 as lang,
    .4 as post,
    .5:str as status_code,
    .6 as content_len
  }
| select sum(
      count_over_time(
          __line__{method="GET", status_code="200"}[4w]
      )
  ) by (lang)
| to_json'

Monthly pageviews by blog post language.

Monthly page views by post:

pq '
/([^\s]+).*?\[([^\]]+).*?"([A-Z]+)\s+\/(en|ru)\/posts\/([a-z0-9-]+)\/.*?HTTP.*?(\d+)\s+(\d+)/
| map {
    .0 as ip,
    .1:ts,
    .2 as method,
    .3 as lang,
    .4 as post,
    .5:str as status_code,
    .6 as content_len
  }
| select sum(
      count_over_time(
          __line__{method="GET", status_code="200"}[4w]
      )
  ) by (post)
| to_json  

Monthly pageviews by blog post.

The above graph looks a bit overwhelming, so here is Top 10 posts by monthly page views:

pq '
/([^\s]+).*?\[([^\]]+).*?"([A-Z]+)\s+\/(en|ru)\/posts\/([a-z0-9-]+)\/.*?HTTP.*?(\d+)\s+(\d+)/
| map {
    .0 as ip,
    .1:ts,
    .2 as method,
    .3 as lang,
    .4 as post,
    .5:str as status_code,
    .6 as content_len
  }
| select topk(
      10,
      sum(
          count_over_time(
              __line__{method="GET", status_code="200"}[4w]
          )
      ) by (post)
  )
| to_json'

Monthly Top-10 blog post.

While working on pq, I often needed to dig into Prometheus code for better understanding of some query logic. So, if you deal with PromQL, I tried to share some of my findings on the way:

Fun fact

I've been starting to write this tool several times already. The first attempt was somewhere in 2014, but I didn't manage to produce anything meaningful then. In 2016, I started the echelon0 project, and it happily coincided with a wonderful time of becoming a father of a little girl. Obviously, I didn't get any spare time for this side project. In 2019 I finally got a chance to work on it again, this time under the codename amnis. However, I overcomplicated it right from the beginning. The design was so powerful and all-around that I never got to the code. And finally, pq resulted in something more or less functional.

Cheers!

Level up your server-side game — join 20,000 engineers getting insightful learning materials straight to their inbox: