This gem is used to report opinionated statistics about modularization to DataDog and other observability systems.
This gem assumes you've correctly configured the packs gem so that pack_stats knows where to find your code's packs.
The gem reports metrics per-team, where each team is configured based on metadata included in Packwerk package.yml files.
Define your teams as described in the Code Team - Package Based Ownership documentation.
The main method to this gem is PackStats#report_to_datadog!. Refer to the Sorbet signature for this method for the exact types to be passed in.
This is an example of how to use this API:
PackStats.report_to_datadog!( # # A properly initialized `Dogapi::Client` # Example: Dogapi::Client.new(ENV.fetch('DATADOG_API_KEY') # datadog_client: datadog_client, # # Time attached to the metrics # Example: Time.now # report_time: report_time # # This is used to determine what files to look at for building statistics about what types of files are packaged, componentized, or unpackaged. # This is an array of `Pathname`. `Pathname` can be relative or absolute paths. # # Example: source_code_pathnames = Pathname.glob('./**/**.rb') # source_code_pathnames: source_code_pathnames, # # A file is determined to be componentized if it exists in any of these directories. # This is an array of `Pathname`. `Pathname` can be relative or absolute paths. # # Example: [Pathname.new("./gems")] # componentized_source_code_locations: componentized_source_code_locations, )
It's recommended to run this in CI on the main/development branch so each new commit has metrics emitted for it.
require 'pack_stats' def report(verbose:, max_enforcements: false) ignored_paths = Pathname.glob('spec/fixtures/**/**') source_code_pathnames = Pathname.glob('{app,components,lib,packs,spec}/**/**').select(&:file?) - ignored_paths PackStats.report_to_datadog!( datadog_client: Dogapi::Client.new(ENV.fetch('DATADOG_API_KEY')), app_name: Rails.application.class.module_parent_name, source_code_pathnames: source_code_pathnames, verbose: verbose, max_enforcements: max_enforcements ) end namespace(:pack_stats) do desc( 'Publish pack_stats to datadog. ' \ 'Example: bin/rails "pack_stats:upload"' ) task(:upload, [:verbose] => :environment) do |_, args| verbose = args[:verbose] == 'true' || false # First send without any changes, tagging metrics with max_enforcements:false report(verbose: verbose, max_enforcements: false) # At Gusto, it's useful to be able to view the dashboard as if all enforce_x were set to true. # To do this, we rewrite all `package.yml` files with `enforce_dependencies` and `enforce_privacy` # set to true, then bin/packwerk update-todo. old_packages = ParsePackwerk.all old_packages.each do |package| new_package = package.with(enforce_dependencies: true, enforce_privacy: true) ParsePackwerk.write_package_yml!(new_package) end Packwerk::Cli.new.execute_command(['update-todo']) # Now we reset it back so that the protection values are the same as the native packwerk configuration old_packages.each do |package| ParsePackwerk.write_package_yml!(package) end # Then send after maxing out enforcements, tagging metrics with max_enforcements:true report(verbose: verbose, max_enforcements: true) end end
Right now this tool sends metrics to DataDog only. However, if you want to use this with other tools, you can call PackStats.get_metrics(...) to get generic metrics that you can then send to whatever observability provider you use.
We recommend the use of the dashboard that is shipped with this gem. You can create a new dashboard and then click "import dashboard JSON" to get a jump start on tracking your metrics. Note you may want to make some tweaks to these dashboards to better fit your organization's circumstances and goals.