Show HN: My Single-File Python Script I Used to Replace Splunk in My Startup (github.com)
"I hated Splunk so much that I spent a couple days a few months ago writing a single 1200 line python script that does absolutely everything I need in terms of automatic log collection, ingestion, and analysis from a fleet of cloud instances. It pulls in all the log lines, enriches them with useful metadata like the IP address of the instance, the machine name, the log source, the datetime, etc. and stores it all in SQlite, which it then exposes to a very convenient web interface using Datasette.
I put it in a cronjob and it's infinitely better (at least for my purposes) than Splunk, which is just a total nightmare to use, and can be customized super easily and quickly. My coworkers all prefer it to Splunk as well. And oh yeah, it's totally free instead of costing my company thousands of dollars a year! If I owned CSCO stock I would sell it-- this deal shows incredibly bad judgment."
I had been meaning to clean it up a bit and open-source it but never got around to it. However, someone asked today in response to my comment if I had released it, so I figured now would be a good time to go through it and clean it up, move the constants to an .env file, and create a README.
This code is obviously tailored to my own requirements for my project, but if you know Python, it's extremely straightforward to customize it for your own logs (plus, some of the logs are generic, like systemd logs, and the output of netstat/ss/lsof, which it combines to get a table of open connections by process over time for each machine-- extremely useful for finding code that is leaking connections!). And I also included the actual sample log files from my project that correspond to the parsing functions in the code, so you can easily reason by analogy to adapt it to your own log files.
As many people pointed out in responses to my comment, this is obviously not a real replacement for Splunk for enterprise users who are ingesting terabytes a day from thousands of machines and hundreds of sources. If it were, hopefully someone would be paying me $28 billion for it instead of me giving it away for free! But if you don't have a huge number of machines and really hate using Splunk while wasting thousands of dollars, this might be for you.
81 comments
[ 3.2 ms ] story [ 316 ms ] threadhttps://docs.python.org/3/library/zipfile.html
Some versions of tar are able to extract zip files.
Try
It might or might not work with the version in your OS:-)
Thank you for sharing!
Recall that the poster said this was for a small startup. If you're Google, by all means, use Google logging tools. If you aren't, then solve the problem you have, not the problem your résumé needs.
> Twenty years later, I still can't fathom why we're spending so much money on Splunk, DataDog a the like.
And the poster above answered that question
I've also been told, time and again, in no uncertain terms, to "buy as much as possible". We've reached the logical conclusion of SaaS-everything: every company just cobbles together expensive, overcomplicated computers from other expensive, overcomplicated computer providers, resulting in expensive, bloated systems that barely work.
More power to you for using sed awk and grep, they're powerful tools and every computer person should know how to use them. But if you're hung up on only using sed awk and grep for emotional reasons, that's self-limiting. We have better tools today, and you don't get hero points for using shittier tools when there are better ones available to you.
https://www.splunk.com/en_us/blog/tips-and-tricks/mapping-wi...
Thanks but no thanks.
"The application has been tested with log files several gigabytes in size from dozens of machines and can process all of it in minutes."
That's the time it takes to connect to 20+ machines, download multiple gigs of log files from all of them, and parse/ingest all the data into a sqlite. If you have a big machine with a lot of cores and a lot of RAM, it's incredibly performant for what it does.
I encourage everyone to share your "splunk in 1kloc of Python" projects! Some of my own:
- https://github.com/rollcat/judo is Ansible without Python or YAML
- https://github.com/rollcat/zfs-autosnap manages rolling ZFS snapshots
But people are actually being surprisingly nice and friendly! I guess people just really hate Splunk!
I hate this meme. It's as if cars, trains, and airplanes all use the same wheels. Or that wheels under my stove, my tiny filing dresser, and my shopping cart are all the same.
Oh yeah, re-inventing the wheel, what a stupid idea and something we obviously don't frequently do and for good reasons.
This meme is almost as bad as the horrible misquoted "premature optimisation is the root of all evil".
I'm currently using this for a small application to easily backup databases in docker containers.
Best things in life come through love and passion. Frustration can be a good motivator but don't let it guide you.
> I thought this would get a lot of hostile takes [...]
To be entirely honest with you, recognizing and praising the good parts is a lot easier than giving proper feedback on what needs to be improved;)
[0] https://github.com/olafal0/configinator
[1] https://github.com/olafal0/configinator/blob/0576a53970bcb4d...
[2] https://github.com/olafal0/configinator/blob/0576a53970bcb4d...
There's also a bunch of other purpose-built tiny utilities on that GitHub account: https://github.com/aaviator42?tab=repositories
You would think. But no, there is lots of room to make it over complicated without the corresponding efforts to manage the complexity.
The only systems I am aware of which are reliably capable of "solving for desired state" are nix and guix.
Ansible might provide idempotence for the builtin things (although I would argue it doesn't, at least not on a bit-by-bit level, since you can't pin down specific versions of package repositories and stuff like that), but to be declarative it would need to provide a 1-to-1 mapping from declared state to running system state. And if what I described above is still the case, then it simply does not do that.
In my experience, ansible tries to build a declarative interface to an imperative mode of system management, which works to some extent, but breaks down in more complex cases because building this declarative interface can only be a leaky abstraction without the right foundation.
To illustrate my point, imagine this playbook:
Apply it and you get GNU hello installed. Now remove the package installation step, which really is just a glorified "ssh $host < script.sh": Apply that and you will still have hello installed, even though it was removed from the "declared state". This just pretends to be declarative but really isn't, the tasks are still imperative steps.Not to blame ansible for this, it is just that ansible is build on a foundation that inherently makes declarative system management impossible to begin with and ansible is more or less the best thing you could do given the constraints.
When I first created Judo, I envisioned some sort of a standard library as a sibling project, sort of an "executable rosetta stone" for Unix, where you could declaratively say things like "ensure this user exists", "ensure this package is installed".
In practice I found out it's fairly easy to just write your scripts to be idempotent. It was the secret "2. and do not overcomplicate things" step that most initially simple software seems to gradually forget about.
That may be so, but beware that acquisitions usually increase stock price rather than decrease it.
There was lots of stuff that relied on splunk, and we had splunk specialists who knew the magic splunkQL to get the graph/data they wanted.
However, we managed to remove most of the need for splunk by using graphite/grafana. It took about 2-3 years but it meant that non techs could create dashbaords and alerts.
As someone once told me, splunk is the most expensive way you can ignore your data.
* Importing Paramiko but regularly call `ssh` via subprocess
* Unused functions like `execute_network_commands_func`
* Sharing state via a global instead of creating a class
Overall it's fit for purpose, but makes a lot of assumptions about the host and client machines. As you said in the thread you're running a very small number of servers (less than 30). I've written similiar things over the years and they are great for what you need.
When I heavily used Splunk (back in 2013) I was in an application production support team that managed over 100 productions servers for over a dozen applications, there were dozens of other teams in similar situations across the company. The Splunk instance was managed by a central team, minimal assumptions about the client environment, had well defined permissions, understood common and essoteric logging formats, and could reinterpret the log structure at query time. A script like this is not competiting in that kind of situation.
In general, comments often add a very vaulable context to your code in a way that readaable function/variable names can't.
I guess I did forget to use the execute_network_commands_func. I'm using the ruff linter extension in VSCode now which would have flagged that to me, but back when I made this I wasn't.
I don't think globals are so awful for certain things. I prefer a more functional approach where you have simple composable standalone functions instead of classes. Obviously classes have a role, but I find they sometimes overly complicate things and make the logic harder to follow and debug.
Anyway, I do appreciate that someone took the time to actually read through the code!
But "globals" and "composable standalone functions" are contradictory, if you're mutating global state your function is neither composable nor standalone.
What you've got is a poor mans class instance using global instead of self.
It's over 1200 lines of code, it's not like it's 100 lines of code and can fit on a single screen
> Globals are fine--they're even marked as such
I would argue that globals in this context are not fine from a code maintainability point of view.
By using globals here it's hard to know from a function call if it's going to mutate global state or not. If all the functions were methods of the same class instance, and other functions were just functions or part of some other class, then it gives you a clear grouping of calls which are related to mutating that state.
In general I would argue if you are ever in the situation of "I have more than two or three functions that are related to each other and they all need to mutate the same state so I use a mutable global" or "I pass around the mutable state via arguments" then make a class! It creates an obvious semantic grouping of callables.
I disagree though that the deal shows any bad judgment on Cisco's part; the gravamen of whether the acquisition was good is not whether many software developers can quickly develop replacements for their own use-cases, or how ergonomic the software is, but whether Splunk is a profitable business with a bunch of paying subscriptions/contracts that aren't going to go away any time soon.
My happy wishes to you and your family!
=Cliff
Hope you're doing well!
Normally you'd avoid all that complexity by shipping logs the other way, sending from each machine. That way you can keep state locally should you need to. All unix-like systems do this out of the box, and almost all software supports the syslog protocol to directly stream logs. But you can also use something like filebeat and a bunch of other modern alternatives.
The analyzer can then run locally on the log server and a whole lot of complexity just disappears.
It's already there, it's supported by most logging packages, and it's dead simple. No additional software required. All text. What it doesn't do is structured logging, but analyzing on the log host is often enough.
Agents aren't all that bad however, and you're likely already running some agent like icinga or zabbix for regular monitoring.
also, with Netdata you can achieve the same architecture design using a Netdata Parent that could be your "control node" and to where you stream the metrics of the nodes you want to keep running with as less load as possible - you can even offload the health engine and the machine learning
take a look at https://learn.netdata.cloud/docs/streaming/ and https://learn.netdata.cloud/docs/configuring/how-to-optimize...
rsync --append is your friend.
It's also not as effective as streaming them to their intended target directly. Syslog can write a complementary local copy too should you wish to keep one.
Logs has been a thing since the past forty years. In order to reinvent it, it is good to be acquainted with the standard systems.
Log analysis isn't one of the core use-cases for Datasette, but I've done my own experiments with it that have worked pretty well - anything up to 10GB or so of data is likely to work just fine if you pipe it into SQLite, and you could go a lot larger than that with a bit of tuning.
I added some features to my sqlite-utils CLI tool a while back to help with log ingestion as well: https://simonwillison.net/2022/Jan/11/sqlite-utils/
For others with a bit more complex needs, take a look at the free (or paid) versions of Graylog Open[1].
It's really improved over the years. I had messed with Graylog in it's early days but was turned off by it. A few years back, I encountered someone doing some neat stuff with it. It looked much improved. I stood up a "pilot project" to test, and it's now been running for years and several different people use it for their areas of responsibility.
It does log collection/transforming and graphing and dashboarding and we use the everloving crap out of it at work. I wish I could publicly post some of the stuff we're doing with it.
It takes input from just about any source.
1. https://graylog.org/products/source-available/
Glad it works for you!