Working on Lumberjack v14, the extensible logging library for Angular

layzee

Lars Gyrup Brink Nielsen

Posted on August 9, 2022

Working on Lumberjack v14, the extensible logging library for Angular

Cover illustration by DALL-E.

@nachovazquez and I have been working on the next release of Lumberjack for a few months.

Maintaining developer tooling

When we implemented version 2 of Lumberjack, we used the Angular CLI with customized Angular architect configurations to support workspace library projects, ESLint, Prettier, and Jest.

Maintaining developer tooling is quite a task on its own. We love Nx but at the time, Nx was behind major Angular releases for several months which isn't suitable for an open-source library.

Since then, a lot has happened in the realm of Nx. The release cadence of Nx is now independent of Angular while major and minor versions of Angular gain native support within a few days or weeks.

We want to spend the majority of our time contributing features and addressing issues, not maintaining developer tooling and configurations.

Cross-version compatibility

The current Lumberjack CI workflow verifying cross-version Angular compatibility

With the current version of Lumberjack, version 2.0, we are able to support many major and minor releases of Angular which is important to a plugin-based library to make it easier for plugin maintainers.

However, the partial Ivy compilation option was marked as stable in Angular version 12.2. Currently, the latest version of Angular is version 14.1. It is now a matter of time before the Angular Compatibility Compiler (NGCC) is removed from Angular, breaking compatibility with View Engine-compatible library bundles.

Ideally, we would have loved to continue to support multiple major and minor versions of Angular with one major version of Lumberjack. The version of Angular removing NGCC will be a milestone ending backward compatibility for Angular libraries without additional tooling.

For Lumberjack version 2, we developed the Angular Versions GitHub Action to verify cross-version compatibility with Angular, RxJS, TypeScript, and Node.js. However, it supports the Angular CLI, not the Nx CLI.

Maybe at some point, we can create a similar GitHub action for Nx or extend the support for the Angular Versions action to cover Nx workspaces using the Angular preset.

Motivation for the upcoming Lumberjack releases

For now, we want to continue the work on Lumberjack which has had a broken CI workflow for a year due to our extensive custom developer tooling and CI workflow.

We want to be ready for the major or minor release of Angular that removes the Angular Compatibility Compiler so that we don't prevent Lumberjack consumers from upgrading to the latest Angular releases.

We also want to explore new opportunities that weren't available in Angular prior to version 14:

  • Standalone APIs
  • Resolving dependencies using the inject function

These features are game changers for certain Angular library use cases.

Lumberjack has a lot of configurations which in version 2 has been available through Angular modules with providers. In a future version of Lumberjack, we plan to add standalone APIs, that is functions accepting options to adjust its returned providers needed by Lumberjack and Lumberjack log drivers.

Support for the inject function in class constructors and property initializers can enable us to remove internal dependencies from Lumberjack classes that are meant to be extended:

  • LumberjackLogger
  • ScopedLumberjackLogger

Migrating to an Nx workspace

As migrating a custom Angular CLI workspace is a relatively big effort, we created a new Nx workspace in the separate GitHub repository, ngworker/lumberjack-next. Here, you can follow our work if you are interested in seeing our future setup which uses:

  • Nx
  • Cypress
  • Distributed Task Execution (DTE) through Nx Cloud

As an added benefit, this effort starts to bridge the gap between the Lumberjack repository and our ngworker monorepo which currently contains Spectacular and uses Nx 12 at the time of writing. Additionally, our ngworker/router-component-store repository uses Nx 13.

It would be great to combine all of our Angular libraries into a monorepo to decrease our maintenance efforts. As an added benefit, we could use Spectacular to integration test the Lumberjack and Router ComponentStore libraries.

Distributed Task Execution

The Lumberjack CI workflow using Distributed Task Execution via Nx Cloud
As an added benefit of migrating to an Nx workspace, we will use Distributed Task Execution via Nx Cloud. Even in a small Nx workspace like the Lumberjack codebase, we can benefit from distributing tasks across a number of agents rather than having one GitHub runner for unit tests, one for end-to-end tests, one for lint checks, and one for builds.

This is a benefit given that most of our unit tests are located in the Lumberjack library project. Instead of waiting for all unit tests to run on a single GitHub runner, the unit tests are distributed to idle agents on a project basis. This results in an overall permanent decrease in CI workflow time as tasks are intelligently distributed by Nx Cloud and the GitHub runner acting as a coordinator based on both historic and actual execution time of tasks.

Should we manage to combine two or more of our Nx repositories into a single Nx monorepo workspace, the benefits of Distributed Task Execution increase as the difference between execution time of similar targets increase combined with the option to increase the number of available agents. It's worth mentioning that DTE agents are GitHub runners, not machines managed by Nx Cloud. Nx Cloud calculates task execution based on input from the DTE coordinator, also a GitHub runner, and caches target output but targets aren't executed inside the Nx Cloud.

Adding a GitHub Actions concurrency strategy

I also managed to set up a GitHub Actions concurrency strategy ensuring that:

  • Workflow runs in progress for the same branch are cancelled when new commits are pushed. This eliminates compute waste.
  • Workflow runs on the main branch, that is when a pull request has been merged, are run sequentially, not in parallel. This prevents multiple releases and other deployment activities with side effects from happening simultaneously so that they don't affect each other.

Take a look at the CI workflow in the ngworker/lumberjack-next repository if you're interested in Distributed Task Execution using Nx Cloud or GitHub Actions concurrency strategies.

Integrating SonarCloud in an Nx workspace using GitHub Actions

The Lumberjack project on SonarCloud
The Lumberjack vNext CI workflow is also where we will integrate SonarCloud similar to our current CI workflow.

Currently missing from our Lumberjack vNext effort is ensuring that our SonarCloud integrations continue to work so that we can:

  • Enforce test coverage thresholds
  • Eliminate code smells
  • Reduce cognitive complexity
  • Prevent security hotspots, vulnerabilities, and bugs
  • Remove code duplication

Abandoning the Angular ng-add schematic

@nachovazquez and I have agreed to leave behind the ng-add Angular schematic for Lumberjack for now. Should we want to add it back to the library, we would have to migrate it to the Nx Devkit with Nx-native end-to-end tests rather than the Angular Devkit and our current custom Jest end-to-end tests.

For one, the ng-add schematic isn't the most important feature of Lumberjack. Another reason is that Angular libraries have to expect both legacy applications using Angular modules, in particular an AppModule, as well as standalone Angular applications without any Angular modules, even without a root Angular module which is typically named AppModule.

Currently, it is not clear how Angular library authors are to support both Angular application types in ng-add schematics.

💖 💪 🙅 🚩
layzee
Lars Gyrup Brink Nielsen

Posted on August 9, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related