From af2fc054e7cbf72004601052bb7359cbc6fdf1f8 Mon Sep 17 00:00:00 2001
From: John Harris <john.harris@nexusmods.com>
Date: Wed, 20 Jul 2022 13:34:08 +0100
Subject: [PATCH] feat: add dogstatsd support

---
 lib/nexus_semantic_logger.rb                  |  2 ++
 lib/nexus_semantic_logger/application.rb      |  6 ++--
 .../datadog_singleton.rb                      | 15 +++++++++
 lib/nexus_semantic_logger/datadog_tracer.rb   | 33 +++++++++++++++++++
 nexus_semantic_logger.gemspec                 |  1 +
 5 files changed, 55 insertions(+), 2 deletions(-)
 create mode 100644 lib/nexus_semantic_logger/datadog_singleton.rb
 create mode 100644 lib/nexus_semantic_logger/datadog_tracer.rb

diff --git a/lib/nexus_semantic_logger.rb b/lib/nexus_semantic_logger.rb
index b7f2a0a..6867bf4 100644
--- a/lib/nexus_semantic_logger.rb
+++ b/lib/nexus_semantic_logger.rb
@@ -2,6 +2,8 @@
 require 'nexus_semantic_logger/appender_filter'
 require 'nexus_semantic_logger/application'
 require 'nexus_semantic_logger/datadog_formatter'
+require 'nexus_semantic_logger/datadog_singleton'
+require 'nexus_semantic_logger/datadog_tracer'
 
 module NexusSemanticLogger
 end
diff --git a/lib/nexus_semantic_logger/application.rb b/lib/nexus_semantic_logger/application.rb
index 67314f6..de5f4ae 100644
--- a/lib/nexus_semantic_logger/application.rb
+++ b/lib/nexus_semantic_logger/application.rb
@@ -36,6 +36,8 @@ module NexusSemanticLogger
       dd_appender = config.semantic_logger.add_appender(io: $stdout, formatter: config.rails_semantic_logger.format)
       dd_appender.filter = NexusSemanticLogger::AppenderFilter.filter_lambda
 
+      NexusSemanticLogger::DatadogTracer.new(service)
+
       logger.info('SemanticLogger initialised.', level: config.log_level)
     end
 
@@ -48,13 +50,13 @@ module NexusSemanticLogger
       color_appender = config.semantic_logger.add_appender(io: $stdout, formatter: :color)
       color_appender.filter = NexusSemanticLogger::AppenderFilter.filter_lambda
 
-      if ENV['DD_AGENT_HOST'].present? && ENV['DD_AGENT_PORT'].present?
+      if ENV['DD_AGENT_HOST'].present? && ENV['DD_AGENT_LOGGING_PORT'].present?
         # Development logs can be sent to datadog via a TCP logging endpoint on a local agent.
         # Each port is assigned a particular service.
         # See https://logger.rocketjob.io/appenders.html
         dd_appender = config.semantic_logger.add_appender(
           appender: :tcp,
-          server: "#{ENV['DD_AGENT_HOST']}:#{ENV['DD_AGENT_PORT']}",
+          server: "#{ENV['DD_AGENT_HOST']}:#{ENV['DD_AGENT_LOGGING_PORT']}",
           formatter: config.rails_semantic_logger.format
         )
         dd_appender.filter = NexusSemanticLogger::AppenderFilter.filter_lambda
diff --git a/lib/nexus_semantic_logger/datadog_singleton.rb b/lib/nexus_semantic_logger/datadog_singleton.rb
new file mode 100644
index 0000000..4edf497
--- /dev/null
+++ b/lib/nexus_semantic_logger/datadog_singleton.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+require 'singleton'
+
+module NexusSemanticLogger
+  # Application wide location to get datadog objects.
+  # Can be moved to its own gem in future, and there is scope to make the usage code even leaner.
+  class DatadogSingleton
+    include Singleton
+    attr_accessor :statsd, :tags
+
+    def flush
+      statsd&.flush(sync: Rails.env.development?) # Force flush sync in development, speed up checks.
+    end
+  end
+end
diff --git a/lib/nexus_semantic_logger/datadog_tracer.rb b/lib/nexus_semantic_logger/datadog_tracer.rb
new file mode 100644
index 0000000..1b3665a
--- /dev/null
+++ b/lib/nexus_semantic_logger/datadog_tracer.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+require 'datadog/statsd'
+require 'ddtrace'
+
+module NexusSemanticLogger
+  class DatadogTracer
+    def initialize(service)
+      Datadog.configure do |c|
+        if ENV['DD_AGENT_HOST'].present?
+          # To enable runtime metrics collection, set `true`. Defaults to `false`
+          # You can also set DD_RUNTIME_METRICS_ENABLED=true to configure this.
+          c.runtime_metrics.enabled = true
+
+          # Configure DogStatsD instance for sending runtime metrics.
+          # By default, runtime metrics from the application are sent to the Datadog Agent with DogStatsD over port 8125.
+          datadog_singleton = DatadogSingleton.instance
+          datadog_singleton.statsd = Datadog::Statsd.new(ENV['DD_AGENT_HOST'], 8125)
+          datadog_singleton.tags = ["env:#{Rails.env}", "service:#{service}"]
+          c.runtime_metrics.statsd = datadog_singleton.statsd
+
+          # Tracer requires configuration to a datadog agent via DD_AGENT_HOST.
+          dd_force_tracer_val = ENV.fetch('DD_FORCE_TRACER', false)
+          dd_force_tracer = dd_force_tracer_val.present? && dd_force_tracer_val.to_s == 'true'
+          c.tracer(enabled: Rails.env.production? || dd_force_tracer, env: Rails.env)
+        end
+
+        c.use(:rails, service_name: service)
+
+        c.logger.level = Logger::WARN # ddtrace info logging is too verbose.
+      end
+    end
+  end
+end
diff --git a/nexus_semantic_logger.gemspec b/nexus_semantic_logger.gemspec
index 5a4a960..6fd50e8 100644
--- a/nexus_semantic_logger.gemspec
+++ b/nexus_semantic_logger.gemspec
@@ -16,5 +16,6 @@ Gem::Specification.new do |spec|
   spec.add_dependency('amazing_print', '~> 1.4.0')
   spec.add_dependency('rails_semantic_logger', '~> 4.10.0')
   spec.add_dependency('net_tcp_client', '~> 2.2.0') # For TCP logging.
+  spec.add_dependency('dogstatsd-ruby', '~> 5.4.0') # For custom application metrics.
   spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
 end
-- 
GitLab