1. 21
  1. 13

    I agree with part of the premise, but this is way, way overengineered. My main problem with logs is they can clutter the code, making the code itself harder to understand at a glance. For that, having a simple function that abstracts the log into a nice compact name, something like LogExternalDependencyResponse() solves all of my problems.

    Why would I introduce all this machinery for logging? To decouple logging code from application logic? Logging code is one of the most clear examples of application logic.

    1. 15

      Isn’t it a Java principle that any problem can be solved by adding more interfaces to the program, and requiring more rituals around everyday tasks?

      1. 2

        The machinery needed to do this can be scaled up and down to your needs. For example, all events can be written to a file as JSON, where another process can be used to subscribe to the events. This key difference between events and logging is that each kind of event has a name and possibly some structure, whereas a log is typically unstructured text.

        1. 1

          What is another use for this?

          1. 3

            Another use for this is to do any sort of async processing based on events. For example, if you have an InviteCreatedEvent, then you can have something else listening for that event that sends out the invite email. The point is that you can get more out of your logs by pulling them into the application layer a tiny bit.

            1. 2

              Unit testing. Verifying that a particular log message has been written to the system log is cumbersome (in Java). Using events makes validating the application behaviour trivial: execute the production code under test and block until the event is received. Presuming that the event machinery is solid, there’s no need to add any extra code to scan the system log file for updates.

              1. 1

                Not at all cumbersome if you use something like SLF4J-test as noted in my article https://www.jvt.me/posts/2019/09/22/testing-slf4j-logs/

                1. 1

                  That’s neat, thank you for sharing. An advantage of the event method remains checking types and avoiding checking log message text except when validating the actual text of the message, which would be a test of the string representation or specific message owned by the event. Looking at the log output is one way to do it but that’s always felt brittle to me, esp. in a high change velocity codebase with multiple contributors having their own opinions of how things should be worded. At least the tests would catch that someone hasn’t deleted a log emission! slf4j-testing seems like a much cleaner way to look for log output than capturing stdout yourself, though!

        2. 7

          I like the concept of the events emitted by this post, but they feel closer to audit events to me, rather than the kind of data I used to want from logging. I say used to want, as for the last year or so, I have been removing/replacing logging with OpenTelemetry Tracing, which is giving me all the information logs would give me, along with call graphs, filterable properties, method timings, and the ability to visualise in multiple tools easily.

          Depending on the project I have used: Honeycomb, NewRelic, Zipkin, and Jaeger to name a few. They all have their own strengths and weaknesses, but the fact that they all can ingest the same data is a massive benefit to me.

          I really can’t see myself wanting to go back to using logs (structured or not) over this.

          1. 5

            Studies have shown both positive and negative impacts on duplicating source code, with findings weighted towards detrimental effects [..]

            The premise of the entire article, but no links to such studies.

              1. 2

                This is excellent stuff. Many thanks

            1. 4

              I’ve tried to do an AOP approach to logging, abstracting it to a sort of event emitter, that just happened to have a logging backend.

              It was an unmaintainable mess, especially in such a verbose language as Java. I’m surprised that’s what their examples are in. Then again, introducing some of the tooling they recommended would have taken more inhibition-overcoming than the group was capable of at the time.

              Now, if you’re already using some sort of event publishing / event sourcing system, you basically get it for free, which is super nice.

              1. 3

                A minor note, but an implication of this approach is that is requires references and all of the required machinery for that (heap allocation etc.). This isn’t a major issue with GC languages from an ergonomic sense (unlike say a Rust) but it does have potential performance implications beyond just the indirection (e.g. Go escape analysis triggering).

                1. 3

                  It seems to me that the major difference between logs and events is a viewpoint: logs are push, events are pull. Both can be structured or unstructured [1]. Both can have names or not [2]. Both can be acted upon, or ignored.

                  [1] At work, our logs are mostly structured.

                  [2] At work, each log has a unique ID, aka, name.

                  1. 3

                    One pattern that has worked well for us is what we call the “instrumentation” pattern (bad name, can’t think of a better one). For instance, we use it for logins, where we want to 1) log something, 2) write an audit trail and 3) update a JMX metric. So for the class Session (session management) we have a SessionInstrumentation class containing (for instance) a failedLogin(String username) method. This does everything necessary, and keeps the Session class itself clean. The business logic stays in Session, the logging/auditing/metrics are in the SessionInstrumentation class

                    1. 1

                      It’s not obvious to me what the benefit of the “event bus” version is over traditional logging.