4 ms·
I fight log infra all the time. I can't win the fight against structured logging anymore, so I'm now fighting against type systems and allergies to global state
by vinceguidry 4y ago
I fight log infra all the time. I can't win the fight against structured logging anymore, so I'm now fighting against type systems and allergies to global state to make log output available everywhere. If you're going to ram structured logging down devs' throats, then the least you can do is to make it easy enough to use. I don't want to have to pass a logging object everywhere. There's like two pieces of information you need in order to make a logger. Just write it to global state somewhere so I don't have to worry about it and can call it anywhere.
I absolutely loathe reinventing global state by passing "context" objects and the like everywhere. It's the dumbest thing in the world but no one ever questions it.
- kevmo314 4y agoAt least passing context objects everywhere is better than dynamic dependency injection. I'm in the "dump the logger in a global variable" boat too though.
- TeMPOraL 4y agoI was somewhat in that boat too, until the first time I had to make several modules in the code log in a special way (that required some custom code), determined at runtime. Mostly I just wish more languages had Lisp-style dynamic binding / "special variables". Logging is one of the perfect use cases for dynamic scope - you'd have your normal logger object/configuration as the top-level value of a global, and then let-bind it whenever you need to alter its value for all code executed within that specific scope. Alas, about the only widely-used form of dynamic binding today is environment variables.
- chriswarbo 4y agoI do this in Scala, via https://www.scala-lang.org/api/2.12.13/scala/util/DynamicVariable.html https://www.scala-lang.org/api/2.12.13/scala/util/DynamicVar... It's not perfect, since it uses JVM's thread-local storage under the hood; this can break when e.g. evaluating Futures in a ThreadPool. For variables which are rarely-overridden, like loggers, I do so with a wrapper that also switches the ExecutionContext to a new ThreadPool (urgh, multithreading...) PS: I do the same for env vars too ;) http://www.chriswarbo.net/blog/2021-04-08-env_vars.html http://www.chriswarbo.net/blog/2021-04-08-env_vars.html
- TeMPOraL 4y agoThat's a neat post, thanks for sharing! I didn't realize Scala had dynamic variables. As for the HN comment that prompted your blog article, I did a double-take reading it, because I could've sworn I wrote the exact same thing around the same time - turns out I did, though on a different thread :).
- marmada 4y agoWhat is the reason structured logging is bad? I'm curious, as I felt like it made my life a lot easier.
- antonyt 4y agoI have the same question! I understand parent's criticisms of context objections and logging boilerplate, but I'm not following the "fight against structured logging." What are the alternatives? No logging? Unstructured logging? Why would either of those be better than structured logs?
- partdavid 4y agoWhile I personally favor no logging, almost everyone who criticizes structured logging would prefer unstructured logging so they can make it someone else's job to restructure it (i.e. index and query it).
- vinceguidry 4y agoIt's no work at all to go from regular language to context-free language, and a whole lot more work (parsing) to go the other way.
- vinceguidry 4y agoSimply put, it makes logs unreadable for humans without tooling. Things primarily touched by humans should be as friendly as possible to them. Prior to the advent of structured logging, every log message had a unique visual 'fingerprint' that the eyes and brain could grok at a glance, and spot anomalies really quickly. With structured logs, everything looks the same and so you can't use the brain's inbuilt abilities to process them. And if you did want tooling to help, then typically the formats were regular and so you could use ordinary text processing tools to help. sed, awk, grep. With structured logging putting everything into nested balanced expressions, you need parsing. Parsing works until you run across something the parser can't figure out. Say you have some Go code with a JSON logger object you're passing around. What if you want to log something but your logging object isn't passed to that function. (you could pass it everywhere but that increases the arity of every single method by 1, and are also reinventing global state poorly) You're SOL, now you're stuck with fmt.Println() and you just broke jq. No, jq does not handle this failure mode. No, Golang does not let you just spit out arbitrary JSON. Thy must use the logging object. The only thing it helps is ingestion into databases for heavy machine processing. Which is fine, but don't make it the only or even default way software tools spit out logs. In every other way, introducing parsing into your workflow just slows it all down. The only way I can see structured logging making anyone's job easier is if they never understood how it all worked before.